Перейти к основному содержимому

Что нового в Gradle 6.0

· 10 мин. чтения

1. Обзор

В выпуске Gradle 6.0 появилось несколько новых функций, которые помогут сделать наши сборки более эффективными и надежными. Эти функции включают улучшенное управление зависимостями, публикацию метаданных модулей, предотвращение конфигурации задач и поддержку JDK 13.

В этом руководстве мы познакомим вас с новыми функциями, доступными в Gradle 6.0. В нашем примере файлы сборки будут использовать Gradle Kotlin DSL.

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. Предположим далее, что в этом проекте используется 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, так и от foo , возникает конфликт. По умолчанию Gradle выбирает последнюю версию. Однако в этом случае выбор последней версии является неправильным выбором , потому что foo должен использовать версию 16.0.

До Gradle 6.0 пользователям приходилось самостоятельно решать конфликты. Поскольку Gradle 6.0 позволяет нам сообщить Gradle, что наш проект может использовать версии Guava от 10.0, Gradle правильно разрешит этот конфликт и выберет версию 16.0.

В дополнение к объявлениям require и Prefer мы можем использовать объявленияstrict и reject . Строгое объявление описывает диапазон версий зависимостей, который должен использовать наш проект. Объявление отказа описывает версии зависимостей, которые несовместимы с нашим проектом. ``

Если наш проект основан на API, который, как мы знаем, будет удален в Guava 29, тогда мы используем строгое объявление, чтобы запретить Gradle использовать версию Guava выше 28. Аналогичным образом, если мы знаем, что в Guava 27.0 есть ошибка, которая вызывает проблемы для нашего проекта, мы используем reject , чтобы исключить его:

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-платформы позволяет нам повторно использовать набор ограничений зависимостей в проектах. Автор платформы объявляет набор тесно связанных зависимостей, версии которых контролируются платформой.

Проекты, зависящие от платформы, не должны указывать версии для каких-либо зависимостей, контролируемых платформой. Пользователи Maven найдут это похожим на функцию dependencyManagement родительского POM Maven .

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

Давайте создадим новую платформу, чтобы убедиться, что наша многопроектная сборка использует одну и ту же версию HTTP-клиента Apache во всех проектах. Во- первых, мы создаем проект 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 , который использует Apache HTTP Client Fluent API. Здесь мы добавляем зависимость от нашего проекта httpclient-platform , используя метод платформы . Мы также добавим зависимость от org.apache.httpcomponents:fluent-hc . Эта зависимость не включает версию, поскольку платформа httpclient определяет используемую версию:

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 . Разработчики генератора последовательности Фибоначчи могут использовать тестовую примесь, чтобы убедиться, что они правильно внедрили генератор последовательности.

Во-первых, давайте создадим новый проект fibonacci-spi для нашей абстракции и тестовых приборов. Для этого проекта требуются плагины java-library и java-test-fixtures :

plugins {
`java-library`
`java-test-fixtures`
}

Далее давайте добавим зависимости JUnit 5 к нашим тестовым приборам. Так же, как подключаемый модуль java-library определяет конфигурации API и реализации , подключаемый модуль java-test-fixtures определяет конфигурации testFixturesApi и testFixturesImplementation :

dependencies {
testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.1")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}

Установив зависимости, давайте добавим тестовую смесь JUnit 5 в исходный набор src/testFixtures/java , созданный подключаемым модулем java-test-fixtures . Этот тестовый микс проверяет контракт нашей абстракции FibonacciSequenceGenerator :

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 , используя метод testFixtures в нашем блоке зависимостей :

dependencies {
api(project(":fibonacci-spi"))

testImplementation(testFixtures(project(":fibonacci-spi")))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}

Наконец, теперь мы можем использовать тестовую смесь, определенную в проекте fibonacci-spi , чтобы создать новый тест для нашего рекурсивного генератора последовательности Фибоначчи:

class RecursiveFibonacciUnitTest implements FibonacciSequenceGeneratorFixture {
@Override
public FibonacciSequenceGenerator provide() {
return new RecursiveFibonacci();
}
}

Плагин Gradle 6.0 java-test-fixtures дает авторам сборок больше гибкости для совместного использования своих тестовых фикстур между проектами .

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 Plugin . Начиная с 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/foreach/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. Сначала мы регистрируем задачу с помощью метода расширения TaskContainer.registering . Поскольку регистрация возвращает TaskProvider , создание экземпляра Task откладывается до тех пор, пока 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
  • configurations.register вместо configurations.create
  • project.layout.buildDirectory.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

Некоторые из самых интересных языковых функций JDK 13, такие как Raw String Literals, все еще находятся в состоянии предварительной версии . Давайте настроим задачи в нашей сборке 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 .