9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ZOZOAdvent Calendar 2023

Day 8

KotlinでCLIツールを作る

Last updated at Posted at 2023-12-07

こんにちは!tkhsktです。
みなさん、Kotlin書いてますか?Kotlin、楽しいですよねぇ。

この記事ではKotlinでCLIツールを作成する方法を紹介します。

Kotlin MultiplatformとKotlin/Native

KotlinでCLIツールを作る場合、2つの方法が考えられます。

  1. jarファイルを作成する
  2. MacやWindowsのネイティブバイナリを作成する

この記事ではJavaの仮想マシン(JVM)が無い環境でも動作する、2つ目の方法を紹介します。

ここで肝となる技術がKotlin MultiplatformKotlin/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を指定していますが、他にもlinuxX64mingwX64などのターゲットを指定可能です。詳しくは公式ドキュメントをご確認ください。

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.kexehelloにリネームすれば最終的な成果物の完成です。

おわりに

この記事ではApple siliconのMacで動作するごくシンプルなCLIツールを作成しましたが、Kotlin/Nativeでは設定次第で様々な環境に向けたバイナリを生成することができます。また、ktorio/ktorなどのKotlin Multiplatformに対応したライブラリなどと組み合わせることで、より高度なCLIツールを作成することも可能です。

興味を持った方はぜひ試してみてください。

  1. 2023/12/05時点

9
1
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
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?