114 篇文章 340 订阅

https://coder.blog.csdn.net/article/details/116955301

# 第五十九章. 组织构建逻辑

### Chapter 59. Organizing Build Logic

Gradle offers a variety of ways to organize your build logic. First of all you can put your build logic directly in the action closure of a task. If a couple of tasks share the same logic you can extract this logic into a method. If multiple projects of a multi-project build share some logic you can define this method in the parent project. If the build logic gets too complex for being properly modeled by methods you want have an OO Model. [25] Gradle makes this very easy. Just drop your classes in a certain directory and Gradle automatically compiles them and puts them in the classpath of your build script.

Here is a summary of the ways you can organise your build logic:

• POGO。你可以直接在构建脚本中声明和使用普通的 Groovy 对象（POGO）。别忘了构建脚本是使用 Groovy 来写的，Groovy 会向你提供许多优秀的方式来组织代码。

POGOs. You can declare and use plain old Groovy objects (POGOs) directly in your build script. The build script is written in Groovy, after all, and Groovy provides you with lots of excellent ways to organize code.

• 继承的属性和方法。在多项目构建中，子项目将继承父项目的属性和方法。

Inherited properties and methods. In a multi-project build, sub-projects inherit the properties and methods of their parent project.

• 配置注入。在多项目构建中，一个项目（通常是根项目）可以将属性和方法注入到另一个项目中。

Configuration injection. In a multi-project build, a project (usually the root project) can inject properties and methods into another project.

• buildSrc 项目。把构建类的源码放到特定目录中， Gradle 会自动编译它们，并将它们包含在构建脚本的类路径中。

buildSrc project. Drop the source for your build classes into a certain directory and Gradle automatically compiles them and includes them in the classpath of your build script.

• 共享脚本。在外部构建中定义公共配置，然后把这个脚本应用于多个项目，这些项目可能在不同的构建中。

Shared scripts. Define common configuration in an external build, and apply the script to multiple projects, possibly across different builds.

• 自定义任务。把构建逻辑放到一个自定义任务中，并在多个地方复用该任务。

• 自定义插件。把构建逻辑放到自定义插件中，并将该插件应用于多个项目。插件必须在构建脚本的类路径中，你可以通过使用 build sources 或通过添加包含插件的 外部库 来实现。

Custom plugins. Put your build logic into a custom plugin, and apply that plugin to multiple projects. The plugin must be in the classpath of your build script. You can achieve this either by using build sources or by adding an external library that contains the plugin.

Execute an external build. Execute another Gradle build from the current build.

• 外部库。直接在构建文件中使用外部库。

External libraries. Use external libraries directly in your build file.

## 59.1. 继承的属性和方法

59.1. Inherited properties and methods

Any method or property defined in a project build script is also visible to all the sub-projects. You can use this to define common configurations, and to extract build logic into methods which can be reused by the sub-projects.

build.gradle

// Define an extra property
ext.srcDirName = 'src/java'

// Define a method
def getSrcDir(project) {
return project.file(srcDirName)
}

child/build.gradle

task show << {
// Use inherited property
println 'srcDirName: ' + srcDirName

// Use inherited method
File srcDir = getSrcDir(project)
println 'srcDir: ' + rootProject.relativePath(srcDir)
}

gradle -q show 的输出结果
Output of gradle -q show

> gradle -q show
srcDirName: src/java
srcDir: child/src/java

## 59.2. 注入配置

59.2. Injected configuration

You can use the configuration injection technique discussed in Section 56.1, “Cross project configuration” and Section 56.2, “Subproject configuration” to inject properties and methods into various projects. This is generally a better option than inheritance, for a number of reasons: The injection is explicit in the build script, You can inject different logic into different projects, And you can inject any kind of configuration such as repositories, plug-ins, tasks, and so on. The following sample shows how this works.

build.gradle

subprojects {
// Define a new property
ext.srcDirName = 'src/java'

// Define a method using a closure as the method body
ext.srcDir = { file(srcDirName) }

println 'project: ' + project.path
println 'srcDirName: ' + srcDirName
File srcDir = srcDir()
println 'srcDir: ' + rootProject.relativePath(srcDir)
}
}

// Inject special case configuration into a particular project
project(':child2') {
ext.srcDirName = "\$srcDirName/legacy"
}

child1/build.gradle

// Use injected property and method. Here, we override the injected value
srcDirName = 'java'
def dir = srcDir()

gradle -q show 的输出结果
Output of gradle -q show

> gradle -q show
project: :child1
srcDirName: java
srcDir: child1/java
project: :child2
srcDirName: src/java/legacy
srcDir: child2/src/java/legacy

## 59.3. 在 buildSrc 项目中的构建源码

59.3. Build sources in the buildSrc project

When you run Gradle, it checks for the existence of a directory called buildSrc. Gradle then automatically compiles and tests this code and puts it in the classpath of your build script. You don't need to provide any further instruction. This can be a good place to add your custom tasks and plugins.

For multi-project builds there can be only one buildSrc directory, which has to be in the root project directory.

Listed below is the default build script that Gradle applies to the buildSrc project:

apply plugin: 'groovy'
dependencies {
compile localGroovy()
}

This means that you can just put you build source code in this directory and stick to the layout convention for a Java/Groovy project (see Table 23.4, “Java plugin - default project layout”).

If you need more flexibility, you can provide your own build.gradle. Gradle applies the default build script regardless of whether there is one specified. This means you only need to declare the extra things you need. Below is an example. Notice that this example does not need to declare a dependency on the Gradle API, as this is done by the default build script:

buildSrc/build.gradle

repositories {
mavenCentral()
}

dependencies {
testCompile 'junit:junit:4.11'
}

buildSrc 项目可以是一个多项目构建，就像其他常规 Gradle 多项目构建。但是，你需要使你想要的所有项目，在 buildSrc 根项目的实际构建 runtime 依赖的类路径上。你可以通过把它添加到每一个想要导出的项目的配置上来完成：
The buildSrc project can be a multi-project build. This works like any other regular Gradle multi-project build. However, you need to make all of the projects that you wish be on the classpath of the actual build runtime dependencies of the root project in buildSrc. You can do this by adding this to the configuration of each project you wish to export:

buildSrc/build.gradle

rootProject.dependencies {
runtime project(path)
}

Note: The code for this example can be found at samples/multiProjectBuildSrc which is in both the binary and source distributions of Gradle.

59.4. Running another Gradle build from a build

You can use the GradleBuild task. You can use either of the dir or buildFile properties to specify which build to execute, and the tasks property to specify which tasks to execute.

build.gradle

task build(type: GradleBuild) {
}

other.gradle

task hello << {
println "hello from the other build."
}

gradle -q build 的输出结果
Output of gradle -q build

> gradle -q build
hello from the other build.

## 59.5. 构建脚本的外部依赖

59.5. External dependencies for the build script

If your build script needs to use external libraries, you can add them to the script's classpath in the build script itself. You do this using the buildscript() method, passing in a closure which declares the build script classpath.

build.gradle

buildscript {
repositories {
mavenCentral()
}
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}

The closure passed to the buildscript() method configures a ScriptHandler instance. You declare the build script classpath by adding dependencies to the classpath configuration. This is the same way you declare, for example, the Java compilation classpath. You can use any of the dependency types described in Section 50.4, “How to declare your dependencies”, except project dependencies.

Having declared the build script classpath, you can use the classes in your build script as you would any other classes on the classpath. The following example adds to the previous example, and uses classes from the build script classpath.

build.gradle

import org.apache.commons.codec.binary.Base64

buildscript {
repositories {
mavenCentral()
}
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}

def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
println new String(encodedString)
}

gradle -q encode 的输出结果
Output of gradle -q encode

> gradle -q encode
aGVsbG8gd29ybGQK

For multi-project builds, the dependencies declared in the a project's build script, are available to the build scripts of all sub-projects.

## 59.6. Ant 可选依赖

59.6. Ant optional dependencies

For reasons we don't fully understand yet, external dependencies are not picked up by Ant's optional tasks. But you can easily do it in another way. [26]

build.gradle

configurations {
}

dependencies {
module("commons-net:commons-net:1.4.1") {
dependencies "oro:oro:2.0.8:jar"
}
}
}

ant {
ftp(server: "ftp.apache.org", userid: "anonymous", password: "me@myorg.com") {
fileset(dir: "htdocs/manual")
}
}
}

This is also nice example for the usage of client modules. The POM file in Maven Central for the ant-commons-net task does not provide the right information for this use case.

## 59.7. 总结

59.7. Summary

Gradle offers you a variety of ways of organizing your build logic. You can choose what is right for your domain and find the right balance between unnecessary indirections, and avoiding redundancy and a hard to maintain code base. It is our experience that even very complex custom build logic is rarely shared between different builds. Other build tools enforce a separation of this build logic into a separate project. Gradle spares you this unnecessary overhead and indirection.

[25] 可能是从一个类到一些非常复杂的东西的范围。
[25] Which might range from a single class to something very complex.

[26] 实际上，我们认为这是更好的解决方案。仅当你的构建脚本和 Ant 的可选任务需要 相同的 库时，你才需要定义它两次。在这种情况下，如果 Ant 的可选任务能自动获取 gradesettings 中定义的类路径，会相当不错。
[26] In fact, we think this is anyway the nicer solution. Only if your buildscript and Ant's optional task need the same library you would have to define it two times. In such a case it would be nice, if Ant's optional task would automatically pickup the classpath defined in the gradesettings.

• 0
点赞
• 0
评论
• 0
收藏
• 一键三连
• 扫一扫，分享海报

04-27 28
07-15 1656

04-21 42
12-01 301
09-27 41
04-23 28
12-02 296
07-09 183
03-04 34
06-11 4万+
12-15 1万+
07-31 5万+
05-06 5775