Gradle batch task that invokes subproject and other tasks in order - gradle

I am writing gradle 1.4 build file for multimodule project. So there is root build.gradle that defines something like:
subprojects {
apply plugin: 'java'
...
which defines build task for all submodules. Submodules are included in settings.gradle and each module has its build file with defined dependencies.
Everything by-the-book, so far:) Now, in the main build file I've added some additional project-scope tasks, like: aggregateJavadoc (collects all javadocs into one) or bundleJar (creates bundle jar from all classes), etc. Each on works when invoked manually.
Now I need a task release that will
build all submodules (as invoked from command line - meaning, i dont want to manually write execute() for each submodule)
invoke additional tasks (using execute() I presume).
I tried dependsOn but the order of listed tasks is not followed. Also, dependent modules seems to be executed after release task execution. I tried several other ideas and failed.
Question: what would be the best way to create such batch task, that has to invoke something on all submodules and additionally to perform some more tasks? What would be the best gradle-friendly solution? Thanx!

It happened that this can be solved with simple dependency management.
So, we have many modules. Now, lets create additional tasks that depends on modules being build:
task aggregateJavadoc(type: Javadoc) {
dependsOn subprojects.build
task bundleJar(type: Jar) {
dependsOn subprojects.build
Finally, our release task would simply look like this:
task release() {
dependsOn subprojects.build
dependsOn aggregateJavadoc
dependsOn bundleJar
...
}
This will build subprojects first; not because it is listed first, but because additional tasks depends on building. Order of additional task is not important. This make sense the most to me.
EDIT
if one of your subprojects (i.e. modules) is non-java module, then you will have a problem building this project. What I do is to group submodules, like this:
def javaModules() {
subprojects.findAll {it.name.contains('jodd-')}
}
and then instead to refer to subprojects, use javaModules everywhere! For example:
configure(javaModules()) {
apply plugin: 'java'
...
and
task prj {
dependsOn javaModules().build
}
btw, I am using this 'dummy' task prj for dependsOn on all those additional projects that depends on building, to prevent repeating.

Related

Gradle - copy jar to another folder as part of the 'jar' task

I have a multi project gradle setup. I want to simply copy the generated jar file of any project anytime the jar is rebuild and thought this would work in my root project's subprojects closure:
task copyJarToGenerated(type: Copy) {
from jar
into "../my-generated-jars/"
}
copyJarToGenerated.mustRunAfter jar
But if I run the 'clean' task then 'jar' task of any sub project, my jar is generated under build/libs as usual but not copied.
Looking at the gradle output, it only runs compileJava, processResources, classes then jar. It isn't running copyJarToGenerated.
The method mustRunAfter does not define a task dependency, it just enforces a specific order. It basically says 'if both tasks are executed in a build (for whatever reason), then they are executed in the specified order'.
So you need to define the task dependency on your own:
jar.finalizedBy copyJarToGenerated
You could also just add copyJarToGenerated as a dependency of the lifecycle task build:
build.dependsOn copyJarToGenerated
Since you specify the task jar in the method from of your task, it is registered as a task input and therefor registered as a task dependency implicitly. So you won't need to define order with mustRunAfter anymore.
You may also consider using the property destinationDirectory of the task jar instead of creating a new task at all.

Task that executes a specific task in all included builds

I have a root project that includes quite a number of other projects via includeBuild in settings.gradle. All the subprojects have a task named publishToMavenLocal.
How can I define a task publishToMavenLocal in the root project that calls each publishToMavenLocal of each subproject without the need to manually define dependsOn for every subproject specifically?
This use-case is actually covered in the documentation on composite builds. The following code adapts the example from the documentation to your use-case:
task publishToMavenLocal {
dependsOn gradle.includedBuilds*.task(':publishToMavenLocal')
}

Calling Gradle task for only one subproject

How do I configure subprojects for Gradle and run a Gradle task from the build.gradle only for the current directory I'm executing the task from?
For example, suppose I have two projects in a parent folder:
root-project
project-1
project-2
build.gradle
settings.gradle
I have project 1 and 2 included in the settings.gradle file:
include(
'project-1',
'project-2'
)
I have a task configured in the build.gradle file that I would like to call. I've nested the tasks within a subprojects{} block:
subprojects {
...
task check(type: JavaExec) {
main = MAIN_CLASS
classpath = sourceSets.main.runtimeClasspath
}
...
}
However, the build.gradle is run for all subprojects that way.
How can I define all possible subprojects but only call a Gradle task for the current project I'm executing from (i.e., avoid calling the task for all subprojects)? I would like to do this with the current task name only (e.g., gradle check).
Invoking the task in a single project with gradle check is not possible.
You have two options:
Leverage the model Gradle proposes, where tasks can be prefixed by the project name in a multi project build which removes the need to navigate to subprojects.
Which means invoking gradle :project-1:check or gradle :project-2:check depending on which task you want to run.
Define the tasks to have different name in each subprojects. See task rules on how to define tasks with dynamic names.
Which means invoking gradle check1 or gradle check2

Gradle task at the end of build with a multiproject build

I am attempting to create my own task, that will create a package of all the artifacts generated during my multi-project build.
Currently what I do right is now just:
gradle build createPackage
Which gives output like the following:
:test-utility:compileJava UP-TO-DATE
:test-utility:processResources UP-TO-DATE
...
:test-utility:check UP-TO-DATE
:test-utility:build UP-TO-DATE
Creating Package...
Created.
BUILD SUCCESSFUL
Total time: 4.654 secs
With the createPackage task being:
task createPackage {
println "Creating Package..."
println "Created."
}
However, I would like to simply it by running just one command, so what would the best way to do this and ensure the order stays maintained?
Ideally I would like to call build within the createPackage task or append to the build task with my task.
Ok reading between the lines there are a few things to clear up
1) Your printlns are being run in the configuration phase, not the execution phase. See http://www.gradle.org/docs/current/userguide/build_lifecycle.html for more info.
2) You do not have a single 'build' task. gradle build on the command line will run the 'build' task of each of your subprojects. Your package task would need to depend on all of them. Something like..
apply plugin: 'java'
evaluationDependsOnChildren()
task createPackage(type:Zip) {
dependsOn subprojects*.build
}
3) You can be more declarative with gradle - just tell it what you want to package up and it will figure out what it needs to run. For example, you can say that you want to zip up all the jars from your subprojects.
apply plugin: 'java'
evaluationDependsOnChildren()
task createPackage(type:Zip) {
from subprojects*.jar
}
There are plenty of ways. One suggestion is to modify build to depend on createPackage to make sure you can call just gradle build. Then you want to enhance your task and tell what are its inputs (probably those are outputs of some other tasks like jar). Gradle will add these tasks to execution when you run the build and can re-run them based on up-to-date status of each task. This is documented in userguide - http://www.gradle.org/docs/current/userguide/more_about_tasks.html#sec:up_to_date_checks and later in a chapter on custom tasks.

Moving common code into `subprojects {}` block breaks build

I have a multi-project (multiple java subprojects) in Grade and I plan to upload it to a maven repository (currently a local one) including the sources and javadoc artifacts. I simply add the following code to each subproject and then maven install does the job:
apply plugin: 'maven'
task sourcesJar(type: Jar, dependsOn:classes) {
from sourceSets.main.allSource
classifier = 'sources'
}
task javadocJar(type: Jar, dependsOn:javadoc) {
from javadoc.destinationDir
classifier = 'javadoc'
}
artifacts {
archives jar
archives sourcesJar
archives javadocJar
}
Now its not that nice to have the same code in 10 subprojects, so I decided I will move this code into the subprojects { ... } configuration of the parent project. However, now it does not work anymore: Only empty sources and javadoc jars are built, the normal jar is ignored, and maven install does not copy the files anymore to the local maven repository. How to do this right?
Thanks.
A parent project's build script gets evaluated before its subprojects' build scripts. When you lift some code to a parent project's build script, you'll have to make sure that the evaluation order stays intact, i.e. that the lifted code doesn't eagerly read properties that are only set by a subproject's build script. For example, if javadoc.destinationDir is set by a subproject's build script, you'll either have to lift that code too, or defer evaluation of the from javadoc.destinationDir expression in the parent build script.
There are several techniques for deferring evaluation. Some properties (like an archive task's from and into) accept a closure; hence you can simply do from { javadoc.destinationDir }. Another option is to use hooks like gradle.projectsEvaluated {}, project.afterEvaluate {}, or task.doFirst {}. The first step in deciding which technique to use is to look up the property in question in the Gradle Build Language Reference.
Without seeing more of your build scripts, my best guess is that your particular problems are related to not lifting apply plugin: "java" into the parent build script's subprojects {} block.
An alternative to configuration injection (e.g. via subprojects {}) is to put common code into a separate build script, and have project build scripts include that code with apply from:. In some cases, this is an easier way to (selectively) share code than configuration injection. Often, the two approaches are used together.

Resources