Gradle构建之自定义Gradle插件

前言

Gradle是一个专注于构建自动化和支持多语言开发的构建工具。使用Gradle构建工具,我们可以方便又快速地实现自己的构建目标。Gradle所有有用的特性都是由插件提供的。一个Gradle插件打包了可重用的构建逻辑,可以在不同的项目和构建中使用。Gradle允许你实现自己的自定义插件,这样你就可以重用你的构建逻辑,并与其他人分享。

基础知识

Groovy语言基础

你可以使用任何你喜欢的语言来实现一个自定义插件,只要这个实现以字节码的形式编译即可。对于本文,我们将使用Groovy作为实现语言。如果你愿意,可以使用Java或者Scala。一般来说,使用Groovy是最简单的选择,因为Gradle API被设计用来与Groovy一起很好的协作。具体Groovy语言的基础知识可以阅读 Gradle构建之Groovy语言基础 这篇文章。

Gradle构建基础

在学习自定义Gradle插件之前,你需要了解一些Gradle构建的基础知识,比如:下载安装、执行任务、编写构建脚本等等。具体Gradle构建的基础知识可以阅读 Gradle构建之Gradle构建基础 这篇文章。

自定义Gradle插件

要自定义一个Gradle插件,你需要写一个实现了 Plugin 接口的类。当你在构建脚本中应用该插件时,Gradle会实例化这个插件类,并调用插件类实例的 Plugin.apply(T) 方法。项目对象将作为参数被传递进去,插件类实例将使用它来配置项目。

自定义Gradle任务

Gradle支持两种类型的任务。一种是 简单任务。你可以用一个配置闭包来定义简单任务,配置闭包决定了任务的行为。这种任务适用于在构建脚本中实现一次性的任务。另一种是 增强任务。这种任务将行为定义在任务中,并提供一些属性。你可以使用这些属性来配置任务的行为。增强任务可以让你在不同的地方重用一个行为。

通常,Gradle插件使用增强任务。要自定义一个增强任务,你需要写一个继承自 DefaultTask 的类。在这个类中定义任务的行为和属性。任务的行为是通过在任务类中添加一个方法并标记 TaskAction 注解来定义的。当任务执行时,Gradle将调用该方法。

自定义插件的方式

自定义Gradle插件的方式有三种:

  • 构建脚本的方式 我们可以在构建脚本中直接自定义插件。这种方式的好处是,插件可以自动编译并包含在构建脚本的类路径中,而无需做任何事情。但是,这种插件在构建脚本之外是不可见的,所以你不能在定义它的构建脚本之外重用插件。
  • buildSrc的方式 我们可以在项目根目录下的buildSrc目录下(具体位置是:rootProjectDir/buildSrc/src/main/groovy/)自定义插件。Gradle将负责编译插件,并使其在构建脚本的类路径中可用。这种插件对于该构建下的每个构建脚本都是可见的。但是,它在构建之外是不可见的,所以你不能在定义它的构建之外重用插件。
  • 独立项目的方式 我们可以创建一个独立的项目来自定义插件。这个项目可以生成并发布一个 JAR 包,你可以在多个构建中使用它,也可以与他人分享。通常,这个JAR包可能包括一些定制的插件,或者将几个相关的任务类捆绑到一个单独的库中,或者两者皆有。

自定义Gradle插件的三种方式各有优缺点。本文将主要介绍采用独立项目来自定义Gradle插件。

Hello World

这里采用构建脚本的方式来自定义Hello World插件。新建一个build.gradle文件,输入如下代码:

apply plugin: HelloWorldPlugin

class HelloWorldPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.task('hello') {
            doLast {
                println 'Hello World!'
            }
        }
    }
}

在当前目录下,输入”gradle hello”命令执行构建。如下所示:

$ gradle hello
:hello
Hello World!

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

可以看到,输出了”Hello World!”字符串,成功地自定义了插件。

自定义插件

接下来我们采用独立项目来自定义一个简单的Gradle插件。项目源码地址:https://github.com/chongyucaiyan/GradlePluginDemo

新建一个项目

新建一个IntelliJ IDEA项目。采用Gradle来构建项目。如下图所示:

点击Next,配置插件信息。如下图所示:

点击Next,配置项目。如下图所示:

点击Next,配置项目名和模块名。如下图所示:

点击Finish。稍等片刻,我们就可以看到IntelliJ IDEA生成的项目。如下图所示:

生成Gradle Wrapper

Gradle Wrapper是你应该添加到项目中的内容。通过Wrapper,任何人无需预先安装Gradle就可以构建你的项目。同时,这样确保了构建用户使用的Gradle版本与项目发布的Gradle版本一致。

在项目的根目录下,输入 “gradle wrapper” 命令执行wrapper任务。如下所示:

$ gradle wrapper
:wrapper

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

执行wrapper任务之后,你可以在项目的根目录下找到以下文件:

  • gradlew (Unix Shell script)
  • gradlew.bat (Windows batch file)
  • gradle/wrapper/gradle-wrapper.jar (Wrapper JAR)
  • gradle/wrapper/gradle-wrapper.properties (Wrapper properties)

生成Gradle Wrapper之后,我们就可以直接使用 gradlew 命令来构建项目了。gradlew命令与gradle命令的使用方法是一致的。

修改构建脚本

修改greetingplugin目录下的build.gradle构建脚本,主要是应用 groovy 插件,并添加 Gradle API 作为编译时依赖。如下所示:

apply plugin: 'groovy'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile gradleApi()
    compile localGroovy()
}

group 'com.github.cyc'
version '0.0.1'

编写任务

greetingplugin/src/main/groovy/ 目录下,逐级创建com.github.cyc包路径。在cyc包下,新建一个名为GreetingTask的Groovy类。输入如下代码:

class GreetingTask extends DefaultTask {
    String greeting = 'hello from GreetingTask'

    @TaskAction
    def greet() {
        println greeting
    }
}

这里定义了一个名为GreetingTask的任务,并定义了任务的行为和一个名为greeting的属性。

编写插件

在cyc包下,新建一个名为GreetingPlugin的Groovy类。输入如下代码:

class GreetingPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        // 创建 greetingExtension 扩展对象
        project.extensions.create('greetingExtension', GreetingPluginExtension)
        // 创建 hello 任务
        project.task('hello', type: GreetingTask) {
            greeting = 'hello from GreetingPlugin'
            doLast {
                println "${project.greetingExtension.message} from ${project.greetingExtension.greeter}"
            }
        }
    }
}

class GreetingPluginExtension {
    String message
    String greeter
}

这里创建了一个名为greetingExtension的扩展对象和一个类型为GreetingTask名为hello的任务。

通常,Gradle插件需要从构建脚本中获得一些配置。有一种方法是使用 扩展对象。Gradle的Project对象有一个关联的ExtensionContainer对象,它可以跟踪传递给插件的所有设置和属性。为了捕获输入,只需要将一个符合Java Bean规则的类添加到ExtensionContainer对象的扩展列表中。

创建属性文件

当我们在构建脚本中应用一个插件时,Gradle将在插件JAR包的 META-INF/gradle-plugins/ 目录下寻找一个文件名与插件ID相匹配的属性文件来获取具体的插件实现。

greetingplugin/src/main/resources/META-INF/gradle-plugins/ 目录下,新建一个com.github.cyc.greeting.properties属性文件。输入如下内容:

implementation-class=com.github.cyc.GreetingPlugin

注意:属性文件名要与插件ID相匹配,implementation-class属性要指定具体的插件实现类。

通常,插件ID的命名方式与Java包名的命名方式类似。这有助于避免冲突,并提供了一种方法来对具有类似所有权的插件进行分组。插件ID应该是名称空间和插件名称的组合,例如:com.github.cyc.greeting。

发布插件

我们可以把插件发布到本地供自己使用,也可以把插件发布到远程仓库供任何人使用。

发布到本地

修改greetingplugin目录下的build.gradle构建脚本,主要是应用 maven 插件,并配置发布插件的 uploadArchives 任务。如下所示:

apply plugin: 'groovy'
apply plugin: 'maven'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile gradleApi()
    compile localGroovy()
}

group 'com.github.cyc'
version '0.0.1'

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri('../repo'))
        }
    }
}

在项目的根目录下,输入 “./gradlew uploadArchives” 命令发布插件。如下所示:

$ ./gradlew uploadArchives
:greetingplugin:compileJava NO-SOURCE
:greetingplugin:compileGroovy
:greetingplugin:processResources
:greetingplugin:classes
:greetingplugin:jar
:greetingplugin:uploadArchivesCould not find metadata com.github.cyc:greetingplugin/maven-metadata.xml in remote (file:/E:/Gradle/Learning/GradlePluginDemo/repo)


BUILD SUCCESSFUL in 5s
4 actionable tasks: 4 executed

执行uploadArchives任务之后,你可以在项目根目录下的repo目录下找到发布的插件。

发布到远程仓库

常见的Maven远程仓库有 Maven CentraljCenter。发布开源库到这两个远程仓库的过程比较繁琐冗长。JitPack 可以简单快速地发布开源库,所以我们将发布插件到JitPack上。

首先,使用Gradle构建的项目需要应用 maven 或者 maven-publishing 插件。这里我们使用maven插件,那么需要在构建脚本中添加如下代码:

apply plugin: 'maven'

group = 'com.github.YourUsername'

在本文的前一小节 发布到本地 中,我们的构建脚本已经包含了上面的代码,所以我们不需要修改构建脚本。虽然group属性的值不是用的Github上的用户名,但是不影响发布。JitPack在发布插件时还是会使用Github上的用户名。刚好我们可以以此来区分发布到本地和远程仓库的插件。

然后,将项目代码上传到 Github 上。具体如何上传代码到Github上,这里就不介绍了。注意:最终JitPack发布的插件的ArtifactId是Github的仓库名。

接着,给上传到Github上的项目打一个 Tag 或者创建一个 Release。具体如何给Github上的项目打一个Tag或者创建一个Release,这里就不介绍了。

最后,复制Github上的项目仓库地址到 JitPack官网。如下图所示:

点击 Look up 按钮。稍等片刻,JitPack就为你生成了插件库。如下图所示:

点击 Get it 按钮。JitPack将为你生成应用插件的代码。如下图所示:

发布成功之后,我们就可以在构建脚本中应用该插件了。

使用插件

要在构建脚本中使用非Gradle内置的插件,你需要将插件类添加到构建脚本的类路径中。要做到这一点,你需要使用一个 buildscript {} 块。在buildscript {} 块中,使用 repositories 指定插件的仓库地址,使用 dependencies 指定插件的类路径。

使用本地插件

在项目根目录下,新建一个demolocal目录。在demolocal目录下,新建一个build.gradle文件。输入如下代码:

buildscript {
    repositories {
        maven {
            url uri('../repo')
        }
    }
    dependencies {
        classpath 'com.github.cyc:greetingplugin:0.0.1'
    }
}

apply plugin: 'com.github.cyc.greeting'

// 配置扩展对象
greetingExtension {
    message = 'Hi'
    greeter = 'demolocal'
}

构建脚本应用了我们自定义的插件,并使用配置闭包配置了插件的扩展对象。

在demolocal目录下,输入”../gradlew hello”命令执行构建。如下所示:

$ ../gradlew hello
:hello
hello from GreetingPlugin
Hi from demolocal

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

可以看到,我们成功地应用了本地插件。

使用远程仓库插件

在项目根目录下,新建一个demoremote目录。在demoremote目录下,新建一个build.gradle文件。输入如下代码:

buildscript {
    repositories {
        maven {
            url 'https://jitpack.io'
        }
    }
    dependencies {
        classpath 'com.github.chongyucaiyan:GradlePluginDemo:0.0.1'
    }
}

apply plugin: 'com.github.cyc.greeting'

// 配置扩展对象
greetingExtension {
    message = 'Hi'
    greeter = 'demoremote'
}

构建脚本应用了我们自定义的插件,并使用配置闭包配置了插件的扩展对象。

在demoremote目录下,输入”../gradlew hello”命令执行构建。如下所示:

$ ../gradlew hello
:hello
hello from GreetingPlugin
Hi from demoremote

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

可以看到,我们成功地应用了远程仓库插件。

总结

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

参考

  • https://gradle.org/
  • https://docs.gradle.org/4.1/userguide/userguide.html
  • https://docs.gradle.org/4.1/userguide/custom_tasks.html
  • https://docs.gradle.org/4.1/userguide/custom_plugins.html
  • https://jitpack.io/
  • https://jitpack.io/docs/#publishing-on-jitpack
  • https://jitpack.io/docs/BUILDING/#gradle-projects

你可能感兴趣的:(Gradle构建)