跨项目配置
虽然子项目之间可以完全隔离单独配置,但是子项目直接有相同特征的情况也是很常见的,多个项目共享配置是更好的选择。
配置和执行
在gradle学习-十八-构建的生命周期这一篇中我们已经讲过Gradle构建过程中的各个阶段,我们继续扩展到多项目构建,来看看多项目构建中的配置阶段和执行阶段。这里所说的配置指的就是build.gradle
文件的执行,这意味着下载所有使用apply plugin
定义的插件。默认情况下,一个任务执行之前所有的项目的配置都会被执行。也就是单个项目的单个任务被请求时,多项目构建中的所有项目都会首先运行配置。这样做的目的是为了可以灵活的访问和更改Gradle项目的任意模块。
按需配置
配置注入特性和访问完整的项目模块都成为可能,因为在执行项目之前每一个项目都进行了配置。但是在一些超大的项目构建面前就显得不那么有效了,比如有数百多个层级嵌套的子项目,大型多项目构建在配置阶段所花的时间就会有点让人难以忍受了,伸缩性对于Gradle来说成为一个非常重要的需求,幸运的是这个需求在1.4版本中就完成了,叫做”按需配置”
按需配置模式仅仅试图对和请求任务相关的项目进行配置,也就是说仅仅会执行组成本次构建的项目的build.gradle
文件。通过这种方式,可以极大的缩短超大项目的配置时间。长远的看,这种模式会成为构建的默认模式也是唯一模式,不过按需配置特性还在孵化阶段,并不能保证每次构建都正确执行,但是这个特性在解耦项目的多项目构建中运行的非常好。
按需配置模式下,工程按照如下进行配置:
- 首先根项目总是会配置,通过这种方式典型的公共配置会被支持,比如
allprojects
和subprojects
的脚本块 - 在执行构建所在目录对应项目也会被配置,但是只有在Gradle不会明确执行任何任务的情况下,这样按需配置时默认的任务就会被执行。
- 支持标准的工程依赖,相关的项目都会被配置,如果A项目compile的classpath中依赖于B项目,那么构建A项目,将会触发两个项目的配置
- 支持通过任务路径声明的依赖,并且相关项目也会被配置。例如
someTask.dependsOn(":someOtherProject:someOtherTask")
- 通过任务路径在命令行下或者Tooling API中执行任务,那么相关的项目也会被配置,比如构建
projectA:projectB:someTask
将会引起projectB项目被配置
这个特性的激活可以通过属性文件gradle.properties
配置org.gradle.configureondemand=(true,false)
,也可以通过命令行选项--configure-on-demand, --no-configure-on-demand
公共行为定义
让我们来看下project tree
的例子,这有个多项目构建,根项目是water
,子项目是bluewhale
构建布局
── water\
│ ├── bluewhale\
│ ├── build.gradle
│ └── settings.gradle
settings.gradle
include 'bluewhale'
你会发现怎么bluewhale
项目没有构建脚本,在Gradle中构建脚本是可选的。诚然对于单项目构建来说,没有构建脚本是没有意义的,但是对于多项目来说就不一样了,让我们继续看water
项目的脚本,并且执行:
build.gradle
Closure cl = { Task task ->
println "I'm $task.project.name"
}
task hello {
doLast(cl)
}
project('bluewhale') {
task hello {
doLast(cl)
}
}
然后执行任务
± % gradle -q hello
I'm water
I'm bluewhale
Gradle允许你在任意脚本中访问多项目构建的任意项目,Project API提供了project()
方法,可以传入路径,获得路径对应的project对象,从任意脚本配置项目构建的能力,我们把它叫做跨项目配置,Gradle通过configuration injection
注入实现这个功能
为每个项目添加相同的任务,这看起来很不方便,我们来改进一下,首先为我们的多项目构建添加一个krill
的子项目
构建布局
├── water/
│ ├── bluewhale/
│ ├── build.gradle
│ ├── krill/
│ ├── settings.gradle
settings.gradle
include 'bluewhale', 'krill'
在修改一下构建脚本
allprojects {
task hello {
doLast { Task task ->
println "I'm $task.project.name"
}
}
}
执行任务的输出
± % gradle -q hello
I'm water
I'm bluewhale
I'm krill
Gradle的Project API提供了allprojects
属性,它会返回当前项目和在它之下的所有子项目,如果使用闭包调用allprojects
,那么闭包的statements会委托给与allprojects
关联的所有项目。
其他的构建系统都是通过继承来实现公共行为的定义,当然Gradle也支持,我们之后再讲,但是Gradle的configuration injection
作为定义公共行为的通用方式,看起来很强力一些。
还有一种方式就是通过通用的外部脚本来共享配置,我们之后也会讲到
子项目配置
Gradle还提供了仅仅访问子项目的属性
定义公共行为
build.gradle
allprojects {
task hello {
doLast { Task task ->
println "I'm $task.project.name"
}
}
}
subprojects {
hello {
doLast {
println " - I depend on water"
}
}
}
执行任务结果如下:
± % gradle -q hello
I'm water
I'm bluewhale
- I depend on water
I'm krill
- I depend on water
你可能已经注意到有两段代码使用到了hello任务,第一段是带task关键字的表示创建hello任务并提供一些基本的配置,第二段不带task关键字,表示在之前的基础上附件上一些配置。你可也只创建一次任务,但是之后可以附加任意数量的配置
增加特定的行为
我们可以在公共的行为上再增加一些特定的行为,通常我们会把特定的行为房子对应项目的构建脚本中,在那里可以apply这些行为。但是也可以在根项目的构建脚本中这么做:
build.gradle
allprojects {
task hello {
doLast { Task task ->
println "I'm $task.project.name"
}
}
}
subprojects {
hello {
doLast {
println " - I depend on water"
}
}
}
project(':bluewhale').hello {
doLast {
println " - I'm the largest animal that has ever lived on this planet."
}
}
执行任务的结果
± % 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
我们再来试下常用的定义特性行为的方法,就是将特定行为定义在对应项目的构建脚本中
构建布局
water
├── krill/
│ └── build.gradle
├── bluewhale/
│ └── build.gradle
├── build.gradle
└── settings.gradle
settings.gradle
include 'bluewhale','krill'
build.gradle
allprojects {
task hello {
doLast { Task task ->
println "I'm $task.project.name"
}
}
}
subprojects {
hello {
doLast {
println " - I depend on water"
}
}
}
bluewhale/build.gradle
hello.doLast {
println " - I'm the largest animal that has ever lived on this planet."
}
krill/build.gradle
hello.doLast {
println "- The weight of my species in summer is twice as heavy as all human beings."
}
然后执行任务
± % 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.
项目过滤
为了展示configuration injection
的威力,我们再增加一个tropicalFish
项目,然后通过water
项目来增加更多的行为
更加名字过滤
构建布局
water
├── tropicalFish/
├── krill/
│ └── build.gradle
├── bluewhale/
│ └── build.gradle
├── build.gradle
└── settings.gradle
settings.gradle
include 'bluewhale','krill','tropicalFish'
build.gradle
allprojects {
task hello {
doLast { Task task ->
println "I'm $task.project.name"
}
}
}
subprojects {
hello {
doLast {
println " - I depend on water"
}
}
}
configure(subprojects.findAll { it.name != 'tropicalFish' }) {
hello.doLast {
println " - I love to spend time in the arctic waters"
}
}
然后执行任务
± % 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
configure()
方法将会接受一个列表参数,并将配置应用到列表中的项目。
根据属性过滤
除了用名字过滤,还可以通过属性来过滤
构建布局
water
├── krill/
│ └── build.gradle
├── tropicalFish/
│ └── build.gradle
├── bluewhale/
│ └── build.gradle
├── build.gradle
└── settings.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 planet."
}
krill/build.gradle
ext.arctic=true
hello.doLast {
println "- The weight of my species in summer is twice as heavy as all human beings."
}
tropicalFish/build.gradle
tropicalFish=false
build.gradle
allprojects {
task hello {
doLast { Task 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"
}
}
}
}
}
执行任务的结果
± % 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
在water项目的构建文件中我们使用afterEvaluate
通知,这意味着我们在所以的子项目完成赋值之后传递了一个闭包再次给它们赋值。由于arctic
属性在这些项目的构建脚本之中,因此我们必须这么做。
多项目构建的执行规则
如果我们从跟项目water项目去执行hello任务,这样可以非常直观的看到所有任务的hello任务都被执行了,但是如果我们在子目录执行hello任务呢,比如bluewhale子项目,我们来看一下执行任务的结果
± % 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
背后的基本规则非常简单,Gradle会从当前目录往下看各个层级,如果遇到hello名字的任务就会执行。有一点非常重要,Gradle会赋值于多项目构建的每一个项目,并且创建每一个任务对象,然后更加任务名和当前目录,去过滤那些应该被执行的任务。因为Gradle的跨项目特性,在任意任务执行之前所有的项目都应该被配置,我们之后再进行深入研究。我们先来继续看海产的例子,我们再分别给bluewhale
和krill
项目增加一个任务
bluewhale/build.gradle
ext.arctic = true
hello.doLast {
println " - I'm the largest animal that has ever lived on this planet."
}
task distanceToIceberg {
doLast {
println "20 nautical miles"
}
}
krill/build.gradle
ext.arctic = true
hello.doLast {
println " - The weight of my species in summer is twice as heavy as all human beings."
}
task distanceToIceberg {
doLast {
println "10 nautical miles"
}
}
执行任务结果
± % gradle -q distanceToIceberg
20 nautical miles
10 nautical miles
去掉-q看一下:
± % gradle distanceToIceberg
> Task :bluewhale:distanceToIceberg
20 nautical miles
> Task :krill:distanceToIceberg
10 nautical miles
BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed
执行环境是water
项目中,但是water
项目和tropicalFish
项目根本就没有这个任务,但是Gradle并不关心这些,它会从含有distanceToIceberg
任务的项目中去执行构建。
通过绝对路径去执行任务
我们之前看到,可以进入多项目构建的任意目录来执行构建,它会从当前目录开始的层级去匹配相同的任务名而执行构建。但是Gradle还提供一种方法,就是通过绝对路径来执行任务
在tropicalFish目录中执行任务的输出:
± % 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 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 tropicalFish
- I depend on water
通过结果我们知道我们执行了water
,krill
和tropicalFish
的hello
任务。前两个任务是通过绝对路径指定的,最后一个任务是采用之前讲到的名字匹配规则执行的。
项目和任务的路径
项目的路径有两个部分:可选的冒号开始代表根项目,根项目是唯一不需要指定名字的项目,剩下的部分用冒号分割,后一个是前一个的子项目。
任务的路径就是项目的路径加上任务名,比如:bluewhale:hello
。
如果不带冒号开始,那么就可以理解为项目的相对路径。
依赖性
我们之前的章节的项目非常特别,他们只有配置依赖,而没有执行依赖,我们这节就来看下他们的区别
执行依赖
依赖性和执行顺序
构建布局
messages
├── consumer/
│ └── build.gradle
├── producer/
│ └── build.gradle
├── build.gradle
└── settings.gradle
build.gradle
ext.producerMessage = null
settings.gradle
include 'consumer', 'producer'
producer/build.gradle
task action {
doLast {
println "Producing message:"
rootProject.producerMessage = "Watch the order of execution."
}
}
consumer/build.gradle
task action {
doLast {
println "Consuming message: ${rootProject.producerMessage}"
}
}
执行任务的结果
± % gradle -q action
Consuming message: null
Producing message:
是不是有点懵,执行顺序和我们预期的不一样,我们试着通过hack的方式来修改一下,把producer
的项目名修改成aProducer
构建布局
messages
├── consumer/
│ └── build.gradle
├── aProducer/
│ └── build.gradle
├── build.gradle
└── settings.gradle
build.gradle
ext.producerMessage = null
settings.gradle
include 'consumer', 'aProducer'
aProducer/build.gradle
task action {
doLast {
println "Producing message:"
rootProject.producerMessage = "Watch the order of execution."
}
}
consumer/build.gradle
task action {
doLast {
println "Consuming message: ${rootProject.producerMessage}"
}
}
然后再执行任务:
± % gradle -q action
Producing message:
Consuming message: Watch the order of execution.
现在可以达到我们的预期效果了,但这种方式可不是依赖,它只是改变了执行顺序,怎么改变的留给读者思考一下
跨工程依赖的本质
当然跨项目的依赖可不仅限于同名任务,让我们来改下两个子项目的任务的名字。
producer/build.gradle
task produce {
doLast {
println "Producing message:"
rootProject.producerMessage = "Watch the order of execution."
}
}
consumer/build.gradle
task consume(dependsOn:':producer:produce') {
doLast {
println "Consuming message: ${rootProject.producerMessage}"
}
}
执行任务的结果:
± % gradle -q consume
Producing message:
Consuming message: Watch the order of execution.
配置时依赖
我们再给producer
项目添加一个属性,然后创建consumer
和producer
之间的配置时依赖。
producer/build.gradle
rootProject.producerMessage = "Watch the order of execution."
consumer/build.gradle
def message = rootProject.producerMessage
task consume {
doLast {
println "Consuming message: ${message}"
}
}
执行任务如下:
± % gradle -q consume
Consuming message: null
默认的赋值顺序是按项目的字母顺序来的,因此consumer
总是在producer
之前被赋值,producerMessage
总是在consumer
读取之后才会被赋值,Gradle针对这种情况也提供了解决方案。
consumer/build.gradle
evaluationDependsOn(":producer")
def message = rootProject.producerMessage
task consume {
doLast {
println "Consuming message: ${message}"
}
}
执行任务如下:
± % gradle -q consume
Consuming message: Watch the order of execution.
使用在evaluationDependsOn
方法会让consumer
被赋值之前先对producer
进行赋值,这样做这是为了讲明白这种情况,但是实际操作中会直接去读关键的属性就可以了。
consumer/build.gradle
task consume {
doLast {
println "Consuming message: ${rootProject.producerMessage}"
}
}
然后执行任务如下:
± % gradle -q consume
Consuming message: Watch the order of execution.
配置时依赖和执行时依赖是非常不同的,配置依赖发生在项目之间,而执行时依赖是发生在任务之间。还要注意即使构建的是从子项目开始,所有项目也会全部被配置,通常情况下都是先配置父项目再配置子项目。
如果你想改变这种顺序,你可以使用evaluationDependsOnChildren()
方法。
在相同的嵌套级别上,配置顺序取决于项目名在字母数字上的顺序,最常见的就是拥有相同生命周期的多项目构建,比如所以的项目都是java插件的项目,他们根据这种顺序自然而然的建立起了某种依赖,有时候依赖并不需要显示的申明。
实例讲解
让我来看个典型的实例,一个根项目包含2个子web项目,根项目为这两个web项目创建发布包,在这个示例中我们仅仅创建一个构建文件并且使用跨项目配置。
构建布局
webDist
├── hello/
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── sweetop/
│ └── testgradle/
│ └── HelloServlet.java
├── date/
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── sweetop/
│ └── testgradle/
│ └── DateServlet.java
├── build.gradle
└── settings.gradle
settings.gradle
include 'date','hello'
build.gradle
allprojects {
apply plugin: 'java'
group 'com.sweetop.testgradle'
version '1.0'
}
subprojects {
apply plugin: 'war'
repositories {
jcenter()
}
dependencies {
compile 'javax.servlet:servlet-api:2.5'
}
}
task explodedDist(type: Copy) {
subprojects {
from tasks.withType(War)
}
into "$buildDir/explodedDist"
}
这种依赖关系很有趣,显然date
和hello
对webDist
有配置时依赖,因为子项目的构建依赖都是webDist
注入的。执行依赖又是另一面,webDist
项目又要依赖于date
和hello
项目的构建输出。还有第三种依赖就是webDist
配置依赖于date
和hello
,因为它要知道archivePath
的具体路径,但是他是在运行时获取这些信息的,因此就不存在所谓的循环依赖了。
像这样的依赖问题,在日常构建中都是比较常见的。
项目lib依赖
想象一下,如果一个项目在编译路径中需要另外一个项目生产的jar包,而且不仅需要jar还需要这种依赖传递关系,这种情况下怎么办。显然这在java项目构建中经常会见到。Gradle也完美的解决了这种问题。
构建布局
java
├── build.gradle
├── settings.gradle
├── shared/
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── sweetop/
│ └── testgradle/
│ └── shared/
│ └── Helper.java
├── api/
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── sweetop/
│ └── testgradle/
│ ├── apiImpl/
│ │ └── PersonImpl.java
│ └── api/
│ └── Person.java
└── services/
└── personService/
└── src/
├── test/
│ └── java/
│ └── services/
│ └── PersonServiceTest.java
└── main/
└── java/
└── com/
└── sweetop/
└── testgradle/
└── services/
└── PersonService.java
这里有三个子项目shared
,api
和personService
,personService
依赖于其他两个子项目,api
依赖于shared
项目,services
也是子项目,但是它只作为一个容器存在,既没有构建脚本,也没有其他额外的注入。
settings.gradle
include 'api','shared','services:personService'
build.gradle
subprojects {
apply plugin: 'java'
group = "com.sweetop.testgradle"
version = "1.0"
repositories {
jcenter()
}
dependencies {
testCompile "junit:junit:4.12"
}
}
project(":api") {
dependencies {
compile project(":shared")
}
}
project(":services:personService") {
dependencies {
compile project(":shared"),project(":api")
}
}
所有的构建逻辑都写在根项目的build.gradle
中,lib依赖是运行时依赖的一种特别方式,它可以首先构建其他项目,并且把生成的jar放在当前的classpath中,也可以传递依赖,将依赖的依赖的jar也放进来。在我们的例子中,他就会先构建shared
项目,再构建api
项目,最后才构建services
项目。如果你进入到api
目录,执行gradle compile
,这种依赖关系也会照样进行,它会先构建shared
项目,再构建api
项目。
禁用项目之间的依赖
有时候你需要禁用掉这种依赖,你可以在命令行增加-a
选项。
并行构建
随着越来越多的cpu核数出现在开发者电脑和CI服务器上,如何应用这些多核对Gradle来说变得很重要,并行构建主要想解决如下问题:
- 减少多项目构建总的构建时间
- 让小项目的构建结果尽快返回,而不需要等其他项目构建完成
尽管通过Test.setMaxParallelForks(int)
已经让Gradle可以做到测试上的并行,但是这里说的是项目构建过程中的并行,并行执行是个孵化中的特性,使用它之前我们来看下它是如何工作的。
并行构建可以让解耦的项目在多项目构建中并行执行,虽然并行构建在配置阶段不要求强制解耦,但是Gradle为完全解耦的项目提供了更为强大的一组特性,这些特性包括:
- 按需配置
- 并行配置
- 配置重用
- 项目级别的up-to-date检查
- 在构建项目依赖时优先使用以及构建好的工件
开启并行构建的方式有两种,一种是在命令行下使用--parallel
选项,另外一种就是配置Gradle的属性org.gradle.parallel=true
,如果你不特别指定并行的线程数,那么默认会以可以用的CPU核数作为线程数。每个wooker只专注的给定的项目去执行任务。任务依赖是同样支持的,worker会先执行上游任务。
需要注意的是,根据字母数字顺序的依赖这并行构建中就无法保证顺序了,因为在并行模式中无法确定哪些任务先完成,因此你需要明确的声明任务依赖和任务的输入输出,来避免任务的执行顺序问题。
解耦项目
Gradle允许任何项目在执行阶段和配置阶段访问其他项目,虽然为构建者提供了比较大的权限和灵活性,但同时也限制了Gradle构建这些项目的灵活性。比如它会影响多项目并行构建,配置项目的单个子集和使用预先存在的工件来代替项目依赖的正确性。
如果两个项目不直接访问彼此的项目模型,那么它们可以称为解耦。解耦的项目仅仅可以通过声明的依赖相互作用:项目依赖和任务依赖。任何其他方式的项目直接的交互都会被耦合(比如修改另一个项目的对象或者读取另一个项目中的值)。配置阶段进行耦合的结果就是如果执行configuration on demand
那么可以会有各种缺陷存在。在执行阶段耦合的结果就是,如果执行并行构建,那么因为有些任务运行的太迟,而达不到并行构建的效果。Gradle并不会去视图检测耦合并警告用户,因为引入耦合的可能性太多了。
最普遍的耦合的方式就是配置注入,它可能并不是立即显现,但是Gradle的一些特性比如关键字allprojects
和subprojects
会让你的项目自动被耦合。这些关键字通常在根项目中的build.gradle
文件中出现,根项目除了一些常定义的配置以外也没有别的作用,但是Gradle来说根项目也是一个完全的项目,并且通过allprojects
耦合到所有子项目中,根项目和子项目的耦合并不会影响configuration on demand
,但是在任意子项目中使用allprojects
和subprojects
都会影响。
这意味着使用任意形式的共享构建脚本逻辑或者配置注入(allprojects
, subprojects
)都会引起项目的耦合,我们在扩展项目结构的概念并且使用解耦项目的特性时,我们还需要引入新的特性来解决常见用例(比如配置注入),使得它们不会被耦合。
为了更好的使用跨项目配置,而不引起并行和按需配置产生的问题,需要遵循以下规则:
- 避免子项目的
build.gradle
引用其他子项目 - 避免在执行阶段更改其他项目的配置
多项目构建和测试
对于单个项目来说,java插件的build任务通常用于编译,测试和执行代码样式检查(如果使用了CodeQuality插件)。而在多项目构建中,通常需要多个项目都做这些事情,buildNeeded
和buildDependents
可以帮助做到这些。
在我们上一个例子中:services:personservice
项目依赖于:api
和:shared
项目,:api
也依赖于:shared
项目。
假设你在:api
项目中做了一下更改,在执行clean任务后还没有执行build任务,你想要构建必须支持的jar,但是仅仅在更改的项目上执行代码质量检查和测试任务,那么你可以使用build
任务。
gradle :api:build
:shared:compileJava
:shared:processResources NO-SOURCE
:shared:classes
:shared:jar
:api:compileJava
:api:processResources NO-SOURCE
:api:classes
:api:jar
:api:assemble
:api:compileTestJava NO-SOURCE
:api:processTestResources NO-SOURCE
:api:testClasses UP-TO-DATE
:api:test NO-SOURCE
:api:check UP-TO-DATE
:api:build
BUILD SUCCESSFUL in 0s
4 actionable tasks: 4 executed
当你在一个标准的开发周期中工作时,你需要不断重复:api
项目,而你明确的知道只在这个项目中更改了文件,你可能不想要承担:shared:compile
的开销来查看:shared
项目更改了什么,你可以选择使用-a
选项,它会用缓存中的jars来解决项目的lib依赖问题而不会去再次构建依赖的项目,我们再构建一次:
gradle -a :api:build
:api:compileJava
:api:processResources NO-SOURCE
:api:classes
:api:jar
:api:assemble
:api:compileTestJava NO-SOURCE
:api:processTestResources NO-SOURCE
:api:testClasses UP-TO-DATE
:api:test NO-SOURCE
:api:check UP-TO-DATE
:api:build
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
如果你是刚刚从版本控制系统中拿到了最新版本的源代码,那么你将不仅仅是想构建所依赖的项目,还想测试一下它们,buildNeeded
将会测试所有testRuntime
配置的lib依赖的项目。
gradle -a :api:buildNeeded !3626
:api:compileJava UP-TO-DATE
:api:processResources NO-SOURCE
:api:classes UP-TO-DATE
:api:jar UP-TO-DATE
:api:assemble UP-TO-DATE
:api:compileTestJava NO-SOURCE
:api:processTestResources NO-SOURCE
:api:testClasses UP-TO-DATE
:api:test NO-SOURCE
:api:check UP-TO-DATE
:api:build UP-TO-DATE
:shared:compileJava
:shared:processResources NO-SOURCE
:shared:classes
:shared:jar
:shared:assemble
:shared:compileTestJava NO-SOURCE
:shared:processTestResources NO-SOURCE
:shared:testClasses UP-TO-DATE
:shared:test NO-SOURCE
:shared:check UP-TO-DATE
:shared:build
:shared:buildNeeded
:api:buildNeeded
BUILD SUCCESSFUL in 1s
4 actionable tasks: 2 executed, 2 up-to-date
你可能重构了:api
项目的某些部分,该项目应用到了其他项目中,如果你有一些类型方面的更改,那么仅仅测试:api
项目还是不够的,你还要测试那些依赖于:api
的项目,buildDependents
将会测试所有在testRuntime
配置中项目lib依赖于:api
的项目。
gradle -a :api:buildDependents !3628
:api:compileJava
:api:processResources NO-SOURCE
:api:classes
:api:jar
:api:assemble
:api:compileTestJava NO-SOURCE
:api:processTestResources NO-SOURCE
:api:testClasses UP-TO-DATE
:api:test NO-SOURCE
:api:check UP-TO-DATE
:api:build
:services:personService:compileJava
:services:personService:processResources NO-SOURCE
:services:personService:classes
:services:personService:jar
:services:personService:assemble
:services:personService:compileTestJava
:services:personService:processTestResources NO-SOURCE
:services:personService:testClasses
:services:personService:test
:services:personService:check
:services:personService:build
:services:personService:buildDependents
:api:buildDependents
BUILD SUCCESSFUL in 2s
8 actionable tasks: 8 executed
最终,你可能需要构建和测试所有项目,那么你在根项目中执行构建任务即可,它的所有子项目都会将执行同名的任务,因此你可以在根项目中使用gradle build
来构建和测试所有项目。
多项目和buildSrc
在多项目中仅仅在根项目中有一个buildSrc
目录即可