開發效率優化之自動化構建系統Gradle(二)下篇

阿里P7移動互聯網架構師進階視頻(每日更新中)免費學習請點擊:https://space.bilibili.com/474380680
本篇文章將繼續從自定義 Gradle 插件開發來介紹自動化構建系統Gradle:java

Gradle 插件簡介

Gradle 插件是一個可以將 Gradle 的構建邏輯(build logic)和構建任務(build task)打包到一塊兒,以便在多個項目的構建腳本(build.gradle)中應用(apply)的工具。android

例如,build.gradle 構建腳本文件內 apply plugin: 'java'apply plugin: 'com.android.application' 中的 javacom.android.application 就是官方提供的 Gradle 插件,經過應用這些插件,能夠豐富項目的構建任務與構建邏輯。git

除官方提供的插件外,Gradle 還容許開發者定義本身的 Gradle 插件。開發者能夠根據實際需求定義本身的構建邏輯和構建任務,將其打包爲 Gradle 插件,從而在多個項目的構建腳本中複用。此外,還能夠將自定義的 Gradle 插件發佈到 plugin portal或其餘倉庫中,更爲方便的分享給他人使用。github

Gradle 插件對編程語言沒有太多限制,只要是可以被編譯爲 JVM 字節碼的編程語言,都能用來編寫 Gradle 插件。Gradle-API 的被設計爲對 Groovy、Java、Koltin 友好的,一般狀況下,使用 Java 或 Kotlin 這類靜態類型語言實現的 Gradle 插件的性能要比使用 Groovy 實現的相同常見的性能更好。web

開始以前

Gradle 做爲一個普通的構建工具,自己並不依賴任何可視化 GUI 工具。爲簡化步驟,本文將採用命令行方式來完成自定義 Gradle 插件的演示。在開始以前,需先將 gradle 命令添加到系統環境變量中。編程

若讀者在 IDEA / Android Studio 使用過 gradle ,則可在當前用戶目錄下找到 gradle 命令,具體路徑以下api

  • Mac:/Users/當前用戶名/.gradle/wrapper/dists/gradle版本/沙盒路徑/gradle版本/bin
  • Win:C:\Users\當前用戶名\.gradle\wrapper\dists\gradle版本\沙盒路徑\gradle版本\bin

若讀者的電腦中還沒有安裝 gradle,則可在 Gradle 官方 下載安裝。bash

以筆者使用的環境爲例,gradle 命令所在目錄爲架構

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

將 gradle 命令所在目錄添加到環境變量中app

  • Mac:在 ~/.bashrc 中添加
export PATH=~/.gradle/wrapper/dists/gradle-5.4.1-all/3221gyojl5jsh0helicew7rwx/gradle-5.4.1/bin:$PATH
  • Win:在系統環境變量中添加
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 插件

優勢:

  • build.gradle 中建立的插件將<u>被自動編譯幷包含在 classpath 中</u>,使用時無需在構建腳本內指定 classpath

缺點:

  • 此插件僅在當前構建腳本中有效,對外部文件不可見,沒法在當前構建腳本之外的其餘地方複用此插件

示例

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 插件的源代碼與構建腳本

優勢:

  • 項目構建時,Gradle 會自動編譯項目目錄下的 buildSrc 文件夾下的構建腳本和源碼,並將其添加到項目構建腳本的 classpath 中,所以在使用 buildSrc 中建立的插件時,無需再手動指定 classpath
  • buildSrc 文件夾中構建腳本和 Gradle 插件同一項目都可見,所以同一項目中的其餘模塊也可使用 buildSrc 中建立的插件

缺點:

  • 此處建立的插件對外部項目不可見,沒法在其餘項目中複用

示例

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 方式的插件只能在項目內共享與複用,若要在其餘項目中使用該插件,還須要再進行下列操做

  • 將插件發佈到 maven 倉庫(任意倉庫)
  • 在須要應用該插件的構建腳本中的 repository 部分添加該插件所在的 maven 倉庫
  • 在須要應用該插件的構建腳本中的 classpath 部分添加該插件對應的 maven 座標 (group : id : version)

由於是在其餘項目中使用該項目 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 倉庫一般存放於當前用戶目錄下,具體路徑爲

  • Mac: ~/.m2/repository
  • Win:C:\Users\當前用戶名\.m2\repository

咱們能夠在此目錄下看到剛剛發佈的插件,以下圖所示

 

 
19956127-e64026bf96590e3c.png
 

 

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 中,點此便可查看。行文匆忙不免疏忽,若有遺漏還請斧正。

相關文章
相關標籤/搜索