我们之前说过,Gradle的核心是一种用于基于依赖的编程的语言. 用Gradle术语,这意味着您可以定义任务以及任务之间的依赖关系. Gradle保证这些任务按照其依赖关系的顺序执行,并且每个任务仅执行一次. 这些任务形成有向无环图 . 有一些构建工具在执行任务时会建立这种依赖关系图. 执行任何任务之前, Gradle会构建完整的依赖关系图. 这是Gradle的核心所在,它使许多事情成为可能,而这在其他情况下是不可能的.

您的构建脚本将配置此依赖关系图. 因此,严格来说,它们是构建配置脚本 .

Build phases

Gradle构建具有三个不同的阶段.

Initialization

Gradle支持单项目和多项目构建. 在初始化阶段,Gradle确定将要参与构建的项目 ,并为每个项目创建一个Project实例.

Configuration

在此阶段,将配置项目对象. 执行作为构建一部分的所有项目的构建脚本.

Execution

Gradle determines the subset of the tasks, created and configured during the configuration phase, to be executed. The subset is determined by the task name arguments passed to the gradle command and the current directory. Gradle then executes each of the selected tasks.

Settings file

除了构建脚本文件外,Gradle还定义了一个设置文件. 设置文件由Gradle通过命名约定确定. 该文件的默认名称是settings.gradle . 在本章的后面,我们将解释Gradle如何查找设置文件.

设置文件在初始化阶段执行. 多项目构建必须在多项目层次结构的根项目中具有settings.gradle文件. 这是必需的,因为设置文件定义了哪些项目正在参与多项目构建(请参见创作多项目构建 ). 对于单项目构建,设置文件是可选的. 除了定义包含的项目之外,您可能还需要将库添加到构建脚本类路径中(请参阅组织Gradle项目 ). 让我们首先用单个项目构建进行一些自省:

例子1.单项目构建
settings.gradle
println 'This is executed during the initialization phase.'
build.gradle
println 'This is executed during the configuration phase.'

task configured {
    println 'This is also executed during the configuration phase.'
}

task test {
    doLast {
        println 'This is executed during the execution phase.'
    }
}

task testBoth {
	doFirst {
	  println 'This is executed first during the execution phase.'
	}
	doLast {
	  println 'This is executed last during the execution phase.'
	}
	println 'This is executed during the configuration phase as well.'
}
settings.gradle.kts
println("This is executed during the initialization phase.")
build.gradle.kts
println("This is executed during the configuration phase.")

tasks.register("configured") {
    println("This is also executed during the configuration phase.")
}

tasks.register("test") {
    doLast {
        println("This is executed during the execution phase.")
    }
}

tasks.register("testBoth") {
    doFirst {
        println("This is executed first during the execution phase.")
    }
    doLast {
        println("This is executed last during the execution phase.")
    }
    println("This is executed during the configuration phase as well.")
}

Output of gradle test testBoth

> gradle test testBoth
This is executed during the initialization phase.

> Configure project :
This is executed during the configuration phase.
This is also executed during the configuration phase.
This is executed during the configuration phase as well.

> Task :test
This is executed during the execution phase.

> Task :testBoth
This is executed first during the execution phase.
This is executed last during the execution phase.

BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed
> gradle test testBoth
This is executed during the initialization phase.

> Configure project :
This is executed during the configuration phase.
This is executed during the configuration phase as well.

> Task :test
This is executed during the execution phase.

> Task :testBoth
This is executed first during the execution phase.
This is executed last during the execution phase.

BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed

对于构建脚本,将属性访问和方法调用委托给项目对象. 同样,设置文件中的属性访问和方法调用也委托给设置对象. 查看API文档中的Settings类以获取更多信息.

Multi-project builds

多项目构建是在一次执行Gradle期间构建多个项目的构建. 您必须在设置文件中声明参与多项目构建的项目. 在致力于该主题的一章中,关于多项目构建的内容还有很多要说的(请参见创作多项目构建 ).

Project locations

多项目构建始终由具有单个根的树表示. 树中的每个元素代表一个项目. 一个项目具有一个路径,该路径表示该项目在多项目构建树中的位置. 在大多数情况下,项目路径与项目在文件系统中的物理位置一致. 但是,此行为是可配置的. 将在settings.gradle文件中创建项目树. 默认情况下,假定设置文件的位置也是根项目的位置. 但是您可以在设置文件中重新定义根项目的位置.

Building the tree

在设置文件中,您可以使用一组方法来构建项目树. 分层和平面物理布局得到特殊支持.

Hierarchical layouts

例子2.分层布局
settings.gradle
include 'project1', 'project2:child', 'project3:child1'
settings.gradle.kts
include("project1", "project2:child", "project3:child1")

include方法将项目路径作为参数. 假定项目路径等于相对物理文件系统路径. 例如,默认情况下,路径" services:api"映射到文件夹" services / api"(相对于项目根目录). 您只需要指定树的叶子即可. 这意味着包含路径" services:hotels:api"将导致创建3个项目:" services"," services:hotels"和" services:hotels:api". 有关如何使用项目路径的更多示例,可以在Settings.include(java.lang.String [])的DSL文档中找到.

Flat layouts

例子3.平面布局
settings.gradle
includeFlat 'project3', 'project4'
settings.gradle.kts
includeFlat("project3", "project4")

includeFlat方法将目录名称作为参数. 这些目录需要作为根项目目录的同级存在. 这些目录的位置被视为多项目树中根项目的子项目.

Modifying elements of the project tree

在设置文件中创建的多项目树由所谓的项目描述符组成 . 您可以随时在设置文件中修改这些描述符. 要访问描述符,您可以执行以下操作:

Example 4. Lookup of elements of the project tree
settings.gradle
println rootProject.name
println project(':projectA').name
settings.gradle.kts
println(rootProject.name)
println(project(":projectA").name)

使用此描述符,您可以更改名称,项目目录和项目的构建文件.

例子5.修改项目树的元素
settings.gradle
rootProject.name = 'main'
project(':projectA').projectDir = new File(settingsDir, '../my-project-a')
project(':projectA').buildFileName = 'projectA.gradle'
settings.gradle.kts
rootProject.name = "main"
project(":projectA").projectDir = File(settingsDir, "../my-project-a")
project(":projectA").buildFileName = "projectA.gradle"

请查看API文档中的ProjectDescriptor类以获取更多信息.

Initialization

Gradle如何知道要执行单个项目还是进行多个项目? 如果您从带有设置文件的目录中触发多项目构建,那么事情很容易. 但是Gradle还允许您从参与构建的任何子项目中执行构建. [ 1 ]如果您在没有settings.gradle文件的项目中执行Gradle,则Gradle会通过以下方式查找settings.gradle文件:

  • 它在名为master的目录中查找,该目录与当前目录具有相同的嵌套级别.

  • 如果尚未找到,它将搜索父目录.

  • 如果尚未找到,则该构建将作为单个项目构建执行.

  • 如果找到settings.gradle文件,则Gradle会检查当前项目是否属于找到的settings.gradle文件中定义的多项目层次结构的一部分. 如果不是,则将构建作为单个项目构建执行. 否则,将执行多项目构建.

这种行为的目的是什么? Gradle需要确定您所在的项目是否是多项目构建的子项目. 当然,如果它是一个子项目,则只会构建子项目及其从属项目,但是Gradle需要为整个多项目构建创建构建配置(请参见创作多项目构建 ). 如果当前项目包含settings.gradle文件,则该构建始终按以下方式执行:

  • 如果settings.gradle文件未定义多项目层次结构,则为单个项目构建

  • 如果settings.gradle文件确实定义了多项目层次结构,则为多项目构建.

自动搜索settings.gradle文件仅适用于具有物理层次结构或平面布局的多项目构建. 对于平面布局,您必须另外遵守上述命名约定(" master "). Gradle支持多项目构建的任意物理布局,但是对于这种任意布局,您需要从设置文件所在的目录执行构建. 有关如何从根目录运行部分构建的信息,请参阅按其绝对路径运行任务 .

Gradle为参与构建的每个项目创建一个Project对象. 对于多项目构建,这些是Settings对象(加上根项目)中指定的项目. 默认情况下,每个项目对象的名称都等于其顶级目录的名称,除根项目外的每个项目都有一个父项目. 任何项目都可以有子项目.

Configuration and execution of a single project build

对于单个项目构建, 初始化阶段之后的工作流程非常简单. 将针对初始化阶段创建的项目对象执行构建脚本. 然后Gradle查找名称与作为命令行参数传递的名称相同的任务. 如果存在这些任务名称,则会按照传递它们的顺序将它们作为单独的版本执行. 在创作多项目构建中讨论了多项目构建的配置和执行.

Responding to the lifecycle in the build script

随着构建在生命周期中的进展,您的构建脚本可以接收通知. 这些通知通常采用两种形式:您可以实现特定的侦听器接口,也可以提供一个闭包以在触发通知时执行. 下面的示例使用闭包. 有关如何使用侦听器接口的详细信息,请参阅API文档.

Project evaluation

您可以在评估项目之前和之后立即收到通知. 一旦构建脚本中的所有定义均已应用,这可用于执行其他配置,或用于某些自定义日志记录或性能分析.

下面的示例将test任务添加到hasTests属性值为true的每个项目中.

例子6.将测试任务添加到每个具有特定属性集的项目
build.gradle
allprojects {
    afterEvaluate { project ->
        if (project.hasTests) {
            println "Adding test task to $project"
            project.task('test') {
                doLast {
                    println "Running tests for $project"
                }
            }
        }
    }
}
projectA.gradle
hasTests = true
build.gradle.kts
allprojects {
    afterEvaluate {
        if (extra["hasTests"] as Boolean) {
            println("Adding test task to $project")
            tasks.register("test") {
                doLast {
                    println("Running tests for $project")
                }
            }
        }
    }
}
projectA.gradle.kts
extra["hasTests"] = true
gradle -q test输出
> gradle -q test
Adding test task to project ':projectA'
Running tests for project ':projectA'

本示例使用Project.afterEvaluate()方法添加一个闭包,该闭包在评估项目后执行.

评估任何项目时,也可能会收到通知. 本示例对项目评估执行一些自定义日志记录. 请注意,无论项目评估成功还是失败,都将收到afterProject通知.

例子7.通知
build.gradle
gradle.afterProject { project ->
    if (project.state.failure) {
        println "Evaluation of $project FAILED"
    } else {
        println "Evaluation of $project succeeded"
    }
}
build.gradle.kts
gradle.afterProject {
    if (state.failure != null) {
        println("Evaluation of $project FAILED")
    } else {
        println("Evaluation of $project succeeded")
    }
}

Output of gradle -q test

> gradle -q test
Evaluation of root project 'buildProjectEvaluateEvents' succeeded
Evaluation of project ':projectA' succeeded
Evaluation of project ':projectB' FAILED

FAILURE: Build failed with an exception.

* Where:
Build file '/home/user/gradle/samples/groovy/projectB.gradle' line: 1

* What went wrong:
A problem occurred evaluating project ':projectB'.
> broken

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s
> gradle -q test
Evaluation of root project 'buildProjectEvaluateEvents' succeeded
Evaluation of project ':projectA' succeeded
Evaluation of project ':projectB' FAILED

FAILURE: Build failed with an exception.

* Where:
Build file '/home/user/gradle/samples/kotlin/projectB.gradle.kts' line: 1

* What went wrong:
broken

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s

您还可以向Gradle添加ProjectEvaluationListener来接收这些事件.

Task creation

将任务添加到项目后,您会立即收到通知. 在构建文件中使任务可用之前,可以使用它来设置一些默认值或添加行为.

以下示例在创建每个任务时设置其srcDir属性.

例子8.为所有任务设置某些属性
build.gradle
tasks.whenTaskAdded { task ->
    task.ext.srcDir = 'src/main/java'
}

task a

println "source dir is $a.srcDir"
build.gradle.kts
tasks.whenTaskAdded {
    extra["srcDir"] = "src/main/java"
}

val a by tasks.registering

println("source dir is ${a.get().extra["srcDir"]}")
gradle -qa输出
> gradle -q a
source dir is src/main/java

您还可以将Action添加到TaskContainer中以接收这些事件.

Task execution graph ready

填充任务执行图后,您会立即收到通知(请参阅" 通过DAG配置" ).

您还可以将TaskExecutionGraphListener添加到TaskExecutionGraph来接收这些事件.

Task execution

您可以在执行任何任务之前和之后立即收到通知.

下面的示例记录每个任务执行的开始和结束. 请注意,无论任务是成功完成还是因异常而失败,都会收到afterTask通知.

例子9.记录每个任务执行的开始和结束
build.gradle
task ok

task broken(dependsOn: ok) {
    doLast {
        throw new RuntimeException('broken')
    }
}

gradle.taskGraph.beforeTask { Task task ->
    println "executing $task ..."
}

gradle.taskGraph.afterTask { Task task, TaskState state ->
    if (state.failure) {
        println "FAILED"
    }
    else {
        println "done"
    }
}
build.gradle.kts
tasks.register("ok")

tasks.register("broken") {
    dependsOn("ok")
    doLast {
        throw RuntimeException("broken")
    }
}

gradle.taskGraph.beforeTask {
    println("executing $this ...")
}

gradle.taskGraph.afterTask {
    if (state.failure != null) {
        println("FAILED")
    } else {
        println("done")
    }
}

Output of gradle -q broken

> gradle -q broken
executing task ':ok' ...
done
executing task ':broken' ...
FAILED

FAILURE: Build failed with an exception.

* Where:
Build file '/home/user/gradle/samples/groovy/build.gradle' line: 5

* What went wrong:
Execution failed for task ':broken'.
> broken

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s
> gradle -q broken
executing task ':ok' ...
done
executing task ':broken' ...
FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':broken'.
> broken

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s

您还可以对TaskExecutionGraph使用TaskExecutionListener来接收这些事件.


1 . Gradle支持部分多项目构建(请参阅创作多项目构建 ).