Gradle не может найти артефакт zip в составной сборке, если применяется плагин Java

У меня есть проект Gradle, который создает артефакт zip. Я определяю артефакт через 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 внутри использует подключаемый модуль Java, поэтому его нельзя запускать отдельно.)


person NO_NAME    schedule 30.07.2019    source источник
comment
Связанный вопрос: stackoverflow.com/questions/57110795/   -  person NO_NAME    schedule 30.07.2019


Ответы (2)


Следующая настройка должна работать с 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 к артефакту JAR testA (myConfiguration 'org.test:testA:0.0.0.+@jar') также сработало.

Некоторое объяснение того, что (я думаю) происходит: Gradle нужен способ автоматического определения того, какой локальный компонент/артефакт в testA он может использовать для замены внешней зависимости testB.

  • Без применения плагина java есть только один компонент/артефакт, и я предполагаю, что Gradle затем просто выбирает этот единственный без лишних слов.
  • Как вы видели, применяя плагин java, к testA добавляется еще один артефакт. Теперь, какой из них должен взять Gradle? Можно было бы ожидать, что он будет смотреть на расширение файла, указанное в зависимости в testB, но, похоже, это не так. Похоже, что Gradle на самом деле работает не на уровне артефактов при замене зависимостей, а скорее на уровне модуля/компонента. Можно сказать, что у нас есть только один компонент с двумя артефактами, поэтому выбор одного компонента должен быть простым. Но похоже, что на самом деле у нас есть два варианта одного и того же компонента, и Gradle хочет выбрать один из этих вариантов. В вашей собственной настройке testB нет подсказки, которая указывала бы Gradle, какой вариант выбрать; поэтому он терпит неудачу (с заведомо плохим/вводящим в заблуждение сообщением об ошибке). В моей измененной настройке testB я даю подсказку: я говорю Gradle, что нам нужен вариант, который имеет определенную атрибут со значением my-zipped-lib. Поскольку я добавил тот же атрибут в опубликованную конфигурацию testA, теперь Gradle может выбрать правильный вариант (поскольку есть только один, который имеет требуемый атрибут). Расширение файла для зависимости по-прежнему актуально на втором этапе: как только Gradle выбрал вариант компонента, ему все равно нужно выбрать правильный артефакт — но только тогда.

Обратите внимание, что на самом деле мы работаем над тем, что поддерживается. с композитными сборками сегодня. См. также выпуск Gradle #2529, в котором говорится, что "проекты, публикующие артефакты, отличные от jar-файлов" не очень хорошо поддерживались. Когда я впервые увидел ваш вопрос, я, честно говоря, подумал, что нам здесь не повезет… но, похоже, есть способ снова подойти немного ближе к краю ;-)


В комментариях возник вопрос, почему добавление нескольких пользовательских артефактов работает, а применение плагина java ломает сборку. Как я пытался объяснить выше, речь идет не о нескольких артефактах, а о нескольких вариантах компонентов. IIUIC, эти варианты происходят из разных атрибутов конфигураций. Когда вы не добавляете такие атрибуты, у вас не будет разных вариантов компонентов. Однако подключаемый модуль Java добавляет такие атрибуты, что приводит к различным вариантам компонентов в вашем проекте (/component). Если вам интересно, вы можете увидеть различные атрибуты, добавив что-то вроде следующего к вашему build.gradle:

configurations.each { conf ->
    println "Attributes of $conf:"
    conf.attributes.keySet().each { attr ->
        println "\t$attr -> ${conf.attributes.getAttribute(attr)}"
    }
}

Теперь, где и когда добавлять какие атрибуты? Это зависит от вашей настройки. Я бы не стал слепо добавлять атрибуты ко всем конфигурациям в надежде, что это волшебным образом решит проблему. Хотя это может работать (в зависимости от ваших настроек), это определенно не будет чистым. Если настройка вашего проекта настолько сложна и/или специфична, как предполагает ваш вопрос, то, вероятно, имеет смысл более глубоко подумать о том, какие конфигурации вам нужны и какие атрибуты они должны нести. Первые разделы страницы документации Gradle, на которые я ссылался выше, вероятно, хорошая отправная точка, если вы еще не знакомы с этими нюансами конфигураций в Gradle. И да, я согласен, что такая логика лучше всего будет жить в плагине Gradle.

person Chriki    schedule 17.08.2019
comment
ВАУ, это работает. Хотя мне придется найти способ сделать это с помощью специального плагина, потому что мне не нравится идея копировать это в десятки проектов. Не сломается ли что-нибудь, если я просто без разбора добавлю это свойство в каждую конфигурацию в каждом проекте? - person NO_NAME; 18.08.2019
comment
Я не думаю, что это просто проблема с несколькими артефактами в одном проекте. Плагин Java должен делать нечто большее, чем добавление еще одного артефакта. Я могу создать второй самостоятельно, и оба они работают без установки каких-либо атрибутов. - person NO_NAME; 18.08.2019
comment
Да, действительно, я не думаю, что это проблема с несколькими артефактами, но с несколькими вариантами компонентов. Я обновил свой ответ, чтобы немного рассказать об этом. Я также попытался ответить на ваш другой вопрос. - person Chriki; 18.08.2019

Проблема, похоже, связана с ошибкой в ​​Gradle, из-за которой составная сборка и ссылка на артефакты не работают.

Некоторое обсуждение здесь: https://discuss.gradle.org/t/composite-build-cant-use-included-artifact-in-buildsrc-build-gradle/24978

Отчет об ошибке: 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
}
person Community    schedule 12.08.2019
comment
Я бы не был так уверен, что это одно и то же. Проблема, которую вы нашли, связана с какой-то странной структурой проекта (признаюсь, я не все понимаю). Он никогда явно не указывает на какую-либо проблему с java-плагином. - person NO_NAME; 14.08.2019
comment
Из того, что я понял, составная сборка и зависимость артефакта не работают, цитата из первой ссылки. Но если я хочу сделать это в buildSrc/build.gradle, он перестанет находить артефакт из композита. Например, мой-проект/buildSrc/build.gradle - person ; 14.08.2019
comment
Ну, я все еще не могу использовать это. Одна из основных причин, по которой я пытаюсь использовать Gradle, заключается в том, что я могу настроить его на использование репозитория вместо встроенной сборки. (У меня есть предложение if вокруг includeBuild('../testA').) Это действительно ускоряет компиляцию в большом дереве проектов. - person NO_NAME; 15.08.2019