我有两个项目。一个有生产者配置:
// gen/build.gradle.kts
...
val outDir = layout.projectDirectory.dir("output")
val run = tasks.named<JavaExec>("run") {
args = listOf(outDir.asFile.absolutePath)
}
configurations.create("generated") {
isCanBeResolved = false
isCanBeConsumed = true
}
artifacts {
add("generated", outDir) {
builtBy(run)
}
}
然后,根项目具有在应用程序中使用的消费者配置:
// build.gradle.kts
...
val generated by configurations.creating<Configuration> {
isCanBeResolved = true
isCanBeConsumed = false
}
dependencies {
generated(project(mapOf(
"path" to ":gen",
"configuration" to "generated",
)))
api(generated)
}
sourceSets {
main {
kotlin {
srcDir(generated.files)
}
}
}
正如您所看到的,我目前使用dependencies
声明:generated
配置依赖于:gen:generated
配置。这是文档目前建议声明此依赖关系的方式。
但是,它会触发弃用警告:Adding a Configuration as a dependency is a confusing behavior which isn't recommended. This behaviour has been deprecated and is scheduled to be removed in Gradle 8.0. If you're interested in inheriting the dependencies from the Configuration you are adding, you should use Configuration#extendsFrom instead. See https://docs.gradle.org/7.4.2/dsl/org.gradle.api.artifacts.Configuration.html#org.gradle.api.artifacts.Configuration:extendsFrom(org.gradle.api.artifacts.Configuration[]) for more details.
所以我尝试使用extendsFrom
代替:
// In build.gradle.kts
val generated by configurations.creating<Configuration> {
isCanBeResolved = true
isCanBeConsumed = false
extendsFrom(project(":gen").configurations.getByName("generated"))
}
dependencies {
api(generated)
}
得到:Configuration with name 'generated' not found.
基于this StackOverflow answer,我怀疑这可能是由于gen
项目的配置阶段还没有发生而导致的。所以我尝试:
// In build.gradle.kts
val generated by configurations.creating<Configuration> {
isCanBeResolved = true
isCanBeConsumed = false
project(":gen").afterEvaluate {
extendsFrom(project(":gen").configurations.getByName("generated"))
}
}
但现在宣布继承似乎为时已晚:
A problem occurred configuring project ':gen'.
> Cannot change dependencies of dependency configuration ':generated' after it has been resolved.
我该怎么做才能避免这个弃用警告?
1条答案
按热度按时间zxlwwiss1#
欢迎来到sharing outputs between Gradle subprojects的有趣世界!
解决这个问题有几种不同的方法。
由于您正在使用JVM项目,因此可能需要配置feature variant。Gradle将创建一个新的源集,您可以将文件生成到其中。
但是,如果你没有使用JVM项目,那么事情就会变得有点复杂。我将解释最简单和最直接的方法。请不要笑了,这不是一个笑话。是的,我知道,它仍然很复杂。相信我,这是值得的。
我们最终将以一种强大的方式在项目之间共享文件,这种方式可缓存(与Build Cache和Configuration Cache兼容),灵活,可重用,并能很好地理解Gradle的工作原理。
任务
以下是需要做的事情:
**是的,configuration这个名字很容易让人混淆。在这种情况下,'configuration'应该更好地重命名为'DependencyContainer' -它们只是一个文件的集合,可能是子项目的 * 传出 * 或 * 传入 ,沿着一些描述内容的元数据。
创建buildSrc约定插件
我们需要能够在提供和消费子项目中设置我们的提供和消费代码。虽然从技术上讲,我们可以复制粘贴它,但这种方式很糟糕,而且太挑剔了。在Gradle中共享配置的最佳方式是使用约定插件。
我已经在另一个答案(为Gradle子项目配置Kotlin扩展)中介绍了设置约定插件,因此我将在这里总结步骤。
1.为buildSrc创建构建配置。
由于buildSrc实际上是一个独立的项目,所以最好创建一个
settings.gradle.kts
文件。build.gradle.kts
只需要KotlinDSL插件。这将使编写我们的约定插件更容易,因为预编译脚本插件1.创建我们的约定插件。文件名(
.gradle.kts
之前的所有内容)和package
(如果定义了一个)将是插件ID完成了!你可以通过将这个什么都不做的插件应用到你的子项目来测试它。
当您运行一个任务(例如
./gradlew help
)时,您应该看到消息I don't do anything yet...
记录到控制台。创建提供和消费文件的配置
下一步是创建一些配置,这些配置是Gradle依赖关系世界的shipping containers。
我们将做两个配置,一个用于 * 传入 * 文件,另一个用于 * 传出 * 文件。
请注意
isCanBeConsumed
和isCanBeResolved
*。在Gradle术语中,传入的将由子项目“解析”,传出的将由其他子项目“使用”。使用正确的组合很重要。**同样,我们有一些令人困惑的名称。术语'consumed'和'resolved'不是很清楚,它们在我看来都是同义词。它们最好重新命名,以表明
consumed=true && resolved=false
表示OUTGOING
,consumed=false && resolved=true
表示INCOMING
*这一步的酷之处在于,如果你现在跳转到你的子项目,你可以使用这些配置,就像它们内置在Gradle中一样(如果需要,在IDE sync之后)。
Gradle为
generatedFiles
配置生成了一个类型安全的访问器。然而,我们还没有完成。我们还没有将任何文件放入传出配置中,所以当然消费项目将无法解析任何文件。但在此之前,我们需要添加我提到的元数据。
通过变量属性区分我们的配置
很有可能提供生成文件的子项目也有其他配置,它们的文件类型完全不同。但是我们不想用大量的文件填充
generatedFiles
配置,我们 * 只 * 需要生成任务生成的文件。这就是变量属性的用武之地。如果配置是运输容器,那么变量属性就是外部的运输标签,描述内容应该发送到哪里。
在基本层面上,变量属性只是键值字符串,除了键必须在Gradle中注册。如果我们使用可以使用的内置标准属性,我们可以跳过注册。Usage属性是一个很好的选择。它非常常用,只要我们选择一个独特的值,Gradle就能够通过比较值来区分两个配置。
重要的是,两个配置都添加了 * 相同 * 的属性键和值。现在,Gradle可以很好地匹配传入和传出配置!
现在所有的部分都已经就位了。我们几乎完成了!现在是时候开始将文件推送到配置中,并将文件拉出。
将文件放入 outgoing 配置
在生成生成文件的子项目中,假设我们有一个生成文件的任务,我将使用一个Sync任务作为实际生成器任务的替身。
请注意,输出目录并不重要,因为Gradle的Provider API可以将任务转换为文件提供程序。
这一点的好处是,现在Gradle只会在请求时配置和运行
myGeneratorTask
任务。当这种configuration avoidance经常使用时,它确实可以帮助加速Gradle构建。解析传入配置
最后一步了!
在消费项目中,我们可以使用常规的
dependencies {}
块添加对提供项目的依赖。现在我们可以从传入配置中获取 incoming 文件:
现在,如果你运行
./gradlew myConsumerTask
,你会注意到,即使你没有显式设置任何任务依赖关系,Gradle也会自动运行myGeneratorTask
。如果检查
myConsumerTask
的临时目录(./my-consumer/build/tmp/myConsumerTask/
)的内容,您将看到生成的文件。如果您重新运行相同的命令,那么您应该看到Gradle将避免运行任务,因为它们是UP-TO-DATE。
您还可以启用Gradle Build Cache(
./gradlew myConsumerTask --build-cache
),即使删除生成的文件(删除./my-generator/build/
目录),您也应该看到myGeneratorTask
和myConsumerTask
都是从缓存加载的