階層型プロジェクト構造
Kotlinマルチプラットフォームプロジェクトは、階層的なソースセット構造をサポートしています。 これにより、一部のサポートされているターゲット間で共通コードを共有するための中間ソースセットの階層を構成できます。中間ソースセットを使用すると、以下のことが可能になります。
- 特定のターゲット向けのAPIを提供する。例えば、ライブラリはKotlin/Nativeターゲット向けの中間ソースセットでネイティブ固有のAPIを追加できますが、Kotlin/JVM向けには追加しません。
- 特定のターゲット向けのAPIを利用する。例えば、中間ソースセットを形成する一部のターゲットにKotlinマルチプラットフォームライブラリが提供する豊富なAPIから恩恵を受けることができます。
- プロジェクトでプラットフォーム依存のライブラリを使用する。例えば、中間iOSソースセットからiOS固有の依存関係にアクセスできます。
Kotlinツールチェーンは、各ソースセットが、そのソースセットがコンパイルされるすべてのターゲットで利用可能なAPIのみにアクセスできるようにします。これにより、Windows固有のAPIを使用してからmacOSにコンパイルするといった、リンケージエラーや実行時の未定義の動作につながるケースを防ぎます。
ソースセットの階層を設定する推奨される方法は、デフォルト階層テンプレートを使用することです。 このテンプレートは、最も一般的なケースをカバーしています。より高度なプロジェクトの場合は、手動で設定できます。 これはより低レベルなアプローチであり、より柔軟ですが、より多くの労力と知識を必要とします。
デフォルト階層テンプレート
Kotlin Gradleプラグインには、ビルトインのデフォルト階層テンプレートが用意されています。 これには、いくつかの一般的なユースケース向けの事前定義された中間ソースセットが含まれています。 プラグインは、プロジェクトで指定されたターゲットに基づいて、これらのソースセットを自動的に設定します。
共有コードを含むプロジェクトのモジュールにある次のbuild.gradle(.kts)
ファイルを検討してください。
kotlin {
androidTarget()
iosArm64()
iosSimulatorArm64()
}
kotlin {
androidTarget()
iosArm64()
iosSimulatorArm64()
}
コードでandroidTarget
、iosArm64
、iosSimulatorArm64
ターゲットを宣言すると、Kotlin Gradleプラグインはテンプレートから適切な共有ソースセットを見つけ、それらを作成します。結果の階層は次のようになります。
色付きのソースセットは実際に作成されプロジェクトに存在しますが、デフォルトテンプレートからの灰色のソースセットは無視されます。例えば、Kotlin Gradleプラグインは、プロジェクトにwatchOSターゲットがないため、watchos
ソースセットを作成しませんでした。
watchosArm64
のようなwatchOSターゲットを追加すると、watchos
ソースセットが作成され、apple
、native
、およびcommon
ソースセットからのコードもwatchosArm64
にコンパイルされます。
Kotlin Gradleプラグインは、デフォルト階層テンプレートのすべてのソースセットに対して型安全な静的アクセサを提供しているため、手動設定と比較してby getting
やby creating
のような構成を使用せずに参照できます。
対応するターゲットを最初に宣言せずに共有モジュールのbuild.gradle(.kts)
ファイルでソースセットにアクセスしようとすると、警告が表示されます。
kotlin {
androidTarget()
iosArm64()
iosSimulatorArm64()
sourceSets {
iosMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
}
// Warning: accessing source set without declaring the target
linuxX64Main { }
}
}
kotlin {
androidTarget()
iosArm64()
iosSimulatorArm64()
sourceSets {
iosMain {
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2'
}
}
// Warning: accessing source set without declaring the target
linuxX64Main { }
}
}
この例では、
apple
とnative
ソースセットはiosArm64
とiosSimulatorArm64
ターゲットにのみコンパイルされます。 それらの名前にもかかわらず、これらは完全なiOS APIにアクセスできます。 これは、native
のようなソースセットにとっては直感的ではないかもしれません。というのも、このソースセットではすべてのネイティブターゲットで利用可能なAPIのみがアクセス可能であると期待されるからです。この動作は将来変更される可能性があります。
追加の設定
デフォルト階層テンプレートを調整する必要がある場合があります。以前にdependsOn
呼び出しで中間ソースを手動で導入している場合、それはデフォルト階層テンプレートの使用をキャンセルし、以下の警告につながります。
The Default Kotlin Hierarchy Template was not applied to '<project-name>':
Explicit .dependsOn() edges were configured for the following source sets:
[<... names of the source sets with manually configured dependsOn-edges...>]
Consider removing dependsOn-calls or disabling the default template by adding
'kotlin.mpp.applyDefaultHierarchyTemplate=false'
to your gradle.properties
Learn more about hierarchy templates: https://kotl.in/hierarchy-template
この問題を解決するには、以下のいずれかの方法でプロジェクトを設定してください。
手動設定の置き換え
ケース。すべての中間ソースセットが、現在デフォルト階層テンプレートでカバーされている場合。
解決策。共有モジュールのbuild.gradle(.kts)
ファイルから、すべての手動のdependsOn()
呼び出しとby creating
構築を持つソースセットを削除します。すべてのデフォルトソースセットのリストを確認するには、フル階層テンプレートを参照してください。
追加ソースセットの作成
ケース。デフォルト階層テンプレートがまだ提供していないソースセットを追加したい場合。例えば、macOSターゲットとJVMターゲットの間にソースセットを追加する場合。
解決策:
共有モジュールの
build.gradle(.kts)
ファイルで、applyDefaultHierarchyTemplate()
を明示的に呼び出してテンプレートを再適用します。dependsOn()
を使用して、追加のソースセットを手動で設定します。kotlinkotlin { jvm() macosArm64() iosArm64() iosSimulatorArm64() // Apply the default hierarchy again. It'll create, for example, the iosMain source set: applyDefaultHierarchyTemplate() sourceSets { // Create an additional jvmAndMacos source set: val jvmAndMacos by creating { dependsOn(commonMain.get()) } macosArm64Main.get().dependsOn(jvmAndMacos) jvmMain.get().dependsOn(jvmAndMacos) } }
groovykotlin { jvm() macosArm64() iosArm64() iosSimulatorArm64() // Apply the default hierarchy again. It'll create, for example, the iosMain source set: applyDefaultHierarchyTemplate() sourceSets { // Create an additional jvmAndMacos source set: jvmAndMacos { dependsOn(commonMain.get()) } macosArm64Main { dependsOn(jvmAndMacos.get()) } jvmMain { dependsOn(jvmAndMacos.get()) } } }
ソースセットの変更
ケース。テンプレートによって生成されたものとまったく同じ名前のソースセットが既にあり、それらがプロジェクト内の異なるターゲットセット間で共有されている場合。例えば、nativeMain
ソースセットがデスクトップ固有のターゲット(linuxX64
、mingwX64
、およびmacosX64
)の間でのみ共有されている場合。
解決策。現在、テンプレートのソースセット間のデフォルトのdependsOn
関係を変更する方法はありません。 また、nativeMain
のようなソースセットの実装と意味がすべてのプロジェクトで同じであることが重要です。
ただし、以下のいずれかを実行することは可能です。
- 目的のために、デフォルト階層テンプレート内にある、または手動で作成された異なるソースセットを見つける。
gradle.properties
ファイルにkotlin.mpp.applyDefaultHierarchyTemplate=false
を追加してテンプレートから完全にオプトアウトし、すべてのソースセットを手動で設定する。
現在、独自の階層テンプレートを作成するためのAPIに取り組んでいます。これは、その階層設定がデフォルトテンプレートと大きく異なるプロジェクトにとって役立つでしょう。
このAPIはまだ準備ができていませんが、試してみたい場合は、
applyHierarchyTemplate {}
ブロックとKotlinHierarchyTemplate.default
の宣言を例として参照してください。 このAPIはまだ開発中であることに注意してください。テストされていない可能性があり、今後のリリースで変更される可能性があります。
フル階層テンプレートを見る
プロジェクトがコンパイルするターゲットを宣言すると、 プラグインはテンプレートから指定されたターゲットに基づいて共有ソースセットを選択し、それらをプロジェクト内に作成します。
この例は、プロジェクトのプロダクション部分のみを示しており、
Main
サフィックスは省略されています (例えば、commonMain
の代わりにcommon
を使用しています)。ただし、すべて*Test
ソースについても同様です。
手動設定
ソースセット構造に中間ソースを手動で導入できます。 これは、複数のターゲット間で共有されるコードを保持します。
例えば、ネイティブLinux、Windows、macOSターゲット(linuxX64
、mingwX64
、およびmacosX64
)間でコードを共有したい場合の対処法は次のとおりです。
共有モジュールの
build.gradle(.kts)
ファイルで、これらのターゲットの共有ロジックを保持する中間ソースセットdesktopMain
を追加します。dependsOn
関係を使用して、ソースセットの階層を設定します。commonMain
をdesktopMain
に接続し、次にdesktopMain
を各ターゲットソースセットに接続します。kotlinkotlin { linuxX64() mingwX64() macosX64() sourceSets { val desktopMain by creating { dependsOn(commonMain.get()) } linuxX64Main.get().dependsOn(desktopMain) mingwX64Main.get().dependsOn(desktopMain) macosX64Main.get().dependsOn(desktopMain) } }
groovykotlin { linuxX64() mingwX64() macosX64() sourceSets { desktopMain { dependsOn(commonMain.get()) } linuxX64Main { dependsOn(desktopMain) } mingwX64Main { dependsOn(desktopMain) } macosX64Main { dependsOn(desktopMain) } } }
結果の階層構造は次のようになります。
以下のターゲットの組み合わせに対して、共有ソースセットを持つことができます。
- JVMまたはAndroid + JS + Native
- JVMまたはAndroid + Native
- JS + Native
- JVMまたはAndroid + JS
- Native
Kotlinは現在、以下の組み合わせでのソースセット共有をサポートしていません。
- 複数のJVMターゲット
- JVM + Androidターゲット
- 複数のJSターゲット
共有ネイティブソースセットからプラットフォーム固有のAPIにアクセスする必要がある場合、IntelliJ IDEAは共有ネイティブコードで使用できる共通の宣言を検出するのに役立ちます。 その他のケースでは、Kotlinのexpect
/actual
宣言のメカニズムを使用してください。