gralde

小码哥:“与大象(Gradle)一见如故?你就是 Gradle?”
大象:“对,我就是那个用来构建 Java 项目的 Gradle 大象。 ”
小码哥:“我好像天天都在用你啊。看,我的项目都是用你构建的。”
大象:“但你不一定真的认识我,你每次修改点构建代码时是不是都要问下谷哥哥(Google)或溢栈哥哥(StackOverflow)?”
小码哥:“额。。。”
大象:“我是你的老朋友了,不要天天如初见哦,咱们得多聊聊,我爸妈给我写了传记文档),估计你也懒得细看,不如我给你做个自我介绍吧。”
小码哥:“👌ok”
大象:“你有用 InteliJ IDEA 吗? ”
小码哥:“对,用的社区版。”
大象:“那我就用 InteliJ IDEA CE 版来给你介绍自己”。

你好,我是 Gradle!

Gradle 是一个用来自动化构建项目的的工具,可以用来构建你常用的 Java、Kotlin 等 JVM 语言开发的项目。我的配置文件可以使用 Gvoovy 或者 Kotlin 来编写,不像隔壁前辈 Maven 那样,要写一大段 XML,你可以很开心的像写代码一样来调整我的配置。

gralde-org-hero

其实我就是你的代码自动化产线,产线有几个工序(Task),你负责喂我代码,我负责打包(Jar/War/JavaDoc/Test)。是不是觉得与科幻片里面的工厂很像?

创建项目

请打开你的 InteliJ IDEA CE,使用 idea 可以很方便的创建一个由我构建的项目(File > New > Project…)。

new-project-with-intelij-1

这里在对话框的左边栏选择 Gradle,右边选择 Java 项目。

new-project-with-intelij-2

输入好项目名之后点击 Finish 就可以啦。

探索项目

idea 帮我们创建了一个 Java 项目,浏览项目很容易发现两个以 gradle 为结尾的文件: build.gradle 和 settings.gradle。我们来看一下它们俩的内容。

settings.gradle

rootProject.name = 'MyProject'

build.gradle

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

小码哥插话:“settings.gradle 很好懂,给 rootProject.name 这个变量赋了一个值,但是 build.gradle 里面写的是什么啊?”
大象:“gradle 的魔法确实就在 build.gradle 之中。build.gradle 是使用 Groovy 编写的脚本,我先介绍一下 Groovy 同学吧。”

你好,Groovy 同学

Gradle 的构建脚本可以使用 Groovy 编写(Kotlin 也可以)。Groovy 也是一种 JVM 语言,并且与 Java 比较相似,写起来比 Java 要简洁得多 (Kotlin 也是),Groovy 是 Java 的超集,也就是说 Java 代码可以当成是 Groovy 代码运行。我们来了解一下 Groovy,以便更容易读懂和修改 build.gradle。

初识 Groovy

在 idea 中打开 Groovy 控制台(Tools > Groovy Console…)

tools-groovy-console-1

接着在控制台里输入一些 Java 代码

tools-groovy-console-2

点击绿色三角形来运行这段代码,可以在下面的窗口看到输出

tools-groovy-console-3

小码哥:“大象,你说这是 Java 代码,为什么没有定义 class 和 main 函数呢?”
大象:“只是看起来是 Java,其实这个就是 Groovy 了,Grovvy 脚本不需要定义 class 和 main 函数就可以执行的,我们继续把它变得更 Groovy 一些吧。”

由于 Groovy 会自动导入 System.out,我们可以把代码简写成这样

tools-groovy-console-4

是不是看起来与 Java 里面函数静态导入写起来一样,我们可以更进一步:

  • 在 Groovy 里面如果函数只有一个参数,那么括弧可以省略;
  • 语句末尾的分号也可以省略;
  • 单引号和双引号是(大部分情况下)可以替换(推荐用双引号,方便迁移到 Kotlin DSL)

tools-groovy-console-5

现在很清楚了:原来 println 是一个函数,在代码里面调用了它,并且给它参数传了一个字符串 my project。

Groovy 闭包(Groovy Closure)

提到闭包,应该或多或少都听说过,Gradle 的 build.gradle 脚本中大量使用了闭包,我们还是用一段代码来说明。

先定义一个类

tools-groovy-console-6

这个类看起来和 Java 没什么区别,我们再添加一个可以接受 Closure 类型参数的函数

tools-groovy-console-7

大象:“是不是看起来与 Java 8 的 Lambda 非常的像,比如 Function 接口?”
小码哥:“是的,难道是一样的东西,只是不同语言写法不同?那在 Groovy 里面如何来使用闭包呢?”
大象:“正是如此,我们来看下如何在 Groovy 里面来使用闭包吧。”

首先需要创建一个 MyProject 实例,然后再调用 doTask 方法。

tools-groovy-console-8

同样点击绿色三角形,运行这段代码

tools-groovy-console-9

我们再回过头来看看 build.gradle, 比如这段代码

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

是不是很好理解:脚本调用了 dependencies 方法,它的参数是一个闭包,在这个闭包里面又调用了 testCompile 方法,并传入了一个字典(Map)参数: group: ‘junit’, name: ‘junit’, version: ‘4.12’

好了,Groovy 同学暂时就介绍到这里,我要继续介绍我自己了。

探索 build.gradle

Gradle 最核心的部分就是 Project 和 Task 了 ,build.gradle 可以直接操控 Project 对象(org.gradle.api.Project 的实例) ,遵循“代码即配置”的基本原则。项目代码可以包括多个 build.gradle, 多个 Project 需要在 settings.gradle 定义。每一个 build.gradle 文件都会对应一个 Project 对象,我们每一个项目的构建信息都会存在对应的 Project 对象中。

站在写代码的角度上可以这样来理解,Gradle 会根据 settings.gradle 定义为每个 Project 创建 org.gradle.api.Project 对象,之后会根据 build.gradle 的配置执行相应的任务(Task)。除了“代码即配置”这个原则之外,还有“约定即配置”这个原则。比如 Gradle 会有默认约定的 Java 项目结构(Java Plugin Project Layout),每一个 Gradle 项目也会约定一些默认的 Task 等。

也就是说,Gradle 的 Project 构建过程是由一系列 Task 组成的,我们可以通过这个命令查看项目默认的有哪些 Task。好,我们把 idea 创建的 build.gradle 清空,然后执行下面的命令。

gradle tasks

可以看到所有默认的 Task。

gradle-tasks

接着一步步恢复 idea 默认创建的 build.gradle,先往空的 build.gradle 文件中添加:

plugins {
    id 'java'
}

可以理解这段代码了么?没错,我们往 build.gradle 中添加了 Java 插件,这样就可以很方便的构建 Java 项目,再次查看 Task。

gradle-tasks-java-plugin

插件可以在 Project 中添加 Task,Gradle 会把这些 Task 构建成一个有向无环图(DAG,Directed Acyclic Graph),加了 Java 插件之后的 DAG 是这样的。

java-plugin-tasks-dag

每一个 Project 包括一系列的 Task,同样,每个 Project 也有很多的属性,例如添加这里代码来修改属性:

group 'org.example'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

我们可以在 build.gradle 中对 Project 的属性进行修改,也可以像查看 Task 一样来查看 Project 所有的属性:

gradle properties

哇,Project 的属性还真不少(只截取了部分属性)。

gradle-properties

自定义 Task

Gradle 的构建过程包括三个阶段:初始化(Initialization)、配置(Configuration)和执行(Execution),为了弄清楚这三个阶段,我们在 build.gradle 中添加这样代码:

println "config"

task task1 {
    println "config in task1"
}

task task2 {
    doLast {
        println "do last in task2"
    }

    println "config in task2"
}

task task3 {
    doFirst {
        println "do first in task3"
    }
    doLast {
        println "do last in task3"
    }

    println "config in task3"
}

在 settings.gradle 中添加:

println "init"

然后执行:

gradle task2 task3

可以看到这样的输出:

gradle-task-phase

从输出日志中可以清晰的看到,Gradle 第一步初始化,然后进行配置,最后执行具体任务,如图所示:

gradle-build-phase

弄清楚构建过程之后,我们可以很容易根据我们的需要对 Task 进行自定义配置或者是新建一个 Task。

老朋友,你好

大象:“我就说到这里,有没有一见如故的感觉?我的 API 非常多,想弄清楚我的细枝末节还是得去看文档,希望我的这个介绍能帮助到你去理解文档。 ”
小码哥:“有种恍然大悟的感觉,配置 Gradle 确实就像写 Java 代码差不多,原来 Gradle 只用通过 build.gradle 想办法修改 Project 对象就可以了。哦对,我还个问题,gradlew 是什么啊?”
大象:“呃,gradlew 我也不知道它是啥啊,你自己查查去吧。”

参考资料

  1. PyScript 官网
  2. Pyodide - 基于 WebAssembly 的 Python