Include jar file with Java sources in Android aar - java

I have a gradle task to create a jar file containing Java source files to be included in an Android aar library package. These files will serve as Javadoc for JNI to a C++ library also bundled in the aar package.
I cannot possibly figure out how to include the jar file and not compile the files within. it seems that the Java files in the jar file are compiled, which does not help me - I just want to include them so that they are available to developers using that aar package.
The created jar file has all the sources and is in the output aar inside its libs directory, but it has no contents.
How can I add the Java sources to my aar?
Jar creation
The Jar is created as follows and ends up in the build/libs folder of my module.
task generateMySources(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
artifacts {
archives generateMySources
}
preBuild.dependsOn(":myModule:generateMySources")
dependencies {
// The jar is included but it is empty inside the aar.
compile files('build/libs/myModule-sources.jar')
}
The output jar contains:
.
├── com
│   └── my
│   └── app
│   └── jni
│   ├── File1.java
│   ├── File2.java
│   ├── File3.java
│   └── File4.java
└── META-INF
└── MANIFEST.MF // Contains "Manifest-Version: 1.0" only.
The jar exists inside the aar inside the libs directory, but now it is empty.

Only thing I could come up with is to add your sources to the .aar file after it's built like so
task generateMySources(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}
task addMySourcesToAar(type: Jar) {
archiveName "myModuleWithSources.aar"
destinationDir file("build")
from zipTree("build/outputs/aar/myModule-release.aar")
from fileTree("build").include("libs/myModule-sources.jar")
}
afterEvaluate { project ->
project.tasks.preBuild.dependsOn generateMySources
project.addMySourcesToAar.dependsOn build
}
artifacts {
archives addMySourcesToAar.archivePath
}
and run
./gradlew myModule:addMySourcesToAar
I didn't add anything to dependencies like you did

For thus who are using Gradle Kotlin DSL:
tasks.register<Jar>(name = "sourceJar") {
from(android.sourceSets["main"].java.srcDirs)
classifier = "sources"
}
publishing {
publications {
create<MavenPublication>(name = "Maven") {
run {
groupId = "groupId"
artifactId = "module-name"
version = "1.0"
artifact("$buildDir/outputs/aar/module-name-release.aar")
artifact(tasks["sourceJar"])
}
}
}
}

Related

Gradle: Exclude file from resourses and add it from different folder

I am trying to build a jar file with Gradle.
I have some resource files (like log4j2 and persistent.xml) into the src/main/resources
that I want to exclude when I was built the jar, on contrary I have the same files (with different content in a project directory) and I want to copy these files rather than the src/main/resource files.
Into my gradle.build
from jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
// The parent folder is: src/main/resources for the (1) and (2)
exclude('META-INF/persistence.xml') // (1)
exclude('log4j2.xml') // (2)
from('distributions/persistence.xml'){ // (3)
into 'META-INF'
filter(ReplaceTokens, tokens: [TEST_PARAM: 'tmp_value1'])
}
from('distributions/log4j2.xml') { // (4)
filter(ReplaceTokens, tokens: [TEST_PARAM: 'tmp_value1'])
}
}
So I exclude the two files manually (1), (2), and then I added (3), (4).
When I extract the jar, I can find the persistence.xml but not the log4j2.xml.
And my question is, why does this happen?
(PS)
If i replace the (1), (2) with
processResources.exclude('*')
Into jar are placed both the persistence.xml but not the log4j2.xml.
Thank's in advance.
Accordingly to this documentation: Any exclusion pattern overrides any inclusions, so if a file or directory matches at least one exclusion pattern, it won’t be included, regardless of the inclusion patterns.
It seems like the behavior you observe is caused by how file exclusion is handled by gradle. The way you did it works for the files copied to a subfolder in jar file. For some reason it does not work for files copied to the root folder of jar file.
You can achieve what you want with this configuration:
jar {
from('distributions') {
include("**/*.xml")
}
eachFile {
if (it.file.path =~ "build/resources/main/") {
// exclude resources copied from build/resources/main/
it.exclude()
}
}
}
Here's the project tree I used since mine can be different from the project you are working on:
├── build.gradle
├── distributions
│   ├── META-INF
│   │   └── persistence.xml
│   └── log4j2.xml
└── src
└── main
   ├── java
   │   └── Main.java
   └── resources
   ├── META-INF
   │   └── persistence.xml
   └── log4j2.xml
This solution is based on these discussions:
https://discuss.gradle.org/t/how-to-override-where-some-files-are-written-to-in-the-jar-file/5282
How do you force Gradle to overwrite a resource file in a custom WAR task?

Refering to main class of a sub-project in a multi-project gradle setup

I'm working on a multi-project gradle setup, where the structure is like below:
pipeline-jobs // root folder
├── gradle.properties
├── settings.gradle
├── build.gradle
└── partition-by-users // sub-project
├── com.client.dataPipelineJobs.partitionByUsers
│ └── PartitionByUsers.java // has the main() method
└── build.gradle
pipeline-jobs is the root project folder and for now I've only one subproject called partition-by-users. In future many more sub-projects will get added. I want to build runnable jars for all these sub-projects. My build.gradle file under partition-by-users sub-project looks like below:
jar {
zip64 = true
manifest {
attributes('Main-Class': 'com.client.dataPipelineJobs.partitionByUsers.PartitionByUsers')
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
Now from the root project dir if I execute gradle jar command and then run the jar file, it throws the following error:
Could not find or load main class
com.client.dataPipelineJobs.partitionByUsers.PartitionByUsers
I'm not able to find out the root cause behind this. What am I missing here? I've tried changing the manifest with attributes('Main-Class': partition-by-users.com.client.dataPipelineJobs.partitionByUsers.PartitionByUsers) but that didn't help.
Update:
I think there's some issue with building the fat-jar. Building the jar without the dependencies solve the problem, but then probably I've find out a different way of preparing the fat-jar.
I found out the answer in this SO thread. Whoever bumps onto this page, please look at the answer provided by #blootsvoets in the previous link.
The basic idea is to use the same manifest in jar and fatJar tasks.

Import class from "outside of the project" (Gradle + Scala)

I have a Gradle project with the following folder structure.
project
   ├── build.gradle
   └── src
   ├── main
   └── provided
The main folder has Scala source files, and the provided folder has some Java classes. I would like to import these Java classes in the Scala source code, but unable to do it. I use IntelliJ for editing. Should I write something in the build.gradle file, or can it be done in the IDE? When I mark the provided\java folder 'Sources root', the classes are still unavailable for import. Also, the build sync removes the mark from the folder.

Working with ceylon import-jar

How can I import a library from maven central into a project with the ceylon import-jar command?
Please show the full command.
E.g. for "joda-time-2.9.4.jar" from "http://repo.maven.apache.org/maven2/" into a local directory.
I guess it must be:
./ceylon-1.2.3/bin/ceylon import-jar --rep "http://repo.maven.apache.org/maven2/" --verbose --out localdir "joda-time:joda-time/2.9.4" "joda-time-2.9.4.jar"
But as far as I can see the tool is not working (ceylon versions 1.2.2 and 1.2.3).
Working with maven central is essential.
This question is linked with The ceylon copy tool because both tools present me with a riddle.
I understand you are asking about the ceylon import-jar tool specifically, but would like to offer a different solution that is easier if your goal is to import a jar from a remote repository.
I would suggest you use the Ceylon Gradle Plugin, which I wrote.
It knows how to grab dependencies from repositories (including JCenter and Maven Central, but many others), and it will run the ceylon -import-jar tool for you automatically.
Full Example:
Run the following command to create a new test project (enter simple for the folder name):
ceylon new simple --module-name=com.athaydes.test --module-version=1.0
Enter the new project name and have a look at what's in it (minimum Ceylon project):
cd simple
tree # or use Finder, Window Explorer or whatever
You'll see this:
└── source
└── com
└── athaydes
└── test
├── module.ceylon
├── package.ceylon
└── run.ceylon
Edit module.ceylon so it has the following contents (add whatever dependencies you want):
module com.athaydes.test "1.0" {
native("jvm")
import joda_time.joda_time "2.9.4";
}
Notice the name of the module must be a valid Ceylon identifier! So, the Gradle plugin replaces invalid characters with _, generating a valid Ceylon identifier from the Maven artifact name.
Create a build.gradle file at the root of the project so the Gradle plugin can work, with the following contents:
plugins {
id "com.athaydes.ceylon" version "1.2.0"
}
repositories {
jcenter()
}
ceylon {
module = "com.athaydes.test"
flatClasspath = false
importJars = true
forceImports = true // necessary to bypass optional dependencies issues in Maven poms
}
dependencies {
ceylonCompile "joda-time:joda-time:2.9.4"
}
We must declare this dependency here as a normal Maven dependency so Gradle knows where to get the Jars from.
Done... now just run importJars:
gradle importJars
Or, to just see the actual command generated (will not actually run it):
gradle -P get-ceylon-command importJars
Here's the generated command:
ceylon import-jar
--force
--descriptor=/Users/renato/programming/experiments/ceylon-gradle/simple/build/module-descriptors/joda_time_2.9.4.properties
--out=/Users/renato/programming/experiments/ceylon-gradle/simple/modules
--rep=aether:/Users/renato/programming/experiments/ceylon-gradle/simple/build/maven-settings.xml
--rep=/Users/renato/programming/experiments/ceylon-gradle/simple/modules
joda_time.joda_time/2.9.4
/Users/renato/.gradle/caches/modules-2/files-2.1/joda-time/joda-time/2.9.4/1c295b462f16702ebe720bbb08f62e1ba80da41b/joda-time-2.9.4.jar
The jars will be imported to the default location, modules (but you can configure that):
── build
│   ├── dependency-poms
│   │   └── joda-time-2.9.4.pom
│   ├── maven-repository
│   │   └── joda-time
│   │   └── joda-time
│   │   └── 2.9.4
│   │   ├── joda-time-2.9.4.jar
│   │   └── joda-time-2.9.4.pom
│   ├── maven-settings.xml
│   └── module-descriptors
│   └── joda_time_2.9.4.properties
├── build.gradle
├── modules
│   └── joda_time
│   └── joda_time
│   └── 2.9.4
│   ├── joda_time.joda_time-2.9.4.jar
│   ├── joda_time.joda_time-2.9.4.jar.sha1
│   └── module.properties
└── source
└── com
└── athaydes
└── test
├── module.ceylon
├── package.ceylon
└── run.ceylon
Now you can run the Ceylon code with the runCeylon task (or just run if there's no other task with this name):
gradle run
NOTE:
Unfortunately, actually importing the specific Jar you chose into the Ceylon repo is impossible with its original name... because in Ceylon, joda-time is an illegal identifier... so you need to change the name of the module when imported by Ceylon. The Gradle plugin does it for you.. but you need to know what the valid identifier will be to be able to write the import statement in the module file (you can just let the plugin run and it will tell you what the name will be).
A much simpler approach
If you want to avoid the complexity of this approach, you can just use the default Gradle plugin approach to NOT import Maven jars into the Ceylon repository and just use the simple Java classpath (which means you relinquish using the Ceylon modules system!).
If you do that, your build.gradle file will look like this:
plugins {
id "com.athaydes.ceylon" version "1.2.0"
}
repositories {
jcenter()
}
ceylon {
module = "com.athaydes.test"
}
And the module.ceylon file:
module com.athaydes.test "1.0" {
native("jvm")
import "joda-time:joda-time" "2.9.4";
}
Notice that we don't need to mess up with the dependency name using this approach. From Ceylon 1.2.3, you should prepend the dependency with the maven: qualifier to avoid warnings.
That simple!
1. As a (partial) answer to my question, this turned out to work:
$ ../bin/ceylon import-jar --rep flat:"../flat/" Jama/1.0.3 ../flat/Jama-1.0.3.jar
I downloaded the jar (in this case Jama-1.0.3.jar) by hand and then I was able to import it.
I had to try a lot to find out where to put the prefix "flat:", i.e. either to put it after "import" in the module descriptor "module.ceylon" or on the command line. The latter turned out to be the right choice.
But still, I haven't been able to find out how to import the jar from maven directly using the import-jar tool.
2. More detailed documentation is needed about managing modules. Specifically, there should be a clarification what the term "legacy repository" means.
Does "legacy" mean "deprecated"?
3. I hope that the following way to import dependencies into a project is not considered as "legacy" or "deprecated":
a) Rename the jar file, so that the name relfects the compressed directory structure within the jar.
b) Put the jar into a directory structure that again reflects the directory structure within the jar.
c) Put all that into the modules directory of the project, merging directories if necessary.
This seems to be the most explicit and reliable way to include dependencies into a project and I hope this way will not be deprecated or considered "legacy" at any time.

How can unpack ZIPs using Gradle and add it to the sourceSets?

I'm migrating my Play framework project to use Gradle. I have a few Play modules which are ZIPped. While I've migrated most of my dependency management to Gradle, I'm a bit stuck with the ZIP dependencies. I have a folder called mods which in turn contains artifact directories containing the ZIP.
mods/
└── play-mockito/
   └── mockito-1.0.zip
I'd like iterate each of the directories in the mods folder and unpack the ZIP in that folder into another directory like so:
unpacked-mods/
└── play-mockito/
   └── Main.java
├── Test.java
   └── AnotherClass.java
Once unpacked I'd like to add the unpacked directory recursively to the sourceSets. I'm a bit lost with Gradle and now sure how to accomplish this.
Got so far:
task modulate << {
def tree = fileTree('mods') {
include '*/*.zip'
}
tree.each {File file ->
def module = file.path.split('/')[-2]
println "Unpacking " + module
copy {
from zipTree(file)
into 'modules/' + module
}
}
}
compileJava.dependsOn(modulate)

Categories