日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

コマンドでコマンドプロンプトのサイズを変更する(別サイトのブックマーク)

ブクマ。

コマンドでコマンドプロンプトのサイズを変更する

余談だがコマンドヘルプ(/?)だと省略記法って書いてなかったんだけど どこに情報あったんだろう?

Clojure の Macro 中(defmacro)での型ヒント

英語ならググれば割とすぐ出てくる話題だが自分用にメモ。

最近は ClojureCLI ツール書く時、GraalVM で実行 exe を生成できる可能性を残すべく 自分が書くコード中では reflection warning を出さないよう型ヒントをつけまくってる。

ある日 defmacro 中で reflection warning 出てる時これを解消するにはどうすれば良いのかが分からなかった。どうも defmacro 中では単純に 公式リファレンスのように書いてもダメみたい。

自分がやりたいのは defmacro 引数中に型ヒントを付与したかったのだが stackoverflow にあった。

(defmacro def-string
  [name value]
  `(def ~(vary-meta name assoc :tag `String) ~value))

引用元:Stackoverflow の Clojure defmacro loses metadata トピック回答

なるほど ^ ってリーダーマクロだったのか(あまり良く分かってない)。

以下余談。

余談

余談1: シンタックスクォート中のアンクォート(~)中にまたクォートする(') のは未だにパッとは分からんなあ。

余談2: はてなmarkdown の解釈の問題かどうかは分からんけどシンタックスクォートの文字も余談 1 に表示したかったけどエスケープの仕方が良く分からんので諦めた。

余談3: 今回初めて知ったけどClojure公式サイトのマクロの説明ってものっそい説明少ないのね。肝になる余談1に上げた主要文字に関する説明全然ないし。あんまり使わせたくないのかなあ。

この Protocol の使い方正しいんだろうか?

Clojure に限らないんだが関数名決めるのってプログラミングで一番難しいとか 誰か偉い人も言ってた気がする。

んで、自分で作った関数に付けたい名前が clojure.core にある関数名と被っている事があった場合どうするのが良いのか。

最も単純なのは被らない名前にしてしまう、だがそれも面白くないなとちょっと考えてたら そういや Protocols 使えるんじゃね?とふと思って試してみた。

name で使った例

name ってのは引数で Symbol か Keyword を受け取ると文字列を返す関数。

(name 'a)
"a"
(name :b)
"b"

これが例えば String と言う java.lang.Class を渡すとエラーになる。 java.lang.Class は未対応なので当然の話。

(name String)
Execution error (ClassCastException) at~

これを Protocols で拡張してみる。

(defprotocol ExtendName (name [x]))
(extend-protocol ExtendName
  Class (name [x] (.getName x))
  Object (name [x] (clojure.core/name x)))

最後に Object に指定しておくとここが実質デフォルト実装になる。 公式 でも以下のように書かれてるのでこの書き方は別に問題なさそう。

To define a default implementation of protocol (for other than nil) just use Object

nil 以外と言う但し書きがあるけどそんな問題にならないかと思う。

これを評価すると既存の実装上書きするよと Warning 出るがそこはしょうがない。 再び実行してみると、

(name 'a)
"a"
(name :b)
"b"
(name String)
"java.lang.String"

結果 java.lang.Class にも対応した name になった。

デフォルト実装の書き方はまだしも、core に定義されてるのを上書きするのは どうなんだろうかと言うのが疑問として残ってる。

name は単純だから元の処理を邪魔しない形になってるが、 拡張しようとする関数を良く理解してないと思わぬ所でハマりそうな気がしないでもないが どうなんだろう。

と、疑問を残しつつも思いついたネタだったので今回記事に上げてみた。

余談: Multimethods と Protocols どちらを使うべきか

自分は関数の引数の最初の型による dispatch で事足りるなら Protocols、 そうでないもっと複雑な dispatch をしたければ Multimethods と言う理解。

実際に比較検証した事はないが Protocols の方がパフォーマンスも良いらしい。

後者が必要になる場面と言うのは実際かなり少ないと思う。

fossil を使ってみる

更新履歴

2020/07/13

  • fossil init --template の使い方追加
  • ブログ内記法を Markdown へ変更
  • 同じ記事の記法変更は不可の為、Markdown 記法の記事を新規に追加し、旧記事は内容削除、新記事へのリンクを入れ、新記事にも旧記事の注記を追加

2017/09/15

  • branch の使い方を追加

記事移行

今回久々に追記したついでに今使ってる Markdown 方式に変更。 しかしはてブロって既存記事の変更はできないのね。微妙に不便。 旧記事 は内容削除して 新記事のリンクだけ残してある。

参考

はてブロヘルプに書いてあるらしい。

いったん作成した記事の編集モードを、後から変更することはできません。

はじめに

最近、個人ツールではあるがちょっと規模が大きめのものを作ろうと色々検討している時、SCM 含むプロジェクト管理ツール使うかどうしようかと頭をもたげてきた。開発業務をまともにやってきた訳ではない為この手のツールを使った事がない。

そんなに比較検討した訳ではないが、使う要件として - 自分ひとりなので導入・運用コストが軽いやつ - 公開するものではない

この辺りで適していそうな fossil なるものを使ってみる事にした。どうやら sqlite の作者が作ったものらしい。

参考リンクとか

以降は自分の作業ログ的なものを書いていく。

インストール

単一実行ファイルなので PATH が通った所に置くだけ。

リポジトリ作成

> fossil init hoge.repo
project-id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
server-id:  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
admin-user: xxxxxx (initial password is "xxxxxx")

ユーザ ID は今ログインしてるユーザ、パスワードは初期のが何か自動生成される。このメッセージはパスワードも書かれているので一応保管しとく。余談だが .repo と拡張子付けないと作れない事に小ハマりしたw

リポジトリ作成時、他既存リポジトリから設定を引き継ぐ

リポジトリ作成後、毎回以降の設定するのも面倒だなと思ってちょっと調べたら 今更だが既存リポジトリから設定を引き継ぐオプションがあったのでメモ。

> fossil init --template other.repo hoge.repo

other.repo に設定を引き継ぎたいリポジトリファイルの指定をする。

リポジトリ設定

UI 画面起動

> fossil ui c:\path\to\hoge.repo

要パス指定。自分の Windows7 環境だとコマンド叩いた後 Firefox で勝手に起動した。またこのコマンドは fossil によるローカル Web サーバが動くので別コマンドプロンプト立ち上げて動かしとくのが良さそう。

プロジェクト名設定

立ち上がると出てくる画面に Project Name 入れてくれと出るのでそれに従い setup/config でプロジェクト名を設定。leiningen で作ったプロジェクト名と同じにした。後は Project Description に軽く概要だけ書いて設定。他にも WikiWYSIWYG にするかどうかとか Wiki マークアップに HTML を使うかどうかの設定とかあるがそれは変えずにそのままとした。

ローカル時間に変更

Timeline 見ると、初っ端のリポジトリ作られました的なものがあるが、その時間が UTC だったので Admin -> Timeline -> Use UTC のチェックをオフに変更。

ファイル追加

無視ファイル指定

コマンドラインだと fossil settings ignore-glob を使うようだが、Web UI にも setting にあったのでそこで設定。

.gitignore, repl-port, *.class,*.dependencies

leiningen でソース管理外にするのはこれくらいかな。残念ながら ディレクトリ指定はできない模様ディレクトリ指定できれば target 以下を除外するとかもう少し簡単にできるんだがしょうがない。

リポジトリチェックアウト

trunk

> fossil open hoge.repo

最新のワーキングコピーをカレントフォルダに作るコマンドだが、全く新規の場合は当然何もない。また、後述のファイル追加後だと FOSSIL とか言うテンポラリファイルらしきものができる。

特定の branch をチェックアウト

> fossil open hoge.repo some-branch

some-branch が branch 名になる。

ファイル追加

hoge という leiningen プロジェクトフォルダ直下に移動した上で fossil add . とする。

> fossil add .
ADDED  hoge/doc/intro.md
ADDED  hoge/LICENSE
ADDED  hoge/project.clj
ADDED  hoge/README.md
ADDED  hoge/src/hoge/core.clj
ADDED  hoge/test/hoge/core_test.clj

無視ファイル・フォルダ設定効いてるみたい。

変更確認

コミット前にどのファイルが変更されるかを確認するためのコマンドはこれ。

fossil changes

コマンド実行結果は add 時と同じなので割愛。

ファイルレベルの追加変更削除ならこちら。個人的には実際はこっちを使う事が多い。

fossil status

変更確定

fossil commit -m "describe changes."

-m オプションで変更点を記述するみたいだが敢えて -m を指定せずにコマンド叩くとメモ帳が開いてコメント入れてくれと出た。 日本語を入れて Web UI で Timeline 確認してみるとちゃんと表示できてる。

ただ改行の Warning がちと気になる。

./doc/intro.md contains CR/NL line endings. Use --no-warnings or the "crnl-glob" setting to disable this warning.
Commit anyhow (a=all/c=convert/y/N)? a
New_Version: 19dd6de9a5e6be5b62509a68023ea34350dbe1d2

改行は自分で管理するので今回はチェックしないように変更。やり方は Setting にある cnrl-glob の設定値に * を入れれば OK。

branch を切る

自分の目的は trunk はリリース用として現状維持しておきたいが、branch を切る事で trunk 影響を与えずに 実験的な変更や大きめの変更を行いたい時に使う。

trunk を open したワーキングディレクトリで、

fossil branch new new-branch-name basis

basis は trunk のどのバージョンで branch を切るか、new-branch-name が作成するブランチの名前になる。 切った branch 以降で作業したい場合は、trunk とは別のワーキングディレクトリを適当に作って そこで fossil open で new-branch-name を指定する。

ワークフロー

  1. チェックアウト fossil open c:\path\to\fossil.repo
    • これもバッチ作って PATH が通った所に置いといて、1 日の開始にまずチェックアウトかける
  2. ゴリゴリコーディング
  3. 変更

    • ファイル追加なら add
    • ファイル削除なら rm
    • 既に管理下にあるファイルの変更ならコミットまで特に何もする必要なく、こまめにファイル保存だけしとく
    • 元に戻したければ revert
  4. そんでキリの良い所でコミット

他調べた事

diff

Web UI だと Timeline -> Show files で diff のリンクが出て前のリビジョンとの差分が確認可能。コマンドラインでも diff とか gdiff があるようだが動作未確認。

emacs 連携

Emacs-Fossil: Emacs Integration for Fossil ここ見るとできなくはなさそうだがめんどくさそうな割に恩恵が少なさそうなので自分は未設定。

UI をスタートアップ設定する

UI 用組込 Web サーバは PC 起動したら動かしっぱなしにしておきたいのでスタートアップ用のバッチファイルを用意する。中身は単に fossil ui c:\path\to\fossil.repo なだけ。Web ブラウザを起動させたくない時は fossil server で良いらしいが一人用だし ui でいいや。

これの繰り返しかな。

複数人でやる場合はログコメントは入れてもらうとかキリの良い所でのコミットを守ってもらうとか追加時はちょいと話し合うとか緩いながらもある程度のルール決めはした方が良さ気。

他気になる部分

SQL 直接叩けるっぽい

スキーマ確認は必要だが、内部 DBMSsqlite 使ってる事もあり SQL 直接叩けるみたい。UI では見せてないような細かい情報もこれで取れるんじゃないかなとちょっと期待。Web UI だと何のチェックもしてないから十分注意してくれとあるが、使うにしても自分は SELECT しかやるつもりはないので多分大丈夫かな。

Clojure でコマンドラインツール作成のお供に lein-jlink のススメ

前置き

英語的には 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 uberjarJRE 上で動作する実行可能 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 packagetar.gz または zip で固められるが 試してみた所 プロジェクト名などでフォルダを作らず、:jlink-image-path の下にある ファイル、フォルダを直接固める動きなのが自分は好きじゃなかったのでこれだけ使っていない。

最後に

やってみて実感したが JPMS は後方互換性も十分に考慮されてるようで、 詳細は出せないが今回自分がテストで使った過去作った Java(Clojure) コマンドラインツールは 依存ライブラリが別にモジュール化されている必要もなければ、 自分が作ったコードを特別何かモジュール化する必要もなく利用可能なのはちょっとした感動だった。

いやね、何か色々作業する必要あるのかなって思ってたんで。

もしかしたら使い続けてて GraalVM native-image の時のように 制限なり特有の問題にぶち当たったりするかもしれないがそれはまた起こった時にでも。

Programming Clojure, Third Edition 読む(宣言)

どうも最近の(と言っても随分前だが) Clojure の新機能イマイチ理解できてないなあと思い公式やネットの解説とかつまみ食い的に読んだりするのだがなーんかあんまり身についた気がせず、そろそろ GW だけど今のご時世遠出はリスクあるしここらで一つ Programming Clojure, Third Edition を買って読もうと思い立った。先のリンクは元の出版社のオンラインブックだが、自分は Kindle 版を購入。後でリンク先の方が安い事を知ったけどまあいいや。それはそうと shiro さんはもう翻訳してくれないのかなあ。

しかし 2nd Edition の Clojure っていくつだったんだろと改めて確認すると 1.3 だったのか。JVM が更新されてる他はほぼ BREAKING CHANGES ってのはないから基本最新版を使ってて問題ないんだが、色々追加されてる機能を自分で使用感も評せずに 2nd Edition 以前の知識でコード組むのも何かもったいないなあと言う気がして。読んだ結果 Multimethod のような必要なければ使わなくても問題ない機能だったとしてもそれはそれで。

さて、自分でモチベ保とうと宣言だけする記事を投稿したが続くかな。 一応プログラミング Clojure 第2版は以前一通り読んだのでそこから何が新しくなったのか、新しく追加された部分は良く解説されてるかくらいの寸評は読み終わったらまた記事で書こうと思う。

Windows GraalVM で Clojure 製CLI ツールの native-image 作ってみた

2020/07/01 追記

GraalVM 20.1.0出たので インストールしてる時 native-imagegu install native-image で入れようとした時に 入れられなかったのでローカルでインストールした手順も追加。20.0 でも同じ PKIX なんちゃらのエラーだったので おそらく証明書かネットワークの問題だと思い keytool で色々やってたが上手く行かなかったので。

GraalVM 20.0.0 になって native-imageWindows 対応が experimental はまだ取れてないが improves significantly な状態になったそうなのでそろそろどうかなと試してみた。

自分の native-image の理解

  • JDK, JRE 無しで単体動作する exe が作れる
  • JVM 起動時間が無いためプログラムの起動が速い
  • 起動後の動作については JVM で動作するものと比べ必ずしも速いとは限らない
    • 検証した人によればむしろ遅いらしい

他でも言われているが、これならJava(Clojure)製アプリの配布が大分楽になる。開発ではないが仕事で必要に応じてちょこちょこ CLI ツールを作っていた自分にとっては前から気になっていた。

ビルド完了までの手順

Graal VM 本体 JDK11 インストール

公式GithubからWindows版のgraalvm-ce-java11-windows-amd64-xx.x.x.zipをダウンロード(xx.x.x は最新版。今回は 20.0.0)

これを展開したフォルダのbinを PATH に通しておく。 自分は既に入れている OpenJDK13 と競合しないよう JAVA_HOME 環境変数を GraalVM の方へ切り替え、PATH%JAVA_HOME%\bin を設定した。

java -version で表示される中に GraalVM が見えていれば OK。

Native Image コンポーネントインストール

GraalVM 20.0.0 リリースノート によると Native Image コンポーネントは GraalVM をインストールしたフォルダの bin 以下にある gu 経由でインストールしてねとあるのでそれに従う。gu ってのは GraalVM Updater の事らしい。

公式ドキュメントInstallation from Catalogの項目によると以下でダウンロード・インストールまでしてくれる。

gu install native-image

他にどんなコンポーネントが入れられるのかはgu availableで一覧が出る。 Windows だと 20.0.0 時点では native-image のみの模様。他 OS だと Ruby とかのコンポーネントあるのかな?

gu install native-image で失敗する場合 [2020/07/01 追記]

ローカルにコンポーネントをダウンロードしてから gu でファイル指定してローカルインストールも可能。 試したのは GraalVM 20.1.0。

Github のリリースから Windows だと graalvm-ce-java8-windows-amd64-20.1.0.zip をダウンロードし、 コマンドプロンプトでダウンロードしたフォルダに移動し gu -L install graalvm-ce-java8-windows-amd64-20.1.0.zip とする。

>gu -L install native-image-installable-svm-java11-windows-amd64-20.1.0.jar
Processing Component archive: native-image-installable-svm-java11-windows-amd64-20.1.0.jar
Installing new component: Native Image (org.graalvm.native-image, version 20.1.0)

公式ドキュメント では Enterprise 版での説明になってるが Community 版でも同様にできるみたい。

Visual Studio 2019 インストール

native-image で単体動作する exe を作るにはどうやら C/C++ コンパイラが必要らしく、また Windows では コンパイラで exe を作るのに必要なヘッダ、ライブラリをあらかじめ指定した状態で立ち上がる開発者用コマンドプロンプトから native-image を 実行する必要があるみたい。

で、これらを満たすにはこの記事時点だと Visual Studio 2019 をインストールする必要がある。

ただこの辺りは上手く動いた推測で話しているので間違っていたり余計な手順があるかもしれない。

実際の手順

  1. Visual Studio 2019インストーラダウンロード
  2. インストーラ起動後、ワークロードC++によるデスクトップ開発を選択

サイズがデカいのでしばらく待つ。

lein-native-image のインストール・設定

leiningen のタスクで native-image を生成してくれる lein-native-image と言うプラグインが既にあるのでありがたくこれを使わせてもらう。

今回テストしたツールのproject.cljはこんな感じ。多分これが最低限の設定。 ツールとしては詳細は書けないが複数ファイルの CSV を読み込んでゴニョゴニョするもの。Clojure 1.9 なのは元々 Java7 JRE で動かす事を想定していたため。

(defproject native-image-test "1.0.0"
  :plugins [[io.taylorwood/lein-native-image "0.3.1"]]

  :dependencies [[org.clojure/clojure "1.9.0"]
                 [org.clojure/data.csv "1.0.0"]
                 [commons-io/commons-io "2.6"]]
  :main ^:skip-aot native-image-test.core
  :target-path "target/%s"

  :profiles {:uberjar 
             {:aot :all
              :native-image
              {:opts ["--initialize-at-build-time"]
               :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}}})

native-imageに渡すオプションの--initialize-at-build-timeはこれを付けないとコンパイル途中で以下のワーニングが出て exe が生成されても JDK が無いと動かないものになるので必須。

Warning: Aborting stand-alone image build due to unsupported features

JVM の方に渡すclojure.compiler.direct-linking=trueコンパイル時間改善になるらしいので付けておく。 他にもオプション設定が色々あるが とりあえずはこれだけ追加している。

Reflection への対処

さて、どんな Java コードも生成できるわけではないようで、 色々と制限があるみたい。

その一つが Reflection で、自分は Clojure で開発する際あまり気にせずに作っていたが、今回 native-image 実行時に初めてつまづいた。

具体的には Clojure コードで *warn-on-reflection*true にしてコンパイル時に出る Reflection warning を放置して lein native-image を実行するとコンパイル途中で以下のワーニングが出て JDK に依存する exe しか生成されない。

Warning: Aborting stand-alone image build due to unsupported features

今回の場合は自分が書いたコードで発生していたもので何の型を受け取るか分かっていたのでタイプヒントを付けて Reflection warning を解消する事で解決できた。

これが外部ライブラリで出るようだったら面倒かも。一応 手動作成した設定ファイルで明示的に指定する 方法もあるみたいだが極力この方法は避けたいところ。

ビルド

  1. 64bit 向け開発者用コマンドプロンプトの起動
    • VS2019 を前述の手順でインストールするとスタートメニューに色々な種類のコマンドプロンプトが出るが、インストールした GraalVM は 64bit 版なのでx64 Native Tools Command Prompt for VS 2019を選択して起動
  2. そこで lein native-image 実行
    • それなりに時間がかかるのでしばらく待つ。

比較

実行時間の計測

ファイルをいじったりするものだったら Clojure-main の中の処理全体を time マクロで囲ってしまうのが手軽だがここらで UNIXtime コマンドのように Windows でも外部から実行時間を測る方法がないものかと調べてみた。

PowerShellMeasure-Command が良さそう(参考)

と言うわけで以下の名前がtimeにならないバッチファイルを PATH の通った所に用意した(時刻設定をする同名 DOS コマンドがあるので)。

@echo off
powershell -C (Measure-Command {%*}).TotalMilliseconds

batch からさらに別のプロセス呼び出すようなのは別プロセス実行分の時間は測れないのとカレントパスの exe の場合 .\ を明示的に指定する必要があるがそこはまあ良しとする。

結果比較

lein uberjarして作ったstandalone版のファイルサイズと実行時間は以下。

Java7 JRE native-image
ファイルサイズ 4.41MB 10.3MB
実行時間 1563.58ms 418.89ms

一見ファイルサイズが増大してるが、JRE7フォルダのサイズが122MBな事を加味すれば 相当小さくなっている。

実行時間も速い。ただ、これはもともと CPU 時間を大して使わないプログラムなので 大部分は起動時間が短縮された事が大きいと思う。Java プログラム特有の起動時のモタつきなく動くのはちょっとした感動。

プログラム本処理部分動作の動作やメモリ使用量がどうかなども気になる所ではあるがそれはまた別の気が向いた時にでも。

ビルドできるまでに調べた事

以下はここまでの手順に到達するまでの調査経緯メモ。興味なければ飛ばして構わない。

最初はVS2019インストールする前にlein-native-imageのインストール・設定のみ終わらせてlein native-imageを実行したのだが、以下のエラーが出てつまづいた。

Error: Unable to compile C-ABI query code. Make sure native software development toolchain is installed on your system.
Error: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
Error: Image build request failed with exit status 1
Failed to create native image

初っ端にエラーが出た後ググると以下がすぐ出た。

Unable to compile C-ABI query code #1258

Windows SDK for Windows 7 をインストールして SDK 開発者用コマンドプロンプトから実行すると良いとの事。自分の環境は Windows10 だし Windows 10 SDK でいいだろと入れてみたものの見当たらない。

Windows 10 での開発者用コマンドプロンプトってどうなってんのと探してみると Windows 7.1 SDKが開発者用コマンドプロンプトが含まれてた最終バージョン らしく、それ以降は Visual Studio の方に含まれてるらしい。どこからかは不明。直前リンクにあるトピだと少なくとも VS2015 以降はそうなってるっぽい。

また、Visual Studio 用開発者コマンド プロンプト って何なのと言うのも気になってみてみると、 公式ドキュメント によれば、

それは、特定の環境変数を自動的に設定するコマンド プロンプトです。

との事。

しかしこの辺の事は 公式ドキュメントの Prerequisites 辺りに書いてくれんのかなあ。どう書くと良いのか決めかねてるからまだ experimental なのかね。