Gradle-CheatSheet

Gradle CheatSheet

在 Grade 中,我们常见的几个关键术语有 Project、Plugin 以及 Task。和 Maven 一样,Gradle 只是提供了构建项目的一个框架,真正起作用的是 Plugin。Gradle 在默认情况下为我们提供了许多常用的 Plugin,其中包括有构建 Java 项目的 Plugin,还有 War,Ear 等。与 Maven 不同的是,Gradle 不提供内建的项目生命周期管理,只是 Java Plugin 向 Project 中添加了许多 Task,这些 Task 依次执行,为我们营造了一种如同 Maven 般项目构建周期。换言之,Project 为 Task 提供了执行上下文,所有的 Plugin 要么向 Project 中添加用于配置的 Property,要么向 Project 中添加不同的 Task。一个 Task 表示一个逻辑上较为独立的执行过程,比如编译 Java 源代码,拷贝文件,打包 Jar 文件,甚至可以是执行一个系统命令或者调用 Ant。另外,一个 Task 可以读取和设置 Project 的 Property 以完成特定的操作。

Configuration | 构建配置

artifacts

和 Maven 一样,Gradle 也是通过 artifact 来打包构建的。得益于上述的 Gradle 本身的特性,artifact 在 Gradle 里实现得更灵活一些。看一个例子:

// jar类型的artifact
task myJar(type: Jar)
artifacts {
    archives myJar
}

// file类型的artifact
def someFile = file('build/somefile.txt')
artifacts {
    archives someFile
}

// 根据自定义task来完成artifact
task myTask(type:  MyTaskType) {
    destFile = file('build/somefile.txt')
}
artifacts {
    archives(myTask.destFile) {
        name 'my-artifact'
        type 'text'
        builtBy myTask
    }
}

// 根据自定义task来完成artifact
task generate(type:  MyTaskType) {
    destFile = file('build/somefile.txt')
}
artifacts {
    archives file: generate.destFile, name: 'my-artifact', type: 'text', builtBy: generate
}

publish | 发布

Gradle 构建的项目,发布到仓库中,也非常容易:

apply plugin: 'maven'

uploadArchives {
    repositories {
        ivy {
            credentials {
                username "username"
                password "pw"
            }
            url "http://repo.mycompany.com"
        }
    }
}

Properties | 属性

在默认情况下,Gradle 已经为 Project 添加了很多 Property,我们可以调用以下命令进行查看:

gradle properties

输出如下:

:properties

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

allprojects: [root project 'gradle-blog']
ant: org.gradle.api.internal.project.DefaultAntBuilder@1342097

buildDir: /home/davenkin/Desktop/gradle-blog/build
buildFile: /home/davenkin/Desktop/gradle-blog/build.gradle
...
configurations: []
convention: org.gradle.api.internal.plugins.DefaultConvention@11492ed
copyFile: task ':copyFile'
...
ext: org.gradle.api.internal.plugins.DefaultExtraPropertiesExtension@1b5d53a
extensions: org.gradle.api.internal.plugins.DefaultConvention@11492ed
...
helloWorld: task ':helloWorld'
...
plugins: [org.gradle.api.plugins.HelpTasksPlugin@7359f7]
project: root project 'gradle-blog'
...
properties: {...}
repositories: []

tasks: [task ':copyFile', task ':helloWorld']
version: unspecified

BUILD SUCCESSFUL

Total time: 2.667 secs
Name Type Default Value
project Project The Project instance
name String The name of the project directory.
path String The absolute path of the project.
description String A description for the project.
projectDir File The directory containing the build script.
buildDir File *projectDir*/build
group Object unspecified
version Object unspecified
ant AntBuilder An AntBuilder instance

Variables | 变量

Dependence | 依赖管理

$ gradle dependencies

Repository | 仓库配置

repositories {
    mavenCentral()          // 定义仓库为maven中心仓库
}
repositories {
    jcenter()               // 定义仓库为jcenter仓库
}
repositories {
    maven {
        url "http://repo.mycompany.com/maven2"      // 定义依赖包协议是maven,地址是公司的仓库地址
    }
}
repositories {                              // 定义本地仓库目录
    flatDir {
        dirs 'lib'
    }
}
repositories {                              // 定义ivy协议类型的仓库
    ivy {
        url "http://repo.mycompany.com/repo"
    }
}
repositories {
 mavenCentral artifactUrls:["file://C:/maven/.m2/repository/"]
}

如果是系统的默认配置的:

repositories {
  mavenLocal()
}

依赖声明

// Now we can set the dependencies by configuring the dependencies closure
// where compile is a configuration not a method that compiles the dependency or so
// It puts the dependency in the classpath of the Java Application
// <groupId>:<artifactId>:<version>
dependencies {
  compile "org.apache.commons:commons-lang3:3.3.2"
}

// Or
dependencies {
  compile group: "org.apache.commons", name: "commons-lang3", version: "3.3.2"
}

Task | 任务

检索

Gradle 在默认情况下为我们提供了几个常用的 Task,比如查看 Project 的 Properties、显示当前 Project 中定义的所有 Task 等。可以通过一下命令查看 Project 中所有的 Task:

$ gradle tasks

输出如下:

:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
setupBuild - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
dependencies - Displays all dependencies declared in root project 'gradle-blog'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradle-blog'.
help - Displays a help message
projects - Displays the sub-projects of root project 'gradle-blog'.
properties - Displays the properties of root project 'gradle-blog'.
tasks - Displays the tasks runnable from root project 'gradle-blog'.

Other tasks
-----------
copyFile
helloWorld

To see all tasks and more detail, run with --all.

BUILD SUCCESSFUL

Total time: 2.845 secs

上面的 other tasks 中列举出来的 task 即是我们自定义的 tasks。

定义

Gradle 的 Project 从本质上说只是含有多个 Task 的容器,一个 Task 与 Ant 的 Target 相似,表示一个逻辑上的执行单元。我们可以通过很多种方式定义 Task,所有的 Task 都存放在 Project 的 TaskContainer 中。让我们来看一个最简单的 Task,创建一个 build.gradle 文件,内容如下:

// 简略定义
task helloWorld << {
   println "Hello World!"
}

// 上述代码中使用了 `<<` 符号用来表征 `{}` 声明的动作是追加在某个任务的末尾,如果使用全声明即是
task printVersion {
// 任务的初始声明可以添加first和last动作
    doFirst {
        println "Before reading the project version"
    }

    doLast {
        println "Version: $version"
    }
}

这里的 << 表示向 helloWorld 中加入执行代码——其实就是 groovy 代码。Gradle 向我们提供了一整套 DSL,所以在很多时候我们写的代码似乎已经脱离了 groovy,但是在底层依然是执行的 groovy。比如上面的 task 关键字,其实就是一个 groovy 中的方法,而大括号之间的内容则表示传递给 task()方法的一个闭包。除了 << 之外,我们还很多种方式可以定义一个 Task。

task(hello) << {
    println "hello"
}

task(copy, type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

//也可以用字符串作为任务名
task('hello') <<
{
    println "hello"
}

task('copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

//Defining tasks with alternative syntax

tasks.create(name: 'hello') << {
    println "hello"
}

tasks.create(name: 'copy', type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

Default tasks | 默认任务

Gradle 允许在脚本中配置默认的一到多个任务来响应没有带参数的gradle命令:

defaultTasks 'clean', 'run'

task clean << {
    println 'Default Cleaning!'
}

task run << {
    println 'Default Running!'
}

task other << {
    println "I'm not a default task!"
}

gradle -q 命令的输出:

> gradle -q
Default Cleaning!
Default Running!

Java Plugin

1,使用 Java plugin,只需要在 build.gradle 中加入这句话:

apply plugin: 'java'

2,了解或设置 Java project 布局。Gradle 和 Maven 一样,采用了“约定优于配置”的方式对 Java project 布局,并且布局方式是和 Maven 一样的,此外,Gradle 还可以方便的自定义布局。在 Gradle 中,一般把这些目录叫做 source set。看下官方的答案:

gradle source set

这里要注意,每个 plugin 的 source set 可能都不一样。同样的,Java plugin 还定义好了一堆 task,让我们可以直接使用,比如:clean、test、build 等等。这些 task 都是围绕着 Java plugin 的构建生命周期的:

图中每一块都是一个 task,箭头表示 task 执行顺序/依赖,比如执行 task jar,那么必须先执行 task compileJava 和 task processResources。另外可以看到,Gradle 的 Java plugin 构建生命周期比较复杂,但是也表明了更加灵活,而且,在项目中,一般只使用其中常用的几个:clean test check build 等等。

gradle 构建过程中,所有的依赖都表现为配置,比如说系统运行时的依赖是 runtime,gradle 里有一个依赖配置叫 runtime,那么系统运行时会加载这个依赖配置以及它的相关依赖。这里说的有点绕,可以简单理解依赖和 maven 类似,只不过 gradle 用 configuration 实现,所以更灵活,有更多选择。下图是依赖配置关系图以及和 task 调用的关系图:

javaPluginConfigurations

可以看到,基本和 Maven 是一样的。其实 Gradle 里面这些依赖(scope)都是通过 configuration 来实现的,这里就不细说,有兴趣的可以研究一下官方资料。

源码配置

// A Closure that configures the sourceSets Task
// Sets the main folder as Source folder (where the compiler is looking up the .java files)
sourceSets {
  main.java.srcDir "src/main"
}

// This can also be written as a function -> srcDir is a method (Syntax sugar of the Groovy language)
souceSets {
  main.java.srcDir("src/main")
}

// Or
sourceSets.main.java.srcDir "src/main"

// Or
sourceSets {
  main {
    java {
      srcDir "src/main"
    }
  }
}

// Or setting the variable directly as a typical groovy enumerational style
sourceSets {
  main.java.srcDirs = ["src/main"]
}

任务

# 执行编译,将 Java 源码编译到 build 目录并且打包到 jar 包中
$ gradle build

自定义 jar 命令的配置参数:

// Configure the jar task to insert the Main Class to the resulting MANIFEST.MF
jar {
  manifest.attributes("Main-Class", "de.example.main.Application")
}

// Or without the parentheses
jar {
  manifest.attributes "Main-Class": "de.example.main.Application"
}

jar {
  from configurations.compile.collect {
    entry -> zipTree(entry)
  }
}

// Or with syntactic sugar
jar {
  from configurations.compile.collect {
    entry -> zipTree entry
  }
}

// And with more syntactic sugar
// (where "it" is like "this" in gradle and "this" is the entry iterating over by the for loop)
jar {
  from configurations.compile.collect {
    zipTree it
  }
}

Web Application

测试 | Test