AntでEclipseのECJ(Eclipse Compiler for Java)を使う
はじめに
知っている人にはよく知られていることですが、EclipseのJava開発環境、JDTがJavaソースのコンパイルに使っているコンパイラは、OracleのJDKなどとは違い、Eclipseが独自実装しているコンパイラ ECJ (Eclipse Compiler for Java)です。
ECJとOracle JDK(OpenJDK)のjavacと何が違うのかというと、
- コンパイラの許容ルールが異なる。(おそらくOracle JDKより緩いケースが多そう)
- コンパイルが一部失敗しても成功したclassファイルは生成される。
- それどころか失敗したクラスでさえ仮のclassファイルを生成させることができる。
- ある程度のコンパイルエラーを許容しなければならない荒っぽいプロジェクトでは使える性質でしょう。
- コンパイラオプションが異なる。
- 警告などの出力フォーマットが異なる。
- JenkinsでWarnings Pluginを使っている場合は、ECJ用のルールがデフォルトで存在しています。
ECJはEclipseを立ちあげなくてもAntから使えます。(batch compiler)
んじゃあ、AntでEclipseのJavaソースコンパイラ使ってみよう
プレーンなbuild.xml
たとえば、下記のようなフォルダ構成だったとします。
project-root/ build.xml src/ Hoge.java compiled/ (Hoge.classがここにできればいいな)
この場合、build.xmlはこうなるでしょう。(わざとシンプルにしてます。Ant1.8以上)
<project default="build"> <target name="build"> <delete dir="compiled" /> <mkdir dir="compiled" /> <javac srcdir="src" destdir="compiled" /> </target> </project>
上記例だとパスの通ってるjavacを叩いているので、これをECJに切り替えましょう。
ECJを手に入れよう
ECJを構成するJARを手に入れましょう。
入手方法1. 直接ダウンロード
http://mvnrepository.com/artifact/org.eclipse.jdt.core.compiler/ecj/
で見つかりました。 (groupId=org.eclipse.jdt.core.compiler, artifactId=ecj)
どっか公式DLページあるんですかね?(検索あきらめた)
ecj-..jar をダウンロードしましょう。JARの中にAnt用のアダプタとコンパイラコア本体が入っています。
ここでは project-root/ant-lib/ にそのjarをおいておきます。
入手方法2. Eclipseから取得
Eclipse本体からでも取得できます。
まずは下記の場所からJDTのコアのプラグインJARを見つけましょう。
eclipseインストールパス/plugins/ org.eclipse.jdt.core_3.10.0.v20140604-1726.jar みたいなの
んで、上記jarをzipファイルとして解凍してAntへのアダプタとなる jdtCompilerAdapter.jar をとっておきましょう。
ここでは下記2つのJARを project-root/ant-lib/ にいれておきます。
- org.eclipse.jdt.core_3.10.0.v20140604-1726.jar
- jdtCompilerAdapter.jar
Ant関係なくECJを起動してみよう
ECJはスタンドアロンでjavacのように扱うことが可能です。javacと基本ほとんど同じですね。
java -jar ECJJARファイル コンパイルしたいソース.java ※ECJJARファイルはそれぞれ下記を使用。 入手方法1の場合はecj-*.*.jar 入手方法2の場合はorg.eclipse.jdt.core_3.10.0.v20140604-1726.jar ※マニフェストで起動したくない場合は起動クラスとして org.eclipse.jdt.internal.compiler.batch.Main を指定すればよい。 ※依存するライブラリがある場合は-classpath "依存.jar"みたいな感じでjavaに渡してあげる。 ※細かいオプションみたい人は -? 引数を指定してみてね。
ECJをbuild.xmlに組み込んでAntから呼んでみよう
ECJをtypedefし、それをjavacで参照、javacのcompilerでorg.eclipse.jdt.core.JDTCompilerAdapter
を使うように指定すればOKです。
<project default="build"> <typedef name="ecj" classname="org.eclipse.jdt.core.JDTCompilerAdapter"> <classpath> <fileset dir="ant-lib" includes="*.jar" /> </classpath> </typedef> <target name="build"> <delete dir="compiled" /> <mkdir dir="compiled" /> <javac srcdir="src" destdir="compiled" compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> <ecj /> </javac> </target> </project>
typedef+<ecj />
が面倒な人は、下記のいずれかのようにすれば、compiler属性の指定だけで使えます。
- Ant起動時に
-lib ECJのjar
を指定する。 ${ANT_HOME}/lib
や~/.ant/lib
にそのままECJ関連のjar放り込む。- typedefを作らず下記のようにcompilerclasspathをインラインで書く。
<javac srcdir="src" destdir="compiled"> <compilerclasspath> <fileset dir="ant-lib" includes="*.jar" /> </compilerclasspath> </javac>
compiler属性の指定が面倒な人は、下記のいずれかをすればいいでしょう。
<property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter" />
をどこかに入れておいたり、- Antの起動引数でプロパティ指定しておけば
まあ、macrodefとか使ってもいいと思うし、お好きに。
動作させてみよう
さっそくこれを動かしてみると、(わざとエラー出させています)
Buildfile: F:\eclipse-luna\workspace\CompileWithEclipseCompiler\build.xml build : [ delete] Deleting directory F:\eclipse-luna\workspace\CompileWithEclipseCompiler\compiled [ mkdir ] Created dir: F:\eclipse-luna\workspace\CompileWithEclipseCompiler\compiled [ javac ] F:\eclipse-luna\workspace\CompileWithEclipseCompiler\build.xml:6: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds [ javac ] Compiling 1 source file to F:\eclipse-luna\workspace\CompileWithEclipseCompiler\compiled [ javac ] ---------- [ javac ] 1. ERROR in F:\eclipse-luna\workspace\CompileWithEclipseCompiler\src\Hoge.java (at line 5) [ javac ] String missing_semicolon [ javac ] ^^^^^^^^^^^^^^^^^ [ javac ] Syntax error, insert ";" to complete BlockStatements [ javac ] ---------- [ javac ] 1 problem (1 error) BUILD FAILED F:\eclipse-luna\workspace\CompileWithEclipseCompiler\build.xml:6: Compile failed; see the compiler error output for details. Total time: 752 milliseconds
ちょっと出力ログ形式がOracle JDK(下記)と違いますね。
[ javac ] F:\eclipse-luna\workspace\CompileWithEclipseCompiler\src\Hoge.java :5: エラー: ';'がありません [ javac ] String missing_semicolon [ javac ] ^ [ javac ] エラー1個
もちろんコンパイルが成功した場合はclassファイルがcompiledフォルダに出力されます。
独自オプション
コンパイラのオプションは http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_batch_compiler.htm とかにまとまっています。
たとえば-proceedOnError
というのを下記のように指定すれば、(compilearg要素@line属性)
<javac srcdir="src" destdir="compiled" compiler="org.eclipse.jdt.core.JDTCompilerAdapter"> <ecj /> <compilerarg line="-proceedOnError" /> </javac>
エラーがでているクラスでも仮のclassファイルを生成してくれます。
もちろんコンパイルエラーが発生しているメソッドを呼んだりすると Exception in thread "main" java.lang.Error: Unresolved compilation problem:
などと怒られます。