Running tasks from an included gradle build in order - gradle

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.

Related

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
}
}

Execute gradle tasks on sub projects in order

we have a project structure such that
ParentProject
projA
projB
projC
projD
...
We have a bunch of tasks that we run on these projects from the parent project, calling gradle clean build make tag push remove
clean: removes the build directory for each project
build: compiles the source code
make: uses the compiled code to create a docker image (custom task)
tag: tags the docker image (custom task)
push: pushes the tagged docker image to our nexus repo (custom task)
remove: removes the local docker image (custom task)
all the tasks work as intended, but not exactly in the order we want.
our issue is disk space. creating all the docker images (we have many to make) takes several gigabytes of space. Gradle fails because it runs out of disk space before reaching the end. We thought we could solve this issue by simply removing the image after it is pushed and free the disk space, but the way gradle runs, that does not work.
Current, gradle executes in the following manner:
projA:clean
projB:clean
projC:clean
...
projA:build
projB:build
projC:build
...
projA:make
projB:make
projC:make
...
going through like this, everything tries to build before anything gets removed. The way we would like to run gradle is as follows:
projA:clean
projA:build
projA:make
projA:tag
projA:push
projA:remove
projB:clean
projB:build
projB:make
projB:tag
projB:push
projB:remove
...
this way the project cleans itself up and frees disk space before the next project starts building.
is there any way to do this?
edit: There may be times where not every task needs to be run. The image may already built and simply needs to be retagged. Or may need to be built for local testing, but not tagged or pushed.
Looks like tasks make, tag, push, remove are custom tasks.
If you want dependency among tasks, for example taskX should be executed whenever taskY is started, then you should use dependsOn property of task.It is also called as task dependencies.
below shows the build.gradle on parent project that make task of projA dependsOn clean task of projA. you should do this for remaining tasks of projA.
project('projA') {
task make(dependsOn: ':projA:build') {
doLast {
println 'make'
}
}
}
project('projA') {
task build {
doLast {
println 'build'
}
}
}
...
...
Then you should continue for remaining projects and remaining tasks. For example, ProjB:clean should dependOn projA:remove
project('projB') {
task clean(dependsOn: ':projA:remove') {
doLast {
println 'clean'
}
}
}
continue for other tasks in project B and remaining projects.
For more details please follow this link.
We found we can create a task of type 'GradleBuild' where we can specify what tasks we want to run within a single task.
task deploy(type: GradleBuild){
tasks = ['clean','build','make','tag','push','remove']
}
this solution prevents linking of tasks with dependencies, so we can still build sub projects individually without worrying about other tasks being run unnecessarily.

Gradle task dependency order

I have a problem with a custom gradle task : i would like to copy my android jar library and rename it after that it as executed a 'clean build'
Here is how i defined it :
task('CreateJar', type: Copy, dependsOn: [':mylibmodule:clean', ':mylibmodule:build']){
doLast {
from('build/intermediates/bundles/release/')
into('libs')
include('classes.jar')
rename('classes.jar', 'MyLib.jar')
}
}
The problem is that in the gradle log results, the 'clean' is done after the 'build' task, so that the lib is never copied to the destination folder :
...
:mylibmodule:testReleaseUnitTest
:mylibmodule:test
:mylibmodule:check
:mylibmodule:build
:mylibmodule:clean
:mylibmodule:CreateJar NO-SOURCE
I have also tried to change the order of tasks in the 'dependsOn:[]', but it does not change anything... Does anyone has any idea of where is my mistake ?
Thanks in advance
The dependsOn list does not impose any ordering guarantees. Usually what is listed first is executed first if there are not other relations that actually do impose ordering guarantees.
(One example is if clean depends on build, then it doesn't matter how you define it in that dependsOn attribute, becuase build will always be run before clean. That this is not the case is clear to me, thus in parentheses, just to clarify what I mean.)
To determine why finally build is run before clean I cannot say without seeing the complete build script. From what you posted it is not determinable.
Maybe what you are after is clean.shouldRunAfter build or clean.mustRunAfter build which define an ordering constraint without adding a dependency. So you can run each task alone, but if both are run, then their order is defined as you specified it. The difference between those two is only relevant if parallelizing task execution, then should run after means they could run in parallel iirc, must run after does not allow that.

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)

Creating a post build copy task with Gradle

I am struggling with the Gradle build lifecycle; specifically with the split between the configuration and execution phases. I have read a number of sections in the Gradle manual and have seen a number of ideas online, but have not found a solution to the following problem:
I want to run a specific task to produce an artifact at the end of my java-library-distribution build that is a flattened version of the runtime configuration jars. That is, I only want to produce the artifact when I run the specific task to create the artifact.
I have created the following task:
task packageSamplerTask(type: Tar, dependsOn: distTar) {
description "Packages the build jars including dependencies as a flattened tar file. Artifact: ${distsDir}/${archivesBaseName}-${version}.tar"
from tarTree("${distsDir}/${archivesBaseName}-${version}.tar").files
classifier = 'dist'
into "${distsDir}/${archivesBaseName}-dist-${version}.tar"
}
Although this task does produce the required artifact, the task runs during gradle's configuration phase. This behavior has the following consequences:
Irrespective of which task I run from the command line, this packageSamplerTask task is always run, often unnecessarily; and
If I clean the project, then the build fails on the next run because $distsDir doesn't exist during the configuration phase (obviously).
It appears that if I extend the Copy task in this manner I'm always going to get this kind of premature behavior.
Is there a way to use the << closure / doLast declarations to get what I want? Or is there something else I'm missing / should be doing?
Update
After further work I have clarified my requirements, and resolved my question as follows (specifically):
"I want to package my code and my code's dependencies as a flat archive of jars that can be deployed as a jMeter plugin. The package can then be installed by unpacking into the jMeter lib/ext directory, as is. The package, therefore, must not include the jMeter jars (and their dependencies) which are used for building and testing"
Because Gradle doesn't appear to support the Maven-like provided dependency management, I created a new configuration for my package which excludes the jMeter jars.
configurations {
jmpackage {
extendsFrom runtime
exclude group: 'org.apache.jmeter', name: 'ApacheJMeter_core', version: '2.11'
exclude group: 'org.apache.jmeter', name: 'ApacheJMeter_java', version: '2.11'
}
}
And then created the following task (using the closure recommendation from Peter Niederwieser):
task packageSamplerTask(type: Tar, dependsOn: assemble) {
from { libsDir }
from { configurations.jmpackage.getAsFileTree() }
classifier = 'dist'
}
This solution appears to work, and it allows me to use just theGradle java plugin, too.
The task declaration is fine, but the flattening needs to be deferred too:
...
from { tarTree("${distsDir}/${archivesBaseName}-${version}.tar").files }
Also, the Tar file should be referred to in a more abstract way. For example:
from { tarTree(distTar.archivePath).files }
First your task isn't executed in the configuration phase but like EVERY task it is configured in that phase. And your closure is just a configuration of your task (a Configuration closure, not an Action closure). That is why your code is "executed" in the configuration phase".
If you want your code to be executed in the execution phase have to write it in a doLastclosure or doFirst. But in your case it is better to keep it in a configuration closure, because you are configuring your task.
To make sure your build doesn't fail because of the missing folder, you can create it with distsDir.mkdirs().

Resources