Преамбула
Есть своя «внешняя» библиотека и есть своё приложение, использующее эту библиотеку (подгружается через внешний репозитарий). Требуется внести изменение и в библиотеку и в приложение.
Казалось бы, собери библиотеку и выложи её в локальный maven-репозитарий, а потом уже собирай приложение. Но хочется, чтобы можно было поправив код в библиотеке сразу попробовать изменения в приложении и при этом сохранить раздельное хранение кода библиотеки и приложения, включая настройки IDE и прочее.
С помощью gradle и символических связей в файловой системе такое можно легко устроить.
Библиотека
Для начала приведу пример содержимого build.gradle в библиотеке:
import java.text.SimpleDateFormat
apply plugin: 'java'
apply plugin: 'maven-publish'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
jar.baseName = 'library'
publishing {
publications {
mavenJava(MavenPublication) {
groupId='name.alenkov.habr.gradle-dynamic-dependency'
version = new SimpleDateFormat('yyyyMMddHHmm').format(new Date())
from components.java
}
}
}
Здесь ключевым является строка «version = new SimpleDateFormat('yyyyMMddHHmm').format(new Date())», которая задаёт версию сборки на основе текущего времени в момент её публикации в репозитарий — в остальное время версия для библиотеки нам не требуется.
Disclaimer: В нашем демонстрационном приложении мы не поддерживаем более одной ветки библиотек в продуктовой среде и потому нет потребности в поддержке версионности вида X.Y.Z
Note: в примерах я использую локальный Maven и не привожу примеры с использованием Artifactory, т.к. это не влияет на подход.
Приложение
Теперь перейдём к настройке нашего build.gradle в приложении.
Исходное состояние build.gradle:
apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenLocal()
jcenter()
}
dependencies {
compile 'name.alenkov.habr.gradle-dynamic-dependency:library:+'
}
Теперь давайте модифицируем конфигурацию gradle так, чтобы получить желаемое — динамическое подключение модулей.
Первый шагом, слинкуем нашу библиотеку в проект приложения в подкаталог ext:
cd ./app/ext
ln -s ../../library/ ./
Вторым шагом добавим небольшой код в settings.gradle, который сканирует каталог "/ext" на наличие gradle-проектов и будет подключать их к нам в проект:
final extDir = new File(rootDir, 'ext')
if (extDir.exists()) {
extDir.eachDir { dir ->
if (new File(dir, 'build.gradle').exists()) {
logger.trace('found ext module: ' + dir.name)
final String prjName = ':' + dir.name
logger.lifecycle('include ext project: ' + prjName)
include prjName
project(prjName).projectDir = dir
project(prjName).name = 'ext-' + dir.name
}
}
}
И третий, заключительный штрих — модифицируем секцию dependencies в build.gradle:
dependencies {
compile subprojects.find({ it.name == 'ext-library' }) ? project(':ext-library')
: 'name.alenkov.habr.gradle-dynamic-dependency:library:+'
}
и пример, когда в library есть дополнительные gradle-задачи:
Gradle-плагин
Аналогичным способом можно добавить и внешние gradle-плагины.
Шаги:
1. Создаём каталог buildSrc
2. Создаём файл buildSrc/settings.gradle
final extDir = rootDir
if (extDir.exists()) {
extDir.eachDir { dir ->
if (new File(dir, 'build.gradle').exists()) {
logger.trace('found ext plugin: ' + dir.name)
final String prjName = ':' + dir.name
logger.lifecycle('include ext plugin: ' + prjName)
include prjName
project(prjName).projectDir = dir
project(prjName).name = 'ext-plugin-' + dir.name
}
}
}
3. Создаём файл buildSrc/build.gradle
dependencies {
runtime subprojects.collect { owner.project(it.path) }
}
Теперь достаточно прилинковать в buildSrc внешний плагин, как он подхватится проектом в работу:
cd ./app/buildSrc
ln -s ../../gradle-plugin/ ./
Из текущего неудобства — в IDEA динамические модули и плагины подключаются не совсем корректно — баг и баг.
Исходный код обоих модулей и плагина можно посмотреть на github
UPD: аналогичным способом можно подключать и плагины gradle, только монтируя их в buildSrc. Если будут желающие, могу написать пример…
UPD: добавил пример с плагином
Поделиться с друзьями
Комментарии (6)
evkaky
05.06.2017 11:56Эта проблема очень легко решается штатными средствами gradle — composite builds. Никакого дополнительного кода в build.gradle или settings.gradle при этом не появится.
https://docs.gradle.org/current/userguide/composite_builds.html
Composite builds, помимо прочего, отлично интегрированы в intellij
https://www.youtube.com/watch?v=grPJanXfRPgBorz
05.06.2017 11:57пункт 10.2.4 накладывает ограничения, которые в моём способе обходятся. Да ещё и зависимость будет дублироваться при композитной сборке, т.к. она не исключается, а добавляется в сборку
da_42
Полезно, спасибо. И я за пример для gradle плагина :)
Borz
добавил пример с плагином