【翻译】gradle的组合构建

组合构建(Composite builds)

译者注:因为技术文档中有些名词并无通俗的译法,针对这一情况,将文中的部分名词解释如下:
组合构建:composite build(s),即本文主要介绍的内容,其包含一个或者多个内嵌构建。
内嵌构建:included build(s),即组合构建的子项。
构建过程:既可是过程,也可指构建脚本,需结合上下语境

注意:组合构建是一个正在开发中的功能,适用于许多场景,但是同时也存在一些bug,还需要继续打磨,增强。欢迎各位尝试!

什么是组合构建

组合构建是一个包含其他构建过程的构建过程。在许多方面,与gradle的多项目构建很类似,除非是只有一个项目,并包含完整的构建过程的那种。 组合构建允许你做到:

  • 聚合那些独立开发的项目构建过程,比如尝试修复应用中引入的library中的bug
  • 将一个多项目的构建过程分割为一些更小、更独立的分支,以便于按需单独或者一起使用

一个被组合构建所包含的构建过程称之为内嵌构建(原文为“included build”)。内嵌构建不会和组合构建或者其他构建共享配置。每一个内嵌构建都是独立被配置和被运行。 内嵌构建与其他构建的交互是通过依赖替换来完成的。在组合构建中的任何构建的依赖如果能够被内嵌构建满足,那么这个依赖,在内嵌构建中会被替换为一个项目依赖。 在默认情况下,gradle可以尝试决定哪些依赖可以被内嵌构建替换。然而,出于灵活性的考虑,当gradle的默认决策出现错误时,我们也可以在组合构建中明确声明那些替代关系。详情请看通过内嵌构建声明依赖替换。 和凭借项目依赖一样,组合构建也能直接在内嵌构建中声明task依赖。但内嵌构建是独立的,不能在组合构建或者其他内嵌构建中声明task依赖。详情请看依赖内嵌构建中的task。 。

定义一个组合构建

下面的例子展示了不同的方式来实现两个正常独立开发的gradle构建过程被组合成为一个组合构建。在这些例子中,mu-utils是一个多项目工程,构建后会产生两个java-lib(number-utils和string-utils),my-app构建后会产生一个使用上述两个lib的可执行文件。

  • 例子: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"
    compile "org.sample:string-utils:1.0"
}

repositories {
    jcenter()
}

注意:这个例子的代码可以在gradle的带有“-all”的分发包中的samples/compositeBuilds/basic目录下找到。

通过--include-build 命令行定义组合构建

--include-build命令的参数,即所指定的可执行构建,会被转化成为一个组合的,从内嵌构建中替换依赖项到可执行构建中。

  • 例子:声明一个命令行的组合构建
    gradle --include-build ../my-utils run 命令输出如下:
> gradle --include-build ../my-utils run
> Task :processResources NO-SOURCE
> Task :my-utils:string-utils:compileJava
> Task :my-utils:string-utils:processResources NO-SOURCE
> Task :my-utils:string-utils:classes
> Task :my-utils:string-utils:jar
> Task :my-utils:number-utils:compileJava
> Task :my-utils:number-utils:processResources NO-SOURCE
> Task :my-utils:number-utils:classes
> Task :my-utils:number-utils:jar
> Task :compileJava
> Task :classes

> Task :run
The answer is 42


BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed

通过settings.gradle定义组合构建

如果需要将上述的改动持久化,我们需要在settings.gradle文件中用Settings.includeBuild(java.lang.Object)来声明一个内嵌构建。这个settings.gradle文件可以被同时用于添加子项目(subproject)和内嵌构建。内嵌构建通过文件路径来添加。如下例子所示。

定义一个额外的组合构建

上面提到的方式的不足之处就是你需要修改一个已经存在构建过程,并作为一个没什么用处的标准构建过程来执行它。为了避免这点,我们可以定义一个额外的组合构建,只用于组合其他独立的构建过程。

  • 例子:声明一个独立的组合构建
    settings.gradle
rootProject.name='adhoc'

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

在这个方案中,被执行的主构建过程即使我们的组合构建,但是目前我们还没有定义一个task来让他执行。为了在my-app的构建任务中执行组合构建的run方法,我们需要定义一个委托task。

  • 例子:依赖内嵌构建中的task

build.gradle

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

更多细节请继续看下面内容。

  • 对内嵌构建项的约束 大多数构建过程都可以做为组合构建的内嵌构建而被包含进来,但是仍然有一些限制条件: 每一个内嵌构建都要满足以下条件:
    • 必须包含一个settings.gradle文件
    • 必须是非组合构建
    • rootProject.name必须与其他内嵌构建的rootProject.name不同
    • rootProject.name必须与组合构建顶层项目的rootProject.name不同
    • rootProject.name必须与组合构建的rootProject.name不同

与组合构建的交互

一般来说,与组合构建交互和与一个多项目的构建过程交互没有什么不同。他们都能够执行task,执行测试,并且能够被导入到IDE中。

执行tasks

组合构建中的tasks可以在命令行中执行,也可以在你的IDE中执行。执行一个task,将会导致直接依赖的task,以及内嵌构建中的被依赖的task也被执行。
注意:这里并不是说用命令行直接执行一个内嵌构建里的task。内嵌构建里的task是在生成依赖关系的时候自动执行,或者一个内嵌构建声明了依赖另一个内嵌构建的task时,被依赖的task也会自动执行。

导入到IDE中

组合构建最有用的特性之一就是可以与IDE集成。通过在build脚本中使用idea或者eclipse插件,可以生成一个单独的IDEA或者eclipse工程,从而可以对组合构建中的所有项目进行开发工作。 除了gradle插件以外,新版本的intellij IDEA和Eclipse Buildship都支持直接导入一个组合构建项目。 导入一个组合构建项目,使得那些原本分别开发的gradle项目可以被放在一起进行开发。对每一个内嵌构建项目来说,每一个子项目都被当做是一个idea的module或者Eclipse的project。源码依赖会被配置,并提供跨项目的导航和反射。

通过内嵌构建声明依赖替换

默认情况下,gradle为了识别内嵌构建的能够提供的依赖项目,会对每一个内嵌构建做一次配置。这个逻辑很简单:gradle会监控内嵌构建项目中的group和name,并将所有匹配*${project.group}:${project.name}*的外部依赖替换为项目依赖。 当然,gradle的默认提供的替换可能不能满足项目需要,甚至一部分是错误的。在这种情况下,我们可以为内嵌构建显示的声明一个替换关系。如下面的“unpublished”的例子所示,这个例子会编译成一个java的工具library,但是没有在group属性中明确声明任何值。

  • 例子:构建不带group值的项目
    build.gradle
apply plugin: 'java'

当我们在组合构建中编译它的时候,gradle将会尝试替换模块依赖“undefined:unpublished”(“undefined”是project.group的默认值,“unpubllished”是root project的名字)。很明显在组合构建中,这是无效的。为了能在组合构建中不做改动的使用这个unpublish-library,我们可以在内嵌构建项目中明确声明它能够提供的替换关系:

  • 例子:声明内嵌构建的替换关系

    settings.gradle

  rootProject.name = 'app'

    includeBuild('../anonymous-library') {
        dependencySubstitution {
            substitute module('org.sample:number-utils') with project(':')
        }
    }

通过这个配置,“my-app”项目将会通过org.sample:number-utils的依

内嵌构建项目替换关系必须被明确声明的场景

那些使用uploadArchives task 来发布构建成果的构建过程,在作为一个内嵌构建时会自动生效,而无需声明替换关系。但是仍然有些场景,是需要明确声明的:

  • archivesBaseName属性被用来设置构建成果的名字
  • 非默认配置被发布:通常意味着uploadArchives被另一个task所替代
  • 使用MavenPom.addFilter()来发布项目构建成果,而且与项目名称不一致
  • 使用maven-publish 或者ivy-publish插件来发布项目构建成果,并且发布的配置与${project.group}:${project .name}不匹配。

组合构建替换关系失败的场景

有些构建过程,甚至是明确声明了依赖替换关系也不能在组合构建中正确执行。这个限制是因为项目依赖替换是按照被目标项目的默认配置来实现的。当一个项目默认配置下的依赖和构建成果与实际发布到中央仓库中的不匹配时,组合构建就可能表现出不一致的行为。 以下是发布项目的配置和项目默认配置可能会出现不一致的情况:

  • 当使用不同于默认的配置的发布时
  • 当使用maven-plugin或者ivy-plugin插件时
  • 当POM或者ivy.xml作为最终发布成果被改动过的时候 使用这些特性的构建过程,被加入到组合构建的时候就不能正确执行。我们计划在将来改善这点。

依赖内嵌构建中的task

由于每个内嵌构建都是相互独立的,并不能直接声明依赖关系,但是组合构建是可以声明对其所包含的内嵌构建task依赖。内嵌构建过程是可以使用 Gradle.getIncludedBuilds()Gradle.includedBuild(java.lang.String),而且可以通过调用方法IncludedBuild.task(java.lang.String)获取对一个task的引用。 使用这些API,是我们能够声明一个task,让他依赖于某个内嵌构建过程的task,或者是某一路径下的全部或部分内嵌构建过程的多个task。

  • 例子:依赖一个内嵌构建的单独task build.gradle
  task run {
    dependsOn gradle.includedBuild('my-app').task(':run')
}
  • 例子:依赖某一路径下的全部内嵌构建过程的task build.gradle
task publishDeps {
    dependsOn gradle.includedBuilds*.task(':uploadArchives')
}

组合构建的限制和未来计划

我们认为组合构建已经是很有用的了。然而还有一些情形不能正常使用,和一些需要提高的地方。 以下是当前的限制:

  • 不支持内嵌构建使用不同于默认配置发布。详情请看组合构建替换关系失败的场景
  • 不支持Native构建(字节码依赖还不能支持Native构建)
  • 替换插件 只在buildscript中发挥作用,而不能写在plugins中

我们计划在未来要改善的地方:

  • 为那些自定义了发布配置的构建过程,以及会输出多个组件的构建等,提供更好的依赖替换检测机制,从而减少需要明确声明依赖替换关系的场景。
  • 从命令行直接运行内嵌构建中的一个或者多个task的能力。我们目前正在研究这个功能的语法,这个功能将会大幅减少在组合构建中写委托task的场景。
  • 使内含的buildSrc项目成为一个内嵌构建过程
  • 支持组合多个组合构建的能力

转载于:https://my.oschina.net/tnjin/blog/1839647

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值