ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 01.01.2026
Просмотров: 1139
Скачиваний: 0
49
Multi-project Builds
The powerful support for multi-project builds is one of Gradle's unique selling points. This topic i also the most intellectually challenging.
49.1. Cross project configuration
Let's start with a very simple multi-project build. After all Gradle is a general purpose build tool a its core, so the projects don't have to be java projects. Our first examples are about marine life.
49.1.1. Defining common behavior
We have the following project tree. This is a multi-project build with a root project water and a subproject bluewhale.
Example 49.1. Multi-project tree - water & bluewhale projects
Build layout
water/
build.gradle
settings.gradle
bluewhale/
Note: The code for this example can be found at samples/userguide/multiproject/fir which is in both the binary and source distributions of Gradle.
settings.gradle
include 'bluewhale'
And where is the build script for the bluewhale project? In Gradle build scripts are optional. Obviously for a single project build, a project without a build script doesn't make much sense. Fo multiproject builds the situation is different. Let's look at the build script for thewater project and execute it:
Page 272 of 343
Example 49.2. Build script of water (parent) project
build.gradle
Closure cl = { task -> println "I'm $task.project.name" } task hello << cl
project(':bluewhale') { task hello << cl
}
Output of gradle -q hello
> gradle -q hello I'm water
I'm bluewhale
Gradle allows you to access any project of the multi-project build from any build script. The Project API provides a method called project(), which takes a path as an argument and returns the Project object for this path. The capability to configure a project build from any build script we call cross project configuration. Gradle implements this via configuration injection.
We are not that happy with the build script of the water project. It is inconvenient to add the task explicitly for every project. We can do better. Let's first add another project calledkrill to our multi-project build.
Example 49.3. Multi-project tree - water, bluewhale & krill projects
Build layout
water/
build.gradle
settings.gradle
bluewhale/
krill/
Note: The code for this example can be found at samples/userguide/multiproject/add which is in both the binary and source distributions of Gradle.
settings.gradle
include 'bluewhale', 'krill'
Now we rewrite the water build script and boil it down to a single line.
Page 273 of 343
Example 49.4. Water project build script
build.gradle
allprojects {
task hello << { task -> println "I'm $task.project.name" }
}
Output of gradle -q hello
> gradle -q hello I'm water
I'm bluewhale I'm krill
Is this cool or is this cool? And how does this work? The Project API provides a property allproje which returns a list with the current project and all its subprojects underneath it. If you call allproj with a closure, the statements of the closure are delegated to the projects associated with allproj
. You could also do an iteration via allprojects.each, but that would be more verbose.
Other build systems use inheritance as the primary means for defining common behavior. We also offer inheritance for projects as you will see later. But Gradle uses configuration injection as the usual way of defining common behavior. We think it provides a very powerful and flexible way of configuring multiproject builds.
49.2. Subproject configuration
The Project API also provides a property for accessing the subprojects only.
49.2.1. Defining common behavior
Example 49.5. Defining common behaviour of all projects and subprojects
build.gradle
allprojects {
task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
hello << {println "- I depend on water"}
}
Output of gradle -q hello
> gradle -q hello I'm water
I'm bluewhale
-I depend on water I'm krill
-I depend on water
Page 274 of 343
49.2.2. Adding specific behavior
You can add specific behavior on top of the common behavior. Usually we put the project specific behavior in the build script of the project where we want to apply this specific behavior. But as we have already seen, we don't have to do it this way. We could add project specific behavior for theb project like this:
Example 49.6. Defining specific behaviour for particular project
build.gradle
allprojects {
task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
hello << {println "- I depend on water"}
}
project(':bluewhale').hello << {
println "- I'm the largest animal that has ever lived on this planet."
}
Output of gradle -q hello
> gradle -q hello I'm water
I'm bluewhale
-I depend on water
-I'm the largest animal that has ever lived on this planet. I'm krill
-I depend on water
As we have said, we usually prefer to put project specific behavior into the build script of this project. Let's refactor and also add some project specific behavior to thekrill project.
Page 275 of 343
Example 49.7. Defining specific behaviour for project krill
Build layout
water/
build.gradle
settings.gradle
bluewhale/
build.gradle
krill/
build.gradle
Note: The code for this example can be found at samples/userguide/multiproject/spr
which is in both the binary and source distributions of Gradle.
settings.gradle
include 'bluewhale', 'krill'
bluewhale/build.gradle
hello.doLast { println "- I'm the largest animal that has ever lived on this p
krill/build.gradle
hello.doLast {
println "- The weight of my species in summer is twice as heavy as all hum
}
build.gradle
allprojects {
task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
hello << {println "- I depend on water"}
}
Output of gradle -q hello
> gradle -q hello I'm water
I'm bluewhale
-I depend on water
-I'm the largest animal that has ever lived on this planet. I'm krill
-I depend on water
-The weight of my species in summer is twice as heavy as all human beings.
Page 276 of 343
49.2.3. Project filtering
To show more of the power of configuration injection, let's add another project calledtropicalFi and add more behavior to the build via the build script of the water project.
49.2.3.1. Filtering by name
Example 49.8. Adding custom behaviour to some projects (filtered by project name)
Build layout
water/
build.gradle
settings.gradle
bluewhale/
build.gradle
krill/
build.gradle
tropicalFish/
Note: The code for this example can be found at samples/userguide/multiproject/add
which is in both the binary and source distributions of Gradle.
settings.gradle
include 'bluewhale', 'krill', 'tropicalFish'
build.gradle
allprojects {
task hello << {task -> println "I'm $task.project.name" }
}
subprojects {
hello << {println "- I depend on water"}
}
configure(subprojects.findAll {it.name != 'tropicalFish'}) {
hello << {println '- I love to spend time in the arctic waters.'}
}
Output of gradle -q hello
> gradle -q hello I'm water
I'm bluewhale
-I depend on water
-I love to spend time in the arctic waters.
-I'm the largest animal that has ever lived on this planet. I'm krill
-I depend on water
-I love to spend time in the arctic waters.
-The weight of my species in summer is twice as heavy as all human beings. I'm tropicalFish
-I depend on water
Page 277 of 343
The configure() method takes a list as an argument and applies the configuration to the
projects in this list.
49.2.3.2. Filtering by properties
Using the project name for filtering is one option. Using extra project properties is another. (See Section 16.4.2, “Extra properties”for more information on extra properties.)
Example 49.9. Adding custom behaviour to some projects (filtered by project properties)
Build layout
water/
build.gradle
settings.gradle
bluewhale/
build.gradle
krill/
build.gradle
tropicalFish/
build.gradle
Note: The code for this example can be found at samples/userguide/multiproject/tro
which is in both the binary and source distributions of Gradle.
settings.gradle
include 'bluewhale', 'krill', 'tropicalFish'
bluewhale/build.gradle
ext.arctic = true
hello.doLast { println "- I'm the largest animal that has ever lived on this p
krill/build.gradle
ext.arctic = true hello.doLast {
println "- The weight of my species in summer is twice as heavy as all hum
}
tropicalFish/build.gradle
ext.arctic = false
build.gradle
Page 278 of 343
allprojects {
task hello << {task -> println "I'm $task.project.name" }
}
subprojects { hello {
doLast {println "- I depend on water"} afterEvaluate { Project project ->
if (project.arctic) { doLast {
println '- I love to spend time in the arctic waters.' }
}
}
}
}
Output of gradle -q hello
> gradle -q hello I'm water
I'm bluewhale
-I depend on water
-I'm the largest animal that has ever lived on this planet.
-I love to spend time in the arctic waters. I'm krill
-I depend on water
-The weight of my species in summer is twice as heavy as all human beings.
-I love to spend time in the arctic waters.
I'm tropicalFish
- I depend on water
In the build file of the water project we use an afterEvaluate notification. This means that the closure we are passing gets evaluated after the build scripts of the subproject are evaluated. As the property arctic is set in those build scripts, we have to do it this way. You will find more on this topic in Section 49.6, “Dependencies - Which dependencies?”
49.3. Execution rules for multi-project builds
When we have executed the hello task from the root project dir things behaved in an intuitive way. All the hello tasks of the different projects were executed. Let's switch to thebluewhale dir and see what happens if we execute Gradle from there.
Example 49.10. Running build from subproject
Output of gradle -q hello
> gradle -q hello I'm bluewhale
-I depend on water
-I'm the largest animal that has ever lived on this planet.
-I love to spend time in the arctic waters.
The basic rule behind Gradle's behavior is simple. Gradle looks down the hierarchy, starting wit the current dir, for tasks with the name hello an executes them. One thing is very important to
Page 279 of 343
note. Gradle always evaluates every project of the multi-project build and creates all existing task objects. Then, according to the task name arguments and the current dir, Gradle filters the tasks
Page 280 of 343
which should be executed. Because of Gradle's cross project configurationevery project has to be evaluated before any task gets executed. We will have a closer look at this in the next section. Let's now have our last marine example. Let's add a task tobluewhale and krill.
Example 49.11. Evaluation and execution of projects
bluewhale/build.gradle
ext.arctic = true
hello << { println "- I'm the largest animal that has ever lived on this plane
task distanceToIceberg << { println '20 nautical miles'
}
krill/build.gradle
ext.arctic = true
hello << { println "- The weight of my species in summer is twice as heavy as
task distanceToIceberg << { println '5 nautical miles'
}
Output of gradle -q distanceToIceberg
> gradle -q distanceToIceberg 20 nautical miles
5 nautical miles
Here the output without the -q option:
Example 49.12. Evaluation and execution of projects
Output of gradle distanceToIceberg
> gradle distanceToIceberg :bluewhale:distanceToIceberg 20 nautical miles :krill:distanceToIceberg
5 nautical miles
BUILD SUCCESSFUL
Total time: 1 secs
The build is executed from the water project. Neither water nor tropicalFish have a task with the name distanceToIceberg. Gradle does not care. The simple rule mentioned already above is: Execute all tasks down the hierarchy which have this name. Only complain if there is no such task!
Page 281 of 343
49.4. Running tasks by their absolute path
As we have seen, you can run a multi-project build by entering any subproject dir and execute the build from there. All matching task names of the project hierarchy starting with the current dir are executed. But Gradle also offers to execute tasks by their absolute path (see also Section 49.5, “Project and task paths”):
Example 49.13. Running tasks by their absolute path
Output of gradle -q :hello :krill:hello hello
> gradle -q :hello :krill:hello hello I'm water
I'm krill
-I depend on water
-The weight of my species in summer is twice as heavy as all human beings.
-I love to spend time in the arctic waters.
I'm tropicalFish
- I depend on water
The build is executed from the tropicalFish project. We execute the hello tasks of the water
, the krill and the tropicalFish project. The first two tasks are specified by there absolute path, the last task is executed on the name matching mechanism described above.
49.5. Project and task paths
A project path has the following pattern: It starts always with a colon, which denotes the root project. The root project is the only project in a path that is not specified by its name. The path :bl corresponds to the file system path water/bluewhale in the case of the example above.
The path of a task is simply its project path plus the task name. For example :bluewhale:hello. Within a project you can address a task of the same project just by its name. This is interpreted as a relative path.
Originally Gradle has used the '/' character as a natural path separator. With the introduction of directory tasks (see Section 14.1, “Directory creation”)this was no longer possible, as the name of the directory task contains the '/' character.
49.6. Dependencies - Which dependencies?
The examples from the last section were special, as the projects had no Execution Dependencies. They had only Configuration Dependencies. Here is an example where this is different:
Page 282 of 343
49.6.1. Execution dependencies
49.6.1.1. Dependencies and execution order
Example 49.14. Dependencies and execution order
Build layout
messages/
settings.gradle
consumer/
build.gradle
producer/
build.gradle
Note: The code for this example can be found at samples/userguide/multiproject/dep
which is in both the binary and source distributions of Gradle.
settings.gradle
include 'consumer', 'producer'
consumer/build.gradle
task action << {
println("Consuming message: " + (rootProject.hasProperty('producerMessage') ? rootProject.producer
}
producer/build.gradle
task action << {
println "Producing message:"
rootProject.producerMessage = 'Watch the order of execution.'
}
Output of gradle -q action
> gradle -q action Consuming message: null Producing message:
This did not work out. If nothing else is defined, Gradle executes the task in alphanumeric order. Therefore :consumer:action is executed before :producer:action. Let's try to solve thi with a hack and rename the producer project to aProducer.
Page 283 of 343