Gradle命令行接口
除了IDE外,我们主要通过Gradle命令行接口来运行Gradle任务和管理Gradle项目。
下面是Gradle命令行使用的一些参考,熟悉后建议实际项目中使用Gradle Wrapper,gradle用法都可以替换为gradlew (macOS / Linux) 或gradlew.bat (windows)。
Gradle命令行格式
gradle [taskName...] [--option-name...]
选项可以在任务名称之前和之后
gradle [--option-name...] [taskName...]
多个任务之间用空格分隔
gradle [taskName1 taskName2...] [--option-name...]
接受值的选项和参数之间使用=号, 不用=也可以,但不推荐。
gradle [...] --console=plain
启用行为的选项具有长格式选项,其反义用--no-指定。
gradle [...] --build-cache
gradle [...] --no-build-cache
许多长格式选项有等价的简写,比如
gradle --help
gradle -h
命令行用法
执行任务(tasks)
执行任务是Gradle最重要也是最常用的,Gradle项目的整个构建流程就是由多个任务按照正确顺序执行来完成的。
在Gradle多项目构建中,有一个项目路径的概念。
项目路径具有以下模式: 它以一个可选的冒号(:)开头,表示根项目。
项目路径的其余部分是项目名称的冒号分隔序列,其中,下一个项目是前一个项目的子项目:
:sub-project-1
表示的是根项目下的sub-project-1子项目,默认对应的文件布局就是sub-project-1目录。
现在我们在根项目上执行一个名为myTask的任务:
$ gradle :myTask
这会执行myTask任务,以及它的所有依赖项。在执行它之前,先执行依赖项。
在根项目上执行子项目中的任务:
$ gradle :subproject:taskName 等效于 $ gradle subproject:taskName
此时,子项目前面的开头冒号可省略。
从根目录执行一个只包含名称的任务,那么整个项目中只要是这个名称的任务都会被执行。
比如我们在根项目和app、app2子项目的构建脚本文件里都注册了myTask任务,从根项目执行下面命令:
$ gradle myTask
> Configure project :
> Configure project :app
> Task :myTask UP-TO-DATE
> Task :app:myTask UP-TO-DATE
> Task :app2:myTask UP-TO-DATE
可以看到根项目和子项目的myTask都执行了。
所以,如果不确定某个任务是不是唯一的,最好还是使用项目路径的形式,这样我们可以明确知道要执行什么任务。
从子项目里执行任务:
$ cd subproject
$ gradle taskName
$ gradle myTask
> Task :app:myTask UP-TO-DATE
Skipping task ':app:myTask' as it has no actions.
BUILD SUCCESSFUL in 1s
从子目录使用Gradle Wrapper命令为 ../gradlew taskName
只执行了子项目中的任务,和在根目录下不同,后者会执行全部子项目中的taskName。
子项目中如果使用冒号开头,比如$ gradle :taskName,执行的则是根项目的taskName,因为前面说过冒号代表根项目。
为任务提供选项
在任务名后加空格,--作为选项的前缀
$ gradle exampleTask --exampleOption=exampleValue
从内置选项中消除任务选项的歧义
Gradle不会阻止任务使用与内建选项冲突的选项,像 --profile 或 --help。
但允许我们在命令行中的任务名称前使用--分隔符来消除内置选项和任务选项的冲突:
$ gradle [--built-in-option-name...] -- [taskName...] [--task-option-name...]
假如mytask任务会接受一个名为profile的选项:
在gradle mytask --profile中, Gradle接受--profile作为内置的gradle选项
在gradle -- mytask --profile=value中, Gradle传递--profile作为任务选项
执行多个任务
我们可以指定多个任务,任务的依赖关系决定了执行顺序,没有依赖的任务可能会在命令行中列出的顺序之前执行。
比如我们在app的构建脚本中定义taskA,taskB,taskA依赖taskB:
tasks.register("taskB") {
println("register app taskB")
doFirst {
// Task action = Execution code
// Run before exiting actions
println("app taskB doFirst")
}
}
tasks.register("taskA") {
println("register app taskA")
dependsOn(tasks.taskB)
doFirst {
// Task action = Execution code
// Run before exiting actions
println("app taskA doFirst")
}
}
然后命令行中的任务顺序是taskA taskB,执行:
$ gradle taskA taskB
register app taskA
register app taskB
> Task :app:taskB
app taskB doFirst
> Task :app:taskA
app taskA doFirst
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
看到taskB先执行了,因为依赖要求执行taskA之前,先要执行taskB。
如果是相互独立的任务,以下命令会按照他们在命令行中的顺序执行:
$ gradle test deploy
命令行顺序安全性
有时候,两个task之间没有依赖关系,但是对两个task的执行顺序却有所要求。
例如,下面的命令中,clean和build分别是负责清理和构建的任务,没有依赖关系,Gradle总是会先执行clean再build,因为build之后执行clean是不对的,clean会删除build的输出。
$ gradle clean build 或者 $ gradle build clean
Gradle会通过内部规则排序任务来尊重任务顺序的安全性。
任务排序和任务依赖之间的主要区别在于,排序规则不会影响将执行哪些任务,只会影响它们的执行顺序。
任务排序在许多场景中都很有用:
强制执行任务的顺序:比如build 永远不会在clean 之前运行。
在构建的早期运行构建验证:例如,在开始发布构建工作之前验证我是否拥有正确的凭据。
通过在长时间验证任务之前运行快速验证任务来更快地获得反馈:例如,单元测试应该在集成测试之前运行。
聚合特定类型的所有任务的结果的任务:例如测试报告任务组合所有已执行测试任务的输出。
在 Gradle 中,提供了两个用于控制任务执行顺序的方法mustRunAfter 和 shouldRunAfter 。这两个方法都用于定义任务之间的依赖关系,但它们有不同的行为和语义。
mustRunAfter
用于确保一个任务在另一个任务之后运行。如果两个任务之间没有直接的依赖关系,并且你希望一个任务在另一个任务之后执行,你可以使用 mustRunAfter。这个方法创建了一个“软依赖”,即如果可能的话,Gradle 会尽量保证这个顺序,但它并不强制要求这个顺序。
如果因为某些原因(比如任务之间的直接依赖关系或其他任务的执行顺序),Gradle 不能保证 mustRunAfter 指定的顺序,它仍然会尝试执行构建。在这种情况下,Gradle 可能会输出一个警告,说明它没有按照预期的顺序执行任务。
shouldRunAfter
shouldRunAfter 与 mustRunAfter 类似,也是用于指定任务执行顺序的。然而,shouldRunAfter 创建的是一个“更软的依赖”。它表达的是一个建议,告诉 Gradle 如果可能的话,尽量按照这个顺序执行任务。但是,如果因为其他原因(如直接依赖关系)导致这个顺序不能实现,Gradle 会忽略这个建议,并且不会输出任何警告。
在实践中,使用 mustRunAfter 和 shouldRunAfter 时需要谨慎,因为过度使用这些软依赖可能会导致构建逻辑变得复杂且难以维护。最好通过明确的依赖关系(使用 dependsOn 或其他依赖配置方法)来定义任务之间的顺序。这样,Gradle 可以更准确地确定任务的执行顺序,并且构建逻辑也更加清晰和可预测。
从执行中排除任务
我们可以使用-x或--exclude-task命令行选项并提供任务名称排除一个任务,排除的任务不会被执行:
$ gradle dist --exclude-task test
> Task :compile
compiling source
> Task :dist
building the distribution
BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed
Figure 1. Simple Task Graph
在这个例子中,我们看到test没有执行,尽管dist依赖它。test任务的依赖项,比如compileTest,也不会被执行。其他任务所依赖的test的依赖项(例如compile)仍然会被执行。
排除test,会排除test自身以及只被test依赖的任务。
强制执行任务
Gradle提供了许多优化方式来加速构建, Gradle会检查这些任务,如果任务能够重用上次执行的输出,就会跳过执行,标记为UP-TO-DATE或FROM-CACHE等。
命令行选项--rerun-tasks可以告诉Gradle忽略这些up-to-date之类的检查, 重新执行任务以及任务的依赖项。
$ gradle test --rerun-tasks
这有点像执行 gradle clean test,但不删除build的输出。
或者使用内置的task选项--rerun告诉Gradle需要重新运行一个特定的任务。
在一个任务失败后继续构建
默认情况下,当任何任务执行失败时,Gradle 都会立即中止执行并标记构建为失败。这种设计有几个优点:
- 快速反馈:当任务失败时立即停止构建,开发者可以更快地得到反馈,从而可以更早地开始调查问题。
- 避免级联失败:有些任务可能依赖于其他任务的输出。如果一个任务失败,那么依赖于它的任务也很可能失败。继续执行这些后续任务可能会产生额外的错误,这些错误可能会掩盖原始问题的真正原因。
- 简化错误诊断:当构建在第一个失败的任务处停止时,开发者可以更容易地确定问题的根源,而不是在多个失败的任务中迷失。
然而,在某些特殊情况下,开发者可能希望即使某些任务失败,也要继续执行其他任务。例如,他们可能想要收集多个独立任务的输出,或者他们可能正在运行一组测试,并希望即使某些测试失败也继续执行其他测试。
在这种情况下,Gradle 提供了 --continue 或 -C 选项,允许开发者覆盖默认行为。
使用 --continue 选项,Gradle 会尝试执行每一个任务,前提是该任务所有依赖项都执行成功,失败的,那么依赖它的其他任务不会执行(避免出现上面说的级联失败)。
$ gradle test --continue
例如,如果被测代码中存在编译错误,则test不会运行,因为测试任务依赖于compilation任务。Gradle会在构建结束时输出每个遇到的失败。
需要注意,即使使用了 --continue,失败的任务仍然会被标记为失败,并且整个构建过程最终也会被标记为失败。
名称缩写
在命令行上指定任务时,我们可以不必提供任务的全名,只提供足够的任务名称来唯一地标识任务。
例如,gradle che就足以让Gradle识别为要执行check任务。
缩写同样可以应用在项目名称上
运行gradle lib:che命令来执行library子项目里的check任务。
对于更复杂的缩写,可以使用驼峰大小写模式。这些模式被扩展以匹配camel case和kebab case名称。例如,模式foBa(或fB)匹配fooBar和foo-bar。
更具体一点,就是您可以使用gradle mAL:cT命令在my-awesome-library子项目中运行compileTest任务。
$ gradle mAL:cT
> Task :my-awesome-library:compileTest
compiling unit tests
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
缩写也可以与-x (--exclude-task)命令行选项一起使用。
跟踪名称扩展
对于复杂的项目,使用缩写名时,一个错别字就可能导致执行意想不到的任务。
使用 INFO 或者 更详细的 日志记录,输出中将包含有关项目和任务名称展开的额外信息。
比如在前面例子中执行的gradle mAL:cT命令,我们将看到以下日志信息:
No exact project with name ':mAL' has been found. Checking for abbreviated names.
Found exactly one project that matches the abbreviated name ':mAL': ':my-awesome-library'.
No exact task with name ':cT' has been found. Checking for abbreviated names.
Found exactly one task name, that matches the abbreviated name ':cT': ':compileTest'.
常见任务
在Gradle中,有一些常见的任务是经常在构建过程中使用的。这些任务涵盖了编译源代码、运行测试、生成文档、打包发布等构建生命周期的不同阶段。
下面是一些Gradle中常见的任务:
执行构建
这是一个聚合任务,它会根据项目的类型和配置来执行所有必要的构建任务。对于Java项目,这通常包括编译源代码、运行测试、生成文档和打包JAR文件等:
$ gradle build
运行应用程序
该任务组装应用程序并执行一些脚本或二进制文件:
$ gradle run
运行所有检查
该任务执行所有验证任务(包括测试和检查):
$ gradle check
清理输出
这个任务会删除构建目录(通常是build/),移除所有之前构建产生的文件和目录,从而导致后续任务执行需要大量额外构建时间:
$ gradle clean
测试任务
运行项目的单元测试:
$ gradle test
项目报告
Gradle提供了几个内置任务来显示构建的具体细节,这对我们理解构建结构和依赖关系以及调试问题很有用。
查看项目结构
这个任务会列出当前 Gradle 多项目构建的所有子项目,以层次结构显示:
$ gradle projects
> Task :projects
------------------------------------------------------------
Root project 'HelloGradle'
------------------------------------------------------------
Root project 'HelloGradle'
+--- Project ':app'
\--- Project ':app2'
查看可用任务清单
运行这个任务会列出当前项目的所有可见任务(那些分配了group的任务):
$ gradle tasks
> Task :tasks
Build tasks
-----------
assemble - Assembles the outputs of this project.
Build Setup tasks
-----------------
init - Initializes a new Gradle build.
Distribution tasks
------------------
assembleDist - Assembles the main distributions
Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.
这些任务是按照分组显示的,比如上面的assemble任务属于任务组Build Setup,短横线后面的“Assembles the outputs of this project. ”是这个任务的描述。
对于那些没有指定group的任务,我们称为隐藏任务,通过在tasks任务中使用--all选项可以列举出所有可用任务,包括隐藏任务,这些没有group的任务会列出在Other分组下, 比如myHideTask:
$ gradle tasks --all
> Task :tasks
Build tasks
-----------
assemble - Assembles the outputs of this project.
Build Setup tasks
-----------------
init - Initializes a new Gradle build.
Distribution tasks
------------------
assembleDist - Assembles the main distributions
Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.
Other tasks
-----------
compileJava - Compiles main Java source.
myHideTask
我们可以使用--group选项只显示这个分组的任务:
$ gradle tasks --group="build setup"
> Task :tasks
------------------------------------------------------------
Tasks runnable from root project 'HelloGradle'
------------------------------------------------------------
Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.
To see all tasks and more detail, run gradlew tasks --all
To see more detail about a task, run gradlew help --task <task>
BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed
显示任务使用详情
运行 gradle help --task someTask 查看一个特定任务的详细信息:
$ gradle -q help --task compileJava
Detailed task information for compileJava
Paths
:compileJava
:app2:compileJava
:app:compileJava
Type
JavaCompile (org.gradle.api.tasks.compile.JavaCompile)
Options
--rerun Causes the task to be re-run even if up-to-date.
Description
Compiles main Java source.
Group
-
这份信息里包含了完整的任务路劲、任务类型、可能的命令行任务选项、描述等。
显示项目依赖关系
运行依赖项任务将为您提供所选项目的依赖项列表,该列表按配置细分。对于每个配置,该配置的直接依赖关系和传递依赖关系显示在树状图中。
$ gradle dependencies
> Task :app:dependencies
------------------------------------------------------------
Project ':app'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- project :model
| \--- org.json:json:20220924
+--- com.google.inject:guice:5.1.0
| +--- javax.inject:javax.inject:1
| +--- aopalliance:aopalliance:1.0
| \--- com.google.guava:guava:30.1-jre -> 28.2-jre
| +--- com.google.guava:failureaccess:1.0.1
| +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
| +--- com.google.code.findbugs:jsr305:3.0.2
| +--- org.checkerframework:checker-qual:2.10.0 -> 3.28.0
| +--- com.google.errorprone:error_prone_annotations:2.3.4
| \--- com.google.j2objc:j2objc-annotations:1.3
+--- com.google.inject:guice:{strictly 5.1.0} -> 5.1.0 (c)
+--- org.json:json:{strictly 20220924} -> 20220924 (c)
+--- javax.inject:javax.inject:{strictly 1} -> 1 (c)
+--- aopalliance:aopalliance:{strictly 1.0} -> 1.0 (c)
+--- com.google.guava:guava:{strictly [28.0-jre, 28.5-jre]} -> 28.2-jre (c)
+--- com.google.guava:guava:{strictly 28.2-jre} -> 28.2-jre (c)
+--- com.google.guava:failureaccess:{strictly 1.0.1} -> 1.0.1 (c)
+--- com.google.guava:listenablefuture:{strictly 9999.0-empty-to-avoid-conflict-with-guava} -> 9999.0-empty-to-avoid-conflict-with-guava (c)
+--- com.google.code.findbugs:jsr305:{strictly 3.0.2} -> 3.0.2 (c)
+--- org.checkerframework:checker-qual:{strictly 3.28.0} -> 3.28.0 (c)
+--- com.google.errorprone:error_prone_annotations:{strictly 2.3.4} -> 2.3.4 (c)
\--- com.google.j2objc:j2objc-annotations:{strictly 1.3} -> 1.3 (c)
Gradle守护进程选项
使用Gradle守护进程来运行构建
Gradle Daemon是一个后台进程,它在构建过程中为Gradle提供性能优化。当Gradle开始一个构建任务时,默认会启动一个Gradle Daemon(如果没有守护进程在运行或现有守护进程繁忙)来处理构建任务。构建完成后,Daemon进程会在后台保持运行,并处于IDLE空闲状态。这样在下一次构建时,会先尝试重用空闲的Daemon,不必重新创建和配置构建环境,从而大大提高了构建速度。
--daemon 启用(默认行为)
--no-daemon 不启用
可以在gradle.properties文件中设置以下属性禁用Gradle Daemon:
org.gradle.daemon=false
停止所有正在运行的Gradle Daemons
Gradle Daemon是安全的,并且通常不需要用户干预。然而在某些情况下,如遇到内存泄漏或守护进程异常崩溃,你可能需要手动停止Gradle Daemon:
$ gradle --stop
Stopping Daemon(s)
1 Daemon stopped
查看正在运行和最近停止的gradle守护进程
$ gradle --status
PID STATUS INFO
93032 IDLE 8.2
92212 STOPPED (stop command received)
日志选项
设置日志级别
在Gradle构建时,我们在控制台会看到各种日志信息,可以使用以下选项自定义Gradle日志记录的详细程度,依次是最不详细到最详细。
-q, --quiet 只记录错误
-w, --warn 设置日志级别为“警告”
-i, --info 设置日志级别为“信息”
-d, --debug 调试模式(最详细的日志级别,包括正常的堆栈跟踪)
也可以通过Gradle 属性设置:
-Dorg.gradle.logging.level=(quiet,warn,lifecycle,info,debug)
如果不指定,默认日志级别是lifecycle,通常包含了关于构建过程的重要信息,如任务的开始和结束,以及构建过程中的关键事件。
引导创建新项目
在项目目录下运行内置的gradle init任务来创建一个新的Gradle构建:
$ gradle init
运行内置的gradle wrapper任务来创建Gradle Wrapper :
$ gradle wrapper