Как создать проект Gradle «все проекты в одном»?

У меня есть монолитный проект Gradle со слишком большим количеством зависимостей.

Я хотел бы разбить его на множество подпроектов и опубликовать все подпроекты (сборка + исходники + javadoc) + дополнительный проект, представляющий собой слияние всех подпроектов.

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

JAR не должен включать зависимости (это не uber-jar), но результирующий pom.xml должен содержать зависимости всех подпроектов (сгенерированный pom.xml артефакта maven должен содержать все зависимости).

Виртуальный артефакт будет включать в себя слияние javadoc и исходников, чтобы соблюдать соглашения Maven Central.

Текущее состояние:

  • Project Main, generate
    • pom.xml
    • main.jar
    • основные-источники.jar
    • основной-javadoc.jar

Ожидаемое состояние:

  • Subproject A, generate
    • A-pom.xml
    • А.банка
    • A-sources.jar
    • A-javadoc.jar
  • Subproject B, generate
    • B-pom.xml
    • Б.банка
    • B-sources.jar
    • B-javadoc.jar
  • virtal-Project Main, generate
    • pom.xml=A-pom.xml+B-pom.xml
    • main.jar=A.jar+B.jar
    • main-sources.jar=A-sources.jar+B-sources.jar
    • main-javadoc.jar=A-javadoc.jar+B-javadoc.jar

Как мне с этим справиться?


person juherr    schedule 06.12.2020    source источник
comment
Может быть, вы будете немного более конкретным в отношении того, что вы спрашиваете? Из того, что я получаю, вы просто делаете то, что описываете :) Вы можете найти главу на многопроектные сборки в руководстве пользователя полезно, если вы его еще не читали.   -  person Bjørn Vester    schedule 07.12.2020
comment
@BjørnVester Спасибо за интерес. Создание подпроектов не проблема, но я не хочу слишком больших изменений для своих пользователей. Поэтому я хотел бы сохранить виртуальный артефакт со всеми моими проектами в одной банке, как сегодня (включая слияние артефактов javadoc и источников). JAR не должен включать зависимости, но результирующий файл pom.xml должен содержать зависимости всех подпроектов. Я надеюсь, что мой вопрос теперь более понятен.   -  person juherr    schedule 07.12.2020
comment
Да, теперь намного понятнее. Сначала я думал, что вы хотите публиковать и использовать подпроекты, имея при этом бинарные взаимозависимости в Maven и исходные зависимости в сборке. У меня нет ответа на слияние подпроектов таким образом, чтобы получить тот же результат, что и монолитный подход.   -  person Bjørn Vester    schedule 08.12.2020
comment
Где определяется это требование: виртуальный артефакт будет включать в себя слияние javadoc и исходных кодов, чтобы соблюдать соглашения Maven Central. ??   -  person Martin Zeitler    schedule 10.12.2020


Ответы (2)


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

  1. Слияние jar-файлов не так просто, как кажется, потому что в jar-файле могут быть такие вещи, как файлы ресурсов, которые не всегда привязаны к пространству имен. Возможно, у двух ваших банок есть файл ресурсов с одинаковым именем, и в этом случае вам придется объединить содержимое этих файлов.

  2. Javadoc очень сложно объединить без доступа к исходным файлам, потому что он имеет сводные страницы (индексные страницы).

Итак, мой совет:

  • Подумайте дважды, может быть, вам действительно нужна НЕ одна банка, а одна зависимость для ваших клиентов? Это разные. Вы можете легко получить артефакт только для помпонов. В зависимости от этого pom only артефакт будет просто переходить в зависимости от отдельных артефактов ваших компонентов подпроектов. Для вашего клиента практически ничего не меняется. Spring Boot использует этот подход. Для этого вы можете создать пустой проект java-библиотеки, сделать все ваши компоненты проектов его api зависимостью. Вам даже не нужен исходный код в этом проекте.

  • Если вы действительно хотите объединиться в одну банку, вы можете попробовать создать толстую банку с настройкой. Настройка не требует использования сторонних зависимостей.

Мы используем плагин Gradle Shadow для слияния jar-файлов. Его первоначальной целью было построить толстую банку, которая будет включать в себя все транзитивные зависимости. Но у него также есть специальная конфигурация shadow, к которой вы можете добавить зависимости, если хотите, чтобы зависимости экспортировались в POM, а не в комплекте. Итак, что вам нужно сделать:

  1. Определите непереходную конфигурацию (скажем, бандлер), к которой вы добавите свой подпроект в качестве зависимостей. Это будет целевая конфигурация для плагина Gradle Shadow.
  2. Определите транзитивную конфигурацию (bundlerTransitive), которая расширяет вашу нетранзитивную конфигурацию. Это будет решено вручную, чтобы найти сторонние зависимости.
  3. в вашем build.gradle зарегистрируйте замыкание afterEvaluate, где вы найдете зависимости второго уровня разрешенной транзитивной конфигурации, добавьте их в конфигурацию shadow. Причина второго уровня заключается в том, что зависимости первого уровня будут вашими артефактами подпроекта.
  4. После всего вышеперечисленного артефакт, созданный задачей shadowJar, должен быть загружен в maven. Вам нужно будет настроить задачу shadowJar для удаления классификатора (по умолчанию это shadow).

Вот полный пример (build.gradle) объединения vertx-web и всех его зависимостей в группе io.vertx:

plugins {
    id 'java'
    id 'maven-publish'
    id 'com.github.johnrengelman.shadow' version '5.2.0'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

configurations {
    bundler {
        transitive = false
    }

    bundlerTansitive {
        extendsFrom bundler
        transitive = true
    }
}

dependencies {
    bundler "io.vertx:vertx-web:4.0.0"
    bundler "io.vertx:vertx-web-common:4.0.0"
    bundler "io.vertx:vertx-core:4.0.0"
    bundler "io.vertx:vertx-auth-common:4.0.0"
    bundler "io.vertx:vertx-bridge-common:4.0.0"
}

shadowJar {
    configurations = [project.configurations.bundler]
    classifier ''
}


publishing {
    publications {
        shadow(MavenPublication) { publication ->
            project.shadow.component(publication)
        }
    }
}

project.afterEvaluate {
    // this is needed because your sub-projects might have inter-dependencies
    def isBundled = { ResolvedDependency dep ->
        return configurations.bundler.dependencies.any {
            dep.moduleGroup == it.group && dep.moduleName == it.name
        }
    }

    logger.lifecycle '\nBundled artifacts and their 1st level dependencies:'

    // level one dependencies
    configurations.bundlerTansitive.resolvedConfiguration.firstLevelModuleDependencies.forEach {
        logger.lifecycle "+--- ${it.getName()}"

        // level two dependencies
        it.children.findAll({ ResolvedDependency dep -> !isBundled(dep) })
                .forEach { ResolvedDependency dep ->
                    logger.lifecycle "|    +--- ${dep.name}"
                    project.dependencies.add('shadow', [group: dep.moduleGroup, name: dep.moduleName, version: dep.moduleVersion])
                }
    }

    logger.lifecycle '\nExported Dependencies:'

    configurations.shadow.getResolvedConfiguration().getFirstLevelModuleDependencies().forEach {
        project.logger.lifecycle "+--- ${it.getName()}"
    }
}

Для javadoc, если вам не нужен индекс (компромисс, как я уже сказал), то это просто задача jar со спецификацией копирования:

configurations {
   javadoc {
        transitive = false
    }
}

dependencies {
    javadoc 'com.my:component-a:1.1.0:javadoc'
    javadoc 'com.my:component-b:1.1.0:javadoc'
    javadoc 'com.my:component-c:1.1.0:javadoc'
    javadoc 'com.my:component-d:1.1.0:javadoc'
}

task javadocFatJar(type: Jar) {
    archiveClassifier.set('javadoc')
    from { 
        configurations.javadoc.collect { it.isDirectory() ? it : zipTree(it) } 
    }
    with jar
}
person Xinchao    schedule 10.12.2020
comment
Спасибо за полный ответ. У вас есть примеры для плагина Shadow? Я не уверен, что понимаю, как исключить сторонние зависимости из сгенерированного jar. - person juherr; 14.12.2020
comment
Я изменил свой ответ, включив в него мини-пример. В зависимости от ваших других потребностей вы можете сделать немного больше в блоке afterEvaluate, например. если ваши зависимости указаны с динамическими версиями, вы можете сначала разрешить конфигурацию сборщика, чтобы заблокировать версию и т. д. - person Xinchao; 14.12.2020
comment
Тем не менее, вы должны сначала подумать о том, чтобы не связывать банки, если это возможно, что, как мне кажется, всегда является правильным выбором. Приведение в действие объединения кажется технически достижением, но оно имеет последствия для всего процесса SLDC, особенно когда вы создаете библиотеку. - person Xinchao; 14.12.2020
comment
Я согласен, я сначала попробую решение, похожее на спецификацию :) - person juherr; 14.12.2020

Это нельзя сделать с maven-publish напрямую, но можно добавить отдельные java-library модули и упаковать каждый из них с исходниками и документацией. С Gradle это будет простая задача jar, но когда артефакты общедоступны... такие транзитивные зависимости лучше предоставлять метапакетом; ничего, кроме зависимостей Maven (локальных/центральных) вместо встроенных JARS. В этом случае это будет просто еще один модуль (который, очевидно, будет собран только после публикации других).

И что касается концепции, что для этого потребуются любые объединенные JavaDocs... https://central.sonatype.org/pages/requirements.html#supply-javadoc-and-sources
Хотя на них есть ссылки (Maven Central) в *.pom, Gradle сможет найти их.
Просто используйте репозиторий mavenLocal() вместо mavenCentral() для целей тестирования.

person Martin Zeitler    schedule 10.12.2020