Reputation: 13668
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?
Upvotes: 66
Views: 35909
Reputation: 76669
For the sake of simplicity, one should better use the base
plugin.
This Kotlin script reads String
from gradle/libs.versions.toml
:
[versions]
app_versionName = "1.0.0"
And applies it to all the output packages, no matter if APK or AAB ...
base {
val versionName: String = libs.versions.app.versionName.get()
archivesName = "SomeApp_$versionName"
}
android { ... }
Which basically means, that it is not required to rename anything.
This also works fine with build-types and product-flavors, by default.
One can avoid some moving, when changing the value of buildDir
.
However, one can as well move and rename files, in case this should be required. I've wrote an Exec
task in Groovy for cross-platform CLI execution, no matter what the commandLine
is. My RenameTask
can detect Linux, Mac & 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
}
}
Upvotes: 9
Reputation: 46480
All the answers to date are using hardcoded paths and potential hacks. Here's a variant-aware solution using public APIs.
There's an API since at least AGP 4.2, but we can use the latest approach from this official recipe: https://github.com/android/gradle-recipes/tree/agp-8.4/getSingleArtifact
Based on this, we can use the following snippet to do the renaming using only Public APIs:
androidComponents.onVariants { variant ->
val copy = tasks.register<Copy>("copy${variant.name.capitalize()}Bundle") {
from(variant.artifacts.get(SingleArtifact.BUNDLE))
into(project.layout.buildDirectory.dir("outputs/bundle-variant"))
rename { "app-${variant.name}.aab" }
}
// TODO replace with tasks.named { it == ... }.configure { ... } in Gradle 8.6.
tasks.configureEach { if (this.name == "bundle${variant.name.capitalize()}") finalizedBy(copy) }
}
Upvotes: 0
Reputation: 7759
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:
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.
Upvotes: 16
Reputation: 825
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:
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
.
Upvotes: 13
Reputation: 4511
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.
Upvotes: 0
Reputation: 204
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
Upvotes: 2
Reputation: 34532
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)
}
}
Upvotes: 31
Reputation: 1029
You could use something like this:
defaultConfig {
applicationId "com.test.app"
versionCode 1
versionName "1.0"
setProperty("archivesBaseName", applicationId + "-v" + versionCode + "(" + versionName + ")")
}
Upvotes: 92