How to create recursive Gradle task? - gradle

I would like to create a task in the root project such that when the task is executed, it executes tasks of the same name within the sub-projects only for those sub-projects that have tasks of that name.
I don't want to hard-code the names of the tasks since more sub-projects may be added later.
task checkArtifacts
subprojects.findAll { subproject ->
subproject.getTasksByName('checkArtifacts', false)
}.forEach { task ->
checkArtifacts.dependsOn(task)
}
doesn't work since it looks like it forces the configuration stage to end and breaks some plugins that make configuration stage changes.
task checkArtifacts
afterEvaluate {
subprojects.findAll { subproject ->
subproject.getTasksByName('checkArtifacts', false)
}.forEach { task ->
checkArtifacts.dependsOn(task)
}
}
emits Could not determine the dependencies of task ':checkArtifacts'.

There's some methods in DomainObjectCollection which are applied to future updates to the collection.
Eg all
Executes the given closure against all objects in this collection, and any objects subsequently added to this collection. The object is passed to the closure as the closure delegate. Alternatively, it is also passed as a parameter.
And matching
Returns a collection which contains the objects in this collection which meet the given closure specification. The returned collection is live, so that when matching objects are added to this collection, they are also visible in the filtered collection.
So something like
subprojects {
tasks.matching { it.name == 'checkArtifacts' }.all { task ->
rootProject.tasks.checkArtifacts.dependsOn task
}
}

Are you sure you need this task defined at the root all? If you just call the task 'checkArtifacts' at the root, it will run it for every subproject that has it defined. There is no need to make the extra task.
If you do need to do it, I'd try something more like this using TaskContainer:
task checkArtifacts
subprojects {
tasks.whenTaskAdded { task ->
if (task.name == 'checkArtifacts') {
rootProject.tasks.checkArtifacts.dependsOn task
}
}
}

Related

Gradle exclude resources for one task but not for second

Is it possible to exclude resources for a task, but do not exclude them for a second task? Both are triggered from a third task like this:
task createBoth {
dependsOn = [createFirst, createSecond]
}
task createFirst {
sourceSets.main.resources.excludes = ['**/images']
}
task createSecond {
sourceSets.main.resources.excludes = []
}
I tried to exclude in doFirst{} method, but it did not work. If run as a single task then it works fine. Both results are the same version of my Java project. Except they have a different main class and one does not need all images. So I want to create both files in a single run. Is it even possible?
Attempt 2:
task createSystemcheckJar(type: OneJar) {
exclude('**/images/ads/images') // And some ohter combinations with stars
}
Same problem.

What is the syntax for a gradle subproject task depending on a parent project's task?

I need some help with a Gradle build file. I have some subprojects which depend on tasks in the top folder. I just need to take a test task and split it into two separate test tasks. Currently the file system structure looks like (tab indicates files inside a directory):
top-level-project-folder
A.gradle
B.gradle
C-subproject-folder
C.gradle
D-subproject-folder
D.gradle
Contents of A.gradle (before refactor):
subprojects {
tasks.test.dependsOn {
bTask
}
}
apply from: 'B.gradle'
Contents of C.gradle (before refactor):
test {
...
}
After the refactor, C.gradle needs to look like:
test {
...
}
task runDifferentTests(type : Test) {
...
}
The tricky part is that C.gradle's test task currently depends on bTask. However, after the refactor, C.gradle's test task should not depend on bTask, but the new runDifferentTests task should depend on bTask. (Currently, D.gradle's test task is marked as depending on bTask, but it does not actually depend on it -- I'd like to remove that dependency. The only task in the two subprojects which depends on bTask is the new runDifferentTests task.)
I've tried some different things but can't seem to find a working solution.
Just remove the declaration in subprojects and declare your dependency directly in the subproject, in C.gradle:
runDifferentTests.dependsOn rootProject.bTask
There are a few syntax solutions here:
runDifferentTests.dependsOn (":bTask")
runDifferentTests.dependsOn rootProject.bTask
task runDifferentTests(type : Test, dependsOn: [":bTask"]) {
...
}
task runDifferentTests(type : Test) {
dependsOn rootProject.bTask
...
}
//in the root build.gradle to apply to all subprojects at once.
subprojects {
runDifferentTests.dependsOn (":bTask")
}
runDifferentTests.dependsOn {
tasks.findAll { task -> task.name.startsWith('bTask') }
}
: can be used to go a level up instead of rootProject depends on the preference and the project structure

Call Zip Task Multiple Times In Gradle

I need to update 9 zip files and the code to do it is about 15 lines.
I'd rather not have to repeat the same 15 lines 9 times in the build script with just different variable names.
Is it possible to call a Zip task in a loop from another task?
Using dynamic tasks seems to be one way but it requires me to list the array of tasks twice which I can see causing an error in future when an extra item is added.
[war1, war2, war3, war4, war5, war6, war7, war8, war9].each { warName ->
task "task$warName"(type: Zip) {
archiveName = "${warName}.war"
//etc etc
}
}
task all(dependsOn: [taskwar1, taskwar2, taskwar3, taskwar4, taskwar5, taskwar6, taskwar7, taskwar8, taskwar9]) {
}
Is there any alternative?
Firs of all your code might be simplified just to:
task allWars
(1..9).each { id ->
task "taskwar${id}"(type: Zip) {
archiveName = "war${id}.war"
}
allWars.dependsOn "taskwar${id}"
}
And a solution with task rules:
tasks.addRule('Pattern: taskwar<ID>') { String taskName ->
if (taskName.startsWith('taskwar')) {
task(taskName, type: Zip) {
archiveName = "${taskName - 'task'}.war"
}
}
}
task allWars {
dependsOn << (1..9).collect { "taskwar$it" }
}
There are almost no obvious pros and cons - when it comes to functionality. Basically solution without rules is shorter as you can see, so if you represent attitude less code is better that's the way to go. However task rules were created in gradle for this kind of situations - where there are lots of predefined tasks. First solution is more groovier while the second one is more gradler ;)
One way is to store the list of 'war' names in a ext property and then iterate over it to create the tasks and use a mapping function for defining the dependencies for the all task.
// Define the variables here
ext.warTaskPrefix = "task"
ext.warNames = ["war1", "war2", "war3", "war4", "war5", "war6", "war7", "war8", "war9"]
// Define the war task dynamically
warNames.each { warName ->
task "${warTaskPrefix}${warName}"(type: Zip) {
archiveName = "${warName}.war"
//etc etc
}
}
// Define the task that depends on all war tasks
task all(dependsOn: warNames.collect{ warName -> "${warTaskPrefix}${warName}" }) {
}

Custom conditional configuration for Gradle project

Having an extract from https://github.com/gradle/gradle/blob/master/build.gradle:
ext {
isDevBuild = {
gradle.taskGraph.hasTask(developerBuild)
}
}
task developerBuild {
description = 'Builds distributions and runs pre-checkin checks'
group = 'build'
dependsOn testedDists
}
When I used this approach to create custom configuration in my project I discovered that:
isDevBuild === true
i.e. it's always true because task 'developerBuild' is inside my build.gradle project, and hence in graph. They have a couple of "different" configs (isCIBuild, isCommitBuild, isFinalReleaseBuild, ...) so I suppose I got something wrong here.
Can someone explain how to make this configs conditional based on some external parameter?
taskGraph.hasTask() tells if a task is in the task execution graph, that is whether it will get executed. Because the task execution graph is only created after the configuration phase, this method has to be called from a whenReady callback (or in the execution phase):
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(developerBuild)) {
// do conditional configuration
}
}
To make this more readable, we can introduce a new method:
def onlyFor(task, config) {
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(task)) {
project.configure(project, config)
}
}
}
Now we can write:
onlyFor(developerBuild) { ... }
onlyFor(ciBuild) { ... }
Another, simpler way to solve this problem is to check whether a particular task name is contained in gradle.startParameter.taskNames. However, this has two limitations: First, it compares task names, which can make a difference in multi-project builds. Second, it will only find tasks that have been specified directly (e.g. on the command line), but not dependencies thereof.
PS.: In your code, isDevBuild always holds because a (non-null) closure is true according to Groovy truth. (In contrast to isDevBuild(), isDevBuild won't call the closure.)

Moving built-in gradle tasks work to doLast/built-in tasks shourtcuts

I want to create a simple sync task that slightly change it behaviour depending on build type (e.g. debug/release) and I use boolean variable 'dummy' decrared in gradle.taskGraph.whenReady:
gradle.taskGraph.whenReady {taskGraph ->
dummy = false
if (taskGraph.hasTask(':dummybuild')) {
dummy = true
}
}
The problem is that task configured by the following way has configuration scope, i.e. before whenReady so it doesn't have access to the 'dummy' variable:
task copySkins(type: Sync) {
from skinsFrom
into skinsInto
rename skinsRename
exclude symbianExclude
if (!dummy) exclude dummyExclude
}
Right now I'm using this workaround
task copySkins {
inputs.dir skinsFrom
outputs.dir skinsInto
doLast {
task skins(type: Sync) {
from skinsFrom
into skinsInto
rename skinsRename
exclude symbianExclude
if (!dummy) exclude dummyExclude
}
skins.execute()
}
}
Is it possible to
detect/setup some build properties in some other place except whenReady
move sync task work to doLast
or at least have some shortcut for sync task (.execute() looks quite ugly)
1) whenReady event allows user to access fully-initialized task graph: all initialization is finished and tasks are ready to run. The only situation, when you need to detect/setup build properties here, is when you need to introspect current build setup.
If you do not need this information, you can place your initialization anywhere in your build script. At the very end, it is nothing but groovy script.
apply plugin: 'java'
def now = new Date()
compileJava.doFirst {
println "It is ${now}. We are starting to compile"
}
2) You can not move sync task work to doLast. But you can always add your actions to doFirst ;) I think, this should work:
task copySkins(type: Sync) {
from skinsFrom
into skinsInto
rename skinsRename
exclude symbianExclude
doFirst {
if (!dummy) exclude dummyExclude
}
}
3) With all said before, missing sync task shortcut should not be that painfull

Resources