Clojure の cljfmt を GraalVM で Native Image 化してサクッと動かすぞー!!

2022-01-08 追記 ここから =====

cljstyle を教えてもらったー!

bufferings.hatenablog.com

2022-01-08 追記 ここまで =====

cljfmt をサクサク動かしたいのやー

Clojure 用の Linter の clj-kondo は GraalVM で Native Image 化してあるのでサクサク動く

GitHub - clj-kondo/clj-kondo: A linter for Clojure code that sparks joy.

↓でも、Clojure 用の Formatter の cljfmt は Leiningen で動かさないといけないので遅い

GitHub - weavejester/cljfmt: A tool for formatting Clojure code

cljfmt も Native Image があったらいいのになぁ

って思って探してみたら

↓こういうのが見つかった

Konrad Mrozek / cljfmt-graalvm · GitLab

けど、ちょっとバージョンが古いし、独自スクリプトでラップしてて、オプションをサポートしてないみたい?なので、もっといい感じのないかなぁ?そのまま cljfmt の機能を全部使いたいんだよなぁって思ってたら

↓あれ?本家に PR あるやん?しかもマージされてるやん?おお?

Native image by bsless · Pull Request #186 · weavejester/cljfmt · GitHub

ということでビルドしてみるー

M1 Mac なんだけどいけるかなぁ???

GraalVM の準備

SDKMAN! で GraalVM をインストールして、最後に「デフォルトにする?」って聞かれるけど、したくないので No にして

❯ sdk i java 21.3.0.r17-grl

Do you want java 21.3.0.r17-grl to be set as default? (Y/n): n

今のシェルでだけで GraalVM を使うようにして

❯ sdk use java 21.3.0.r17-grl

Using java version 21.3.0.r17-grl in this shell.

Native Image をインストール

❯ gu install native-image
Downloading: Release index file from oca.opensource.oracle.com
Downloading: Component catalog from www.graalvm.org
Processing Component: Native Image
Downloading: Component native-image: Native Image  from github.com
Installing new component: Native Image (org.graalvm.native-image, version 21.3.0)

これで GraalVM の準備は OK

ビルドの準備

cljfmt のソースコードを clone してきて、プロジェクトの中の cljfmt フォルダに入る

❯ git clone git@github.com:weavejester/cljfmt.git

❯ cd cljfmt/cljfmt

↓lein-native-image ってプラグインがあるんだねー。便利

GitHub - taylorwood/lein-native-image: A Leiningen plugin to build GraalVM native images

これを使ってビルドするんだけど、そのままだとビルドが通らない↓

❯ lein native-image
Compiling ClojureScript...
Reloading Clojure file "cljfmt.test-util.common" failed.
clojure.lang.Compiler$CompilerException: Syntax error compiling at (cljfmt/test_util/common.cljc:1:1).

よく分かんないけど project.clj の :hooks に書いてある cljsbuild をコメントアウトすればいいのかな?↓

Macro cljfmt.core/read-resource does not exist error when building native-image · Issue #236 · weavejester/cljfmt · GitHub

;;   :hooks [leiningen.cljsbuild]

はいビルド

❯ lein native-image
...
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]     (clinit):     386.71 ms,  2.51 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]   (typeflow):  15,335.96 ms,  2.51 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]    (objects):  36,926.89 ms,  2.51 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]   (features):   4,169.75 ms,  2.51 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]     analysis:  58,275.88 ms,  2.51 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]     universe:   3,879.56 ms,  2.47 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]      (parse):   4,026.37 ms,  2.31 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]     (inline):   3,612.80 ms,  2.24 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]    (compile):  34,094.63 ms,  2.37 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]      compile:  43,646.86 ms,  2.37 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]        image:   3,863.29 ms,  2.30 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]        write:   3,045.97 ms,  2.33 GB
[/Users/..../cljfmt/cljfmt/target/cljfmt:38771]      [total]: 127,314.04 ms,  2.33 GB
# Printing build artifacts to: /Users/..../cljfmt/cljfmt/target/cljfmt.build_artifacts.txt
Created native image /Users/..../cljfmt/cljfmt/target/cljfmt

お。できたっぽい!Native Image のビルドだから10分くらい覚悟してたけど2分で終わった!平和な気持ち。

パスの通ったところに置いとこか

❯ sudo cp target/cljfmt /usr/local/bin

❯ cljfmt
cljfmt [OPTIONS] COMMAND [PATHS ...]
      --help
      --file-pattern FILE_PATTERN                  \.clj[csx]?$
      --indents INDENTS_PATH
      --alias-map ALIAS_MAP_PATH
      --project-root PROJECT_ROOT                  .
      --[no-]ansi
      --[no-]indentation
      --[no-]remove-multiple-non-indenting-spaces
      --[no-]remove-surrounding-whitespace
      --[no-]remove-trailing-whitespace
      --[no-]insert-missing-whitespace
      --[no-]remove-consecutive-blank-lines

動かすー

適当なプロジェクトで試してみる。まずは普通に lein で実行した場合

❯ time lein cljfmt check
...
13 file(s) formatted incorrectly
lein cljfmt check .  7.67s user 3.48s system 104% cpu 10.678 total

10秒!遅い!

Native Image で実行した場合

❯ time cljfmt check
...
13 file(s) formatted incorrectly
cljfmt check .  0.41s user 0.08s system 96% cpu 0.513 total

0.5秒!良い!

IDEA に設定

この速さなら保存するたびに実行するんでもいけるぞー!って、FileWatcher プラグインで保存時に実行されるように設定してみたけど、IDEA の自動保存で書いてる途中にフォーマットされてしまうのでちょっと無理だったw

なので、External Tools に設定して、ショートカットで実行するようにしておいた。便利。

f:id:bufferings:20220106221144p:plain:w600

おしまい!

GraalVM (Native Image) すごいなー。そして cljfmt の公式で Native Image のビルド設定が入ってて素敵!