Используйте подпроекты Gradle с мультиплатформой Kotlin

Я использую мультиплатформенность Kotlin (JVM & JS), которая в IDEA создает три проекта: demo, demo-js а также demo-jvm,

Я хотел бы разделить общий код на несколько подпроектов / подмодулей. Допустим, я добавляю commonmod; как мне сделать это скомпилировать?

Ошибка прямо сейчас, для gradle run -p demo-jvm, является:

demo/demo-js/src/main/kotlin/demo/commonmod/example.kt: (3, 12): Actual function 'getPlatform' has no corresponding expected declaration

но я думаю, что я делаю это в корне неправильно, так как не знаю, что должно зависеть от чего (хотя я пробовал довольно много итераций). Если я решу эту ошибку, я получу другие, а затем еще раз, пока я не вернусь к этой.


В качестве минимального, но все же большого примера я имею:

demo / settings.gradle:

rootProject.name = 'demo'

include 'demo-jvm', 'demo-js', 'commonmod'

demo / build.gradle:

buildscript { ... }

apply plugin: 'kotlin-platform-common'

repositories {
    mavenCentral()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
    testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlin_version"
    testCompile "org.jetbrains.kotlin:kotlin-test-common:$kotlin_version"
    compile project(':commonmod')
}

demo / demo-jvm / settings.gradle:

rootProject.name = 'demo'

demo / demo-jvm / build.gradle:

buildscript { ... }

apply plugin: 'kotlin-platform-jvm'
apply plugin: 'application'

repositories {
    mavenCentral()
}

mainClassName = "demo.MainKt"

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    expectedBy project(":")
    testCompile "junit:junit:4.12"
    testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
    testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}

demo / demo-js / settings.gradle:

rootProject.name = 'demo'

demo / demo-js / build.gradle:

buildscript { ... }

apply plugin: 'kotlin-platform-js'

repositories {
    mavenCentral()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
    expectedBy project(":")
    testCompile "org.jetbrains.kotlin:kotlin-test-js:$kotlin_version"
}

demo / commonmod / settings.gradle:

rootProject.name = 'demo'

include 'demo-jvm', 'demo-js'

demo / commonmod / build.gradle:

buildscript { ... }

apply plugin: 'kotlin-platform-common'

repositories {
    mavenCentral()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
    testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlin_version"
    testCompile "org.jetbrains.kotlin:kotlin-test-common:$kotlin_version"
    compile project(':demo-js')
    compile project(':demo-jvm')
}

3 ответа

Решение

Это заняло сумасшедшее количество времени, поэтому я надеюсь, что это кому-нибудь пригодится!

На Github есть функциональный пример: kotlin_multiplatform_gradle_demo

Несколько источников помогли, но во многом это было методом проб и ошибок, поэтому, если что-то не так, пожалуйста, дайте мне знать!


Для минимального примера структура выглядит так:

├── alpha
│   ├── alpha-js
│   │   └── build.gradle
│   ├── alpha-jvm
│   │   └── build.gradle
│   ├── build.gradle
│   └── src
│       └── main
│           ├── kotlin
│           │   └── demo
│           │       └── alpha
│           │           └── main.kt
├── beta
│   ├── beta-js
│   │   ├── build.gradle
│   │   └── src
│   │       └── main
│   │           └── kotlin
│   │               └── demo
│   │                   └── beta
│   │                       └── platform.kt
│   ├── beta-jvm
│   │   ├── build.gradle
│   │   └── src
│   │       └── main
│   │           └── kotlin
│   │               └── demo
│   │                   └── beta
│   │                       └── platform.kt
│   ├── build.gradle
│   └── src
│       └── main
│           └── kotlin
│               └── demo
│                   └── beta
│                       └── platform.kt
├── build.gradle
└── settings.gradle

Общие модули (alpha а также beta) нужны модули платформы для каждой платформы, по крайней мере с `build.gradle``.

settings.gradle Файл импортирует все модули, в том числе и платформенные.

Зависимости, например от альфа на бета, объявлены в общем альфа-модуле и всех модулях альфа-платформы.


Некоторые образцы, которые я изучил:

  • Каждый "нормальный" (общий) модуль имеет один модуль платформы для каждой платформы.
  • Для общего модуля alpha, модуль платформы javascript должен быть вызван alpha-js (аналогично для -jvm).
  • Если нет специфичного для платформы кода, этот модуль может быть просто файлом Gradle в каталоге.
  • Модули платформы могут быть удобно размещены внутри общего каталога модулей (так alpha:alpha-js).
  • Общий модуль не должен ссылаться на модули платформы; модули платформы имеют зависимость expectedBy project(":the_common_module"),
  • Если модуль alpha зависит от beta, затем

    • alpha должен иметь dependencies { compile project(":beta") }
    • alpha-js должен иметь dependencies { compile project(":beta:beta-js") } (в дополнение к expectedBy)
    • alpha-jvm должен иметь dependencies { compile project(":beta:beta-jvm") } (в дополнение к expectedBy) так далее
  • Только верхний модуль имеет settings.gradle, который включает ВСЕ субмодули (в том числе платформенные).

  • Удостоверьтесь в правильности имен, так как неправильные не вызывают ошибку, они просто молча терпят неудачу. (Это кажется смешным, но я думаю, что есть причина.)
  • НЕ помещайте весь вывод в один общий каталог сборки - это приводит к нескольким странным недетерминированным ошибкам.

Раньше у меня были полные конфигурационные файлы, но лучше просто проверить код на Github, потому что он очень длинный.

Подмодули должны быть объявлены в рамках зависимостей с помощью наборов источников. Если подмодуль определен в общем наборе исходников, его не нужно определять в одной из конкретной платформы, но не наоборот.

build.gradle.kts (:kmm_shared:feature_a)

      kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                api(project(":kmm_shared:domain"))
            }
        }
}

При этом нет необходимости, чтобы исходный набор Android также включал зависимость.

ГЛ

Для тех, кто приходит с поиском Google для:

"Фактическая функция / класс / объект-компаньон / и т.д. не имеет соответствующего ожидаемого объявления"

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

Другие вопросы по тегам