codingWang / Issue

private space .Do NOT look at it.
3 stars 0 forks source link

doc-gradle-CustomPlugin #3

Open codingWang opened 7 years ago

codingWang commented 7 years ago

Gradle自定义插件

翻译自: http://gradledoc.qiniudn.com/1.12/userguide/custom_plugins.html

转载请注明出处:https://github.com/codingWang @auther duwei

一、概述

Gradle插件用于封装可重用的构建逻辑并可在不同的项目中重复使用。 你可以使用Java,Scala等语言编写插件,但最好还是使用Groovy语言吧。 只要最后提供字节码就行了

二、你可以把插件的源码放在如下几个地方:

下面我们先在构建脚本中写插件,最后再在单独的项目中写。

三、写一各简单的插件

要写一个简单的插件,需要实现Plugin接口,当插件在项目中被使用的时候,Gradle实例化插件,并调用实例的Plugin.apply()方法。 project对象被当成参数传给插件用于配置项目。下面的代码演示了给project对象添加一个hello任务的插件

 /*******************简单的自定义插件*******************************/
 apply plugin: GreetingPlugin

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

当该插件被应用到一个项目中时,一个新的插件实例就会被创建。

四.从构建中获取输入

大多数的插件需要从构建脚本中获取配置信息。方法之一是使用扩展对象(extension objects)。 Gradle的Project对象有个相关联的ExtensionContainer对象用来跟踪被传递给插件的设置和属性。 你可以通过告诉插件的extension container来捕获用户的输入。 为了捕获输入, 添加一个类似Java Bean的类去拓展容器列表. Groovy对于插件来说是个好语言,因为它默认包含getter和setter方法。我们来个project添加一个简单的拓展. 这里我们给project添加一个允许你配置的greeting拓展

/*********************自定义插件的扩展***********************************/
apply plugin: GreetingPlugin

greeting.message = 'Hi from Gradle'

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        // 添加 'greeting' 拓展对象
        project.extensions.create("greeting", GreetingPluginExtension)
        // 这个任务使用了拓展的配置
        project.task('hello') << {
            println project.greeting.message
        }
    }
}

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

In this example, GreetingPluginExtension is a plain old Groovy object with a field called message. The extension object is added to the plugin list with the name greeting. This object then becomes available as a project property with the same name as the extension object.

在一个单独的插件中我们时常会需要几个相关联的属性,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
}

在这个例子中,一些设置项被分组在greeting闭包中。构建脚本中闭包块的名字需要和拓展对象的名字匹配。当闭包被执行的时候,拓展对象的fields会被映射到闭包中的变量(Groovy闭包代理)

五、在自定义任务和插件中使用文件

当开发自定义插件和任务的时候,从本地文件灵活的接收输入参数是非常好的,你可以利用Project.file()方法尽可能晚的加载值文件。

/************************懒加载文件********************************/
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"

在这个例子中,我们配置了greet任务的destination属性作为闭包(懒得翻译) In this example, we configure the greet task destination property as a closure, which is evaluated with the Project.file() method to turn the return value of the closure into a file object at the last minute. You will notice that in the example above we specify the greetingFile property value after we have configured to use it for the task. This kind of lazy evaluation is a key benefit of accepting any value when setting a file property, then resolving that value when reading the property.

六.一个单独的项目

现在,让我们把插件移到一个单独的项目,然后我们才能发布它让其他人使用。这是一个Groovy项目,输出为一个包含插件类的Jar包。 下面是项目的构建脚本。它应用了Groovy插件并添加了Gradle的API作为编译时依赖。

/***************插件的build.gradle*********************/
apply plugin: 'groovy'

dependencies {
    compile gradleApi()
    compile localGroovy()
}

那么,Gradle怎么找到插件的实现呢?你需要在META-INF/gradle-plugins目录提供一个属性文件来匹配你插件的名字

/************************配置文件*********************************/
//目录:
src/main/resources/META-INF/gradle-plugins/greeting.properties
//内容
implementation-class=org.gradle.GreetingPlugin

注意:属性文件名必须和插件名一致,在资源文件中被替换,implementation-class属性区分插件实现类。

6.1在别的项目中使用你的插件

要在构建脚本中使用插件,你需要添加插件类去构建脚本的路径中。为此,你需要使用buildscript{}块,下面的代码演示了当已经大步Jar文件去本地仓库后的使用方法。

/*************************在其他项目中使用自定义插件***********************************/
buildscript {
    repositories {
        maven {
            url uri('../repo')
        }
    }
    dependencies {
        classpath group: 'org.gradle', name: 'customPlugin', version: '1.0-SNAPSHOT'
    }
}
apply plugin: 'greeting'

6.2为你的插件写测试类

你可以使用ProjectBuilder类去创建一个Project实例来测试你的插件实现。

/**************** 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)
    }
}

七.维护多个域对象

Gradle提供了一些工具类来维护集合对象,它能和好的和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
    }
}

Project.container()方法创建一个NamedDomainObjectContainer实例,它有很多好用的方法来管理配置对象。为了能用任何一种project.container方法创建一个类型,必须暴露一个名为“named”的独一无二的常量名给对象。(这段不好翻译!) The project.container(Class) variant of the container method creates new instances by attempting to invoke the constructor of the class that takes a single string argument, which is the desired name of the object. See the above link for project.container method variants that allow custom instantiation strategies.