LoginSignup
7
2

Kotlin Multiplatformのbuild.gradle.ktsを書く上でハマったこと

Posted at

はじめに

またも半年ぶりの投稿になってしまいました。

ここ半年ほど、Kotlin Multiplatform(KMP)でネイティブとJVMの両方をターゲットとしたアプリケーションを作成しています。

KMPとは文字通り、Kotlinでマルチプラットフォームなプロジェクトを簡単に扱うためのフレームワークであり、プラットフォーム間でコードを可能な限り共通化し、API呼び出しなどプラットフォーム固有の機能だけを個別に書くようにすることで、プロジェクトのメンテナンスコスト等を下げることを目的としています1

KMP自体はとても素晴らしい物なのですが、安定版になったのが去年の11月と、かなり新しい技術であるため、公式ドキュメント等が全体的に発展途上であり、日本語資料はおろか英語ですら資料が見つからないことも多いのが現状です。

そこで本記事では、KMP向けのビルドスクリプト(build.gradle.kts) を書くにあたってハマった点と、その解決法をいくつかメモ程度に記しておきたいと思います。

KMPを使おうとする方々が同じ問題にハマってしまい、時間を浪費することを多少なりとも防げれば幸いです。

なお、KMP向けの build.gradle.kts の書き方自体は公式リファレンスを読んで、トリッキーなこと2をせず、注意深く書けばそれほど難しくないかと思いますので割愛します。

KMPでfat JARを作成する

一般的に、KMPプロジェクトでJVMをターゲットとする場合、Kotlin Multiplatform Gradle pluginを plugins ブロックに追加し、kotlin ブロックに jvm ブロックを追加するなど、公式リファレンス通りの手順を踏めばOKです。

しかし、Gradle Shadow PluginKtor Gradle plugin3を使用してJVMのバイナリをfat JARとして配布したい場合、単に当該のプラグインを plugins ブロックに追加するだけではうまくいきません4

これを解決するためには、以下のコードのように、build.gradle または build.gradle.ktsplugins ブロックに application プラグインを追加し、 kotlinjvmブロック内でwithJava()を呼び出す必要があります。

plugins {
    alias(libs.plugins.kotlin.multiplatform)
    application // required by shadowJar
    alias(libs.plugins.shadowJar)
}

kotlin {
    jvm {
        withJava() // required by shadowJar
    }
}

KMPを使用してマルチプラットフォームアプリケーションを作成する以上、Javaのコードを書くことはあまり多くないと思います。

それにも関わらず、Javaコードを含めるような書き方をしなければ期待した通りのJARが得られないため、この問題に引っかかってしまった場合は、解決に時間を要してしまう方が多いかと思います。

KMPでのテストにKotestを使用する

Kotlinでよく使われるテストフレームワークとして、Kotestというものがあります。

こちらは(おそらくJVMターゲットでのテスト時に)JUnit Platform Gradle pluginに依存しているため、KMPで使用する場合であっても以下のようなコードで JVM ターゲットでのテストタスクの実行時に useJUnitPlatform() が呼ばれるようにしてやる必要があります。

kotlin {
    jvm {
        testRuns.named("test") {
            executionTask.configure {
                useJUnitPlatform() // required by kotest
            }
        }
    }
}

この点については一応Kotestの公式ページに載っていますが、コード例がJVM/Gradleの欄に載っているため、KMPでは不要だと思い込んで飛ばしてしまうかもしれません。

shadowminimize の例外設定

先述のfat JAR作成プラグインである shadow は、生成するJARから不要なclassファイルを削ってくれる minimize という機能を持っています。

しかし、使用する実装を実行時に動的に探すような一部ライブラリでは、本来必要なclassファイルをshadowが勝手に削ってしまい、実行時に一見不可解なエラーが出る、というケースに行き当たることがあります。

これはKMP固有の問題ではないため、通常のKotlin(Kotlin/JVM)などでも起こりますが、比較的苦戦してしまった問題であったため、こちらに載せておきます。

私がこの問題で引っかかったのは、

  • slf4j全般
  • KtorがJSONのデシリアライズを行うための ktor-serialization-kotlinx-json

であったため、build.gradle.ktsの当該ブロックに以下のような項目を追加してあります。

tasks.shadowJar {
    minimize {
        exclude(dependency("org.slf4j:.*:.*"))
        exclude(dependency("io.ktor:ktor-serialization-kotlinx-json:.*"))
    }
}
  1. https://kotlinlang.org/docs/multiplatform.html

  2. 同一のプラットフォームに複数のターゲットを作るなど

  3. 有名なWebサーバ・クライアントライブラリのKtorのGradle pluginにもfat JARを作成する機能がありますが、この機能は(コードを読む限り)shadowを使用しているらしく、shadowでの場合と同じ問題が発生します

  4. JAR自体は生成できるのですが、実行しようとすると「メインクラスを検出およびロードできない」というようなエラーが出ます

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2