java - 基于属性值的动态任务依赖性

标签 java gradle kotlin build

我需要创建一个提供交​​互式任务的插件,如下所示:

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

相关文章:

JavaScript/Java GWT 与 Rails

java - Android 应用程序连接到 Web 服务失败

java - 生成时出错-找不到属性 'MyOpenWeatherMapApiKey'

generics - Kotlin 泛型继承

java - 预期有单个匹配 bean,但在 junitTest 中发现 3 : objectMapper, halObjectMapper,_halObjectMapper

java - 如何在不使用 select 子句的情况下使用 apache cayenne 更新记录

java - GridBagLayout 按钮.setVisible(真/假)

Gradle 和 asciidoc

c++ - 如何使用 Gradle DSL(领域特定语言)上的文档?

android - 在两个 Kotlin 中拆分字符串