Gradle复合构建

本文是边看Gradle英文文档边作的笔记:Chapter 11. Composite builds
建议看的时候动手实验一下,帮助理解java

什么是复合构建

简单地说,Gradle的复合构建就是一个构建包含了其它构建。
Gradle的复合构建跟多项目构建很类似,
惟一的区别是多项目构建引入的是单个project(引入的java依赖可使用),
而复合构建引入的是一个完整的构建(除了引入的java依赖,引入的task也可使用)。web

能够先看如下代码示例,有个直观的区分:微信

多项目构建:app

文件:my-app/build.gradle:

dependencies {
    compile project(':util')
    compile project(':web')    
}
//这里假设有一个my-app的构建,my-app构建依赖了util工程和web工程。

复合构建:ide

文件: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),只会向复合构建提供构建的结果,而配置信息是不会被导入到复合构建中的。gradle

若是被复合构建(my-app)包含的构建(util、web)中有依赖能够知足复合构建的依赖,会优先采用(util、web)该依赖。ui

Gradle默认会自行决定是否采用引入构建的依赖,可是官方建议最好显式声明(11.4, “Declaring the dependencies substituted by an included build”)idea

复合构建(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属性不能与复合构建的该属性相同