How to access variant.outputFileName in Kotlin

How to access variant.outputFileName in Kotlin

absolute path are not supported when setting an output file name
gradle kotlin
gradle output filename
kotlin dsl android
applicationvariants all example
android gradle dependencies
gradle tips and tricks for android
expected a name but was string at line 1 column 99 path $(0 apkinfo versionname)

We've been using a snippet like this one to rename the APK file generated by our Gradle build:

android.applicationVariants.all { variant ->
    variant.outputs.all {
        outputFileName = "${variant.name}-${variant.versionName}.apk"
    }
}

Source: https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration#variant_output

I am now in the process of converting my build.gradle to build.gradle.kts, i. e. to the Gradle Kotlin DSL. This is one of the last missing pieces: I can't figure out how to access outputFileName.

According to the API docs it does not even seem to exist:

  • BaseVariant.getOutputs() returns a DomainObjectCollection<BaseVariantOutput> which provides the all method used in the snippet.
  • BaseVariantOutput extends OutputFile which extends VariantOutput but none of these has an outputFileName or any getters or setters of a matching name.

So, I suspect there is some advanced Groovy magic at work to make this work - but how do I get there in Kotlin?


Browsing through the source code of the Android Gradle plugin, I think I found the answer - here we go:

We are actually dealing with objects of type BaseVariantOutputImpl and this class does have both these methods:

public String getOutputFileName() {
    return apkData.getOutputFileName();
}

public void setOutputFileName(String outputFileName) {
    if (new File(outputFileName).isAbsolute()) {
        throw new GradleException("Absolute path are not supported when setting " +
                    "an output file name");
    }
    apkData.setOutputFileName(outputFileName);
}

Using this knowledge we can now:

import com.android.build.gradle.internal.api.BaseVariantOutputImpl

and then cast our target objects like so:

applicationVariants.all(object : Action<ApplicationVariant> {
    override fun execute(variant: ApplicationVariant) {
        println("variant: ${variant}")
        variant.outputs.all(object : Action<BaseVariantOutput> {
            override fun execute(output: BaseVariantOutput) {

                val outputImpl = output as BaseVariantOutputImpl
                val fileName = output.outputFileName
                        .replace("-release", "-release-v${defaultConfig.versionName}-vc${defaultConfig.versionCode}-$gitHash")
                        .replace("-debug", "-debug-v${defaultConfig.versionName}-vc${defaultConfig.versionCode}-$gitHash")
                println("output file name: ${fileName}")
                outputImpl.outputFileName = fileName
            }
        })
    }
})

So, I guess: Yes, there is some Groovy magic at work, namely that Groovy's dynamic type system allows you to just access getOutputFileName and setOutputFileName (by way of the abbreviated outputImpl.outputFileName syntax, as in Kotlin) from your code, hoping they will be there at runtime, even if the compile time interfaces that you know about don't have them.

Replace file content in Kotlin DSL - gradle - Python, or any getters or setters of a matching name. So, I guess: Yes, there is some Groovy magic at work, namely that Groovy's dynamic type system allows you to just access getOutputFileName and setOutputFileName (by way of the abbreviated outputImpl.outputFileName syntax, as in Kotlin) from your code, hoping they will be there at runtime, even if the compile time interfaces that you know about


A little simplified version of @david.mihola answer:

android {

    applicationVariants.all {
        val variant = this
        variant.outputs
                .map { it as BaseVariantOutputImpl }
                .forEach { output ->
                    output.outputFileName = output.outputFileName
                            .replace("app-", "FooBar-")
                            .replace(".apk", "-${variant.versionName}.${variant.versionCode}.apk")
                }
    }

}

Android Studio 3.0, How to access variant.outputFileName in Kotlin. We've been using a snippet like this one to rename the APK file generated by our Gradle build: android. List Specific Operations. List is the most popular type of built-in collection in Kotlin. Index access to the elements of lists provides a powerful set of operations for lists. Retrieving elements by index. Lists support all common operations for element retrieval: elementAt(), first(), last(), and others listed in Retrieving Single Elements


Shorter version using lambdas:

    applicationVariants.all{
        outputs.all {
            if(name.contains("release"))
                (this as BaseVariantOutputImpl).outputFileName = "../../apk/$name-$versionName.apk"
        }
    }

This will place APK into app/apk folder with name made of variant name and version code. You can change the format of filename as you wish. Important: it must be done only on release builds, because ".." in path corrupts debug build process with strange errors.

Name your .apk & .aab files - Giorgos Neokleous, How to access variant.outputFileName in Kotlin. We've been using a snippet like this one to rename the APK file generated by our Gradle build: android. Access elements of Array in Kotlin . There are 2 ways to access elements of array in kotlin and perform some operations on them. A. Using indexing property. B. Using get() functions. A. Access elements of array in kotlin using indexing property. Let’s say we have an array (named arr) of size n. Each element in array has an index.


Building Multiplatform Projects with Gradle, Sign up and get an extra one for free. android.applicationVariants.all { variant -​> variant.outputs.each { output -> outputFile' to 'outputFileName' Managing Android Multi-module Project with Gradle Plugin and Kotlin. Kotlin array reduction Reduction is a terminal operation that aggregates array values into a single value. The reduce() method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.


Producing better named Android APKs with Gradle, it was not working. applicationVariants.all { variant -> variant.outputs.all { outputFileName Migrating Android build scripts from Groovy to Kotlin DSL · Oussama A button that says 'Get it on, Google Play', and if clicked. Stack Overflow Public questions and answers; Teams Private questions and answers for your team; Enterprise Private self-hosted questions and answers for your enterprise; Jobs Programming and related technical career opportunities


Build variants, Gradle tasks, and Kotlin , To create or access several targets from multiple presets dynamically, you can use To publish artifacts produced by a set of Android variants, specify the variant By default the output file name is based on binary name prefix or, if the name  The lateinit keyword promises the Kotlin compiler that the variable will be initialized before the code calls any operations on it. Therefore we don't need to initialize the variable to null here, and we can treat it as a non-nullable variable when we use it.