独自登高楼 望断天涯路

学lucene 学hadoop,Google/baidu搜:“独自登高楼”,进入本博客

Gradle学习(八)——复合构建

转载请注明出处:http://blog.csdn.net/lastsweetop/article/details/78905882

简介

复合构建是一个包含了其他构建的构建,很多时候和多项目构建非常相似,但是它包含的不是完整的项目,而是完整的构建。组合构建可以做到以下两点:

  • 和比较独立的开发进行组合构建,比如修复一个项目中引用的lib库
  • 把非常大的工程切成几个小块,各个小块直接是可以独立工作的

复合构建比多项目构建中的小项目更具备独立性,复合构建中的构建可以被称为“被包含的构建”,被包含的构建和复合构建或者其他被包含的构建之间不会共享任何配置信息,每个被包含的构建都会独立的配置和执行。

被包含构建和其他构建之间通过dependency substitution来关联,如果被包含的构建和其他构建之间满足依赖关系,那么他们之间的依赖将被替换为工程依赖。默认情况下,Gradle会去替换被包含构建需要满足的依赖关系,但是有时候默认值不一定正确,那就需要显示的指明替换。除了通过替换为项目依赖来解决这种情况外,复合构建还可以直接定义依赖于被包含构建的任务,但是被包含构建是无法这样做的。

定义

下面这个例子中两个gradle构建是分开独立开发,我们会展示如何用各种不同的方式把它们做成复合构建。其中my-utils包含两个子项目string-utils和number-utils,分别会产生两个jar包,供my-app调用,但是my-app并不会直接声明是依赖于my-utils项目,而仅仅声明了依赖于它产出的两个jar包。

目录树如下:

.
├── my-app
│   ├── build.gradle
│   ├── my-app.iml
│   ├── settings.gradle
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── lastsweetop
│                       └── myapp
│                           └── Main.java
└── my-utils
    ├── my-utils.iml
    ├── number-utils
    │   ├── build.gradle
    │   └── src
    │       └── main
    │           └── java
    │               └── com
    │                   └── lastsweetop
    │                       └── number
    │                           └── NumberUtils.java
    ├── settings.gradle
    └── string-utils
        ├── build.gradle
        └── src
            └── main
                └── java
                    └── com
                        └── lastsweetop
                            └── string
                                └── StringUtils.java

my-app里的build.gradle配置如下:

apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'idea'


group 'com.lastsweetop'
version '1.0'
mainClassName='com.lastsweetop.myapp.Main'

dependencies {
    compile 'com.lastsweetop:string-utils:1.0'
    compile 'com.lastsweetop:number-utils:1.0'
}

repositories {
    jcenter()
}

通过--include-build定义复合构建

在我们第一种方法中,IDE都会提示错误的,因为它不知道去哪找这两个包,代码中也会提示找不到XX类,如图所示:
compositeBuild
但是通过--include-build选项把my-utils引入进来作为被包含的构建确实可以复合构建,gradle --include-build '../my-utils'的输出如下:

> Task :run
composite build


BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

通过settings.gradle定义复合构建

我们的第二种方法就会清爽好多,通过在settings.gradle文件中,用Settings.includeBuild(java.lang.Object)方法声明需要被包含的构建,被包含的构建是本地的项目目录,这是IDE就不会提示错误了,因为IDE会把被包含的包同时导入到工程中,当然所需的依赖和类也都会找到了。效果如下图:
compositeBuild2

单独定义复合构建

我们一开始的初衷是在独立的构建上做组合构建,但是第二个方法需要修改原来的构建,这使得被修改的构建不是一个标准的构建。第三个方法就是单独定义一个复合构建的项目,这我们的例子中是my-cp项目,整体的目录结构图如下:

├── my-app
│   ├── build.gradle
│   ├── my-app.iml
│   ├── settings.gradle
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── lastsweetop
│                       └── myapp
│                           └── Main.java
├── my-cp
│   ├── build.gradle
│   ├── my-cp.iml
│   └── settings.gradle
└── my-utils
    ├── my-utils.iml
    ├── number-utils
    │   ├── build.gradle
    │   └── src
    │       └── main
    │           └── java
    │               └── com
    │                   └── lastsweetop
    │                       └── number
    │                           └── NumberUtils.java
    ├── settings.gradle
    └── string-utils
        ├── build.gradle
        └── src
            └── main
                └── java
                    └── com
                        └── lastsweetop
                            └── string
                                └── StringUtils.java

在my-cp的settings.gradle中包含两个构建,如下:

rootProject.name='my-cp'

includeBuild '../my-app'
includeBuild '../my-utils'

my-cp作为复合构建项目来执行,但是它自己并没有定义可执行的任务,如果想执行my-app里的run任务就需要在复合构建项目中的build.gradle定义,如下:

task run {
    dependsOn gradle.includedBuild('my-app').task(':run')
    group 'application'
}

我在定义clean任务时遇到一个问题,应该算是bug,及复合构建项目无法识别被包含项目的任务的父子项目归属关系,只能指定子项目的具体任务,如下:

task clean {
    dependsOn gradle.includedBuild('my-app').task(':clean'),
            gradle.includedBuild('my-utils').task(':string-utils:clean'),
            gradle.includedBuild('my-utils').task(':number-utils:clean')

}

被包含构建的限制条件

被复合构建包含的构建有一些限制条件:

  • 必须有settings.gradle文件,及时文件内容是空的
  • 本身不可以是复合构建
  • rootProject.name不能与其他被包含的构建相同
  • rootProject.name不能与复合构建的root工程相同
  • rootProject.name不能与复合构建的rootProject.name相同

组合构建的交互

一般来说,组合构建的交互和多项目构建一样,一样可以运行任务,执行测试,导入到IDE等

任务执行

不管是命令行还是IDE都可以执行复合构建中的任务,gradle会去判断那些依赖的任务需要执行,要注意的是被包含构建的任务的执行目前只能在IDE中执行,命令行中还不支持。

IDE导入

IDE的集成是组合构建最重要的一个特性,只需导入组合构建任务的root项目,其他被包含的构建项目就会自动导入,有利于同时开发多个项目。

定义由被包含构建替换的依赖

默认情况下,Gradle会设置每个被包含的构建来确定他们可以提供的依赖关系。其中的算法也非常简单:Gradle会检查被包含构建项目的group和name,当工程的依赖与${project.group}:${project.name}.匹配时,就会替换成被包含的工程。

这个过程大多数情况下交给Gradle去搞定就OK了,但是一些特别的项目还是会出错的,这种情况下就需要明确的去定义被包含构建的替换关系。比如一个单独的构建unpublished,它没有指定group,build.gradle里就一句

apply plugin: 'java'

gradle这里检查到这个被包含的构建可以替换的依赖模块为undefined:unpublished,undefined是因为没有group名,unpublished是项目的目录名称,非常明显这时无法匹配到实际的依赖的。
这时,my-app的依赖增加了一个date-utils

dependencies {
    compile 'com.lastsweetop:string-utils:1.0'
    compile 'com.lastsweetop:number-utils:1.0'
    compile 'com.lastsweetop:date-utils:1.0'
}

如何才能将date-utils和我们刚刚包含到的unpublished关联上呢?其实也很容易,只需要在复合构建导入时增加如下配置:

includeBuild('../unpublished') {
    dependencySubstitution {
        substitute module('com.lastsweetop:date-utils') with project(':')
    }
}

显示的指定了这个被包含的构建应该对应到的依赖模块的名字,至此他们的替换关系就确认了。注意project(‘:’)表示当前项目,意思就是还可以指定子项目,具体操作看你的灵活应用吧

被包含的构建必须声明替换关系的情况

很多构建通过uploadArchives任务来发布它们的产出,它们的替换关系可以使用默认的就OK了。但是有一些情况下,必须声明替换关系:

  • 发布产出时使用了archivesBaseName熟悉
  • 发布时除了默认设置外,还有其他的一些操作,同城不仅仅执行了uploadArchives任务
  • 发布时使用了MavenPom.addFilter(),有时候和项目名无法对应
  • 发布时使用了maven-publish或者ivy-publish插件,最终的产出可能与${project.group}:${project.name}不匹配
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lastsweetop/article/details/78905882
文章标签: gradle
个人分类: gradle
所属专栏: Gradle学习
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

Gradle学习(八)——复合构建

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭