Android开发

开发效率优化之自动化构建系统Gradle(二)下篇

2019-11-13  本文已影响0人  Alvin老师

阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
本篇文章将继续从自定义 Gradle 插件开发来介绍自动化构建系统Gradle:

Gradle 插件简介

Gradle 插件是一个能够将 Gradle 的构建逻辑(build logic)和构建任务(build task)打包到一起,以便在多个项目的构建脚本(build.gradle)中应用(apply)的工具。

例如,build.gradle 构建脚本文件内 apply plugin: 'java'apply plugin: 'com.android.application' 中的 javacom.android.application 就是官方提供的 Gradle 插件,通过应用这些插件,可以丰富项目的构建任务与构建逻辑。

除官方提供的插件外,Gradle 还允许开发者定义自己的 Gradle 插件。开发者可以根据实际需求定义自己的构建逻辑和构建任务,将其打包为 Gradle 插件,从而在多个项目的构建脚本中复用。此外,还可以将自定义的 Gradle 插件发布到 plugin portal或其他仓库中,更为方便的分享给他人使用。

Gradle 插件对编程语言没有太多限制,只要是能够被编译为 JVM 字节码的编程语言,都能用来编写 Gradle 插件。Gradle-API 的被设计为对 Groovy、Java、Koltin 友好的,通常情况下,使用 Java 或 Kotlin 这类静态类型语言实现的 Gradle 插件的性能要比使用 Groovy 实现的相同常见的性能更好。

开始之前

Gradle 作为一个普通的构建工具,本身并不依赖任何可视化 GUI 工具。为简化步骤,本文将采用命令行方式来完成自定义 Gradle 插件的演示。在开始之前,需先将 gradle 命令添加到系统环境变量中。

若读者在 IDEA / Android Studio 使用过 gradle ,则可在当前用户目录下找到 gradle 命令,具体路径如下

若读者的电脑中尚未安装 gradle,则可在 Gradle 官方 下载安装。

以笔者使用的环境为例,gradle 命令所在目录为

~/.gradle/wrapper/dists/gradle-5.4.1-all/3221gyojl5jsh0helicew7rwx/gradle-5.4.1/bin

将 gradle 命令所在目录添加到环境变量中

export PATH=~/.gradle/wrapper/dists/gradle-5.4.1-all/3221gyojl5jsh0helicew7rwx/gradle-5.4.1/bin:$PATH
C:\Users\用户名\.gradle\wrapper\dists\gradle-5.4.1-all\805usxkvhgx6e1wbo8o64g0tx\gradle-5.6.1\bin

牛刀小试

在命令行中输入

gradle --version

得到如下输出则表示 gradle 环境设置成功

------------------------------------------------------------
Gradle 5.4.1
------------------------------------------------------------

Build time:   2019-04-26 08:14:42 UTC
Revision:     261d171646b36a6a28d5a19a69676cd098a4c19d

Kotlin:       1.3.21
Groovy:       2.5.4
Ant:          Apache Ant(TM) version 1.9.13 compiled on July 10 2018
JVM:          1.8.0_171 (Oracle Corporation 25.171-b11)
OS:           Mac OS X 10.14.6 x86_64

自定义 Gradle 插件

自定义 gradle 插件可以在以下三个地方创建,分别是:

  1. 构建脚本内
  2. buildSrc 模块内
  3. 单独项目

构建脚本内建方式

build.gradle 内直接创建 Gradle 插件

优点:

缺点:

示例

1. 创建 BuildInDemo 目录

mkdir BuildInDemo

2. 在 BuildInDemo 目录内中新建 build.gradle 文件

cd BuildInDemo
touch build.gradle

使用 tree 命令查看创建后的目录结构,如下所示

BuildInDemo
.
└── build.gradle

0 directories, 1 file

3. 在 BuildInDemo/build.gradle 内创建并应用 Gradle 插件,代码如下

// 1. 创建插件 BuildInPlugin
class BuildInPlugin implements Plugin<Project> {
    // 2. 应用插件时执行此函数
    @Override void apply(Project target) {
        println("hello form build-in plugin")
    }
}
//3. 应用插件
apply plugin: BuildInPlugin
 // 2\. 应用插件时执行此函数
 @Override void apply(Project target) {
 println("hello form build-in plugin")
 }
}
//3\. 应用插件
apply plugin: BuildInPlugin

4. 构建此 build.gradle 文件

gradle build

Gradle 构建时将执行 build.gradle 中的代码,当执行到 apply plugin: BuildInPlugin 时,将会调用 BuildInPlugin 的实例方法 apply(Project p)。因此在构建过程中,可以看到如下输出,其中第 2 行即为上一步自定义插件中打印的内容,表明插件应用成功

> Configure project :
hello form build-in plugin

> Task :buildEnvironment

------------------------------------------------------------
Root project
------------------------------------------------------------

classpath
No dependencies

A web-based, searchable dependency report is available by adding the --scan option.

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

buildSrc 模块方式

rootProject/buildSrc 文件夹是 Gradle 的预留目录,用来存放当前项目私有 Gradle 插件的源代码与构建脚本

优点:

缺点:

示例

1. 创建 PluginBuildSrcDemo 项目模块

创建 PluginBuildSrcDemo 目录,并在该目录下创建 build.gradle 文件

mkdir PluginBuildSrcDemo && cd PluginBuildSrcDemo && touch build.gradle

2. 创建 buildSrc 子模块

2.1 在 PluginBuildSrcDemo 目录下创建 buildSrc 目录
mkdir buildSrc
2.2 在 PluginBuildSrcDemo/buildSrc 目录下创建 buildSrc 子模块的构建脚本文件 build.gradle
cd buildSrc
touch build.gradle

PluginBuildSrcDemo/buildSrc/build.gradle 的内容如下

// 使用 plugins 块语法应用插件
plugins {
  // 应用 kotlin 插件
  id 'org.jetbrains.kotlin.jvm' version '1.3.50'
}
dependencies {
  // 仅在编译时使用 Grdale-API 依赖
  compileOnly gradleApi()
  // 在插件源码中添加 kotlin 标准库依赖
  implementation 'org.jetbrains.kotlin:kotlin-stdlib'
}
2.3 创建 buildSrc 模块的源码目录
cd PluginBuildSrcDemo/buildSrc
mkdir -p /src/main/kotlin/com/example/plugin
2.4 创建插件文件 PluginBuildSrc.kt
cd PluginBuildSrcDemo/buildSrc/src/main/kotlin/com/example/plugin
touch PluginBuildSrc.kt

PluginBuildSrc.kt 的代码如下

package com.example.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project

class PluginBuildSrc : Plugin<Project> {
  override fun apply(target: Project) {
    println("hello from buildSrc plugin")
  }
}
2.5 声明 Gradle 插件的 ID 与实现类

此步骤是可选的:若使用插件 ID 形式应用自定义插件,则必须进行此步骤;若使用插件实现类的形式应用自定义插件,则可跳过此步骤。

完成此步骤的方式有两种,任选其一即可

方式 1. META-INF 方式

创建 PluginBuildSrcDemo/buildSrc/src/main/resources/META-INF/gradle-plugins 目录

cd PluginBuildSrcDemo/buildSrc
mkdir -p src/main/resources/META-INF/gradle-plugins

gradle-plugins 目录下创建 com.example.plugin.properties 属性文件,红色部分表示插件的 ID

cd src/main/resources/META-INF/gradle-plugins
touch com.example.plugin.properties

属性文件的内容如下,表示插件 ID 为 com.example.plugin 的插件所对应的实现类为 com.example.plugin.PluginBuildSrc

implementations-class=com.example.plugin.PluginBuildSrc
方式 2. java-gradle-plugin 插件方式

java-gradle-plugin 是一个用于开发 Gradle 插件的辅助插件,它内置了很多辅助功能:

  1. 为宿主模块添加 gradlePlugin 配置块,可在此处配置插件的 ID 和实现类
  2. 为宿主模块添加 complile gradleApi() 依赖
  3. etc…

此处我们主要使用 gradlePlugin 配置块代替 META-INF 目录下的属性文件。java-gradle-plugin 的使用方式非常简单,只需在 PluginBuildSrcDemo/buildSrc/build.gradle 构建脚本文件中简单配置即可,如下所示。

 plugins {
      id 'org.jetbrains.kotlin.jvm' version '1.3.50'
+ //1. 应用 java-gradle-plugin 插件
+     id 'java-gradle-plugin' 
  }
     
  dependencies {
-     compileOnly gradleApi() // java-gradle-plugin 插件已为宿主添加 gradleApi 依赖,此行可移除 
      implementation 'org.jetbrains.kotlin:kotlin-stdlib'
  }
+ //2. 添加 gradlePlugin 配置块信息
+ gradlePlugin {
+     plugins{
+         // 此处的 tag 可以为任意名称
+         tag1{
+             id = 'com.example.plugin.buildsrc' //自定义插件的 ID
+             implementationClass  = 'com.example.plugin.PluginBuildSrc' //自定义插件的实现类
+         }
+     }
+ }

此时在 PluginBuildSrcDemo 目录下使用 tree 命令,可以看到当前的目录结构如下

PluginBuildSrcDemo
.
├── build.gradle
├── buildSrc
│   ├── build.gradle
│   └── src
│       └── main
│           └── kotlin
│               └── com
│                   └── example
│                       └── plugin
│                           └── PluginBuildSrc.kt
7 directories, 3 files

3. 在 PruginBuildSrcDemo 项目模块中应用 buildSrc 中声明的 Gradle 插件

PluginBuildSrcDemo/build.gradle 构建脚本文件中添加如下代码

//apply plugin: '插件 ID' 
apply plugin: 'com.example.plugin'
//apply plugin: 实现类
//apply plugin: com.example.plugin.PluginBuildSrc

应用插件时,指定插件 ID 或指定插件的实现类都可以,但指定插件 ID 的形式更为简短及灵活,在实际开发中也更为常见。

PluginBuildSrcDemo 目录下执行 gradle build 命令进行构建,可看到如下输出

> Configure project :
hello from buildSrc plugin

> Task :buildEnvironment
------------------------------------------------------------
Root project
------------------------------------------------------------
classpath
No dependencies

A web-based, searchable dependency report is available by adding the --scan option.

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

其中第 2 行是我们在 buildSrc 模块中定义的 Gradle 插件所打印日志,表明 buildSrc 模块 中的自定义插件在根项目中已生效。

4. 创建 SubModule 子模块

buildSrc 模块中定义的插件可以在同一项目的任意模块的构建脚本中使用,接下来便演示在 SubModule 子模块中的应用。

4.1 创建 SubModule 目录与构建脚本
// 1. 在 PluginBuildSrcDemo 目录下创建 SubModule 目录
cd PluginBuildSrcDemo && mkdir SubModule
// 2. 在 SubModule 目录下新建 gradle 构建脚本 
cd SubModule
touch build.gralde

PluginBuildSrcDemo/SubModule/build.gradle 的内容如下

apply plugin: 'com.example.plugin'
4.2 将 SubModule 模块关联到 PluginBuildSrcDemo 项目中

PluginBuildSrcDemo 目录下新建 settings.gradle 文件,内容如下

// 将SubModule 子模块添加到 PluginBuildSrcDemo 项目模块中
include ':SubModule'

此时在 PluginBuildSrcDemo 目录下使用 tree 命令,可以看到项目当前的目录结构如下

PluginBuildSrcDemo
.
├── SubModule
│   └── build.gradle
├── build.gradle
├── buildSrc
│   ├── build.gradle
│   └── src
│       └── main
│           └── kotlin
│               └── com
│                   └── example
│                       └── plugin
│                           └── PluginBuildSrc.kt
└── settings.gradle

5. 在 SubModule 模块执行构建命令

cd PluginBuildSrcDemo/SubModule
gradle build

得到如下输出

> Configure project :SubModule
hello from buildSrc plugin

> Task :SubModule:buildEnvironment

------------------------------------------------------------
Project :SubModule
------------------------------------------------------------

classpath
No dependencies

A web-based, searchable dependency report is available by adding the --scan option.

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

独立项目方式

采用 buildSrc 模块方式时,Gradle 会妥善处理 buildSrc 模块的构建脚本与源码,并将其添加到当前项目的 classpath 中。但 buildSrc 方式的插件只能在项目内共享与复用,若要在其他项目中使用该插件,还需要再进行下列操作

因为是在其他项目中使用该项目 buildSrc 模块 中的自定义 Gradle 插件,所以 Gradle 的 buildSrc 保留目录优势不再。如果将模块名由 buildSrc 修改为其他名称,则可将其称为独立的 Gradle 插件模块。

在本小节中,我们主要学习 gradle 插件的发布与使用。

1. 创建 gradle 插件项目

buildSrc 方式相同,先创建构建脚本与自定义插件类

mkdir StandaloneDemo 
cd StandaloneDemo
touch build.gradle
mkdir src/main/kotlin/com/example/plugin
touch src/main/kotlin/com/example/plugin/StandalonePlugin.kt

StandaloneDemo/build.gradle 的内容如下

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.3.50'
    id 'java-gradle-plugin'
}

gradlePlugin{
    plugins{
        sometag{
            id = 'com.example.plugin'
            implementationClass = 'com.example.plugin.StandalonePlugin'
        }
    }
}

StandaloneDemo/src/main/kotlin/com/example/plugin/StandalonePlugin.kt 的内容如下

package com.example.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project

class StandalonePlugin : Plugin<Project> {
    override fun apply(target: Project) {
        println("hello from standalone plugin")
    }
}

2. 将 gradle 插件发布到 maven 仓库

2.1 在 StandaloneDemo/build.gradle 文件中配置发布信息

使用 maven-publish 插件将自定义插件发布到本地 maven 仓库中,如下所示

plugins {
     id 'org.jetbrains.kotlin.jvm' version '1.3.50'
     id 'java-gradle-plugin'
+    //1. 应用 maven-publish 插件
+    id 'maven-publish'
 }
 
 gradlePlugin{
     plugins{
         sometag{
             id = 'com.example.plugin'
             implementationClass = 'com.example.plugin.StandalonePlugin'
         }
     }
 }
+//2. 设置发布相关配置
+publishing {
+    publications {
+        //3. 将插件发布到 maven 仓库
+        maven(MavenPublication) {
+                       //4. 设置插件的 maven 坐标
+            groupId 'com.example'//组织 ID
+            artifactId 'plugin'  //制品 ID
+            version 'snapshot'   //制品版本
+            from components.kotlin
+        }
+    }
+   //5. 设置发布仓库
+   repositories {
+       // 6. 发布到本地 maven 仓库 
+       mavenLocal()
+   }
+}
2.2 使用 publishMavenPublicationToMavenLocal 任务将插件发布到本地仓库
cd StandaloneDemo
gradle publishMavenPublicationToMavenLocal

执行以上命令后,gradle 会把 StandaloneDemo 中的代码编译打包后发布到本地 maven 仓库中,本地 maven 仓库通常存放于当前用户目录下,具体路径为

我们可以在此目录下看到刚刚发布的插件,如下图所示

3. 在其他项目中使用本地 maven 仓库中的 gradle 插件

3.1 新建 OtherProject 项目
mkdir OtherProject
cd OtherProject
touch build.gradle

build.gradle 的文件内容如下,相比 buildSrc 方式,应用时多了 11 行的 buildscript 相关的配置信息。

+buildscript {
+  repositories {
+    //1. 为当前构建脚本添加插件所在的 maven 仓库,本例中为 maven 本地仓库
+    mavenLocal()
+  }
+  dependencies {
+    //2. 为当前构建脚本添加如下依赖
+    //`com.exaple`、`plugin`、`snapshot` 是在上一步中设置的 maven 三维坐标
+    classpath 'com.example:plugin:snapshot'
+  }
+}
 //3. 应用独立插件项目中的自定义插件
 // apply plugin: '插件 ID'
 apply plugin: 'com.example.plugin'
3.2 构建 OtherProject 项目
cd OtherProject
gradle build

输入以上命令,得到如下输出

> Configure project :
hello from standalone plugin

> Task :buildEnvironment

------------------------------------------------------------
Root project
------------------------------------------------------------

classpath
\--- com.example:plugin:snapshot

A web-based, searchable dependency report is available by adding the --scan option.

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

小结

本文演示了 gradle 插件的三种创建方式,相关代码已上传至 github 中,点此即可查看。行文匆忙难免疏忽,如有遗漏还请斧正。

上一篇下一篇

猜你喜欢

热点阅读