Gradle maven-publish: как добавить зависимости testCompile к сгенерированному pom?

При публикации с использованием maven-publish (инкубация, я знаю), compile зависимости добавляются к генерируемому POM (в области runtime), но testCompile зависимости игнорируются.

Как получить зависимости testCompile в сгенерированном POM как область test?


person Markus Pscheidt    schedule 02.03.2015    source источник
comment
Пожалуйста, объясните, почему вы хотите опубликовать свой тестовый код как артефакты. Это, конечно, не стандартная процедура.   -  person Jolta    schedule 03.03.2015
comment
Существуют тестовые библиотеки, такие как spring-test, spring-batch-test, jsonpath, которые используются в модульной настройке базовым и зависимыми модулями. Поэтому было бы неплохо объявить их в базовом модуле в области test так же, как, например. spring-context объявлен в области runtime.   -  person Markus Pscheidt    schedule 03.03.2015


Ответы (3)


Я потратил несколько часов, чтобы найти хороший способ выбрать, какую конфигурацию сборки должен использовать артефакт или публикация, но безуспешно. Мои выводы заключались в том, что единственный способ добиться этого - изменить полученный POM XML, как показано ниже:

// build.gradle

publishing {
    repositories { /* skipped for brevity */ }

    publications {
        core(MavenPublication) {
            from components.java
            artifactId project.name

            artifact sourcesJar {
                classifier 'sources'
            }
        }

        generators(MavenPublication) {
            from components.java
            artifactId "${project.name}-generators"

            artifacts = [ generatorsJar ]
            artifact generatorsSourcesJar {
                classifier 'sources'
            }

            pom.withXml { pomXml -> replaceDependenciesWith('generatorsBase', pomXml) }
        }
    }
}

void replaceDependenciesWith(String configurationName, XmlProvider pomXml) {
    Node configurationDependencies = new Node(null, 'dependencies')
    project.configurations.getByName(configurationName).allDependencies.each { dep ->
        Node dependency = new Node(null, 'dependency')
        dependency.appendNode('groupId', dep.group)
        dependency.appendNode('artifactId', dep.name)
        dependency.appendNode('version', dep.version)
        dependency.appendNode('scope', 'compile')
        configurationDependencies.append(dependency)
    }
    pomXml.asNode().dependencies*.replaceNode(configurationDependencies)
}

Выше работал над Gradle 3.3


Комментарий к синтаксису Groovy, похожему на конструктор XML

Я также пытался использовать синтаксис Groovy, похожий на XML-конструктор, но, к сожалению, к замыканию, переданному методу replaceNode, был прикреплен неверный контекст, и, следовательно, это не сработало. При встраивании он получал тот же контекст, что и замыкание publications {}, а при извлечении в метод version dep.version работал не так, как ожидалось).

// Does not work!
void replaceDependenciesWith(String configurationName, Node pomXmlNode) {
    pomXmlNode.dependencies*.replaceNode {
        dependencies {
            project.configurations.getByName(configurationName).allDependencies.each { dep ->
                dependency {
                    groupId dep.group
                    artifactId dep.name
                    version dep.version
                    scope 'compile'
                }
            }
        }
    }
}
person krzychu    schedule 17.06.2017

POM используется только при публикации артефакта; он загружается в репозиторий Maven вместе с артефактом. Следовательно, POM нужны только зависимости времени выполнения.

Gradle выполняет тесты независимо от вашего подключаемого модуля развертывания, поэтому он не использует файл POM.

Предполагая, что вы используете подключаемый модуль Java, он добавляет исходный набор test. Это, в свою очередь, создает задачу testCompile.

Теперь Gradle предполагает, что ваши зависимости времени выполнения будут такими же, как ваши зависимости времени компиляции, если вы не настроите иначе. Однако он рассматривает только исходный набор main. Вот почему ваш POM не включает зависимости test.

Таким образом, настройте свои тестовые зависимости, как показано ниже. Затем просто живите счастливо, зная, что опубликованный артефакт не будет включать ваш тестовый код или его зависимости.

dependencies {
    testCompile 'org.springframework:spring-test:4.+'
}

Если у вас есть исключительная ситуация, когда тесты выполняются на машине, у которой нет доступа к исходному коду тестов, пожалуйста, опишите более подробно, каковы ваши требования. Должна быть возможность настроить отдельный выходной артефакт для тестового кода, чтобы его можно было опубликовать, но я все же не думаю, что вам следует выпускать его в том же пакете (или POM), что и исходный набор main.

person Jolta    schedule 03.03.2015
comment
Хорошо, я уже публикую отдельный artifact testJar с общим тестовым кодом между модулями. Также было бы неплохо сделать дополнительный POM для тестовой банки с объявлением внешних библиотек, таких как spring-test. Возможно ли это? - person Markus Pscheidt; 03.03.2015
comment
Каков ваш вариант использования для публикации вашего тестового кода? Извините за настойчивость, но я думаю, что важно не тратить силы на то, что не нужно. =) Да, вы могли объявить больше артефактов для публикации. См. gradle.org/docs/current/userguide/ для как объявить свой publications. - person Jolta; 03.03.2015
comment
Нет проблем, вот некоторые детали, касающиеся мотивации. Возьмем сценарий инициализации базы данных, который инициализирует базу данных в памяти или класс тестовых констант Java. Они используются модульными тестами в основном/базовом модуле, а также в зависимых модулях. Альтернативой может быть копирование и вставка таких тестовых ресурсов. - person Markus Pscheidt; 03.03.2015
comment
Проблема транзитивных тестовых зависимостей в многомодульной настройке проиллюстрирована здесь для Maven: stackoverflow.com/q/15816805/606662 - person Markus Pscheidt; 03.03.2015
comment
Вывод, похоже, таков, что транзитивные тестовые зависимости не считаются правильным решением. Поэтому я буду объявлять тестовые зависимости для каждого проекта, даже если это дублирование кода и нарушение принципа DRY. - person Markus Pscheidt; 04.03.2015
comment
Возможно, использование конфигурации testCompile не лучшая практика, но я хотел сгенерировать две публикации из одного проекта - одну публикацию с моделью и тому подобное, а другую с генераторами, которые можно использовать для тестов (когда необходимо сгенерировать значения модели). Затем проекты, использующие эту библиотеку, могут отдельно включать генераторы в свои зависимости testCompile. - person krzychu; 17.06.2017

Я не мог использовать какие-либо функции языка groovy, когда мне нужно было изменить XML-файл POM. И мне пришлось полагаться на API напрямую, как ответ krzychu.

В противном случае закрытие xml не было применено, как я ожидал, сборка завершилась с ошибкой с некоторым предупреждением или закрытие было применено неправильно, что привело к недопустимому pom.

Но недавно, внимательно прочитав замыкание Groovy, я заметил, что можно применять resolutionStrategy к замыкание, чтобы помочь среде выполнения найти правильный контекст (неявное this).

Стратегия разрешения по умолчанию — Closure.OWNER_FIRST, что объясняет, почему я получил ошибки о закрытии, примененном к publications в некоторых моих испытаниях. Из их документации я попытался установить стратегию на Closure.DELEGATE_FIRST, и это сработало, как и ожидалось.

Однако обратите внимание, что закрытие должно применяться к Node, поэтому .children() возвращает список, .last() возвращает Node, на котором вы можете добавить еще один узел либо с помощью метода .plus(...), либо с помощью его псевдонима +.

publishing {
    publications {
        core(MavenPublication) {
            pom.withXml {
                def dependenciesNode = 
                    asNode().dependencyManagement
                            .first()
                            .dependencies
                            .first()

                dependenciesNode.children().last().plus( {
                    resolveStrategy = Closure.DELEGATE_FIRST
                    dependency {
                        'groupId'('org.springframework.boot')
                        'artifactId'('spring-boot-dependencies')
                        'version'(rootProject.'spring-boot.version')
                        'type'('pom')
                        'scope'('import')
                    }
                })
            }
        }
    }
}

Поиск правильного синтаксиса был похож на поиск булавки в стоге сена, вот несколько ссылок (1), (2), (3), который мне помог, я нашел resolutionStrategy.

person Brice    schedule 21.10.2019