gradle入門

構建系統介紹

之前只會用 make,第一次接觸 gradle 時,明顯感受和 make 的套路很不同。若是說 make 是通用構建系統,那 gradle 構建腳本第一眼看上去甚至感受跟構建毫無關聯。html

本文章解決的疑問: 對比 make/ant, 爲何 gradle 被稱爲構建系統以及和其餘構建系統的類似之處。java

make

make 是一個很是古老且經久不衰的構建系統。shell

make 的關鍵: shell/文件。apache

make 的規則

make 是基於 shell 的構建系統。api

make 的規則很是簡單,一個簡單的用法就是把長命令精簡爲短命令. 下面是一個簡單的例子。bash

mycli:
    echo "this is a very very long command!"
    echo "this is another very very long command!"

將上面的內容保存到當前目錄的 makefile 文件,而後測試如下命令。jvm

$ make mycli

    echo "this is a very very long command!"
    this is a very very long command!
    echo "this is another very very long command!"
    this is another very very long command!

這種用法下, make 至關於給一組命令提供了快捷方式,當執行 make <shortcut> 的時候,
就會執行對應的命令,並且會把執行的命令打印出來。
不過通常狀況下,這樣回顯命令不太好看,能夠加上 -s 取消回顯。
(在每條命令前面加上 @ , 也能夠防止命令回顯)maven

$ make -s mycli

    this is a very very long command!
    this is another very very long command!

這個例子中 mycli 能夠看做是make 的 目標.ide

make 目標的依賴

聲明目標時,能夠冒號後面聲明依賴的子目標。
語法以下:函數

<target>: <subtarget1> <subtarget2>
    command1
    command2
    ...

<subtarget1>: ...
    ...

上面聲明的 target 依賴了 subtarget1/subtarget2

make 目標的狀態

make 真正的做用是做爲構建系統,而 make 的目標有另一層含義,
目標通常來講並非一個簡單的字符串,而是一個文件名。

下面看一個真實的例子:

# 過濾出 test.txt 裏面全部非空行, 生成 content.txt
content.txt: test.txt
    grep -E ".+" test.txt > content.txt

測試:

$ make content.txt
    grep -E ".+" test.txt > content.txt

$ make content.txt
    make: 「content.txt」已經是最新。

能夠看到,第一次生成 content.txt 後,第二次執行 make 會跳過命令,
它的原理是比較目標和依賴的時間戳,只有發現依賴(test.txt)更新的狀況下
纔會執行下面的 grep 命令生成(content.txt)目標。
即 make 經過文件系統時間戳來表示構建目標的狀態。

這另外一個例子,組合了上面全部特性。

backup.tar.gz: content.txt
    tar -zcvf backup.tar.gz userlist.txt

content.txt: test.txt
    grep -E '.*' text.txt > content.txt

ant

apache ant 是一個相似 make 的構建系統,主要優點在於使用 java 編寫,跨平臺。

下載地址

對比

ant 與 make 對比

_ make ant
目標 文件 字符串
腳本 makefile xml
指令 shell java class

樣例

一個簡單的 ant 腳本(build.xml):

<project name="MyProject">
    <target name="MyTarget" depends="SubTarget">
        <echo message="This is target message." level="error"/>
    </target>
    <target name="SubTarget">
        <echo message="This is sub-target message1." />
        <echo message="This is sub-target message2." />
    </target>
</project>

運行效果:

$ ant MyTarget
Buildfile: D:\sphinx\record\docs\gradle\build.xml

SubTarget:
     [echo] This is sub-target message1.
     [echo] This is sub-target message2.

MyTarget:
     [echo] This is target message.

BUILD SUCCESSFUL
Total time: 0 seconds

這個組織結構和 makefile 很像。

原理

xml 中分爲三層: project、target、task。
其中 target 即對應 makefile 中的目標,重點是 task。

makefile 中 task 通常對應一條 shell 命令,而 ant 的 task 通常對應執行了一個 java class。

例如 <echo message="This is target message." level="error"/>, 能夠翻譯成代碼:
new Echo("This is target message", "error").execute()

echo 僞代碼以下:

public class Echo extends Task {
    Echo(String message, String level) {
        this.message = message;
        this.level = level;
    }
    void execute() {
        System.out.println("[%s] %s", this.level, this.message);
    }
}

ant 中內置了不少這種經常使用 task 。並且用戶也能夠本身實現自定義 Task。

構建系統的基本要素

從 make 和 apache ant 中能夠大體感知到, 構建系統兩個重要功能就是:

  1. 狀態管理(增量構建)
  2. 依賴管理(DAG 圖)

gradle

gradle 是一個更偏向 Java 定製化的構建工具。

使用的話,最好使用新版的 gradle,若是要跟 jetbrains、kotlinDsl 結合使用,最好用 gradle-all 版本。

下面的例子所有使用 kotlin-dsl 舉例,構建腳本爲 build.gradle.kts

注意: 使用 jetbrains idea 能夠對 gradle 構建腳本有更好的提示。

簡介

gradle 使用 groovy/kotlin 語言做爲構建語言,能夠理解爲是一個構建腳本。

Ant 中存在 project、target、task 的概念,gradle 也擁有 project/task 的概念。對好比下

概念 ant gradle
項目 project project
構建目標 target task
構建指令 task groovy/kotlin代碼

須要注意的是 gradle 的 task 對應了 ant 的 target。
腳本中能夠經過相似與引用全局變量的方式引用 tasks。

用法

// build.gradle.kts
tasks.register("hello") {
    doLast {
        println("Hello world!")
    }
}
tasks.register("intro") {
    dependsOn("hello")
    doLast {
        println("I'm Gradle")
    }
}

運行:

$ gradle intro

    Hello world!
    I'm Gradle
  • task.register(name: String): 定義指定名稱的 task, 大括號體包含構建這個 task 的代碼。
  • doLast: task 構建完成後的執行函數。
  • dependsOn: 本 task 依賴的其餘 task。

也能夠編寫任意複雜的腳本。

repeat(4) {
    tasks.register("intro${it}") {
        doLast {
            println("I'm ${it}")
        }
    }
}

project

對於 ant 來講,整個 build.xml 的頂層元素就是 project; gradle 也相似。

對整個 build.gradle.kts 來講, 整個文件都處在 project 的做用域中,
能夠經過 this 訪問 project 自己及其成員、方法, 不過通常都省略 this,
好比用 repositories {...}, 而不是 this.repositories {...}

project

如上面的例子,整個構建流程相似於下面的僞代碼:

interface Task {
        fun doLast();
        fun dependsOn();
        ...
    }
    class TaskContainer extends Set<Task> {
        fun register(name: String): Task
    }
    class Project {
        val tasks: TaskContainer

        fun build() {
            // Start: 將 build.gradle.kts 的內容在這裏展開
+           tasks.register("hello") {
+               doLast {
+                   println("Hello world!")
+               }
+           }
+           tasks.register("intro") {
+               dependsOn("hello")
+               doLast {
+                   println("I'm Gradle")
+               }
+           }
            // End: 
        }
    }

    new Project().build()

Project 具體聲明見: org.gradle.api.Project

除了 tasks 外,幾乎構建腳本中全部調用的方法都綁定於 Project,
這些方法的做用是設置 Project 屬性。
這相似於 Ant, Ant 也有不少針對 Project 的屬性設置。

好比 defaultTasks 設置默認構建目標, repositories 設置項目 maven 源, dependencies 設置項目依賴的第三方包, group 設置項目包名。

tasks

tasks 是 Project 的成員。能夠經過 tasks 訪問,其底層是一個容器(Set<Task>),
不過提供了不少自定義方法作其餘管理操做。

tasks 具體聲明見: org.gradle.api.tasks.TaskContainer

基本操做

// 建立 task: 默認類型爲 DefaultTask
tasks.register("myTask") {
    // 能夠在裏面作一些操做
    doLast {...}
}
// 也能夠在後期引用,而後進行其餘設置
tasks.named("myTask") {
    dependsOn(...)
}

// 建立指定類型的 Task
tasks.registor<Copy>("copyTask") {
    from(file("srcDir"))
    to(...)
}

建立 task 能夠指定類型,如上面的 Copy ,
此時能夠調用 Copy 的專有方法,如 from()

第三方庫

對於 kotlin-dsl, 構建腳本中可使用任何 kotlin 代碼,也可使用kotlin 標準庫。

可是構建腳本默認沒法使用第三方庫,若是須要第三方庫,則要在 buildScripts 中引入.

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        "classpath"(group = "commons-codec", name = "commons-codec", version = "1.2")
    }
}

tasks.register("encode") {
    doLast {
        val encodedString = Base64().encode("hello world\n".toByteArray())
        println(String(encodedString))
    }
}

buildScripts.dependencies 表示構建腳本自己的第三方依賴,而不是用戶項目的依賴。

plugins

gradle 腳本中默認做用域就是 Project, 因此構建腳本中能夠隨意調用 Project 的方法。但 Project 中的方法是有限的,可能知足不了業務需求。

gradle 插件能夠擴充 Project 的方法,或者增長其餘功能、task 等。

plugin 的使用:

plugins {
    id("org.jetbrains.kotlin.jvm") version "1.4.10"
}

plugins 和 dependencies 的聲明方法差很少,

因爲 maven 下載速度可能很慢,能夠新建 settings.gradle.kts 設置 plugins 源。

pluginManagement {
    repositories {
        maven(url="https://maven.aliyun.com/repository/public/")
        maven(url="https://maven.aliyun.com/repository/gradle-plugin")
    }
}

下一步

task入門

完整文檔: gradle 安裝目錄的 docs。

相關文章
相關標籤/搜索