My android build is set to use a versionName and versionCode from a file in release mode. They are set to static values when not creating a release build to keep incremental builds working.
The relevant parts of the gradle file are:
android {
defaultConfig {
versionCode 1
versionName "1.0"
// SNIP...
}
applicationVariants.all { variant ->
if (variant.buildType.name == "release") {
variant.versionCode = file('version-code.txt').text as int
variant.versionName = file('version.txt').text
}
}
// SNIP ...
}
Example contents of the version files could be:
version.txt: 0.7
version-code.txt: 7
This was done by following the Use static build config values with your debug build section in the recommended guidelines for keeping incremental builds working.
For example, using dynamic version codes, version names, resources, or any other build logic that changes the manifest file requires a full APK build every time you want to run a change—even though the actual change might otherwise require only a hot swap. If your build configuration requires such dynamic properties, then isolate them to your release build variants and keep the values static for your debug builds, as shown in the build.gradle file below.
However, we've found this has broken since upgrading to version 3 of the gradle plugin this no longer works. The Modifying variant outputs at build time may not work section of the gradle plugin 3.0.0 migration guide says:
Using the Variant API to manipulate variant outputs is broken with the new plugin. It still works for simple tasks, such as changing the APK name during build time, as shown below:
However, more complicated tasks that involve accessing outputFile objects no longer work. That's because variant-specific tasks are no longer created during the configuration stage. This results in the plugin not knowing all of its outputs up front, but it also means faster configuration times.
There doesn't seem to be any alternative recommended in the migration guide. Is there another way this can be achieved?
UPDATE
Thanks to the answer from #nhoxbypass, changing my gradle file to contain following got things working again:
applicationVariants.all { variant ->
if (variant.buildType.name == "release") {
variant.outputs.all { output ->
output.setVersionNameOverride(file('version.txt').text)
output.setVersionCodeOverride(file('version-code.txt').text as int)
}
}
}
The migration guide still works for simple tasks, such as changing the APK name during build time (at least worked for my project). However, more complicated tasks that involve accessing outputFile objects no longer work.
But if you need to try a workaround there is one existing before the 3.0 release, if anybody is looking for a solution, you can use:
output.setVersionCodeOverride(Integer.parseInt(buildTimeSmall()))
See: Unable to change project versionCode for different build types
Related
I have a gradle build i need to have different configurations based whether it is a release or debug build.
The problem is that the gradle build does not distinguish between those two.
for example :
apply plugin: 'com.android.library'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
buildTypes {
debug {
println 'debug'
}
release {
println 'release'
}
}
}
When i build using 'gradle assembleRelsease' or using 'gradle assembleDebug', it prints both 'release' and 'debug' in both cases or even when i build using debug (from Build Variants) in android studio. It simply does not distinguish it. What i would excpect is when i build release it only prints 'release' and when i build degub it only prints 'debug'. Does any body have a solution to this problem ? am i do doing something wrong?
Your script will produce the expected results (different .aar's) for debug and release library variants: it just prints everything all the time.
What you're encountering is the Gradle lifecycle, which goes through an Initialization, Configuration and Execution phase.
Since many build.gradle files (Android projects usually have at least 2) are used in the build process for different directories and subprojects, and you can define tasks after you use them in the ordering of the build file, I am guessing the first two phases perform scanning and analysis on the build files before actually building anything.
Configuration goes through all commands, including your println statement. (see this example) Maybe they've chosen to have println actually print during configuration to ease debugging.
I have an app where I'd like to add an Android Wear app extension. The main app has three build types (debug, beta and release). Beta builds have an applicationIdSuffix which allows me to install the play-store version and the current development version in parallel on the same device. This all worked fine until I added the wear app.
The main app`s build.gradle looks like this:
apply plugin: 'com.android.application'
android {
...
defaultConfig {
...
applicationId "com.example.mainApp"
...
}
buildTypes {
debug {
applicationIdSuffix '.debug'
}
beta {
applicationIdSuffix '.beta'
}
release {
}
}
}
dependencies {
...
wearApp project(':wear')
}
The Wear-App has the same build types with the same applicationIdSuffix values. However, when I build the beta app (by calling gradle assembleBeta) the build process builds :wear:assembleRelease instead of :wear:assembleBeta which is why I get the following error message during build:
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:handleBetaMicroApk'.
> The main and the micro apps do not have the same package name.
How can I tell the build process to build the correct build type when packaging the main app with build type beta?
Following the link posted by Scott Barta (http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication) I came up with this :
In the build.gradle of the wear app, add publishNonDefault true (to publish all variants):
android {
publishNonDefault true
}
In the build.gradle of the main app,
Replace
wearApp project(':wear')
By
debugWearApp project(path:':wear', configuration: 'debug')
releaseWearApp project(path:':wear', configuration: 'release')
You can't do what you want; the build variant of a module isn't propagated to the builds of dependent modules on build. This is tracked in https://code.google.com/p/android/issues/detail?id=52962
There's a facility to make one module depend on a specific variant of another one, as documented in http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication, but I don't think this mechanism can be extended to do differential packaging of Wear apps.
UPDATE
Now there is official support for build variants (see answer of Cyril Leroux). Hence this answer is deprecated.
I found a very (very) ugly solution which has some drawbacks but works for now until there is support for build variants for wear apps.
I set a global variable in the rootProject which contains the applicationIdSuffix of the currently built main app.
Within build.gradle of the main app I added the following:
// Set a global variable, depending on the currently built build-type.
// This allows us to set the applicationIdSuffix of the wear app depending on
// the build-type of the main app.
android.applicationVariants.all { variant ->
def task = variant.checkManifest
def suffix = variant.buildType.applicationIdSuffix
task.doLast {
rootProject.ext.currentApplicationIdSuffix = suffix
}
}
In the build.gradleof the wear app I added the following snipped:
android.applicationVariants.all { variant ->
def task = variant.generateBuildConfig
task.dependsOn(propagateApplicationIdSuffix)
}
task propagateApplicationIdSuffix << {
project.android.buildTypes.all { type ->
if (rootProject.hasProperty('currentApplicationIdSuffix')) {
type.applicationIdSuffix = rootProject.ext.currentApplicationIdSuffix
}
}
}
This has several drawbacks:
You can't build multiple variants (i.e. gradle assembleBeta assembleRelease) because the wear app is only built once and hence the second build type fails
gradle check fails because of reason 1
The wear app is still built with build type release but the package name is just changed according to to application id suffix of the main app
Don't worry, you CAN do what you want to do. I just did it for the enterprise app I work on.
The key is NOT to use wearApp project(':wear'), since that only works when you have the same applicationId in your wear app as your main app. And let's face it, how often in real life does that situation happen? If it happens, you probably aren't using Gradle to the best of it's ability.
You want to follow the instructions for Package Manually in the google wear docs
https://developer.android.com/training/wearables/apps/packaging.html#PackageManually
Unfortunately, this will require you to build your wear app with the same applicationId as the particular build variant you are making at the time, but it does allow you to successfully package the wear app inside an app with multiple applicationId.
Also, one trick I do that helps is to not put the wear apk inside /res/raw, but in /assets, that way you don't have to deal with Andriod Studio compressing the apk.
Hope this helps! Drove me crazy for a couple days finding a solution. And the only tutorial out there is in French, and I had to translate the website to read it!
https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=http%3A%2F%2Fblog.octo.com%2Fpackager-une-application-android-wear-dans-la-vraie-vie%2F
My application has a bunch of librarys that are essential that is why I was forced to use multidex support library and it works nicely. But where the problem shows is in the gradle buid speed. It takes on average 2minutes to build and when I am developing and testing this is quite annoying.
Is there a way to speed up my debug builds?
You can speed-up your development builds by specifying the minimum SDK version = 21.
Official documentation includes a whole section about that.
Example (from documentation):
android {
productFlavors {
// Define separate dev and prod product flavors.
dev {
// dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
// to pre-dex each module and produce an APK that can be tested on
// Android Lollipop without time consuming dex merging processes.
minSdkVersion 21
}
prod {
// The actual minSdkVersion for the application.
minSdkVersion 14
}
}
...
buildTypes {
release {
runProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:multidex:1.0.0'
}
Once you added the product flavors, you can use the devDebug task (instead of default debug task) for your development builds:
- from command line: run ./gradlew installDevDebug
- from Android Studio: open Build Variants window and select the devDebug build variant.
You should, of course, work against a device whose SDK >= 21.
There's also a solution for those who don't want to use flavors. As suggested in this gist, dynamically calculate the minSdkVersion value:
int minSdk = hasProperty('devMinSdk') ? devMinSdk.toInteger() : 14
apply plugin: 'com.android.application'
android {
...
defaultConfig {
minSdkVersion minSdk
...
}
}
In this example, we're checking if devMinSdk property defined, and if true - we're using it. Otherwise, we default to 14.
How do we pass devMinSdk value to build script? Two options:
Using command line:
./gradlew installDebug -PdevMinSdk=21
Using Android Studio preferences:
Go to Preferences (Settings on Windows) -> Build, Execution, Deployment -> Compiler -> put -PdevMinSdk=21 in Command-line Options text box.
Recently build cache was introduced by team working on Android Gradle plugin. You can enable it by adding android.enableBuildCache=true to gradle.properties.
More info here
http://tools.android.com/tech-docs/build-cache
For me it increased incremental build times by ~30 seconds.
It doesn't work with legacy multidex (com.android.support:multidex) introduced as part of support library, so it's suitable only if your minSDK >= 21. You can set it only for your your development builds and do release builds with minSDK < 21.
It also works without multidexing enabled.
Android Studio 1.3 (currently in Preview 3) is using a new build system which improved gradle build time (really, like 10-30x faster).
More information in the Live Session at Google I/O 2015
Multidexing uses more memory. As you get closer to your max heap size in Java you'll find Java spends more time doing GC than it does doing any real work, this can slow things down a lot.
I'd strongly recommend increasing the max heap size when using multidex. Add the following to the android closure in your build.gradle file to make the max heap size 4GB (Make it smaller if you wish):
dexOptions {
javaMaxHeapSize "4g"
}
Changing MinSdk to 21 made everything back to normal for me.Now everything compiles in like 6s
This is no longer needed with the latest Android Studio 3.0
Go in setting , search compiler , type "--offline" in Command line options and than compile.
I followed instructions similar to these:
Android Library Gradle release JAR
However, I get the following error:
Could not find method isReleaseBuild() for arguments [] on project ':myProject'.
None of the examples I saw on Maven integration define this function, so I assumed it was built-in. However, I get this error with Gradle 1.12 and 2.1, and Android plugin 0.12.+ and 0.13.+
If it's not a built-in function, what is the best practice for defining it?
No. It is user defined, but seems to permeate examples because a lot of Gradle examples are copied.
This is the way I ended up defining it, so that I could pass in -PMAVEN_RELEASE_BUILD on the command line to turn on release builds.
def isReleaseBuild() {
return hasProperty("MAVEN_RELEASE_BUILD") && MAVEN_RELEASE_BUILD == "true";
}
Some people also like to define as version names ending in -SNAPSHOT. I do not prefer to do this, because I want to be able to choose whether to build a release or debug build without checking in a change to gradle.properties.
def isReleaseBuild() {
return !VERSION_NAME.contains("SNAPSHOT");
}
Can the pre-defined build-types somehow being renamed?
We use some custom build types that represent our internal staging setup (DEV/TEST/LIVE) and do not need the build in buildTypes (release/debug). Can these somehow being renamed or disabled when calling assemble?
import com.android.builder.core.BuilderConstants
android.variantFilter { variant ->
def build = variant.buildType.name
if (build == BuilderConstants.DEBUG || build == BuilderConstants.RELEASE) {
variant.setIgnore(true)
}
}
Note that the very first time importing the project Android Studio will pick any variant named debug else it will pick the first build variant in alphabetical order, which may not be the one you prefer as the default.
One solution i have come up with is to acctually just assemble the specifics and not run assemble. Which assembles all.
Try out gradle tasks to see the possibilites
But im looking for a better solution too, one that can be defined in the gradle.build file.