https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:projects_and_tasks
https://docs.gradle.org/current/userguide/build_lifecycle.html
这是来自官网的学习笔记,含有很多复制粘贴,链接也都贴在上面和文章超链接里了
The most notable restriction is that dependency management currently only supports Maven- and Ivy-compatible repositories and the filesystem. But, Gradle makes it easy to build common types of project — say Java libraries — by adding a layer of conventions and prebuilt functionality through plugins.
Initialization
Sets up the environment for the build and determine which projects will take part in it.
Configuration
Constructs and configures the task graph for the build and then determines which tasks need to run and in which order, based on the task the user wants to run.
Execution
Runs the tasks selected at the end of the configuration phase.
These phases form Gradle’s Build Lifecycle.
Declarative code: Built-in, language-agnostic DSL elements (e.g.
Project.dependencies{}
orProject.repositories{}
) or DSLs exposed by plugins
Imperative code: Conditional logic or very complex task action implementations. (e.g.doLast {}
anddoFirst {}
)
The end goal of every build script should be to only contain declarative language elements which makes the code easier to understand and maintain. Imperative logic should live in binary plugins and which in turn is applied to the build script.
这里没有很理解,像Imperative code,应该是在execution phase才被evaluate的吧,那为什么还说,为了避免configuration phase任务繁重,要避免把imperative code写在build script里呢?即使写在里面,它也不会在configuration phase被evaluate吧。是不是因为不是所有的imperative code都会在execution phase被evaluate?
Most builds have some special requirements that mean you need to add custom build logic.
Gradle provides several mechanisms that allow you to extend it, such as:
View Gradle’s build scripts as executable code is correct.
Understanding how the syntax of the build script maps to Gradle’s API.
Everything in Gradle sits on top of two basic concepts: projects and tasks.
Each project is made up of one or more tasks. A task represents some atomic piece of work which a build performs.
build.gradle file == a build script
, strictly, build.gradle file is a build configuration script.
build.gradle
task hello {
doLast {
println 'Hello world!'
}
}
Above build script defines a single task, called
hello
, and adds an action to it
- Gradle’s build scripts give you the full power of Groovy and Kotlin
task taskX {
dependsOn 'taskY'
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
// output of "gradle -q taskX"
> gradle -q taskX
taskY
taskX
… and more
Beside the build script files, Gradle defines a settings file - settings.gradle
, indicating multi-project hierarchy.
The settings file is executed during the initialization phase.
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.'
}
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
include 'project1', 'project2:child', 'project3:child1'
- The include method takes project paths as arguments.
- A path ‘services:api’ is mapped by default to a folder ‘services/api’ (relative from the project root)
- The inclusion of the path ‘services:hotels:api’ will result in creating 3 projects: ‘services’, ‘services:hotels’ and ‘services:hotels:api’.
includeFlat 'project3', 'project4'
- The includeFlat method takes directory names as an argument.
- The location of these directories are considered as child projects of the root project in the multi-project tree.
The multi-project tree created in the settings file is made up of so called project descriptors. You can modify these descriptors in the settings file at any time. To access a descriptor you can do:
println rootProject.name
println project(':projectA').name
rootProject.name = 'main'
project(':projectA').projectDir = new File(settingsDir, '../my-project-a')
project(':projectA').buildFileName = 'projectA.gradle'
Gradle needs to determine whether the project you are in is a subproject of a multi-project build or not. Of course, if it is a subproject, only the subproject and its dependent projects are built, but Gradle needs to create the build configuration for the whole multi-project build (see Authoring Multi-Project Builds).
For a single project build, the workflow of the after initialization
phases are pretty simple. The build script is executed against the project object that was created during the initialization phase. Then Gradle looks for tasks with names equal to those passed as command line arguments, if these task names exist, they are executed as a separate build in the order you have passed them.
Your build script can receive notifications as the build progresses through its lifecycle. These notifications generally take two forms:
The examples below use closures.
Below is an example which adds a test task to each project with “hasTests == True”:
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
Output of gradle -q test
> gradle -q test
Adding test task to project ':projectA'
Running tests for project ':projectA'
This example uses method Project.afterEvaluate() to add a closure which is executed after the project is evaluated.
This example performs some custom logging of project evaluation. Notice that the afterProject notification is received regardless of whether the project evaluates successfully or fails with an exception.
build.gradle
gradle.afterProject { project ->
if (project.state.failure) {
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
You can receive a notification immediately after a task is added to a project. This can be used to set some default values or add behaviour before the task is made available in the build file.
The following example sets the srcDir property of each task as it is created.
build.gradle
tasks.whenTaskAdded { task ->
task.ext.srcDir = 'src/main/java'
}
task a
println "source dir is $a.srcDir"
Output of gradle -q a
gradle -q a
source dir is src/main/java
You can receive a notification immediately after the task execution graph has been populated.
You can also add a TaskExecutionGraphListener
to the TaskExecutionGraph
to receive these events.
You can receive a notification immediately before and after any task is executed.
The following example logs the start and end of each task execution. Notice that the afterTask notification is received regardless of whether the task completes successfully or fails with an exception.
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"
}
}
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
You can also use a TaskExecutionListener
to the TaskExecutionGraph
to receive these events.
https://guides.gradle.org/creating-multi-project-builds/
In a multi-project you can use the top-level build script (also known as the root project) to configure as much commonality as possible, leaving sub-projects to customize only what is necessary for that subproject.
Gradle automatically detected that there is a build task in greeting-library and executed it. This is one of the powerful features of a Gradle multi-project build.