Как стать автором
Поиск
Написать публикацию
Обновить
357.48

Прочие оптимизации кода Gradle Convention Plugins, выводы по результатам использования подхода

Уровень сложностиСредний
Время на прочтение6 мин
Количество просмотров1.8K

Всем привет! На связи Дима Котиков, и мы завершаем цикл статей о том, как облегчить себе жизнь и уменьшить boilerplate в gradle-файлах. В предыдущих статьях мы подготовили и настроили базовый модуль для написания Gradle Convention Plugins, написали несколько convention-плагинов в файлах -.gradle.kts, сделали еще один модуль и создали convention-плагины на базе kotlin-классов. В заключительной части мы немного порефакторим написанный код, попытаемся настроить области видимости convention-плагинов и extension-функций для конфигурации сборки, а также подведем итоги. 

Рефакторинг зависимостей в composite builds

В разработке нет ничего более постоянного, чем временное рефакторинг. Взглянем на содержимое модуля convention-plugins/base и увидим, что некоторые extension-функции и плагины мы бы не хотели отдавать пользователям в руки, а хотели бы использовать только при написании кастомных плагинов. Например, плагины базовых конфигураций и extension-ы вроде Project.libs, Project.androidConfig, которые и так доступны в файлах build.gradle.kts модулей проекта через сгенерированные accessor-ы или из-за и так подключенных плагинов. 

Мы бы не хотели терять красивые extension-функции для указания зависимостей для каждого таргета и функцию для конфигурации iOS Framework. Можно просто и безболезненно перенести файлы IosExtensions.kt и KmpDependenciesExtensions.kt в модуль convention-plugins/project, что и сделаем:

Перенос IosExtensions.kt и KmpDependenciesExtensions.kt в модуль convention-plugins/project
Перенос IosExtensions.kt и KmpDependenciesExtensions.kt в модуль convention-plugins/project

Теперь ничто не мешает убрать подключение модуля convention-plugins/base из файла settings.gradle.kts корневого проекта:

Исключение модуля convention-plugins/base из settings.gradle.kts корневого проекта
Исключение модуля convention-plugins/base из settings.gradle.kts корневого проекта

Синхронизируем проект, пытаемся запустить — все работает. Для проверки того, что код из convention-plugins/base недоступен, пробуем добавить в composeApp/build.gradle.kts плагин android.base.config и extension-функцию androidConfig:

Проверка отключения convention-plugins/base
Проверка отключения convention-plugins/base

Пытаемся синхронизировать проект, запустить… И он синхронизируется и запускается, хотя мы хотели бы падать. Это происходит потому, что плагины из convention-plugins/base подключены в convention-plugins/project, а также в build.gradle.kts корневого проекта остался подключенным мок-плагин base.plugin. Давайте его удалим:

Удаление base.plugin из build.gradle.kts корневого проекта 
Удаление base.plugin из build.gradle.kts корневого проекта 

Пробуем синхронизироваться, запустить проект — все равно работает. Все потому, что плагин, подключенный через includeBuild, транзитивно отдает свою логику. Частично эту проблему можно решить, если в convention-plugins/project/build.gradle.kts поменять содержимое plugins-блока:

Смена конфигурации plugins-блока в convention-plugins/project/build.gradle.kts
Смена конфигурации plugins-блока в convention-plugins/project/build.gradle.kts

Тогда при попытке собрать проект перестанут быть видны convention-плагины, описанные в файлах -build.gradle.kts, но это все равно не решает проблему с тем, что остаются видными extension-функции из модуля convention-plugins/base.

Пока я не нашел способа эффективно скрыть код из подключенного как includeBuild проекта без применения модификаторов видимости на классах. Делитесь в комментариях, если знаете.

В общий код можно вынести объявление точки входа в compose-desktop на случай, если в проекте предполагается несколько app-модулей. Сейчас в composeApp/build.gradle.kts это выглядит вот так:

Объявление точки входа для desktop-таргета
Объявление точки входа для desktop-таргета

Вынесем это в отдельный extension — добавим зависимости на compose-плагины в convention-plugins/project/build.gradle.kts, как и в convention-plugins/base/build.gradle.kts, и создадим отдельный файл - ComposeMultiplatformExtensions.kt в модуле convention-plugins/project.

Чтобы сконфигурировать extension, мы должны понять, что именно конфигурировать. Провалимся в реализацию функции compose.desktop {}, откроется сгенерированный файл accessor:

Accessor-файл со сгенерированной функцией compose.desktop {} и extension property
Accessor-файл со сгенерированной функцией compose.desktop {} и extension property

Видим, что содержимое функции скрыто. Попробуем в лоб сконфигурировать DesktopExtension, который у нас приходит в функции ComposeExtension.desktop(). Заполним ComposeMultiplatformExtensions.kt:

package io.github.dmitriy1892.conventionplugins.project.extensions
 
import io.github.dmitriy1892.conventionplugins.base.extensions.libs
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.jetbrains.compose.desktop.DesktopExtension
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
 
fun Project.composeDesktopApplication(
    mainClass: String,
    packageName: String,
    version: String = libs.versions.appVersionName.get(),
    targetFormats: List<TargetFormat> = listOf(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
) {
    configure<DesktopExtension> {
        application {
            this.mainClass = mainClass
             
            nativeDistributions {
                targetFormats(*targetFormats.toTypedArray())
                this.packageName = packageName
                this.packageVersion = version
            }
        }
    }
}

Применим наш extension в composeApp/build.gradle.kts:

Применение composeDesktopApplication в composeApp/build.gradle.kts
Применение composeDesktopApplication в composeApp/build.gradle.kts

Пробуем собрать, видим ошибку:

Ошибка конфигурации DesktopExtension
Ошибка конфигурации DesktopExtension

Ошибка указывает, что DesktopExtension не найден в проекте. Скорее всего, он достается другим способом, но каким? Чтобы выяснить, вернемся к accessor-файлу и декомпилируем его в java-класс:

Путь до инструмента декомпиляции kotlin-файла в java
Путь до инструмента декомпиляции kotlin-файла в java
Декомпилированный accessor
Декомпилированный accessor

Видим, что на вход функции приходит ComposeExtension, который достается из Project.extensions, потом у ComposeExtension вызывается getExtensions(), — и только уже в этих экстеншенах конфигурируется desktop. Теперь можем вернуться в ComposeMultiplatformExtensions.kt и откорректировать внутрянку нашей extension-функции:

package io.github.dmitriy1892.conventionplugins.project.extensions
 
import io.github.dmitriy1892.conventionplugins.base.extensions.libs
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.getByType
import org.jetbrains.compose.ComposeExtension
import org.jetbrains.compose.desktop.DesktopExtension
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
 
fun Project.composeDesktopApplication(
    mainClass: String,
    packageName: String,
    version: String = libs.versions.appVersionName.get(),
    targetFormats: List<TargetFormat> = listOf(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
) {
    extensions.getByType<ComposeExtension>().extensions.configure<DesktopExtension> {
        application {
            this.mainClass = mainClass
 
            nativeDistributions {
                targetFormats(*targetFormats.toTypedArray())
                this.packageName = packageName
                this.packageVersion = version
            }
        }
    }
}

Пробуем синхронизироваться — успешно. Собираем desktop-таргет командой ./gradlew :composeApp:run — все работает.

Выводы, плюсы и минусы подхода

Пришла пора подвести черту, указать на достоинства и недостатки подхода.

Плюсы подхода:

  • При применении convention-плагинов может существенно сократиться размер файлов build.gradle.kts из-за выделения части конфигураций в плагины и extension-функции.

  • Создание и настройка нового модуля упрощаются за счет применения convention-плагинов с обобщенной логикой.

  • Основная логика с настройкой сборки модулей собрана в одном месте. В нашем примере — в двух модулях.

  • Миграция на новые версии плагинов gradle-wrapper/agp/kmp с какими-либо breaking changes становится легче, потому что изменения нужно будет внести точечно в конкретных convention-плагинах и extension-функциях вместо кучи файлов build.gradle.kts модулей.

Минусы подхода:

  • Увеличение скорости сборки — сначала должны собраться модули с нашими плагинами, подключенные как composite builds, а только потом основной проект.

  • При изменении обобщенных плагинов есть риск сломать сборку в модулях и других плагинах, которые его используют.

  • Чтобы понять, что происходит в build.gradle.kts-файлах основного проекта, нужно смотреть, из чего состоит convention plugin, что в нем сконфигурировано и подключено из внешних зависимостей.

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

Выводы:

  • Стоит здраво оценить, нужно ли внедрять convention-плагины в конкретном проекте. Для пет-проектов с 1—2 модулями это может быть избыточным, но для production-проектов с большим количеством модулей подход однозначно будет полезен за счет обобщения логики и вытекающих из этого плюсов.

  • Нужно разбивать общие части в плагины так, чтобы модули можно было гибко сконфигурировать. Плагины для feature-модулей могут быть избыточными для core/common-модулей.

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

  • Важно знать, когда остановиться. Не стоит переусложнять внутреннюю реализацию convention-плагинов.

  • Стоит перестать бояться Gradle и навести порядок в скриптах сборки, это не так уж и сложно! :) 

Ссылки на предыдущие части:

  1. Gradle Convention Plugins: как облегчить себе жизнь и уменьшить boilerplate в gradle-файлах

  2. Создание плагинов и переиспользуемых частей в .gradle.kts-файлах и Kotlin extension-функциях

  3. Создание Convention Plugin-ов на базе Kotlin-классов

Теги:
Хабы:
Всего голосов 7: ↑7 и ↓0+11
Комментарии2

Публикации

Информация

Сайт
l.tbank.ru
Дата регистрации
Дата основания
Численность
свыше 10 000 человек
Местоположение
Россия