Multi-project builds
随着大型的单项目构建发展所面临的各种挑战,多项目构建应运而生。
多项目构建的结构如下:包括一个root顶层项目(包括build和setting脚本),之下再包含一个或多个子项目(包含build脚本):
root-project ├── build.gradle ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── log ├── settings.gradle └── sub-project ├── build.gradle └── src ├── main │ └── java │ └── App.java └── test └── java └── AppTest.java
Multi-project builds的特点是:
- 包含多个独立模块,且每个模块可独自进行构建,如Android应用构建;
- 各个子项目可以拥有独立的构建脚本,注意子项目的构建脚本是非必须的;
- 根项目的配置文件可以被整个项目所共享,即公共配置应该在根项目的构建脚本声明;
- 不一定都是build.gradle,也可以是.gradle后缀结尾
注意:不管是单个模块或多个模块task的构建,都会在task执行之前完成所有的构建配置(比如插件下载与使用等)。因此,小的配置的变动都需要重新配置整个项目。虽然gradle提出了Configuration on demand的方式,但稳定性难以保证。
在Multi-project builds中,从配置的作用范围来看,可粗略地分为cross project configuration和subproject configuration两种。
首先是cross project configuration:构建的配置是跨越脚本而覆盖整个项目的,比如在某个子项目的配置脚本中直接修改另一个子项目的配置脚本:
Closure cl = { task -> println "I'm $task.project.name" } task('hello').doLast(cl) project(':bluewhale') { task('hello').doLast(cl) }
使用allprojects会更简洁,注意:allprojects作用的范围是本项目及其子项目。
allprojects { task hello { doLast { task -> println "I'm $task.project.name" } } }
总之,相比其它的构建系统,gradle的这种配置方式是以configuration injection的方式来实现。
另一是subproject configuration:子项目的配置行为分为两种,不仅能配置项目构建的通用配置,还能实现特定配置、项目过滤配置等功能:
- common behavior:公共的配置,同样借助于allprojects和subprojects来完成;
- specific behavior:特定配置,可在私有构建脚本上或在公共构建脚本中使用project来完成;
task依赖:无依赖的同名task,执行顺序是字母顺序。为了保证的多个task的执行顺序,可借助于dependsOn来实现有序执行。如下dependsOn的用法:
task action(dependsOn: ":producer:action") { doLast { println("Consuming message: ${rootProject.producerMessage}") } }
属性依赖:配置脚本中的属性同样可能存在读写的顺序依赖,可通过evaluationDependsOn解决这个问题,该evaluationDependsOn能保证依赖的项目会先执行,如下例子:
evaluationDependsOn(':producer') def message = rootProject.producerMessage task consume { doLast { println("Consuming message: " + message) } }
lib依赖:Gradle支持lib依赖,比如jar、aar等,需要借助于gradle的compile来完成依赖的构建,因此构建task的执行顺序也能得以保证。
局部编译:gradle -a build命令可加快编译速度,该命令不会编译依赖项目。
多项目构建的执行方式:
- 直接切换目录到对应的子项目下,使用gradle命令运行;
- 在根目录下执行指定子项目的的构建,如gradle :services:webservice:build,注意依赖也会参与执行。
小结:虽然Gradle的依赖管理非常灵活、功能强大,但如果在构建脚本当中滥用allprojects, subprojects、project、evaluationDependsOn等便利方法是会带来了构建项目之间的耦合问题,比较好的解耦方式是使用dependencies进行管理。
Composite builds
面对日益复杂的构建系统,为了解决多项目构建代码的限制,Gradle又提出了Composite builds,称为混合构建。其目的是为了拆分大型的多项目构建。
混合构建的项目结构通常如下,可以认为参与构建的项目都是独立的项目,拥有settings.gradle脚本:
composite ├── rootProject1 │ ├── build.gradle │ ├── settings.gradle │ └── src │ └── main │ └── java │ └── App.java └── rootProject2 ├── build.gradle ├── settings.gradle └── src └── main └── java └── Api.java
混合构建方式有两种使用方式:
- 命令行方式:gradle –include-build root_project task
- Setting.gradle脚本方式:使用includeBuild将外部工程引入。
混合构建的局限性:
- 不支持native项目
- 必须包含settings.gradle文件
- 混合构建不能嵌套
- 要保证所有参与构建的rootProject.name的唯一性,包括顶层项目名
依赖替换:通过dependencySubstitution完成includeBuild中相关模块的替换。
实例basic工程结构如下图