I have a task that updates my app's version code, called changeVersionCode. This task runs before my Android build tasks (assembleRelease), but obviously it happens after the android { } closure. This seems to mean that versionCode is set and cannot be changed even when changeVersionCode runs.
Here's a simplified build script that demonstrates how I have tried to approach this problem:
// .version is a file that starts at "1" the first time I call this
def loadVersionCode() {
// fetch version code from a file, '.version'
return loadVersionCodeFromFile('.version')
}
def incrementVersionCode() {
// fetch version code, update it, and save it back to a file
def newVersion = loadVersionCode() + 1
saveVersionCodeToFile('.version', newVersion)
}
apply plugin: 'com.android.application'
android {
// ... snip ...
defaultConfig {
// Set the version code to contents of .version (eg. 1)
versionCode loadVersionCode()
// ...
}
}
task incrementVersionCode << {
println "Old version code: " + android.defaultConfig.versionCode // prints 1
incrementVersionCode()
def newVersion = loadVersion() // this now returns 2
android.defaultConfig.versionCode = loadVersionCode()
// Also tried:
// android.defaultConfig.versionCode loadVersionCode()
println "New version code: " + android.defaultConfig.versionCode // prints 2
// android.defaultConfig.versionCode is now 2, but APK still has version 1 (until next time I run gradle)
}
Then:
# Build an APK with versionCode 1
$ ./gradlew assembleRelease
# This seems to change versionCode to 2, but still builds an APK with versionCode 1
#
# Prints:
# Old version code: 1
# New version code: 2
$ ./gradlew incrementVersionCode assembleRelease
I am using:
Gradle 2.5
Groovy 2.3.10
Ant 1.9.3
Java 1.8.0_45
Mac OS X 10.10.5
Android build tools 22.0.1
Is there any way I can change my version code from a task before invoking Android build tasks?
How to configure versionCode before a task is launched
You can use the DSL tasks.whenTaskAdded. You can read the official doc, chapter 58.6.2. Task creation.
You can receive a notification immediately after a task is added to a project. This can be used to set some default values or add behaviour before the task is made available in the build file.
You can define a task:
task incrementVersionCode << {
//do something
}
Then define the dependency :
tasks.whenTaskAdded { task ->
if (task.name == 'xxxxx') {
task.dependsOn incrementVersionCode
}
}
In your case you can do somenthing like this:
tasks.whenTaskAdded { task ->
if (task.name == 'generateReleaseBuildConfig' || task.name == 'generateDebugBuildConfig') {
task.dependsOn 'increaseVersionCode'
}
}
How to configure versionCode with a function
In the top-level file you can configure a function like this:
ext {
buildVersionCode = {
//...
}
}
In your module/build.gradle you can do somehing like this:
defaultConfig {
versionCode buildVersionCode()
//....
}
Otherwise you can do in your build.gradle something like:
defaultConfig {
//...
versionCode getMyNumber()
}
def getMyNumber() {
return //.... ;
}
Related
In my sub-project that builds an android app, i'm attempting to set the version code / name based on variables in my root build.gradle.
sub-project build.gradle:
apply plugin: 'com.android.application'
repositories {
google()
mavenCentral()
jcenter()
}
android {
compileSdkVersion 24
// current atak build tools version"
buildToolsVersion "28.0.3"
defaultConfig {
multiDexEnabled true
versionCode project.commit_head_count
versionName project.full_version
}
lintOptions {
abortOnError false
}
}
// set the build info prior to building
build.dependsOn rootProject.setBuildInfo
root build.gradle:
// gathers git / build information and dumps it to VERSION files to be read by web app and data analysis program
task setBuildInfo() {
doLast {
// read the core version file and store in variable
def coreVersionFile = new File("$projectDir/VERSION.txt")
def coreVersion = coreVersionFile.readLines().get(0)
// get the git hash value (short)
def getShortGitHashCmd = "git rev-parse --short HEAD"
def getShortGitHashProcess = getShortGitHashCmd.execute()
// get the count of commits on this branch at HEAD
def getCommitCountCmd = "git rev-list HEAD --count"
def getCommitCountProcess = getCommitCountCmd.execute()
ext.commit_head_count = getCommitCountProcess.text.trim()
ext.git_hash = getShortGitHashProcess.text.trim()
ext.full_version = "$coreVersion.$ext.commit_head_count"
ext.build_date = new Date().format('yyyy-MM-dd HH:mm:ss')
// assigns the full_version for global use in other task
// https://stackoverflow.com/a/29597784/680268
project.ext.$full_version = ext.full_version
project.ext.$commit_head_count = ext.commit_head_count
def fileContent =
"Short Version:$coreVersion\n" +
"Long Version:$ext.full_version\n" +
"Git hash:$ext.git_hash\n" +
"Commit count:$ext.commit_head_count\n" +
"Build date:$ext.build_date\n"
print fileContent
}
}
// set the build info prior to building
compileJava.dependsOn setBuildInfo
When this executes, the android app sub-project says that it doesn't know what the commit_head_count variable is referring to. I feel like if I could actually get setBuildInfo to run first, it would work, but can't get that to work
Use below code snippet to execute your custom task when some other task executed:
tasks.matching { it.name == 'name of dependent task (i.e build)' }.all { Task task ->
task.dependsOn setBuildInfo
}
This would make all other matching tasks force to be dependent of your custom task.
Or other solution would be making your build info when project gets evaluated.
Put this code below in your root level build.gradle and check the result:
project.afterEvaluate {
print("This line prints every single time")
}
Gradle changed it's behavior when I updated from 5.0 to 5.1.
Lets assume that we have an android project with single module named library.
On Gradle 5.0 (and previous versions) when I executed ./gradlew assembleRelease or ./gradlew assembleDebug generated output was library-release.aar or library-debug.aar respectively.
After I updated to Gradle 5.1 (I also tried 5.1.1) it generates only library.aar for any build type without any build-type classifier in output file name.
So my question is: how can I force Gradle 5.1 to set correct output file naming for different build types like it was before? Below is my library module's build.gradle.kts, but I doesn't think that there is something wrong with it:
import org.jetbrains.kotlin.config.KotlinCompilerVersion
plugins {
id("com.android.library")
id("kotlin-android")
id("maven-publish")
}
val libraryVersion = "1.5.0"
android {
compileSdkVersion(28)
defaultConfig {
minSdkVersion(16)
targetSdkVersion(28)
versionCode = 1
versionName = libraryVersion
}
lintOptions {
isAbortOnError = false
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
}
}
dependencies {
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
implementation(kotlin("stdlib-jdk7", KotlinCompilerVersion.VERSION))
}
UPDATE:
If I add a following code to build.gradle.kts:
afterEvaluate {
android.libraryVariants.forEach { libraryVariant ->
libraryVariant.outputs.forEach { output ->
println(output.outputFile.name)
}
}
}
It will print:
library-debug.aar
library-release.aar
Now it starts looking like a bug in gradle 5.1
So as we can see in https://github.com/gradle/gradle/issues/8328, it is a bug which is fixed in Android Gradle Plugin 3.4.
libraryVariants.all { variant ->
variant.outputs.all { output ->
def filePrefix = "$buildDir/outputs/aar/$archivesBaseName"
outputFileName = "${archivesBaseName}-${version}.aar"
def fileSuffix = "aar"
def originalFile = file("$filePrefix-${variant.buildType.name}.$fileSuffix")
def renamedFile = "$filePrefix-${variant.buildType.name}-$version.$fileSuffix"
tasks.named("assemble").configure {
doLast {
originalFile.renameTo(renamedFile)
}
}
}
}
where archivesBaseName and version can defined in default block of libraries build.gradle
So to change the generated APK filename inside gradle android I could do something like:
applicationVariants.output.all {
outputFileName = "the_file_name_that_i_want.apk"
}
Is there a similar thing for the generated App Bundle file? How can I change the generated App Bundle filename?
You could use something like this:
defaultConfig {
applicationId "com.test.app"
versionCode 1
versionName "1.0"
setProperty("archivesBaseName", applicationId + "-v" + versionCode + "(" + versionName + ")")
}
As a more generic way to Martin Zeitlers answer the following will listen for added tasks, then insert rename tasks for any bundle* task that gets added.
Just add it to the bottom of your build.gradle file.
Note: It will add more tasks than necessary, but those tasks will be skipped since they don't match any folder. e.g. > Task :app:renameBundleDevelopmentDebugResourcesAab NO-SOURCE
tasks.whenTaskAdded { task ->
if (task.name.startsWith("bundle")) {
def renameTaskName = "rename${task.name.capitalize()}Aab"
def flavor = task.name.substring("bundle".length()).uncapitalize()
tasks.create(renameTaskName, Copy) {
def path = "${buildDir}/outputs/bundle/${flavor}/"
from(path)
include "app.aab"
destinationDir file("${buildDir}/outputs/renamedBundle/")
rename "app.aab", "${flavor}.aab"
}
task.finalizedBy(renameTaskName)
}
}
Solution from #SaXXuM works great! Task is not necessary for renaming artifact. You can call setProperty() directly in the android {} block. I prefer to have in the file name:
app id
module name
version name
version code
date
build type
This is how I use it in my projects:
build.gradle:
apply from: "../utils.gradle"
android {
...
setProperty("archivesBaseName", getArtifactName(defaultConfig))
}
utils.gradle:
ext.getArtifactName = {
defaultConfig ->
def date = new Date().format("yyyyMMdd")
return defaultConfig.applicationId + "-" + project.name + "-" + defaultConfig.versionName + "-" + defaultConfig.versionCode + "-" + date
}
The result is:
com.example-app-1.2.0-10200000-20191206-release.aab
It works for both - APK and AAB.
Now I've wrote kind of a Exec template for cross-platform CLI execution, no matter what the commandLine is. My RenameTask can detect Linux & Windows, as well as release & debug.
Property archivesBaseName needs to be defined in defaultConfig:
android {
defaultConfig {
setProperty("archivesBaseName", "SomeApp_" + "1.0.0")
}
}
RenameTask extends Exec performs the renaming (not to be confused with type: Rename):
import javax.inject.Inject
/**
* App Bundle RenameTask
* #author Martin Zeitler
**/
class RenameTask extends Exec {
private String buildType
#Inject RenameTask(String value) {this.setBuildType(value)}
#Input String getBuildType() {return this.buildType}
void setBuildType(String value) {this.buildType = value}
#Override
#TaskAction
void exec() {
def baseName = getProject().getProperty('archivesBaseName')
def basePath = getProject().getProjectDir().getAbsolutePath()
def bundlePath = "${basePath}/build/outputs/bundle/${this.getBuildType()}"
def srcFile = "${bundlePath}/${baseName}-${this.getBuildType()}.aab"
def dstFile = "${bundlePath}/${baseName}.aab"
def os = org.gradle.internal.os.OperatingSystem.current()
if (os.isUnix() || os.isLinux() || os.isMacOsX()) {
commandLine "mv -v ${srcFile} ${dstFile}".split(" ")
} else if (os.isWindows()) {
commandLine "ren ${srcFile} ${dstFile}".split(" ")
} else {
throw new GradleException("Cannot move AAB with ${os.getName()}.")
}
super.exec()
}
}
And it finalizes two other tasks:
// it defines tasks :renameBundleRelease & :renameBundleDebug
task renameBundleRelease(type: RenameTask, constructorArgs: ['release'])
task renameBundleDebug(type: RenameTask, constructorArgs: ['debug'])
// it sets finalizedBy for :bundleRelease & :bundleDebug
tasks.whenTaskAdded { task ->
switch (task.name) {
case 'bundleRelease': task.finalizedBy renameBundleRelease; break
case 'bundleDebug': task.finalizedBy renameBundleDebug; break
}
}
The advance is, that it leaves nothing behind and one can move the files wherever one wants.
Why no one is using existing gradle tasks for this?
There is a gradle task with the type FinalizeBundleTask and it is called as the last step of bundle generation and it is doing two things:
Signing generated AAB package
Move and rename AAB package where was requested
All You need to do is just to change the "output" of this task to any that You want. This task contains a property finalBundleFile - full path to the final AAB package.
I'm using it something like that:
applicationVariants.all {
outputs.all {
// AAB file name that You want. Falvor name also can be accessed here.
val aabPackageName = "$App-v$versionName($versionCode).aab"
// Get final bundle task name for this variant
val bundleFinalizeTaskName = StringBuilder("sign").run {
// Add each flavor dimension for this variant here
productFlavors.forEach {
append(it.name.capitalizeAsciiOnly())
}
// Add build type of this variant
append(buildType.name.capitalizeAsciiOnly())
append("Bundle")
toString()
}
tasks.named(bundleFinalizeTaskName, FinalizeBundleTask::class.java) {
val file = finalBundleFile.asFile.get()
val finalFile = File(file.parentFile, aabPackageName)
finalBundleFile.set(finalFile)
}
}
}
It works perfectly with any flavors, dimensions, and buildTypes. No any additional tasks, works with any path set for output in Toolbar -> Generate signed Bundle, a unique name can be set for any flavor.
I've found a much better option to auto increment your app versioning and auto renaming when you generate an apk / aab. Solution as below (do remember to create "version.properties" file on your root folder:
android {
...
...
Properties versionProps = new Properties()
def versionPropsFile = file("${project.rootDir}/version.properties")
versionProps.load(new FileInputStream(versionPropsFile))
def value = 0
def runTasks = gradle.startParameter.taskNames
if ('assemble' in runTasks || 'assembleRelease' in runTasks) {
value = 1
}
def versionMajor = 1
def versionPatch = versionProps['VERSION_PATCH'].toInteger() + value
def versionBuild = versionProps['VERSION_BUILD'].toInteger() + 1
def versionNumber = versionProps['VERSION_NUMBER'].toInteger() + value
versionProps['VERSION_PATCH'] = versionPatch.toString()
versionProps['VERSION_BUILD'] = versionBuild.toString()
versionProps['VERSION_NUMBER'] = versionNumber.toString()
versionProps.store(versionPropsFile.newWriter(), null)
defaultConfig {
applicationId "com.your.applicationname"
versionCode versionNumber
versionName "${versionMajor}.${versionPatch}.${versionBuild}(${versionNumber})"
archivesBaseName = versionName
minSdkVersion 26
targetSdkVersion 29
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
signingConfig signingConfigs.release
setProperty("archivesBaseName","${applicationId}-v${versionName}")
...
}
Credits to this website and this post
Based on Martin Zeitler's answer I did this on Windows:
Please note that on my setup, .aab files are created in release folder and it deletes everything else in that folder as per this bug report.
In my app's module gradle:
apply from: "../utils.gradle"
...
tasks.whenTaskAdded { task ->
switch (task.name) {
case 'bundleRelease':
task.finalizedBy renameBundle
break
}
}
And in utils.gradle:
task renameBundle (type: Exec) {
def baseName = getProperty('archivesBaseName')
def stdout = new ByteArrayOutputStream()
def stderr = new ByteArrayOutputStream()
commandLine "copy.bat", rootProject.getProjectDir().getAbsolutePath() + "\\release\\${baseName}-release.aab", "<MY_AAB_PATH>\\${baseName}.aab", "D:\\Android\\studio\\release"
workingDir = rootProject.getProjectDir().getAbsolutePath()
ignoreExitValue true
standardOutput stdout
errorOutput stderr
doLast {
if (execResult.getExitValue() == 0) {
println ":${project.name}:${name} > ${stdout.toString()}"
} else {
println ":${project.name}:${name} > ${stderr.toString()}"
}
}
}
The copy.bat is created in project's folder and contains this:
COPY %1 %2
RMDIR /Q/S %3
Be careful with 3rd argument to make sure you don't use a folder that's important to you.
EDIT: Why a .BAT for 2 commands you might ask. If you try commandLine "copy", ... on Windows it results in "system does not recognize the command copy". Put anything, like COPY, REN, RENAME, etc, won't work.
I'm in the early stages of migrating an android project to gradle, using the experimental plugin 0.4.0
As part of the build process I have a number of scripts that should run prior to compiling my code / building the apk. The arguments or tasks themselves will be different for a debug or release build.
I'm struggling to find a straight forward way to achieve this.
I've stripped everything back to a simple hello world project while I figure this out.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.4.0'
}
}
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
}
android.buildTypes {
debug {
//
}
release {
//runProguard, sign etc.
}
}
android.productFlavors {
create("free")
{
applicationId = "com.example.app.free"
versionName = "1.0-free"
}
create("full")
{
applicationId = "com.example.app.full"
versionName = "1.0-full"
}
}
}
repositories {
jcenter()
}
dependencies {
compile 'joda-time:joda-time:2.7'
}
task runScript(type: Exec) {
executable "sh"
args "-c", "echo SomeScriptHere"
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn runScript
}
task wrapper(type: Wrapper) {
gradleVersion = '2.5'
}
Ideally I want to differentiate by buildType ( not productFlavor ) as the tasks required as based on the type of build not the variety of app produced.
Is it possible to specify that a task should only run on a release or debug build?
Alternatively is it possible to define different arguments to be used for my runScript task based on being a release or debug build?
Apologies if I'm missing something obvious, I'm pretty new to using gradle.
using onlyIf() would be one option gradle docs here but having to define properties seemed awkward especially as a project gets larger and more complicated.
ZXStudio has a good blog post / example here where instead of using properties or rules will iterate over the existing tasks and create a new task based on the buildType / flavor.
So for my original question the answer mean removing the runScript task above and replacing tasks.withType(JavaCompile) as follows;
Could also be extended to match build flavors and create tasks appropriately.
tasks.withType(JavaCompile) {
def newTaskName = "runScript_" + name;
def isDebug = false;
if( name.contains("Debug") )
{
isDebug = true;
}
//Create a new task
tasks.create( newTaskName, Exec ) {
if( isDebug )
{
executable "sh"
args "-c", "echo this is a DEBUG task"
}
else
{
executable "sh"
args "-c", "echo this is a RELEASE task"
}
}
dependsOn newTaskName
}
I'm trying to emulate Maven release plugin in Android by using a customized version of gradle-release plugin: https://github.com/townsfolk/gradle-release
The interesting steps are:
Check uncommitted changes
Step version code and remove -SNAPSHOT
suffix from version name
Build
Step version name and add -SNAPSHOT
suffix for next development version
However the generated APK always has the previous versions (i.e. 1.0.0-SNAPSHOT instead of 1.0.0).
Version numbers are stored and correctly updated in gradle.properties, so I'm assuming that I need to update the versions in the data model as well for the changes to take effect.
My android plugin config:
defaultConfig {
versionCode versionCode as int // taken from gradle.properties
versionName versionName // taken from gradle.properties
minSdkVersion 10
targetSdkVersion 19
}
Things I tried:
preBuild << {
android.applicationVariants.each { variant ->
variant.versionName = versionName
}
}
But there's no versionName in a variant.
preBuild << {
android.buildTypes.each { type ->
type.versionName = versionName
}
}
But there's no versionName in a type.
preBuild << {
android.productFlavors.each { flavor ->
flavor.versionName = versionName
}
}
But there are no flavors in my app (plain debug and release build types only).
My alternative is to write a bash/bat script to step the versions before invoking Gradle, which pretty much defeats the purpose of using Groovy to improve build customization.
How can I update versions dynamically in the Android Gradle plugin in the execution phase?
That's what buildTypes are for. What you're describing is a release build, IMO.
Here's an example: when executing assembleDebug it will give you a snapshot build, and executing assembleRelease will give you a clean build without any suffix and incremented version number. The next debug build will also use the incremented number.
The following is a fully functional build when the files are created in a folder. It should also work with flavors, but that's just a side product :). Gradle 2.2.1, Android plugin 1.1.3
build.gradle
apply plugin: 'com.android.application'
apply from: 'auto-version.gradle'
buildscript {
repositories { jcenter() }
dependencies { classpath 'com.android.tools.build:gradle:1.1.3' }
}
android {
buildToolsVersion = "21.1.2"
compileSdkVersion = "android-21"
buildTypes {
debug {
versionNameSuffix "-SNAPSHOT"
}
}
}
println "config code: ${calculateVersionCode()}, name: ${calculateVersionName()}"
src/main/AndroidManifest.xml
<manifest package="com.example" />
auto-version.gradle
ext {
versionFile = new File(project.rootDir, 'version.properties')
calculateVersionName = {
def version = readVersion()
return "${version['major']}.${version['minor']}.${version['build']}"
}
calculateVersionCode = {
def version = readVersion()
def major = version['major'] as int // 1..∞
def minor = version['minor'] as int // 0..99
def build = version['build'] as int // 0..999
return (major * 100 + minor) * 1000 + build
}
}
Properties readVersion() {
def version = new Properties()
def stream
try {
stream = new FileInputStream(versionFile)
version.load(stream)
} catch (FileNotFoundException ignore) {
} finally {
if (stream != null) stream.close()
}
// safety defaults in case file is missing
if(!version['major']) version['major'] = "1"
if(!version['minor']) version['minor'] = "0"
if(!version['build']) version['build'] = "0"
return version
}
void incrementVersionNumber() {
def version = readVersion()
// careful with the types, culprits: "9"++ = ":", "9" + 1 = "91"
def build = version['build'] as int
build++
version['build'] = build.toString()
def stream = new FileOutputStream(versionFile)
try {
version.store(stream, null)
} finally {
stream.close()
}
}
task incrementVersion {
description "Increments build counter in ${versionFile}"
doFirst {
incrementVersionNumber()
}
}
if (plugins.hasPlugin('android') || plugins.hasPlugin('android-library')) {
android {
defaultConfig {
versionName = calculateVersionName()
versionCode = calculateVersionCode()
}
afterEvaluate {
def autoIncrementVariant = { variant ->
if (variant.buildType.name == buildTypes.release.name) { // don't increment on debug builds
variant.preBuild.dependsOn incrementVersion
incrementVersion.doLast {
variant.mergedFlavor.versionName = calculateVersionName()
variant.mergedFlavor.versionCode = calculateVersionCode()
}
}
}
if (plugins.hasPlugin('android')) {
applicationVariants.all { variant -> autoIncrementVariant(variant) }
}
if (plugins.hasPlugin('android-library')) {
libraryVariants.all { variant -> autoIncrementVariant(variant) }
}
}
}
}
Execute gradle assembleDebug to build normally, gradle assembleRelease to increment and build, and gradle incrementVersion to just increment.
Note: be careful with gradle assemble because the order of assembleDebug and assembleRelease will yield different results.
Check the generated files in the build directory to see if the values are to your liking.
Manual execution (from comments)
It is possible you have multiple flavors in which case the version is incremented multiple times because multiple variants match the release build type. The original quesion was for no flavors. If you want to have more control when the version number is incremented just remove the afterEvaluate block and call the incrementVersion task whenever you want:
gradle incrementVersion assembleFreeRelease assemblePaidRelease
(The above manual execution is an untested idea.)
Check uncommitted changes
The "Check uncommitted changes" are not covered in this answer, that's another game. You could hook on to tasks.preBuild.doFirst { /*fail here if uncommited changes*/ } if I understand correctly. But that highly depends on your version control. Ask another question for more!
I needed to append current git commit count of code revision to the version name. Its real handy in many situation. I ended up with below simple gradle file
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
def gitCommitCount = "git rev-list HEAD --count".execute().text.trim()
defaultConfig {
applicationId "my.app.package.name"
minSdkVersion 16
targetSdkVersion 21
versionCode 6
versionName "0.8"
}
buildTypes {
debug {
versionNameSuffix ".${gitCommitCount}"
}
release {
versionNameSuffix ".${gitCommitCount}"
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
Similar to gitCommitCount, You can generate variables of your own to customise version name. As i am just executing a terminal command to store its result in a variable.
This doesn't directly address your question of how to completely change the versionName, but this is what I use to append a suffix for my buildTypes:
defaultConfig {
versionName "1.0"
}
buildTypes {
debug {
versionNameSuffix "-SNAPSHOT"
}
}
I just used Javanator's answer and modified it a bit so that commit count not only helps in changing the name but also makes sure that version code also remains unique. Here is a sample of what I did (Maybe a couple of things can be optimized, but nevertheless does the job for me) :
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
def gitCommitCount = "git rev-list HEAD --count".execute().text.trim().toBigInteger()
project.ext.set("versionCode", gitCommitCount)
project.ext.set("versionNameSuffix", "(${gitCommitCount})")
defaultConfig {
applicationId "my.app.package.name"
minSdkVersion 15
targetSdkVersion 25
versionCode project.versionCode
versionName "1.0"
versionNameSuffix project.versionNameSuffix
setProperty("archivesBaseName", "MyProject-$versionName")
....
}
signingConfigs {
config {
.........
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
}
}
packagingOptions {
.....
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(
output.outputFile.parent,
output.outputFile.name.replace(".apk", "-${variant.versionName}.apk"))
}
}
}
Edit :
The last bit could also be like
applicationVariants.all { variant ->
if (variant.name.contains('release')) {
variant.outputs.each { output ->
variant.outputs.all {
outputFileName = "MyProject-${variant.versionName}${variant.versionCode}.apk"
}
}
}
}
I was facing similar need of having separate build logic for release and non-release builds.
Apart from different versioning, I had to use a different set of dependencies, even different repositories.
None of the available plugins had all of the features that I needed, so I developed my own solution, based on simple approach - command line argument.
You can pass a command line parameter when invoking gradle build script like this:
gradle build -PmyParameter=myValue
or in my case
gradle build -PisRelease=true
Gradle will parse it, and it would automagically be available as a property of the project object.
You could then use it like this:
if (project.hasProperty('isRelease') && project.isRelease) {
// Here be the logic!
}
I extracted this logic into a separate plugin, and I've been successfully using it across different projects.
Although this doesn't answer your question directly, I hope I gave you another angle to think about the problem and another possible solution.
Late to this question but you can try the below way to attach the dynamic build suffix to the versionName in build.gradle .
def buildCode = (int)(((new Date().getTime()/1000) - 1451606400) / 10)
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
...
versionName "0.1.${buildCode}"
...
}
}
Both version name and version code can be generated dynamically as follows.
Comments in code.
/**
* computedVersionCode()
* do not name this to getVersionCode. getVersionCode conflicts with the automatic getter of versionCode
* version code is an int a value between 0 and max int value 2147483647 is expected.
* This function returns at int in yyyMMddHH format
* For example, 2022061121 for 11 June 2022 between 21:00 to 21:59
* This gives a new versioncode for every different hour of day and same code within same hour of hour of day
* Max int value is 2147483647. So after year 2147 it will overflow to -ve values.
* max value in year 2147 will be 2147121223 so Lot of scope of manually incrementing up-to 2147483647 will be there.
* #return an int corresponding to current hour in yyyyMMddHH format
*/
static def computedVersionCode() {
def date = new Date()
def formattedDate = date.format('yyyyMMddHH')
int versionCodeInt = (int) Long.parseLong(formattedDate)
return versionCodeInt
}
/**
* computedVersionCode2()
* do not name this to getVersionCode. getVersionCode conflicts with automatic getter of versionCode
* version code is an int a value between 0 and Max int value 2147483647 is expected.
* This function returns total hours since epoch
* For example, it returns 459711 for 11 June 2022 at 21:21 IST
* This gives a new versioncode for every different hour
* Max int value is 2147483647. This format is good till 09-Oct-246953 12:30:00 PM
*
* #return hours since epoch which can be used as version code
*/
static def computedVersionCode2() {
long millisSinceEpoch = System.currentTimeMillis();
long hoursSinceEpoch = millisSinceEpoch/(3600*1000);
int hoursSinceEpochInt = (int)hoursSinceEpoch;
//Hours since epoch changes every hour automatically.
//If data type int remains of same size forever this value will be good till year 4419.
return hoursSinceEpochInt;
}
static def computedVersionSuffix() {
def date = new Date()
def formattedDate = date.format('yyyyMMdd.HH.mm')
return formattedDate
}
android {
compileSdkVersion 32
defaultConfig {
...
versionCode computedVersionCode()
versionName "1.0.8.".concat(computedVersionSuffix())
....
}