Function executed even if it belongs to a different build variant? - gradle

My setup:
buildToolsVersion "25.0.3"
com.android.tools.build:gradle:2.3.0
Code:
apply plugin: 'com.android.application'
android {
// ...
signingConfigs {
release {
storeFile getReleaseKeyStoreFile()
// ...
File getReleaseKeyStoreFile() {
String keyStoreFile = System.getenv("KEYSTORE_FILE")
if (keyStoreFile == null || keyStoreFile == "") {
println 'ERROR: Failed getting release keyStoreFile'
return null
}
return file(keyStoreFile)
}
When I run the gradle task assembleDebug I'm getting this in the Gradle build messages:
Failed getting releaseKeyStoreFile
Why is this function evaluated even though the build variant is debug and not release?
Is there a simple work-around to avoid this unwanted behavior?

You need to distinguish between the configuration phase and the execution phase.
In the configuration phase, the whole build script is evaluated and executed.
Only task actions (defined by task types), doFirst and doLast closures are executed in the execution phase.
Whether a task is executed (explicit or as dependency) only affects the execution phase, but nevertheless it will always be configured.
You can call each task, the configuration code in your android closure will always be executed.
To answer your second question: Your method must be fail-safe (as it is right now). There is no problem in returning null to the storeFile property, since only the task (which is not executed) will fail.
An additional hint: You can simplify your method. There is no need to check for null and an empty string, you can simply check for Groovy truth. You can use a ternary expression, too.
return keyStoreFile ? file(keyStoreFile) : null

Related

Gradle configure task based on subproject property

I'm trying to configure a Zip task based on one of the property inside sub-projects, but the property is not yet accessible at the time of configuring the task. For instance, I want to exclude all my projects that has toexclude = true from my zip file. So, the build.gradle of the sub-projects that I want to exclude starts with this:
ext.toexclude = true;
...
And my main build.gradle has this task:
task zipContent (type: Zip){
def excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
println excludedProjects
destinationDir = "/some/path"
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
The problem is that excludedProjects is always empty. Indeed, when I am executing the task, I can see []. I believe this is due to the fact that the property that I set in the subproject's build.gradle is not available at the moment the task is configured. As a proof, if I replace the first line of the task by this:
def excludedProjects = allprojects.collect{it.name}
The task prints out all of my project's name, and the zip contains nothing (which means the problem is in the p.toexclude == true).
Also, if I try this:
task zipContent (type: Zip){
def excludedProjects = []
doFirst{
excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
println "IN DOFIRST"
println excludedProjects
}
println "IN TASK CONFIG"
println excludedProjects
destinationDir = "/some/path"
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
The task prints out IN TASK CONFIG followed by an empty array, then IN DOFIRST with the array containing only the subprojects that I set ext.toexclude == true.
So, is there a way to get the properties of the sub-projects at configuration time?
Well, the crucial question is: At which point of the build is all necessary information available?
Since we want to know each project in the build, where the extra property toexclude is set to true and it is possible (and by design) that the property is set via the build script, we need each build script to be evaluated.
Now, we have two options:
By default, subprojects are evaluated after the parent (root) project. To ensure the evaluation of each project, we need to wait for the point of the build, where all projects are evaluated. Gradle provides a listener for that point:
gradle.addListener(new BuildAdapter() {
#Override
void projectsEvaluated(Gradle gradle) {
tasks.getByPath('zipContent').with {
exclude allprojects.findAll { it.toexclude }.collect{ it.name }
}
}
})
Gradle provides the method evaluationDependsOnChildren(), to turn the evaluation order around. It may be possible to use your original approach by calling this method before querying the excluded projects. Since this method only applies on child projects, you may try to call evaluationDependsOn(String) for each project in the build to also apply for 'sibling' projects. Since this solution breaks Gradle default behavior, it may have undesired side effects.
Just define excludedProjects outside the task
def excludedProjects = allprojects.findAll{Project p -> p.toexclude == true}.collect{it.name}
task zipContent (type: Zip){
destinationDir = file("/some/path")
baseName = "myFile.zip"
exclude excludedProjects
from "/some/other/path"
}
You can call evaluationDependsOnChildren() in the root project so that child projects are evaluated before the root
Eg
evaluationDependsOnChildren()
task zipContent (type: Zip) { ... }
Another option is to use an afterEvaluate { ... } closure to delay evaluation
Eg:
afterEvaluate {
task zipContent (type: Zip) { ... }
}

How to configure lazily a Gradle task?

I'm trying to configure the following custom task:
task antecedeRelease(type: AntecedeReleaseTask) {
antecedeWithVersion = project.'antecede-with-version'
antecedeToVersion = project.'antecede-to-version'
}
The problem is that the properties antecede-with-version and antecede-to-version are to be set through the command line with a -P option. If they're not set and antecedeRelease isn't being called, that shouldn't be a cause for an error:
$ ./gradlew tasks
org.gradle.api.GradleScriptException: A problem occurred evaluating project ...
Caused by: groovy.lang.MissingPropertyException: Could not find property 'antecede-with-version' on project ...
I could conditionally define the antecedeRelease task such that it's defined only if those properties are defined but I'd like to keep the build.gradle file as clean as possible.
If you need the antecedeRelease task to run "lazily" as-in, at the end of the configuration phase, or at the beginning of the execution phase, your best bet is to use doFirst
task antecedeRelease(type: AntecedeReleaseTask) {
doFirst {
antecedeWithVersion = project.'antecede-with-version'
antecedeToVersion = project.'antecede-to-version'
}
}
One option might be to use Groovy's elvis operator like so:
task antecedeRelease(type: AntecedeReleaseTask) {
antecedeWithVersion = project.ext.get('antecede-with-version') ?: 'unused'
antecedeToVersion = project.ext.get('antecede-with-version') ?: 'unused'
}
If this fails still, you can consider project.ext.has('property') when setting the value.

exclude tasks from the task that my task depends on

I created task which depends on build.
task packageJar(dependsOn: build, type: JavaExec) {
main = 'com.xxxx.util.KiePackageCreator'
classpath = sourceSets.main.runtimeClasspath
}
But build task invokes other tasks like checkstyle, test and etc.
How to exclude them?
I can do it through console -x but how to do it inside task?
You can simply disable the tasks, by setting it's enabled property to false in the root of the script:
test.enabled = false
But in that case, those tasks won't ever run. If you just need them to not running if some other task is called, then you have to use execution graph:
gradle.taskGraph.whenReady {
taskGraph ->
if (taskGraph.hasTask(packageJar)) {
test.enabled = false
}
}
But not sure at the moment, whether it is possible to change this property when the graph is ready. If not, then you can make a variable and in the tasks, you want to exclude, add the doFirst block, which will throw the StopExecutionException according to this variable value.

Code block in gradle.processResources conditional on whether another task was requested

We have an optional gradle task docker that depends on task war, which if executed, needs a war file generated with an extra file in it. This extra file can be added to the resources within the processResources task (or potentially directly in the war task). However, the corresponding code block must not run if task docker has not been requested and will not be run.
We need a correct condition in the following block checking if task docker is in the pipeline:
processResources {
if (/* CONDITION HERE: task docker is requested */) {
from ("${projectDir}/docker") {
include "app.properties"
}
}
}
task docker(type: Dockerfile) {
dependsOn build
...
Clarification: processResources is a standard dependency of the war task and the latter is a standard dependency of the build task. processResources is always executed on build, with or without the docker task to collect resources for assembling the war and may not be fully disabled in this case. One could move the code in question to a separate task dependent on docker and working on the output directory of processResources, yet before war is run, however, such a construct will result in much less clarity for such a simple thing.
You can simply add additional dependency to your docker task, to make it relying not only on build task, but also on processResources. In that case, your processResources task will be called only if docker should be executed.
Another solution is to use TaskExecutionGraph. This let you initialize some variable, which could tell you, whether or not some task will be executed. But you have to understand, that graph is prepared only after all the configuration is done and you can rely on it only during the execution phase. Here is a short example, how it could be used:
//some flag, whether or not some task will be executed
def variable = false
//task with some logic executed during the execution phase
task test1 << {
if (variable) {
println 'task2 will be executed'
} else {
println 'task2 will not be executed'
}
}
//according to whether or not this task will run or not,
//will differs test1 task behavior
task test2(dependsOn: test1) {
}
//add some logic according to task execution graph
gradle.taskGraph.whenReady {
taskGraph ->
//check the flag and execute only if it's true
if (taskGraph.hasTask(test2)) {
variable = true
println 'task test2 will be executed'
}
}
Furthermore, you can try to configure your custom task to make it disabled by setting is enabled property to false, if docker task is not in the execution graph. In that case you don't have to provide some flags and logic in execution phase. Like:
task test1 {
//some logic for execution
doLast {
println "execute some logic"
}
}
task test2(dependsOn: test1) {
}
gradle.taskGraph.whenReady {
taskGraph ->
if (!taskGraph.hasTask(test2)) {
//Disable test1 task if test2 will not run
test1.enabled = false
}
}
But it'll be impossible to run this custom task separately without some additional configuration.

Gradle task that depends on a failure

Can a gradle task depend on the failure of another task?
For example, I have an auxillary task that opens the test report in a browser. I want the report to only appear when the task "test" fails, not when all tests pass as it does now.
task viewTestReport(dependsOn: 'test') << {
def testReport = project.testReportDir.toString() + "/index.html"
"powershell ii \"$testReport\"".execute()
}
You can try to set task's finilizedBy property, like:
task taskX << {
throw new GradleException('This task fails!');
}
task taskY << {
if (taskX.state.failure != null) {
//here is what shoud be executed if taskX fails
println 'taskX was failed!'
}
}
taskX.finalizedBy taskY
You can find the explanation gradle's user guide in chapter 14.11 "Finalizer tasks". Shortly, due to docs:
Finalizer tasks will be executed even if the finalized task fails.
So, you just have to check the state of the finilized task with TaskState and if it was failed, do what you wanted.
Update:
Unfortunately, because configuration is always executed for all tasks, seems not possible to create some custom task to set the flag to show report within script. On execution phase it is not possible too, because the task will not be called if previewsly runned task has failed. But you can do, what you wanted, providing the build script arguments, like:
task viewTestReport << {
if (project.hasProperty("showReport") && test.state.failure != null) {
//here is what shoud be executed on taskX fail
println 'test was failed!'
}
}
test.finalizedBy(viewTestReport)
In that case, you have to provide -PshowReport arguments, while you call any gradle task, if ou want to get the report in test task fail. For example, if you call:
gradle test -PshowReport
then report will be shown if test task fails, but if you call it:
gradle test
no report will be shown in any case.

Resources