Этот документ содержит руководство для авторов плагинов по правильному обнаружению, взаимодействию и настройке Kotlin Multiplatform (KMP), с особым акцентом на интеграцию с целевыми платформами Android в рамках проекта KMP. Поскольку KMP продолжает развиваться, понимание соответствующих хуков и API — таких как KotlinMultiplatformExtension , типы KotlinTarget и интерфейсы интеграции, специфичные для Android, — имеет важное значение для создания надежных и перспективных инструментов, которые бесперебойно работают на всех платформах, определенных в многоплатформенном проекте.
Проверьте, использует ли проект плагин Kotlin Multiplatform.
Во избежание ошибок и для обеспечения запуска плагина только при наличии KMP необходимо проверить, использует ли проект плагин KMP. Рекомендуется использовать plugins.withId() для реагирования на применение плагина KMP, а не проверять его наличие немедленно. Такой реактивный подход предотвращает негибкость плагина в зависимости от порядка применения плагинов в скриптах сборки пользователя.
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
// The KMP plugin is applied, you can now configure your KMP integration.
}
}
}
Получите доступ к модели
Точкой входа для всех конфигураций Kotlin Multiplatform является расширение KotlinMultiplatformExtension .
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
}
}
}
Реагирование на многоплатформенные цели Kotlin
Используйте контейнер targets для реактивной настройки вашего плагина для каждой цели, добавленной пользователем.
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.configureEach { target ->
// 'target' is an instance of KotlinTarget
val targetName = target.name // for example, "android", "iosX64", "jvm"
val platformType = target.platformType // for example, androidJvm, jvm, native, js
}
}
}
}
Примените логику, специфичную для целевого объекта.
Если вашему плагину необходимо применять логику только к определенным типам платформ, распространенный подход заключается в проверке свойства platformType . Это перечисление, которое в общих чертах классифицирует целевой объект.
Например, используйте это, если вашему плагину нужно лишь общее разграничение (например, запуск только на платформах, подобных JVM):
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.configureEach { target ->
when (target.platformType) {
KotlinPlatformType.jvm -> { /* Standard JVM or Android */ }
KotlinPlatformType.androidJvm -> { /* Android */ }
KotlinPlatformType.js -> { /* JavaScript */ }
KotlinPlatformType.native -> { /* Any Native (iOS, Linux, Windows, etc.) */ }
KotlinPlatformType.wasm -> { /* WebAssembly */ }
KotlinPlatformType.common -> { /* Metadata target (rarely needs direct plugin interaction) */ }
}
}
}
}
}
Подробности, специфичные для Android.
Хотя все целевые платформы Android имеют индикатор platformType.androidJvm , KMP имеет две различные точки интеграции в зависимости от используемого плагина Android Gradle: KotlinAndroidTarget для проектов, использующих com.android.library или com.android.application , и KotlinMultiplatformAndroidLibraryTarget для проектов, использующих com.android.kotlin.multiplatform.library .
API KotlinMultiplatformAndroidLibraryTarget был добавлен в AGP 8.8.0, поэтому, если потребители вашего плагина работают на более старой версии AGP, проверка target is KotlinMultiplatformAndroidLibraryTarget может привести к исключению ClassNotFoundException . Чтобы избежать проблем, проверьте AndroidPluginVersion.getCurrent() перед проверкой типа цели. Обратите внимание, что AndroidPluginVersion.getCurrent() требуется AGP 7.1 или выше.
import com.android.build.api.AndroidPluginVersion
import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.configureEach { target ->
if (target is KotlinAndroidTarget) {
// Old kmp android integration using com.android.library or com.android.application
}
if (AndroidPluginVersion.getCurrent() >= AndroidPluginVersion(8, 8) &&
target is KotlinMultiplatformAndroidLibraryTarget
) {
// New kmp android integration using com.android.kotlin.multiplatform.library
}
}
}
}
}
Получите доступ к расширению Android KMP и его свойствам.
Ваш плагин будет в первую очередь взаимодействовать с расширением Kotlin, предоставляемым плагином Kotlin Multiplatform, и расширением Android, предоставляемым AGP для целевой платформы KMP Android. Блок android {} внутри расширения Kotlin в проекте KMP представлен интерфейсом KotlinMultiplatformAndroidLibraryTarget , который также расширяет KotlinMultiplatformAndroidLibraryExtension . Это означает, что вы можете получить доступ как к свойствам DSL, специфичным для целевой платформы, так и к свойствам DSL, специфичным для Android, через этот единый объект.
import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
// Access the Android target, which also serves as the Android-specific DSL extension
kmpExtension.targets.withType(KotlinMultiplatformAndroidLibraryTarget::class.java).configureEach { androidTarget ->
// You can now access properties and methods from both
// KotlinMultiplatformAndroidLibraryTarget and KotlinMultiplatformAndroidLibraryExtension
androidTarget.compileSdk = 34
androidTarget.namespace = "com.example.myplugin.library"
androidTarget.withJava() // enable Java sources
}
}
}
}
В отличие от других плагинов Android (таких как com.android.library или com.android.application ), плагин KMP для Android не регистрирует свое основное расширение DSL на уровне проекта. Он находится в иерархии целевых платформ KMP, чтобы гарантировать, что он применяется только к конкретной целевой платформе Android, определенной в вашей многоплатформенной настройке.
Обработка компиляций и исходных наборов.
Зачастую плагинам необходимо работать на более детальном уровне, чем просто на уровне целевого объекта — в частности, им нужно работать на уровне компиляции . Объект KotlinMultiplatformAndroidLibraryTarget содержит экземпляры KotlinMultiplatformAndroidCompilation (например, main , hostTest , deviceTest ). Каждая компиляция связана с наборами исходных кодов Kotlin. Плагины могут взаимодействовать с ними, добавляя исходные коды, зависимости или настраивая задачи компиляции.
import com.android.build.api.dsl.KotlinMultiplatformAndroidCompilation
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val kmpExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.configureEach { target ->
target.compilations.configureEach { compilation ->
// standard compilations are usually 'main' and 'test'
// android target has 'main', 'hostTest', 'deviceTest'
val compilationName = compilation.name
// Access the default source set for this compilation
val defaultSourceSet = compilation.defaultSourceSet
// Access the Android-specific compilation DSL
if (compilation is KotlinMultiplatformAndroidCompilation) {
}
// Access and configure the Kotlin compilation task
compilation.compileTaskProvider.configure { compileTask ->
}
}
}
}
}
}
Настройка компиляции тестов в плагинах соглашений
При настройке значений по умолчанию для компиляции тестов (например, targetSdk для инструментированных тестов) в плагине соглашений следует избегать использования методов включения, таких как withDeviceTest { } или withHostTest { } . Вызов этих методов немедленно запускает создание соответствующих вариантов тестов Android и компиляций для каждого модуля, который применяет плагин соглашений, что может быть нецелесообразно. Кроме того, эти методы нельзя вызывать повторно в конкретном модуле для уточнения настроек, поскольку это вызовет ошибку, указывающую на то, что компиляция уже создана.
Вместо этого мы рекомендуем использовать реактивный блок configureEach в контейнере компиляций. Это позволяет задавать конфигурации по умолчанию, которые применяются только в том случае, если модуль явно включает компиляцию тестов:
import com.android.build.api.dsl.KotlinMultiplatformAndroidDeviceTestCompilation
import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val kmpExtension =
project.extensions.getByType(KotlinMultiplatformExtension::class.java)
kmpExtension.targets.withType(KotlinMultiplatformAndroidLibraryTarget::class.java)
.configureEach { androidTarget ->
androidTarget.compilations.withType(
KotlinMultiplatformAndroidDeviceTestCompilation::class.java
).configureEach {
targetSdk { version = release(34) }
}
}
}
}
}
Этот шаблон гарантирует, что ваш плагин для работы с соглашениями останется "ленивым" и позволит отдельным модулям вызывать withDeviceTest { } для включения и дальнейшей настройки своих тестов без конфликта с настройками по умолчанию.
Взаимодействие с API вариантов
Для задач, требующих настройки на поздних этапах, доступа к артефактам (например, манифестам или байт-коду) или возможности включения или отключения определенных компонентов, необходимо использовать Android Variant API. В проектах KMP расширение имеет тип KotlinMultiplatformAndroidComponentsExtension .
Расширение регистрируется на уровне проекта при применении плагина KMP для Android.
Используйте beforeVariants для управления созданием вариантов или их вложенных тестовых компонентов ( hostTests и deviceTests ). Это правильное место для программного отключения тестов или изменения значений свойств DSL.
import com.android.build.api.variant.KotlinMultiplatformAndroidComponentsExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val androidComponents = project.extensions.findByType(KotlinMultiplatformAndroidComponentsExtension::class.java)
androidComponents?.beforeVariants { variantBuilder ->
// Disable all tests for this module
variantBuilder.hostTests.values.forEach { it.enable = false }
variantBuilder.deviceTests.values.forEach { it.enable = false }
}
}
}
}
Используйте onVariants для доступа к конечному объекту варианта ( KotlinMultiplatformAndroidVariant ). Здесь вы можете проверить разрешенные свойства или зарегистрировать преобразования для таких артефактов, как объединенный манифест или классы библиотек.
import com.android.build.api.variant.KotlinMultiplatformAndroidComponentsExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.plugins.withId("com.android.kotlin.multiplatform.library") {
val androidComponents = project.extensions.findByType(KotlinMultiplatformAndroidComponentsExtension::class.java)
androidComponents?.onVariants { variant ->
// 'variant' is a KotlinMultiplatformAndroidVariant
val variantName = variant.name
// Access the artifacts API
val manifest = variant.artifacts.get(com.android.build.api.variant.SingleArtifact.MERGED_MANIFEST)
}
}
}
}
Рекомендуем вам
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Настройте свою среду
- Добавить модуль KMP в проект
- Настройка плагина библиотеки Android Gradle для KMP