こんにちは!tkhsktです。
みなさん、Kotlin書いてますか?Kotlin、楽しいですよねぇ。
この記事ではKotlinでCLIツールを作成する方法を紹介します。
Kotlin MultiplatformとKotlin/Native
KotlinでCLIツールを作る場合、2つの方法が考えられます。
- jarファイルを作成する
- MacやWindowsのネイティブバイナリを作成する
この記事ではJavaの仮想マシン(JVM)が無い環境でも動作する、2つ目の方法を紹介します。
ここで肝となる技術がKotlin MultiplatformとKotlin/Nativeです。Kotlin Multiplatformは異なるプラットフォーム間でコードを共有するための技術で、Kotlin/NativeはKotlinで記述されたコードを仮想マシンなしで実行可能なバイナリにコンパイルする技術です。この記事では、これらの技術を使用してCLIツールを実装します。
ゴール
この記事ではhello --lang <言語コード>
のようにコマンドを実行すると、指定した言語コードと対応する言語で「こんにちは」と出力するCLIツールを作成します。
hello --lang ja
> こんにちは
hello --lang fr
> Bonjour
セットアップ
まずはIntelliJ IDEAなどでKotlinのプロジェクトを作成しましょう。
この記事では使用しませんが、Kotlin Multiplatform Wizardを使用すると、目的に応じたKotlin Multiplatformのテンプレートをダウンロードできます。
この時点でのディレクトリ構造は下記のようになります。(一部省略)
.
├── src
│ └── main
│ └── kotlin
│ └── Main.kt
├── build.gradle.kts
└── settings.gradle.kts
mainディレクトリのリネーム
プロジェクトの作成後、main
ディレクトリをcommonMain
にリネームします。これはKotlin Multiplatformでの開発では、main
ではなくcommonMain
をデフォルトのSourceSetとして認識するためです。
build.gradle.kts
次に、ビルドの設定をします。Kotlin Multiplatformでの開発で一番面倒なのがgradleの設定です。最低限の設定は下記のようになります。コピーして使いましょう。
plugins {
kotlin("multiplatform") version "1.9.20"
}
group = "com.example.cli"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
macosArm64 { // Apple siliconのMacで動作するバイナリを生成する
binaries {
executable("hello") // hello.kexeという名前のバイナリを生成する
}
}
sourceSets {
commonMain {
dependencies {
// ここにライブラリの依存関係を追加する
}
}
}
}
この例ではターゲットにmacosArm64
を指定していますが、他にもlinuxX64
やmingwX64
などのターゲットを指定可能です。詳しくは公式ドキュメントをご確認ください。
Clikt
次に、コマンドラインの入力をパースするパーサーを追加します。Kotlin Multiplatformに対応したコマンドラインパーサーとしては、公式のライブラリであるKotlin/kotlinx-cliが存在しますが、これは実質メンテナンスされていない状態のよう1なので、この記事ではajalt/cliktというサードパーティー製のライブラリを使用します。
ライブラリの依存は、前のステップで設定したbuild.gradle.kts
に記述します。
...
kotlin {
...
sourceSets {
commonMain {
dependencies {
implementation("com.github.ajalt.clikt:clikt:4.2.1") // 追加
}
}
}
}
...
ここまででCLIツールの実装準備は完了です。
実装
まずは言語コードをenumで定義します。
enum class LanguageCode(val value: String) {
EN("en"),
JA("ja"),
FR("fr"),
;
companion object {
fun findByValue(value: String): LanguageCode? {
return entries.find { it.value == value }
}
}
}
次に、言語コードと対応した「こんにちは」を取得するクラスを作成します。
class Hello(languageCode: LanguageCode) {
val value = when (languageCode) {
LanguageCode.EN -> "Hello"
LanguageCode.JA -> "こんにちは"
LanguageCode.FR -> "Bonjour"
}
}
そして、Cliktを使用してコマンドを実装します。
class HelloCommand : CliktCommand() {
private val lang: LanguageCode by option()
.convert { // コマンドラインの入力をLanguageCodeに変換
LanguageCode.findByValue(it) ?: throw UsageError("サポートしていない言語コードです")
}.default(LanguageCode.EN)
override fun run() {
val helloValue = Hello(lang).value
echo(helloValue)
}
}
最後にMain.kt
を修正します。
fun main(args: Array<String>) = HelloCommand().main(args)
これで実装は完了です。
ビルド
実行可能なバイナリは、下記のタスクを実行することで生成できます。
./gradlew macosArm64Binaries
コマンドの実行後、<プロジェクトのルート>/build/bin/macosArm64/helloReleaseExecutable
を確認するとhello.kexe
というファイルが生成されていることがわかります。これが実行ファイルです。あとはhello.kexe
をhello
にリネームすれば最終的な成果物の完成です。
おわりに
この記事ではApple siliconのMacで動作するごくシンプルなCLIツールを作成しましたが、Kotlin/Nativeでは設定次第で様々な環境に向けたバイナリを生成することができます。また、ktorio/ktorなどのKotlin Multiplatformに対応したライブラリなどと組み合わせることで、より高度なCLIツールを作成することも可能です。
興味を持った方はぜひ試してみてください。
-
2023/12/05時点 ↩