本文是边看Gradle英文文档边做的笔记:Chapter 11. Composite builds
建议看的时候动手实验一下,帮助理解
什么是复合构建
简单地说,Gradle的复合构建就是一个构建包含了其它构建。
Gradle的复合构建跟多项目构建很相似,
唯一的区别是多项目构建引入的是单个project(引入的java依赖可以使用),
而复合构建引入的是一个完整的构建(除了引入的java依赖,引入的task也可以使用)。
可以先看以下代码示例,有个直观的区分:
多项目构建:
文件:my-app/build.gradle:
dependencies {
compile project(':util')
compile project(':web')
}
//这里假设有一个my-app的构建,my-app构建依赖了util工程和web工程。
复合构建:
文件:my-app/build.gradle:
dependencies {
compile "org.sample:util:1.0"
compile "org.sample:web:1.0"
}
//my-app构建没有直接依赖util和web。而是声明了对util和web的构建结果(class文件)的依赖
多项目构建和复合构建非常相似,因此比较难区分,我是从两种构建方式关注点上区分的。
- 多项目构建关注点是:如何组织多个项目,项目之间往往是存在业务逻辑联系的。比如一个商城项目包含了前台部分和后台管理部分,那可以分成两个子项目来做(fun-shop/front和fun-shop/admin)
- 复合构建关注点是:如何整合多个独立的项目,被引入的项目是可以独立运行的,但不一定有逻辑联系。复合构建就像万能胶水一样,能整合独立的gradle项目进来。再拿上面的商城举例,如果这个商城项目要引入微信支付,这时公司早就有人写好了一个独立的微信支付调用的模块(就叫awesome-wepay吧,假设人家也是用gradle构建的)。你只需要调用他封装好的简单的代码即可。但是由于人家的awesome-wepay是一个独立项目,而不是一开始就属于你的fun-shop项目(awesome-wepay不是fun-shop的子项目,两者关系是相互独立的),那这个时候,就需要用Gradle的复合构建功能来引入人家的awesome-wepay到你的fun-shop了。
复合构建适用的场景:
- 对多个独立开发的库进行组合
- 对一个复杂的多项目构建进行拆分,拆分成多个独立的模块
被复合构建(my-app)引用的构建(util、web),只会向复合构建提供构建的结果,而配置信息是不会被导入到复合构建中的。
如果被复合构建(my-app)包含的构建(util、web)中有依赖可以满足复合构建的依赖,会优先采用(util、web)该依赖。
Gradle默认会自行决定是否采用引入构建的依赖,但是官方建议最好显式声明(11.4, “Declaring the dependencies substituted by an included build”)
复合构建(my-app)可以直接声明对包含的构建(util、web)的task的依赖,
但是被包含的构建(util、web)由于它们是独立的,
被包含的构建(util、web)不能依赖复合构建(my-app)中的task,
也不能依赖其它被包含的构建的task。
如何声明复合构建
首先需要声明复合构建的依赖
下面的多个例子演示如何将2个独立开发的构建组合到一个复合构建里面。
在例子里面,my-utils是一个多项目构建,包含了2个java库的构建(number-utils和string-utils),
而my-app则是一个复合构建,my-app使用了这两个java库里面的函数。
需要注意的是,my-app不是直接依赖my-utils,而是声明对my-utils的两个java库的打包结果的依赖
声明my-app的依赖项:
my-app/build.gradle
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'idea'
group "org.sample"
version "1.0"
mainClassName = "org.sample.myapp.Main"
dependencies {
compile "org.sample:number-utils:1.0"//<--依赖number-utils子项目的产出结果
compile "org.sample:string-utils:1.0"//<--依赖string-utils子项目的产出结果
}
repositories {
jcenter()
}
方式1:通过gradle命令行的--include-build选项实现复合构建
目录结构是这样的:
/samples/compositeBuilds/basic
|--my-app
|--build.gradle
|--src/main/java
|--my-utils
|--number-utils
|--src/main/java
|--string-utils
|--src/main/java
|--build.gradle
|--setting.gradle
在my-app目录下运行命令行:gradle --include-build ../my-utils run
即可实现复合构建
这个命令行意思是:
通过gradle --include-build ../my-utils
命令告诉gradle先去执行my-utils的构建,my-utils如无意外会产出两个java库的构建,number-utils和string-utils目录下会出现build目录,build目录就是构建的产出结果。
然后,由于my-app/build.gradle中声明了对number-utils和string-utils产出结果的依赖(就是下面的那部分代码),所以my-app的构建也能成功执行。
dependencies {
compile "org.sample:number-utils:1.0"//<--依赖number-utils子项目的产出结果
compile "org.sample:string-utils:1.0"//<--依赖string-utils子项目的产出结果
}
最后,对my-app执行run任务,即运行my-app的代码。
方式2:在setting.gradle文件中使用includeBuild语句声明复合构建
实现如下:
my-app/build.gradle的配置依旧不变
......
dependencies {
compile "org.sample:number-utils:1.0"//<--依赖number-utils子项目的产出结果
compile "org.sample:string-utils:1.0"//<--依赖string-utils子项目的产出结果
}
......
my-app/setting.gradle
rootProject.name = 'my-app'
includeBuild '../my-utils'
方式2-补充:不修改my-app/setting.gradle文件实现复合构建
方式2中通过在my-app/setting.gradle文件中添加includeBuild '../my-utils'
配置来引入my-utils构建。
如果有特定场景要求不能修改my-app项目的任何文件该怎么办呢?这时可以采取另一种方式来实现,建立一个新的构建(即一个目录),在新构建中引入my-app和my-utils,注意,原来是在my-app中引入my-utils的。
实现如下:
目录结构是这样的:
/samples/compositeBuilds/basic
|--composite
|--build.gradle
|--setting.gradle
|--my-app
|--build.gradle
|--src/main/java
|--my-utils
|--number-utils
|--src/main/java
|--string-utils
|--src/main/java
|--build.gradle
|--setting.gradle
composite/setting.gradle:
//在composite构建中引入my-app构建和my-utils构建
rootProject.name='adhoc'
includeBuild '../my-app'
includeBuild '../my-utils'
由于我们新建了一个名为composite的构建来包含my-app构建和my-utils构建,但是composite构建到目前为止是没有run
任务的(原来的my-app构建中定义了mainClassName = "org.sample.myapp.Main"
所以能执行run
任务),那该怎么办呢?
答案就是我们在composite构建中定义一个的run
任务,把该run
任务的执行委派给my-app构建的run
任务:
composite/build.gradle:
apply plugin: 'idea'
defaultTasks 'run'
task run {
//composite构建中定义一个run任务,委派给my-app的run任务
dependsOn gradle.includedBuild('my-app').task(':run')
}
注意:一个构建需要满足什么条件才能被复合构建引入?
- 必须要有settings.gradle文件
- 构建本身不能是复合构建
- 构建的rootProject.name属性不能与其它被引入的构建的该属性相同
- 构建的rootProject.name属性不能与复合构建的最顶层级工程的该属性相同
- 构建的rootProject.name属性不能与复合构建的该属性相同
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。