ClojureのコードをMaven2でビルドする。

そこそこ頑張ってビルドしたので、成果をまとめておきますですだよ。
まずは、

を見てクダサシ。正直にビルドするだけなら、ここに記述されている内容を読めば特に問題ないです。

Clojureソースコードたる.cljファイルを配布物に含めるには。

clojure-maven-pluginにおけるClojureソースコードを配置するデフォルトの推奨ディレクトリは、
maven-resources-pluginの管理対象外なので、こんな感じに追記しる。

<build>
  <resources>
    <resource>
      <directory>src/main/clojure</directory>
    </resource>
  </resources>
〜以下省略〜
</build>

これで、.classが詰まってるjarファイルと、-sources.jarの中に、.cljファイルが含まれるます。

ユニットテストを、自分の好きなコンテキストで動かすには。

clojure-maven-pluginでは、testフェイズにおいて、clojure.testをブートストラップする為の、特別なスクリプトを自動生成して、そこからテストを動かします。
コードはここですね。

deftestの中で、必要なコンテキストを全て揃えて動かしているのであれば、それで特に問題ないのですけど、僕が書いていたテストコードは、ブートストラップコストが少々高めなモノを動かしていたので、出来れば、名前空間単位で生成した上で、束縛したいオブジェクトがあるました。


と、言うわけで、こんな感じの記述をpom.xmlにしる。
ポイントは、testScriptって所。
普通に使う分には、pom.xmlからの相対パスを書いておけば、特に問題無いのだけど、マルチプロジェクトに対応する為には、前の${basedir}ってのを付けておかないと、プラグインの中で、パスの処理をちゃんとやってくれないので、思いとおりに動かない事になるます。
resources-pluginとは、出来が違うって事であります。

<build>
  〜前省略〜
  <plugins>
    <plugin>
      <groupId>com.theoryinpractise</groupId>
      <artifactId>clojure-maven-plugin</artifactId>
      <version>1.3.2</version>
      <executions>
        <execution>
          <id>test-clojure</id>
          <phase>test</phase>
          <goals>
            <goal>test</goal>
          </goals>
          <configuration>
            <testScript>${basedir}/src/test/clojure/test.clj</testScript>
          </configuration>
        </execution>
      </executions>
    </plugin>
〜以下省略〜
</build>

ちなみに、test.cljの中はこんな感じ。

(require 'org.t2framework.lucy.clojure.test-lucy-config)
(use 'org.t2framework.lucy.clojure.lucy-config) 
(use 'clojure.test)
(import 
    (org.t2framework.lucy.clojure ClojureLucyConfiguration) 
    (org.t2framework.lucy 
      Lucy
      LucyBootstrap
    )
)
(when-not *compile-files*
  (let [results (atom [])]
    (let [report-orig report]
      (binding [report (fn [x] (report-orig x)
                         (swap! results conj (:type x)))]
        (binding [org.t2framework.lucy.clojure.lucy-config/lucy 
          (LucyBootstrap/init (ClojureLucyConfiguration.))]
            (try
              (run-tests 'org.t2framework.lucy.clojure.test-lucy-config)
            (finally (LucyBootstrap/destroy))) 
        )
    (shutdown-agents)
    (System/exit (if (empty? (filter {:fail :error} @results)) 0 -1))))))

.cljファイルをREPLでロードする分には動くのに、.classファイルを出力しようとすると出る謎のエラー対策

まぁ、コードとしても、そこそこ変な事をやっていて、Javaenumをあるルールに乗っとって、マクロでdefするみたいな。

(defmacro defvalues [values namer]
  `(do
    ~@(map 
      (fn [%] 
        `(def ~(symbol ((eval namer) %)) ~%)
      )
      (eval values))))
(defvalues (AutoInject/values) #(. #^AutoInject % getName))

ちなみに、ここで登場するAutoInjectってのは、こんなenumでつ。


こいつは普通に動かしてる分には特に問題なく動くのだけど、.classファイルを出力するべくコンパイラを起動すると謎のスタックトレースが出力されてコケてしまうのです。

Caused by: java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: property
        at clojure.lang.Compiler$FnExpr.emitValue(Compiler.java:3379)
        at clojure.lang.Compiler$FnExpr.emitConstants(Compiler.java:3417)
        at clojure.lang.Compiler.compile(Compiler.java:5123)
        at clojure.lang.RT.compile(RT.java:358)
        at clojure.lang.RT.load(RT.java:397)
        at clojure.lang.RT.load(RT.java:371)
        at clojure.core$load__6449$fn__6458.invoke(core.clj:4171)
        at clojure.core$load__6449.doInvoke(core.clj:4170)
        at clojure.lang.RestFn.invoke(RestFn.java:413)
        at clojure.core$load_one__6379.invoke(core.clj:4007)
        at clojure.core$load_lib__6400.doInvoke(core.clj:4044)
        at clojure.lang.RestFn.applyTo(RestFn.java:147)
        at clojure.core$apply__4370.invoke(core.clj:438)
        at clojure.core$load_libs__6417.doInvoke(core.clj:4070)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply__4370.invoke(core.clj:440)
        at clojure.core$use__6443.doInvoke(core.clj:4148)
        at clojure.lang.RestFn.invoke(RestFn.java:413)
        at org.t2framework.lucy.clojure.aspect$loading__6309__auto____1.invoke(aspect.clj:1)
        at clojure.lang.AFn.applyToHelper(AFn.java:171)
        at clojure.lang.AFn.applyTo(AFn.java:164)
        at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:2775)

print-dupなる何かがアレで、ソレ…らしい。さっぱり分かりませんが、要は、何か直列化したいらしい。
と言うわけで、こんなコードを足します。
処理としては、print-methodなる別なマルチメソッドにディスパッチしているだけ。

(defmethod print-dup AutoInject [o w] (print-method (. #^AutoInject o (getName)) w))

print-dupは、clojure.coreと言う名前空間にに定義されているマルチメソッドなのだけど、具体的なファイルとしては、core_print.cljに入っております。


これで、期待した動作になるのだけど、何故これでOKなのかは、実は良くわかりませぬ。
誰か教えて下さい。