Gradle 是一个JVM语言(比如Java、Groovy或者Scala)的自动化构建工具。Gradle可以被配置以运行任务(Tasks),比如编译jar包,运行测试用例,创建文档等等。
当用intelij创建新的项目的时候,你可以从左边的选项中选择gradle,并且选择你想使用的语言:
创建完你的项目后,InteliJ会为你创建一个build.gradle文件,其中有你选择的语言相关的一些很有用的默认配置,让我们来看看build.gradle文件。我将在示例中使用Java,其它语言其实也类似。
build.gradle文件是所有的魔法发生的地方,这是InteliJ为java语言创建的默认的文件:
plugins {
id 'java'
}
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
这个文件是什么意思?里面的语法从哪里来的?
Gradle构建脚本是用Groovy写的,一个类似Java的JVM语言,但是有着更简洁的语法。让我们先学一些基本知识,从而能更好的理解Gradle脚本是怎么运行的:
你可以在InteliJ中的groovy控制台中体验groovy脚本,在InteliJ的tools->groovy console中可以打开groovy控制台:
在控制台,我们可以输入任何合法的Java代码:
点击绿色的运行按钮,在底部的窗口会有如下的输出:
有一点需要注意的是,我们没有在运行的代码外用class和main方法包裹它。我们还可以更进一步。Groovy自动帮我们导入了System.out,所以我们可以省略System.out:
println("hello groovy");
在Groovy中,一个参数的方法调用,圆括号和分号都可以省略,所以我们可以将System.out.println("hello groovy")
简写成
println "hello groovy"
,够时髦吧?
理解Groovy的闭包就能够使得build.gradle中发生的事情不再神秘。
如果你使用过Java8的lambda表达式,就会感觉groovy闭包似曾相识——它们能够让你像处理正常值一样去处理方法(所以你可以将闭包作为参数传递给其它方法)。让我们来看一个例子:
在Groovy控制台,我们定义一个类:
class MyClass {
}
让我们添加一个方法,入参是闭包:
class MyClass {
void doSomething(Closure closure) {
closure.call()
}
}
我们可以通过closure.call()
运行任何代码,那么我们怎么使用它呢?我们需要创建一个MyClass实例,然后调用doSomething方法:
class MyClass {
void doSomething(Closure closure) {
closure.call()
}
}
myClass = new MyClass()
myClass.doSomething {
println "doing something"
}
控制台会输出:
> class MyClass {
> void doSomething(Closure closure) {
> closure.call()
> }
> }
>
> myClass = new MyClass()
>
> myClass.doSomething {
> println "doing something"
> }
doing something
我们调用闭包的代码和下面的是否有点像?
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
dependencies
就是一个方法,它的入参是一段代码块(其实就是一个闭包)。在代码块中,我们调用了testCompile
方法,testCompile
方法的入参就是group:'junit',name:'junit',version:'4.12'
(group,name,version部分其实就是groovy map的简写方式,本质上就是一个kv对)。
现在我们已经较好地理解了groovy语法是怎么工作的了,让我们来深入研究一下build.gradle文件。
build.gradle
文件和project对象有一一对应的关系,project对象表示了我们项目(指IDEA中的项目)的信息。如果你使用过Maven(另一个Gradle要兼容的流行的构建工具),你将认识到,这个主意来自于pom.xml
文件(工程对象模型),pom.xml
文件的作用和build.gradle
基本上是一样的。
每一个project
(Gradle的project概念)由一系列的Tasks
组成:Task代表了构建我们开发的项目的时候需要执行的一系列不可分割的工作单元。
Gradle的项目对象模型(project object model)中有很多默认的Tasks,我们可以通过运行下面的命令来看看有哪些任务(本地安装了gradle,并且在命令行中可以执行gradle命令,在IDEA打开gradle项目的Terminal中执行):
>gradle tasks
如果你的项目的build.gradle
文件是空的(比如你删除了它的内容),运行gradle tasks
将会输出:
tasks
任务展示了一个项目可用的所有的Tasks。真正的魔法在添加了java plugin后发生:
plugins {
id 'java'
}
引用Java插件,将会添加很多我们可以使用的额外的tasks。现在再运行gradle tasks
,将会输出:
这些是许多Java项目都可能会使用到的有用的默认tasks。
关键的要点是,我们可以通过修改build.gradle
脚本来修改项目对象模型——因为在Gradle中代码即配置。
可以在https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_usage中查看Java插件的完整文档。
和Tasks一样,我们的项目对象也有"properties"(属性),属性也表达了我们项目的一些信息。比如一些简单的属性,version、sourceCompatibility:
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
还有一些更复杂的属性,通过一些接受闭包的方法来修改。在我刚开始使用Gradle的时候,sourceSets
属性的一些魔法使我困惑,这个属性是Java插件添加的。sourceSets属性定义了Gradle将从哪里寻找你的源码。默认情况下,Gradle遵从Maven的约定,即从src/main/java
目录寻找源码,从src/test/java
寻找测试用例。
但是假设我们把源码放到了my-java-directory
目录下(实际使用中,我不推荐这样放置源码),或者我们接手了遗留项目(它的源码没有遵从Maven约定放在src/main/java
)。我们可以告诉Gradle去指定的目录下寻找源码:
sourceSets {
main {
java {
srcDir "my-java-directory"
}
}
}
和Tasks一样,我们可以通过下面的命令查看完整的gradle的属性列表(注意,这个列表会很大):
>gradle properties
我们可以通过Tasks来实现同样的定制化魔法。当构建我的井字游戏控制台项目的时候,我在寻找一个通过Gradle来方便地运行程序的方式——application
插件有一个遍历的task叫run
。
然而,它需要配置指明System.in
作为标准输入。增加下面这行让我重新配置了run
任务:
run {
standardInput = System.in
}
这儿需要重点强调的是,Tasks也有它们自己的属性,并且允许我们配置。
我们也可以创建我们自定义的任务,既可以从头开始创建,也可以扩展已有的任务,后者更常用。上面的run
任务实际上扩展了更底层的任务类型,叫做JavaExec
。我们可以像下面这样定义上面的run
任务同样的Task:
task customRun(type: JavaExec){
classpath = sourceSets.main.runtimeClasspath
standardInput = System.in
main = 'tictactoe.cli.Main'
}
这为项目对象增加了一个customRun
任务,我们可以通过gradle命令使用它:
>gradle customRun
这和之前的run
任务的效果一样。
Gradle的api是很多的,并且一开始很难掌握。这篇文章只是触及皮毛,但是希望能让Gradle中的语法和概念对于你来说不那么神秘,从而可以让理解Gradle文档更容易一点。
更优秀的指南请参考Gradle官网:https://gradle.org/guides/。
本文翻译自:https://medium.com/@andrewMacmurray/a-beginners-guide-to-gradle-26212ddcafa8