本文介绍了Gradle shouldRunAfter不适用于任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了此存储库,以完全重现我的意思看到.我有一个正在使用Gradle构建的项目.我想配置我的Gradle构建以使其运行:

I created this repo to reproduce exactly what I'm seeing. I have a project that I'm building with Gradle. I would like to configure my Gradle build so that running:

./gradlew build

具有与跑步完全相同的效果:

Has the exact same effect as running:

./gradlew clean build scalafmt shadowJar fizzbuzz

Meaning Gradle按以下顺序调用任务:

Meaning Gradle calls tasks in the following order:

  1. clean
  2. build(编译并运行单元测试)
  3. scalafmt(运行一个将我的代码格式化为样式指南的工具)
  4. shadowJar(创建一个自包含的可执行文件"fat" jar)
  5. fizzbuzz(将"Fizzbuzz!"输出到控制台)
  1. clean
  2. build (compile & run unit tests)
  3. scalafmt (run a tool that formats my code to a styleguide)
  4. shadowJar (creates a self-contained executable "fat" jar)
  5. fizzbuzz (prints out "Fizzbuzz!" to the console)

根据为订购任务分级文档,看来我可以使用shouldRunAfter指定所有任务的顺序...

According to the Gradle docs on ordering tasks, it seems that I can use shouldRunAfter to specify ordering on all tasks...

如果您在上面克隆我的仓库,然后运行./gradlew build,您将获得以下输出:

If you clone my repo above and then run ./gradlew build you'll get the following output:

./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.

So even though I specify fizzbuzz to run last...its running first! And there are (obviously) errors with the rest of my config. And I'm not sure how to "hook" clean so that even when I run ./gradlew build it first runs clean.

我会接受一个解决方案,该解决方案要求我编写自己的包装器任务"以实现所需的顺序,然后通过./gradlew buildMyApp等调用它.我想要.

I'd be OK with a solution that requires me to write my own "wrapper task" to achieve the order I want, and then invoke it, say, via ./gradlew buildMyApp, etc. Just not sure how to accomplish what I want.

我对build.gradle进行了一些更改,现在看到了:

I made some changes to build.gradle and am now seeing this:

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

So when I run ./gradlew fullBuild task execution seems to be:

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

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

So the ordering is still wrong even given my most recent changes...

推荐答案

如Oliver所述,您需要将控制台输出置于doFirstdoLast闭包中,否则将在定义任务时执行(在配置阶段).

As Oliver already stated, you need to put the console output to a doFirst or doLast closure, otherwise it will be executed when the task is defined (during configuration phase).

此异常是由于以下事实引起的:将扩展属性和任务都添加到了 Project 对象,但是如果扩展属性和具有相同名称的任务(在本例中为scalafmt)同时存在,则将访问扩展属性.如错误消息所告诉您的那样,您正在尝试访问PluginExtension类型的对象上的shouldRunAfter方法,该对象不存在.您需要确保访问任务:

The exception is caused by the fact that both extension properties and tasks are added to the scope of the Project object, but if both an extension property and a task with the same name (scalafmt in this case) exist, the extension property will be accessed. As the error message tells you, you are trying to access the shouldRunAfter method on an object of the type PluginExtension, where it does not exist. You need to make sure to access the task:

tasks['scalafmt'].shouldRunAfter build


更新

实际上,我认为您只需要解决两个特定的问题,但是已经为基本的Gradle结构提供了解决方案.


Update

Actually, I thought that you only need the two specific problems to be solved, but already have a solution for your basic Gradle structure.

首先,shouldRunAftermustRunAfter实际上都不会导致任务被执行,它们仅定义如果两个任务都被执行的顺序(由命令行或任务引起)依赖性).这就是为什么如果您调用gradle build不能执行任务cleanscalafmtshadowJar甚至fizzbuzz的原因.因此,要解决您的第一个问题,可以让您显式调用的build任务依赖于它们:

First of all, both shouldRunAfter and mustRunAfter do not actually cause a task to be executed, they only define the order if both tasks are executed (caused by command line or a task dependency). This is the reason why the tasks clean, scalafmt, shadowJar and even fizzbuzz are not executed if you call gradle build. So, to solve your first problem, you could let the build task, which you call explicitly, depend on them:

build.dependsOn 'clean', 'scalafmt', 'shadowJar', 'fizzbuzz'

但是任务依赖项将始终在父任务之前运行,因此所有任务将在build任务之前执行.这应该不成问题,因为build任务只需要为所有必需的构建步骤收集任务依赖项即可.您还需要不仅在任务依赖关系之间定义顺序,例如clean和父任务build,但主要在现有任务依赖项之间,例如compileJava.否则clean可以在compileJava之后运行,这将删除编译的文件.

But a task dependency will always run before the parent task, so all tasks will be executed before the build task. This should not be a problem, since the build task does nothing more than collecting task dependencies for all required build steps. You would also need to define the order not only between of your task dependencies, e.g. clean and the parent task build, but mainly between the existing task dependencies, e.g. compileJava. Otherwise clean could run after compileJava, which would delete the compiled files.

另一种选择是定义一个新任务,然后该任务取决于您要执行的所有任务:

Another option would be to define a new task, which then depends on all the tasks you want to execute:

task fullBuild {
    dependsOn 'clean', 'build', 'scalafmt', 'shadowJar', 'fizzbuzz'
}

这仍然需要定义任务和现有任务依赖项之间的顺序,例如

This would still require to define the order between your tasks and the existing task dependencies, e.g.

compileJava.mustRunAfter 'clean'
[...]

请注意,现在您必须从命令行调用gradle fullBuild.如果确实只需要通过命令行调用gradle build并仍在实际build任务之后之后执行一些任务,则可以在settings.gradle文件中使用一些技巧:

Please note, that now you would have to call gradle fullBuild from command line. If you really need to only call gradle build via command line and still execute some tasks after the actual build task, you could use a little trick in your settings.gradle file:

startParameter.with {
    if (taskNames == ['build']) {
        taskNames = ['clean', 'build', 'scalafmt', 'shadowJar', 'fizzbuzz']
    }
}

如果仅包含build任务,则这段代码检查您通过命令行输入的任务名称,并将其替换.这样,您就不必为任务顺序而烦恼,因为命令行任务是连续执行的.

This piece of code checks the task name input you entered via command line and replaces it, if it only contains the build task. This way you would not have to struggle with the task order, since the command line tasks are executed consecutively.

但是,这不是一个干净的解决方案.一个好的解决方案包括为确实相互依赖的所有任务定义任务依赖关系,并通过命令行调用多个任务(您要避免).特别是cleanbuild任务之间的硬连接绕过了Gradle平台的许多有用功能,例如增量构建.

However, this is not a clean solution. A good solution would include the definition of task dependencies for all tasks that really depend on each other and the invocation of multiple tasks via command line (which you want to avoid). Especially the hard connection between the clean and the build task bypasses a lot of useful features from the Gradle platform, e.g. incremental builds.

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

Regarding your second point in the update, it is wrong that the fizzbuzz task is running first. It is not running at all. The command line output is printed when the task is configured. Please move the println call to a doFirst / doLast closure:

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

这篇关于Gradle shouldRunAfter不适用于任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-19 08:22