玩转Gradle构建工具(二)、Project、Task常用API

516 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

前言

在编写Gradle脚本时,需要调用大量API来完成你预想的需求,Project接口在官网是这样说的:此接口是用于从构建文件与 Gradle 交互的主要 AP,可以通过编程访问Gradle的所有功能

所以先从Project接口下的方法开始介绍。

Project

afterEvaluate

看名字意思是之后评估,似乎很难理解是什么意思,他是用于在配置项目后进行回调,当然还有一个在之前回调的方法beforeEvaluate,但beforeEvaluate必须在父模块的 build.gradle 对子模块进行配置才能生效,因为在当前模块的 build.gradle 中配置,它自己本身都没配置好,所以不会监听到。

this.afterEvaluate {
    println("afterEvaluate")
}

copy

用来复制文件,如下,作用是把/home/HouXinLin/test/a.txt这个文件复制到当前目录

task taskA(){
    doLast {
        this.copy {
            from("/home/HouXinLin/test/a.txt")
            into "./"
        }
    }
}

当然也可以使用通配符,如下,用来把目标文件夹下所有文件复制到当前目录下的test下,include用来做包含,**表示所有文件。

task taskA(){
    doLast {
        this.copy {
            from("/home/HouXinLin/test/"){
                include "**"
            }
            into "./test/"
        }
    }
}

from这个函数是由CopySpec提供,可以事先创建一个CopySpec对象,然后创建一个type为Copy的任务,进行复制。

def baseCopy =copySpec {
    from("/home/HouXinLin/test/")
    include "**"
}
task taskA(type:Copy){
    into "./test"
    with baseCopy
}

delete

用于删除文件,如下,用于删除当前目录下test文件夹和其中所有文件。

task taskA(){
    doLast {
        this.delete("./test")
    }
}

exec

执行外部命令,如执行ls命令,executable用于指定命令名称,args代表参数。

task taskA(){
    doLast {
        println(this.exec {
            executable("ls")
            args("-l")
        })
    }
}

还可以通过type指定任务为Exec,workingDir设置工作目录,commandLine设置命令,如下,使用touch命令创建一个test.txt文件。

task taskA(type:Exec){
    workingDir("./test")
    commandLine("touch","test.txt")
}

file

创建一个文件对象,返回值就是File,可以调用其任意方法,参数不仅仅可以是字符串,还可以是File、Path、URL

task taskA(){
    doLast {
        def testFile =file("./test/test.txt")
        println(testFile.path)
    }
}

getBuildDir()

返回此项目的构建目录。

task taskA(){
    doLast {
        println(this.getBuildDir())

    }
}

getBuildFile()

此项目的构建脚本路径。

task taskA(){
    doLast {
        println(this.getBuildFile())

    }
}

getRootDir()

返回此项目根目录

task taskA(){
    doLast {
        println(this.getRootDir())

    }
}

getVersion()

返回此项目版本,如果不写version,则返回unspecified。

version("1.1")
task taskA(){
    doLast {
        println(this.getVersion())
    }
}

mkdir

创建目录。

task taskA(){
    doLast {
        mkdir("./a")
    }
}

task

根据给定名称创建一个Task, 并将其添加到此项目中,如下,在配置阶段创建一个testB,并设置testA依赖testB,这样在执行testA时,会自动先执行testB。

task taskA(){
    task("testB"){
        doLast {
            println("testB")
        }
    }
    doLast {
        println("taskA")
    }
    tasks.taskA.dependsOn("testB")
}

configurations

负责声明和管理配置,配置叫Configuration,而他表示一个artifacts或者其他依赖项。 如下,可以遍历现有的配置。

task taskA(){
    doLast {
        configurations.configureEach{
            println(it.name)
        }
    }
}

输出如下

annotationProcessor
apiElements
archives
compileClasspath
compileOnly
default
implementation
mainSourceElements
runtimeClasspath
runtimeElements
runtimeOnly
testAnnotationProcessor
testCompileClasspath
testCompileOnly
testImplementation
testResultsElementsForTest
testRuntimeClasspath
testRuntimeOnly

如果已经使用过Gradle的人来说,看到这些会非常熟悉,这些都是用来引入一个依赖的。

但是他们都有区别,比如runtimeOnly意为只有在运行时候引入,而compileOnly意为只有在编译时引入,最常用的就是implementation,表示在编译和运行时都引入,configurations是ConfigurationContainer的实例,他所提供的create方法可以让我们创建一个类似implementation的功能,如下所示。

configurations.create("testConfig")

这样在使用刚才代码遍历时,就会出现testConfig,同时我们可以在dependencies下使用他,如下

configurations.create("testConfig")
task taskA(){
    doLast {
        configurations.testConfig.getFiles().forEach{
            println(it.absolutePath)
        }
    }
}
repositories {
    mavenCentral()
}
dependencies {
    testConfig group: 'mysql', name: 'mysql-connector-java', version: '8.0.29'
    testConfig files("./test/test.txt")
}

通过configurations.testConfig.getFiles()就可以拿到所有通过testConfig引入的依赖,注意,除了jar我们称之为依赖,其他文件也当然算依赖了,然后拿到依赖路径后就可以做下一步逻辑了,但上面会输出三个路径,可明明只引入了2个,为什么会输出三个路径呢?

因为依赖还可以依赖其他第三方依赖,上面我们引入的mysql驱动,他里面还依赖了protobuf-java这个jar包,所以会输出三个路径。

下面介绍一个常用的功能,排除依赖,拿上面来说,我们把protobuf-java排除掉,可以这样做。

configurations.create("testConfig")
configurations {
    testConfig.exclude group:"com.google.protobuf",module:"protobuf-java"
}
task taskA(){
    doLast {
        configurations.testConfig.getFiles().forEach{
            println(it.absolutePath)
        }
    }
}
repositories {
    mavenCentral()
}
dependencies {
    testConfig group: 'mysql', name: 'mysql-connector-java', version: '8.0.29'
    testConfig files("./test/test.txt")
}

这样输出就只有两个文件了,但这样属于全局排除,任何一个依赖引入了protobuf-java都会排除,其实还可以针对单个依赖,可以这样做。

testConfig (group: 'mysql', name: 'mysql-connector-java', version: '8.0.29'){
    exclude group:"com.google.protobuf",module:"protobuf-java"
}

关于依赖管理我们在后续文章单独说。

zipTree

通常在打包jar的时候需要,如把第三方依赖解压后放入主工程中,zipTree用来根据一个zip文件,创建一个FileTree,其中包含这个zip中的所有文件,这时候可以使用copy、into去解压。

task taskA(){
    doLast {
        copy {
            from zipTree('/home/HouXinLin/test/maven-demo-1.0.jar')
            into './test'
        }
    }
}

还有个tarTree也是类似。

javaexec

用于启动java进程,如下。

task taskA(){
    doLast {
        javaexec{
            classpath("/home/HouXinLin/test/maven-demo-1.0.jar")
            mainClass="com.hxl.Main"
        }
    }
}

关于Project的API就到这里。

Task

Task是关于任务的接口。

dependsOn

用于依赖其他任务,如下,用于将taskB依赖taskA。

task taskA(){
    doFirst {
        println("taskA")
    }
}
task taskB(){
    doFirst {
        println("taskB")
    }

}
tasks.getByName("taskB").configure {
    dependsOn("taskA")
}

tasks是Project中提供的方法,用于获取TaskContainer,他负责管理Task,getByName则是根据名字获取任务。

configure用于配置任务。

doFirst

用于将给定的闭包添加到此任务的操作列表的开头,可以执行多次,把他想象成一个队列,每次doFirst的时候向队列前面添加一个闭包

task taskB(){
    doFirst {
        println("taskB1")
    }

}
tasks.getByName("taskB").configure {
    doFirst {
        println("taskB2")
    }
    doFirst {
        println("taskB3")
    }
}

在上面,第一个doFirst是taskB中配置的,将打印taskB1,而顺着书写顺序,又新增加了一个doFirst,打印taskB2,最后又增加了一个,打印taskB3,所以,第一个应该执行最后一个添加的doFirst,所以最终结果如下。

taskB3
taskB2
taskB1

doFirst也是类似。

finalizedBy

用于添加一个最终执行的任务,和java中的finally一样,也就是当本任务执行后,执行一个最终的任务。

task taskA(){
    doFirst {
        println("taskA")
    }
}
task taskB(){
    doFirst {
        println("taskB1")
    }

}
tasks.getByName("taskB").configure {
    finalizedBy("taskA")
}

getActions

Task由一堆Action对象组成,就是doFirst或者doLast中的代码块,抽象成了Action,当任务执行时,每个Action依次执行,可以通过Action.execute(T)手动调用,而getActions就是获取这个任务的所有Action。

如下,我们在配置阶段获取了taskB的所有Action,然后依次手动调用每个doFirst和doLast代码块。

task taskB(){
    doFirst {
        println("doFirst")
    }

    doLast {
        println("doLast")
    }
}
tasks.getByName("taskB").configure {
    getActions().forEach{
        it.execute(tasks.getByName("taskB"))
    }
}

当taskB执行时,相当于执行了2次。

onlyIf

onlyIf需要一个返回值,如果为true,则表示这个任务可以执行,如果为false,那么这个任务将不执行。

task taskB(){
    doFirst {
      println("doFirst")
    }
}
tasks.getByName("taskB").configure {
    onlyIf {
        return 1>2
    }
}