С самого появления Gradle существовало 2 способа разбить свою сборку на компоненты: через бинарные зависимости и с помощью многопроектной сборки. Каждый из этих способов имеет свои плюсы и минусы. В случае с бинарными зависимостями возникает необходимость в публикации артефактов, что усложняет сборку. В случае использования многопроектной сборки становится
сложнее изолировать компоненты друг от друга.
Композитные сборки
В готовящейся к релизу версии 3.1 в Gradle появляется новый поход к организации сборок, состоящих из нескольких компонентов: композитные сборки (ориг. Composite Builds).
Композитные сборки позволяют:
- Быстро подложить исправленную версию исходников библиотеки в другой проект без необходимости собирать её, опубликовывать и править сборку.
- Делить большие проекты на несколько небольших, изолированных сборок, над каждой из которых можно работать как по отдельности, так и одновременно.
- Отделить разработку плагина для системы сборки от проекта, его использующего (аналог
buildSrc
)
Разберем простой пример использования новой возможности.
Для этого создадим простенький проектик:
--app/
|-src/main/java/Main.java
|-build.gradle
- lib/
|-src/main/java/A.java
|-build.gradle
|-settings.gradle
lib/build.gradle:
apply plugin: 'java'
group "ru.shadam"
version "1.0"
task wrapper(type: Wrapper) {
gradleVersion = '3.1-rc-1'
}
app/build.gradle
apply plugin: 'java'
apply plugin: 'application'
mainClassName='Main'
dependencies {
compile 'ru.shadam:lib1:1.0'
}
task wrapper(type: Wrapper) {
gradleVersion = '3.1-rc-1'
}
Теперь, если мы попробуем запустить app
с помощью команды ./gradlew run
Gradle будет ругаться на неразрешенную зависимость:
$ ./gradlew run
:compileJava
FAILURE: Build failed with an exception.
* What went wrong:
Could not resolve all dependencies for configuration ':compileClasspath'.
> Cannot resolve external dependency ru.shadam:lib1:1.0 because no repositories are defined.
Required by:
project :
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 1.027 secs
Но, если мы добавим новый флаг --include-build, то Gradle разрешит зависимости автоматически:
$ ./gradlew run --include-build ../lib1
[composite-build] Configuring build: C:\Users\sala\projects\gradle-compose\lib1
:compileJava
:lib1:compileJava UP-TO-DATE
:lib1:processResources UP-TO-DATE
:lib1:classes UP-TO-DATE
:lib1:jar UP-TO-DATE
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run
Hello
BUILD SUCCESSFUL
Total time: 1.092 secs
Продвинутые варианты использования.
Встраиваем --include-build в скрипт
Представленный выше вариант больше подходит для одноразового использования — здесь и сейчас. Каждый раз указывать флаги не хочется — даже если зашить их во wrapper.
Для этого gradle предлагает использовать конфигурацию с использованием settings.gradle. Так, указанный выше флаг можно заменить с помощью следующего settings.gradle:
includeBuild('../lib1')
Подстановки
Что если в проекте, который вы хотите включить не указаны координаты артефакта? (группа, версия)
На помощь приходят подстановки:
includeBuild('../lib1') {
dependencySubstitution {
substitute('ru.shadam:lib1') with project(':')
}
}
Эта возможность позволяет подставить любую зависимость на ru.shadam:lib1
зависимостью на проект lib1
.
Зависимости между задачами
В случае композитной сборки проекты изолированы друг от друга, поэтому нельзя объявлять зависимости между сборками напрямую.
В связи с этим появился новый синтаксис для доступа к включаемым сборкам. Например, можно определить зависимость от задачи включенной сборки:
task run {
dependsOn gradle.includedBuild('lib1').task(':jar')
}
Что пока не работает?
- Не поддерживаются в качестве включаемых проекты, у которых есть публикуемые артифакты, которые не соответствуют конфигурации по умолчанию. ссылка
- Пока нет поддержки в IDE (но поддерживается генерация проекта с помощью команды
./gradlew idea
) - Не поддерживаются native builds.
Планы команды
- Добавить возможность вызывать задачи напрямую из включенных сборок.
- Добавить возможность параллельно исполнять включенные сборки
- Добавить обнаружение изменений во включенных сборках, когда используется флаг
-t
. - Сделать неявный проект buildSrc включенной сборкой.
xkor
А что то из этого не позволяют сделать многопроектные сборки?
saladinkzn
Многопроектная сборка — это дерево в файловой системе. Композитные сборки же позволяют разделить собираемые проекты в файловой системе со всеми вытекающими отсюда преимуществами.
К тому же, у Gradle появляются гарантии, что включаемые сборки не зависят друг от друга, поэтому они собираются сделать возможность запускать их параллельно — а это уже должно дать прирост к производительности
Prototik
Ну, формально нет, проекты можно было тянуть из совершенно раных мест. Этот composite builds — лишь сахар над многопроектной сборкой, имхо. Я не говорю, что он бесполезен, но и новых возможностей он не привносит.
saladinkzn
Тут есть отличие в том, что:
Но, вообще говоря, да: это комбинация из фичи с подстановками + возможность делать include из командной строки.
То есть никто не мешает сделать тоже самое, не используя Composite build: создать проект, заинклюдить туда :lib1 и :app и сделать Dependency Substituion.