我需要创建一个提供交互式任务的插件,如下所示:
> ./gradlew analyzeSomething --configuration compileClasspath
在这里,
--configuration
参数应该是项目中用于分析的配置名称。但是,此分析需要从配置中计算出一些数据,这可能会很昂贵。因此,我想缓存此数据,但是自然地,如果配置的内容发生更改(例如,用户添加了依赖性),将导致缓存无效。理想情况下,我会利用内置的Gradle任务依赖性机制,该机制将跟踪任务依赖性,并且除非其输入已更改,否则不允许运行计算。
这是我想出的解决方案,但是没有用。首先,在
AnalyzeSomething
类中声明两个属性:abstract class AnalyzeSomethingTask : DefaultTask() {
@get:Internal
@get:Option(option = "configuration", description = "Configuration")
abstract val configuration: Property<String>
@get:Internal
abstract val dataCacheFile: RegularFileProperty
}
其次,创建任务规则以实例化执行昂贵计算的任务:
abstract class PrecomputeTask : DefaultTask() {
@get:InputFiles
abstract val classpath: ConfigurableFileCollection
@get:OutputFile
abstract val outputFile: RegularFileProperty
}
project.tasks.addRule("Pattern precompute<Configuration>") {
val taskName = this
if (taskName.startsWith("precompute")) {
val configurationName = taskName.removePrefix("precompute").decapitalize()
val configuration = project.configurations.getByName(configurationName)
project.tasks.register<PrecomputeTask>(taskName) {
classpath.from(configuration)
outputFile.set(temporaryDir.resolve("data.txt"))
}
}
}
最后,创建
analyzeSomething
任务,并在project.afterEvalute
块中(因此已经设置了该任务的所有属性,包括命令行上的属性),在analyzeSomething
和一个规则生成的任务之一之间建立了依赖关系:val analyzeSomething = project.tasks.register<AnalyzeSomethingTask>("analyzeSomething")
project.afterEvaluate {
analyzeSomething.configure {
val precomputeTaskName = "precompute" + configuration.get().capitalize()
dataCacheFile.set(
project.tasks.named<PrecomputeTask>(precomputeTaskName)
.flatMap { it.outputFile }
)
}
}
但是,这种方法不起作用,因为显然在这种设置中调用
register()
和任何其他集合配置方法的任何时候都无法调用它们:> ./gradlew analyzeSomething --configuration runtimeClasspath
Caused by: org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreationException: Could not create task ':analyzeSomething'.
...
Caused by: org.gradle.api.internal.AbstractMutationGuard$IllegalMutationException: DefaultTaskContainer#create(String, Class, Action) on task set cannot be executed in the current context.
所以我的问题是,是否有一种方法可以基于属性名称设置任务依赖关系,如果没有,那么哪种方法更好地完成我想要的工作?
最佳答案
有趣的是,一旦我摆脱了project.afterEvaluate
,并且某些中等复杂的提供者进行了改组,这种方法就可以工作:
// instead of
val analyzeSomething = project.tasks.register<AnalyzeSomethingTask>("analyzeSomething")
project.afterEvaluate {
analyzeSomething.configure {
val precomputeTaskName = "precompute" + configuration.get().capitalize()
dataCacheFile.set(
project.tasks.named<PrecomputeTask>(precomputeTaskName)
.flatMap { it.outputFile }
)
}
}
// do this
project.tasks.register<AnalyzeSomethingTask>("analyzeSomething") {
dataCacheFile.set(
configuration.flatMap { c ->
val precomputeTaskName = "precompute" + c.capitalize()
project.tasks.named<PrecomputeTask>(precomputeTaskName)
.flatMap { it.outputFile }
}
)
}
我真的很好奇为什么这种方法与基于
project.afterEvaluate
的方法相比有效,但是它确实可以正常工作,因为它将正确的precompute*
任务添加到任务图中,并将其输出用于分析。
关于java - 基于属性值的动态任务依赖性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60254126/