Gradle复合构建
Posted IT人.阿标
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gradle复合构建相关的知识,希望对你有一定的参考价值。
文章目录
什么是复合构建(Composing builds)?
复合构建是指包含其他构建的构建。在许多方面,复合构建类似于 Gradle 多项目构建,不同之处在于它不包含单个项目,而是包括完整的构建。
多项目构建和复合构建区分:
多项目构建:
多项目构建重点是如何组织多个项目,项目之间往往是存在业务逻辑联系的。也是我们平常用的比较多的场景:比如项目APP,依赖ModuleA和ModuleB,表现形式如下:
implementation project(":ModuleA")
implementation project(":ModuleA2")
复合构建:
复合构建重点是如何整合多个独立的项目,被引入的项目是可以独立运行的,但不一定有逻辑联系。比如一个项目,它依赖公司另一个独立的 SDK,这个 SDK 可以作为独立的项目运行。表现形式如下:
//步骤1:主项目的settings.gradle文件添加如下代码
includeBuild('../APPB')
dependencySubstitution
substitute module('com.beason.moduleb:ModuleB') using project(':ModuleB')
//步骤2:再在需要引入Moduel地方,引入即可
implementation "com.beason.moduleb:ModuleB"
包含在复合构建中的构建很自然地称为“包含构建”。包含的构建不与复合构建或其他包含的构建共享任何配置。每个包含的构建都是独立配置和执行的。
包含构建通过依赖替换与其他构建交互。如果组合中的任何构建具有可被包含构建满足的依赖项,则该依赖项将被包含构建的项目依赖项替换。 由于对依赖替换的依赖,复合构建可能会在组合任务执行图时强制更早地解析配置。这会对整体构建性能产生负面影响,因为这些配置不是并行解决的。
默认情况下,Gradle 将尝试确定可以被包含的构建替换的依赖项。但是,为了获得更大的灵活性,如果 Gradle 确定的默认替换对组合不正确,则可以显式声明这些替换。
除了通过项目依赖使用输出外,复合构建还可以直接在包含的构建上声明任务依赖。包含的构建是隔离的,并且无法声明对复合构建或其他包含构建的任务依赖关系。
定义复合构建
以下示例演示了将通常单独开发的 2 个 Gradle 构建组合成复合构建的各种方式。对于这些示例,my-utils
多项目构建会生成 2 个不同的 java 库(number-utils
和string-utils
),并且my-app
构建会使用这些库中的函数生成可执行文件。
my-app 构建对 my-utils 没有直接依赖关系。 相反,它声明了对 my-utils 生成的库的二进制依赖。
文件:my-app/app/build.gradle
plugins
id 'application'
application
mainClass = 'org.sample.myapp.Main'
dependencies
implementation 'org.sample:number-utils:1.0'
implementation 'org.sample:string-utils:1.0'
方式1:通过命令--include-build
定义复合构建:
命令行参数将--include-build
执行的构建转换为组合,将包含的构建中的依赖项替换为执行的构建。
命令:gradle --include-build ../my-utils run
> gradle --include-build ../my-utils run
> Task :app: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 :app:compileJava
> Task :app:classes
> Task :app:run
The answer is 42
BUILD SUCCESSFUL in 0s
6 actionable tasks: 6 executed
方式2:通过settings.gradle定义复合构建:
通过使用 Settings.includeBuild(java.lang.Object) 在 settings.gradle(或 Kotlin 中的 settings.gradle.kts)文件中声明包含的构建。 设置文件可用于同时添加子项目和包含的构建。 包含的构建按位置添加。
上述方法的一个缺点是它需要您修改现有构建,使其作为独立构建。避免这种情况的一种方法是定义一个单独的复合构建,其唯一目的是组合其他单独的构建。
settings.gradle
rootProject.name = 'my-composite'
includeBuild 'my-app'
includeBuild 'my-utils'
在这种情况下,执行的“主”构建是复合的,它没有定义任何有用的任务来执行自身。为了在“my-app”构建中执行“运行”任务,复合构建必须定义一个委派任务。
build.gradle
tasks.register('run')
dependsOn gradle.includedBuild('my-app').task(':app:run')
下面是有关依赖于包含的构建任务的任务的更多详细信息。
定义 Gradle 插件的构建
包含构建的一个特例是定义 Gradle 插件的构建。应该使用设置文件块includeBuild
内的语句包含这些构建。pluginManagement
使用这种机制,包含的构建还可以提供一个设置插件,该插件可以应用于设置文件本身。
settings.gradle
pluginManagement
includeBuild '../url-verifier-plugin'
通过插件管理块包含插件构建是一个孵化功能。您也可以使用
includeBuild
外部的稳定机制pluginManagement
来包含插件构建。但是,这并不支持所有用例,一旦新机制稳定,包括这样的插件构建将被弃用。
对包含的构建的限制
大多数构建都可以包含在组合中,包括其他组合构建。但是有一些限制。
- 不得
rootProject.name
与另一个包含的构建相同。 - 不得
rootProject.name
与复合构建的顶级项目相同。 - 不得
rootProject.name
与复合构建相同rootProject.name
。
与复合构建交互
通常,与复合构建的交互与常规的多项目构建非常相似。可以执行任务,可以运行测试,并且可以将构建导入 IDE。
执行任务
复合构建中的任务可以从命令行或您的 IDE 执行。执行任务将导致执行直接任务依赖项,以及从包含的构建构建依赖项工件所需的那些任务。
您使用完全限定的路径(通常是:included-build-name:subproject-name:taskName
. 子项目和任务名称可以缩写。包含的构建名称不支持此功能。
$ ./gradlew :included-build:subproject-a:compileJava
> Task :included-build:subproject-a:compileJava
$ ./gradlew :included-build:sA:cJ
> Task :included-build:subproject-a:compileJava
要从命令行中排除任务,您还需要提供该任务的完全限定路径。
包含的构建任务会自动执行以生成所需的依赖项工件,或者包含构建可以声明对包含构建的任务的依赖。
导入 IDE
组合构建最有用的特性之一是 IDE 集成。通过将idea或eclipse插件应用到您的构建中,可以生成单个 IDEA 或 Eclipse 项目,该项目允许组合中的所有构建一起开发。
除了这些 Gradle 插件之外,最新版本的IntelliJ IDEA和Eclipse Buildship 还支持直接导入复合构建。
导入复合构建允许将来自不同 Gradle 构建的源轻松地一起开发。对于每个包含的构建,每个子项目都包含为一个 IDEA 模块或 Eclipse 项目。配置源依赖项,提供跨构建导航和重构。
声明由包含的构建替换的依赖项
默认情况下,Gradle 将配置每个包含的构建,以确定它可以提供的依赖项。执行此操作的算法非常简单:Gradle 将检查包含构建中项目的组和名称,并用项目依赖项替换任何外部依赖项匹配$project.group:$project.name
。
默认情况下,不会为主构建注册替换。 要使主构建的(子)项目可通过 p r o j e c t . g r o u p : project.group: project.group:project.name 寻址,您可以告诉 Gradle 通过自包含它来将主构建视为包含构建:includeBuild(". ”)。
在某些情况下,由 Gradle 确定的默认替换是不够的,或者它们对于特定的组合不正确。对于这些情况,可以显式声明包含构建的替换。以单项目构建“anonymous-library”为例,它生成一个 java 实用程序库,但不声明 group 属性的值:
示例 . 不声明组属性的构建
build.gradle
plugins
id 'java'
当此构建包含在组合中时,它将尝试替换依赖模块“undefined:anonymous-library”(“undefined”是 的默认值project.group
,“anonymous-library”是根项目名称)。显然,这在复合构建中不会很有用。要在组合构建中使用未修改的未发布库,组合构建可以显式声明它提供的替换:
包含的构建声明替换
settings.gradle
rootProject.name = 'declared-substitution'
include 'app'
// tag::composite_substitution[]
includeBuild('anonymous-library')
dependencySubstitution
substitute module('org.sample:number-utils') using project(':')
// end::composite_substitution[]
使用此配置,“my-app”复合构建将用org.sample:number-utils
对“anonymous-library”根项目的依赖替换任何依赖。
复合构建替换不起作用的情况
某些构建包含在组合中时将无法正常运行,即使显式声明了依赖项替换也是如此。此限制是由于被替换的项目依赖项将始终指向default
目标项目的配置。每当为项目的默认配置指定的工件和依赖项与实际发布到存储库的内容不匹配时,复合构建可能会表现出不同的行为。
以下是发布模块元数据可能与项目默认配置不同的一些情况:
- 发布以外的配置时
default
。 - 使用
maven-publish
或ivy-publish
插件时。 - 当
POM
orivy.xml
文件作为发布的一部分进行调整时。
当包含在复合构建中时,使用这些功能的构建会无法正常运行。我们计划在未来对此进行改进。
取决于包含的构建中的任务
虽然包含的构建彼此隔离并且不能声明直接依赖关系,但复合构建能够在其包含的构建上声明任务依赖关系。使用Gradle.getIncludedBuilds()或Gradle.includedBuild(java.lang.String)访问包含的构建,并通过IncludedBuild.task(java.lang.String)方法获得任务引用。
使用这些 API,可以在特定包含的构建中声明对任务的依赖关系,或者在所有或部分包含构建中声明具有特定路径的任务。
复合构建的当前限制和未来计划
当前实施的限制包括:
- 不支持包含不反映项目默认配置的发布的构建。请参阅复合构建不起作用的情况。
- 不支持基于软件模型的本机构建。(本机构建尚不支持二进制依赖项)。
- 如果多个复合构建包含相同的构建,则并行运行时可能会发生冲突。Gradle 不会在 Gradle 调用之间共享共享复合构建的项目锁,以防止并发执行。
我们为即将发布的版本计划的改进包括:
- 使隐式
buildSrc
项目成为包含的构建。
以上是关于Gradle复合构建的主要内容,如果未能解决你的问题,请参考以下文章