Gradle-使用Gradle构建和测试-2-GradleTasks(二)

原版电子书下载地址:Build and Test with Gradle.pdf

Chapter 2. Gradle Tasks

2.5 Task Types 任务类型

正如我们在上一节中讨论的“Tasks Are Objects”,每一个任务都有一种类型。除了DefaultTask之外,还有像copying(拷贝),archiving(归档),executing(执行)程序的任务类型甚至更多。声明一个任务类型很像面向对象编程语言中的继承一个基类:你可以在你的任务中自由地获得某个可用的方法和属性。这使得非常简单的任务却可以完成很多事。

一个完整的任务参考已经超出了本节的范围,但是这里有一些如何使用每一个重要类型的例子。

2.5.1 Copy

copy任务是用来从一个地方拷贝文件到另一个地方(例2-33).它的最基本的形式是拷贝一个文件从一个目录到另一个目录,限制条件是对某中文件格式包含与否。

例2-33. A simple example of the copy task

task copyFiles(type: Copy) {
 from 'resources'
 into 'target'
 include '**/*.xml', '**/*.txt', '**/*.properties'
}

这个拷贝任务将会创建一个目标目录如果不存在该目录。在本例中,copyFiles任务将会拷贝任何的以.xml,.properties或.txt为后缀的文件从resources目录到target目录。注意到from,into,include方法都是继承自Copy。

2.5.2 Jar

Jar任务即从源文件创建一个Jar文件(例2-34). Java plug-in创建了这个任务类型,毫无疑问地称作jar。它用一个微不足道的清单文件将main源码集和resources资源集一起打包成一个jar,这个jar文件以build/libs目录下的项目名称命名。下面的任务是高度可定制的。

例2-34. A simple example of the Jar task in the jar-task example project

apply plugin: 'java'
task customJar(type: Jar) {
 manifest {
 attributes firstKey: 'firstValue', secondKey: 'secondValue'
 }
 archiveName = 'hello.jar'
 destinationDir = file("${buildDir}/jars")
 from sourceSets.main.classes
}

注意到archive名和destination目录名是很容易配置的 。同样地,manifest也可以用自定义的使用了可读的Gradle map语法的属性来填充。JAR文件的内容由 from sourceSet.main.classes这行定义,指定了编译的main源码的.class文件要被包含进来。from方法与使用在CopyTask示例中的完全相同,这揭示了一个有趣的观点:Jar任务继承自Copy任务。甚至在之前我们已经看到了Gradle对象模型和DSL的详尽的文档,这些细节暗示了底层框架的丰富性和顺序性。

那个赋值给destinationDir的表达式是要值得注意的地方。它只是很自然地将一个字符串赋值给了destinationDir,但是这个属性期待一个与java.io.File相兼容的参数。通常在Gradle构建文件里面都可以使用的file()方法是用来将一个字符串转变成一个File对象。

提示:你总是可以打开 docs/dsl/index.html文件来查看标准的Gradle功能像Jar任务的文档。

2.5.3 JavaExec

JavaExec任务运行带有man()方法的java类。Java命令行可能有点难,但这个任务消除了这个难度而且还集成了Java命令行到build。
例2-35.A Gradle task executing a command-line Java program (from the javaexec-task
example)

apply plugin: 'java'
repositories {
 mavenCentral()
}
dependencies {
 runtime 'commons-codec:commons-codec:1.5'
}
task encode(type: JavaExec, dependsOn: classes) {
 main = 'org.gradle.example.commandline.MetaphoneEncoder'
 args = "The rain in Spain falls mainly in the plain".split().toList()
 classpath sourceSets.main.classesDir
 classpath configurations.runtime
}

提示:在encode任务中的classpath属性被设置成称作configuration.runtime的东西。配置是一个有一些共同点的依赖的集合。在本例中,runtime配置保持了所有的依赖,这些依赖必须可用于运行时的程序。这个与依赖形成了对比,依赖只在编译期间所需要或者只在测试运行时,或者只在编译和运行期间所需要,但是依赖是由运行时环境如应用服务器所提供。在Gradle中的configuration属性是由build定义的所有配置项的集合,每一个配置属性是实际依赖的集合。

这个构建文件声明了一个外部的依赖:Apache Commons Codec库。通常地,我们要编译我们的Java文件时,则要编造一个包含了编译类和JAR依赖的路径的java命令行。然后,在这个build文件中,我们只需简单地指定要运行的main类(org.gradle.example.commandline.MetaphoneEncoder),以列表的形式提供命令行参数,然后指向所需的Gradle classpath元素。在本例中,我们可以象征性地指向 main sourceSet的类以及所有在compile配置中声明的依赖。如果我们有一个来自多个仓库的几十个依赖的复杂集,这个简单的任务依然可以工作。

2.6 Custom Task Types 自定义任务类型

有一些这样的场合,Gradle的内置任务不足以满足需求工作,反而,开发你的build的最有表现力的方式将是创建一个自定义的任务。Gradle有多种方式可以这样做。这里我们将讨论两种最普遍的方式。

2.6.1 Custom Tasks Types in the Build File

假设你的build文件需要针对MySQL数据库发布任意的查询。在Gradle中有多种方式来完成这个目标,但是你自己决定一个自定义的任务会是最有表现力的方式。最简单的方式来介绍这个任务就是简单地在你的build脚本按如下方式创建:
例2-36. A custom task to perform queries against a MySQL database (from the custom-task
example)

task createDatabase(type: MySqlTask) {
 sql = 'CREATE DATABASE IF NOT EXISTS example'
}
task createUser(type: MySqlTask, dependsOn: createDatabase) {
 sql = "GRANT ALL PRIVILEGES ON example.*
 TO exampleuser@localhost IDENTIFIED BY 'passw0rd'"
 }
task createTable(type: MySqlTask, dependsOn: createUser) {
 username = 'exampleuser'
 password = 'passw0rd'
 database = 'example'
 sql = 'CREATE TABLE IF NOT EXISTS users
 (id BIGINT PRIMARY KEY, username VARCHAR(100))'
}
class MySqlTask extends DefaultTask {
 def hostname = 'localhost'
 def port = 3306
 def sql
 def database
 def username = 'root'
 def password = 'password'
 @TaskAction
 def runQuery() {
 def cmd
 if(database) {
 cmd = "mysql -u ${username} -p${password} -h ${hostname}
 -P ${port} ${database} -e "
 }
 else {
 cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} -e "
 }
 project.exec {
 commandLine = cmd.split().toList() + sql
 }
 }
}

这个自定义的MySqlTask,继承自DefaultTask.所有的自定义任务必须继承这个类或者它的一个子类。这个任务用Gradle传统的习语来声明属性(如hostname,database,sql等)。然后它声明一个简单的方法runQuery(),这个方法用@TaskAction所注解,它将会在任务运行时运行。

实际的在build文件顶部的任务都将它们自己声明为MySqlType类型。通过这样做,它们就自动继承了该任务类型的属性和动作。因为大多数属性都有默认值,每个任务调用将只有一小部分需要配置。createDatabasecreateUser任务可以仅仅配置为一个单一的SQL查询,而且可以允许默认值去接管那里。

createTable任务覆盖了username,password以及database属性,由于它的任务依赖已经创建了一个新的数据库,username就从默认的administrative设置中分离了出来。在Gradle中,这种提供一个有用的当需要时可以被覆盖的默认配置的模式是一种经常出现的主题。

2.6.2 Custom Tasks in the Source Tree

大量的自定义任务逻辑将不会很好地适应构建文件。一些简单得脚本行可以很实用地插入到一个短任务中,正如上一节中的示例。然而,在某种程度上,一个复杂的任务将可以开发一个它自己的类层次,可以开发一个基于外部APIs的依赖,而且将需要自动测试。构建也就是代码,复杂的构建代码应该被当作一个开发世界的顶级对象。Gradle让这点变得简单。

当自定义任务逻辑过长而不适于构建文件时,我们可以把它迁移到项目根目录的buildSrc目录。
例2-37. A build file using a custom task not defined in the build script

task createDatabase(type: MySqlTask) {
 sql = 'CREATE DATABASE IF NOT EXISTS example'
}
task createUser(type: MySqlTask, dependsOn: createDatabase) {
 sql = "GRANT ALL PRIVILEGES ON example.*
 TO exampleuser@localhost IDENTIFIED BY 'passw0rd'"
}
task createTable(type: MySqlTask, dependsOn: createUser) {
 username = 'exampleuser'
 password = 'passw0rd'
 database = 'example'
 sql = 'CREATE TABLE IF NOT EXISTS users
 (id BIGINT PRIMARY KEY, username VARCHAR(100))'
}

例2-38. The definition of the custom task under the buildSrc directory

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class MySqlTask extends DefaultTask {
 def hostname = 'localhost'
 def port = 3306
 def sql
 def database
 def username = 'root'
 def password = 'password'
 @TaskAction
 def runQuery() {
 def cmd
 if(database) {
 cmd = "mysql -u ${username} -p${password} -h ${hostname}
 -P ${port} ${database} -e "
 }
 else {
     cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} -e "
 }
 project.exec {
 commandLine = cmd.split().toList() + sql
 }
 }
}

注意到在buildSrc目录下的任务定义与之前例子中的build脚本代码非常相似。然而,现在我们已经有了一个强健的平台用于阐释简单的任务定义,生成一个对象模型,写测试代码及一切其他我们开发软件时正常做的事情。

提示:有4种选择去放置你的自定义Gradle构建代码。第一种是放置到build脚本本身,在一个任务动作块中。第二种是放置在buildSrc目录下正如我们刚展示的例子。第三种是通过单独的build脚本文件imported into导入到主build脚本中。第四中是用Java或者Groovy写的自定义的插件plug-in。用自定义的plug-in编写Gradle程序将会有一个单独的章节来阐述。

例2-39. The structure of a Gradle project with custom code in the buildSrc directory.


├── build.gradle
├── buildSrc
│     └── src
│           └── main
│               └── groovy
│                    └── org
│                       └── gradle
│                           └── example
│                               └── task
│                                   └── MySqlTask.groovy

2.7 Where Do Tasks Come From? 任务来自哪?

到目前为止,我们都是通过直接编码的方式创建任务,要么在Gradle build脚本里要么在buildSrc目录作为Groovy代码。这是一种非常好的方式来了解任务,因为这很容易详细地查看所有的活动部分。然而,许多你使用的任务并不是你所写的任务,而是它们来自于plug-ins插件。

你已经看到了这个章节中的在building Java code里的一个示例。通过应用Java plug-in 插件,构架脚本自动继承一些列的你无法直接看到其代码的任务。你可以修改这些任务的行为,通过使用我们之前章节讲到的task configuration,doFirst(),及doLast()方法,但是你自己不用去对它们编码。事实上,Gradle将为你提供丰富的,可扩展的你通过Gradle DSL而不是许多的字面Groovy代码调用的任务功能代码,这也正是Gradle的提供低复杂高扩展的核心理念。

Gradle也有一些内置的任务,像tasksproperties.这些由任何plug-in或任务你写的重要代码所提供,但是它们只是Gradle DSL的一个标准部分。它们将会在Gradle command line章节中讲到。

2.8 Conclusion 总结

在本章中我们已经对任务有了一个很全面的看法。我们已经知道了如何去配置它们和如何去编写它们的脚本,以及对Gradle在两个生命周期之间如何分割配置和执行的工作有了一个概念。我们已经知道任务是有着它们自己丰富的API的顶级Groovy对象。我们已经探讨了API仅能够去教你如何思考任务作为可编程的实体。我们也已经了解到了一些标准的提供了开箱即用的真正功能的class类型。

最后我们了解了下如何编写你自己的任务。Gradle内置任务和插件是足以为许多用户去编写它们自己的builds脚本而不用任何的自定义代码,当然不总是这样。对Gradle一个最基本的感觉是对你应该很容易去扩展你的build而不用因为有很多难以维护的Groovy代码而使得你的build脚本很乱。自定义的任务示例在本章中我们也有讲过。

在Gradle中任务是构建活动最基本的单元。对于它们的story不是一个简介就能讲清的,但是通过本章的学习,你将准备好开始使用它们然后继续学习它们。

原版电子书下载地址:Build and Test with Gradle.pdf

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值