Автор оригинала: Johnathan Gilday.
1. Обзор
Выпуск Gradle 6.0 содержит несколько новых функций, которые помогут сделать наши сборки более эффективными и надежными. Эти функции включают улучшенное управление зависимостями, публикацию метаданных модуля, предотвращение конфигурации задач и поддержку JDK 13.
В этом уроке мы познакомим вас с новыми функциями, доступными в Gradle 6.0. В нашем примере файлы сборки будут использовать DSL Kotlin Gradle.
2. Улучшение Управления Зависимостями
С каждым выпуском в последние годы Gradle постепенно улучшала то, как проекты управляют зависимостями. Эти улучшения зависимостей достигают кульминации в Gradle 6.0. Давайте рассмотрим улучшения управления зависимостями, которые теперь стабильны.
2.1. Разделение API и реализации
Плагин java-library помогает нам создать многоразовую библиотеку Java. Плагин поощряет нас отделять зависимости, которые являются частью общедоступного API нашей библиотеки, от зависимостей, которые являются деталями реализации. Это разделение делает сборки более стабильными, поскольку пользователи не будут случайно ссылаться на типы, которые не являются частью общедоступного API библиотеки.
Плагин java-библиотека и его api и реализация конфигурации были введены в Gradle 3.4. Хотя этот плагин не является новым для Gradle 6.0, расширенные возможности управления зависимостями, которые он предоставляет, являются частью комплексного управления зависимостями, реализованного в Gradle 6.0.
2.2. Расширенные версии
Наши графики зависимостей проектов часто имеют несколько версий одной и той же зависимости. Когда это произойдет, Gradle необходимо выбрать, какую версию зависимости в конечном итоге будет использовать проект.
Gradle 6.0 позволяет нам добавлять расширенную информацию о версии в наши зависимости. Богатая информация о версии помогает Gradle сделать наилучший выбор при разрешении конфликтов зависимостей.
Например, рассмотрим проект, который зависит от гуавы. Предположим далее, что этот проект использует Guava версии 28.1-jre, хотя мы знаем, что он использует только API Guava, которые были стабильны с версии 10.0.
Мы можем использовать объявление require , чтобы сообщить Gradle, что этот проект может использовать любую версию Guava начиная с 10.0, и мы используем объявление prefer , чтобы сообщить Gradle, что он должен использовать 28.1-jre, если никакие другие ограничения не препятствуют этому. В объявлении потому что добавляется примечание, объясняющее эту богатую информацию о версии:
implementation("com.google.guava:guava") { version { require("10.0") prefer("28.1-jre") because("Uses APIs introduced in 10.0. Tested with 28.1-jre") } }
Как это помогает сделать наши сборки более стабильными? Предположим, что этот проект также опирается на зависимость foo , которая должна использовать Guava версии 16.0. Файл сборки для проекта foo объявит эту зависимость как:
dependencies { implementation("com.google.guava:guava:16.0") }
Поскольку проект foo зависит от Guava 16.0, а наш проект зависит как от Guava версии 28.1-jre , так и от food , у нас возник конфликт. Поведение Gradle по умолчанию заключается в выборе последней версии. В этом случае, однако, выбор последней версии является неправильным выбором , потому что foo должен использовать версию 16.0.
До Gradle 6.0 пользователям приходилось самостоятельно решать конфликты. Поскольку Gradle 6.0 позволяет нам сообщить Gradle, что наш проект может использовать версии Guava до 10.0, Gradle правильно разрешит этот конфликт и выберет версию 16.0.
В дополнение к объявлениям require и prefer мы можем использовать объявления strictly и reject . Объявление строго описывает диапазон версий зависимостей, который должен использоваться нашим проектом. Объявление reject описывает версии зависимостей, несовместимые с нашим проектом.
Если наш проект опирался на API, который, как мы знаем, будет удален в Guava 29, то мы используем объявление strictly , чтобы предотвратить использование Gradle версии Guava, превышающей 28. Аналогично, если мы знаем, что в Guava 27.0 есть ошибка, которая вызывает проблемы для нашего проекта, мы используем отклонить , чтобы исключить ее:
implementation("com.google.guava:guava") { version { strictly("[10.0, 28[") prefer("28.1-jre") reject("27.0") because(""" Uses APIs introduced in 10.0 but removed in 29. Tested with 28.1-jre. Known issues with 27.0 """) } }
2.3. Платформы
Плагин java-platform позволяет нам повторно использовать набор ограничений зависимостей в разных проектах. Автор платформы объявляет набор тесно связанных зависимостей, версии которых контролируются платформой.
Проектам, зависящим от платформы, не нужно указывать версии для каких-либо зависимостей, контролируемых платформой. Пользователи Maven найдут это похожим на функцию Maven parent POM Управление зависимостями .
Платформы особенно полезны при сборке нескольких проектов. Каждый проект в сборке с несколькими проектами может использовать одни и те же внешние зависимости, и мы не хотим, чтобы версии этих зависимостей были несинхронизированы.
Давайте создадим новую платформу, чтобы убедиться, что наша многопроектная сборка использует одну и ту же версию Apache HttpClient в разных проектах. Сначала мы создаем проект httpclient-platform, который использует плагин java-platform :
plugins { `java-platform` }
Далее мы объявляем ограничения для зависимостей , включенных в эту платформу. В этом примере мы выберем версии httpкомпонентов Apache, которые мы хотим использовать в нашем проекте:
dependencies { constraints { api("org.apache.httpcomponents:fluent-hc:4.5.10") api("org.apache.httpcomponents:httpclient:4.5.10") } }
Наконец, давайте добавим проект person-rest-client , который использует API Apache HTTP Client Fluent. Здесь мы добавляем зависимость от нашего проекта httpclient-platform , используя метод platform . Мы также добавим зависимость от org.apache.httpкомпонентов:fluent-hc . Эта зависимость не включает версию, поскольку httpclient-platform определяет версию для использования:
plugins { `java-library` } dependencies { api(platform(project(":httpclient-platform"))) implementation("org.apache.httpcomponents:fluent-hc") }
То java-платформа плагин помогает избежать нежелательных сюрпризов во время выполнения из-за несогласованных зависимостей в сборке.
2.4. Испытательные приспособления
До Gradle 6.0 авторы сборок, которые хотели совместно использовать тестовые приспособления в разных проектах, извлекали эти приспособления в другой проект библиотеки. Теперь авторы сборки могут публиковать тестовые приспособления из своего проекта с помощью плагина java-test-fixtures .
Давайте создадим библиотеку, которая определяет абстракцию и публикует тестовые приспособления, которые проверяют контракт, ожидаемый этой абстракцией.
В этом примере наша абстракция представляет собой генератор последовательностей Фибоначчи, а тестовое приспособление-это JUnit 5 test mix-in . Разработчики генератора последовательностей Фибоначчи могут использовать тестовый микс, чтобы убедиться, что они правильно реализовали генератор последовательностей.
Во-первых, давайте создадим новый проект fibonacci-spi для нашей абстракции и тестовых приспособлений. Для этого проекта требуется java-библиотека и java-тест-светильники плагины:
plugins { `java-library` `java-test-fixtures` }
Далее, давайте добавим зависимости JUnit 5 в наши тестовые приспособления. Так же, как java-библиотека плагин определяет api и реализацию конфигурации, java-test-fixtures плагин определяет testFixturesApi и testFixturesImplementation конфигурации:
dependencies { testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.5.2") testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.5.2") }
С нашими зависимостями на месте, давайте добавим тестовый микс JUnit 5-в src/test Fixtures/java исходный набор, созданный плагином java-test-fixtures . Этот тестовый микс проверяет контракт нашего Генератора последовательностей Фибоначчи абстракции:
public interface FibonacciSequenceGeneratorFixture { FibonacciSequenceGenerator provide(); @Test default void whenSequenceIndexIsNegative_thenThrows() { FibonacciSequenceGenerator generator = provide(); assertThrows(IllegalArgumentException.class, () -> generator.generate(-1)); } @Test default void whenGivenIndex_thenGeneratesFibonacciNumber() { FibonacciSequenceGenerator generator = provide(); int[] sequence = { 0, 1, 1, 2, 3, 5, 8 }; for (int i = 0; i < sequence.length; i++) { assertEquals(sequence[i], generator.generate(i)); } } }
Это все, что нам нужно сделать, чтобы поделиться этим тестовым приспособлением с другими проектами .
Теперь давайте создадим новый проект fibonacci-recursive , который будет повторно использовать это тестовое приспособление. Этот проект объявит зависимость от тестовых приспособлений из нашего проекта fibonacci-spi , используя метод test Fixtures в нашем блоке dependencies :
dependencies { api(project(":fibonacci-spi")) testImplementation(testFixtures(project(":fibonacci-spi"))) testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.5.2") }
Наконец, теперь мы можем использовать тестовый микс, определенный в проекте fibonacci-spiral , для создания нового теста для нашего рекурсивного генератора последовательностей фибоначчи:
class RecursiveFibonacciUnitTest implements FibonacciSequenceGeneratorFixture { @Override public FibonacciSequenceGenerator provide() { return new RecursiveFibonacci(); } }
Gradle 6.0 java-test-fixtures plugin дает авторам сборок больше гибкости для совместного использования своих тестовых приспособлений в разных проектах .
3. Публикация метаданных модуля Gradle
Традиционно проекты Gradle публикуют артефакты сборки в репозиториях Ivy или Maven. Это включает в себя генерацию ivy.xml или pom.xml файлы метаданных соответственно.
В ivy.xml и pom.xml модели не могут хранить богатую информацию о зависимостях, которую мы обсуждали в этой статье. Это означает, что нижестоящие проекты не извлекают выгоду из этой богатой информации о зависимостях, когда мы публикуем нашу библиотеку в репозитории Maven или Ivy .
Gradle 6.0 устраняет этот пробел, вводя спецификацию метаданных модуля Gradle . Спецификация метаданных модуля Gradle-это формат JSON, который поддерживает хранение всех расширенных метаданных зависимостей модулей, представленных в Gradle 6.0.
Проекты могут создавать и публиковать этот файл метаданных в репозиториях Ivy и Maven в дополнение к традиционным ivy.xml и pom.xml файлы метаданных. Эта обратная совместимость позволяет проектам Gradle 6.0 использовать метаданные этого модуля, если они присутствуют , не нарушая устаревшие инструменты.
Чтобы опубликовать файлы метаданных модуля Gradle, проекты должны использовать новый плагин Maven Publish/| или Плагин Ivy Publish . Начиная с Gradle 6.0, эти плагины по умолчанию публикуют файл метаданных модуля Gradle. Эти плагины заменяют устаревшую систему публикации|/.
3.1. Публикация метаданных модуля Gradle в Maven
Давайте настроим сборку для публикации метаданных модуля Gradle в Maven. Во-первых, мы включаем maven-publish в наш файл сборки:
plugins { `java-library` `maven-publish` }
Далее мы настраиваем публикацию. Публикация может содержать любое количество артефактов. Давайте добавим артефакт, связанный с конфигурацией java :
publishing { publications { register("mavenJava", MavenPublication::class) { from(components["java"]) } } }
Плагин maven-publish добавляет задачу publishToMavenLocal . Давайте используем эту задачу для тестирования публикации метаданных нашего модуля Gradle:
./gradlew publishToMavenLocal
Далее давайте перечислим каталог для этого артефакта в вашем локальном репозитории Maven:
ls ~/.m2/repository/com/baeldung/gradle-6/1.0.0/ gradle-6-1.0.0.jar gradle-6-1.0.0.module gradle-6-1.0.0.pom
Как мы видим в выводе консоли, Gradle генерирует файл метаданных модуля в дополнение к Maven POM.
4. API предотвращения конфигурации
Начиная с версии 5.1, Gradle поощряла разработчиков плагинов использовать новые, инкубирующие API для предотвращения конфигурации. Эти API помогают сборкам избежать относительно медленных шагов настройки задач, когда это возможно . Gradle называет это улучшением производительности Предотвращение конфигурации задач . Gradle 6.0 продвигает этот инкубационный API до стабильного состояния.
В то время как функция предотвращения конфигурации в основном влияет на авторов плагинов, авторы сборки, которые создают любую пользовательскую Конфигурацию , Задачу или Свойство в своей сборке, также подвержены влиянию. Авторы плагинов и авторы сборок теперь могут использовать новые ленивые API конфигурации для обертывания объектов с типом Provider , чтобы Gradle избегал “реализации” этих объектов до тех пор, пока они не понадобятся .
Давайте добавим пользовательскую задачу с помощью ленивых API. Сначала мы регистрируем задачу с помощью метода Task Container.registering extension. Поскольку регистрация возвращает Поставщика задач , создание экземпляра Задачи откладывается до тех пор, пока Gradle или автор сборки не вызовет TaskProvider.get() . Наконец, мы предоставляем закрытие, которое настроит нашу Задачу после того, как Gradle создаст ее:
val copyExtraLibs by tasks.registering(Copy::class) { from(extralibs) into(extraLibsDir) }
Руководство по настройке задач Gradle Руководство по миграции помогает авторам плагинов и авторам сборок переходить на новые API. Наиболее распространенные миграции для авторов сборки включают:
- tasks.register вместо tasks.create
- tasks.named вместо tasks.getByName
- конфигурации.зарегистрировать вместо конфигурации.создать
- project.layout.build Directory.dir(“foo”) вместо File(project.buildDir, “foo”)
5. Поддержка JDK 13
Gradle 6.0 вводит поддержку строительных проектов с помощью JDK 13. Мы можем настроить вашу сборку Java для использования Java 13 со знакомыми sourceCompatibility и targetCompatibility настройками:
sourceCompatibility = JavaVersion.VERSION_13 targetCompatibility = JavaVersion.VERSION_13
Некоторые из наиболее интересных языковых функций JDK13, такие как необработанные строковые литералы, все еще находятся в состоянии предварительного просмотра . Давайте настроим задачи в нашей сборке Java, чтобы включить эти функции предварительного просмотра:
tasks.compileJava { options.compilerArgs.add("--enable-preview") } tasks.test { jvmArgs.add("--enable-preview") } tasks.javadoc { val javadocOptions = options as CoreJavadocOptions javadocOptions.addStringOption("source", "13") javadocOptions.addBooleanOption("-enable-preview", true) }
6. Заключение
В этой статье мы обсудили некоторые новые функции в Gradle 6.0.
Мы рассмотрели расширенное управление зависимостями, публикацию метаданных модуля Gradle, предотвращение конфигурации задач и то, как ранние пользователи могут настроить свои сборки для использования функций языка предварительного просмотра Java 13.
Как всегда, код для этой статьи находится на GitHub .