Clojure でコマンドラインツール作成のお供に lein-jlink のススメ
- 前置き
- 前提
- jdeps で必要モジュールを調べる
- project.clj に必要モジュール指定の設定を追加する
- カスタム JRE 作成
- カスタム JRE 作成場所の設定
- 実行スクリプト作成
- パッケージング
- 最後に
前置き
英語的には Motivation に当たるのかな。
Clojure + GraalVM で Java ランタイム不要の CLI 作成可能な事は 以前の記事 でも紹介したが、その後作ったツールは全部 GraalVM でいいかと言うとそういう訳でもなく、 実際試してみると Unsupported Feature やらでコケる事が結構ある。
その場合別の配布の選択肢として Java ランタイム含めるケースがあるが、そういや 最近の Java って JRE バイナリ配布されてないよな?どうすんだっけとふと思い出したのが Java Platform Module System、略して JPMS、正式リリース前は Project Jigsaw と呼ばれてたやつ。
これによってできる事の一つに必要モジュールのみのJRE作成
と言うものがあり、
Clojure でもその恩恵を受けられないかなと探してみたら
lein-jlink
と言う Leiningen の PlugIn を見つけた。
名前の通り jlink
と言う 必要モジュールのみ入れたJRE作成
が可能な
JDK ツールコマンドを leiningen から設定・使用可能にしたプラグインである。
さっとググった限り jlink
単体の日本語記事は割と出てきたが、Clojure と絡めたものは
すぐには見つからなかったのでさほど大した事は書いてないが記事を書くことにした。
コマンドラインツールを GraalVM native-image で作れなかった場合にでもどうぞ。 さすがに native-image で作成された単体 exe にはシンプルさで負けるがそれでもさほど大きくないサイズで JRE を同梱できるのは良い。
前提
JDK 入れたフォルダの bin
にPATH を通しててコマンドプロンプトで jlink
が使えたら ok。
jdeps で必要モジュールを調べる
まず、lein uberjar
で JRE 上で動作する実行可能 jar を作る。
次に、jdeps --list-deps xxx.jar
で必要 Java モジュールをリストアップする。
自分が作ったとあるツールではこんな出力が出た。
>jdeps --list-deps test-tool-0.1.0-SNAPSHOT-standalone.jar java.base java.desktop java.management java.security.jgss java.sql java.xml.crypto jdk.javadoc jdk.jdi jdk.unsupported
オプションなしで jar だけ指定するともっと細かく出るがこの後設定するのにはこれで十分だと思う。 ・・・何か leiningen の追加 task で設定できそうな出力だなあ。
project.clj に必要モジュール指定の設定を追加する
さっきの情報を元にproject.clj
にこんな感じの設定を追加する。
:jlink-modules ["java.desktop" "java.management" "java.security.jgss" "java.sql" "java.xml.crypto" "jdk.javadoc" "jdk.jdi" "jdk.unsupported"]
java.base
モジュールはデフォルトで指定されるので追加不要との事(参考)。
カスタム JRE 作成
leiningen プロジェクトフォルダルートに入って以下を実行。
lein jlink init
するとデフォルトでは target\image
下にカスタム JRE が出来る。
:jlink-modules
で指定したモジュールをカスタム JRE に含めるようで、指定なしのjava.base
のみだとサイズは 24.1 MB と中々小さめだが
今回の指定では約 49 MB と倍増した。とは言え昔のような JRE 全体を同梱していたサイズと比較すればそれでも十分小さい方だと思う。
ただすぐ後で書いているが、デフォルトの場所は変更した方が良さそう。
カスタム JRE 作成場所の設定
自分がまだちゃんと公式ドキュメント類を読んでないだけかもしれないが
後述する lein jlink assemble
時に target\image
フォルダが消される動きをするみたいで assemble
タスクが
完了しなかった。これを回避するにはカスタム JRE 作成場所を決める :jlink-image-path
の値に別のパスを指定してやれば良い。
例えば project.clj
に以下を追加する。
:jlink-image-path ["image"]
これで assemble
タスクが最後まで行けるようになる。
実行スクリプト作成
既に出てきたが以下を実行。
lein jlink assemble
これで :jlink-image-path
に指定した場所に uberjar
された実行可能 jar ファイルがコピーされ、
その場所にあるカスタム JRE で動かすスクリプト(bat/ps/sh)が作られる。
コマンドライン引数無しならただスクリプト実行すれば良いし、 有りなら自分が使う起動スクリプトにコマンドライン引数を受け入れるようちゃちゃっと修正すれば良い。
パッケージング
一応 lein jlink package
で tar.gz
または zip
で固められるが
試してみた所 プロジェクト名などでフォルダを作らず、:jlink-image-path
の下にある
ファイル、フォルダを直接固める動きなのが自分は好きじゃなかったのでこれだけ使っていない。
最後に
やってみて実感したが JPMS は後方互換性も十分に考慮されてるようで、 詳細は出せないが今回自分がテストで使った過去作った Java(Clojure) コマンドラインツールは 依存ライブラリが別にモジュール化されている必要もなければ、 自分が作ったコードを特別何かモジュール化する必要もなく利用可能なのはちょっとした感動だった。
いやね、何か色々作業する必要あるのかなって思ってたんで。
もしかしたら使い続けてて GraalVM native-image の時のように 制限なり特有の問題にぶち当たったりするかもしれないがそれはまた起こった時にでも。