在整个Gradle,有两个最基础的概念:项目 和 任务。
Everything in Gradle sits on top of two basic concepts: projects and tasks.
任何一个Gradle构建都是由一个或多个项目组成。一个项目代表着什么,取决于你想通过Gradle来做什么。比如,一个项目可能代表着一个JAR库,或者是一个Web应用程序。它也可能代表从其他项目所生成的JAR包组装起来的ZIP文件。一个项目不一定是代表一个要构建的东西,它也可能代表一个要完成的东西,比如把您的应用部署到预发布或生产环境。如果现在看得不是很明白,也不用担心。Gradle的按约定来构建的支持为项目提供了更具体的定义。
Every Gradle build is made up of one or more projects. What a project represents depends on what it is that you are doing with Gradle. For example, a project might represent a library JAR or a web application. It might represent a distribution ZIP assembled from the JARs produced by other projects. A project does not necessarily represent a thing to be built. It might represent a thing to be done, such as deploying your application to staging or production environments. Don't worry if this seems a little vague for now. Gradle's build-by-convention support adds a more concrete definition for what a project is.
每一个项目都由一个或多个任务组成。一个任务表示构建执行的一些原子工作,比如编译一些类,创建一个JAR包,生成javadoc,或者是把一些档案发布到仓库中。
Each project is made up of one or more tasks. A task represents some atomic piece of work which a build performs. This might be compiling some classes, creating a JAR, generating javadoc, or publishing some archives to a repository.
现在,我们来看一下如何在一个项目的构建中定义一些简单的任务。后续的章节将更多的关注多项目的构建,以及多项目和多任务的内容。
For now, we will look at defining some simple tasks in a build with one project. Later chapters will look at working with multiple projects and more about working with projects and tasks.
您可以使用 gradle 命令来运行Gradle构建。 gradle命令会在当前目录下查找一个 build.gradle
文件。 [2] 我们把这个 build.gradle
文件称为是一个 构建脚本,尽管严格上来讲,它只是一个构建的配置脚本,我们将会在后面看到。这个构建脚本定义了一个项目和它的任务。
You run a Gradle build using the gradle command. The gradle command looks for a file called build.gradle
in the current directory. [2] We call this build.gradle
file a build script, although strictly speaking it is a build configuration script, as we will see later. The build script defines a project and its tasks.
试着创建一个叫 build.gradle
的构建脚本,如下所示:
To try this out, create the following build script named build.gradle
.
示例 6.1. 第一个构建脚本 - Example 6.1. The first build script
build.gradle
task hello {
doLast {
println 'Hello world!'
}
}
然后在命令行shell里,进入到当前目录并运行gradle -q hello
来执行这个构建脚本:
In a command-line shell, enter into the containing directory and execute the build script by running gradle -q hello
:
-q
参数的作用是什么?本用户指南中的很多例子都用了 -q
参数运行,这个参数是用于抑制Gradle的日志消息,以便只显示任务的输出结果,这样会使得在本用户指南里的文档的输出更清晰一点。如果你不想要的话,你可以不加上这个参数。如果想了解更多影响Gradle的输出的命令参数,请参阅第十八章, 日志
Most of the examples in this user guide are run with the -q
command-line option. This suppresses Gradle's log messages, so that only the output of the tasks is shown. This keeps the example output in this user guide a little clearer. You don't need to use this option if you don't want. See Chapter 18, Logging for more details about the command-line options which affect Gradle's output.
-q
do?示例 6.2. 执行构建脚本 - Example 6.2. Execution of a build script
gradle -q hello
的输出结果
Output of gradle -q hello
> gradle -q hello Hello world!
我们来看看这个脚本做了些什么。它定义了一个叫做 hello
的任务,并且给它加了一个动作。当你执行gradle hello
的时候,Gradle会执行这个hello
任务,而这个任务又会执行您所提供的动作。这个动作只是一个包含了一些要执行的Groovy代码的闭包。
What's going on here? This build script defines a single task, called hello
, and adds an action to it. When you run gradle hello
, Gradle executes the hello
task, which in turn executes the action you've provided. The action is simply a closure containing some Groovy code to execute.
如果你认为它看上去和Ant的目标很像,那就对了。Gradle的任务相当于Ant的目标。但是,正如你所见,它们更加强大。我们使用与Ant不同的术语,是因为我们认为任务这个词比目标的含义更丰富。不过不幸的是,这也导致了与Ant的术语冲突。Ant会调用它自己的一些命令,比如像javac
或是 copy
任务。因为,当我们讨论任务时,通常指的是Gradle的任务,相当于Ant的目标。当我们讨论Ant 的任务时,我们会明确地说是 ant 任务。
If you think this looks similar to Ant's targets, well, you are right. Gradle tasks are the equivalent to Ant targets. But as you will see, they are much more powerful. We have used a different terminology than Ant as we think the word task is more expressive than the word target. Unfortunately this introduces a terminology clash with Ant, as Ant calls its commands, such as javac
or copy
, tasks. So when we talk about tasks, we always mean Gradle tasks, which are the equivalent to Ant's targets. If we talk about Ant tasks (Ant commands), we explicitly say ant task.
有一种简单的方法可以定义像上面我们的这类hello
任务,它看起来更简洁。
There is a shorthand way to define a task like our hello
task above, which is more concise.
示例6.3. 任务定义的快捷方式 - Example 6.3. A task definition shortcut
build.gradle
task hello << {
println 'Hello world!'
}
这里再一次使用了一个闭包的方式来定义一个 hello
任务去执行。在本用户指南中,我们还会再使用这种任务定义的风格。
Again, this defines a task called hello
with a single closure to execute. We will use this task definition style throughout the user guide.
Gradle的构建脚本向您开放了Groovy的全部功能。作为开胃菜,可以看看下这个例子:
Gradle's build scripts expose to you the full power of Groovy. As an appetizer, have a look at this:
示例6.4. 在Gradle任务中使用Groovy - Example 6.4. Using Groovy in Gradle's tasks
build.gradle
task upper << { String someString = 'mY_nAmE' println "Original: " + someString println "Upper case: " + someString.toUpperCase() }
Output of gradle -q upper
的输出结果
Output of gradle -q upper
> gradle -q upper Original: mY_nAmE Upper case: MY_NAME
或者
or
示例6.5. 在Gradle任务中使用Groovy - Example 6.5. Using Groovy in Gradle's tasks
build.gradle
task count << {
4.times { print "$it " }
}
Output of gradle -q count
的输出结果
Output of gradle -q count
> gradle -q count 0 1 2 3
你应该也已经猜到了,我们可以声明任务之间的依赖关系。
As you probably have guessed, you can declare dependencies between your tasks.
示例6.6. 声明任务之间的依赖关系 - Example 6.6. Declaration of dependencies between tasks
build.gradle
task hello << { println 'Hello world!' } task intro(dependsOn: hello) << { println "I'm Gradle" }
gradle -q intro
的输出结果
Output of gradle -q intro
> gradle -q intro Hello world! I'm Gradle
在添加依赖时,对应的任务不一定要存在。
To add a dependency, the corresponding task does not need to exist.
示例6.7. 延迟依赖——另一个任务(暂)不存在 - Example 6.7. Lazy dependsOn - the other task does not exist (yet)
build.gradle
task taskX(dependsOn: 'taskY') << { println 'taskX' } task taskY << { println 'taskY' }
gradle -q taskX
的输出结果
Output of gradle -q taskX
> gradle -q taskX taskY taskX
taskX
依赖 taskY
是在 taskY
之前定义的。这点在多项目构建中非常有用。有关任务依赖的更多信息,可以参阅 第15.4节,“给任务添加依赖”。
The dependency of taskX
to taskY
is declared before taskY
is defined. This is very important for multi-project builds. Task dependencies are discussed in more detail in Section 15.4, “Adding dependencies to a task”.
请注意,当一个任务还没有被定义的时候,不能使用快捷符号(见 第6.8节,“快捷标记”)。
Please notice that you can't use shortcut notation (see Section 6.8, “Shortcut notations”) when referring to a task that is not yet defined.
Groovy的强大之处,不仅仅是用在定义一个任务做什么的时候。例如,你也可以使用它来动态地创建一些任务。
The power of Groovy can be used for more than defining what a task does. For example, you can also use it to dynamically create tasks.
示例6.8. 任务的动态创建 - Example 6.8. Dynamic creation of a task
build.gradle
4.times { counter -> task "task$counter" << { println "I'm task number $counter" } }
gradle -q task1
的输出结果
Output of gradle -q task1
> gradle -q task1 I'm task number 1
与Ant不同,一旦任务被创建了,就可以通过一个 API去访问它。例如,你可以去给它们添加其他的依赖。
Once tasks are created they can be accessed via an API. This is different to Ant. For example you can create additional dependencies.
示例6.9. 通过API访问任务——添加依赖 - Example 6.9. Accessing a task via API - adding a dependency
build.gradle
4.times { counter -> task "task$counter" << { println "I'm task number $counter" } } task0.dependsOn task2, task3
gradle -q task0
的输出结果
Output of gradle -q task0
> gradle -q task0 I'm task number 2 I'm task number 3 I'm task number 0
或者,你也可以对已经存在的任务添加行为。
Or you can add behavior to an existing task.
示例6.10. 通过API访问任务——添加行为 - Example 6.10. Accessing a task via API - adding behaviour
build.gradle
task hello << { println 'Hello Earth' } hello.doFirst { println 'Hello Venus' } hello.doLast { println 'Hello Mars' } hello << { println 'Hello Jupiter' }
gradle -q hello
的输出结果
Output of gradle -q hello
> gradle -q hello Hello Venus Hello Earth Hello Mars Hello Jupiter
doFirst
和doLast
可以被多次调用,它们分别是向任务的动作列表的开头或结尾添加一个动作。当任务执行的时候,动作列表的这些动作会依次执行。<<
操作只是 doLast
的别名。
The calls doFirst
and doLast
can be executed multiple times. They add an action to the beginning or the end of the task's actions list. When the task executes, the actions in the action list are executed in order. The <<
operator is simply an alias for doLast
.
你可能在前面的例子中也注意到了,一个已存在任务会有一个便捷的符号用于访问。 每一个任务都可作为这个构建脚本中的一个属性。
As you might have noticed in the previous examples, there is a convenient notation for accessing an existing task. Each task is available as a property of the build script:
示例6.11. 以构建脚本的属性的方式访问任务 - Example 6.11. Accessing task as a property of the build script
build.gradle
task hello << { println 'Hello world!' } hello.doLast { println "Greetings from the $hello.name task." }
gradle -q hello
的输出结果
Output of gradle -q hello
> gradle -q hello Hello world! Greetings from the hello task.
这使得代码很易于阅读,尤其是当使用插件所提供的任务时(例如 compile
)。
This enables very readable code, especially when using the out of the box tasks provided by the plugins (e.g. compile
).
您可以把您自己的属性添加到一个任务中。譬如如果要添加一个 myProperty
属性,可以为 ext.myProperty
设置一个初始值,然后您就可以像使用预定义的任务属性一样对它进行读取或设置。
You can add your own properties to a task. To add a property named myProperty
, set ext.myProperty
to an initial value. From that point on, the property can be read and set like a predefined task property.
示例6.12. 为任务添加额外的属性 - Example 6.12. Adding extra properties to a task
build.gradle
task myTask {
ext.myProperty = "myValue"
}
task printTaskProperties << {
println myTask.myProperty
}
gradle -q printTaskProperties
的输出结果
Output of gradle -q printTaskProperties
> gradle -q printTaskProperties myValue
Ant任务是Gradle的一级公民。Gradle通过简单地依赖Groovy,对Ant任务提供了强大的集成。Groovy自带了一个神奇的AntBuilder
,在Gradle中使用Ant任务比在build.xml
中调用更方便和强大。通过下面的例子,您可以学习到如何执行ant任务,以及如何访问ant属性:
Ant tasks are first-class citizens in Gradle. Gradle provides excellent integration for Ant tasks by simply relying on Groovy. Groovy is shipped with the fantastic AntBuilder
. Using Ant tasks from Gradle is as convenient and more powerful than using Ant tasks from a build.xml
file. From the example below, you can learn how to execute ant tasks and how to access ant properties:
示例6.13. 使用AntBuilder 执行 ant.loadfile 目标 - Example 6.13. Using AntBuilder to execute ant.loadfile target
build.gradle
task loadfile << { def files = file('../antLoadfileResources').listFiles().sort() files.each { File file -> if (file.isFile()) { ant.loadfile(srcFile: file, property: file.name) println " *** $file.name ***" println "${ant.properties[file.name]}" } } }
gradle -q loadfile
的输出结果
Output of gradle -q loadfile
> gradle -q loadfile *** agile.manifesto.txt *** Individuals and interactions over processes and tools Working software over comprehensive documentation Customer collaboration over contract negotiation Responding to change over following a plan *** gradle.manifesto.txt *** Make the impossible possible, make the possible easy and make the easy elegant. (inspired by Moshe Feldenkrais)
在构建脚本中您可以利用Ant做更多的事情,更多细节请参阅 第十七章,在Gradle中使用Ant。
There is lots more you can do with Ant in your build scripts. You can find out more in Chapter 17, Using Ant from Gradle.
Gradle的内向扩展取决于您如何组织您的构建逻辑。在上面的例子中,组织您的构建逻辑的第一个层次是抽取出方法。
Gradle scales in how you can organize your build logic. The first level of organizing your build logic for the example above, is extracting a method.
示例6.14. 在构建逻辑中使用方法 - Example 6.14. Using methods to organize your build logic
build.gradle
task checksum << { fileList('../antLoadfileResources').each {File file -> ant.checksum(file: file, property: "cs_$file.name") println "$file.name Checksum: ${ant.properties["cs_$file.name"]}" } } task loadfile << { fileList('../antLoadfileResources').each {File file -> ant.loadfile(srcFile: file, property: file.name) println "I'm fond of $file.name" } } File[] fileList(String dir) { file(dir).listFiles({file -> file.isFile() } as FileFilter).sort() }
gradle -q loadfile
的输出结果
Output of gradle -q loadfile
> gradle -q loadfile I'm fond of agile.manifesto.txt I'm fond of gradle.manifesto.txt
在后面您将会看到,像这样的方法可以在多项目构建的子项目之间共享。即使您的构建逻辑变得很复杂,Gradle也提供了一种非常方便的方式去组织它。关于这一点,我们会用一整个章节来讨论,详请参阅 第五十九章,组织构建逻辑。
Later you will see that such methods can be shared among subprojects in multi-project builds. If your build logic becomes more complex, Gradle offers you other very convenient ways to organize it. We have devoted a whole chapter to this. See Chapter 59, Organizing Build Logic.
Gradle允许您在构建中定义一个或多个的默认任务。
Gradle allows you to define one or more default tasks for your build.
示例6.15. 定义一个默认任务 - Example 6.15. Defining a default tasks
build.gradle
defaultTasks 'clean', 'run' task clean << { println 'Default Cleaning!' } task run << { println 'Default Running!' } task other << { println "I'm not a default task!" }
gradle -q
的构建结果
Output of gradle -q
> gradle -q Default Cleaning! Default Running!
这与运行 gradle clean run
的结果是一样的。在多项目构建中,每一个子项目都可以有它自己的指定的默认任务。如果一个子项目没有指定默认任务,而父项目定义了的话,那么将会使用父项目的。
This is equivalent to running gradle clean run
. In a multi-project build every subproject can have its own specific default tasks. If a subproject does not specify default tasks, the default tasks of the parent project are used (if defined).
正如我们后面会详细描述的的(见第五十五章,构建的生命周期),Gradle有一个配置阶段和一个执行阶段。在配置阶段后,Gradle会了解所有应该执行的任务。Gradle提供了一个钩子来让你使用这些信息。一个使用场景是,可以检查某个发布任务是否在这些要执行的任务当中。借由此,您可以为一些变量进行不同的赋值。
As we later describe in full detail (see Chapter 55, The Build Lifecycle), Gradle has a configuration phase and an execution phase. After the configuration phase, Gradle knows all tasks that should be executed. Gradle offers you a hook to make use of this information. A use-case for this would be to check if the release task is among the tasks to be executed. Depending on this, you can assign different values to some variables.
在下面的例子中,distribution
和release
任务的执行会导致version
变量有不同的结果。
In the following example, execution of the distribution
and release
tasks results in different value of the version
variable.
示例6.16. 根据所选择的任务输出不同的构建结果 - Example 6.16. Different outcomes of build depending on chosen tasks
build.gradle
task distribution << { println "We build the zip with version=$version" } task release(dependsOn: 'distribution') << { println 'We release now' } gradle.taskGraph.whenReady {taskGraph -> if (taskGraph.hasTask(release)) { version = '1.0' } else { version = '1.0-SNAPSHOT' } }
gradle -q distribution
的输出结果
Output of gradle -q distribution
> gradle -q distribution We build the zip with version=1.0-SNAPSHOT
gradle -q release
的输出结果
Output of gradle -q release
> gradle -q release We build the zip with version=1.0 We release now
在这里,很重要的一点是,whenReady
会在发布任务执行之前影响到它。即使这个发布任务不是 主要的 (即传给 gradle 命令行的任务),这一点也同样有效。
The important thing is that whenReady
affects the release task before the release task is executed. This works even when the release task is not the primary task (i.e., the task passed to the gradle command).
在本章中,我们已经对任务有了初步的了解。但是关于任务的内容还不仅仅是这些,如果你想了解更多的细节,请参阅 第十五章,任务进阶。
In this chapter, we have had a first look at tasks. But this is not the end of the story for tasks. If you want to jump into more of the details, have a look at Chapter 15, More about Tasks.
另外,本教程接下来是 《第七章, Java 快速入门》 以及《第八章, 依赖管理基础》。
Otherwise, continue on to the tutorials in Chapter 7, Java Quickstart and Chapter 8, Dependency Management Basics.
[2] 有一些命令行开关可以改变这一行为,请参阅《附录 D, Gradle 命令行》。
[2] There are command line switches to change this behavior. See Appendix D, Gradle Command Line)