阅读 333

自定义插件

Gradle插件打包可重用的构建逻辑片段,可用于许多不同的项目和构建。 Gradle允许实现自己的插件,因此可以重用构建逻辑,并与其他人共享。

可以使用任何语言实现Gradle插件,前提是实现最终编译为JVM字节码。 在我们的示例中,我们将使用Groovy作为实现语言。 Groovy,Java或Kotlin都是用于实现插件的语言的不错选择,因为Gradle API旨在与这些语言一起使用。 通常,使用Java或Kotlin实现的静态类型的插件将比使用Groovy实现的相同插件执行得更好。

有几个地方可以放置插件的源代码。

Build script

可以直接在构建脚本中包含插件的源代码。这样做的好处是插件可以自动编译并包含在构建脚本的类路径中,而无需执行任何操作。但是,插件在构建脚本之外是不可见的,因此您无法在其定义的构建脚本之外重用该插件。

buildSrc project

可以将插件的源代码放在rootProjectDir / buildSrc / src / main / groovy目录(或rootProjectDir / buildSrc / src / main / java或rootProjectDir / buildSrc / src / main / kotlin中。)Gradle将负责编译和测试插件并使其在构建脚本的类路径中可用。 该插件对于构建使用的每个构建脚本都是可见的。 但是,它在构建之外是不可见的,因此您无法在其定义的构建之外重用该插件。

Standalone project

可以为插件创建单独的项目。 该项目生成并发布一个JAR,然后您可以在多个版本中使用它并与其他人共享。 通常,此JAR可能包含一些插件,或将多个相关的任务类捆绑到单个库中。 或两者的某种组合。

基于 Build script 编写插件

要创建Gradle插件,需要编写一个实现Plugin接口的类。 当插件应用于项目时,Gradle会创建插件类的实例并调用实例的Plugin.apply()方法。 项目对象作为参数传递,插件可以使用它来配置项目。

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

// Apply the plugin
apply plugin: GreetingPlugin
复制代码
> gradle -q hello
Hello from the GreetingPlugin
复制代码

需要注意的一点是,为应用它的每个项目创建了一个新的插件实例。 另请注意,Plugin类是泛型类型。 此示例使其接收Project类型作为类型参数。 插件可以改为接收类型为Settings的参数,在这种情况下,插件可以应用于设置脚本或Gradle类型的参数,在这种情况下,插件可以应用于初始化脚本中。

使插件可配置

大多数插件需要从构建脚本中获取一些配置。执行此操作的一种方法是使用扩展对象。Gradle项目具有关联的ExtensionContainer对象,该对象包含已应用于项目的插件的所有设置和属性。可以通过向此容器添加扩展对象来为插件提供配置。扩展对象只是一个Java Bean兼容类。

##build.gradle
class GreetingPluginExtension {
    String message = 'Hello from GreetingPlugin'
}

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

apply plugin: GreetingPlugin

// Configure the extension
greeting.message = 'Hi from Gradle'
复制代码
> gradle -q hello
Hi from Gradle
复制代码

在此示例中,GreetingPluginExtension是一个普通的Groovy / Kotlin对象,其中包含一个名为message的属性。 扩展对象将添加到名称为greeting的插件列表中。 然后,此对象可用作与扩展对象同名的项目属性。

通常,您需要在单个插件上指定几个相关属性。 Gradle为每个扩展对象添加配置块,因此您可以将设置组合在一起。

##build.gradle
class GreetingPluginExtension {
    String message
    String greeter
}

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

apply plugin: GreetingPlugin

// Configure the extension using a DSL block
greeting {
    message = 'Hi'
    greeter = 'Gradle'
}
复制代码
> gradle -q hello
Hi from Gradle
复制代码

在此示例中,可以在greeting闭包内将多个设置组合在一起。 构建脚本(greeting)中闭包块的名称需要与扩展对象名称匹配。 然后,当执行闭包时,扩展对象上的字段将根据标准Groovy闭包委托功能映射到闭包内的变量。

使用自定义任务和插件中的文件

在开发自定义任务和插件时,最好在接受文件位置的输入配置时保持灵活性。 为此,您可以利用Project.file(java.lang.Object)方法尽可能晚地将值解析为文件。

## 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) {
    doLast {
        println file(greetingFile).text
    }
}

ext.greetingFile = "$buildDir/hello.txt"
复制代码
> gradle -q sayGreeting
Hello!
复制代码

在此示例中,我们将greet任务的destination属性配置为闭包/提供程序,使用Project.file(java.lang.Object)方法对其进行评估,以将闭包/提供程序的返回值转换为最后的File对象。在上面的示例中,我们在配置为将其用于任务后指定greetingFile属性值。 这种延迟评估是在设置文件属性时接受任何值,然后在读取属性时解析该值的关键优势。

将扩展属性映射到任务属性

通过扩展从构建脚本捕获用户输入并将其映射到自定义任务的输入/输出属性是一种有用的模式。 构建脚本作者仅与扩展名定义的DSL进行交互。 命令式逻辑隐藏在插件实现中。

Gradle提供了一些可以在任务实现和扩展中使用的类型来帮助您完成此任务。

一个独立的项目

现在我们将把我们的插件移到一个独立的项目中,这样我们就可以发布它并与其他人共享。 这个项目只是一个Groovy项目,它生成一个包含插件类的JAR。 这是项目的简单构建脚本。 它应用Groovy插件,并将Gradle API添加为编译时依赖项。

##build.gradle
plugins {
    id 'groovy'
}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
}
复制代码

那么Gradle如何找到插件实现呢? 答案是你需要在jar的META-INF / gradle-plugins目录中提供一个与你的插件的id匹配的属性文件。

示例:为自定义插件接线

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

implementation-class=org.gradle.GreetingPlugin
复制代码

请注意,属性文件名与插件ID匹配,并且放在resources文件夹中,而implementation-class属性标识插件实现类。

创建插件ID

插件ID以类似于Java包的方式(即反向域名)完全限定。 这有助于避免冲突,并提供了一种对具有类似所有权的插件进行分组的方法。

插件ID应该是反映命名空间(指向您或您的组织的合理指针)的组件的组合,以及它提供的插件的名称。例如,如果您有一个名为“foo”的Github帐户,并且您的插件名为“bar”,则合适的插件ID可能是com.github.foo.bar。

发布插件到本地

## build.gradle
classpath "com.github.dcendents:android-maven-gradle-plugin:1.5"
复制代码
## java-publish.gradle
apply plugin: 'maven-publish'

def javadocJar = task("javadocJar", type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}
def sourcesJar = task("sourcesJar", type: Jar) {
    classifier = 'sources'
    from sourceSets.main.java.srcDirs
}

publishing {
    publications {
        Component(MavenPublication) {
            from components.java
            groupId = group
            artifactId = POM_ARTIFACT_ID
            version = version

            artifact sourcesJar
            artifact javadocJar
        }
    }
}

task buildAndPublishToLocalMaven(type: Copy, dependsOn: ['build', 'publishToMavenLocal']) {
    group = POM_NAME

    // save artifacts files to artifacts folder
    from configurations.archives.allArtifacts.files
    into "${rootProject.buildDir}/outputs/artifacts/"
    rename { String fileName ->
        fileName.replace("release.aar", "${version}.aar")
    }

    doLast {
        println "* published to maven local: ${project.group}:${project.name}:${project.version}"
    }
}
复制代码
## gradle.properties
POM_ARTIFACT_ID=greeting-gradle-plugin
POM_NAME=greeting
POM_PACKAGING=jar
复制代码

点击之后会在本地仓库生成对应的jar包:

在另一个项目中使用插件

找到项目的根目录的build.gradle文件:

buildscript {
    repositories {
        mavenLocal()

        google()
        jcenter()
    }

    dependencies {
        // classpath
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath("com.zpw.greeting:greeting-gradle-plugin:1.0")
    }
}
复制代码

然后在项目app的build.gradle文件中添加:

apply plugin: 'com.zpw.greeting_plugin'
复制代码

为插件提供配置DSL

可以使用扩展对象为插件提供配置。 使用扩展对象还扩展了Gradle DSL,为插件添加项目属性和DSL块。 扩展对象只是一个常规对象,因此您可以通过向扩展对象添加属性和方法来提供嵌套在此块中的DSL元素。

嵌套的DSL元素

当Gradle创建任务或扩展对象时,Gradle会修改实现类以混合DSL支持和可扩展性。 要创建嵌套的DSL元素,可以使用ObjectFactory类型创建类似装饰的对象。 然后,可以通过插件扩展的属性和方法使这些装饰对象对DSL可见:

class GreetingPluginExtension {
    String message
}
复制代码
class GreetingPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        def extension = project.extensions.create('greeting', GreetingPluginExtension)

        project.task('hello') {
            doLast {
                println "${extension.message}"
            }
        }
    }
}
复制代码
apply plugin: 'com.zpw.greeting_plugin'
greeting {
    message = 'Hi'
}
复制代码
> gradle -q hello
Hi
复制代码
关注下面的标签,发现更多相似文章
评论