How to run a Gradle task after apks are produced in Android Studio? - gradle

The following task (in build.gradle of an app's module) seems to run always before the apk is produced:
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
def releaseBuildTask = tasks.create(name: "debug") {
println(".................... test ..............................")
}
releaseBuildTask.mustRunAfter variant.assemble
}
}
Could anyone offer a tip on how to run a task after the apks are produced?

Android tasks are typically created in the "afterEvaluate" phase.
Starting from gradle 2.2, those tasks also include "assembleDebug" and
"assembleRelease". To access such tasks, the user will need to use an
afterEvaluate closure:
afterEvaluate {
assembleDebug.dependsOn someTask
}
source: https://code.google.com/p/android/issues/detail?id=219732#c32

try add this in you app/build.gradle
assembleDebug.doLast {
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
def releaseBuildTask = tasks.create(name: "debug") {
println(".................... test ..............................")
}
releaseBuildTask.mustRunAfter variant.assemble
}
}
println "build finished"
}
invoke the build command and specify the task assembleDebug
./gradlew assembleDebug

I found a solution that works, to copy the release APK into the project root automatically on build completion.
android {
...
task copyReleaseApk(type: Copy) {
from 'build/outputs/apk'
into '..' // Into the project root, one level above the app folder
include '**/*release.apk'
}
afterEvaluate {
packageRelease.finalizedBy(copyReleaseApk)
}
}

Related

Gradle release composite build with a release order

I have a gradle composite build in following directory structure.
.
├── component-1
├── component-2
├── component-3
└── composite-build
Here, all components are multi-project builds. Each component has implemented the release plugin and can be released individually. The composite-build has following settings.gradle.
rootProject.name = 'composite-build'
includeBuild '../component-1'
includeBuild '../component-2'
includeBuild '../component-3'
I want to execute release task of each component in a specific order but I couldn't find a proper way. Following are some cases that I tried and failed. In these cases individual release tasks are also failing due to conflicting git tag commits done by release plugin.
Case 1:
In build.gradle
task releaseAll {
dependsOn gradle.includedBuild('component-1').task(':release')
dependsOn gradle.includedBuild('component-2').task(':release')
dependsOn gradle.includedBuild('component-3').task(':release')
}
Case 2:
In build.gradle
task releaseComponent1 {
dependsOn gradle.includedBuild('component-1').task(':release')
}
task releaseComponent2 {
dependsOn gradle.includedBuild('component-2').task(':release')
}
task releaseComponent3 {
dependsOn gradle.includedBuild('component-3').task(':release')
}
releaseComponent2.configure {
mustRunAfter releaseComponent1
}
releaseComponent3.configure {
mustRunAfter releaseComponent2
}
task releaseAll {
dependsOn releaseComponent1
dependsOn releaseComponent2
dependsOn releaseComponent3
}
Sample error message:
> Task :component-1:component-1-release:preTagCommit FAILED
Running [git, commit, -m, [Gradle Release Plugin] - pre tag commit: 'component-1-0.1.10'., -a] produced an error: []
Running [git, push, --porcelain, origin, main] produced an error: [error: failed to push some refs to 'https://github.com/<username>/<repo-name>.git']
> Task :component-1:release FAILED
Release process failed, reverting back any changes made by Release Plugin.
Running [git, checkout, gradle.properties] produced an error: [error: pathspec 'gradle.properties' did not match any file(s) known to git]
1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':component-1:component-1-release:preTagCommit'.
> Failed to push to remote - [To https://github.com/<username>/<repo-name>.git
! refs/heads/main:refs/heads/main [remote rejected] (cannot lock ref 'refs/heads/main': is at <xxxx> but expected <yyyy>)
Done
][error: failed to push some refs to 'https://github.com/<username>/<repo-name>.git'
]
There is always an option with gradle where you can automate and link task execution with finalizedBy and you can test this simple code to check how it work :
task A() {
println 'taskA'
}
task B() {
println 'taskB'
}
tasks.named("B") { finalizedBy("A") }
task C() {
println 'taskC'
}
tasks.named("C") { finalizedBy("B") }
This way, by calling gradle C it will backtrack to execute A then B then C
Same way, You can do 3 tasks to get the desired ordered execution like this
task c1release(type:Exec) {
workingDir '.\\component-1'
commandLine("cmd", "/c", "gradle", "release")
doLast {
println "component-1 release is being excuted"
}
}
tasks.named("c1release") { finalizedBy("c2release") }
task c2release(type:Exec) {
workingDir '.\\component-2'
commandLine("cmd", "/c", "gradle", "release")
doLast {
println "component-2 release is being excuted"
}
}
tasks.named("c2release") { finalizedBy("c3release") }
task c3release(type:Exec) {
workingDir '.\\component-3'
commandLine("cmd", "/c", "gradle", "release")
doLast {
println "component-3 release is being excuted"
}
}
tasks.named("releaseAll") { finalizedBy("c1release") }
task releaseAll() {
println "Start releasing"
}
by calling gradle releaseAll it will execute c1release first then c2release then c3release
But I still wish ti know what kind of error you got with the cases you have included in your question.

How to check from within a Gradle file if a task has been or has not been excluded?

In a Gradle file is the following configuration:
publishing {
publications {
if (os == 'osx') {
nebulaIvy(IvyPublication) {
artifact('build/final/java_plugin-linux-x86_64/linux-x86_64.exe') {
classifier 'linux-x86_64'
extension 'exe'
type 'exe'
}
}
}
The Linux binary is built through the task buildInDocker. If the buildInDocker task is excluded (eg gradlew -x buildInDocker), that part of the configuration shouldn't be done. How can this be done?
Use https://docs.gradle.org/current/javadoc/org/gradle/StartParameter.html#getExcludedTaskNames(): !project.getGradle().startParameter.getExcludedTaskNames().contains('buildInDocker').

is there an anchor task that contains bundleDebug and bundleRelease in Android Gradle plugin?

In Android Gradle plugin, task "assemble" is an anchor task that contains assembleDebug and assembleRelease. Is there a similar anchor task that contains bundleDebug and bundleRelease. I currently have following build script where a task depends on bundleDebug:
android.libraryVariants.all {
variant -> variant.javaCompile.classpath += configurations.provided
}
task removeCameraApiJar(dependsOn: 'bundleDebug') << {
FileCollection outputs = tasks['bundleDebug'].getOutputs().getFiles()
outputs.each {
File file ->
println file.name
}
println 'removeCameraApiJar'
}
task assemble.dependsOn(removeCameraApiJar)
However if I replace bundleDebug with just "bundle", the script would fail with following message:
What went wrong: Could not determine the dependencies of task ':camerasupport:removeCameraApiJar'.
Task with path 'bundle' not found in project ':camerasupport'.
It doesn't seem as though the Android plugin creates such a task. You could however do something like
task removeCameraApiJar(dependsOn: tasks.matching { it.name.startsWith('bundle') })
In the current version of the gradle android plugin (1.5.0), the bundle task can be found as a property ("packageLibrary") of the variant output :
android.libraryVariants.all {
variant ->
variant.outputs.each { output ->
FileCollection outputs =
output.packageLibrary.getOutputs().getFiles()
}
}
prinln output.packageLibrary.name will yield "bundle"+buildVariant.

Execute gradle task on sub projects

I have a MultiModule gradle project that I am trying to configure.
Root
projA
projB
other
projC
projD
projE
...
What I want to be able to do is have a task in the root build.gradle which will execute the buildJar task in each of the projects in the other directory.
I know I can do
configure(subprojects.findAll {it.name != 'tropicalFish'}) {
task hello << { task -> println "$task.project.name"}
}
But this will also get projA and projB, I want to only run the task on c,d,e...
Please let me know the best way to achieve this.
Not entirely sure which of these you're after, but they should cover your bases.
1. Calling the tasks directly
You should just be able to call
gradle :other/projC:hello :other/projD:hello
I tested this with:
# Root/build.gradle
allprojects {
task hello << { task -> println "$task.project.name" }
}
and
# Root/settings.gradle
include 'projA'
include 'projB'
include 'other/projC'
include 'other/projD'
2. Only creating tasks in the sub projects
Or is it that you only want the task created on the other/* projects?
If the latter, then the following works:
# Root/build.gradle
allprojects {
if (project.name.startsWith("other/")) {
task hello << { task -> println "$task.project.name" }
}
}
and it can then be called with:
$ gradle hello
:other/projC:hello
other/projC
:other/projD:hello
other/projD
3. Creating a task that runs tasks in the subprojects only
This version matches my reading of your question meaning there's already a task on the subprojects (buildJar), and creating a task in root that will only call the subprojects other/*:buildJar
allprojects {
task buildJar << { task -> println "$task.project.name" }
if (project.name.startsWith("other/")) {
task runBuildJar(dependsOn: buildJar) {}
}
}
This creates a task "buildJar" on every project, and "runBuildJar" on the other/* projects only, so you can call:
$ gradle runBuildJar
:other/projC:buildJar
other/projC
:other/projC:runBuildJar
:other/projD:buildJar
other/projD
:other/projD:runBuildJar
Your question can be read many ways, hope this covers them all :)
All of the ways mentioned by Mark can be used but all of them have some cons. So I am adding one more option:
4. Switching the current project
gradle -p other hello
This switches the "current project" and then runs all tasks named hello under the current project.
Example 5. Defining common behavior of all projects and subprojects,
allprojects {
task hello {
doLast { task ->
println "I'm $task.project.name"
}
}
}
subprojects {
hello {
doLast {
println "- I depend on water"
}
}
}
From the Gradle documentation,
https://docs.gradle.org/current/userguide/multi_project_builds.html

How to run copy task with android studio into assets folder

So far I have added the following to the end of my "build.gradle"
task copyFiles(type: Copy)
copyFiles {
description = 'Copies html5 files from the common library...'
from '../../www'
into 'assets/www'
include('**/*')
}
Now I just need some help on how o make this task get executed everytime (before) compiling the android source. I can run the copy task manually from command line, but Id like to have it run when I click "run" in android studio.
With the help of suggestion below, I have added
clean.dependsOn copyFiles
clean.mustRunAfter copyFiles
And with this addition I can get my copy task to run by doing rebuild -> run.
It's better than nothing but it would be nice to skip the rebuild step.
Here is the whole build.gradle that im using with android studio.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.5.+'
}
}
apply plugin: 'android'
dependencies {
compile files('/libs/acra-4.3.0.jar')
}
android {
compileSdkVersion 17
buildToolsVersion "17"
defaultConfig {
minSdkVersion 8
targetSdkVersion 17
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src','libs']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
instrumentTest.setRoot('tests')
}
}
task copyFiles(type: Copy)
copyFiles {
description = 'Copies html5 files from the common library...'
from '../../www'
into 'assets/www'
include('**/*')
}
clean.dependsOn copyFiles
clean.mustRunAfter copyFiles
This way I do custom copying of file assets in my android-gradle build system
preBuild.dependsOn copyFiles
Here is the module's build.gradle that I am using which successfully copies the files that I wanted as a pre-build task. The "into" is modelled after the File class in Java, so it should be familiar on how to use it. The two lines at the end is optional - it will run the copyFiles task when invoking gradle clean:
android {
.....
}
task copyFiles(type: Copy) {
description = 'copying some file(s)....'
from 'src/main'
into project(':Path:To:ModuleFrom:Settings.gradle').file('./res')
include 'file1.suffix'
include '**/*.html'
}
project.afterEvaluate {
preBuild.dependsOn copyFiles
}
clean.dependsOn copyFiles
clean.mustRunAfter copyFiles
I had a very similar problem to yours and I was able to solve it as follows:
android.buildTypes.all{ buildType ->
task "${buildType.name}CopyFiles" (type: Copy)
"${buildType.name}CopyFiles" {
description = 'Copies html5 files from the common library...'
from '../../www'
into 'assets/www'
include('**/*')
}
tasks.getByPath(":${project.name}:assemble${buildType.name.capitalize()}").dependsOn "${buildType.name}CopyFiles"
}
The problem is, that Android Studio seems to call a specific assemble task like assembleDebug when you click on run, that's why you have to make sure to make all assemble tasks depend on your copy task.
task myCopyToAssets(type: Copy) {
def toDir = rootProject.file('app/assets')
from (rootProject.file('app/usb')) {
include 'libhotplug.so'
}
into toDir
}
tasks.whenTaskAdded { task ->
//println task.getName()
if (task.name == 'assembleDebug' || task.name == 'assembleRelease') {
println 'myCopy'
myCopyToLibs.execute()
myCopyToAssets.execute()
}
}
Try this below your build.gradle:
tasks.whenTaskAdded { task ->
if (task.name == 'assemble') {
task.dependsOn copyFiles
}
}
In my case I manipulate some 'token' values inside res/values/strings.xml, and then copy it into ${project_root}/build/filtered-resources due to project cleaning issue.
To work correctly with this manipulated resource, android.sourceSets.res should be redefined to copied folder.
For your case copy assets folder with your www resource into ${PROJECT_ROOT}/build/your/desired/location, and asign android.sourceSets.assets point to it.
I use the following copy task in my project to copy strings into another directory:
task copyStringsUniversal(type: Copy) {
from 'src/main/res/values'
include 'strings.xml'
include 'array.xml'
into 'src/universal/res/values'
}
build.dependsOn copyStringsUniversal
Add this line to your build.gradle file:
assemble.dependsOn copyFiles
assemble.mustRunAfter copyFiles
where assemble can be any task.
Edit: I added the mustRunAfter bit to make sure the copyFiles task is run before any of the other assemble dependencies.

Resources