I am experimenting with Android's Dynamic Feature Modules (a.k.a. on demand modules) on the side of the project I am working on.
In my PoC (proof-of-concept) project, Dynamic Feature Modules (DFM) work fine and gradle builds the project with no issue. After finishing with my PoC, I decided to apply it to an existing project. However, gradle failed building the project with:
Could not determine the dependencies of task ':my-app:checkSomeBuildFlavorDebugLibraries'.
> Could not resolve all task dependencies for configuration ':my-app:someBuildFlavorDebugMetadataValues'.
> Could not resolve project :features:myDynamicFeatureModule.
Required by:
project :my-app
> Cannot choose between the following variants of project :features:myDynamicFeatureModule:
- anotherBuildFlavorDebugAndroidTestCompile
- anotherBuildFlavorDebugAndroidTestRuntime
- ...
All of them match the consumer attributes:
- Variant 'anotherBuildFlavorDebugAndroidTestCompile' capability myproject.features:myDynamicFeatureModule:unspecified:
- Unmatched attributes:
- Required com.android.build.api.attributes.BuildTypeAttr 'debug' but no value provided.
- Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' but no value provided.
- Required endpoint 'someBuildFlavor' but no value provided.
- Found org.jetbrains.kotlin.localToProject 'local to :features:myDynamicFeatureModule' but wasn't required.
- Found org.jetbrains.kotlin.platform.type 'androidJvm' but wasn't required.
- Variant ...
Note that this error is quite long, iterating through all possible flavors.
What I have tried, but did not work:
I added exactly the same flavors that base module (my-app) has in my dynamic module
In build.gradle of my DFM, I tried to set configuration explicitly like : implementation project(path: ':my-app', configuration: 'default') and also configuration: 'someFlavor'
In build.gradle of the base module (my-app) I used missingDimensionStrategy 'mydimension', 'myflavor'
Building from Android Studio and also from terminal
Removing dependencies and other code from build.gradle files
What I have tried and it worked:
Removing the dependency of base app (my-app) from DFM (i.e. removing implementation project(':my-app',) ) - but I need it. Just to clarify, the DFM applies the following gradle plugins: com.android.dynamic-feature and kotlin-android
Removing all flavors from the base app and DFM - but I need them in the base app.
The PoC I made works fine and gradle compiles the project even with the same flavors I use in my actual project
Notes:
I use DexGuard (but I disabled it to make sure it isn't interfering)
I don't have any custom build types, only 'debug' and 'release'
Gradle version is 3.5.2
Gradle wrapper is 5.5.1
Both the PoC and my actual project use the same versions of gradle, gradle wrapper, build types and flavors
Unfortunately I could not reproduce the problem in a separate project. I tried removing as much of things as possible from my project but I could not resolve the issue.
Do you have any idea what could be causing this issue and how could I possibly fix it? Thank you!
I had this issue and it was due to not removing the implementation project(':features:myDynamicFeatureModule') line from my app module's gradle file.
Related
We've an Android project with many modules, including dynamic feature modules. The build script boilerplate is currently handled by using script plugins (Gradle files that are applied by using apply from: 'something.gradle').
I am trying to migrate the script plugins to become build convention plugins, either buildSrc/ or an included build (includeBuild 'build-conventions') so that it can be applied by using plugin DSL (plugins { id 'mybuild.something-convention' }). However it fails when running ./gradlew bundleDebug saying that the base module and one of the dynamic feature modules both contain the same entry (struct.proto) with different content.
* What went wrong:
Execution failed for task ':app:packageDebugBundle'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.PackageBundleTask$BundleToolWorkAction
> Modules 'base' and '‹dynamicfeature1›' contain entry 'root/google/protobuf/struct.proto' with different content.
Please help me. I am trying to figure out and so far get these clues:
./gradlew <module>:dependencies --configuration debugRuntimeClasspath shows that the base module and the dynamic feature module have different version of com.google.protobuf:protobuf-javalite dependency. This is also the case before migrating to build convention plugins, but the resulting AAB of the dynamic feature module did not contain struct.proto entry then, so that there was no duplicate entry. Illustration of the indirect graph:
# In both module:
com.google.firebase:firebase-bom:30.3.1
└> com.google.firebase:firebase-perf-ktx:20.1.0 (c)
└> com.google.firebase:firebase-firestore:24.2.1 (c)
# In base module:
com.google.firebase:firebase-perf-ktx:20.1.0
└> com.google.protobuf:protobuf-javalite:3.17.3
# In dynamic feature module:
com.google.firebase:firebase-perf-ktx:20.1.0
└> com.google.protobuf:protobuf-javalite:3.19.2
com.google.firebase:firebase-firestore:24.2.1
└> com.google.protobuf:protobuf-javalite:3.19.2
Adding com.google.protobuf:protobuf-javalite dependency to the base module (with the same version that the dynamic feature module resolved) solves the problem so far (bundleDebug succeeds) but it sounds like a hack instead of a real solution, because it makes me need to inspect the dependency that causes the conflict and what its resolved versions are.
The dynamic feature module has dependency to com.google.firebase:firebase-firestore that bumps com.google.protobuf:protobuf-javalite to 3.19.2.
Removing com.google.firebase:firebase-firestore dependency (and all the code that needs it) from the dynamic feature module makes bundleDebug succeed.
Trying to backtrack, found out that it happens as soon as I move com.android.tools.build:gradle:<version> from /build.gradle to /build-conventions/build.gradle.
Based on the last clue, it seems a bug of Android Gradle Plugin.
It happens in 7.3.0 and 7.3.1.
It doesn't happen in 7.2.0.
So the workaround is to downgrade com.android.tools.build:gradle to version 7.2.0.
Trying to find the bare minimum source and build files needed to build an android project in Android Studio. I want to publish to github and avoid uploading generated build files or binaries.
I do have a Android.gitignore from but I still see some more files getting pushed into the repo which may not be necessary. I understand the few obvious ones but about others, do I need them and if so kindly explain the usage.
So the question, do I need the following and if so then a short description of why?
root
build.gradle
gradle.properties
gradlew
gradlew.bat
settings.gradle
/app
app/build.gradle
app/proguard-rules.pro
/gradle (tested, android can re-download/generate following it if not present)
gradle/wrapper/gradle-wrapper.jar
gradle/wrapper/gradle-wrapper.properties
This question can have two different answers based on the meaning of the word needed.
First (the real one)
Assuming your project has currently those files, if your question is:
Should I commit these files on my Git repo?
The answer is yes, all of them, and I'm explaining why:
root
build.gradle -> defines the configuration for all the Gradle modules in your project (e.g. use the same remote repositories to download some Gradle plugins)
gradle.properties -> defines some optional flags used when building the app (e.g. enabling the incremental KAPT, enabling the AndroidX jetifier)
gradlew -> invokes the Gradle wrapper (which can be found under gradle/wrapper/gradle-wrapper.jar) to avoid to have Gradle installed when building your project on Darwin/Linux
gradlew.bat -> the same of gradlew but for Windows
settings.gradle -> defines the list of modules which are part of your project
app/
app/build.gradle -> defines the configuration only for your app module (e.g. its build types, its flavors, its version code and version name)
app/proguard-rules.pro -> defines the obfuscation rules when your app enables the minification
gradle/
gradle/wrapper/gradle-wrapper.jar -> provides the same version of the Gradle wrapper jar for all the users. This is very important because it forces the users to use the same version of the Gradle wrapper to compile your app
gradle/wrapper/gradle-wrapper.properties -> same as above, it defines which version of the Gradle wrapper you need
Second (the useless one)
Now, I'll give you the answer to the question:
Are these files strictly needed to compile an Android project?
To successfully compile an Android project with Gradle you just need the root build.gradle if you have Gradle installed on your machine or build.gradle + the wrapper files if you have not Gradle installed on your machine.
Theoretically you can:
put your application code in the root project and that avoids you one build.gradle and settings.gradle
disable the obfuscation and that avoids you proguard-rules.pro
remove gradle.properties and set the properties via command line
Obviously this solution won't happen on a real project scenario.
I'm currently working on migrating our project to the new gradle plugin (3.0.0) via the provided migration guide:
https://developer.android.com/studio/preview/features/new-android-plugin-migration.html
In our Android project we have a single library module and 2 app modules. The library module, as it stands, has no flavours and just the debug & release build types whereas the apps have multiple flavors and build types.
What I'm finding is that the buildTypes of the library module have to match those of the app modules exactly. For instance,
If the app module has a buildType called debugProguard, then the library module must also have a buildType called debugProguard. This means that in the library module I end up having to declare the buildTypes with no body:
buildTypes {
...
debugProguard {
}
...
}
(From here:
Android Studio 3.0 Error. Migrate dependency configurations for local modules)
Is there anyway to avoid this? It seems strange that the library needs some knowledge of the architecture of the consuming app.
What I'd like to do, ideally, is to tell the build system to use a certain buildType of the library for a buildType of the app. For instance, for buildType x, y, z in the app use 'debug' in the library, and for i, j, k use 'release'.
Thanks in advance
They've just addressed this in the Canary 7 release of Android Studio 3.0:
You can now specify an alternative build type the consumer should use
from the producer using the android.buildTypeMatching property as
shown below. The plugin uses the alternative build type only if a
matching build type is not found.
android {
…
// Let's say your app configures a 'staging' build type and a library module
// it depends on does not. You can use the property below to tell the Android plugin
// to use a library's 'debug' build type instead.
buildTypeMatching 'staging', 'debug'
}
I have small log library and it is published to jcenter. I need to have two versions of the library - debug and release. To do this I found flag publishNonDefault true and pushed new version of library.
Structure of files in repository before flag was set:
Structure of files in repository after flag was set:
And now
dependencies {
compile 'me.shikhov:wlog:1.3.1'
}
gives me error
Error:A problem occurred configuring project ':Project'.
Could not find wlog.jar (me.shikhov:wlog:1.3.1).
Searched in the following locations:
https://jcenter.bintray.com/me/shikhov/wlog/1.3.1/wlog-1.3.1.jar
I have found syntax for local dependency, for example:
debugCompile project(path: ':myLocalLibrary', configuration: 'debug')
releaseCompile project(path: ':myLocalLibrary', configuration: 'debug')
How to set remote library dependency?
The extra string after the version in the artifact name is the classifier.
The classifier allows to distinguish artifacts that were built from
the same POM but differ in their content. It is some optional and
arbitrary string that - if present - is appended to the artifact name
just after the version number. As a motivation for this element,
consider for example a project that offers an artifact targeting JRE
1.5 but at the same time also an artifact that still supports JRE 1.4. The first artifact could be equipped with the classifier jdk15 and the
second one with jdk14 such that clients can choose which one to use.
Another common use case for classifiers is the need to attach
secondary artifacts to the project's main artifact. If you browse the
Maven central repository, you will notice that the classifiers sources
and javadoc are used to deploy the project source code and API docs
along with the packaged class files.
From here.
Gradle dependency declaration takes the form:
[organisation]:[module]:[revision]:[classifier]#[ext]
so you should be consuming the dependency as:
compile 'me.shikhov:wlog:1.3.1:release#aar'
I'm working on my gradle build for an android product to get product flavors working.
I have following project structure:
at.mkw.inlocs.android - Library Project
at.mkw.inlocs.android.lib - Library Project - depends on at.mkw.inlocs.android
at.mkw.inlocs.android.login - App Project - depends on at.mkw.inlocs.android
at.mkw.inlocs.android.core - App Project - depends on at.mkw.inlocs.android.lib
at.mkw.inlocs.android.breeding - App Project - depends on at.mkw.inlocs.android.lib
at.mkw.inlocs.android.localization - App Project - depends on at.mkw.inlocs.android.lib
at.mkw.inlocs.android.health - App Project - depends on at.mkw.inlocs.android.lib
Since adding product flavors to all app and library projects, I am getting an exception when building.
I am using the newest gradle artifact (com.android.tools.build:gradle:0.9+) and android build tools 19.0.3
Another problem is, that Android Studio doesn't show the at.mkw.inlocs.android.lib module in project view.
After switching to packages view, the module is visible, but has no content.
I have pasted the build.gradle files:
root build script
at.mkw.inlocs.android
at.mkw.inlocs.android.lib
at.mkw.inlocs.android.login
at.mkw.inlocs.android.core
at.mkw.inlocs.android.localization
at.mkw.inlocs.android.health
at.mkw.inlocs.android.breeding
Looking at the build.gradle at.mkw.inlocs.android, you are using flavors in a library project.
While this is fine, current flavor support in library project is not final and by default the variants are not published (for consumption by other projects) due to some limitations.
To enable it you'll need to do
android {
publishNonDefault true
}
reference: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication