I need to build an Android library as a JAR (as well as an AAR), but also with flavors. The flavors happen to be based on the source compatibility.
I have this,
android.productFlavors {
java6 {
// yes, other stuff here
}
java7 {
// and here
}
}
...
task jar(type: Jar) {
from android.sourceSets.main.java.srcDirs
}
But I only get one .jar in build/libs. I see the build output,
:common-taskqueue:jarJava6Debug
:common-taskqueue:jarJava7Debug
...
So it seems like it's doing the work, but possibly writing each JAR to the same output file for each flavor?
Related
I have an android project where a library is included (aar). Gradle will unpack and store the files in the gradle cache (.gradle\caches\transforms-2). I need a gradle script where I can retrieve a file from this cache and move it into my project. Does anybody know how this can be done?
The moving part is working. I just need to get the correct path
tasks.register('moveFile', Copy) {
from "${path_to_build_cache}/path/to/folder/file.conf"
into "${project.rootDir.path}/path/to/folder/"
}
EDIT
I have now tried the solution posted by #tim_yates. The problem is that I am now receiving the error CustomMessageMissingMethodException: Could not find method from() for arguments [ZIP 'C:\Users\nthemmer\.gradle\caches\modules-2\files-2.1\path\to\aar on task ':my-project:movefiles' of type org.gradle.api.DefaultTask It seems that the aar file is read correctly but it has not only one file.
Instead of searching the cache, you should be able to use Gradle to find it for you...
Here's an example:
configurations {
aar
}
dependencies {
aar('com.mikhaellopez:circularimageview:4.3.0#aar')
}
tasks.register('moveFile', Copy) {
from(zipTree(configurations.aar.singleFile)) {
include "res/values/values.xml"
}
into project.layout.buildDirectory.dir('here')
}
That copies the file into ./build/here/res/values/values.xml
Edit
So there's probably multiple ways of doing this, but here's one.
Define a configuration that we will use for the single dependency you want a file from, and make compileClasspath extend from it (so the dependency ends up back in the compile classpath where it was previously)
configurations {
aar
compileClasspath.extendsFrom aar
}
Then in the dependencies where you reference the aar, you should be able to use aar instead of compileClasspath
dependencies {
aar('com.mikhaellopez:circularimageview:4.3.0#aar')
}
Then you can use the moveFile task from above, and there will just be a single file
Not 100% sure what you have currently, so not sure how this fits in, but it should give you a good direction.
Here's the full build file which runs on Gradle 7.2 and uses the circularimageview arr off maven central as the test subject
plugins {
id('java')
}
repositories {
mavenCentral()
}
configurations {
aar
compileClasspath.extendsFrom aar
}
dependencies {
aar('com.mikhaellopez:circularimageview:4.3.0#aar')
}
tasks.register('moveFile', Copy) {
from(zipTree(configurations.aar.singleFile)) {
include "res/values/values.xml"
}
into project.layout.buildDirectory.dir('here')
}
I'm trying to publish a Kotlin Multiplatform library including sources using the maven-publish plugin. This is generally working but when I want to have a look at the code of the dependency inside my Android Studio project I can't see the sources but instead will see decompiled Kotlin code.
I tried several approaches of adding a sources.jar to the generated aar file already. Right now I'm following this approach: https://stackoverflow.com/a/28704799.
But I still can't see the sources(gradle clean cache and restart included).
Library build.gradle:
apply plugin: 'maven-publish'
task sourceJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier "sources"
}
publishing {
publications {
bar(MavenPublication) {
groupId 'com.foo'
artifactId 'bar'
version '0.1'
artifact(sourceJar)
artifact("$buildDir/outputs/aar/bar-release.aar")
}
}
repositories {
maven {
url "$buildDir/repo"
}
}
}
I also tried using different sourceSets like specifying one by name since the content of android.sourceSets.main.java.srcDirs is leading to a path where no sources are inside.
The library is included in the app project like this:
dependencies {
implementation 'com.foo:bar:0.1'
}
Looking at the local maven repository I can even see the bar-release-sources.jar but it seems like it's not part of the aar?
How would one create an Android Studio (Gradle) multi-project configuration such that projB depends on project(':projA') if projA is defined, but uses a file in libs/ otherwise?
Since it may be asked, in this case projA is an SDK; projB is a test application designed to demonstrate the SDK. If the SDK team gets a bug report, it often includes reproduction steps using projB.
When projB team does work, they do so on RC builds of projA, whereas the SDK team uses projB, with a dependency on project(':projA') so that a debug session can be run.
projB has no specific definition of its dependency on projA; that team takes the projA output from the build server and drops it in the libs/ folder, and has a wildcard dependency.
EDIT
I finally went with this code in the dependencies closure, and it works like a charm:
def sdkRef
project.getRootProject().allprojects.each { proj ->
if (proj.name.equals("Sdk")) {
sdkRef = proj;
return true;
}
}
if (sdkRef) {
println "SDK present in project; using project reference as dependency"
compile sdkRef
} else {
println "SDK is not present in project; using libs/"
}
I wonder if that's something you can do with flavors and build variants.
Through code you might try in your build file :
dependencies {
if (project.getRootProject().findProject(":projectA")) {
compile project(":projectA")
} else {
compile files("libs/projectA.jar")
}
}
One thing you have to consider is that your settings.gradle defines what modules are included in your project. So your two teams might end up with different files anyway for the project.
You can achieve that with productFlavors.
You just have to define:
2 product flavors in projB/build.gradle
a specific dependency for each flavor
android {
productFlavors {
demo{}
sdkdev{}
}
...
}
dependencies{
demoCompile files("libs/projectA.jar")
sdkdevCompile project(":projectA")
...
}
The build will produce 2 apks.
In Android studio, someone from the demo team can run the demo flavor by selecting the "demoDebug" (or "demoRelease") variant (in Build Variant tab) and someone from sdk team will select the "sdkdevDebug" variant.
The gradle.settings must contains references for projA and projB, but a user from demo team will never have to compile projA because the demo flavor have no dependencies on it.
How can we refer an archive file ( aar for android or jar in general) from the build.gradle.
eg : compile files('https://--artifactoryonline.com-url--Debug.aar')
Background/Root Cause :
Android Library project has multiple flavors to be supported.
productFlavors {
flavor1 {
// Add any falvor1 flavor specific details here
}
flavor2 {
// Add any flavor2 specific details here
}
}
While the flavors are working fine and aar is generated and uploaded to the articfactory, reference from the maven is not getting resolved as POM files are not generated for library flavors. which in turn leads to maven-metadata.xml not being updated.
This seems to be an issue - Reference :How to upload multiple android archives (one for each flavor)
So to circumvent the situation wanted to get the archive files as they are generated correctly.
Any help would be greatly appreciated!
So I've been playing in Gradle and Android Studio now since the early days of their inception. However, I find myself banging my head on the walls far more times then it is worth at times :(.
I have spent a day and a half trying to resolve my current dilemma.
Where I work we use a lot of shared library projects. This means that unlike Gradle's native assumption, my projects are NOT all nested under one parent project. (BUT this is NOT my question) I have gotten this working.
After our projects have become complete and ready to go, it has been asked to create an SDK for the current project infrastructure for external use. Now in the old IntelliJ I would simply generate some JavaDocs and create an Artifact that includes all dependencies, then another on that does not include the dependency jars and name them respectfully.
However, in Gradle this appears to be very difficult, maybe even unsupported, and I can't find anyone else that has done it after more then 10 hours of Googling and trial and error, I finally decided to make a demo project to show exactly what I'm doing and what I'm trying to accomplish.
I need to do a few things.
Generate a Jar file that includes ALL module dependency code and dependent Jar files
Generate a Jar file that includes ALL module dependency code and ZERO dependent Jar files
Generate an AAR file that includes All module dependency code and dependent Jar files as well as resources for launching our Activity if they want to use it.
Generate an AAR file that includes All module dependency code and ZERO Jar files as well as resources for launching our Activity if they want to use it.
So there in lies my problem. Each module when running a Task with (type: Jar) simply generates it's own code. I'm managed to get the dependent Jar files to compile in once, but then there is no regular source code, but I have NEVER been able to get a Module's source code to be included in the Jar file which is my biggest hurdle right now.
Here is a list of Tasks that I have tried without accomplishing this simple task yet.
evaluationDependsOn(':dependencyModule')
task myJar(type: Jar){
appendix = 'myJar'
from android.sourceSets.main.allSource.files
}
task uberJar (type: Jar){
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
}
task fatJar(type: Jar, dependsOn: 'compileJava') {
from {
sourceSets.main.output.classesDir
}
// Add all dependencies except for android.jar to the fat jar
from {
configurations.compile.findAll {
it.getName() != 'android.jar'
}.collect {
it.isDirectory() ? it : zipTree(it)
}
}
archiveName = 'fatJar.jar'
}
task jarFat(type: Jar) {
appendix = "fat"
from android.sourceSets.main.java
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.allSource
classifier = 'sources'
}
task clearJar(type: Delete) {
delete 'build/libs/myCompiledLibrary.jar'
}
task makeJar(type: Copy) {
from('build/bundles/release/')
into('build/libs/')
include('classes.jar')
rename ('classes.jar', 'myCompiledLibrary.jar')
}
makeJar.dependsOn(clearJar, build)
task jar(type: Jar) {
from android.sourceSets.main.allJava
}
task deployJar(type: Jar, dependsOn: jar) {
baseName = project.name + '-deploy'
deps = configurations.runtime + configurations.archives.allArtifactFiles
depClasses = { deps.collect { it.isDirectory() ? it : zipTree(it) } }
from(depClasses) {
exclude 'META-INF/MANIFEST.MF'
}
}
task modelJar(type: Jar) {
from sourceSets.main.output
}
task jarWithDependency(type: Jar){
from android.sourceSets.main.classesDir
from {configurations.compile.collect {zipTree(it)}}
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.allJava
}
Nothing has quite done the job yet. Any help would be greatly appreciated!!
Thanks in advance for taking the time to look at this.
I have the full functioning Sample Project if anyone would like it, but I don't see an option to upload here so is the link to the Demo project I built. It is very small and very easy to follow. One class or method basically per Project.
Demo Project
Using the application plugin, you can just call "distZip" to get a zip with all the libraries. By default you get a bin directory with a batch file and a shell script to run the program and a lib directory with all the jar files.
You probably want to update the Manifest to include all the necessary libraries like the following (I have additional stuff in mine).
EDIT I removed the references to "project" because it should not be necessary in this instance.
jar.doFirst
{
// aggregate all the jars needed at runtime into a local variable (array)
def manifestClasspath = configurations.runtime.collect { it.name }
// remove duplicate jar names, and join the array into a single string
manifestClasspath = manifestClasspath.unique().join(" ")
// set manifest attributes - mainClassName must be set before it is used here
manifest.attributes.put("Main-Class", mainClassName)
manifest.attributes.put("Class-Path", manifestClasspath)
}
I'm not an Android developer, so you would have to add some additional code for the AAR stuff.
Update from Author.
Ok as time has gone on, I have learned that the best practice is to allow dependency management tools to do their job and not try to package these files as such.
You can create fat jars and fat aar files, there are plugins to help, but it is hacky and doesn't allow the user to exclude transitives properly if they are nested in the compiled product. the exclude is intended for maven server pom files to include or exclude dependency files.
So using a Maven Repo Server is the best way to manage this instead as the FAT compiler plugins are unreliable and break with each gradle update and more importantly limit your user's ability to exclude "some and not all" when it comes to transitives.
So stay away from doing this if you can avoid it and do it the right way I recommend it. I'll leave this post up in case anyone else was heading down this bad path as well and hope you move to the right path of dependency management servers with transitive dependency managed by pom files.