diff --git a/learning/gradle/build-environment.md b/learning/gradle/build-environment.md index ceecbcf3e..c79e4f0e3 100644 --- a/learning/gradle/build-environment.md +++ b/learning/gradle/build-environment.md @@ -8,20 +8,21 @@ Gradle предоставляет некоторые механизмы наст конкретных проектов. Параметры Gradle можно установить в четырех разных местах: -- Глобально - глобальные свойства применяются на уровне пользователя или системы. Целью глобальных свойств является применение общих настроек в нескольких проектах Gradle. Например, использование глобально применяемых паролей упрощает обслуживание и устраняет необходимость копирования паролей в несколько областей. +- Глобально — глобальные свойства применяются на уровне пользователя или системы. Целью глобальных свойств является применение общих настроек в нескольких проектах Gradle. Например, использование глобально применяемых паролей упрощает обслуживание и устраняет необходимость копирования паролей в несколько областей. - Такие параметры хранятся в директории `.gradle` папки вашей учетной записи, например на Mac это `~/.gradle/gradle.properties`. -- В проекте - свойства проекта применяются ко всему проекту Gradle, например, к проекту moko-template. + Такие параметры хранятся в директории `.gradle` папки вашей учетной записи, например, на Mac это `~/.gradle/gradle.properties`. +- В проекте — свойства проекта применяются ко всему проекту Gradle, например, к проекту moko-template. Используйте свойства на уровне проекта для управления поведением Gradle по всему проекту. Например, можно указать необходимые переменные, будет ли кешироваться сборка, ну или включить "отложенную конфигурацию" проекта. Эти параметры хранятся в `gradle.properties` проекта. -- В module - "модульные" свойства применяются к одному модулю в проекте. Например, вы можете использовать свойства уровня модуля для хранения адресов доступа к api. +- В модуле — «модульные» свойства применяются к одному модулю в проекте. Например, вы можете использовать свойства уровня модуля для хранения адресов доступа к api. Модульные параметры хранятся в `gradle.properties` конкретного модуля проекта, например, `moko-template/android-app`. -- В командной строке, например: -Dorg.gradle.java.home, этот флаг указывает директорию используемого jdk. Флаги командной строки имеют приоритет над свойствами и переменными среды. +- В командной строке, например, `-Dorg.gradle.java.home` — флаг указывает директорию используемого JDK. Флаги командной строки имеют приоритет над свойствами и переменными среды. + Альтернатива — Gradle Toolchains: в `build.gradle.kts` можно указать `kotlin { jvmToolchain(17) }`, и Gradle сам найдёт подходящий JDK. -Файл `gradle.properties` состоит из пар ключ-значение параметров настройки запуска Gradle. Использование `gradle.properties` это альтернатива использованию флагов командной строки для конфигурации проекта. +Файл `gradle.properties` состоит из пар ключ-значение параметров настройки запуска Gradle. Использование `gradle.properties` — это альтернатива использованию флагов командной строки для конфигурации проекта. :::info Также вместо параметров в файле `gradle.properties` вы можете указывать переменные вашего Gradle-окружения. @@ -32,7 +33,7 @@ Gradle предоставляет некоторые механизмы наст Как мы уже выяснили, файл глобальных свойств должен находиться в вашем домашнем каталоге: - В Windows: `C:\Users\\.gradle\gradle.properties` -- На Mac/Linux: `~\.gradle\gradle.properties` +- На Mac/Linux: `~/.gradle/gradle.properties` Следующие свойства могут быть использованы для настройки среды сборки Gradle: @@ -42,17 +43,16 @@ Gradle предоставляет некоторые механизмы наст - ***org.gradle.daemon=(true,false)*** - ***org.gradle.daemon.idletimeout=(# of idle millis)*** - Прочитать об этих параметрах и найти остальные можете [тут](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties). +Прочитать об этих параметрах и найти остальные можете [тут](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties). -Параметры, объявленные глобально, будут применены для любого запущенного от текущего пользователя -Gradle проекта. +Параметры, объявленные глобально, будут применены для любого запущенного от текущего пользователя Gradle проекта. Рассмотрим пример: ```bash -# ~/.gradle/gradle.properties +# ~/.gradle/gradle.properties -# На запуск jvm будет выделено 6 GB памяти +# На запуск JVM будет выделено 6 GB памяти org.gradle.jvmargs=-Xmx6g # максимальное количество "воркеров" делаем равным трем @@ -69,17 +69,17 @@ org.gradle.workers.max=3 Например, в нашем [boilerplate-проекте](https://gitlab.icerockdev.com/scl/boilerplate/mobile-moko-boilerplate) используются такие параметры: ```bash -# выделение памяти jvm +# выделение памяти JVM org.gradle.jvmargs=-Xmx4096m -# параметр отложенной конфигурации -# это когда градл конфигурирует проект только в тот момент, -# когда от проекта что-то потребовалось. -# это должно ускорять работу гредла, но по факту до сих пор слабо поддерживается плагинами +# параметр отложенной конфигурации (deprecated, вместо него используйте configuration cache) +# Gradle конфигурирует проект, только когда от него что-то потребовалось org.gradle.configureondemand=false # параллельное выполнение задач (при возможности) org.gradle.parallel=true -# включение кеширование сборок (для ускорения) +# включение кэширования сборок (для ускорения) org.gradle.caching=true +# включение configuration cache — даёт наибольший прирост скорости (доступен с Gradle 8.1) +org.gradle.configuration-cache=true # использовать официальный стандарт кода kotlin.code.style=official @@ -87,21 +87,25 @@ kotlin.code.style=official # плагин Android будет использовать библиотеку AndroidX вместо стандартной библиотеки android.useAndroidX=true -# отключить предупреждения о том, что технология mpp является экспериментальной +# формат директорий android source set (version=2 значит src/androidMain/kotlin) +kotlin.mpp.androidSourceSetLayoutVersion=2 +# отключить предупреждения о нестабильности мультиплатформы (KMP стабилен с 2023 года) kotlin.mpp.stability.nowarn=true -# отключить предупреждение об использовании ios таргета +# отключить static framework warning в moko-resources +moko.resources.disableStaticFrameworkWarning=true + +# отключить предупреждение об использовании iOS таргета # из mobile-multiplatform-gradle-plugin mobile.multiplatform.iosTargetWarning=false # указание версии проекта для Android таргета # версия iOS таргета меняется в другом месте -# это единственный кейс версионирования через gradle.properties -# в проекте +# это единственный кейс версионирования через gradle.properties в проекте VERSION_NAME=0.1.0 VERSION_CODE=1 -# переменная, хранящая путь до нашего xcode-проекта +# переменная, хранящая путь до нашего Xcode-проекта xcodeproj=ios-app/ios-app.xcworkspace ``` @@ -124,7 +128,7 @@ xcodeproj=ios-app/ios-app.xcworkspace wrapper'а: ```bash -./gradlew build -Dorg.gradle.jvmargs= #... +./gradlew build -Dorg.gradle.jvmargs="-Xmx2g" ``` :::important @@ -142,7 +146,7 @@ wrapper'а: org.gradle.jvmargs=-Xmx8g ``` -При настройке этого параметра стоит учитывать количество ОЗУ на вашем устройстве и оставлять пару резервных ГБ. Например, если на устройстве у вас 16 ГБ ОЗУ, то можете спокойно выделять 8 ГБ на работу Java-машины, остальной памяти вам будет достаточно, чтобы комфортно пользоваться устройством во время сборки проекта. Если вы не будете выставлять этот параметр ни в проекте, ни глобально, то Gradle по умолчанию выставит `-Xmx512m`. +При настройке этого параметра стоит учитывать количество ОЗУ на вашем устройстве и оставлять пару резервных ГБ. Например, если на устройстве у вас 16 ГБ ОЗУ, то можете спокойно выделять 8 ГБ на работу Java-машины – остальной памяти вам будет достаточно, чтобы комфортно пользоваться устройством во время сборки проекта. Если вы не будете выставлять этот параметр ни в проекте, ни глобально, то Gradle по-умолчанию выставит `-Xmx512m`. ```bash org.gradle.workers.max=3 @@ -157,10 +161,14 @@ org.gradle.workers.max=3 ## Влияет на скорость сборки -Также сильное влияние (помимо персонализированных настроек из предыдущего пункта) на скорость сборки +Также сильное влияние на скорость сборки (помимо персонализированных настроек из предыдущего пункта) оказывают параметры: - `org.gradle.parallel=true` - `org.gradle.caching=true` +- `org.gradle.configuration-cache=true` (доступен с Gradle 8.1) + +Параметр `org.gradle.configureondemand` считается устаревшим (deprecated) — вместо него используйте +configuration-cache, он даёт гораздо больший прирост скорости. Данные параметры должны быть указаны в проектных настройках, чтобы они применялись у всех разработчиков. diff --git a/learning/gradle/buildSrc.md b/learning/gradle/buildSrc.md index cfdf26581..fbafd353b 100644 --- a/learning/gradle/buildSrc.md +++ b/learning/gradle/buildSrc.md @@ -4,6 +4,36 @@ sidebar_position: 6 # buildSrc +`buildSrc` — это специальная директория в корне Gradle-проекта. Всё, что лежит в `buildSrc/src/main/kotlin`, +автоматически компилируется и становится доступным в `build.gradle.kts` всех модулей проекта. + +Раньше buildSrc был популярным способом вынести общие версии зависимостей: + +```kotlin +// buildSrc/src/main/kotlin/Deps.kt +object Deps { + const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2" +} +``` + +## Недостатки buildSrc + +- Любое изменение в buildSrc **инвалидирует весь build cache** проекта — Gradle пересобирает всё с нуля +- buildSrc не поддерживает номера версий и не публикуется — это часть проекта +- С ростом проекта сборка buildSrc замедляется, так как не закеширована + +## Альтернатива: convention plugins (рекомендуется) + +Современный подход — выносить общую логику в convention plugins через `includeBuild` (отдельный +Gradle-проект `build-logic`). Подробнее — в разделе [Convention plugins](./intro-gradle#convention-plugins-build-logic). + +Преимущества: +- Изменения в convention plugins **не трогают build cache** основного проекта +- Convention plugins можно версионировать и публиковать +- Поддерживаются плагином `kotlin-dsl`, дающим автодополнение в IDE + +## Материалы +

diff --git a/learning/gradle/check-yourself.md b/learning/gradle/check-yourself.md index d6e7778f8..e7bbceda7 100644 --- a/learning/gradle/check-yourself.md +++ b/learning/gradle/check-yourself.md @@ -6,7 +6,7 @@ sidebar_position: 11 ## Практические задачи -Результатом выполнения следующих задач должен быть репозиторий на https://github.com / +Результатом выполнения следующих задач должен быть репозиторий на https://github.com или https://gitlab.icerockdev.com с 4 ветками (каждое задание в своей ветке). Полное выполнение задач можно посмотреть в видео Gradle с нуля. Более детальная информация в конкретных страницах раздела Gradle (смотрите по названиям). @@ -25,11 +25,11 @@ https://gitlab.icerockdev.com с 4 ветками (каждое задание 6. Добавить AuthViewModel и ProfileViewModel (с использованием moko-mvvm) в соответствующие фичи 7. Сделать в mpp-library фабрику для создания этих вьюмоделей 8. Вызвать из android-app создание вьюмоделей -9. Сделать компиляцию mpp-library в ios framework -10. Сделать ios-app xcode проект +9. Сделать компиляцию mpp-library в iOS framework +10. Сделать ios-app Xcode проект 11. Настроить интеграцию ios-app с mpp-library через cocoapods 12. Вызвать в ios-app создание вьюмоделей -13. Сделать gradle task которая выводит все sourceSet’ы проекта (задача должна быть доступна во всех подпроектах) +13. Сделать gradle task, которая выводит все sourceSet’ы проекта (задача должна быть доступна во всех подпроектах) 14. Сделать sh скрипт для максимально быстрой проверки компилируемости обоих приложений и мультиплатформы 15. Сделать sh скрипт для максимально быстрой проверки компилируемости андроид приложения 16. Сделать sh скрипт для максимально быстрой проверки компилируемости ios приложения @@ -37,22 +37,22 @@ https://gitlab.icerockdev.com с 4 ветками (каждое задание ### Задача N2 1. Взять проект-результат задачи N1 -2. Вынести зависимости (моко мввм и другие подключенные, если есть) в Deps объект внутри buildSrc -3. Тамже завести Modules объект с указанием путей до всех модулей -4. Подключить новые константы вместо хардкод-строк и убедиться что все работает +2. Вынести зависимости (moko-mvvm и другие подключенные, если есть) в Deps объект внутри buildSrc +3. Там же завести Modules объект с указанием путей до всех модулей +4. Подключить новые константы вместо хардкод-строк и убедиться, что все работает ### Задача N3 1. Взять проект-результат задачи N1 2. Выделить повторяющуюся между подпроектами логику в convention плагины внутри composite build’а (не buildSrc) -3. Подключить convention плагины и убедиться что все работает +3. Подключить convention плагины и убедиться, что все работает ### Задача N4 1. Взять проект-результат задачи N1 2. Вынести зависимости в version catalog, а подпроекты связать через projects accessors -3. Подключить вместо хардкод строк новые константы и убедиться что все работает +3. Подключить вместо хардкод строк новые константы и убедиться, что все работает ## Тестирование -Coming soon +Скоро появится diff --git a/learning/gradle/composite-build.md b/learning/gradle/composite-build.md index b9c22d314..312966b16 100644 --- a/learning/gradle/composite-build.md +++ b/learning/gradle/composite-build.md @@ -4,9 +4,35 @@ sidebar_position: 7 # Composite builds +Composite build (композитная сборка) — это подключение одного самостоятельного Gradle-проекта к сборке +другого. В `settings.gradle.kts` это выглядит так: + +```kotlin +includeBuild("build-logic") +``` + +В отличие от `buildSrc`, composite build — это полноценный Gradle-проект со своим `settings.gradle.kts`, +`build.gradle.kts` и версиями. Его можно даже опубликовать и переиспользовать между разными проектами. + +## Зачем нужен + +Composite build — основа для **convention plugins** (см. раздел [Convention plugins](./intro-gradle#convention-plugins-build-logic)). +Вместо того чтобы дублировать настройки Gradle в каждом модуле, вы выносите общую логику в `build-logic` +и подключаете его через `includeBuild`. + +## Composite build vs buildSrc + +| | buildSrc | Composite build | +|-------------------|---------------------------------------------|---------------------------------------------------| +| Инвалидация cache | Любое изменение сбрасывает весь кеш проекта | Кеш проекта не трогается | +| Версионирование | Нет | Можно публиковать | +| Скорость | Замедляется на больших проектах | Работает как обычный Gradle-проект с кешированием | + +## Материалы +

-- [Gradle docs - Composing builds](https://docs.gradle.org/7.0/userguide/composite_builds.html) +- [Gradle docs — Composing builds](https://docs.gradle.org/current/userguide/composite_builds.html) - [How to use Composite builds as a replacement of buildSrc in Gradle](https://medium.com/bumble-tech/how-to-use-composite-builds-as-a-replacement-of-buildsrc-in-gradle-64ff99344b58) diff --git a/learning/gradle/configuration.md b/learning/gradle/configuration.md index 85f4bbae6..b4f5ce508 100644 --- a/learning/gradle/configuration.md +++ b/learning/gradle/configuration.md @@ -27,7 +27,7 @@ Gradle представляет область зависимости с пом ![test-project-struct](configuration/gradle-deps-conf-test-project-struct.png) -Создадим два подпроекта: `LibA` и `LibB`. В директории каждого из этих подпроекта создадим собственный +Создадим два подпроекта: `LibA` и `LibB`. В директории каждого из этих подпроектов создадим собственный `build.gradle.kts` файл для настройки сборки. А в рутовом `build.gradle.kts` подключим плагин `kotlin-jvm`: ```kotlin @@ -37,11 +37,10 @@ Gradle представляет область зависимости с пом // подключение плагина plugins { - kotlin("jvm") version ("1.5.21") + kotlin("jvm") version ("2.1.10") } -// указывает в каких репозиториях -// искать нужные зависимости +// указывает, в каких репозиториях искать нужные зависимости allprojects { repositories { mavenCentral() @@ -101,6 +100,7 @@ dependencies { ``` В корне нашего проекта заведем директорию `src/main/kotlin` с файлом `Main.kt`, которая и будет входной точкой нашего приложения: + ```kotlin /* * project/src/main/kotlin/Main.kt @@ -173,7 +173,7 @@ dependencies { к классам в самом сценарии сборки. В корневом `build.gradle.kts` как раз используется блок `buildscript`. Объявить путь к классам сценария сборки вы можете, использовав метод `classpath`. -Для мультипроектной сборки, зависимости, объявленные с помощью метода `buildscript()`, доступны для сценариев сборки всех его подпроектов. +Для мультипроектной сборки зависимости, объявленные с помощью метода `buildscript()`, доступны для сценариев сборки всех его подпроектов. Рассмотрим небольшой пример, в котором мы подключим уже знакомый нам плагин `kotlin-jvm`, но не через метод `plugins()`. @@ -187,7 +187,7 @@ buildscript { gradlePluginPortal() } dependencies { - classpath("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm:gradle.plugin:1.5.20") + classpath("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm:gradle.plugin:2.1.10") } } ``` @@ -201,86 +201,7 @@ buildscript { gradle buildEnvironment ``` -Вы увидите такую архитектуру зависимостей: - -```bash ------------------------------------------------------------- -Root project 'testProject' ------------------------------------------------------------- -classpath -+--- org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.5.21 -| \--- org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21 -| +--- org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-native-utils:1.5.21 -| | | \--- org.jetbrains.kotlin:kotlin-util-io:1.5.21 -| | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 -| | | +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.4.31 -| | | \--- org.jetbrains:annotations:13.0 -| | \--- org.jetbrains.kotlin:kotlin-project-model:1.5.21 -| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| +--- org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.5.21 -| +--- org.jetbrains.kotlin:kotlin-util-klib:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| | \--- org.jetbrains.kotlin:kotlin-util-io:1.5.21 (*) -| +--- org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| | \--- org.jetbrains.kotlin:kotlin-native-utils:1.5.21 (*) -| +--- org.jetbrains.kotlin:kotlin-tooling-metadata:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| | \--- com.google.code.gson:gson:2.8.6 -| +--- org.jetbrains.kotlin:kotlin-project-model:1.5.21 (*) -| +--- com.google.code.gson:gson:2.8.6 -| +--- com.google.guava:guava:29.0-jre -| | +--- com.google.guava:failureaccess:1.0.1 -| | +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava -| | +--- com.google.code.findbugs:jsr305:3.0.2 -| | +--- org.checkerframework:checker-qual:2.11.1 -| | +--- com.google.errorprone:error_prone_annotations:2.3.4 -| | \--- com.google.j2objc:j2objc-annotations:1.3 -| +--- de.undercouch:gradle-download-task:4.1.1 -| +--- com.github.gundy:semver4j:0.16.4 -| +--- org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| | +--- org.jetbrains.kotlin:kotlin-script-runtime:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-reflect:1.5.21 -> 1.4.31 -| | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.4.31 (*) -| | +--- org.jetbrains.kotlin:kotlin-daemon-embeddable:1.5.21 -| | \--- org.jetbrains.intellij.deps:trove4j:1.0.20181211 -| +--- org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| | \--- org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.21 (*) -| +--- org.jetbrains.kotlin:kotlin-android-extensions:1.5.21 -| | \--- org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.21 (*) -| +--- org.jetbrains.kotlin:kotlin-compiler-runner:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-build-common:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-daemon-client:1.5.21 -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8 -| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.71 -> 1.4.31 (*) -| | | | \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.3.71 -> 1.4.31 -| | | \--- org.jetbrains.kotlin:kotlin-reflect:1.5.21 -> 1.4.31 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8 (*) -| | \--- org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.21 (*) -| +--- org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.5.21 -| | +--- org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.5.21 -| | | +--- org.jetbrains.kotlin:kotlin-scripting-common:1.5.21 -| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| | | | \--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8 (*) -| | | +--- org.jetbrains.kotlin:kotlin-scripting-jvm:1.5.21 -| | | | +--- org.jetbrains.kotlin:kotlin-script-runtime:1.5.21 -| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| | | | \--- org.jetbrains.kotlin:kotlin-scripting-common:1.5.21 (*) -| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| | | \--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8 (*) -| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.21 -> 1.4.31 (*) -| \--- org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.5.21 (*) -+--- org.jetbrains.kotlin:kotlin-stdlib:{strictly 1.4.31} -> 1.4.31 (c) -+--- org.jetbrains.kotlin:kotlin-reflect:{strictly 1.4.31} -> 1.4.31 (c) -+--- org.jetbrains.kotlin:kotlin-stdlib-common:{strictly 1.4.31} -> 1.4.31 (c) -\--- org.jetbrains:annotations:{strictly 13.0} -> 13.0 (c) -``` - -Проверим, появились ли таски, которые предоставляет подключенный нами плагин. Для этого выполним задачу -`tasks` из терминала или IDE: +Вы увидите список зависимостей в classpath. А чтобы проверить, какие задачи появились у плагина, — выполните `tasks` из терминала или IDE: ```bash gradle tasks @@ -312,12 +233,12 @@ tasks - Displays the tasks runnable from root project 'testProject' (some of the ``` Таски не появились, т.к. плагин, подключенный при помощи `classpath`, сразу не применяется. -Чтобы заюзать этот плагин в нашем рутовом `build.gradle.kts`, необходимо использовать метод `apply()`. +Чтобы применить этот плагин в нашем рутовом `build.gradle.kts`, необходимо использовать метод `apply()`. В подпроектах же вы можете подключить этот плагин, используя привычный метод `plugins()`. -Это происходит из-за того, что сборщик gradle не может проиндексировать id плагина, +Это происходит из-за того, что сборщик Gradle не может проиндексировать ID плагина, подключенного в том же build-файле, в котором тот добавляется в classpath. - ```kotlin +```kotlin /* * project/build.gradle.kts */ @@ -389,45 +310,24 @@ buildscript { mavenCentral() google() gradlePluginPortal() - - jcenter { - content { - includeGroup("org.jetbrains.trove4j") - } - } + maven(url = "https://jitpack.io") } // добавление зависимостей в выполнение gradle скриптов - // как мы уже выяснили они действительны для любых подпроектов dependencies { - classpath("dev.icerock.moko:resources-generator:0.16.1") - classpath("dev.icerock.moko:network-generator:0.16.0") - classpath("dev.icerock.moko:units-generator:0.6.1") - classpath("org.jetbrains.kotlin:kotlin-serialization:1.5.20") - classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") - classpath("com.google.gms:google-services:4.3.8") - classpath("com.google.dagger:hilt-android-gradle-plugin:2.35") + classpath(libs.moko.resourcesGeneratorGradle) + classpath(libs.moko.networkGeneratorGradle) + classpath(libs.kotlinSerializationGradle) + classpath(libs.firebaseCrashlyticsGradle) + classpath(libs.googleServicesGradle) + classpath(libs.navigationPlugin) classpath(":build-logic") } } -allprojects { - // принудительное использование coroutines-native-mt - configurations.configureEach { - resolutionStrategy { - val coroutines: MinimalExternalModuleDependency = rootProject.libs.coroutines.get() - val forcedCoroutines: ModuleVersionSelector = DefaultModuleVersionSelector.newSelector( - coroutines.module, - coroutines.versionConstraint.requiredVersion - ) - force(forcedCoroutines) - } - } -} - // таска на очистку билдов проекта tasks.register("clean", Delete::class).configure { group = "build" - delete(rootProject.buildDir) + delete(rootProject.layout.buildDirectory) } ``` @@ -446,22 +346,25 @@ plugins { repositories { mavenCentral() google() - gradlePluginPortal() + + maven { url = uri("https://jitpack.io") } } -// подключение зависимостей к композитному проекту -// предоставляющий функционал внутренних библиотек +// подключение зависимостей к композитному проекту, +// предоставляющему функционал внутренних библиотек dependencies { - api("dev.icerock:mobile-multiplatform:0.12.0") - api("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21") - api("com.android.tools.build:gradle:4.2.1") - api("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0") + api(libs.moko.multiplatformPlugin) + api(libs.kotlinGradlePlugin) + api(libs.androidGradlePlugin) + api(libs.detektGradlePlugin) + api(libs.skieGradle) + api(libs.composeGradlePlugin) } ``` Для дальнейшего изучения нужно понимать, что такое sourceset'ы, о них вы можете прочитать -[тут](https://kotlinlang.org/docs/mpp-dsl-reference.html#source-sets). +[тут](https://kotlinlang.org/docs/multiplatform-dsl-reference.html#source-sets). Если мы хотим использовать зависимости для конкретного sourceset'а, мы можем воспользоваться следующим шаблоном: @@ -476,47 +379,49 @@ dependencies { * mobile-moko-boilerplate/mpp-library/build.gradle.kts */ +plugins { + id("multiplatform-library-convention") + id("org.jetbrains.kotlin.native.cocoapods") + id("kotlinx-serialization") +} + +kotlin { + cocoapods { + framework { + baseName = "MultiPlatformLibrary" + export(libs.multiplatformSettings) + export(libs.napier) + export(libs.moko.resources) + } + } +} + dependencies { - // зависимости, нужные для внутренней логики модуля - // подключаются к sourceset'у commonMain commonMainImplementation(libs.coroutines) commonMainImplementation(libs.kotlinSerialization) commonMainImplementation(libs.ktorClient) commonMainImplementation(libs.ktorClientLogging) + commonMainImplementation(libs.ktorClientAuth) + commonMainImplementation(libs.moko.network) - // зависимости, нужные для внутренней логики модуля, в android sourceset'е + // зависимости, нужные для androidMain sourceset'а androidMainImplementation(libs.lifecycleViewModel) - // зависимости, нужные для sourceset'а commonMain самого модуля - // и для пользователей библиотеки - commonMainApi(projects.mppLibrary.feature.auth) + // зависимости, которые видны пользователям библиотеки (commonMainApi) commonMainApi(libs.multiplatformSettings) commonMainApi(libs.napier) - commonMainApi(libs.mokoParcelize) - commonMainApi(libs.mokoResources) - commonMainApi(libs.mokoMvvmCore) - commonMainApi(libs.mokoMvvmLiveData) - commonMainApi(libs.mokoMvvmState) - commonMainApi(libs.mokoUnits) - commonMainApi(libs.mokoFields) - commonMainApi(libs.mokoNetwork) - commonMainApi(libs.mokoErrors) - commonMainApi(libs.mokoNetworkErrors) - commonMainApi(libs.mokoCrashReportingCore) - commonMainApi(libs.mokoCrashReportingCrashlytics) - commonMainApi(libs.mokoCrashReportingNapier) - - // зависимости, нужные для внутренней логики модуля, в тестовом sourceset'е - commonTestImplementation(libs.mokoTestCore) - commonTestImplementation(libs.mokoMvvmTest) - commonTestImplementation(libs.mokoUnitsTest) - commonTestImplementation(libs.multiplatformSettingsTest) - commonTestImplementation(libs.ktorClientMock) + commonMainApi(libs.moko.resources) + + // модули проекта + commonMainApi(projects.mppLibrary.utils) + + // тесты + commonTestImplementation(projects.mppLibrary.testUtils) } ``` ## Материалы -- [Документация - Multiplatform Gradle DSL reference](https://kotlinlang.org/docs/mpp-dsl-reference.html) +- [Документация - Multiplatform Gradle DSL reference](https://kotlinlang.org/docs/multiplatform-dsl-reference.html) - [Документация - Gradle declaring dependencies](https://docs.gradle.org/current/userguide/declaring_dependencies.html) -- [Документация - MPP Dependencies](https://kotlinlang.org/docs/mpp-add-dependencies.html) +- [Документация - MPP Dependencies](https://kotlinlang.org/docs/multiplatform-add-dependencies.html) diff --git a/learning/gradle/convention-plugins.md b/learning/gradle/convention-plugins.md index 6c2a0450a..c480b47f9 100644 --- a/learning/gradle/convention-plugins.md +++ b/learning/gradle/convention-plugins.md @@ -12,4 +12,85 @@ sidebar_position: 8

-- [Gradle docs - Convention plugins](https://docs.gradle.org/7.0/userguide/sharing_build_logic_between_subprojects.html#sec:convention_plugins ) +Convention plugins (precompiled script plugins) — это Gradle-плагины, которые подключаются по короткому +имени и содержат переиспользуемую конфигурацию. Они — современная замена `buildSrc`. + +## Как устроены + +В отдельном Gradle-проекте (`build-logic`) в директории `src/main/kotlin` лежат файлы +`*.gradle.kts`. Имя файла становится ID плагина. + +``` +build-logic/ +├── build.gradle.kts # подключает `kotlin-dsl` plugin +├── settings.gradle.kts # импортирует version catalog из корня проекта +└── src/main/kotlin/ + ├── base-convention.gradle.kts + ├── multiplatform-library-convention.gradle.kts + └── android-app-convention.gradle.kts +``` + +Пример плагина из boilerplate: + +```kotlin +// build-logic/src/main/kotlin/multiplatform-library-convention.gradle.kts +plugins { + id("base-convention") + id("com.android.library") + id("android-base-convention") + id("org.jetbrains.kotlin.multiplatform") +} + +kotlin { + androidTarget() + iosX64() + iosArm64() + iosSimulatorArm64() + + sourceSets { + applyDefaultHierarchyTemplate() + } +} +``` + +Подключение в модуле: + +```kotlin +// mpp-library/build.gradle.kts +plugins { + id("multiplatform-library-convention") + id("detekt-convention") + id("org.jetbrains.kotlin.native.cocoapods") +} +``` + +## Как подключить build-logic к проекту + +В `settings.gradle.kts` корня проекта: + +```kotlin +includeBuild("build-logic") +``` + +А в `build-logic/settings.gradle.kts` — импорт version catalog из основного проекта: + +```kotlin +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} +``` + +Это позволяет convention plugins использовать те же версии зависимостей, что и весь проект. + +## Convention plugins vs buildSrc + +Convention plugins решают главную проблему buildSrc: изменения в них **не инвалидируют build cache** +основного проекта. Подробное сравнение — в разделе [buildSrc](./buildSrc). + +## Материалы + +- [Gradle docs — Convention plugins](https://docs.gradle.org/current/userguide/sharing_build_logic_between_subprojects.html#sec:convention_plugins) diff --git a/learning/gradle/gradle-wrapper.md b/learning/gradle/gradle-wrapper.md index f607c7aa3..cc37f0148 100644 --- a/learning/gradle/gradle-wrapper.md +++ b/learning/gradle/gradle-wrapper.md @@ -4,18 +4,18 @@ sidebar_position: 3 # Gradle Wrapper -[Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) (или короче говоря, просто "Wrapper") - +[Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) (или короче говоря, просто "Wrapper") — это специальный скрипт (а также несколько дополнительных файлов), который вызывает объявленную версию Gradle, при необходимости загружая ее заранее. :::important -Рекомендуемый способ выполнения любой сборки Gradle - это с помощью Gradle Wrapper'а. +Рекомендуемый способ выполнения любой сборки Gradle - с помощью Gradle Wrapper'а. ::: ## Содержимое К его файлам относятся: -- `gradlew` и `gradlew.bat` - сами скрипты для запуска gradle через wrapper; +- `gradlew` и `gradlew.bat` — сами скрипты для запуска gradle через wrapper; - `gradle/wrapper/gradle-wrapper.jar` - сам wrapper, небольшая java программа; - `gradle/wrapper/gradle-wrapper.properties` - настройки gradle wrapper'а, в которых указывается версия gradle для всего проекта. @@ -42,7 +42,7 @@ gradlew.bat 2. Считывается конфигурация из `gradle-wrapper.properties`, а именно - `distributionUrl`, в котором определено, какую версию gradle нам нужно скачать. 3. Если данная версия gradle уже скачивалась, то она доступна в кешах в директории `~/.gradle` и будет использоваться. - Иначе же Gradle Wrapper скачает gradle нужной версии и сохранит в указанную выше кеш директорию. + Иначе же Gradle Wrapper скачает gradle нужной версии и сохранит в указанную выше кеш-директорию. 4. Запускает gradle нужной версии, передавая ему все опции запуска, которые были переданы в Gradle Wrapper. Таким образом, несколько небольших файлов, лежащих в git репозитории, позволяют разработчику не вспоминать о @@ -65,8 +65,7 @@ Gradle Wrapper автоматически сохраняет скачиваем ```bash # PROJECT_DIR/gradle/gradle-wrapper.properties -# определяет, следует ли хранить распакованный дистрибутив-оболочку в проекте -# или в домашнем каталоге пользователя gradle. +# определяет базовый каталог для хранения дистрибутивов Gradle distributionBase=GRADLE_USER_HOME # путь, по которому распаковываются дистрибутивы gradle, необходимые для оболочки @@ -74,7 +73,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists # URL-адрес для загрузки дистрибутива gradle -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip # указание путей для распаковки zipStoreBase=GRADLE_USER_HOME diff --git a/learning/gradle/intro-gradle.md b/learning/gradle/intro-gradle.md index 9fbdda918..42ea8f768 100644 --- a/learning/gradle/intro-gradle.md +++ b/learning/gradle/intro-gradle.md @@ -5,92 +5,93 @@ sidebar_position: 0 # Введение в Gradle Работая с Kotlin Multiplatform, для iOS разработчика главным испытанием становится не изучение -Kotlin, а изучение билд системы Gradle, которая собирает мультиплатформенную библиотеку. В данном -разделе разобрано что есть Gradle с перспективы iOS разработчиков. +Kotlin, а изучение билд-системы Gradle, которая собирает мультиплатформенную библиотеку. В данном +разделе разобрано, что из себя представляет Gradle с точки зрения iOS разработчиков. ## Gradle -[Gradle](https://gradle.org/) это система сборки, имеющая гибкую систему конфигурации через плагины +[Gradle](https://gradle.org/) — это система сборки, имеющая гибкую систему конфигурации через плагины и позволяющая описывать конфигурацию сборки в виде kotlin файлов. -Задача Gradle, как и любой системы сборки, скомпилировать исходный код в исполняемое приложение, -либо подключаемую библиотеку. Благодаря ему разработчику не требуется писать команды вызова +Задача Gradle, как и любой системы сборки, скомпилировать исходный код в исполняемое приложение +или подключаемую библиотеку. Благодаря ему разработчику не требуется писать команды вызова компилятора kotlin и передавать ему список исполняемых файлов, подключенных библиотек и прочее. -Также Gradle имеет управление зависимостями (подключение внешних библиотек или разных модулей одного -проекта). Зависимости скачиваются с Maven репозиториев, -например [mavenCentral](https://maven.apache.org/repository/index.html) (хоть сам Maven тоже -является билдсистемой, но Gradle использует от него только репозитории, на которых хранятся +Также Gradle имеет управление зависимостями (подключение внешних библиотек или разных модулей одного проекта). +Зависимости скачиваются с Maven репозиториев, например [mavenCentral](https://maven.apache.org/repository/index.html) +(хоть сам Maven тоже является билд-системой, но Gradle использует от него только репозитории, на которых хранятся скомпилированные опубликованные зависимости). -Gradle написан на java и является JVM (Java Virtual Machine) приложением, то есть для его -использования требуется установленная на исполняемой машине JDK (Java Development Kit). Наиболее -стабильная версия JDK - Oracle JDK (рекомендуется к -скачиванию [Oracle JDK 11](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html) для -работы с KMP). +Gradle написан на Java и является JVM (Java Virtual Machine) приложением, то есть для его +использования требуется установленная на исполняемой машине JDK (Java Development Kit). +Наиболее стабильная версия JDK — JDK 17 или новее (минимальная версия для Gradle 8.x). JDK можно скачать +на сайте [Adoptium](https://adoptium.net/) (Temurin) или использовать версию, встроенную в Android Studio. -Gradle имеет обширную, подробную документацию, -доступную [тут](https://docs.gradle.org/current/userguide/userguide.html). +Gradle имеет обширную, подробную документацию, доступную [тут](https://docs.gradle.org/current/userguide/userguide.html). ## Gradle Daemon -Это долгоживущий фоновый процесс, который запускается на выбранной JVM при первой сборке. Он помогает избежать затратного процесса начальной загрузки JVM, при этом кешируя данные о ваших предыдущих билдах в память, что заметно ускоряет скорость сборки проекта. +Это долгоживущий фоновый процесс, который запускается на выбранной JVM при первой сборке. Он помогает избежать +затратного процесса начальной загрузки JVM, при этом кешируя данные о ваших предыдущих билдах в память, +что заметно ускоряет скорость сборки проекта. -Подробнее о Gradle Daemon и как его подключать к проекту вы можете прочитать [тут](https://docs.gradle.org/current/userguide/gradle_daemon.html) и [тут](https://docs.gradle.org/current/userguide/command_line_interface.html#gradle_daemon_options). +Подробнее о Gradle Daemon и его подключении к проекту вы можете прочитать +[тут](https://docs.gradle.org/current/userguide/gradle_daemon.html) и [тут](https://docs.gradle.org/current/userguide/command_line_interface.html#gradle_daemon_options). ## Контекст для понимания дальнейших разделов 1. Gradle при каждом запуске проходит по нескольким фазам - инициализация, конфигурация, выполнение. -2. Файлы gradle во всех новых проектах написаны на Kotlin Script, расширение файла `.gradle.kts`. При использовании Kotlin Script IDE предоставляет полноценный анализ с подсказками. На старых проектах могут встретиться файлы gradle на groovy (тогда расширение просто `.gradle`). +2. Файлы gradle во всех новых проектах написаны на Kotlin Script, расширение файла `.gradle.kts`. + При использовании Kotlin Script IDE предоставляет полноценный анализ с подсказками. + На старых проектах могут встретиться файлы gradle на Groovy (тогда расширение просто `.gradle`). ## Составляющие конфигурации проекта Проект, использующий Gradle в качестве системы сборки, содержит: -1. `settings.gradle.kts` - настройки проекта, например подключение модулей - проекта; +1. `settings.gradle.kts` - настройки проекта, например подключение модулей проекта; 2. `build.gradle.kts` - конфигурация конкретного gradle модуля; -3. `gradle.properties` - файл содержащий набор ключ+значение передаваемыми в gradle. +3. `gradle.properties` - файл, содержащий набор пар ключ+значение, передаваемых в Gradle. ### settings.gradle Файл с настройками всего проекта (данные настройки влияют на все модули). -Во всех новых проектах на kotlin - `settings.gradle.kts`, в старых может быть написан на groovy (тогда имя `settings.gradle`). -Подробная информация -в [документации](https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:settings_file) -. -Код в данном файле выполняется в момент инициализации проекта (при каждом запуске градл происходит -по стадиям инициализация, конфигурация, выполнение). +Во всех новых проектах на Kotlin - `settings.gradle.kts`, в старых может быть написан на Groovy (тогда имя `settings.gradle`). +Подробная информация в [документации](https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:settings_file). + +Код в данном файле выполняется в момент инициализации проекта (при каждом запуске Gradle происходит +по стадиям: инициализация, конфигурация, выполнение). Пример содержимого с пояснениями: ``` -// блок pluginManagement позволяет настроить работу с плагинами билдсистемы +// Блок pluginManagement позволяет настроить работу с плагинами билдсистемы pluginManagement { - // определяем список maven репозиториев, в которых нужно искать подключаемые плагины. - // Если данный блок не объявлять то будет использоваться gradlePluginPortal - https://plugins.gradle.org/ + // Определяем список maven репозиториев, в которых нужно искать подключаемые плагины. + // Если данный блок не объявлять, то будет использоваться gradlePluginPortal - https://plugins.gradle.org/ repositories { mavenCentral() google() } } -// данный блок позволяет настроить для всех модулей проекта работу с зависимостями +// Данный блок позволяет настроить для всех модулей проекта работу с зависимостями dependencyResolutionManagement { - // определяем список maven репозиториев, в которых нужно искать подключаемые библиотеки. + // Определяем список maven репозиториев, в которых нужно искать подключаемые библиотеки. repositories { mavenCentral() google() } } -// подключение composite build - является темой для продвинутого погружения, обычно на проектах это не встретить -// если кратко - это подключение другого самостоятельного gradle проекта к сборке нашего проекта, с возможностью подключать модули подключенного проекта как внешние зависимости в нашем проекте +// Подключение composite build - является темой для продвинутого погружения, обычно на проектах это не встречается. +// Если кратко - это подключение другого самостоятельного gradle проекта к сборке нашего проекта, +// с возможностью подключать модули подключенного проекта как внешние зависимости в нашем проекте. // https://docs.gradle.org/current/userguide/composite_builds.html includeBuild("network-generator") -// подключение модулей проекта, каждый из них будет определяться как gradle модуль и будет читаться его build.gradle файл -// двоеточие в пути обозначает уровень иерархии в файловой структуре. +// Подключение модулей проекта, каждый из них будет определяться как gradle модуль и будет читаться его build.gradle файл. +// Двоеточие в пути обозначает уровень иерархии в файловой структуре. include(":network") include(":sample:mpp-library") ``` @@ -98,70 +99,69 @@ include(":sample:mpp-library") _Является упрощенным вариантом с [moko-network](https://github.com/icerockdev/moko-network/blob/master/settings.gradle.kts)_. -(!) Основной сценарий когда iOS разработчику нужно работать с файлом `settings.gradle.kts` - разработчик -сам создает новый gradle модуль и нужно подключить его к билдсистеме. То есть -добавляет `include(":mymodule")`. +(!) Основной сценарий, когда iOS разработчику нужно работать с файлом `settings.gradle.kts` - разработчик +сам создает новый gradle модуль, и нужно подключить его к билдсистеме (то есть добавляет `include(":mymodule")`). ### build.gradle -Файл с конфигурацией модуля gradle проекта. Определяет всю логику сборки данного модуля (что -собираем, как собираем). -В новых проектах на kotlin - `build.gradle.kts`, в старых может быть написан на groovy (тогда имя `build.gradle`). -Подробная информация -в [документации](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html). +Файл с конфигурацией модуля gradle проекта. Определяет всю логику сборки данного модуля (что собираем, как собираем). +В новых проектах на Kotlin - `build.gradle.kts`, в старых может быть написан на Groovy (тогда имя `build.gradle`). +Подробная информация в [документации](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html). Пример содержимого с пояснениями: ``` -// подключение плагинов, которые и содержат всю основую логику сборки +// Подключение плагинов, которые и содержат всю основую логику сборки plugins { - // плагин для сборки android библиотек. Требуется у нас в проектах так как мы собираем из мультиплатформы android код, помимо ios - // подробнее - https://developer.android.com/studio/build/index.html + // Плагин для сборки android библиотек. Требуется у нас в проектах, так как мы собираем из мультиплатформы android код, помимо ios. + // Подробнее - https://developer.android.com/studio/build/index.html id("com.android.library") - // плагин мультиплатформы. дает возможность собирать kotlin код разными компиляторами - Kotlin/JVM, Kotlin/JS, Kotlin/Native. - // подробнее - https://kotlinlang.org/docs/mpp-dsl-reference.html + // Плагин мультиплатформы, дает возможность собирать kotlin код разными компиляторами - Kotlin/JVM, Kotlin/JS, Kotlin/Native. + // Подробнее - https://kotlinlang.org/docs/multiplatform-dsl-reference.html id("org.jetbrains.kotlin.multiplatform") - // наш плагин мобильной мультиплатформы, упрощает настройку градл проектов для mobile использования (android, ios) - // подробнее - https://github.com/icerockdev/mobile-multiplatform-gradle-plugin + // Наш плагин мобильной мультиплатформы, упрощает настройку градл проектов для mobile использования (android, ios). + // Подробнее - https://github.com/icerockdev/mobile-multiplatform-gradle-plugin id("dev.icerock.mobile.multiplatform") - // плагин для генерации кода сериализации в момент компиляции, от библиотеки kotlinx.serialization - // подробнее - https://github.com/Kotlin/kotlinx.serialization + // Плагин для генерации кода сериализации в момент компиляции, от библиотеки kotlinx.serialization. + // Подробнее - https://github.com/Kotlin/kotlinx.serialization id("org.jetbrains.kotlin.plugin.serialization") } -// объявление зависимостей данного модуля. Чем меньше зависимостей объявлено, тем быстрее будет производиться компиляция модуля. -// зависимости ищутся в репозиториях, которые могут быть указаны как в самом build.gradle, так и в settings.gradle централизованно +// Объявление зависимостей данного модуля. Чем меньше зависимостей объявлено, тем быстрее будет производиться компиляция модуля. +// Зависимости ищутся в репозиториях, которые могут быть указаны как в самом build.gradle, так и в settings.gradle централизованно dependencies { - // подключение зависимости к common коду, в виде реализации (implementation). Это означает что классы данной зависимости не будут видны вне данного модуля, без явного ее подключения. - commonMainImplementation(Deps.Libs.MultiPlatform.coroutines) + // Подключение зависимости к common коду, в виде реализации (implementation). + // Это означает, что классы данной зависимости не будут видны вне данного модуля без явного ее подключения. + commonMainImplementation(libs.coroutines) - // подключение зависимости к common коду, транзитивно (api). Это означает что классы данной зависимости будут видны вне данного модуля при подключении нашего модуля. - commonMainApi(Deps.Libs.MultiPlatform.kotlinSerialization) - commonMainApi(Deps.Libs.MultiPlatform.ktorClient) + // Подключение зависимости к common коду, транзитивно (api). + // Это означает, что классы данной зависимости будут видны вне данного модуля при подключении нашего модуля. + commonMainApi(libs.kotlinSerialization) + commonMainApi(libs.ktorClient) - // подключение зависимости к андроид таргету, транзитивно. Классы данной зависимости видны только в androidMain сорссете. - androidMainApi(Deps.Libs.Android.ktorClientOkHttp) + // Подключение зависимости к android таргету, транзитивно. Классы данной зависимости видны только в androidMain сорссете. + androidMainApi(libs.ktorClient.okHttp) - // подключение зависимости к ios таргету, транзитивно. Классы данной зависимости видны только в iosMain сорссете. - iosMainApi(Deps.Libs.Ios.ktorClientIos) + // Подключение зависимости к ios таргету, транзитивно. Классы данной зависимости видны только в iosMain сорссете. + iosMainApi(libs.ktorClient.ios) - // подключение другого модуля нашего проекта, в виде реализации + // Подключение другого модуля нашего проекта, в виде реализации. commonMainImplementation(project(":network")) - // подключение зависимостей к общему коду тестов, в виде реализации. - commonTestImplementation(Deps.Libs.MultiPlatform.ktorClientMock) - commonTestImplementation(Deps.Libs.MultiPlatform.Tests.kotlinTest) - commonTestImplementation(Deps.Libs.MultiPlatform.Tests.kotlinTestAnnotations) + // Подключение зависимостей к общему коду тестов, в виде реализации. + commonTestImplementation(libs.ktorClient.mock) + commonTestImplementation(libs.kotlinTest) + commonTestImplementation(libs.kotlinTestAnnotations) - // подключение зависимостей к андроид таргету тестов, в виде реализации. - androidTestImplementation(Deps.Libs.Android.Tests.kotlinTestJUnit) + // Подключение зависимостей к android таргету тестов, в виде реализации. + androidTestImplementation(libs.kotlinTestJUnit) } ``` _Является упрощенным вариантом с [moko-network](https://github.com/icerockdev/moko-network/blob/master/network/build.gradle.kts)_. -Основные сценарий когда iOS разработчику нужно работать с файлом `build.gradle`: +Основные сценарии, когда iOS разработчику нужно работать с файлом `build.gradle`: 1. Подключение новой зависимости к модулю 2. Подключение плагина с дополнительным функционалом ( @@ -183,23 +183,25 @@ org.gradle.configureondemand=false # включение параллельной сборки - разные gradle модули могут выполнять свои задачи параллельно org.gradle.parallel=true -# какой вариант кодстайла котлина используеся в проекте - используется IDE для включения верного кодстайла +# какой вариант кодстайла kotlin используется в проекте — используется IDE для включения верного кодстайла kotlin.code.style=official -# специальные флаги для активации Commonizer чтобы в iosMain видно было методы ios, а не только в iosArm64 и iosX64 -# подробнее тут - https://www.youtube.com/watch?v=Q99HvynwjtY -# https://kotlinlang.org/docs/migrating-multiplatform-project-to-14.html#try-the-hierarchical-project-structure -kotlin.native.enableDependencyPropagation=false -kotlin.mpp.enableGranularSourceSetsMetadata=true -kotlin.mpp.enableCompatibilityMetadataVariant=true - -# использование androidX библиотек для андроида, нужно android gradle plugin +# использование androidX библиотек для андроида, нужно android gradle plugin'у android.useAndroidX=true -# отключение предупреждения о том что используется ios шорткат для настройки таргетов ios +# формат директорий android source set (version=2 значит src/androidMain/kotlin, а не src/main/kotlin) +kotlin.mpp.androidSourceSetLayoutVersion=2 +# отключение предупреждений о нестабильности мультиплатформы (KMP стабилен с 2023 года) +kotlin.mpp.stability.nowarn=true + +# отключение статического framework warning moko-resources +moko.resources.disableStaticFrameworkWarning=true + +# отключение предупреждения о том, что используется iOS-шорткат для настройки таргетов iOS mobile.multiplatform.iosTargetWarning=false -# путь до xcode проекта или воркспейса, используется Kotlin Multiplatform плагином для Android Studio, чтобы запускать ios приложение с отладчиком +# путь до Xcode проекта или workspace, +# используется Kotlin Multiplatform плагином для Android Studio, чтобы запускать iOS приложение с отладчиком # Подробнее https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile xcodeproj=./sample/ios-app ``` @@ -209,59 +211,85 @@ _Является упрощенным вариантом # Gradle Sync -Система сборки Gradle не связана напрямую с IDE и расчитана в первую очередь на работу без UI, через +Система сборки Gradle не связана напрямую с IDE и рассчитана в первую очередь на работу без UI, через консоль. Но в IDEA и Android Studio реализована полная интеграция с Gradle, позволяющая запускать -команды Gradle, видеть модули Gradle и прочее. Чтобы IDE могла считать конфигурацию проекта -используется импорт проекта, называется действие Gradle Sync. +команды Gradle, видеть модули Gradle и прочее. Чтобы IDE могла считать конфигурацию проекта, +используется импорт проекта, действие называется Gradle Sync. Кнопка для запуска Gradle Sync в Android Studio: ![gradle sync in android studio](/assets/gradle-sync-android-studio.png) После успешного завершения импорта проекта, через Gradle Sync, мы получаем проиндексированный -проект, в котором каждый gradle модуль обработан и считаны подключенные зависимости, настройки +проект, в котором каждый gradle модуль обработан и считаны подключенные зависимости и настройки проекта (используется ли котлин, мультиплатформа и прочее): ![project panel](/assets/idea-gradle-project-modules.png) Также после импорта проекта в IDE доступна панель работы с Gradle - в ней можно посмотреть все -Gradle модули и все задачи, которые доступны в каждом модуле: +Gradle-модули и все задачи, которые доступны в каждом модуле: ![gradle tasks panel](/assets/idea-gradle-tasks.png) -Самая полезная, и часто используемая для iOS разработчиков задача - скомпилировать iOS фреймворк и +Самая полезная и часто используемая для iOS разработчиков задача - скомпилировать iOS фреймворк и перенести в директорию для Cocoapods. -Зовется она `syncMultiPlatformLibraryDebugFrameworkIosX64` (добавляется -плагином [mobile-multiplatform](https://github.com/icerockdev/mobile-multiplatform-gradle-plugin)). -Где: +Если используется плагин [mobile-multiplatform](https://github.com/icerockdev/mobile-multiplatform-gradle-plugin), +таска называется `syncMultiPlatformLibraryDebugFrameworkIosX64`, где: -- MultiPlatformLibrary - имя фреймворка, который будет получен на выходе -- Debug - конфигурация сборки (для разработки собираем дебаг с отладочной инфой, Release делает CI) -- IosX64 - таргет, который должен быть собран (то есть iOS для запуска в симуляторе на x64 машине) +- MultiPlatformLibrary - имя фреймворка +- Debug - конфигурация сборки +- IosX64 - таргет (симулятор на Intel Mac) -Для разработки используем именно Debug + IosX64, так как этот вариант имеет оптимизацию на уровне -Kotlin/Native компилятора с множеством кешей. Работает быстрее всех остальных вариантов сборки -фреймворка. +Если используется официальный CocoaPods плагин (`org.jetbrains.kotlin.native.cocoapods`), фреймворк +собирается и подключается через CocoaPods автоматически. Xcode вызывает задачу +`embedAndSignAppleFrameworkForXcode` перед сборкой — отдельно запускать её не нужно. + +Для отладки на Apple Silicon (M1/M2/M3) используйте таргет `iosSimulatorArm64` вместо `iosX64`. ![gradle cocoapods task](/assets/idea-gradle-cocoapods.png) +# Convention plugins (build-logic) + +Когда в проекте несколько модулей с повторяющейся Gradle-конфигурацией, дублировать её в каждом +`build.gradle.kts` неудобно. Решение — вынести общую логику в convention plugins, которые +подключаются как includeBuild в `settings.gradle.kts`: + +``` +// settings.gradle.kts +includeBuild("build-logic") +``` + +Внутри `build-logic` лежат precompiled script plugins — обычные `.gradle.kts` файлы в +`src/main/kotlin`, которые подключаются по имени: + +```kotlin +// mpp-library/build.gradle.kts +plugins { + id("multiplatform-library-convention") + id("detekt-convention") +} +``` + +Plugin'ы могут настраивать таргеты, зависимости, компилятор — всё, что обычно пишется в +`build.gradle.kts`. Подробнее в [документации](https://docs.gradle.org/current/userguide/custom_plugins.html). + # Внесение изменений в конфигурацию -Какие блоки конфигурации можно использовать в конкретном `build.gradle` зависит от подключенных к +Какие блоки конфигурации можно использовать в конкретном `build.gradle`, зависит от подключенных к данному проекту плагинов. Каждый плагин может добавлять свои блоки конфигурации и свои задачи. Например, плагин `org.jetbrains.kotlin.multiplatform` добавляет блок `kotlin` и множество задач типа `compileKotlinIosX64` (если в блоке `kotlin` включен таргет `iosX64`). -Детальная информация о том какие настройки доступны в блоке `kotlin` доступна -на [сайте документации](https://kotlinlang.org/docs/mpp-dsl-reference.html). +Детальная информация о том, какие настройки доступны в блоке `kotlin`, доступна +на [сайте документации](https://kotlinlang.org/docs/multiplatform-dsl-reference.html). +Начиная с Kotlin 2.0 опции компилятора задаются через блок `compilerOptions { }` внутри `kotlin { }`. -Помимо документации узнать досутпный функционал предоставляемый плагином можно используя подсказки -IDEA, когда используется Gradle Kotlin DSL, вместо Groovy. В таком случае, при успешно завершенной -индексации (после клика на Gradle Sync) можно использовать автозавершение кода и переход к -объявлению. +Помимо документации узнать доступный функционал, предоставляемый плагином, можно, используя подсказки +IDEA, когда используется Gradle Kotlin DSL, а не Groovy. В таком случае при успешно завершенной +индексации (после клика на Gradle Sync) можно использовать автозавершение кода и переход к объявлению. -Использование автодополнения (либо подождать при наборе кода, либо нажать `ctrl + space`) +Использование автодополнения (либо подождать при наборе кода, либо нажать `Cmd + space`) ![gradle kotlin dsl autocomplete](/assets/kotin-script-autocomplete.png) Использование перехода к объявлению типа (`Cmd + left click`): diff --git a/learning/gradle/optimization.md b/learning/gradle/optimization.md index c71507f66..dde77faee 100644 --- a/learning/gradle/optimization.md +++ b/learning/gradle/optimization.md @@ -8,6 +8,52 @@ sidebar_position: 10

-Полезные ссылки: +## Память и параллелизм + +Эти параметры задаются в `gradle.properties` (глобально или в проекте): + +```bash +# выделить достаточно памяти JVM +org.gradle.jvmargs=-Xmx4096m +# параллельная сборка модулей +org.gradle.parallel=true +``` + +Подробнее — в разделе [Build Environment](./build-environment). + +## Build cache + +Кеширует результаты сборок. Повторная сборка без изменений берёт результат из кеша, а не компилирует заново: + +```bash +org.gradle.caching=true +``` + +## Configuration cache (рекомендуется) + +Сохраняет результат фазы конфигурации между запусками. На больших проектах даёт наибольший прирост +скорости — фаза конфигурации выполняется только один раз и загружается из кеша при повторных запусках. + +Доступен с Gradle 8.1: + +```bash +org.gradle.configuration-cache=true +``` + +Если плагин не поддерживает configuration cache, Gradle сообщит об ошибке с указанием проблемного плагина. +В таких случаях можно оставить `false` и попробовать снова после обновления плагинов. + +## Dependency resolution + +- Используйте version catalog (`libs.versions.toml`) — это ускоряет разрешение зависимостей за счёт typesafe accessors +- Не объявляйте зависимости, которые не используются — Gradle всё равно будет их резолвить +- Подключайте зависимости в **самом узком** source set (`commonMainImplementation`, а не `commonMainApi`, если зависимость не должна быть видна наружу) + +## Работа с iOS + +- Для разработки используйте `iosSimulatorArm64` (Apple Silicon) или `iosX64` (Intel), но не все таргеты сразу — сборка всех архитектур (`iosArm64` + `iosSimulatorArm64` + `iosX64`) дольше +- `embedAndSignAppleFrameworkForXcode` (CocoaPods плагин KGP) кеширует собранный framework, не пересобирая его при каждом запуске из Xcode + +## Полезные ссылки - [Improving dependency sync speeds for your Gradle project](https://msfjarvis.dev/posts/improving-dependency-sync-speeds-for-your-gradle-project/) diff --git a/learning/gradle/updating-versions.md b/learning/gradle/updating-versions.md index addd6468c..e48ecdd64 100644 --- a/learning/gradle/updating-versions.md +++ b/learning/gradle/updating-versions.md @@ -19,7 +19,7 @@ sidebar_position: 5 # ... # URL-адрес для загрузки дистрибутива Gradle -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip # ... @@ -35,7 +35,7 @@ Gradle придерживается такого подхода к версио ## Plugins -> Что делать если Android Studio предложила обновить Android Gradle Plugin (AGP) или Kotlin Gradle Plugin? +> Что делать, если Android Studio предложила обновить Android Gradle Plugin (AGP) или Kotlin Gradle Plugin? > Какие плагины и зависимости можно обновлять без последствий? ### Kotlin и Android @@ -43,23 +43,22 @@ Gradle придерживается такого подхода к версио В принципе, вы можете обновлять все плагины и зависимости, поддерживающие семантическое версионирование, но на минорных апдейтах, чтобы не нарушалась обратная совместимость. -С ноября 2020 года Android Gradle Plugin поддерживает семантическое версионирование, -поэтому при обновлении AGP с версии `7.0` на версию `7.1` ничего сломаться не должно. +Android Gradle Plugin поддерживает семантическое версионирование, +поэтому при обновлении AGP в рамках минорной версии (например, `8.7` → `8.9`) ничего сломаться не должно. -Kotlin поддерживает немного другой вид версионирования, о котором вы можете прочитать [тут](https://kotlinlang.org/docs/releases.html). +Kotlin придерживается собственной схемы версионирования, подробнее — [тут](https://kotlinlang.org/docs/releases.html). Например, если в вашем проекте используется Kotlin Multiplatform Plugin: ```kotlin plugins { - kotlin("multiplatform") version "1.4.21" + kotlin("multiplatform") version "2.1.10" } ``` -то при смене *feature* версии `1.4.21 ~> 1.5.21` нужно проверить компиляцию обеих платформ и работу кода во время выполнения. -Также скорее всего придется обновлять и версии библиотек на те, которые поддерживают новую версию. - -А при смене *incremental* (`1.5.21` -> `1.5.30`) и *bugfix* (`1.5.20` -> `1.5.21`) версий обратная совместимость сохраняется и проблем быть не должно. +При смене *major* версии (`2.0` → `3.0`) возможны breaking changes — проверяйте компиляцию обеих платформ и работу кода. +Смена *feature* версии (`2.0` → `2.1`) обратно совместима, но могут потребоваться обновления библиотек. +*Bugfix* обновления (`2.1.10` → `2.1.20`) полностью безопасны. ## Материалы diff --git a/learning/gradle/version-catalogs.md b/learning/gradle/version-catalogs.md index 3bb351d0d..19dea96ae 100644 --- a/learning/gradle/version-catalogs.md +++ b/learning/gradle/version-catalogs.md @@ -4,25 +4,23 @@ sidebar_position: 9 # Version catalogs - -
-
+Version catalog (каталог версий) — файл `gradle/libs.versions.toml`, в котором централизованно +объявляются версии, библиотеки, плагины и их группы (bundles). Gradle генерирует по нему typesafe +accessors — вместо строковых координат вы пишете `libs.coroutines` или `libs.ktorClient`. + +## Структура TOML + +```toml +[versions] +kotlinVersion = "2.1.10" +coroutinesVersion = "1.10.2" +ktorClientVersion = "2.3.12" -Сейчас в наших проектах используются и bundles, о которых упоминается в видео. -Например, ниже часть блоков [libraries] и [bundles] в каталоге версий нашего mobile-compose-boilerplate, с помощью которого стартуем ComposeMultiplatform проекты: -```kotlin [libraries] -... -# Koin -koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koinBom" } -koin-core = { module = "io.insert-koin:koin-core" } -koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koinKsp" } -koin-compose = { module = "io.insert-koin:koin-compose" } -... -# moko -moko-resources = { module = "dev.icerock.moko:resources", version.ref = "mokoResources" } -moko-resources-compose = { module = "dev.icerock.moko:resources-compose", version.ref = "mokoResources" } -... +coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutinesVersion" } +ktorClient = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientVersion" } +ktorClientOkHttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktorClientVersion" } +ktorClientIos = { module = "io.ktor:ktor-client-darwin", version.ref = "ktorClientVersion" } [bundles] koin = [ @@ -34,19 +32,74 @@ moko-resources = [ "moko-resources", "moko-resources-compose" ] + +[plugins] +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlinVersion" } +androidLibrary = { id = "com.android.library", version.ref = "androidGradleVersion" } ``` -Использование bundles в buld.gradle фичи в общем коде mpp-library: + +## Как использовать + +В `build.gradle.kts` подключение через accessors: + ```kotlin +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidLibrary) +} + dependencies { - ... - commonMainImplementation(platform(libs.koin.bom)) - commonMainApi(libs.bundles.koin) + commonMainImplementation(libs.coroutines) + commonMainApi(libs.ktorClient) + androidMainImplementation(libs.ktorClientOkHttp) + // bundles — группа зависимостей + commonMainApi(libs.bundles.koin) commonMainApi(libs.bundles.moko.resources) - ... - } +} +``` + +Accessors сохраняют регистр и меняют `-` на точки: `moko-resources` становится `libs.bundles.moko.resources`, +`ktorClientOkHttp` — `libs.ktorClientOkHttp`. + +## Project accessors + +Помимо библиотек, Gradle генерирует accessors для модулей проекта через `projects.`: + +```kotlin +dependencies { + commonMainApi(projects.mppLibrary.utils) + commonMainImplementation(projects.mppLibrary.feature.example) +} ``` -- [Gradle docs - Version catalogs](https://docs.gradle.org/7.2/userguide/platforms.html#sub:central-declaration-of-dependencies ) -- [Gradle docs - Project accessors](https://docs.gradle.org/7.2/userguide/declaring_dependencies.html#sec:type-safe-project-accessors ) -- [Gradle docs - Centralized repository declaration](https://docs.gradle.org/7.2/userguide/declaring_repositories.html#sub:centralized-repository-declaration ) +Для этого нужно включить флаг в `settings.gradle.kts`: + +```kotlin +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +``` + +## Импорт в build-logic + +Чтобы convention plugins могли использовать тот же каталог, в `build-logic/settings.gradle.kts` +нужно импортировать его из корня проекта: + +```kotlin +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} +``` + +## Материалы + + +
+
+ +- [Gradle docs — Version catalogs](https://docs.gradle.org/current/userguide/platforms.html#sub:central-declaration-of-dependencies) +- [Gradle docs — Typesafe project accessors](https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:type-safe-project-accessors) +- [Gradle docs — Centralized repository declaration](https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:centralized-repository-declaration)