I have a multi-flavored, multi-build-typed android project and I want to integrate the NewRelic plugin. But I have to apply it only for one of the customers, thus only for one product flavor.
NewRelic uses instrumentation and the plugin would generate code in other flavors if I applied the plugin there, and that is not permitted for us.
So my question is: How can I use the apply plugin: something command in the gradle file to be applied to only one of my flavors?
Use this code:
if (!getGradle().getStartParameter().getTaskRequests()
.toString().contains("Develop")){
apply plugin: 'com.google.gms.google-services'
}
getGradle().getStartParameter().getTaskRequests().toString() returns something like [DefaultTaskExecutionRequest{args=[:app:generateDevelopDebugSources],projectPath='null'}] so as stated in the comments Develop must start with an uppercase.
Tried different solutions, but none of them worked for me. This is what I came up with and seems to work as far as I tested:
build.gradle
productFlavors {
someFlavorWithGoogleGcm {
dimension "type"
applicationId "com.example.withGcm"
ext.useGoogleGcm = true
}
someFlavorWithoutGoogleGcm {
dimension "type"
applicationId "com.example.withoutGcm"
}
}
And outside the configuration, inside the build.gradle file:
android.productFlavors.each { flavor ->
if (getGradle().getStartParameter().getTaskRequests().toString().toLowerCase().contains(flavor.name) && flavor.ext.useGoogleGcm) {
println("Building flavor with Google GCM [${flavor.name}] - applying plugin")
apply plugin: 'com.google.gms.google-services'
}
}
I simply used apply plugin: 'com.google.gms.google-services' inside the flavor in app level build.gradle and it works just fine.
productFlavors {
..... your other flavors ....
yourFlv {
....
....
apply plugin: 'com.google.gms.google-services'
}
}
No extra step needed.
Define variable - def fl
Initialize variable in you Flavours (and/or builds)
productFlavors {
freeFlavour {
(...)
fl = "free"
}
paidFlavour {
(...)
fl = "paid"
}
}
Use if statement -
if (fl == "free") {
apply plugin: something
}
I found a solution, but it is not the best so far.
So I'm not sure anymore, that what I wanted to do initially is possible.
The gradle file evaluation and the choosing of the right flavor and build type is in different phases of the gradle build, so what I've done is:
I use a build parameter from the command line. When that paramerer is true, I apply the plugin, when it is not even there, I also apply it (for IDE build).
I use Jenkins, so I could write that parameter in the build job.
build.gradle file:
// First we have to attach a task to the project, which will be running first
gradle.projectsEvaluated {
preBuild.dependsOn(applyNewRelicByProperty)
}
// Then check on the parameter, which comes from the command line
task applyNewRelicByProperty {
if(!project.hasProperty('compileNewRelic')) {
// NewRelic on: 'compileNewRelic' property not set, so running from IDE.
apply plugin: 'newrelic'
} else if(project.hasProperty('compileNewRelic') && project.getProperties().get('compileNewRelic').equals('true')) {
// NewRelic on: 'compileNewRelic' property is set and it is 'true'.
apply plugin: 'newrelic'
} else {
// NewRelic off
println("No NewRelic")
}
}
And you have to run the gradle build by this:
assembleYourApp -PcompileNewRelic=true
After upgrading to Gradle 4.2, the approach by Tarps stopped working, so I ended up combining it with Erik's answer.
Set ext.useGoogleGcm for each flavour in your build.gradle:
productFlavors {
a {
...
ext.useGoogleGcm = true
}
b {
...
ext.useGoogleGcm = false
}
}
Then at the bottom of the file:
apply plugin: 'com.google.gms.google-services'
afterEvaluate {
android.productFlavors.each { flavor ->
tasks.matching {
it.name.contains('GoogleServices') && it.name.contains(flavor.name.capitalize())
}*.enabled = flavor.ext.useGoogleGcm
}
}
In the output for your assembleRelease task, you should see tasks with "GoogleServices" in the name have run for those that are true and skipped for those that are false. If your build complains about toCapitalize, you could use toLowerCase on both it.name and flavor.name.
In the case of the Google Services Gradle plugin, the following works.
apply plugin: 'com.google.gms.google-services'
afterEvaluate {
tasks.matching { it.name.contains("GoogleServices") && !it.name.contains(yourFlavorName) }*.enabled = false
}
where yourFlavorName is a capitalised string containing the name of your flavor that must apply the plugin; all other flavors don't use the plugin.
Note that the plugin is still applied to other flavors; this solution just disables the *GoogleServices* task(s) for them. That assumes that the Google Services Gradle plugin only applies changes by adding a task containing substring "GoogleServices", which might not work in the future.
To check that this works, you can check the dependencies, e.g. like so:
./gradlew app:dependencyInsight --configuration yourFlavorNameDebugCompileClasspath --dependency google | grep -i services
That should show some dependencies for yourFlavorName but not for other flavor names:
./gradlew app:dependencyInsight --configuration otherFlavorNameDebugCompileClasspath --dependency google | grep -i services
Related
I have Google this problem, but the results are not work for me.
The detail as following.
public final class App extends com.zhixin.wedeep.common.BaseApplication implements androidx.lifecycle.LifecycleOwner {
^
// Expected #HiltAndroidApp to have a value. Did you forget to apply the Gradle Plugin?
The App code.
#HiltAndroidApp
class App : BaseApplication(), LifecycleOwner {
#Inject
lateinit var service: EventService
private val mLifecycleRegistry = LifecycleRegistry(this)
}
This module gradle file.
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-allopen'
apply plugin: 'androidx.navigation.safeargs.kotlin'
apply plugin: 'dagger.hilt.android.plugin'
dependencies {
implementation rootProject.ext.dependencies["hilt-android"]
implementation rootProject.ext.dependencies["hilt-lifecycle-viewmodel"]
kapt rootProject.ext.kapt["hilt-compiler"]
kapt rootProject.ext.kapt["hilt-android-compiler"]
}
Who has ideas? Thanks!
I just hit this problem this morning. Do you have anything in your build.gradle that adds arguments to the annotationProcessOptions? For example:
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
}
If so, try changing from "arguments =" to "arguments +=", as just using equals overwrites anything set previously.
EDIT: Looks like kotlin gradle plugin 1.5.21 solves the problem without using the bellow javacOptions.
UPDATE Kotlin and try again!
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
If you are not using Room and still get the error put this in the android block of build.gradle:
kapt {
javacOptions {
// These options are normally set automatically via the Hilt Gradle plugin, but we
// set them manually to workaround a bug in the Kotlin 1.5.20
option("-Adagger.fastInit=ENABLED")
option("-Adagger.hilt.android.internal.disableAndroidSuperclassValidation=true")
}
}
It's a kapt bug on kotlin 1.5.20: https://github.com/google/dagger/issues/2684
SOLUTION 1 : Downgrade kotlin
If you are using kotlin-gradle-plugin:1.5.20 (in your project level build.gradle), downgrading it to 1.5.10 should fix the issue.
The issue will probably be fixed in the next versions, then you will upgrade to the new version.
SOLUTION 2 : Disable Gradle worker API
Add this line to your gradle.properties file:
kapt.use.worker.api=false
It will disable the gradle worker API.
It works for me, but as said in the documentation:
Using the worker API lets Gradle run independent annotation processing tasks from a single project in parallel, which in some cases significantly decreases the execution time.
So by disabling it, your build may be slowed down.
Just don't forget to add Hilt classpath dependency to your project level gradle file:
classpath "com.google.dagger:hilt-android-gradle-plugin:$versions.daggerHiltCoreVersion"
Define the specific version number instead of $versions.daggerHiltCoreVersion above.
And add plugin to your app level gradle:
apply plugin : 'dagger.hilt.android.plugin'
For later Gradle versions, add the plugin as follows
plugins {
id 'dagger.hilt.android.plugin'
}
Adding to sitatech's answer, I've also encountered this issue using kotlin-grade-plugin-1.5.20. The new 1.5.21 patch solved it for me.
Kotlin Grade Plugin v1.5.21 release notes: https://github.com/JetBrains/kotlin/releases/tag/v1.5.21
Issue in Jetbrains issue tracker: https://youtrack.jetbrains.com/issue/KT-47416
To backup #SteveC answer, when using Kotlin Gradle DSL is a bit different
We can't use either += or arguments = mapOf(). As stated in the official Dagger-Hilt documentation here & the github issue here regarding the docs as well
See below image for explanations:
arguments = mapOf() will call setArguments with this.arguments.clear(), thus will overwrite previous argument (in this case Hilt)
Workaround approach:
javaCompileOptions {
annotationProcessorOptions {
arguments(
mapOf(
"dagger.gradle.incremental" to "true",
"room.incremental" to "true"
)
)
}
}
Wrapping the arguments() as a functions instead of calling setter, it'll retain the previous arguments as well.
in my case, multi module in presentaion layer, I just removed :
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
in defaultConfig block
i want to use checkstyle plugin in my gradle project, the gradle documentation says that it will add a few tasks:
https://docs.gradle.org/current/userguide/checkstyle_plugin.html
checkstyleMain, checkstyleTest, checkstyleSourceSet
I added this into my app build.gradle file:
apply plugin: 'checkstyle'
I want to run gradle task from cmd to perform code style check, but there are no one checkstyle task. I checked the whole list by typing:
./gradlew tasks
I also tried to add checkstyle jar as library dependency to app module.
Can anyone tell me what i am doing wrong and how can i get my checkstyle tasks?
Well, the checkstyle plugin adds its tasks in the Other tasks virtual task group. Those are really the tasks which have not been assigned to a task group. So, they are shown only when you run ./gradlew tasks --all (note the --all).
Complete working build.gradle:
apply plugin: 'java';
apply plugin: 'checkstyle';
Then run ./gradlew tasks --all, output:
<...snip...>
Other tasks
-----------
checkstyleMain - Run Checkstyle analysis for main classes
checkstyleTest - Run Checkstyle analysis for test classes
<...snip...>
If you want the Checkstyle tasks to appear without --all, then assign them to a task group, for example:
tasks.withType(Checkstyle).each {
it.group = 'verification'
}
Looking at other Android projects, built with Gradle, that run checkstyle (thanks Square) I found that I needed to do some setup for the task to appear. Without the task declaration I would still see my initial error.
build.gradle:
...
apply plugin: 'checkstyle'
...
checkstyle {
configFile rootProject.file('checkstyle.xml')
ignoreFailures false
showViolations true
toolVersion = "7.8.1"
}
task Checkstyle(type: Checkstyle) {
configFile rootProject.file('checkstyle.xml')
source 'src/main/java'
ignoreFailures false
showViolations true
include '**/*.java'
classpath = files()
}
// adds checkstyle task to existing check task
afterEvaluate {
if (project.tasks.getByName("check")) {
check.dependsOn('checkstyle')
}
}
You also need a checkstyle configuration file, either by placing one at the default location as documented or by configuring it explicitly.
For example:
checkstyle {
config = resources.text.fromFile('config/checkstyle.xml')
}
I am following the Gradle tutorial on https://guides.gradle.org/building-android-apps/ . So the last step of this part is Run a Build Scan. I am doing the exact same thing as it asked me to do, but Android Studio keeps saying "Error:(14, 0) Could not get unknown property 'com' for root project 'HelloWorldGradle' of type org.gradle.api.Project."
Here is my Top-level build file(build.gradle(Project: HelloWorldGradle)):
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
maven { url 'https://plugins.gradle.org/m2' }
}
dependencies {
classpath 'com.android.tools.build:gradle:2.4.0-alpha7'
classpath 'com.gradle:build-scan-plugin:1.7.1'
}
}
apply plugin: com.gradle.build-scan
buildScan {
licenseAgreementUrl = 'https://gradle.com/terms-of-service'
licenseAgree = 'yes'
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
My issue was solved after writing the following in build.gradle the top-level file for the project
plugins {
id 'com.gradle.build-scan' version '1.16'
}
buildScan {
licenseAgreementUrl = 'https://gradle.com/terms-of-service'
licenseAgree = 'yes'
publishAlways()
}
Edit in May 2020:
Now in May 2020, the way to do it is, add the following to the app gradle file:
plugins {
id "com.gradle.build-scan" version "3.3"
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
publishAlways()
}
You should place these outside of the buildscript { .. }, e.g., right after it, rather than inside it.
Then the build scan, e.g., with ./gradlew build --scan, should work.
Edit in October 2019:
The current (October 2019) way to get the build scan working is to add the following to the app gradle file
plugins {
id 'com.gradle.build-scan' version '2.4.2'
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
publishAlways()
}
and it would probably be best to get it directly from the gradle build scan user manual in the future for when it changes again.
The issue is with the following line in your build.gradle:
apply plugin: com.gradle.build-scan
You need to update as
apply plugin: 'com.gradle.build-scan'
Another thing you need to pay attention is ALWAYS put the com.gradle.build-scan plugin as the very first one, like this:
apply plugin: 'com.gradle.build-scan'
apply plugin: 'java'
Otherwise, you would see this:
WARNING: The build scan plugin was applied after other plugins. The
captured data is more comprehensive when the build scan plugin is
applied first.
Please see https://gradle.com/scans/help/plugin-late-apply for how to
resolve this problem.
Let me know if this works.
According to gradle doc, this should be the latest implementation
plugins {
id 'com.gradle.build-scan' version '2.3'
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
publishAlways()
}
Run from Terminal next command ./gradlew build --scan or gradlew build --scan
Also, agree to the Terms of Service in the command line.
My app has several flavors for several markets in-app-billing systems.
I have a single library which shares the base code for all of my projects. So I decided to add those payment systems to this library as product flavors.
The question is can android library have product flavors?
If so, how can I include different flavors in respective flavor of the app?
I searched a lot, and I couldn't find anything about this scenario. The only close thing I found was this in http://tools.android.com/tech-docs/new-build-system/user-guide:
dependencies {
flavor1Compile project(path: ':lib1', configuration: 'flavor1Release')
flavor2Compile project(path: ':lib1', configuration: 'flavor2Release')
}
I changed configuration to different things but it did not work!
I'm using android studio 0.8.2.
Finally I found out how to do this, I will explain it here for others facing same problem:
If App and Library have same Flavor name(s)
It's possible since Gradle Plugin 3.0.0 (and later) to do something like:
Library build.gradle:
apply plugin: 'com.android.library'
// Change below's relative-path
// (as the `../` part is based on my project structure,
// and may not work for your project).
apply from: '../my-flavors.gradle'
dependencies {
// ...
}
android {
// ...
}
Project build.gradle:
buildscript {
// ...
}
apply plugin: 'com.android.application'
// Note that below can be put after `dependencies`
// (I just like to have all apply beside each other).
apply from: './my-flavors.gradle'
dependencies {
api project(':lib')
}
android {
productFlavors {
// Optionally, configure each flavor.
market1 {
applicationIdSuffix '.my-market1-id'
}
market2 {
applicationIdSuffix '.my-market2-id'
}
}
}
My flavors .gradle:
android {
flavorDimensions 'my-dimension'
productFlavors {
market1 {
dimension 'my-dimension'
}
market2 {
dimension 'my-dimension'
}
}
}
If App or Library has different Flavor-name (old answer)
The key part is to set publishNonDefault to true in library build.gradle, Then you must define dependencies as suggested by user guide.
Update 2022; publishNonDefault is now by default true, and setting it to false is ignored, since said option is deprecated.
The whole project would be like this:
Library build.gradle:
apply plugin: 'com.android.library'
android {
....
publishNonDefault true
productFlavors {
market1 {}
market2 {}
market3 {}
}
}
project build.gradle:
apply plugin: 'com.android.application'
android {
....
productFlavors {
market1 {}
market2 {}
market3 {}
}
}
dependencies {
....
market1Compile project(path: ':lib', configuration: 'market1Release')
market2Compile project(path: ':lib', configuration: 'market2Release')
// Or with debug-build type support.
android.buildTypes.each { type ->
market3Compile project(path: ':lib', configuration: "market3${type.name}")
}
}
Now you can select the app flavor and Build Variants panel and the library will be selected accordingly and all build and run will be done based on the selected flavor.
If you have multiple app module based on the library Android Studio will complain about Variant selection conflict, It's ok, just ignore it.
There are one problem with Ali answer. We are losing one very important dimension in our build variants. If we want to have all options (in my example below 4 (2 x 2)) we just have to add custom configurations in main module build.gradle file to be able to use all multi-flavor multi-buildType in Build Variants. We also have to set publishNonDefault true in the library module build.gradle file.
Example solution:
Lib build.gradle
android {
publishNonDefault true
buildTypes {
release {
}
debug {
}
}
productFlavors {
free {
}
paid {
}
}
}
App build.gradle
android {
buildTypes {
debug {
}
release {
}
}
productFlavors {
free {
}
paid {
}
}
}
configurations {
freeDebugCompile
paidDebugCompile
freeReleaseCompile
paidReleaseCompile
}
dependencies {
freeDebugCompile project(path: ':lib', configuration: 'freeDebug')
paidDebugCompile project(path: ':lib', configuration: 'paidDebug')
freeReleaseCompile project(path: ':lib', configuration: 'freeRelease')
paidReleaseCompile project(path: ':lib', configuration: 'paidRelease')
}
Update for Android Plugin 3.0.0 and higher
According to the official Android Documentation - Migrate dependency configurations for local modules,
With variant-aware dependency resolution, you no longer need to use variant-specific configurations, such as freeDebugImplementation, for local module dependencies—the plugin takes care of this for you
You should instead configure your dependencies as follows:
dependencies {
// This is the old method and no longer works for local
// library modules:
// debugImplementation project(path: ':library', configuration: 'debug')
// releaseImplementation project(path: ':library', configuration: 'release')
// Instead, simply use the following to take advantage of
// variant-aware dependency resolution. You can learn more about
// the 'implementation' configuration in the section about
// new dependency configurations.
implementation project(':library')
// You can, however, keep using variant-specific configurations when
// targeting external dependencies. The following line adds 'app-magic'
// as a dependency to only the "debug" version of your module.
debugImplementation 'com.example.android:app-magic:12.3'
}
So in Ali's answer, change
dependencies {
....
market1Compile project(path: ':lib', configuration: 'market1Release')
market2Compile project(path: ':lib', configuration: 'market2Release')
}
to
implementation project(':lib')
And plugin will take care of variant specific configurations automatically. Hope it helps to others upgrading Android Studio Plugin to 3.0.0 and higher.
My Android Plugin is 3.4.0,and I find that it doesn't need configurations now.All you need is to make sure the flavorDimensions and productFlavors in application contains one productFlavor of the same flavorDimensions and productFlavors in libraries.For sample:
In mylibrary's build.gradle
apply plugin: 'com.android.library'
android {
....
flavorDimensions "mylibFlavor"
productFlavors {
market1
market2
}
}
application's build.gradle:
apply plugin: 'com.android.application'
android {
....
flavorDimensions "mylibFlavor", "appFlavor"
productFlavors {
market1 {
dimension "mylibFlavor"
}
market2 {
dimension "mylibFlavor"
}
common1 {
dimension "appFlavor"
}
common2 {
dimension "appFlavor"
}
}
}
dependencies {
....
implementation project(path: ':mylibrary')
}
After sync,you can switch all options in Build Variants Window:
To get the flavors working on an AAR library, you need to define defaultPublishConfig in the build.gradle file of your Android Library module.
For more information, see: Library Publication.
Library Publication
By default a library only publishes its release variant. This variant
will be used by all projects referencing the library, no matter which
variant they build themselves. This is a temporary limitation due to
Gradle limitations that we are working towards removing. You can
control which variant gets published:
android {
defaultPublishConfig "debug" }
Note that this publishing configuration name references the full
variant name. Release and debug are only applicable when there are no
flavors. If you wanted to change the default published variant while
using flavors, you would write:
android {
defaultPublishConfig "flavor1Debug" }
I also ran into a problem compiling modules for various options.
What i've found:
It looks like we don't need add publishNonDefault true into lib's build.gradle file, since Gradle 3.0.1.
After decompiling a class BaseExtension found this:
public void setPublishNonDefault(boolean publishNonDefault) {
this.logger.warn("publishNonDefault is deprecated and has no effect anymore. All variants are now published.");
}
And instead of:
dependencies {
...
Compile project(path: ':lib', configuration: 'config1Debug')
}
We should use:
dependencies {
...
implementation project(':lib')
}
Only the important thing, is to add a configurations {...} part to the build.gradle.
So, the final variant of app's build.gradle file is:
buildTypes {
debug {
...
}
release {
...
}
}
flavorDimensions "productType", "serverType"
productFlavors {
Free {
dimension "productType"
...
}
Paid {
dimension "productType"
...
}
Test {
dimension "serverType"
...
}
Prod {
dimension "serverType"
...
}
}
configurations {
FreeTestDebug
FreeTestRelease
FreeProdDebug
FreeProdRelease
PaidTestDebug
PaidTestRelease
PaidProdDebug
PaidProdRelease
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':lib')
...
}
Also, you can use Filter variants to restrict build variants.
P.s. don't forget to include modules in the settings.gradle file, like:
include ':app'
include ':lib'
project(':lib').projectDir = new File('app/libs/lib')
At the moment it's not possible, although if I recall correctly its a feature they want to add. (Edit 2: link, link2 )
Edit:
For the moment I'm using the defaultPublishConfig option to declare which library variant get's published:
android {
defaultPublishConfig fullRelease
defaultPublishConfig demoRelease
}
I know this subject has been closed, but just an update with gradle 3.0, see this : https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#variant_aware and grep matchingFallbacks and missingDimensionStrategy.
Now it's way more simple to declare the dependencies between module flavors.
...and in this precise case with gradle3.0, as flavors share the same name, gradle would map them magically, there is no configuration required.
In this situation. How could I import the dependency for a specific build. For example: market1Common1Debug
market1Common1DebugImplementation 'androidx.appcompat:1.2.0'
I have a suite of projects that use the same module, which contains nearly all the actual code. The project is setup like:
project/
- app/
- build.gradle
- libraries/
- module/
- build.gradle
- build.gradle
- settings.gradle
The dependencies are all setup correctly, and I can build and run apps great, however I can only add flavors to the project, which is not the ideal solution. settings.gradle contains the following:
include ':app', ':libraries:module'
In the app directory's build.gradle file, I added the following block:
productFlavors {
alpha
production
}
Using gradle 0.11, this syncs and creates assembleAlphaDebug, assembleAlphaRelease, assembleProductionDebug, assembleProductionRelease tasks. When I attempt to do this in the module instead, I get the error:
No resource found that matches the given name (at 'theme' with value '#style/MyCustomTheme')
in the built app/src/main/AndroidManifest.xml. For some reason, the module is not being built, so the custom theme is not working. What am I doing wrong?
In the library module's build.gradle, you need a couple extra lines to tell it to export the flavors and which build variant to use by default if not specified when being included from another module:
android {
defaultPublishConfig "productionRelease"
publishNonDefault true
productFlavors {
alpha {
}
production {
}
}
}
That publishNonDefault bit is only necessary if someone would want to depend on something other than the productionRelease variant. Presumably this is the case if you set up multi-flavors in your library in the first place.
Now if you add a dependency from another module via this in its build.gradle:
dependencies {
compile project(':module')
}
it will depend on the productionRelease variant by default. If you'd like to depend on a non-default variant:
dependencies {
compile project(path: ':module', configuration:'alphaDebug')
}
First add below gradle code snippet to your app/build.gradle
flavorDimensions "env"
productFlavors {
dev {
dimension "env"
}
pre {
dimension "env"
}
prod {
dimension "env"
}
}
Second, add below gradle code snippet to your module/build.gradle
flavorDimensions "env"
productFlavors {
register("dev")
register("pre")
register("prod")
}
I have posted an ansower in this
Use different library module for each android flavor