I have a gradle script which needs to be imported as a dependency like this:
compile project(':subproject', { ext.app = 'myApp'; ext.serverUrl = 'https://example.com'; ext.system = 'LIVE'})
This is working fine, if I set the variables directly in the dependency statement.
As I have a different system for debug and for release I tried to move these properties to the buildTypes:
...
debug {
debuggable true
serverUrl = 'https://example.com'
system = 'TEST'
}
prerelease {
debuggable true
serverUrl = 'https://example.com'
system = 'STAGING'
}
release {
serverUrl = 'https://example.com'
system = 'LIVE'
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
...
dependencies {
compile project(':subproject', { ext.app = appName; ext.serverUrl = serverUrl; ext.system = system })
}
So, when I build assembleDebug it should use TEST and with assemblePrerelease it should use STAGING. However it is always using the release build type variables to compile the dependency.
The library already contains publishNonDefault true
What's wrong with this gradle script?
I answer my own question.
Gradle does not parse the gradle file as expected. The closures will be evaluated in a single step when the tasks are being generated. This means that there is no concept of having a variable which will only be used when the specific task is being executed. The file is being read once which causes the variables to override the previous values of another flavor or buildType. This is also the reason why changing the order of the flavors results in different values.
The correct solution would be to define a custom task which is run right after the file has been generated. That task will generate a set of tasks for each variant of the app which themselves contain the configuration what to do.
This SO article helped me alot: How to get current buildType in Android Gradle configuration
Related
I have an app with multiple flavors where I'm using dexguard, and I've decided to set it up on Jenkins.
Dexguard licenses are located in /app/flavor/ for eash license, but Jenkins always takes the license from the last flavor.
The build fails with the error code:
The package name from the AndroidManifest.xml file [com.example.android.flavor1.something] doesn't match the package name [com.example.android.flavor6.something,com.example.android.flavor6.test,com.example.android.flavor6.something.prod,com.example.android.flavor6.test] from your DexGuard license [C:\Users\CurrentUser\AndroidStudioProjects\MyApp\flavor6\dexguard-license.txt]
I have tried renaming dexguard.license to dexguard-licenseX.txt (where X is the number of flavor) and setting in flavors build.gradle to look for that name, that couldn't even find the license file.
I have also tried setting up the licence location in gradle.properties with systemProp.dexguard.licence=./flavor1.
I'm currently using
release {
System.properties['dexguard.license'] = buildscript.sourceFile.parent
proguardFiles getDefaultDexGuardFile('dexguard-release.pro'), 'dexguard-rules.pro', 'proguard-rules.pro'
}
And that works only if I try to build the last flavor, otherwise I have to copy dexguard-license to home folder (which isn't a problem locally, but it is a problem on Jenkins).
Is there a way to set up dexguard on jenkins ?
dexguard-license1.txt will not be taken into account, start with dexguard-license2.txt if you have additional license files.
Solved it:
Added this code to build.gradle of every module:
def getCurrentModule() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
if(tskReqStr.indexOf( ":currentModule:" ) >= 0 )
return ":currentModule:"
else
{
println "NO MATCH FOUND"
return ""
}
}
and this in buildTypes-release:
if (getCurrentModule() == ":currentModule:") {
System.properties['dexguard.license'] = buildscript.sourceFile.parent
}
proguardFiles getDefaultDexGuardFile('dexguard-release.pro'), 'dexguard-rules.pro', 'proguard-rules.pro'
Been working on an Android Webview app and I've only just now started to use a node_modules folder in there, so naturally I did some research on how to exclude it and I ended up at this question here
Tried some of the answers and the one that actually worked the best for me was the one about aaptOptions
So of course naturally I'm playing around with it, trying to figure out what works, and I succeed at excluding a few folders from the debug apk.
aaptOptions {
ignoreAssetsPattern '!node_modules:!jsunmin:!.idea:!jade:!css-scss:'
}
And I can indeed confirm that those folders aren't included in the final APK in Android studio! Success!
So then I realize that I can do something a bit clever: when I'm running my app, testing it on my end, debugging it, I like to have certain credentials in certain places automatically put in - I do this with javascript - but obviously I don't want these credentials included in the APKs I might send out into the world -- even though I've already coded it to not input the credentials automatically unless I'm debugging, the credentials are still actually in the javascript files, and presumably someone could look in there and see them!
So my idea was to create a file, 'example-creds.js', and use aaptOptions to not include that file, ONLY on release builds, so I came up with something that looks approximately like this (extra details stripped out):
android {
buildTypes {
release {
aaptOptions {
ignoreAssetsPattern '!node_modules:!jsunmin:!.idea:!jade:!css-scss:!example-creds.js:'
}
}
debug {
aaptOptions {
ignoreAssetsPattern '!node_modules:!jsunmin:!.idea:!jade:!css-scss:'
}
}
}
}
BUT IT DOESN'T WORK! I've tested it and it seems to run whatever the last-defined aaptOptions is, regardless of the build type. If I put release after debug, I get no example-creds in either build. If I put debug after release, I get example-creds in both.
How can I get what I'm looking for?
Generally it should be possible to configure AaptOptions alike any other configuration block. Try to run that script afterEvaluate (which is after those aaptOptions had been evaluated):
task afterEvaluate {
doLast {
applicationVariants.all { variant ->
variant.outputs.all { output ->
def pattern = "!node_modules:!jsunmin:!.idea:!jade:!css-scss:"
if(variant.getBuildType().getName() == 'release') {
pattern = pattern + "!example-creds.js:"
}
aaptOptions.ignoreAssetsPattern = pattern
}
}
}
}
aaptOptions's ignoreAssetPattern cannot be set in afterEvaluate block. If you try, you will run into this error
com.android.build.gradle.internal.dsl.AgpDslLockedException: It is too late to set ignoreAssetsPattern
It has already been read to configure this project.
Consider either moving this call to be during evaluation,
or using the variant API.
The solution is to use the variant API during configuration (also note that aaptOptions is depricated and is renamed to androidResources AGP 7.1.3 onwards)
android {
buildTypes {
release {
androidResources {
ignoreAssetsPattern '!node_modules:!jsunmin:!.idea:!jade:!css-scss:'
}
}
debug {
androidResources {
ignoreAssetsPattern '!node_modules:!jsunmin:!.idea:!jade:!css-scss:'
}
}
}
androidComponents {
onVariants(selector().withName("release")) { variant ->
def pattern = variant.androidResources.ignoreAssetsPattern
pattern.add("!example-creds.js")
}
}
}
I know we can edit build types in Android Studio:
I know we can edit each build type setting in gradle:
android {
buildTypes {
release {
minifyEnabled true
}
}
I know we can detect build types in code. How do I detect if I am in release or debug mode?
But where actually are the build types defined? Let say I want to commit it to git. What should I do to keep build types of the project consistent?
Where actually are the build types defined?
Basically, BuildConfig is the auto-generated class that resides under path :
app/build/generated/source/buildConfig/yourBuildType/yourPackageName/BuildConfig.java.
This class holds variables provided by buildTypes {} block from app level build.gradle file. So, on every clean & rebuild of project, Gradle auto generates BuildConfig class that can be used in further Android development environment.
I.e. BuildConfig.DEBUG is the default variable that we can use in our application code to determine it's buildType.
We can provide our own fields through buildType from build.gradle file like following:
android {
. . .
buildTypes {
debug {
buildConfigField "String", "SOME_VARIABLE", '"This string value is from build config class"'
}
}
. . .
}
I want Fabric to stop generating a UUID on each build. What used to work with Gradle's Groovy DSL does not work with the newer Kotlin DSL. How can I achieve my goal with the Kotlin DSL?
(Gradle version 4.10.2, Fabric 1.25.4)
According to Fabric's documentation, you can add the following to your app's build script
android {
buildTypes {
debug {
// Only use this flag on builds you don't proguard or upload
// to beta-by-crashlytics
ext.alwaysUpdateBuildId = false
and this works. It prevents Fabric from generating a UUID on each debug build. However, if I convert my build script to Kotlin DSL, the following doesn't work
android {
buildTypes {
getByName("debug") {
// Only use this flag on builds you don't proguard or upload
// to beta-by-crashlytics
ext.set("alwaysUpdateBuildId", false)
Fabric ignores this value, now.
I have tried variations, such as the following:
project.ext.set("alwaysUpdateBuildId", false)
rootProject.ext.set("alwaysUpdateBuildId", false)
val alwaysUpdateBuildId by extra(false)
val alwaysUpdateBuildId by project.extra(false)
val alwaysUpdateBuildId by rootProject.extra(false)
None work.
For further reference, the Gradle task generating this value appears to be named :app:fabricGenerateResourcesDebug, and has type DefaultTask.
As Martin Rajniak mentioned, you can only call extra on ExtensionAware objects, with BuildType not being declared as one.
However, during runtime, build types actually are ExtensionAware, which is why this works in Groovy due to its dynamicity, but not in Kotlin where extra in this scope will reference the Project's extensions.
In order to achieve this without Groovy, we can simply cast the build type to ExtensionAware:
android {
buildTypes {
getByName("debug") {
(this as ExtensionAware).extra["alwaysUpdateBuildId"] = false
}
}
}
I have found a workaround to this problem. Create a file, fabric.gradle (Groovy build script!) and place it in your project structure somewhere. It will have the following contents:
// or "com.android.library"
project.pluginManager.withPlugin("com.android.application") {
android.buildTypes.debug.ext.alwaysUpdateBuildId = false
}
Now, in the build script for your module (let's call it app/build.gradle.kts), apply this script plugin:
apply(from = "path/to/fabric.gradle")
This workaround is based on the advice here, in the Kotlin DSL primer.
I am trying to migrate an android project from maven to gradle.
In maven I have two profiles (dev and prod), dev profile uses a dev.properties file to set up some properties and prod uses prod.properties.
I want to be able to tell gradle to use dev.properties for debug build and prod.properties for release build.
More specifically all I need to do is rename the file to constants.properties
How can I achieve this?
Assuming you already have a way to distinguish between dev vs prod, you can rename the file in Groovy with something like this:
def dev = true // set to true or false
def propFile
if (dev) {
propFile = file("dev.properties")
} else {
propFile = file("prod.properties")
}
propFile.renameTo("constants.properties")