Play!で使っているJDT Compilerを使ってみる。(未完
これは Play! framework Advent Calendar 2011 jp の12月12日のブログです。
※日付変わっているとかは気にしない!
Play!ではJavaのソースコードを内部でコンパイルしていると聞いたので
ちょっと読んで試してみました。
さくっと見たところ、Play! でコンパイル処理している箇所は2カ所ほどあり、
(1)Javaファイルをコンパイルするとき
(2)テンプレートファイルをGroovy
でコンパイルする時でそれぞれ使っているコンパイラとクラスローダが違います。
で(1)で使っているコンパイラですが、これは
play.classloading.ApplicationCompiler
を使っているようです。
これはEclipseのJDTコンパイラを利用してコンパイルしているっぽいです。
Play!内部での使い方はだいたい以下の通りです。
【用意するもの】
・org.eclipse.jdt.internal.compiler.env.ICompilationUnitのImplったやつ
・org.eclipse.jdt.internal.compiler.IErrorHandlingPolicyのImplったやつ
・org.eclipse.jdt.internal.compiler.IProblemFactoryのImplったやつ
・org.eclipse.jdt.internal.compiler.env.INameEnvironmentのImplったやつ
・org.eclipse.jdt.internal.compiler.ICompilerRequestorのImplったやつ
・org.eclipse.jdt.internal.compiler.Compiler.Compiler
【説明】
ICompilationUnit
それぞれのコンパイルする対象です。
ここにファイル名やらパッケージ名やらソースコードが入ります。
IErrorHandlingPolicy
エラーが起こった時のポリシーです。
IProblemFactory
特別なタイプの問題処理や、エラー・メッセージ用に他の言語をサポートする場合に使います。
INameEnvironment
コンパイラーと外部環境と結合します。クラスパスみたいなものらしいです。
コンパイラーはこれを使って、パッケージなのかみたいな情報を判別します。
ICompilerRequestor
コンパイル結果が返ります。
Compiler
こいつでコンパイルします。
【ざくっとした使い方】
- CompilationUnitをコンパイルするクラス分つくります。
- ErrorHandlingPolicyとProblemFactoryを設定します。
- NameEnvironmentとCompilerRequestorを環境に合わせて設定します。
- 2と3で用意したものを使って Compiler を生成します。
- 4で生成したCompilerでCompilationUnitをコンパイルします。
まぁ見たところそんなに難しくなさそう。
で、ソースコードを書いてみましたがここでハマり日付変更線を越えた訳です。
そんな訳でソースコードの一部をのせときます。
※全部読みたい方はこちら
package com.plugram.jdt; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; import org.eclipse.jdt.internal.compiler.IProblemFactory; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; /** * AppCompiler * Java 1.6でコンパイルします。 * * @author tango */ public class AppCompiler { /** * コンパイルします。 * @param classNames */ @SuppressWarnings("deprecation") public void compile(String[] classNames) { ICompilationUnit[] compilationUnits = new CompilationUnit[classNames.length]; for (int i = 0; i < classNames.length; i++) { compilationUnits[i] = new CompilationUnit(classNames[i]); } IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.exitOnFirstError(); IProblemFactory problemFactory = new DefaultProblemFactory(Locale.JAPANESE); INameEnvironment nameEnvironment = new NameEnvironment(); ICompilerRequestor compilerRequestor = new CompilerRequestor(); Compiler jdtCompiler = new Compiler(nameEnvironment, policy, settings(), compilerRequestor, problemFactory) { @Override protected void handleInternalException(Throwable e, CompilationUnitDeclaration ud, CompilationResult result) { } }; jdtCompiler.compile(compilationUnits); } private Map<String, String> settings() { Map<String, String> settings = new HashMap<String, String>(); settings.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); settings.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.GENERATE); settings.put(CompilerOptions.OPTION_SourceFileAttribute, CompilerOptions.GENERATE); settings.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.IGNORE); settings.put(CompilerOptions.OPTION_ReportUnusedImport, CompilerOptions.IGNORE); settings.put(CompilerOptions.OPTION_Encoding, "UTF-8"); settings.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE); settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_6); settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_6); settings.put(CompilerOptions.OPTION_PreserveUnusedLocal, CompilerOptions.PRESERVE); settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_6); return settings; } }
package com.plugram.jdt; /** * Mainクラス * * @author tango */ public class Main { public static final String SOURCE_DIR = "srcディレクトリまでのパス"; public static void main( String[] args ) { new Main().run(); } public void run(){ AppCompiler compiler = new AppCompiler(); String[] classes = new String[]{"com.plugram.sample.Hello"}; compiler.compile(classes); System.out.println("Compiled."); } }
んで、ここまで書いて、以下のコンパイルするソースコードを書いてコンパイルしたら
実行結果が・・・・
package com.plugram.sample; public class Hello { public void say(){ System.out.println("Hello World"); } }
Exception in thread "main" com.plugram.jdt.UnexpectedException: Syntax error, insert "}" to complete ClassBody at com.plugram.jdt.CompilerRequestor.acceptResult(CompilerRequestor.java:22) at org.eclipse.jdt.internal.compiler.Compiler.handleInternalException(Compiler.java:672) at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:516) at com.plugram.jdt.AppCompiler.compile(AppCompiler.java:51) at com.plugram.jdt.Main.run(Main.java:20) at com.plugram.jdt.Main.main(Main.java:14)
うーん、コンパイルエラー。。。なんでだろ。。
コンパイルできるはずなのになー。
という訳で中途半端に終わってしまいました。
そんなわけで明日(今日)は @takayuki_koba さんです!
よろしくお願いします!
※あわせて読みたい
Compiling Java code
http://help.eclipse.org/helios/index.jsp?topic=%2Forg.eclipse.jdt.doc.isv%2Fguide%2Fjdt_api_compile.htm
EclipseのASTParserを試す
http://www.ibm.com/developerworks/opensource/library/os-ast/