私は普段主にScalaを使っているので、ちょっと手の込んだ処理が必要だったりJava/Scalaライブラリを使ったツールが必要な場合にScalaで書けると便利だなと思うことがあります。
AirframeはScala用のDIコンテナを中心とした様々な機能を提供するライブラリ群ですが、そのうちの1つとしてairframe-launcherというCLIツール作成用のモジュールがあり、コマンドラインオプションを簡単に扱うことができます。また、sbt-packというsbtプラグインを使うと作成したアプリケーションを実行可能なコマンドとしてパッケージング、インストールできます。これらを組み合わせてScalaでCLIツールを作る方法を紹介したいと思います。
まずはbuild.sbt
に以下の記述を追加し、airframe-launcherを依存関係に追加します。
libraryDependencies ++= Seq( "org.wvlet.airframe" %% "airframe-launcher" % "19.11.1" )
手始めにメッセージを表示するだけの簡単なCLIツールを作成してみます。コマンドラインオプションは@option
アノテーション、サブコマンドは@command
アノテーションで指定します。実行時にコマンドラインオプションでサブコマンドが指定されていない場合、isDefault = true
が指定されたコマンドが実行されます。
package com.github.takezoe import wvlet.airframe.launcher.{Launcher, command, option} class Hello(@option(prefix = "-n,--name", description = "your name") name: String, @option(prefix = "-h,--help", description = "show help message", isHelp = true) displayHelp: Boolean) { @command(isDefault = true) def default(): Unit = { println(s"Hello ${name}!") } } object Main extends App { Launcher.execute[Hello](args) }
これをsbt-packでパッケージングします。plugins.sbt
に以下の記述を追加します。
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.11")
build.sbt
にパッケージングの設定を追加する必要があります。PackPlugin
を有効にし、コマンドと起動クラスのマッピングを行ないます。
enablePlugins(PackPlugin) packMain := Map("hello" -> "com.github.takezoe.Main")
sbt pack
でtarget/pack
ディレクトリに必要なファイル一式が生成されます。また、sbt packInstall
で$HOME/local/bin
ディレクトリに実行可能なコマンドがインストールされるのでこのディレクトリにPATHを通しておけば普通のコマンドと同じように使用できます。
インストールしたコマンドを実行してみます。
$ hello -n Naoki Hello Naoki!
続いてScalaコードを修正してサブコマンドを追加してみます。Hello
クラスに以下のメソッドを追加します。サブコマンド固有のオプションも定義できます。
@command(description = "display a message repeatedly") def repeat(@option(prefix = "-c,--count", description = "repeat count") count: Int): Unit = { for(_ <- 0 to count){ println(s"Hello ${name}!") } }
以下のように実行します。
$ hello repeat -n Naoki -c 3 Hello Naoki! Hello Naoki! Hello Naoki!
airframe-launcherにはヘルプを自動生成する機能もあります。isHelp = true
が指定されたオプションを指定して実行すると以下のようなヘルプが表示されます。
$ hello --help usage: hello [options] <command name> [options] -n, --name:[NAME] your name -h, --help show help message [commands] repeat display a message repeatedly
JVMの起動速度という問題はあるものの、CLIツールでもそこそこ大きなものであれば既存のJVM資産が活用できタイプセーフかつ記述能力の高いScalaで書くメリットもあるかと思います。こういった用途にもScalaの活用の幅が広がればと思います。