Gradle 提供了多种方式来组织你的构建逻辑。首先,你可以把你的构建逻辑直接放到一个任务的action闭包里。如果两个任务有相同的逻辑,你可以把这段逻辑抽取为一个方法。如果一个多项目构建中的多个项目有相同的逻辑,你可以把这个方法定义在父项目里。如果构建逻辑通过方法来正确建模比较复杂的话,你就要使用面向对象的模型了。[25] 这对Gradle来说很简单。只需要把你的类放在一个确定的目录,Gradle会自动地对它们编译,并且把它们加入到你的构建脚本的classpath中。
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:
POGOs。你可以直接在你的构建脚本中定义和使用普通的旧Groovy对象(POGOs)。别忘了构建脚本是用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会自动地编译它们,并把它们包含到你的构建脚本的classpath里。
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.
共享脚本。Define common configuration in an external build, and apply the script to multiple projects, possibly across different builds.
Shared scripts. Define common configuration in an external build, and apply the script to multiple projects, possibly across different builds.
自定义任务。把你的构建逻辑放到一个自定义任务中,然后在多个地方复用这个任务。
Custom tasks. Put your build logic into a custom task, and reuse that task in multiple places.
自定义插件。把你的构建逻辑放到一个自定义插件中,然后在多个项目应用这个插件 。这个插件必须在你的构建脚本的classpath中。你可以通过使用build sources
,或者是添加一个包含了该插件的外部 library,来获得这个插件。
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.
执行一个外部构建。从当前的构建中执行另一个Gradle构建。
Execute an external build. Execute another Gradle build from the current build.
外部 libraries。直接在你的构架文件中使用外部libraries。
External libraries. Use external libraries directly in your build file.
任何定义在一个项目的构建脚本中的属性和方法,对于它的所有子项目都是可见的。你可以通过这个方法去定义共同的配置,然后把构建逻辑抽取到可以被子项目重用的方法里。
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.
示例59.1. 使用继承的属性和方法 - Example 59.1. Using inherited properties and methods
build.gradle
srcDirName = 'src/java' 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
你可以使用在 第56.1节,“跨项目配置”和第56.2节,“子项目配置” 所论述的配置注入技术,来向不同的项目注入属性和方法。通常来说,它是比继承更好的选择,有好几个原因:注入是显示地存在于构建脚本中的,你可以向不同的项目注入不同的逻辑,并且你可以注入任意一种配置,例如仓库,插件,任务等等。下面是展示注入的例子。
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.
示例59.2. 使用注入的属性和方法 - Example 59.2. Using injected properties and methods
build.gradle
subprojects { // Inject a property and method srcDirName = 'src/java' srcDir = { file(srcDirName) } // Inject a task task show << { 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') { 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
当你运行Gradle时,它会检查buildSrc
目录是否存在。然后Gradle会自动的编译和测试它的代码,并且把它们加入到你的构建脚本的classpath中。你不需要再提供任何进一步的指示。这可以是用于添加你的自定义任务和插件的好地方。
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.
对于多项目构建,它们可能只有一个buildSrc
目录,在根项目的目录中。
For multi-project builds there can be only one buildSrc
directory, which has to be in the root project directory.
下面列出的是Gradle应用到 buildSrc
项目的默认构建脚本。
Listed below is the default build script that Gradle applies to the buildSrc
project:
图 59.1. 默认的buildSrc构建脚本 - Figure 59.1. Default buildSrc build script
apply plugin: 'groovy' dependencies { compile gradleApi() compile localGroovy() }
这意味着你可以只把你的构建源代码放到这个目录当中,并且保留一个 Java/Groovy 项目的约定布局(见表 23.4,“Java 插件 - 默认项目布局”)。
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”).
如果你需要更多的灵活性,你可以提供你自己的build.gradle
。Gradle配置了一个默认的构建脚本,无论是否有脚本被指定。这意味着,你只需声明你所需要的额外的东西。下面是一个例子。请注意,此示例不需要声明对 Gradle API的依赖,因为这由默认的构建脚本完成:
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:
示例 59.3. 自定义buildSrc构建脚本 - Example 59.3. Custom buildSrc build script
buildSrc/build.gradle
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:4.11'
}
The buildSrc
project can be a multi-project build. 这就像其他常规的Gradle多项目构建。但是,你需要使你想要的所有项目在buildSrc
的根项目的实际构建runtime
依赖的classpath上。 你可以通过把它添加到每一个你想导出的项目的配置上来完成:
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:
示例 59.4. 将子项目添加到根 buildSrc 项目 - Example 59.4. Adding subprojects to the root buildSrc project
buildSrc/build.gradle
rootProject.dependencies { runtime project(path) }
注意: 此例子的代码可以在Gradle的二进制文件或源码中的 samples/multiProjectBuildSrc
里看到。
Note: The code for this example can be found at samples/multiProjectBuildSrc
which is in both the binary and source distributions of Gradle.
你可以使用GradleBuild
任务。你可以通过使用 dir
或 buildFile
属性来指定执行哪一个构建,以及使用 tasks
属性来指定执行哪一个任务。
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.
示例 59.5. 从一个构建运行另一个构建 - Example 59.5. Running another build from a build
build.gradle
task build(type: GradleBuild) { buildFile = 'other.gradle' tasks = ['hello'] }
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.
如果你的构建脚本需要使用一些外部库,你可以在这个构建脚本的把它们添加到该脚本的classpath中。你可以通过使用buildscript()
方法,传入一个定义构建脚本classpath的闭包。
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.
示例 59.6. 声明构建脚本的外部依赖 - Example 59.6. Declaring external dependencies for the build script
build.gradle
buildscript { repositories { mavenCentral() } dependencies { classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' } }
这个传给buildscript()
方法的闭包配置了一个ScriptHandler
实例。你可以通过添加依赖到classpath
配置来定义构建脚本的classpath。这和你定义Java编译classpath是同样的。你可以使用在 第50.4节,“如何定义你的依赖”描述的除了项目依赖之外的任何依赖类型。
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.
声明了构建脚本的classpath之后,你可以使用构建脚本中的类,就像classpath上的任何其他类一样。下面的示例将添加到前面的示例中,并使用构建脚本classpath中的类。
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.
示例 59.7. 具有外部依赖的构建脚本 - Example 59.7. A build script with external dependencies
build.gradle
import org.apache.commons.codec.binary.Base64 buildscript { repositories { mavenCentral() } dependencies { classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' } } task encode << { 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.
For reasons we don't fully understand yet, external dependencies are not picked up by Ant's optional tasks. 但是你可以很容易地通过另一种方法做到。[26]
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]
示例 59.8. Ant 的可选依赖 - Example 59.8. Ant optional dependencies
build.gradle
configurations { ftpAntTask } dependencies { ftpAntTask("org.apache.ant:ant-commons-net:1.9.3") { module("commons-net:commons-net:1.4.1") { dependencies "oro:oro:2.0.8:jar" } } } task ftp << { ant { taskdef(name: 'ftp', classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP', classpath: configurations.ftpAntTask.asPath) ftp(server: "ftp.apache.org", userid: "anonymous", password: "me@myorg.com") { fileset(dir: "htdocs/manual") } } }
这对于客户端模块的用法也是一个很好的例子。在这个用例中,针对 ant-commons-net 任务的Maven中央仓的POM没有提供正确的信息。
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.
Gradle 提供了多种组织你的构建逻辑的方式。你可以选择最适合你的领域的方式,并在不必要的间界引用之间找到平衡,避免冗余和难以维护的代码库。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为你节省了这些不必要的开销和间接引用。
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.
[26] 事实上,我们认为这是更好的解决方案。仅当你的构建脚本和Ant的可选任务需要相同的 library时,你才需要定义它两次。在这种情况下,如果Ant 的可选任务会自动地获取在 gradesettings
中定义的classpath,就很好。
[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
.