【问题标题】:Gradle shouldRunAfter not available for a taskGradle shouldRunAfter 不适用于任务
【发布时间】:2021-01-30 11:56:28
【问题描述】:

我创建了this repo 来准确再现我所看到的。我有一个正在使用 Gradle 构建的项目。我想配置我的 Gradle 构建以便运行:

./gradlew build

和跑步的效果完全一样:

./gradlew clean build scalafmt shadowJar fizzbuzz

意思是 Gradle 按以下顺序调用任务:

  1. clean
  2. build(编译和运行单元测试)
  3. scalafmt(运行将我的代码格式化为样式指南的工具)
  4. shadowJar(创建一个独立的可执行“胖”jar)
  5. 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 等。只是不确定如何完成我要。


更新

我对@9​​87654338@ 做了一些更改,现在看到了:

./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时,任务执行似乎是:

  1. build(通过check调用compileJava
  2. clean
  3. fizzbuzz
  4. scalafmt
  5. shadowJar

所以即使考虑到我最近的更改,排序仍然是错误的......

【问题讨论】:

  • 您需要将 println 包装在 doFirst/doLast 块中。
  • 感谢@OliverCharlesworth (+1) - 对异常有任何想法吗?

标签: gradle


【解决方案1】:

正如 Oliver 所说,您需要将控制台输出放入 doFirstdoLast 闭包,否则它将在定义任务时执行(在配置阶段)。

异常是由于扩展属性和任务都添加到Project对象的范围内,但是如果扩展属性和同名任务(本例中为scalafmt)同时存在,将访问扩展属性。正如错误消息告诉您的那样,您正在尝试访问 PluginExtension 类型的对象上的 shouldRunAfter 方法,但它不存在。您需要确保访问任务:

tasks['scalafmt'].shouldRunAfter build

更新

其实我以为你只需要解决这两个具体问题,但你的基本Gradle结构已经有了解决方案。

首先,shouldRunAftermustRunAfter 都不会真正导致任务被执行,它们只是定义了顺序如果两个任务都被执行(由命令行或任务引起依赖)。这就是为什么调用gradle build 时不会执行任务cleanscalafmtshadowJar 甚至fizzbuzz 的原因。因此,要解决您的第一个问题,您可以让您明确调用的 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 任务。这样您就不必纠结于任务顺序,因为命令行任务是连续执行的。

但是,这不是一个干净的解决方案。一个好的解决方案包括为所有真正相互依赖的任务定义任务依赖关系,以及通过命令行调用多个任务(您希望避免这种情况)。尤其是 cleanbuild 任务之间的硬连接绕过了 Gradle 平台的许多有用功能,例如增量构建。

关于更新中的第二点,fizzbuzz 任务首先运行是错误的。它根本没有运行。配置任务时会打印命令行输出。请将println 调用移至doFirst / doLast 闭包:

task fizzbuzz {
    doFirst {
        println "Fizzbuzz!"
    }
}

【讨论】:

  • 感谢@lu.koerfer (+1) - 请查看我的更新(我也将它们推送到我的仓库,以便您重现)。仍然看到一些关于任务排序的问题,有什么想法吗?再次感谢!
  • 我更新了我的答案以适合您的整个问题。此外,您还没有修复您的 fizzbuzz 任务。
  • 感谢@lu.koerfer(再次+1)-我喜欢您的fullBuild 解决方案,并愿意使用它。我对 repo 进行了更改(请参阅更新的build.gradle)以包含您的fullBuild 任务,并确保我将doLast 关闭到我的fizzbuzz 任务。但是仍然,当我运行./gradlew fullBuild 时,任务运行不正常(请参阅我更新后的控制台输出问题)。有任何想法吗?再次感谢!
  • 现在需要指定顺序:tasks['compileJava'].mustRunAfter 'clean'/tasks['scalafmt'].mustRunAfter 'build'/tasks['shadowJar'].mustRunAfter 'scalafmt'/tasks['fizzbuzz'].mustRunAfter 'shadowJar'
【解决方案2】:

导致该错误的可能原因之一可能是因为 Gradle 无法编译方法 shouldRunAftermustRunAftertask

当存在与方法shouldRunAftermustRunAftertask 同名的另一个类型的实体(不是任务)时,也可能发生这种情况。在这种情况下,您可以使用语法 tasks['task1_name'].shouldRunAfter tasks['task2_name']tasks['task1_name'].mustRunAfter tasks['task2_name'] 来确保 Gradle 引用的是 task 类型的实体。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-16
    • 2013-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-30
    • 2016-06-04
    相关资源
    最近更新 更多