日曜プログラミング

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

leiningen の AOT 設定

leiningen(と言うか Clojure)では gen-class を指定した名前空間(言い換えると Java の名前付きクラス)を参照したプログラムを走らせる場合、AOT コンパイルが必要になるのは多分 leiningen を使う人ならほぼ周知の事だと思う。

例えば leiningen では名前空間 foo.b に gen-class 指定されてる場合 project.clj に以下の一行が追加になる。

:aot [foo.b]

また uberjar する場合、最近の Version の leiningen だと自分は app テンプレートを見てこう入れておく事が多かった。

:profiles {:uberjar {:aot :all}}

つまりプロジェクトの全ファイル AOT コンパイルすると。その方が起動早くなるしね。

ただ、この設定の時にプロジェクトのファイル構成が以下のような関係だった場合に lein run と lein uberjar でちょっと引っかかる事になる。

foo.a
foo.b を import している
foo.b
gen-class 指定有り

これは上手く行くが(bar は適当な関数)、

lein rum -m foo.a/bar

uberjar する時はダメ。

> lein uberjar
[foo.b] and :all have a type mismatch merging profiles.
Compiling foo.a
Exception in thread "main" java.lang.ClassNotFoundException: foo.b, compiling:(a.clj:1:1)

uberjar 時のメッセージをよくよく見ると分かるのだが、どうも

  • :aot と :profiles {:uberjar {:aot :all}} とのマージが上手く行ってないっぽい(メッセージの 1 行目)
  • :aot :all 指定時のコンパイル順がどうやら名前空間の昇順でやろうとしている(メッセージ 2 行目)
  • コンパイル順と依存を解決する順番が合ってない(ここでコンパイルして欲しいのは gen-class 指定した foo.b からやって欲しい)

これらが重なると発生するみたい。そういやこのメッセージ前も見たけど無視してた(この記事の一番下)。

とりあえずの回避法としては :profiles {:uberjar {:aot :all}} は無くして、:aot 指定を以下のようにして uberjar する。

:aot [foo.b foo.a]

ただこれも例示の為に極力ファイル減らしたんだが、ちょっとファイルが増えてくると途端に面倒になる。何かいいプラグインないかな。