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.
Related
In my app, I have more than 30 build variants. Every time when I release the app, I need to publish it to different platforms, therefore I build 5 different build variants.
Currently, I am doing this:
switch to build variant A
wait for the Gradle build
build APK/Bundle of build variant A
the same steps for B, C, E, and D.
What I am looking for is a Gradle task that just builds me these specific build variants when I run it. I know there is a task to build all build variants but it is too much for me.
I searched SO but couldn't find anything related to a point that I started to think it is impossible.
Could someone point me in the right direction? Thanks.
Calling a gradle task from another gradle task is not the best idea. You should rather describe their relationship as it usually works with all other gradle parts - by using mustRunAfter, dependsOn and so on. For your purposes you can use GradleBuild. I think you're searching for this - if I got your point right :D
I would assume that your task will look something like that (add your flavours instead of mine mocked)
task assembleFlavourBuilds(type: GradleBuild) {
description = 'creating flavour builds for the provided config'
tasks = ['assembleFree', 'assemblePro']
}
If you can use CommandLine then Gradle has good support for that.
For example :
task makeDir(type: Exec) {
workingDir "."
commandLine("cmd", "/c", "mkdir", "example")
}
Am just trying to show an example of how this work, by creating a folder here named example.
You can even add this to be automated with an already defined task, Like a build task. This can be done by using finalizeBy
tasks.named("build") { finalizedBy("makeDir") }
This will only call the task after a successful build.
And my suggestion is to make a .bat file with all the needed commands and call it in the same way as the following code :
task BuildAll(type: Exec) {
workingDir "."
commandLine("cmd", "/c", "mybat.bat")
}
And mybat.bat will contain all the needed commands to
switch build variant
build
bundle
repeat
Switch Build variant
productFlavors {
variantA {
dimension "version"
versionNameSuffix ".a"
}
variantB {
dimension "version"
versionNameSuffix ".b"
}
variantC {
dimension "version"
versionNameSuffix ".c"
}
variantD {
dimension "version"
versionNameSuffix ".d"
}
variantE {
dimension "version"
versionNameSuffix ".e"
}
}
you can use gradle task. in your root build.gradle define task like bellow:
task assembleFlavorBuilds(type: GradleBuild) {
description = 'Description of task.'
tasks = ['assembleFlavorName1Debug', 'assembleFlavorName1Release']
}
each of item in tasks are like this: "assemble"+FlavorName(first of name must be capital)+build type(Debug or Release).
for instance if you have flavors with names "free" and "paid", your task must be like this:
task assembleFlavorBuilds(type: GradleBuild) {
description = 'Description of task.'
tasks = ['assembleFreeRelease', 'assemblePaidRelease']
}
after sync project you can see this task in gradle area in idea or just click on run icon next of this task.
In my app I use the assets directory to store some XML files (some are large).
In my design time I want the files to use indentation and also put some comments in it.
This is enlarging my xml files and can add up to a large size.
Is it possible to add a task to the gradle build to remove all indentation and comments for the xml files before packaging it in the apk? If so how?
This will not only shrink my apk, but will also assist at run time with the xml processing.
EDIT
The answer by fhomovc was correct, but was missing some part.
I will mark it as correct but if anyone else will need it, here are the details:
In general I need a task that will run the minify utility and it should look like:
task minifyAssets(type:Exec) {
workingDir dirName // the directory of the merged assets under the build directory
commandLine 'minify', '-r', '-o', '.', '.'
doFirst {
println 'minifyAssets...'
}
}
This task should only be executed after the merged assets task is executed and before the package task is executed.
The main problem is that there should be a dedicated task for each variant, so I needed to do it dynamically:
First create the exec task and make it dependent on the merge task
applicationVariants.all { variant ->
// dynamically add minify task for specific variant
def dirName = new File("app\\build\\intermediates\\merged_assets\\" + variant.name + "\\out\\levels").getAbsolutePath()
def minifyTaskName = "minifyAssets" + variant.name
def mergeAssetsTaskName = "merge" + variant.name + "Assets"
def myTask = tasks.register(minifyTaskName, Exec) {
workingDir dirName
// require that minify utility will be in the path. Download from https://github.com/tdewolff/minify/tree/master/cmd/minify
commandLine 'minify', '-r', '-o', '.', '.'
doFirst {
println 'minifyAssets...' + workingDir
}
}
// set the minify task dependant on the merge assets task
myTask.get().dependsOn mergeAssetsTaskName
}
Now we need to make the specific package task depend on the minify task:
// when the package task is added make it dependant on the minify task
tasks.whenTaskAdded { theTask ->
if (theTask.name.startsWith("package") && (theTask.name.endsWith("Debug") || theTask.name.endsWith("Release"))) {
def minifyTaskName = theTask.name.replace("package", "minifyAssets")
theTask.dependsOn minifyTaskName
}
}
You can run a custom script with an xml minifier.
For the minifier: you can install minify following the installation steps provided.
For the script: you can refer to this answer. Essentially your task would look something like this
task executeScript(type:Exec) {
println 'Minifying xmls...'
//on linux
commandLine 'minify -r -o ./ --match=\.xml ./values' // ./values should be the path to your resources directory
}
Check the documentation to understand better how minify works. I haven't tested this solution myself, so it may need a few adjustments but you get the general idea. If you are using a Windows machine then the Script (commandLine) should be different, if I can find any examples online I'll add them.
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()}"
}
}
We have a gradle task that will automatically generate codes for us before building. See the following as an example,
task djinniTask(type: org.gradle.api.tasks.Exec) {
commandLine 'sh', './Djinni/run_djinni.sh'
}
assembleDebug.dependsOn djinniTask
Basically, the above run_djinni.sh is using a library djinni to generate JNI codes. The above works fine except that it will run this script every time we build even if we didn't update the script file, which is obviously not very efficient. We did a bit of research and found 17.9. Skipping tasks that are up-to-date. And as a result, the following works fine. It will skip this task if we didn't modify run_djinni.sh.
task transform {
ext.srcFile = file('./Djinni/run_djinni.sh')
ext.destDir = new File(buildDir, 'generated')
doLast {
commandLine 'sh', './Djinni/run_djinni.sh'
}
}
Now the problem is, the run_djinni.sh is not the only script file that we have. The project is big and we multiple scripts files like: run_foo_djinni.sh, run_bar_djinni.sh and etc. run_djinni.sh will call each of the other scripts. So is there a way to declare the inputs of a gradle task as multiple files, for example, in our case, every files that is under the Djinni folder?
Ok, according to gradle DSL you can define multiple inputs:
task transform {
inputs.files('file path', 'another file path')
}
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
}