真面目なブログはこっち 👉 blog.s64.jp

MavenにKotlin Multiplatform向けライブラリを公開する方法

先日 Kotlin Multiplatform 製モバイルアプリ向けの ライブラリを作って公開した ので、その時にちょっとつまづいた部分について。

検証環境

  • Gradle 4.10.3
  • Kotlin Gradle Plugin 1.3.31
    • org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31
  • Android Gradle plugin 3.3.2
    • com.android.tools.build:gradle:3.3.2

前提

Kotlin Multiplatform ProjectはGradleを用いて(Kotlin Gradle Pluginを用いて)ビルドされ、それらの依存関係はMavenリポジトリから取得されます。
すなわちKotlin MPP向けにライブラリを公開する際はMavenリポジトリへ公開する必要があり、またGradleでの依存性解決が前提とされています。

Gradle Module Metadataを有効にする

settings.gradleに以下の1行を記述します:

enableFeaturePreview('GRADLE_METADATA')

Kotlin MPP向けのライブラリは、Gradle Module Metadataを用いて依存関係を解決します。具体的には こういったファイル を出力します。

これがあると何が嬉しいかというと、依存関係としてたったこの1行だけでiOSやAndroid向けのバイナリをよしなに読み込ませることができます:

kotlin {
    ...
    sourceSets {
        commonMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                implementation "com.example:your-published-library:1.0.0"
                // ^ これだけ
            }
        }
        iosMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                // なにか参照しなくても、commonで記述した内容でiOS向けバイナリが取ってこれる
            }
        }
        androidMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                // こっちも同様
            }
        }
    }
}

各プラットフォーム向けに複数バイナリを出力する

平易な表現にしようとして厳密さを失っていますが、これはiOSやAndroidなどのプラットフォーム特有の問題を解決するためのものです。

iOSの場合

iOS向けアプリ開発では、開発時にはSimulatorを用いる場合が多々あります。
流通しているiOSデバイス実機はARM64アーキテクチャなのに対し、このSimulatorはx64アーキテクチャで動きます。
そのため、ビルドするアーキテクチャとして2通り分(必要ならそれ以上) の記述が必要です:

kotlin {
    def ios64 = iosArm64('iosArm64')
    def iosSim = iosX64('iosX64')

    sourceSets {
        ...
        iosMain {
            ...
        }
        ios64.compilations.main.defaultSourceSet {
            dependsOn iosMain
        }
        iosSim.compilations.main.defaultSourceSet {
            dependsOn iosMain
        }
    }
}

Androidの場合

現行のGradle Module Metadataの都合上、各variant向けの出力を設定しておく必要があります:

kotlin {
    android {
        publishLibraryVariants('debug', 'release')
    }
   ...
}

maven-publishプラグインを設定する

Kotlin Gradle Pluginは親切なので、maven-publishプラグインが適切に有効化されていれば MavenPublication を自動的に設定してくれます。
よって必要な設定は以下です:

plugins {
    id 'maven-publish' // apply plugin: 'maven-publish'
}
...
group = 'com.example.your.artifact.group'
version = '0.0.1-SNAPSHOT-YOUR-VERSION'

publishする

ローカルリポジトリであれば以下のコマンドでOKです:

./gradlew publishToMavenLocal

実行すると、たとえばiOS, Android向けにここまでの内容を参考に設定していた場合は 以下の成果物が得られます:

  • com.example:your-published-library:1.0.0
  • com.example:your-published-library-metadata:1.0.0
  • com.example:your-published-library-android:1.0.0
  • com.example:your-published-library-android-debug:1.0.0
  • com.example:your-published-library-iosarm64:1.0.0
  • com.example:your-published-library-iosx64:1.0.0

先頭のサフィックスが無いパッケージは .pom と .module ファイルしか持ちません。ここを起点に各プラットフォーム向けの依存性を解決する役割のみを持ちます。
-metadataはcommonで記述された公開I/Fの定義を持ちます。
残るモジュールは、各プラットフォームやABI向けのバイナリです。

使われ方

公開したライブラリの参照方法は Gradle Module Metadataを有効にする のセクションの通りで、サフィックスの無いモジュールを依存関係に加えるのみです。
ただし、Gradle Module Metadataを有効にしていないアプリの場合、各モジュールの参照が必要になる模様です。

こうして開発したライブラリは Kotlin MPP, Kotlin/Native, Pure Androidプロジェクト(Kotlinが有効なもの)で共通して利用することができました。