Correlation between tasks, sub-projects and configurations - gradle

I have next structure for my project:
Root
|- A
|- C (depends on A)
\- B (depends on A)
For all of sub-projects we use own plugin for generate-resources: https://github.com/terma/gradle-sqlj-plugin/blob/master/src/main/groovy/org/github/terma/sqljgradleplugin/SqljPlugin.groovy the task from plugin doesn't depend on any however JavaCompile task depends on it
When I build my project in build log I see:
:A:myPluginTask
:B:myPluginTask
:C:myPluginTask
:A:compileJava
:A:processResources
:A:classes
:B // next normal build way
Question why Gradle execute my plugin task for all sub-projects before java tasks? And why it execute java tasks in normal way first all java tasks for A than B ...?
Optional question how Gradle build task execution tree, separated for each project or cross projects?
Thx a lot.

All that can be said (and relied upon) is that Gradle will choose a task order that satisfies the declared task relationships (dependsOn, mustRunAfter, shouldRunAfter, finalizedBy). All execution dependencies are between tasks (not projects), and it's common that tasks belonging to different projects will get executed in alternation (or in parallel if --parallel is used). There is a single task execution graph for the whole build.

Related

Gradle: where are task dependencies defined for distZip task

I am using the gradle application plugin.
I noticed that:
distZip task get's run before the assemble task.
And I can see
from the docs (link above), and confirmed during my gradle run, that
the distZip tasks depends on: jar, startScripts tasks
So execution looks something like this:
> Task :my-app:processResources
> Task :my-app:compileJava
> Task :my-app:classes
> Task :my-app:jar
> Task :my-app:startScripts
> Task :my-app:distTar
> Task :my-app:distZip
> Task :my-app:assemble
Looking through the code in ApplicationPlugin.java and/or DistributionPlugin.java, I expected to see the task dependencies defined.
e.g. something like this:
distZipTask.dependsOn(jarTask)
distZipTask.dependsOn(createScriptsTask)
assembleTask.dependsOn(distZipTask)
...but I couldn't find anything like this in the java code.
Would very much appreciate it if someone could point me in the direction of where to look to find out how these tasks are 'linked'.
Gradle can determine implicit task dependencies from task inputs. If (the output of) a task is used as input of another task, Gradle can derive that there must be a dependency on the task. This functionality is called incremental build support, as it also supports checking for changes in the inputs or outputs to skip tasks if the inputs did not change.
Regarding your example, the task dependencies are defined implicitly in line 197 and line 208 of the file ApplicationPlugin.java:
libChildSpec.from(jar);
...
binChildSpec.from(startScripts);
The arguments in these lines are the respective tasks (to be exact: providers of the tasks). The from method can often be used to define sources of a file operation (e.g. copying, zipping ...). So we define the results (outputs) of the tasks jar and startScripts as a source of some operation (CopySpec). The resulting CopySpec objects are then passed to both tasks distZip and distTar. When a task tries to resolve the defined sources it finds the tasks jar and startScripts and defines the dependencies on them on its own.
Please note that the mentioned task assemble is a so-called lifecycle task. It does not run any action but can be used to organize tasks into build phases (just like build and check).

Gradle subprojects: how do I execute `check` only on main project?

I added a subproject to my gradle project and now whenever I execute the target check, it also runs the tests on my subproject.
I was hoping that I could run the check-task on the main project by specifying it as absolute task as
gradle :check
or
gradle ::check
but both commandlines still execute all tests.
Any idea?
There is no way around this, so you would need to restructure your build (I think check task of the parent is by definition a aggregate of the sub-projects). You can have sub-projects depending on each other, I assume that might be what you want to model in your build.
Let's assume you have the sub-projects core and myModule.
If you want myModule to depend on (and use the output of) core, you add a project dependency to myModule:
dependencies {
compile project(':core')
}
This will also setup the task dependencies correctly.
you can access the task-graph of a subproject from the main projects `build.gradle' and disable tasks.
This way you can disable the task when you call it on the main project, but it will still be enabled when you call the build file of the subproject directly:
project('subproject') {
//disable 'check' and 'test' task
gradle.taskGraph.whenReady {
tasks.find {it.name=="check"}.enabled=false
tasks.find {it.name=="test"}.enabled=false
}
}

Running tasks from an included gradle build in order

I have a gradle composite build project, which runs tasks from an included build. I am not able to make changes to the included build.
For a certain task, I want to first clean the included build and then build it. I.e., I want to do (loosely):
some-project:clean
some-project:build
where some-project is the included build. I tried doing this by setting up tasks which depend on those external tasks:
task cleanIncludedBuild {
dependsOn gradle.includedBuild("some-project").task("clean")
}
task buildIncludedBuild {
dependsOn cleanIncludedBuild
dependsOn gradle.includedBuild("some-project").task("build")
}
However, since there is no ordering between dependencies, this execution order is valid as declared:
some-project:build
some-project:clean
cleanIncludedBuild
buildIncludedBuild
Though this is of course not what I'm after.
I thought of doing something like
gradle.includedBuild("some-project").task("build").dependsOn cleanIncludedBuild
but the value returned by task() above is of type IncludedBuildTaskReference, which essentially are read-only references to the underlying task, and do not allow adding dependencies.

Conditionally skip subproject in multi-module Gradle build

Consider the following Gradle project structure:
- root
|- build.gradle
|- barProject
|- build.gradle
|- fooProject
|- build.gradle
where the root build.gradle configures its subprojects like so:
subprojects {
apply plugin: 'java'
//bunch of other stuff
}
Now when I call gradlew build on the root project it automatically configures both subprojects and then builds them - all well and good.
I also know that I can skip a specific task with various means (onlyIf(), [taskName].enabled = false, etc.) but when I utilize any of those on build Gradle still runs all the dependent tasks (compileJava, processResources, classes, etc.) until finally hitting build which it then skips.
My question is, is there any way to have Gradle stop and go to the next subproject right after the configuration phase?
EDIT:
To clarify; each subproject has a property called skip that is evaluated in the configuration phase.
Now when I call gradlew build on the root project I want Gradle to, ideally, check that property and if it's true then to completely skip the corresponding project.
Executing external task 'build'...
//check project.skip -> IF true -> skip project, go to :foo ELSE continue
:bar:compileJava
:bar:processResources UP-TO-DATE
:bar:classes
:bar:jar
:bar:startScripts
:bar:distTar
:bar:distZip
:bar:assemble
:bar:compileTestJava UP-TO-DATE
:bar:processTestResources UP-TO-DATE
:bar:testClasses UP-TO-DATE
:bar:test UP-TO-DATE
:bar:check UP-TO-DATE
:bar:build
//check project.skip -> IF true -> skip project, go to end ELSE continue
:foo:compileJava
:foo:processResources UP-TO-DATE
:foo:classes
:foo:jar
:foo:startScripts
:foo:distTar
:foo:distZip
:foo:assemble
:foo:compileTestJava UP-TO-DATE
:foo:processTestResources UP-TO-DATE
:foo:testClasses UP-TO-DATE
:foo:test UP-TO-DATE
:foo:check UP-TO-DATE
:foo:build
BUILD SUCCESSFUL
I hope this makes more sense
Well, first of all: An easy solution would be calling exactly the task(s) you need. In simple Gradle builds, task names are unique. However, in multi-project builds, each project can have a task with a specific name. This is the reason why Gradle introduced task paths. Task paths combine the unique project paths with the intra-project unique task names: :projectX:taskY.
Using project paths, you can easily specify the project-specific task you want to execute: :build for the build task in the root project and :<subproject>:build for the build task in a subproject. If a task name, e.g. build, is provided for a multi-project build, Gradle will search through any project (root and subs) for a task with the specified name and execute them all. This explains the current behaviour.
The task names for execution are managed by a StartParameter object of the Gradle Settings. These Settings can be modified in your settings.gradle file, where you also include subprojects:
include ':foo', ':bar'
startParameter.excludedTaskNames += ':foo:build'
This line excludes the build task of the foo subproject from the execution. You could add the subproject task path (even independent from the task name) to the excluded task names, if a specific condition is met. But, I did not find a way to access the Settings in your build file, so this solution can not be used during configuration phase. However, if your condition is solely based on project / system properties, they can be accessed from settings.gradle.
Another solution for the configuration phase came to my mind, but it's basically what you already mentioned, because it simply skips the tasks:
if (project.skip) {
project.tasks.all { task -> task.enabled = false }
}
This way, all tasks from the project (not only the build task) will be skipped.
Please consider, that tasks can also be executed, because they create a dependency for another project. Even gradle :bar:build will execute the task :foo:jar and its task dependencies, if foo is a project dependency of bar. You can disable this behaviour with the Gradle command line options -a or --no-rebuild or the following entry in your settings.gradle:
startParameter.buildProjectDependencies = false

Task with path 'build' not found in root project

I have a multiproject and after the last subproject is built, I'd like to process all jars.
Therefore I created a task in the root-project:
task install(dependsOn: 'build', type: Copy) {
doLast {
println "exec install task"
}
}
Upon calling ./gradlew install in the root directory, I'm facing this error:
FAILURE: Build failed with an exception.
* What went wrong:
Could not determine the dependencies of task ':install'.
> Task with path 'build' not found in root project 'foo'.
However, calling ./gradlew tasks shows me these tasks:
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
...
How can I achieve the desired functionality?
I assume, that your root project organizes the build, but does not define build action taken by itself. The build task is often defined by language plugins (in most cases via apply plugin: 'java'), but if your root project does not use any of them, it won't have a build task.
The description of the help task tasks, which you used, says:
Displays the tasks runnable from root project 'projectReports' (some of the displayed tasks may belong to subprojects).
The help task followes the same logic as the task activation via the command line. You can provide a task name and any task with the name in any subproject will be executed (thats why gradle build works).
But if you define a dependsOn dependency, the given string is evaluated as task path for a single task. Since each task name can only be used once in a project, the name is unique for tasks in the root project, but many tasks could be found, if subprojects would be considered. Therefor, one can use the syntax :<projectName>:<taskName> to identify tasks in subprojects.
Now, let's face your specific problem: If the install task should depend on the build task of one single subproject, you could use dependsOn ':<mySubproject>:build'. But I assume you want the install task to depend on each subproject build task, so I'd like to propose this approach:
task install(type: Copy) {
dependsOn subprojects*.tasks*.findByName('build').minus(null)
doLast {
println "exec install task"
}
}
This way, for each registered subproject, findByName('build') is called and the result (the found task or null) is put into a list, which is then used as task dependency list. I added the .minus(null) part to remove null entries from the list, because I am not sure how Gradle handles such entries in a dependency collection. If you are sure, that each subproject provides a build task, you can use getByName('build'), too.
EDIT: OP found the optionally recursive getTasksByName method, which suits this case even better than iterating over all subprojects manually:
dependsOn getTasksByName('build', true)

Resources