We develop an Android library that has two flavors called "armv7" and "x86", each one of them is dependent on platform-specific native (NDK) libraries, and they're bundled separately.
We also have a test Android application that uses the library project mentioned before as dependency, also built as two different flavors. This app is also dependent on the Android compatibility library (appcompat).
The issue is when I try to build only one flavor of the app, it still builds both flavors of the library (see examples below), which makes compilation in Android studio very long (our NDK phase is pretty lengthy), and blocks us from separating the builds of the flavors in our Deployment jobs in Jenkins.
To demonstrate the issue, I've created a demo project you can see here: https://bitbucket.org/siltus/gradlebug/src
Basically what you have is skeletons of android app and android library, both have two flavors: armv7 and x86, and the app is also dependent on the appcompat.
App build.gralde
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "com.test.test1"
minSdkVersion 19
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
debug {}
release {}
}
productFlavors {
armv7 {}
x86 {}
}
}
dependencies {
armv7Compile 'com.android.support:appcompat-v7:20.0.0'
armv7Compile project(path: ':lib', configuration: 'armv7Debug')
x86Compile 'com.android.support:appcompat-v7:20.0.0'
x86Compile project(path: ':lib', configuration: 'x86Debug')
}
Library build.gradle
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
minSdkVersion 19
targetSdkVersion 25
versionCode 1
versionName "1.0"
defaultPublishConfig "debug"
publishNonDefault true
}
buildTypes {
debug {}
release {}
}
productFlavors {
armv7 {}
x86 {}
}
}
When I run this command (note I ran it as dry-run, but you can skip the -m flag) :
$ ./gradlew -m assembleArmv7Debug
This is the output I get (note the tasks for lib that has X86 in their name):
:app:preBuild SKIPPED
:app:preArmv7DebugBuild SKIPPED
:app:checkArmv7DebugManifest SKIPPED
:app:preArmv7ReleaseBuild SKIPPED
:app:preX86DebugBuild SKIPPED
:app:preX86ReleaseBuild SKIPPED
:lib:preBuild SKIPPED
:lib:preArmv7DebugBuild SKIPPED
:lib:checkArmv7DebugManifest SKIPPED
:lib:prepareArmv7DebugDependencies SKIPPED
:lib:compileArmv7DebugAidl SKIPPED
:lib:compileArmv7DebugNdk SKIPPED
:lib:compileLint SKIPPED
:lib:copyArmv7DebugLint SKIPPED
:lib:mergeArmv7DebugShaders SKIPPED
:lib:compileArmv7DebugShaders SKIPPED
:lib:generateArmv7DebugAssets SKIPPED
:lib:mergeArmv7DebugAssets SKIPPED
:lib:mergeArmv7DebugProguardFiles SKIPPED
:lib:packageArmv7DebugRenderscript SKIPPED
:lib:compileArmv7DebugRenderscript SKIPPED
:lib:generateArmv7DebugResValues SKIPPED
:lib:generateArmv7DebugResources SKIPPED
:lib:packageArmv7DebugResources SKIPPED
:lib:processArmv7DebugManifest SKIPPED
:lib:generateArmv7DebugBuildConfig SKIPPED
:lib:processArmv7DebugResources SKIPPED
:lib:generateArmv7DebugSources SKIPPED
:lib:incrementalArmv7DebugJavaCompilationSafeguard SKIPPED
:lib:compileArmv7DebugJavaWithJavac SKIPPED
:lib:processArmv7DebugJavaRes SKIPPED
:lib:transformResourcesWithMergeJavaResForArmv7Debug SKIPPED
:lib:transformClassesAndResourcesWithSyncLibJarsForArmv7Debug SKIPPED
:lib:mergeArmv7DebugJniLibFolders SKIPPED
:lib:transformNative_libsWithMergeJniLibsForArmv7Debug SKIPPED
:lib:transformNative_libsWithSyncJniLibsForArmv7Debug SKIPPED
:lib:bundleArmv7Debug SKIPPED
:lib:preX86DebugBuild SKIPPED
:lib:checkX86DebugManifest SKIPPED
:lib:prepareX86DebugDependencies SKIPPED
:lib:compileX86DebugAidl SKIPPED
:lib:compileX86DebugNdk SKIPPED
:lib:copyX86DebugLint SKIPPED
:lib:mergeX86DebugShaders SKIPPED
:lib:compileX86DebugShaders SKIPPED
:lib:generateX86DebugAssets SKIPPED
:lib:mergeX86DebugAssets SKIPPED
:lib:mergeX86DebugProguardFiles SKIPPED
:lib:packageX86DebugRenderscript SKIPPED
:lib:compileX86DebugRenderscript SKIPPED
:lib:generateX86DebugResValues SKIPPED
:lib:generateX86DebugResources SKIPPED
:lib:packageX86DebugResources SKIPPED
:lib:processX86DebugManifest SKIPPED
:lib:generateX86DebugBuildConfig SKIPPED
:lib:processX86DebugResources SKIPPED
:lib:generateX86DebugSources SKIPPED
:lib:incrementalX86DebugJavaCompilationSafeguard SKIPPED
:lib:compileX86DebugJavaWithJavac SKIPPED
:lib:processX86DebugJavaRes SKIPPED
:lib:transformResourcesWithMergeJavaResForX86Debug SKIPPED
:lib:transformClassesAndResourcesWithSyncLibJarsForX86Debug SKIPPED
:lib:mergeX86DebugJniLibFolders SKIPPED
:lib:transformNative_libsWithMergeJniLibsForX86Debug SKIPPED
:lib:transformNative_libsWithSyncJniLibsForX86Debug SKIPPED
:lib:bundleX86Debug SKIPPED
:app:prepareComAndroidSupportAppcompatV72000Library SKIPPED
:app:prepareComAndroidSupportSupportV42000Library SKIPPED
:app:prepareGradlebugLibUnspecifiedArmv7DebugLibrary SKIPPED
:app:prepareArmv7DebugDependencies SKIPPED
:app:compileArmv7DebugAidl SKIPPED
:app:compileArmv7DebugRenderscript SKIPPED
:app:generateArmv7DebugBuildConfig SKIPPED
:app:generateArmv7DebugResValues SKIPPED
:app:generateArmv7DebugResources SKIPPED
:app:mergeArmv7DebugResources SKIPPED
:app:processArmv7DebugManifest SKIPPED
:app:processArmv7DebugResources SKIPPED
:app:generateArmv7DebugSources SKIPPED
:app:incrementalArmv7DebugJavaCompilationSafeguard SKIPPED
:app:compileArmv7DebugJavaWithJavac SKIPPED
:app:compileArmv7DebugNdk SKIPPED
:app:compileArmv7DebugSources SKIPPED
:app:mergeArmv7DebugShaders SKIPPED
:app:compileArmv7DebugShaders SKIPPED
:app:generateArmv7DebugAssets SKIPPED
:app:mergeArmv7DebugAssets SKIPPED
:app:transformClassesWithDexForArmv7Debug SKIPPED
:app:mergeArmv7DebugJniLibFolders SKIPPED
:app:transformNative_libsWithMergeJniLibsForArmv7Debug SKIPPED
:app:processArmv7DebugJavaRes SKIPPED
:app:transformResourcesWithMergeJavaResForArmv7Debug SKIPPED
:app:validateSigningArmv7Debug SKIPPED
:app:packageArmv7Debug SKIPPED
:app:assembleArmv7Debug SKIPPED
:lib:compileArmv7DebugSources SKIPPED
:lib:assembleArmv7Debug SKIPPED
BUILD SUCCESSFUL
Total time: 1.631 secs
To have a clear understanding of who adds the dependencies of x86 flavor tasks, I used Visteg gradle plugin to generate a DOT file, which I rendered to PNG using this command:
$ dot -T png build/reports/visteg.dot -o visteg.dot.png
And this is what I've got:
If you look closely you'll see that the task ":lib:bundleX86Debug" that effectively triggers all other X86 build tasks is a dependency of ":app:prepeareComAndroidSupportAppcompatV72000Library" task.
While not understanding what is the connection between these two unrelated dependencies (as the library does NOT depend on Appcompat).
Things I've tried to change, and it didn't matter:
- Gradle android plugin versions + gradle versions
- Build tools versions
- Android SDK target/compile/minimum settings
- Use other maven library dependencies other than Appcompat dependency
The only thing that did work is when I used two different versions for appcompat. For example:
armv7Compile 'com.android.support:appcompat-v7:20.0.0'
armv7Compile project(path: ':lib', configuration: 'armv7Debug')
x86Compile 'com.android.support:appcompat-v7:21.0.0'
x86Compile project(path: ':lib', configuration: 'x86Debug')
When I did that, this was the output of the same gradle command from before:
:app:preBuild SKIPPED
:app:preArmv7DebugBuild SKIPPED
:app:checkArmv7DebugManifest SKIPPED
:app:preArmv7ReleaseBuild SKIPPED
:lib:preBuild SKIPPED
:lib:preArmv7DebugBuild SKIPPED
:lib:checkArmv7DebugManifest SKIPPED
:lib:prepareArmv7DebugDependencies SKIPPED
:lib:compileArmv7DebugAidl SKIPPED
:lib:compileArmv7DebugNdk SKIPPED
:lib:compileLint SKIPPED
:lib:copyArmv7DebugLint SKIPPED
:lib:mergeArmv7DebugShaders SKIPPED
:lib:compileArmv7DebugShaders SKIPPED
:lib:generateArmv7DebugAssets SKIPPED
:lib:mergeArmv7DebugAssets SKIPPED
:lib:mergeArmv7DebugProguardFiles SKIPPED
:lib:packageArmv7DebugRenderscript SKIPPED
:lib:compileArmv7DebugRenderscript SKIPPED
:lib:generateArmv7DebugResValues SKIPPED
:lib:generateArmv7DebugResources SKIPPED
:lib:packageArmv7DebugResources SKIPPED
:lib:processArmv7DebugManifest SKIPPED
:lib:generateArmv7DebugBuildConfig SKIPPED
:lib:processArmv7DebugResources SKIPPED
:lib:generateArmv7DebugSources SKIPPED
:lib:incrementalArmv7DebugJavaCompilationSafeguard SKIPPED
:lib:compileArmv7DebugJavaWithJavac SKIPPED
:lib:processArmv7DebugJavaRes SKIPPED
:lib:transformResourcesWithMergeJavaResForArmv7Debug SKIPPED
:lib:transformClassesAndResourcesWithSyncLibJarsForArmv7Debug SKIPPED
:lib:mergeArmv7DebugJniLibFolders SKIPPED
:lib:transformNative_libsWithMergeJniLibsForArmv7Debug SKIPPED
:lib:transformNative_libsWithSyncJniLibsForArmv7Debug SKIPPED
:lib:bundleArmv7Debug SKIPPED
:app:prepareComAndroidSupportAppcompatV72000Library SKIPPED
:app:prepareComAndroidSupportSupportV42000Library SKIPPED
:app:prepareGradlebugLibUnspecifiedArmv7DebugLibrary SKIPPED
:app:prepareArmv7DebugDependencies SKIPPED
:app:compileArmv7DebugAidl SKIPPED
:app:compileArmv7DebugRenderscript SKIPPED
:app:generateArmv7DebugBuildConfig SKIPPED
:app:generateArmv7DebugResValues SKIPPED
:app:generateArmv7DebugResources SKIPPED
:app:mergeArmv7DebugResources SKIPPED
:app:processArmv7DebugManifest SKIPPED
:app:processArmv7DebugResources SKIPPED
:app:generateArmv7DebugSources SKIPPED
:app:incrementalArmv7DebugJavaCompilationSafeguard SKIPPED
:app:compileArmv7DebugJavaWithJavac SKIPPED
:app:compileArmv7DebugNdk SKIPPED
:app:compileArmv7DebugSources SKIPPED
:app:mergeArmv7DebugShaders SKIPPED
:app:compileArmv7DebugShaders SKIPPED
:app:generateArmv7DebugAssets SKIPPED
:app:mergeArmv7DebugAssets SKIPPED
:app:transformClassesWithDexForArmv7Debug SKIPPED
:app:mergeArmv7DebugJniLibFolders SKIPPED
:app:transformNative_libsWithMergeJniLibsForArmv7Debug SKIPPED
:app:processArmv7DebugJavaRes SKIPPED
:app:transformResourcesWithMergeJavaResForArmv7Debug SKIPPED
:app:validateSigningArmv7Debug SKIPPED
:app:packageArmv7Debug SKIPPED
:app:assembleArmv7Debug SKIPPED
:lib:compileArmv7DebugSources SKIPPED
:lib:assembleArmv7Debug SKIPPED
BUILD SUCCESSFUL
Total time: 1.199 secs
And the dependency graph is much simpler without any X86 build tasks:
So my hunch is that Gradle prepares tasks for every dependency, but when it sees the same dependency (library AND version) for two separate flavors, it merges the tasks so effectively it will run both flavors.
I couldn't find any documentation around that or already existing bug ticket, and one step before opening a bug ticket, I thought maybe someone that reads this will have an idea how can I override this behavior, or will point out what's wrong with my configuration.
Thanks!

