Gradle 1.12用户指南翻译——第五十八章. 编写自定义插件

其他章节的翻译请参见:
http://blog.csdn.net/column/details/gradle-translation.html
翻译项目请关注Github上的地址:
https://github.com/msdx/gradledoc
本文翻译所在分支:
https://github.com/msdx/gradledoc/tree/1.12。
直接浏览双语版的文档请访问:
http://gradledoc.qiniudn.com/1.12/userguide/userguide.html。
另外,Android 手机用户可通过我写的一个程序浏览文档,带缓存功能的,目前0.6开发中版本兼容 Android 2.3以上系统,项目地址如下:
https://github.com/msdx/gradle-doc-apk
翻译不易,转载请注明本文在CSDN博客上的出处:
http://blog.csdn.net/maosidiaoxian/article/details/70549708

关于我对Gradle的翻译,以Github上的项目及http://gradledoc.qiniudn.com 上的文档为准。如发现翻译有误的地方,将首先在以上两个地方更新。因时间精力问题,博客中发表的译文基本不会同步修改。

另外,目前Gradle1.12版本的文档已经翻译完并进入校稿阶段,校稿的方式为到该项目https://github.com/msdx/gradledoc 提交issue或是pull request。校稿的结果不只是在此版本更新,也会用于改善Gradle下一版本(2.0)文档的翻译。


第五十八章. 编写自定义插件

Gradle 插件打包了可以复用的构建逻辑块,这些逻辑可以在不同的项目和构建中使用。Gradke 允许你实现你自己的自定义插件,因此你可以重用你的构建逻辑,并且与他人分享。

你可以使用你喜欢的任何一种语言来实现一个自定义插件,只要这个实现最终能够提供编译的字节码。在这里的例子中,我们将使用Gradle来作为实现语言。如果你想的话,你也可以使用Java或者是Scala来代替。

58.1. 打包插件

有几个地方可以让你放这个插件的源码。

构建脚本

你可以在构建脚本中直接包含这个插件的源码。这样做的好处是,你不需要再做什么,这个插件就能够被自动地编译并且包含到这个构建脚本的类路径当中。然而,在这个构建脚本脚本之外,这个插件是不可见的,因此你不能够在你定义这个插件的脚本以外的地方来复用它。

buildSrc 项目

你可以把插件的源码放在 rootProjectDir/buildSrc/src/main/groovy 目录中。Gradle将会编译和测试这个插件,并且使它在构建脚本的类路径中可用。这个插件在该构建所使用的每一个构建脚本当中都是可见的。然而,它在这个构建之外并不可见,因为你不能在定义它的这个构建之外的其他地方来重用这个插件。

有关buildSrc 项目的更详细信息,请参阅 第五十九章, 组织构建逻辑 。

独立项目

你可以为你的插件创建一个单独的项目。这个项目会输出和发布一个JAR文件,然后你可以在多个构建中使用,并且分享出去。一般来说,这个JAR可能包含一些自定义的插件,或者是捆绑几个相关的任务类到一个单独的库当中。或者是上面两者都有。

在我们的例子中,为了简单,我们将从在构建脚本中编写插件开始。然后我们会看看创建一个单独的项目的方式。

58.2. 编写一个简单的插件

想创建一个自定义插件,你需要写一个类实现 Plugin接口。当这个插件被用在一个project上时,Gradle会实例化这个插件,并且调用这个插件实例的 Plugin.apply() 方法。这个project对象会被作为一个参数传进去,该参数可以让插件用于对这个project配置它所需要的东西。下面的例子包含了一个问候语插件,它把一个 hello 任务添加到project中。

示例 58.1. 自定义插件

build.gradle

apply plugin: GreetingPlugin

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('hello') << {
            println "Hello from the GreetingPlugin"
        }
    }
}

gradle -q hello的输出结果

> gradle -q hello
Hello from the GreetingPlugin

需要注意的一点是,对于一个给定的插件,每一个配置使用它的项目都会创建一个新的实例。

58.3. 从构建中获取输入

大部分插件都需要从构建脚本中获得一些配置。实现这种目的的其中一种做法是使用 扩展对象。Gradle Project 与 ExtensionContainer 对象会有关联,该对象可以帮助追踪传给插件的所有配置和属性。通过你的插件相关的扩展容器,你可以获取用户的输入。要获取输入,只需将一个Java Bean兼容类添加到扩展容器的扩展列表中。对于一个插件而言,Groovy是一种不错的语言选择,因为普通的旧Groovy对象包含了一个Java Bean所需要的所有的getter和setter方法。

让我们向项目中添加一个简单的扩展对象。在这里,我们向该project添加了一个 greeting 扩展对象,它能够让你配置问候内容。

示例 58.2. 自定义插件扩展

build.gradle

apply plugin: GreetingPlugin

greeting.message = 'Hi from Gradle'

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        // Add the 'greeting' extension object
        project.extensions.create("greeting", GreetingPluginExtension)
        // Add a task that uses the configuration
        project.task('hello') << {
            println project.greeting.message
        }
    }
}

class GreetingPluginExtension {
    def String message = 'Hello from GreetingPlugin'
}

gradle -q hello的输出结果

> gradle -q hello
Hi from Gradle

在这个示例中,GreetingPluginExtension 是一个plain old Groovy 对象,它有一个成员变量 message。这个扩展对象以greeting名字添加到插件列表中。然后该对象变为有效的一个项目属性,与这个扩展对象的名称相同。

通常情况下,你会有几个相关的属性,所以你需要在单个插件上指定。Gradle 为每一个扩展对象添加了一个配置闭包块,因此你可以把这些配置分组。下面是如何分组的示例。

示例 58.3. 使用配置闭包的自定义插件

build.gradle

apply plugin: GreetingPlugin

greeting {
    message = 'Hi'
    greeter = 'Gradle'
}

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create("greeting", GreetingPluginExtension)
        project.task('hello') << {
            println "${project.greeting.message} from ${project.greeting.greeter}"
        }
    }
}

class GreetingPluginExtension {
    String message
    String greeter
}

gradle -q hello的输出结果

> gradle -q hello
Hi from Gradle

在这个例子中,通过 greeting闭包,多个配置可以被组合在一起。在构建脚本中,这个闭包块的名字(greeting)需要和扩展对象的名字相匹配。然后,当这个闭包被执行的时候,这个扩展对象的变量会被映射到基于标准的Groovy闭包代理功能的闭包里的这些变量中。

58.4. 在自定义任务和插件中使用文件

当开发自定义任务和插件时,接收输入配置的文件位置能使得在灵活性上表现得更好。为此,你可以利用 Project.file() 方法来尽可能晚地把值解析到文件中。

示例 58.4. 文件属性的惰性评估

build.gradle

class GreetingToFileTask extends DefaultTask {

    def destination

    File getDestination() {
        project.file(destination)
    }

    @TaskAction
    def greet() {
        def file = getDestination()
        file.parentFile.mkdirs()
        file.write "Hello!"
    }
}

task greet(type: GreetingToFileTask) {
    destination = { project.greetingFile }
}

task sayGreeting(dependsOn: greet) << {
    println file(greetingFile).text
}

greetingFile = "$buildDir/hello.txt"

Output of gradle -q sayGreeting

> gradle -q sayGreeting
Hello!

在这个例子中,我们配置了 greet 任务的 destination 属性为一个闭包, 它将在最后通过Project.file() 方法将闭包中的返回值转为一个文件对象。你会注意到,在上面的例子中,我们是在已经配置了在作用中使用 greetingFile 属性之后才指定它的值。这种懒评估的主要好处是当设置文件属性时它能够接收任何值,并在读取这个属性的时候去解析这个值。

58.5. 一个独立项目

现在我们将移动我们的插件到一个单独的项目中,这样我们就可以发布它,并与他人分享。这个项目只是一个简单的Groovy项目,它将产生一个包含插件类的JAR包。下面是该项目的一个简单的构建脚本。它配置使用Groovy插件,并且添加Gradle API 作为编译时依赖。

示例 58.5. 自定义插件的构建

build.gradle

apply plugin: 'groovy'

dependencies {
    compile gradleApi()
    compile localGroovy()
}

注意: 此例子的代码可以在Gradle的二进制文件或源码中的 samples/customPlugin/plugin 里看到。

那么,Gradle是怎么找到 Plugin 实现的?答案是你需要在jar文件里的META-INF/gradle-plugins目录中提供一个属性文件,让它与你的插件名相匹配。

示例 58.6. 编写一个自定义插件

src/main/resources/META-INF/gradle-plugins/greeting.properties

implementation-class=org.gradle.GreetingPlugin

注意,属性的文件名要和插件名相匹配,放在resources夹里,并且implementation-class属性要标识Plugin 的实现类。

58.5.1. 在另一个项目中使用你的插件

要在一个构建脚本中使用一个插件,你需要先将这个插件的类添加到构建脚本的classpath中。要做到这一点,你要使用在第 59.5 节,“构建脚本的外部依赖”中描述的 buildscript { } 块。下面的示例展示了当包含了插件的JAR文件已经被发布到一个本地仓库时,你可以怎么做。

示例 58.7. 在另一个项目中使用一个自定义插件

build.gradle

buildscript {
    repositories {
        maven {
            url uri('../repo')
        }
    }
    dependencies {
        classpath group: 'org.gradle', name: 'customPlugin', version: '1.0-SNAPSHOT'
    }
}
apply plugin: 'greeting'

58.5.2. 为你的插件编写测试

当你要测试你的插件实现时,你可以使用ProjectBuilder 类来创建 Project 实例去使用你的任务类。

示例 58.8. 测试自定义插件

src/test/groovy/org/gradle/GreetingPluginTest.groovy

class GreetingPluginTest {
    @Test
    public void greeterPluginAddsGreetingTaskToProject() {
        Project project = ProjectBuilder.builder().build()
        project.apply plugin: 'greeting'

        assertTrue(project.tasks.hello instanceof GreetingTask)
    }
}

58.6. 维护多个域对象

Gradle 提供了一些实用工具类,能够良好地在Gradle构建语言中使用,用于维护对象集合。

示例 58.9. 管理域对象

build.gradle

apply plugin: DocumentationPlugin

books {
    quickStart {
        sourceFile = file('src/docs/quick-start')
    }
    userGuide {

    }
    developerGuide {

    }
}

task books << {
    books.each { book ->
        println "$book.name -> $book.sourceFile"
    }
}

class DocumentationPlugin implements Plugin<Project> {
    void apply(Project project) {
        def books = project.container(Book)
        books.all {
            sourceFile = project.file("src/docs/$name")
        }
        project.extensions.books = books
    }
}

class Book {
    final String name
    File sourceFile

    Book(String name) {
        this.name = name
    }
}

gradle -q books的输出结果

> gradle -q books
developerGuide -> /home/user/gradle/samples/userguide/organizeBuildLogic/customPluginWithDomainObjectContainer/src/docs/developerGuide
quickStart -> /home/user/gradle/samples/userguide/organizeBuildLogic/customPluginWithDomainObjectContainer/src/docs/quick-start
userGuide -> /home/user/gradle/samples/userguide/organizeBuildLogic/customPluginWithDomainObjectContainer/src/docs/userGuide

Project.container()方法创建了一个 NamedDomainObjectContainer实例,该实例具体许多有用的方法,用于管理及配置这些对象。为了使用任意一种project.container类型的方法,必须公开一个 “name” 属性,作为该对象的唯一且恒定的名称。容器方法里的project.container(Class)通过尝试调用该类带有一个string参数的构造方法创建了新实例,该参数是这个对象所需的名称。请参阅上面的链接,查看project.container 允许自定义实例化策略的的其他重载方法。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值