我创建了this repo来准确再现我所看到的内容。我有一个项目,我正在使用Gradle构建。我想配置我的Gradle构建,以便运行:
./gradlew build
与跑步完全相同:
./gradlew clean build scalafmt shadowJar fizzbuzz
含义Gradle按以下顺序调用任务:
clean
build
(编译和运行单元测试)scalafmt
(运行将我的代码格式化为样式指南的工具)shadowJar
(创建一个自包含的可执行文件“fat”jar)fizzbuzz
(打印出“Fizzbuzz!”到控制台)根据Gradle docs on ordering tasks,似乎我可以使用shouldRunAfter
指定所有任务的排序......
如果你克隆我上面的repo,然后运行./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
(通过compileJava
调用check
)clean
fizzbuzz
scalafmt
shadowJar
所以即使考虑到我最近的变化,排序仍然是错误的......
答案 0 :(得分:2)
正如Oliver已经说过的那样,您需要将控制台输出置于doFirst
或doLast
闭包,否则将在定义任务时执行(在配置阶段)。
异常是由于扩展属性和任务都添加到Project
对象的范围,但是如果扩展属性和具有相同名称的任务(scalafmt
in这种情况)存在,将访问扩展属性。正如错误消息所示,您正在尝试访问shouldRunAfter
类型的对象上的PluginExtension
方法,该对象不存在。您需要确保访问该任务:
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!"
}
}
答案 1 :(得分:0)
该错误的可能原因之一可能是因为 Gradle 无法编译方法 task
或 shouldRunAfter
的 mustRunAfter
。
当存在与方法 task
或 shouldRunAfter
的 mustRunAfter
同名的其他类型实体(不是任务)时,也可能发生这种情况。在这种情况下,您可以使用语法 tasks['task1_name'].shouldRunAfter tasks['task2_name']
或 tasks['task1_name'].mustRunAfter tasks['task2_name']
来确保 Gradle 引用 task
类型的实体。