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
Related
I have to create a gradle custom task in KOTLIN with different args as input.
So based on the args, the custom task should run other tasks.
e.g: I want to run:
./gradlew ci type=release distribution=true version=1.2.2
OR
./gradlew ci type=debug distribution=true version=1.2.2
This command should run tasks: clean, assembleRelease OR assembleDebug (based on type param) and also another task to distribute the artifact (already have this one) if the distribute param is true.
Question 1: Is there any way to create a custom task that runs other tasks based on external params?
Question 2: Is there any way to inject the args? (the above commands are not valid I think)
You can pass properties with -P command and here is an example.
task passP() {
if (customProp.equals("myProp")) {
println customProp
}
}
this will only print if you execute the following command Gradle -PcustomProp=myProp passP
Now that you can pass parameters, You can clean, assembleRelease OR assembleDebug according to the passed parameters.
There Are two ways you can achieve that.
First way :
Make a gradle custome task to execute another gradle command which do not look neat. but the code will look like this
task passP(type: Exec) {
commandLine("cmd", "/c")
if (customProp.equals("clean")) {
args "gradle clean"
}
}
This will execute a normal clean if you passed clean as a parameter.
The second way :
You would be using finializeBy keyword
You can call the 3 tasks according to the passed parameters.
The code will be something like this (not tested) :
task passP() {
if (customProp.equals("clean")) {
tasks.named("clean") { finalizedBy("passP") }
if (customProp.equals("debug")) {
tasks.named("assembleDebug ") { finalizedBy("passP") }
}
}
The first way work 100%, But am not sure about the second one, As am only used to use finalizedBy out side of the custom task scope.
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()}"
}
}
UPDATE
I could get hold of the task at the runtime. But I am not able to finalize another task. The code I used for is:
gradle.taskGraph.whenReady {taskGraph ->
if (taskGraph.hasTask(spoonFreeDebugAndroidTest)) {
for(Task task : project.gradle.taskGraph.allTasks.iterator())
{
if(task.name.startsWith("spoonFreeDebugAndroidTest")) {
println "spoonFreeDebugAndroidTest task found"
ciIntegrationTests.mustRunAfter task.name
task.finalizedBy "ciIntegrationTests"
}
}
}
}
But, still the task "ciIntegrationTests" does not get executed when "spoonFreeDebugAndroidTest" fails. (The println line gets printed though.)
ORIGINAL QUESTION
I have a spoon task which is built at the runtime (when the task graph is generated) depending on my app flavours. e.g. A task "spoonFreeAndroidDebugTest" is generated at run time and is not accessible to me in build.gradle.
I want to add a dependancy on this task so that this task executes even if some other task fails. I tried these approaches:
spoon.finalizedBy "ciIntegrationTests"
which gives me this error
Could not find method finalizedBy() for arguments [ciIntegrationTests] on com.stanfy.spoon.gradle.SpoonExtension_Decorated#147456b6.
and
spoonFreeAndroidDebugTest.finalizedBy "ciIntegrationTests"
which gives me an error
Could not find property 'spoonDeltaAndroidDebugTest'
and
"spoonDeltaAndroidDebugTest".finalizedBy "ciIntegrationTests"
which results in
No signature of method: java.lang.String.finalizedBy() is applicable for argument types: (java.lang.String) values: [ciIntegrationTests]
Is there a way we can access a task in build.gradle which is generated at runtime.
Wrap your task string with tasks["taskName"]
tasks["spoonDeltaAndroidDebugTest"].finalizedBy tasks["ciIntegrationTests"]
Reference:
More About Tasks
How to set up gradle to run a particular custom copy task, only when running unit tests?
EDIT
I want to run these tasks when i press build, i. e only in the flavor of the build with unit test execution included.
I've finally found the solution, with the help of this documentation which presents all the tasks that run during build, test, release etc. in a very concise manner. So by making tasks clean, preBuild depend on my copyTask I can ensure that the copy task is run every time the project is cleaned or built.
But since I don't want to run this during building or cleaning process but want to run it when I only run tests, I identified a task that compiles release unit test sources called the compileReleaseUnitTestSources but just mentioning it in build.gradle as
compileReleaseUnitTestSources.dependsOn(myCopyTask)
doesn't actually work, because gradle will give an error saying it cannot find the task compileReleaseUnitTestSources as for some reason that task is not available yet. Instead by enclosing it in a afterEvaluate block we can ensure that this block is executed after all tasks are evaluated, that way we have access to that task now, So finally I added this to my build.gradle
afterEvaluate {
compileReleaseUnitTestSources.dependsOn(copyResDirectoryToClasses)
}
All the answers here mention to use the dependsOn keyword to attach my task to another task that is run during general build/test execution, but none of them mentioned how to go around the problem where gradle is not able to find the tasks even though you know for sure that these tasks were available and run during build/test execution.
you have to set up a "customCopyTask" and make the "test-task" which does the unittests depend on the "customCopyTask" like this
task customCopyTask(type: Copy) {
from sourceSets.test.resources
into sourceSets.test.output.classesDir
}
test.dependsOn customCopyTask
You can make some task finalizing another, in that case this task will run only if another one was called, right after it. This could be done as:
task runUnitTest << {
println 'running tests'
}
task copyTestResults << {
println 'copying results'
}
//make copyTestResults finalize runUnitTest
runUnitTest.finalizedBy copyTestResults
You can read about it in the official user guide.
Additionally, if your unit test could be up-to-date and you don't want to run you copy task in that case, you can check the test task status and skip copy-task, as:
task copyTestResults {
doFirst {
//chtck anothe task status and skip this one if it didn't actually work
if (!tasks.getByName("runUnitTest").getState().didWork) {
throw new StopExecutionException();
}
}
doLast{
println 'copying results'
}
}
Or, if you just need to run copy-task before unit tests, make the test task depending on copy-task by setting it's dependsOn property, read about it with a number og examples here
My gradle task looks like this
task xml_file_to_vs_build(type: Copy) {
outputs.upToDateWhen { false }
doLast {
outputs.upToDateWhen { false }
println("copying strings.xml");
from 'src/main/res/values'
into '../vso_build'
include 'strings.xml'
}
}
The task is always up to date . If I put the block in the configuration phase i.e. out of doLast, everything works fine. But then the code executed even if I execute gradlew Tasks which I do not want. What am I doing wrong ?
A Copy task only gets executed if it has something to copy. Telling it what to copy is part of configuring the task, and therefore needs to be done in the configuration phase, rather than the execution phase. These are very important concepts to understand, and you can read up on them in the Gradle User Guide or on the Gradle Forums.
doFirst and doLast blocks get executed in the execution phase, as part of executing the task. Both are too late to tell the task what to copy: doFirst gets executed immediately before the main task action (which in this case is the copying), but (shortly) after the skipped and up-to-date checks (which are based on the task's configuration). doLast gets executed after the main task action, and is therefore clearly too late.
You should define the task in the following way:
task xml_file_to_vs_build(type: Copy) {
println("copying strings.xml")
from 'src/main/res/values'
into '../vso_build'
include 'strings.xml'
}