メインコンテンツまでスキップ

階層的なプロジェクト構造

Kotlin Multiplatformプロジェクトは、階層的なソースセット構造をサポートしています。 つまり、すべてではなく一部のサポートされているターゲット間で共通のコードを共有するために、中間ソースセットの階層を配置できます。中間ソースセットを使用すると、次のことが容易になります。

  • 一部のターゲットに特定のAPIを提供します。たとえば、ライブラリはKotlin/Nativeターゲット用の中間ソースセットにネイティブ固有のAPIを追加できますが、Kotlin/JVMターゲットには追加できません。
  • 一部のターゲットに特定のAPIを利用します。たとえば、Kotlin Multiplatformライブラリが中間ソースセットを形成する一部のターゲットに提供する豊富なAPIを利用できます。
  • プロジェクトでプラットフォーム依存のライブラリを使用します。たとえば、中間iOSソースセットからiOS固有の依存関係にアクセスできます。

Kotlinツールチェーンは、各ソースセットが、そのソースセットのコンパイル対象となるすべてのターゲットで利用可能なAPIのみにアクセスできるようにします。これにより、Windows固有のAPIを使用してからmacOSにコンパイルし、ランタイム時にリンケージエラーや未定義の動作が発生するようなケースを防ぎます。

ソースセットの階層を設定する推奨される方法は、デフォルトの階層テンプレートを使用することです。このテンプレートは、最も一般的なケースをカバーしています。より高度なプロジェクトがある場合は、手動で構成することができます。これはより低レベルのアプローチです。より柔軟性がありますが、より多くの労力と知識が必要です。

デフォルトの階層テンプレート

Kotlin Gradleプラグインには、組み込みのデフォルト階層テンプレートがあります。これには、一般的なユースケース向けにあらかじめ定義された中間ソースセットが含まれています。プラグインは、プロジェクトで指定されたターゲットに基づいて、これらのソースセットを自動的に設定します。

共有コードを含むプロジェクトのモジュールにある、次のbuild.gradle(.kts)ファイルを検討してください。

kotlin {
androidTarget()
iosArm64()
iosSimulatorArm64()
}

コード内でターゲットandroidTargetiosArm64、およびiosSimulatorArm64を宣言すると、Kotlin Gradleプラグインはテンプレートから適切な共有ソースセットを見つけ出し、それらを作成します。結果として得られる階層は次のようになります。

デフォルトの階層テンプレートの使用例

色付きのソースセットは実際に作成され、プロジェクトに存在しますが、デフォルトのテンプレートからの灰色のソースセットは無視されます。たとえば、プロジェクトにwatchOSターゲットがないため、Kotlin Gradleプラグインはwatchosソースセットを作成していません。

watchosArm64のようなwatchOSターゲットを追加すると、watchosソースセットが作成され、applenative、および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.1")
}
// 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ターゲットの間に追加したいとします。

解決策:

  1. 共有モジュールのbuild.gradle(.kts)ファイルで、applyDefaultHierarchyTemplate()を明示的に呼び出して、テンプレートを再適用します。

  2. dependsOn()を使用して、手動で追加のソースセットを構成します。

    kotlin {
    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)
    }
    }

ソースセットを変更する

ケース。テンプレートによって生成されたものとまったく同じ名前のソースセットがすでに存在しますが、プロジェクト内の異なるターゲットセット間で共有されています。たとえば、nativeMainソースセットは、デスクトップ固有のターゲット(linuxX64mingwX64、およびmacosX64)間でのみ共有されます。

解決策。現在、テンプレートのソースセット間のデフォルトのdependsOn関係を変更する方法はありません。 また、nativeMainなどのソースセットの実装と意味が、すべてのプロジェクトで同じであることが重要です。

ただし、次のいずれかを実行できます。

  • デフォルトの階層テンプレートまたは手動で作成されたソースセットで、目的に合った別のソースセットを見つけます。
  • kotlin.mpp.applyDefaultHierarchyTemplate=falsegradle.propertiesファイルに追加してテンプレートを完全にオプトアウトし、すべてのソースセットを手動で構成します。
ヒント

現在、独自の階層テンプレートを作成するためのAPIに取り組んでいます。これは、階層構成がデフォルトのテンプレートと大幅に異なるプロジェクトに役立ちます。

このAPIはまだ準備ができていませんが、試してみたい場合は、 applyHierarchyTemplate {}ブロックと、例としてKotlinHierarchyTemplate.defaultの宣言を確認してください。 このAPIはまだ開発中であることに注意してください。テストされていない可能性があり、今後のリリースで変更される可能性があります。

完全な階層テンプレートを見る

プロジェクトのコンパイル対象となるターゲットを宣言すると、 プラグインは、指定されたターゲットに基づいてテンプレートから共有ソースセットを選択し、プロジェクトに作成します。

デフォルトの階層テンプレート
ヒント

この例では、プロジェクトのプロダクション部分のみを示しており、Mainサフィックスを省略しています (たとえば、commonMainの代わりにcommonを使用)。ただし、*Testソースでもすべて同じです。

手動構成

ソースセット構造に中間ソースを手動で導入できます。 これは、いくつかのターゲットの共有コードを保持します。

たとえば、ネイティブのLinux、Windows、およびmacOSターゲット(linuxX64mingwX64、およびmacosX64)間でコードを共有する場合、次の手順を実行します。

  1. 共有モジュールのbuild.gradle(.kts)ファイルで、これらのターゲットの共有ロジックを保持する中間ソースセットdesktopMainを追加します。

  2. dependsOn関係を使用して、ソースセット階層を設定します。commonMaindesktopMainに接続し、次にdesktopMainを各ターゲットソースセットに接続します。

    kotlin {
    linuxX64()
    mingwX64()
    macosX64()

    sourceSets {
    val desktopMain by creating {
    dependsOn(commonMain.get())
    }

    linuxX64Main.get().dependsOn(desktopMain)
    mingwX64Main.get().dependsOn(desktopMain)
    macosX64Main.get().dependsOn(desktopMain)
    }
    }

結果として得られる階層構造は次のようになります。

手動で構成された階層構造

次のターゲットの組み合わせに対して共有ソースセットを持つことができます。

  • JVMまたはAndroid + JS + Native
  • JVMまたはAndroid + Native
  • JS + Native
  • JVMまたはAndroid + JS
  • Native

Kotlinは現在、これらの組み合わせのソースセットの共有をサポートしていません。

  • 複数のJVMターゲット
  • JVM + Androidターゲット
  • 複数のJSターゲット

共有ネイティブソースセットからプラットフォーム固有のAPIにアクセスする必要がある場合、IntelliJ IDEAは、共有ネイティブコードで使用できる共通の宣言を検出するのに役立ちます。 その他の場合は、Kotlinのexpectedとactualの宣言のメカニズムを使用してください。