Gradle: Only resolve dependencies for the desired variant - android

I have a test app with three flavors:
dev: Uses a local copy of the library during development
qa: Uses a snapshot during QA
rc: Uses a prerelease build for release candidate testing.
dependencies {
devCompile project(':library')
qaCompile 'com.example:library:1.0.0-SNAPSHOT#aar'
rcCompile 'com.example:library:1.0.0#aar'
}
I run Gradle, and expect it to do the minimum amount of work necessary to build just what I want:
./gradlew :test-app:connectedAndroidTestDevDebug
However, the build fails, because it is trying to resolve dependencies for all build flavors, not just the one I am building.
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring project ':test-app'.
Could not resolve all dependencies for configuration ':test-app:_qaDebugCompile'.
Could not find com.example:library1.0.0-SNAPSHOT.
Searched in the following locations:
https://repo1.maven.org/maven2/com/example/library/1.0.0-SNAPSHOT/maven-metadata.xml
https://repo1.maven.org/maven2/com/example/library/1.0.0-SNAPSHOT/library-1.0.0-SNAPSHOT.pom
https://repo1.maven.org/maven2/com/example/library/1.0.0-SNAPSHOT/library-1.0.0-SNAPSHOT.aar
http://oss.sonatype.org/content/repositories/snapshots/com/example/library/1.0.0-SNAPSHOT/maven-metadata.xml
http://oss.sonatype.org/content/repositories/snapshots/com/example/library/1.0.0-SNAPSHOT/library-1.0.0-SNAPSHOT.pom
http://oss.sonatype.org/content/repositories/snapshots/com/example/library/1.0.0-SNAPSHOT/library-1.0.0-SNAPSHOT.aar
file:/opt/android-sdk-macosx/extras/android/m2repository/com/example/library/1.0.0-SNAPSHOT/maven-metadata.xml
file:/opt/android-sdk-macosx/extras/android/m2repository/com/example/library/1.0.0-SNAPSHOT/library-1.0.0-SNAPSHOT.pom
file:/opt/android-sdk-macosx/extras/android/m2repository/com/example/library/1.0.0-SNAPSHOT/library-1.0.0-SNAPSHOT.aar
file:/opt/android-sdk-macosx/extras/google/m2repository/com/example/library/1.0.0-SNAPSHOT/maven-metadata.xml
file:/opt/android-sdk-macosx/extras/google/m2repository/com/example/library/1.0.0-SNAPSHOT/library-1.0.0-SNAPSHOT.pom
file:/opt/android-sdk-macosx/extras/google/m2repository/com/example/library/1.0.0-SNAPSHOT/library-1.0.0-SNAPSHOT.aar
Required by:
project-name:test-app:unspecified
The SNAPSHOT that the qa flavor is trying to resolve doesn't exist yet, and that should be fine, because I'm not trying to build the qa flavor. If that SNAPSHOT build is present, then everything works fine.
Questions:
Why are all build flavors having their dependencies resolved?
How can I accomplish building just one flavor without encountering this problem?
Is there some better way to do this that will be more "Gradley"?

I assume that the build works if you exclude the task manually?
gradle connectedAndroidTestDevDebug -x _qaDebugCompile
It looks like the task connectedAndroidTestDevDebug has a dependency on the task _qaDebugCompile, which causes your problem. I don't know how your tasks are defined, but you can study your dependencies using Gradle's built-in tasks gradle dependencies and gradle dependencyInsight. Maybe that will give you a hint in the right direction:
gradle dependencyInsight --dependency com.example:library:1.0.0-SNAPSHOT
You can read more about task dependencies in the gradle User's guide.

I finally solved this by checking the list of tasks and if there is a task for the desired build variant, adding the dependency.
dependencies {
gradle.startParameter.taskRequests.each { taskRequest ->
taskRequest.args.each { taskName ->
String flavorName = "qa";
if (taskName.toLowerCase().endsWith(flavorName+"debug") ||
taskName.toLowerCase().endsWith(flavorName+"release")) {
qaCompile 'com.example:my-library:1.0.0-SNAPSHOT#aar'
}
}
}
devCompile project(':localLibrary')
//qaCompile 'com.example:my-library:1.0.0-SNAPSHOT#aar' // What I used to do.
rcCompile 'com.example:my-library:1.0.0#aar'
}
Keep in mind that the task list will not contain dependent task names, so if you aren't invoking a task that contains your build flavor name, it won't work. This is just something I got working.

Related

How to make android-app-module identify and use gradle-plugin-module sources

I've created a standalone plugin along with a simple demo app, that I need for development. Both are added to the same project as an app-module and a plugin-module, so that I can easily develop and test features I write in the plugin. I'm assuming that if I add the sources from the plugin to buildSrc/build.gradle.kts, I'll be able to reference the plugin sources from the app (I need that to build and apply the plugin). The demo project in its entirety can be found here: https://github.com/oizo/gradle-plugin-sample.
Currently, it seems that when I apply the plugin in app/build.gradle.kts it's available (autocomplete is working) but when I try to build with ./gradlew build it fails with this message:
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/MyUser/github/gradle-plugin-sample/app/build.gradle.kts' line: 10
* What went wrong:
Script compilation error:
Line 10: apply<io.hvam.android.plugin.StringPlugin>()
^ Unresolved reference: io
Clearly, I'm missing something.
I've tried the solution proposed in this post-https://stackoverflow.com/a/42263532/1181023, which seems to be a similar problem, but when I run ./gradlew build --include-build plugin/ it also fails to build, with the following:
FAILURE: Build failed with an exception.
* What went wrong:
Included build in /Users/MyUser/github/gradle-plugin-sample/plugin has a root project whose name 'plugin' is the same as a project of the main build.
I see two issues:
Your plugin must be packaged in such a way that your Plugin implementing class is visible to consumers of the plugin as the entry point. One easy way of doing that is by using gradlePlugin {} block. I would recommend to upgrade to at least Gradle 5.5.1 and use build init plugin to automate all these for you.
Please try gradle init --type kotlin-gradle-plugin to generate the plugin project. This would add code similar to below in your plugin build.gradle which ensures your consumers can apply the plugin using apply plugin: "stringPlugin".
gradlePlugin {
plugins {
stringPlugin {
id = "stringPlugin"
implementationClass = "com.example.StringPlugin"
}
}
}
Secondly, your plugin must be visible to the buildscript classpath so that it can apply your plugin. If you have already published the plugin to maven, then you can add the below in project level build.gradle to let gradle discover your plugin.
buildscript {
...
dependencies {
classpath "com.example:stringplugin"
}
}
Publishing to maven might not be convenient during development. You could utilize Gradle's composite builds to help in this regard. In your project level settings.gradle, add below:
includeBuild('./plugin') {
dependencySubstitution {
substitute module('com.example:stringplugin') with project(':plugin')
}
}
This instructs gradle to use locally present :plugin module when com.example:stringplugin is requested. This would be convenient for development.
It seems I misunderstood how gradle uses the buildSrc folder. I've moved the content of buildscript into buildSrc/build.gradle.kts but omitting the actual buildscript extension. This somehow fixed the issue of the app-module not being able to recognize the plugin sources when building. The specific changes related to the fix can be reviewed int this commit. And the sample code in the repo now builds as expected.
This gives some more clarity about the purpose of the buildSrc folder: https://stackoverflow.com/a/13875350/1181023

Android Gradle Build: duplicate entry: META-INF/app_release.kotlin_module

I'm trying to build a release app via Android Studio > Generate Signed Bundle or APK > Android App Bundle > Release
However gradle fails with
: > Task :core:transformClassesWithMergeClassesForRelease FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':core:transformClassesWithMergeClassesForRelease'.
> 1 exception was raised by workers:
java.util.zip.ZipException: duplicate entry: META-INF/app_release.kotlin_module
In my build.gradle I've tried adding:
packagingOptions {
exclude 'META-INF/app_release.kotlin_module'
}
But it isn't making any difference whatsoever.
How do I fix this?
For extra context, it's a multi module project.
I have a core module, and an installed module which is declared in the core build.gradle with dynamicFeatures = [":installed"]
Thanks
Please make sure all your dependencies are api or implementation,
I have the flowing dependency grap.
meemo_sdk:
api project(":gvoice")
app project:
implementation project(":gvoice")
implementation project("meemo_sdk")
It complains "META-INF/gvoice_debug.kotlin_module" collision.
After change api to implementation, it works!
So I figured it out.
I pressed shift twice in Android studio (to open up the search everywhere dialog) and searched for app_release.kotlin_module
I saw two files, that were under two of my dependencies (which funnily enough were libraries that I had created!)
I opened up these library projects, and in the build.gradle file I had to add:
ext {
PUBLISH_GROUP_ID = 'com.companyname'
PUBLISH_ARTIFACT_ID = 'packagename'
}
android {
...
compileOptions {
kotlinOptions.freeCompilerArgs += ['-module-name', "$PUBLISH_GROUP_ID.$PUBLISH_ARTIFACT_ID"]
}
}
Rebuilt the library projects with new versions, used these new versions in my other project, and it started compiling :)
Build -> Clean Project Worked for me
I found the same issue comes randomly by using Android Studio 4.2.1 and Java8, I sorted out successfully by deleting .gradle file in the project (not the main one) every time I get the error.
Java version: JDK8221
Kotlin: 1.5.0
AndroidStudio: 4.2.1
Not the best solution but is a good workaround for now.

Run a gradle task before resolving dependencies

I want to run a gradle task that fetches additional sources and sets them up before gradle tries to resolve dependencies.
In build.gradle there is a task that fetches a sub project's source code. The task needs to be run before Gradle tries to resolve dependencies, because the sub project is part of the dependencies. The task involves fetching sources from a remote repository and replacing a few build.gradle files to make the build possible.
What happens now is that:
I run the task.
Gradle tries to resolve dependencies before actually running the task.
It fails because one of the dependencies requires the sub project (the sources that my task is supposed to fetch).
Of course, resolving dependencies is part of the "Configuration" build phase, so it's pretty clear why the task is run after. The question is how to make it run before.
Of course I can make it work if I replace my gradle task with a separate bash script and run it manually before gradle does anything. However, that would mean that I duplicate some variables in the gradle and the bash scripts (like version names and git tag names). Those variables are used for other purposes in gradle, and having them in two places is bad. There are other reasons I want to avoid that, one of them being - using a bash script would mean that gradle fails at doing our build from start to finish...
Firstly you are incorrect that resolving dependencies is part of the "Configuration" phase. If you make use of the lazy evaluation of FileCollection then it will actually be resolved in the execution phase. A configuration will be resolved the first time that resolve() is invoked. Please see the javadoc for the methods which cause a Configuration to be resolved. AFAIK the core gradle code won't resolve a configuration in the "Configuration" phase but your custom code may cause this (I suggest you refactor if this is the case)
You can do something like this:
dependencies {
// this is lazy evaluated
compile fileTree(dir: "$buildDir/dynamicJars", include: "*.jar")
}
task getDynamicJars(type: Copy) {
from zipTree('path/to/somefile.zip')
into "$buildDir/dynamicJars"
}
compileJava.dependsOn getDynamicJars

Android: Skip Gradle "testClasses" task for a dependency project

I have followed this guide to create a JUnit test file for my main Android module (let's call it "module-a"), in Android Studio v1.4.
My "module-a" has a dependency on an external library that is provided as a .aar file and for which I had to create a dedicated module.
This dependency causes an error:
When right clicking the test Java file and hitting "Run MyTestName" , it fails with this error
Error:Gradle:
FAILURE: Build failed with an exception.
* What went wrong:
Task 'testClasses' not found in project ':module-b'.
Removing the dependency on module-b solves the problem.
Excerpt of module-a build.gradle:
compile project(':module-b')
module-b build.gradle:
configurations.create("default")
artifacts.add("default", file('library-b.aar'))
How should I configure Gradle so that it does not try to run the testClasses task on "module-b" ? (this should solve my issue)
I did not find a way to skip the testClasses task for module-b: it seems that actions started from Android Studio (like running a JUnit test) run Gradle commands that cannot be modified. In my case:
Information:Gradle: Executing tasks:
[:module-a:prepareFree_flavorDebugUnitTestDependencies,
:module-a:generateFree_flavorDebugSources,
:module-a:mockableAndroidJar,
:module-a:assembleFree_flavorDebug,
:module-a:assembleFree_flavorDebugUnitTest,
:module-b:testClasses]
I found a workaround for my problem, though:
Add the following code to module-b build.gradle:
task testClasses {
doLast {
println 'This is a dummy testClasses task'
}
}

Gradle clean task fails for not resolving all dependencies

When I ran ./gradlew clean for my Android project, the task failed for not resolving all dependencies. The complaints came from the gradle task _debugCompile.
I can verify some of the local dependencies didn't exist which failed the dependencies resolving process, but I'm still curious about why the clean task would check for dependencies while not building the project.
Shouldn't it just delete some directories as mentioned in the following link?
https://docs.gradle.org/current/userguide/java_plugin.html#sec:clean
Is there any gradle built-in task that could just clean the build folder without checking dependency?
Thanks!
Gradle already resolves dependencies during its configuration phase which precedes the execution phase, see The Build Lifecycle (it downloads dependencies only once they are needed, though). However, dependencies being out of date does not make resolution fail. If resolution fails that dependency is likely not available online anymore. If you still have the dependency in your Gradle cache you might be able to work around this by specifying the --offline switch:
$ ./gradlew --offline clean

Categories

Resources