我创建了这个存储库来准确重现我所看到的。我有一个项目正在与 Gradle 一起构建。我想配置我的 Gradle 构建,以便运行:
./gradlew build
具有与跑步完全相同的效果:
./gradlew clean build scalafmt shadowJar fizzbuzz
含义 Gradle 按以下顺序调用任务:
clean
build
(编译和运行单元测试)scalafmt
(运行将我的代码格式化为样式指南的工具)shadowJar
(创建一个独立的可执行"胖"罐)fizzbuzz
(将"Fizzbuzz!"打印到控制台)
根据 Gradle 关于排序任务的文档,我似乎可以使用shouldRunAfter
来指定所有任务的排序......
如果您克隆上面的存储库,然后运行./gradlew build
您将获得以下输出:
./gradlew build
Fizzbuzz!
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/myUser/thelab/idea-scala-hate-each-other/build.gradle' line: 65
* What went wrong:
A problem occurred evaluating root project 'idea-scala-hate-each-other'.
> Could not find method shouldRunAfter() for arguments [task ':build'] on cz.alenkacz.gradle.scalafmt.PluginExtension_Decorated@6b24ddd7.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 10.983 secs
因此,即使我指定最后运行fizzbuzz
...它先跑了!我的配置的其余部分(显然)存在错误。而且我不确定如何"钩住"clean
这样即使我运行./gradlew build
它也会首先运行clean
.
我可以接受一个解决方案,它要求我编写自己的"包装器任务"来实现我想要的顺序,然后调用它,比如说,通过./gradlew buildMyApp
等。只是不知道如何完成我想要的。
更新
我对build.gradle
进行了一些更改,现在看到以下内容:
./gradlew fullBuild
:compileJava UP-TO-DATE
:compileScala
:processResources UP-TO-DATE
:classes
:jar
:startScripts
:distTar
:distZip
:assemble
:compileTestJava UP-TO-DATE
:compileTestScala UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build
:clean
:fizzbuzz
Fizzbuzz!
:scalafmt
:shadowJar
:fullBuild
因此,当我运行./gradlew fullBuild
任务执行似乎是:
build
(通过check
调用compileJava
)clean
fizzbuzz
scalafmt
shadowJar
因此,即使考虑到我最近的更改,排序仍然是错误的......
正如 Oliver 已经说过的,您需要将控制台输出放入doFirst
或doLast
闭包,否则它将在定义任务时执行(在配置阶段)。
异常是由于扩展属性和任务都添加到Project
对象的作用域中,但如果扩展属性和具有相同名称的任务(在本例中scalafmt
)都存在,则将访问扩展属性。正如错误消息告诉您的那样,您正在尝试访问PluginExtension
类型的对象上的shouldRunAfter
方法,而该对象不存在。您需要确保访问任务:
tasks['scalafmt'].shouldRunAfter build
更新
实际上,我认为您只需要解决两个特定问题,但已经为您的基本 Gradle 结构提供了解决方案。
首先,shouldRunAfter
和mustRunAfter
实际上并不会导致任务被执行,它们只定义两个任务都执行时的顺序(由命令行或任务依赖关系引起)。这就是为什么clean
、scalafmt
、shadowJar
甚至fizzbuzz
的任务在调用gradle build
时不执行的原因。因此,要解决您的第一个问题,您可以让显式调用的build
任务依赖于它们:
build.dependsOn 'clean', 'scalafmt', 'shadowJar', 'fizzbuzz'
但是任务依赖项将始终在父任务之前运行,因此所有任务都将在build
任务之前执行。这应该不是问题,因为build
任务只不过是收集所有必需构建步骤的任务依赖项。您还需要定义任务依赖关系之间的顺序,例如clean
和父任务build
,但主要是在现有的任务依赖关系之间,例如compileJava
.否则clean
可以在compileJava
之后运行,这将删除编译的文件。
另一种选择是定义一个新任务,这取决于您要执行的所有任务:
task fullBuild {
dependsOn 'clean', 'build', 'scalafmt', 'shadowJar', 'fizzbuzz'
}
这仍然需要定义任务和现有任务依赖关系之间的顺序,例如
compileJava.mustRunAfter 'clean'
[...]
请注意,现在您必须从命令行调用gradle fullBuild
。如果你真的只需要通过命令行调用gradle build
,并且在实际build
任务之后仍然执行一些任务,你可以在settings.gradle
文件中使用一个小技巧:
startParameter.with {
if (taskNames == ['build']) {
taskNames = ['clean', 'build', 'scalafmt', 'shadowJar', 'fizzbuzz']
}
}
这段代码检查您通过命令行输入的任务名称输入,如果它仅包含build
任务,则会替换它。这样,您就不必为任务顺序而苦恼,因为命令行任务是连续执行的。
但是,这不是一个干净的解决方案。一个好的解决方案包括为所有真正相互依赖的任务定义任务依赖关系,并通过命令行调用多个任务(您希望避免这种情况)。特别是clean
和build
任务之间的硬连接绕过了 Gradle 平台的许多有用功能,例如增量构建。
关于更新中的第二点,首先运行fizzbuzz
任务是错误的。它根本没有运行。配置任务时会打印命令行输出。请将println
调用移至doFirst
/doLast
关闭:
task fizzbuzz {
doFirst {
println "Fizzbuzz!"
}
}
该错误的可能原因之一可能是因为 Gradle 无法shouldRunAfter
或mustRunAfter
编译方法的task
。
当有另一个其他类型的实体(不是任务)与方法shouldRunAfter
或mustRunAfter
的task
同名时,也可能发生这种情况。在这种情况下,您可以使用语法tasks['task1_name'].shouldRunAfter tasks['task2_name']
或tasks['task1_name'].mustRunAfter tasks['task2_name']
来确保 Gradle 引用的是task
类型实体。