Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using version code and version name for renaming generated packages with archivesBaseName #9

Open
timmikk opened this issue Nov 16, 2020 · 10 comments

Comments

@timmikk
Copy link

timmikk commented Nov 16, 2020

In my project I need to rename the generated packages. Previously I have done it with following code in android defaultConfig block:

android {
    defaultConfig {
        versionCode = getMyVersionCode()
        versionName = VERSION_NAME
        archivesBaseName = "${versionName}-vc${versionCode}"
    }
}

Now as I took the app-versioning plugin into use (actually the 0.4.0 version) I removed the versionCode and versionName definitions:

android {
    defaultConfig {
        archivesBaseName = "${versionName}-vc${versionCode}"
    }
}

The problem is that the versionName and versionCode are now null and package name will be something like null-vcnull-release.apk

What might be best practice to rename the package when using the app-versioning plugin? Is there some way to get the versionName and versionCode from the app-versioning plugin in build.gradle during build?

@ychescale9
Copy link
Member

ychescale9 commented Nov 17, 2020

This is unfortunately not straightforward with the new Variant APIs in AGP 4.1+, where the focus is lazy task configuration.

The following is a response from Xavier Ducrohet from the AGP team:

Regarding the renaming of APKs, the new API is not going to allow you to rename the app to something else. If you care about a particular name you should create a task that copies it wherever you want with the right names. This task should then consume versionCode/Name (as Provider) and use that. This will ensure proper task dependencies. If you have other needs please let us know so that we can decide this. The reason for this change is that renaming APKs when dealing with multi-APKs is non trivial and in most cases (when building/deploying from Studio) you really don't care about the name, so it should be a CI-only thing

I put together the following custom task (based on the AGP recipe) to copy the raw APK to app/build/outputs/renamed_apk/MyApp-<versionName>-<versionCode>.apk:

android {
    onVariantProperties {
        val mainOutput = outputs.single { it.outputType == VariantOutputConfiguration.OutputType.SINGLE }
        tasks.register<CreateRenamedApk>("createRenamedApkFor${name}") {
            this.originalApkFolder.set(artifacts.get(ArtifactType.APK))
            this.builtArtifactsLoader.set(artifacts.getBuiltArtifactsLoader())
            this.newApkFolder.set(layout.buildDirectory.dir("outputs/renamed_apk/${this@onVariantProperties.name}"))
            this.versionCode.set(mainOutput.versionCode)
            this.versionName.set(mainOutput.versionName)
        }
    }
}


import com.android.build.api.variant.BuiltArtifactsLoader
import java.io.File
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction

abstract class CreateRenamedApk : DefaultTask() {

    @get:InputFiles
    abstract val originalApkFolder: DirectoryProperty

    @get:OutputDirectory
    abstract val newApkFolder: DirectoryProperty

    @get:Internal
    abstract val builtArtifactsLoader: Property<BuiltArtifactsLoader>

    @get:Input
    abstract val versionCode: Property<Int>

    @get:Input
    abstract val versionName: Property<String>

    @TaskAction
    fun taskAction() {
        val builtArtifacts = builtArtifactsLoader.get().load(originalApkFolder.get()) ?: throw RuntimeException("Cannot load APKs")
        File(builtArtifacts.elements.single().outputFile).copyTo(
            File(newApkFolder.asFile.get(), "MyApp-${versionName.get()}-${versionCode.get()}.apk")
        )
    }
}

If your build variant is prodRelease with versionCode 123 and versionName "1.2.3" , running ./gradlew createRenamedApkForProdRelease will assemble the prodRelease APK, copy it with the new path / name app/build/outputs/renamed_apk/prodRelease/MyApp-1.2.3-123.apk.

Note that these APIs are all incubating so there might be easier ways to do it in the future. In the meantime adding this custom task to your buildSrc seems like the best approach.

Update: simplified task to do a simple copy instead of an artifact transform.

@ThanosFisherman
Copy link

How can I add the task above into my buildSrc file? To be specific BuiltArtifactsLoader cannot be resolved.

@ychescale9
Copy link
Member

This is the import:

import com.android.build.api.variant.BuiltArtifactsLoader

Updated snippet above with all the imports required.

@ThanosFisherman
Copy link

ThanosFisherman commented Oct 2, 2021

Thanks @ychescale9 I see that BuiltArtifactsLoader comes from AGP so I would need to add this dependency to my buildSrc build.gradle.kts file something that seems surprisingly difficult to do it right in an Android project at least on my end.

If you already have such implementation in any of your projects here I'd appreciate if you could point me to it. Otherwise no worries I'll figure it out hopefully.

@ychescale9
Copy link
Member

This is a sample project that has AGP dependencies in buildSrc. I basically just add implementation(agp) to the buildSrc/build.gradle.kts.

@ThanosFisherman
Copy link

Your sample helped me a lot plus some nice kotlin script tricks demonstrated in there however I just realized that onVariantProperties is no longer available on AGP 4.2.2 (or I'm completely blind at that moment) which I'm currently using and I can't even use artifacts.get the way it's presented here. Generally a lot of things have changed and it's kinda frustrating.

What I ended up doing in order to rename my apk artifacts is simply translate an old groovy snippet into kotlin like so.

  import com.android.build.gradle.api.ApplicationVariant
  import com.android.build.gradle.api.BaseVariantOutput
  import com.android.build.gradle.internal.api.ApkVariantOutputImpl

   // use the code below inside android{} block

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

fun setVariantOutputName(output: BaseVariantOutput, variant: ApplicationVariant) {
    
    val outputVariant = (output as ApkVariantOutputImpl) // I just blindly copied this and it worked.
    if (outputVariant.outputFileName.endsWith(".apk")) {
        outputVariant.outputFileName =
            "MyApp-" + variant.versionName + "-" + output.versionCode + ".apk"
    }
}

Most of the time I have no idea what I'm doing with gradle but this seemed to work in my case.

@koolfreak
Copy link

Your sample helped me a lot plus some nice kotlin script tricks demonstrated in there however I just realized that onVariantProperties is no longer available on AGP 4.2.2 (or I'm completely blind at that moment) which I'm currently using and I can't even use artifacts.get the way it's presented here. Generally a lot of things have changed and it's kinda frustrating.

What I ended up doing in order to rename my apk artifacts is simply translate an old groovy snippet into kotlin like so.

  import com.android.build.gradle.api.ApplicationVariant
  import com.android.build.gradle.api.BaseVariantOutput
  import com.android.build.gradle.internal.api.ApkVariantOutputImpl

   // use the code below inside android{} block

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

fun setVariantOutputName(output: BaseVariantOutput, variant: ApplicationVariant) {
    
    val outputVariant = (output as ApkVariantOutputImpl) // I just blindly copied this and it worked.
    if (outputVariant.outputFileName.endsWith(".apk")) {
        outputVariant.outputFileName =
            "MyApp-" + variant.versionName + "-" + output.versionCode + ".apk"
    }
}

Most of the time I have no idea what I'm doing with gradle but this seemed to work in my case.

This strategy is not anymore working on Giraffe? any other updated solution? thanks

@ychescale9
Copy link
Member

What's not working on Giraffe?

AFAIK there's still no official way of renaming apk, so the gist above should work otherwise it would be a bug that we should report.

@wavky
Copy link

wavky commented Jun 5, 2024

This is how I did it in my new project for 2024:

android {
  tasks.whenTaskAdded {
    val newFileName = when (name) {
      "packageDebug" -> "${baseName}_${debug}_${apkVersionName()}"
      "packageBeta" -> "${baseName}_${beta}_${apkVersionName()}"
      "packageStaging" -> "${baseName}_${staging}_${apkVersionName()}"
      "packageRelease" -> "${baseName}_${apkVersionName()}"
      else -> return@whenTaskAdded
    }
    doLast {
      val apkFile = outputs.files.files.last().listFiles().find { it.extension == "apk" }
        ?: throw RuntimeException("APK file not found")
      apkFile.renameTo(File(apkFile.parent, "$newFileName.apk"))
    }
  }
}

@fahimfarhan
Copy link

Your sample helped me a lot plus some nice kotlin script tricks demonstrated in there however I just realized that onVariantProperties is no longer available on AGP 4.2.2 (or I'm completely blind at that moment) which I'm currently using and I can't even use artifacts.get the way it's presented here. Generally a lot of things have changed and it's kinda frustrating.

What I ended up doing in order to rename my apk artifacts is simply translate an old groovy snippet into kotlin like so.

  import com.android.build.gradle.api.ApplicationVariant
  import com.android.build.gradle.api.BaseVariantOutput
  import com.android.build.gradle.internal.api.ApkVariantOutputImpl

   // use the code below inside android{} block

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

fun setVariantOutputName(output: BaseVariantOutput, variant: ApplicationVariant) {
    
    val outputVariant = (output as ApkVariantOutputImpl) // I just blindly copied this and it worked.
    if (outputVariant.outputFileName.endsWith(".apk")) {
        outputVariant.outputFileName =
            "MyApp-" + variant.versionName + "-" + output.versionCode + ".apk"
    }
}

Most of the time I have no idea what I'm doing with gradle but this seemed to work in my case.

I ran into same problem, and this solution worked for me! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants