I am trying to create a custom Gradle task for an Android project that will build both my flavours at the same time, rename the APK's and then copy them to a different folder.
The issue is I have another version of this task that is almost identical to the first one and when I run one task a part of the other task gets run too.
Here is my first task:
task buildWithVersion(type: GradleBuild) {
delete "$buildDir/outputs/apk"
delete "$buildDir/outputs/verionapks"
tasks = ['assembleMyAppDebug', 'assembleMyAppRelease']
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
outputFileName = "${variant.name}_${variant.versionName}.apk"
}
}
doLast {
copy {
from "$buildDir/outputs/apk"
into "$buildDir/outputs/verionapks"
include '**/release/*.apk'
}
}
}
And then the second one:
task buildWithoutVersion(type: GradleBuild) {
delete "$buildDir/outputs/apk"
delete "$buildDir/outputs/noneverionapks"
tasks = ['assembleMyAppDebug', 'assembleMyAppRelease']
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
outputFileName = "${variant.name}.apk"
}
}
doLast {
copy {
from "$buildDir/outputs/apk"
into "$buildDir/outputs/noneverionapks"
include '**/release/*.apk'
}
}
}
I need two seperate tasks for this but the problem is when I run the first task then the apks get renamed as if the second task has run, so the apk's are renamed in this format "${variant.name}.apk", instead of the format I wanted in the first task. (The second task is after the first in my apps build.gradle file)
It looks like the android.applicationVariants.all loop gets run every time even though I only want it to run inside the task when I call it. Why is this? And is it possible to only have this run specifically when a specific task is run?
Related
My Customtask unzips the apk and collects sha1 for some file, so I have created my task but I want to execute it every time any apk(any built or any variant) is generated, dont know how to trigger my task exactly after apk is created. Please help.
Some thing like the following
android {
task customTask {}
afterEvaluate {
package<VariantName>Release.finalizedBy(customTask)
}
}
After each of the variant apk is generated I need to run my custom task. I am able to do this for one variant by explicitly specifying the task name like
afterEvaluate {
packageDbRelease.finalizedBy(customTask)
}
But I have 5 more variants like packageARRelease etc after which I want to run my same custom task.
You need to create a task for each build variant:
android {
applicationVariants.all { variant
task "customTaskFor${variant.name.capitalize()}" {
// configure your task
}
tasks["package${variant.name.capitalize()}Release"].finalizedBy "customTaskFor${variant.name.capitalize()}"
}
}
I want to process generated .apk after gradle build. However, the place where the generated file resides depends on product flavor and build type. So how can I get those in the task?
Here's the relevant section of build.gradle:
productFlavors {
normal { }
tinybuild {}
}
// The following two blocks copy the generated .apk to server so that tablet you're working on can download it fast
task copyDebugAPK(type: Exec) {
workingDir "$projectDir/.."
commandLine 'python', 'myscript.py', 'script parameter'
}
afterEvaluate {
packageNormalDebug.finalizedBy(copyDebugAPK)
packageTinybuildDebug.finalizedBy(copyDebugAPK)
}
I could simply declare multiple tasks, one for each flavor / build, but I'm pretty sure that even using the script in flavor / build-type-dependent context should provide me with the relevant fields right within the task. Am I wrong?
Edit: after Robert's suggestion, I modified my task like so:
task copyDebugAPK(type: Exec) {
android.applicationVariants.all { variant ->
print("${com.android.build.OutputFile.ABI}")
print("${variant.name}")
print("${variant.flavorName}")
print("${variant.versionName}")
workingDir "$projectDir/.."
commandLine 'python', 'myscript.py', '${variant.name}'
}
}
However, no prints are being printed. I also added the variables to script input, but they are not resolved.
I have an Android Library, it's generating a debug.aar and a release.aar, I need to copy the release.aar to another folder as a reference to other part of the project.
What I've done now is in this Android Library build.gradle I defined a task:
task copyAARToCommonLibs(type: Copy) {
from('../build/outputs/aar') {
include '*-release.arr'
}
into '../SomeSampleApps/libs'
}
I'm trying to run this task after the arr is generated, which I assume is assembleRelease stage, so I tried do this in this build.gradle
assembleRelease.doLast{
copyAARToCommonLibs
}
I build the overall project using
gradle build
But this task is running at the very beginning of the whole process.
I also tried this:
applicationVariants.all { variant ->
variant.assemble.doLast {
copyAARToCommonLibs
}
}
inside android{} property(I guess that's what it's called?)
Running gradle build, got this error: Could not find property 'applicationVariants'
I then came across this snippet:
tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn copyAARToCommonLibs }
But it seems this makes the task to run after compiling, I don't know exactly how to modify this to run after assemble.
Could someone please correct me where I did wrong and how can I get this copy task work after the .arr file is generated?
It seems that finalizedBy might be helpful.
assembleRelease.finalizedBy(copyAARToCommonLibs)
Mind the fact that in the following way you won't define a dependency:
assembleRelease.doLast {
copyAARToCommonLibs
}
actually.. it does exactly nothing. You need to execute the task:
assembleRelease.doLast {
copyAARToCommonLibs.execute()
}
but running task in the following way is discouraged and very bad practice.
You can also try:
assembleRelease.doLast {
copy {
from('../build/outputs/aar') {
include '*-release.aar'
}
into '../AscendonSDKSamples/libs'
}
}
I went with finalizedBy() but had to include it within an afterEvaluate...
afterEvaluate {
if (gradle.startParameter.taskNames.contains(":app:assembleFatReleaseInternal")) {
play.enabled = true
play.commit = true
play.track = "internal"
play.releaseStatus = "completed"
play.releaseName = versionName
generateFatReleaseInternalBuildConfig.dependsOn set_build_date
assembleFatReleaseInternal.finalizedBy(uploadCrashlyticsSymbolFileFatReleaseInternal)
uploadCrashlyticsSymbolFileFatReleaseInternal.finalizedBy(publishFatReleaseInternal)
}
}
This worked well for automating the upload of native symbols to Fabric / Crashlytics and other things such as automated play store publishing.
Because android studio add task by dynamic,so assembleRelease will not be recognized.
Just add hook after task added event happens.
tasks.whenTaskAdded {
theTask ->
if (theTask.name.contains('externalNativeBuild')) {
theTask.doLast{
println "[*] begin to copy file."
}
}
// println theTask.name
}
In my project I have 3 product flavors (dev, production and testing). I've recently switched from Ant, so maybe I still have Ant mindset, but this is what I am trying to achieve:
create custom task (let's call it "buildAndUpload"), which will call assembleDevRelease and then call another task.
Here is what I've tried:
task myCustomTask {
println "My Custom Task"
}
task buildAndUpload {
tasks.assembleDevRelease.execute()
tasks.myCustomTask.execute()
}
When I call buildAndUpload task (gradlew buildAndUpload) I am getting following output:
:buildAndUpload
"My Custom Task"
As you can see assembleDevRelease task is not called. I know that I can use doLast closure, but I really want to have myCustomTask call contained in separate task.
A task cannot call another task, but it can depend on it. For example:
task myCustomTask {
dependsOn "assembleDevRelease"
doLast {
println "executing..."
}
}
You should set the two tasks as dependencies. You'll want to do something along the lines of
task buildAndUpload(dependsOn: ['assembleDevRelease', 'myCustomTask'] {
}
Also please refer to the Gradle documentation on the build lifecycle for more details on execution order:
http://www.gradle.org/docs/current/userguide/build_lifecycle.html
I need to post-process my APK by adding a second signature, so I'm trying to understand how to hook into the build process at that point. my first attempt,
task doubleSign {
if (signing.androidBuildExists) {
android.buildTypes.all { type ->
tasks.("assemble${type.name.capitalize()}").finalizedBy doDoubleSign
}
}
}
okay fine. but if any of the install* tasks are executed, then much to my surprise, assemble* isn't in the dependency tree. so next i tried adding the line,
tasks.("install${type.name.capitalize()}").dependsOn doDoubleSign
however, gradle tells me that task doesn't exist!
> Could not find property 'installDebug' on task set.
installDebug won't be present when that code is executed because the android plugin creates its tasks in the afterEvaluate phase.
What you should do:
android {
applicationVariants.all { variant ->
if (!variant.isSigningReady()) {
// only add double signing on variants that are signed with
// default signing method.
return
}
// get output file
def outputFile = variant.outputFile
// add new signing task.
def signingTask = project.tasks.create("double${variant.name}Sign", MyCustomSigning)
// configure task
signingTask.inputFile = outputFile
// create the final apk name using baseName to guarantee it's unique.
signingTask.outputFile = new File(project.buildDir, "apks/${project.name}-${variant.baseName}-2sign.apk")
// configure the task to be run after the default signing task.
signingTask.dependsOn variant.packageApplication
// configure zip align task to use the output of the 2nd signing task,
// and also to run after 2nd signing task.
variant.zipAlign.inputFile = signingTask.outputFile
variant.zipAlign.dependsOn signingTask
}
}
Note that if you don't run zipalign (but really you should) you'll have to tweak the end to make variant.assemble depend on your signing task instead and to set the output of your signing task to variant.outputFile so that deployment from command line or IDE still works.
For the actual signing you would do a custom task with the annotations so that it only runs if the input file actually change.
class MyCustomSigning extends DefaultTask {
#InputFile
File inputFile
#OutputFile
File outputFile
#TaskAction
void sign() {
// call script to sign inputFile into outputFile
...
}
}
Even though this question has been answered, I want to share the solution that worked best for me.
All I needed was a suitable hook to post-process a completely finished apk. For this I used the hook "assemble.doLast":
android {
/* ... */
applicationVariants.all { variant ->
variant.assemble.doLast {
variant.outputs.each { output ->
File apk = output.outputFile
// Post process apk
}
}
}
}