如果应用 java 插件,Gradle 无法在复合构建中找到 zip 工件
Gradle is unable to find zip artifact in composite build if java plugin is applied
我有一个创建 zip 工件的 Gradle 项目。我通过 artifacts.add('default', zipTask)
定义工件。我通过 includeBuild
将此项目添加到另一个项目并使用 zip 作为依赖项 (dependencies { myConfiguration 'org.example:testA:+@zip' }
)。
到目前为止,一切都很好。有效。
当我将插件 java
添加到第一个项目时,问题就开始了。由于某种原因,它会阻止 Gradle 找到 zip 工件。
错误是:
Execution failed for task ':doubleZipTask'.
> Could not resolve all files for configuration ':myConfiguration'.
> Could not find testA.zip (project :testA).
为什么?如何解决?
完整示例:
项目testA
settings.gradle
:
rootProject.name = 'testA'
build.gradle
:
plugins {
id 'base'
// Uncomment the line below to break the zip artifact
//id 'java'
}
group = 'org.test'
version = '0.0.0.1_test'
task zipTask(type: Zip) {
from './settings.gradle' // just so the zip isn't empty
}
artifacts.add('default', zipTask)
项目testB
settings.gradle
:
rootProject.name = 'testB'
// This line may be commented out in some cases and then the artifact should be downloaded from Maven repository.
// For this question it should be always uncommented, though.
includeBuild('../testA')
build.gradle
:
plugins {
id 'base'
}
configurations {
myConfiguration
}
dependencies {
myConfiguration 'org.test:testA:0.0.0.+@zip'
}
task doubleZipTask(type: Zip) {
from configurations.myConfiguration
}
更新 1
我在 build.grade
的末尾添加了一些诊断代码:
configurations.default.allArtifacts.each() {
println it.toString() + ' -> name: ' + it.getName() + ', extension: ' + it.getExtension()
}
在带有 java
插件的版本中它打印:
ArchivePublishArtifact_Decorated testA:zip:zip: -> name: testA, extension: zip
org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact@2c6aaa5 -> name: testA, extension: jar
但是,我不确定额外的神器是否可以破坏某些东西。
我自己加第二个神器好像没有问题
更新 2
也许 zip 文件不能最好地表达我的意图。毕竟,我可以在一个项目中构建 java 相关文件,然后将它们压缩到另一个项目中。
但是,该问题也适用于 war 文件。 (War插件内部使用Java插件,所以不能单独运行。)
问题似乎是 Gradle 中的错误,其中复合构建和对工件的引用被破坏。
错误报告:https://github.com/gradle/gradle/issues/3768
解决方法是将工件依赖项移动到任务依赖项:
plugins {
id 'base'
}
configurations {
myConfiguration
}
dependencies {
}
task doubleZipTask(type: Zip) {
dependsOn gradle.includedBuild('testA').task(':zipTask')
from configurations.myConfiguration
}
以下设置应适用于 Gradle 5.6(使用其他属性时,它可能也适用于以前的版本)。它主要对应于您的原始设置,但 XXX
.
指示的更改除外
项目testA
settings.gradle
:
rootProject.name = 'testA'
build.gradle
:
plugins {
id 'base'
// Uncomment the line below to break the zip artifact
//id 'java'
}
group = 'org.test'
version = '0.0.0.1_test'
task zipTask(type: Zip) {
from './settings.gradle' // just so the zip isn't empty
}
// XXX added an attribute to the configuration
configurations.default.attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements, 'my-zipped-lib'))
}
artifacts.add('default', zipTask)
项目testB
settings.gradle
:
rootProject.name = 'testB'
// This line may be commented out in some cases and then the artifact should be downloaded from Maven repository.
// For this question it should be always uncommented, though.
includeBuild('../testA')
build.gradle
:
plugins {
id 'base'
}
configurations {
// XXX added the same attribute as in the testA project
myConfiguration {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements, 'my-zipped-lib'))
}
}
}
dependencies {
myConfiguration 'org.test:testA:0.0.0.+@zip'
}
task doubleZipTask(type: Zip) {
from configurations.myConfiguration
}
我已经在使用和不使用 java
插件的情况下测试了此设置。我还测试了发布到 Maven 存储库并让 testB
从那里而不是从 testA
的包含构建中获取其依赖项。从 testB
添加对 testA
(myConfiguration 'org.test:testA:0.0.0.+@jar'
) 的 JAR 工件的额外依赖也有效。
关于(我相信)正在发生的事情的一些解释:Gradle 需要一种方法来自动确定 testA
中的哪个本地 component/artifact 可以用于替换的外部依赖testB
.
- 在不应用
java
插件的情况下,只有一个 component/artifact,我的猜测是 Gradle 然后只是 select 一个,不用多说。
- 如您所见,通过应用
java
插件,另一个工件被添加到 testA
。现在 Gradle 应该选哪一个?人们会期望它会查看在 testB
中的依赖项中指定的文件扩展名,但事实并非如此。似乎 Gradle 在替换依赖项时实际上并没有在 artifacts 级别工作,而是在 module/组件级。您可能会说我们只有一个组件和两个工件,所以 select 一个组件应该很简单。但似乎我们实际上有两个相同组件的 variants 并且 Gradle 想要 select 其中一个 variants .在您自己的 testB
设置中,没有任何线索可以告诉 Gradle 哪个变体是 select;所以它失败了(公认的 bad/misleading 错误消息)。在我更改的 testB
设置中,我提供了线索:我告诉 Gradle 我们想要具有特定 attribute 且值为 my-zipped-lib
的变体。由于我在 testA
的已发布配置上添加了相同的属性,Gradle 现在能够 select 正确的变体(因为只有一个具有所需的属性)。依赖项的文件扩展名在第二步中仍然相关:一旦 Gradle 已经 select 编辑了组件变体,它仍然需要 select 正确的工件——但只有到那时。
请注意,我们实际上正在研究 what is supported with composite builds today. See also Gradle issue #2529 的边缘,它指出“发布非 jar 工件的项目”没有得到很好的支持。当我第一次看到你的问题时,我真的以为我们在这里运气不好......但似乎有办法再次靠近篮筐 ;-)
在评论中,出现了为什么在应用 java
插件时添加多个自定义工件会破坏构建的问题。正如我在上面试图解释的那样,这不是多个 工件 的问题,而是多个 组件变体 的问题。 IIUIC,这些变体源自配置的不同属性。当您不添加此类属性时,您将不会有不同的组件变体。然而,Java 插件 确实 添加了这样的属性,因此导致项目(/组件)中的不同组件变体。如果您有兴趣,可以通过在 build.gradle
:
中添加类似以下内容来查看不同的属性
configurations.each { conf ->
println "Attributes of $conf:"
conf.attributes.keySet().each { attr ->
println "\t$attr -> ${conf.attributes.getAttribute(attr)}"
}
}
现在何时何地添加哪些属性?这取决于您的设置。我不会盲目地为所有配置添加属性,希望这能神奇地解决问题。虽然它可能有效(取决于您的设置),但它肯定不是干净的。如果您的项目设置像您的问题所暗示的那样复杂 and/or 特殊,那么更深入地思考您需要哪些配置以及它们应该携带哪些属性可能是有意义的。如果您还不十分熟悉 Gradle 中配置的这些细微差别,那么我上面链接的 the Gradle documentation page 的第一部分可能是一个很好的起点。是的,我同意这种逻辑最好存在于 Gradle 插件中。
我有一个创建 zip 工件的 Gradle 项目。我通过 artifacts.add('default', zipTask)
定义工件。我通过 includeBuild
将此项目添加到另一个项目并使用 zip 作为依赖项 (dependencies { myConfiguration 'org.example:testA:+@zip' }
)。
到目前为止,一切都很好。有效。
当我将插件 java
添加到第一个项目时,问题就开始了。由于某种原因,它会阻止 Gradle 找到 zip 工件。
错误是:
Execution failed for task ':doubleZipTask'.
> Could not resolve all files for configuration ':myConfiguration'.
> Could not find testA.zip (project :testA).
为什么?如何解决?
完整示例:
项目testA
settings.gradle
:
rootProject.name = 'testA'
build.gradle
:
plugins {
id 'base'
// Uncomment the line below to break the zip artifact
//id 'java'
}
group = 'org.test'
version = '0.0.0.1_test'
task zipTask(type: Zip) {
from './settings.gradle' // just so the zip isn't empty
}
artifacts.add('default', zipTask)
项目testB
settings.gradle
:
rootProject.name = 'testB'
// This line may be commented out in some cases and then the artifact should be downloaded from Maven repository.
// For this question it should be always uncommented, though.
includeBuild('../testA')
build.gradle
:
plugins {
id 'base'
}
configurations {
myConfiguration
}
dependencies {
myConfiguration 'org.test:testA:0.0.0.+@zip'
}
task doubleZipTask(type: Zip) {
from configurations.myConfiguration
}
更新 1
我在 build.grade
的末尾添加了一些诊断代码:
configurations.default.allArtifacts.each() {
println it.toString() + ' -> name: ' + it.getName() + ', extension: ' + it.getExtension()
}
在带有 java
插件的版本中它打印:
ArchivePublishArtifact_Decorated testA:zip:zip: -> name: testA, extension: zip
org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact@2c6aaa5 -> name: testA, extension: jar
但是,我不确定额外的神器是否可以破坏某些东西。
我自己加第二个神器好像没有问题
更新 2
也许 zip 文件不能最好地表达我的意图。毕竟,我可以在一个项目中构建 java 相关文件,然后将它们压缩到另一个项目中。 但是,该问题也适用于 war 文件。 (War插件内部使用Java插件,所以不能单独运行。)
问题似乎是 Gradle 中的错误,其中复合构建和对工件的引用被破坏。
错误报告:https://github.com/gradle/gradle/issues/3768
解决方法是将工件依赖项移动到任务依赖项:
plugins {
id 'base'
}
configurations {
myConfiguration
}
dependencies {
}
task doubleZipTask(type: Zip) {
dependsOn gradle.includedBuild('testA').task(':zipTask')
from configurations.myConfiguration
}
以下设置应适用于 Gradle 5.6(使用其他属性时,它可能也适用于以前的版本)。它主要对应于您的原始设置,但 XXX
.
项目testA
settings.gradle
:
rootProject.name = 'testA'
build.gradle
:
plugins {
id 'base'
// Uncomment the line below to break the zip artifact
//id 'java'
}
group = 'org.test'
version = '0.0.0.1_test'
task zipTask(type: Zip) {
from './settings.gradle' // just so the zip isn't empty
}
// XXX added an attribute to the configuration
configurations.default.attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements, 'my-zipped-lib'))
}
artifacts.add('default', zipTask)
项目testB
settings.gradle
:
rootProject.name = 'testB'
// This line may be commented out in some cases and then the artifact should be downloaded from Maven repository.
// For this question it should be always uncommented, though.
includeBuild('../testA')
build.gradle
:
plugins {
id 'base'
}
configurations {
// XXX added the same attribute as in the testA project
myConfiguration {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.objects.named(LibraryElements, 'my-zipped-lib'))
}
}
}
dependencies {
myConfiguration 'org.test:testA:0.0.0.+@zip'
}
task doubleZipTask(type: Zip) {
from configurations.myConfiguration
}
我已经在使用和不使用 java
插件的情况下测试了此设置。我还测试了发布到 Maven 存储库并让 testB
从那里而不是从 testA
的包含构建中获取其依赖项。从 testB
添加对 testA
(myConfiguration 'org.test:testA:0.0.0.+@jar'
) 的 JAR 工件的额外依赖也有效。
关于(我相信)正在发生的事情的一些解释:Gradle 需要一种方法来自动确定 testA
中的哪个本地 component/artifact 可以用于替换的外部依赖testB
.
- 在不应用
java
插件的情况下,只有一个 component/artifact,我的猜测是 Gradle 然后只是 select 一个,不用多说。 - 如您所见,通过应用
java
插件,另一个工件被添加到testA
。现在 Gradle 应该选哪一个?人们会期望它会查看在testB
中的依赖项中指定的文件扩展名,但事实并非如此。似乎 Gradle 在替换依赖项时实际上并没有在 artifacts 级别工作,而是在 module/组件级。您可能会说我们只有一个组件和两个工件,所以 select 一个组件应该很简单。但似乎我们实际上有两个相同组件的 variants 并且 Gradle 想要 select 其中一个 variants .在您自己的testB
设置中,没有任何线索可以告诉 Gradle 哪个变体是 select;所以它失败了(公认的 bad/misleading 错误消息)。在我更改的testB
设置中,我提供了线索:我告诉 Gradle 我们想要具有特定 attribute 且值为my-zipped-lib
的变体。由于我在testA
的已发布配置上添加了相同的属性,Gradle 现在能够 select 正确的变体(因为只有一个具有所需的属性)。依赖项的文件扩展名在第二步中仍然相关:一旦 Gradle 已经 select 编辑了组件变体,它仍然需要 select 正确的工件——但只有到那时。
请注意,我们实际上正在研究 what is supported with composite builds today. See also Gradle issue #2529 的边缘,它指出“发布非 jar 工件的项目”没有得到很好的支持。当我第一次看到你的问题时,我真的以为我们在这里运气不好......但似乎有办法再次靠近篮筐 ;-)
在评论中,出现了为什么在应用 java
插件时添加多个自定义工件会破坏构建的问题。正如我在上面试图解释的那样,这不是多个 工件 的问题,而是多个 组件变体 的问题。 IIUIC,这些变体源自配置的不同属性。当您不添加此类属性时,您将不会有不同的组件变体。然而,Java 插件 确实 添加了这样的属性,因此导致项目(/组件)中的不同组件变体。如果您有兴趣,可以通过在 build.gradle
:
configurations.each { conf ->
println "Attributes of $conf:"
conf.attributes.keySet().each { attr ->
println "\t$attr -> ${conf.attributes.getAttribute(attr)}"
}
}
现在何时何地添加哪些属性?这取决于您的设置。我不会盲目地为所有配置添加属性,希望这能神奇地解决问题。虽然它可能有效(取决于您的设置),但它肯定不是干净的。如果您的项目设置像您的问题所暗示的那样复杂 and/or 特殊,那么更深入地思考您需要哪些配置以及它们应该携带哪些属性可能是有意义的。如果您还不十分熟悉 Gradle 中配置的这些细微差别,那么我上面链接的 the Gradle documentation page 的第一部分可能是一个很好的起点。是的,我同意这种逻辑最好存在于 Gradle 插件中。