[gradle] Java6 Annotation Processing

  • QueryDsl(JPA)を例に GradleでAnnotation Processingを実行させてみる
  • antの例があるので、ほぼこのままだったりするけど。。

annotation processingとは

dependenciesの設定

  • Annotation Processorを含むjarに対する依存関係を設定する。QueryDslは本体機能へのAPI(querydsl-jpa.jar など)とは別に、Annotation処理のjarが切り出されている(querydsl-apt.jar)。querydsl-apt.jarは、実行時には不要であるため、独自にdependency condfigurationを設定した。(下記processor)

 configurations {
      processor {
          description: "classpath for annotation processors"
      }
  }
  • dependenciesクロージャで、processorquerydsl-apt.jar を指定する
 dependencies {
      compile "org.eclipse.persistence:javax.persistence:2.0.0"
      compile "org.eclipse.persistence:eclipselink:2.4.0"
      compile "com.mysema.querydsl:querydsl-jpa:2.7.2"
      compile "org.slf4j:slf4j-log4j12:1.6.6"

      processor "com.mysema.querydsl:querydsl-apt:2.7.2"
  }

antのjavac呼出タスク定義

  • Annotation Processingの出力先は、build/processorGen/srcとした。今回は拡張プロパティext.destdirに設定しておく。

task execProcessor {
      ext.destdir = new File(buildDir, "processorGen/src")
      // ...
  }
  • buildディレクトリ配下としたのは、cleanタスクで消してくれるため。例えばモデルクラスの名前を変更すると旧名のメタクラスが残ったままになってしまうなど、cleanできたほうが都合がよい。

  • もちろん毎回消して再生成もよいのだけど、今回はUP-TO-DATEを仕掛けてビルドを効率化したかった。
  • UP-TO-DATEは下記のように inputsとoutputsにそれぞれファイルまたはディレクトリを設定することで、変更の有無(つまりAnnotation Processingタスクの実行が必要かどうか)をGradleが判定してくれるようになる。

 task execProcessor {
      ext.destdir = new File(buildDir, "processorGen/src")
      inputs.source(sourceSets.main.java)
      outputs.dir(ext.destdir)
      doLast{ /* ここにjavacを呼び出す処理 */ }
  }
  • doLastクロージャで、このタスクの実行処理を記述する。この例ではantjavacを呼び出してみる。

doLast {
      ext.destdir.mkdirs() // 出力先が事前に存在しないとエラーになるのを回避

      def primaryOptions = [
          includeAntRuntime: false,
          destdir: ext.destdir,
          classpath: configurations.compile.plus(configurations.processor).asPath,
      ]

      ant.javac(primaryOptions) {
         sourceSets.main.java.addToAntBuilder((Object)ant, 'src', FileCollection.AntType.MatchingTask)
         compilerarg(value: "-proc:only") 
         compilerarg(value: "-implicit:none")
         compilerarg(value: "-processor")
         compilerarg(value: "com.mysema.query.apt.jpa.JPAAnnotationProcessor")
      }
  }
  • 上記のant.javacはおおよそ下記のようなant記述と同じ意味

    <javac includeAntRuntime=false
           destdir="${ext.destdir}"
           classpathref="${compiler.plus(processor).asPath}">
      <src ...ここはaddToAntBuilder()がゴニョゴニョする  />
      <compilerarg value="-Xlint"/>
      <compilerarg value="-proc:only"/>
      <compilerarg value="-implicit:none/>
      <compilerarg value="-processor/>
      <compilerarg value="com.mysema.query.apt.jpa.JPAAnnotationProcessor/>
    </javac>

compileJavaの設定

  • これで execProcessorを実行すればQueryDslによってメタクラスがbuild/processorGen/srcに生成される。だがこのソースが compileの対象になっていないのでこのままではコンパイルされない。
  • ここで src/main/javaディレクトリにコピーするという手もあるが、UP-TO-DATE判定を考えると好ましくない。また、Checkstyleといった静的解析ツールが処理する対象ディレクトリから自動生成されたコードを分離したいということもあるかもしれない
  • ということで、compileJavaの対象ソースディレクトリとして execProcessorの出力先ディレクトリを追加する。
  • また、compileJavaの依存タスクとして execProcessorを設定する。

compileJava {
      dependsOn execProcessor
      source execProcessor.ext.destdir
 }

これでcompileJavaを実行するとexecProcessorが先に実行される。

$ gradle compileJava
:execProcessor
[ant:javac] 注意:Running JPAAnnotationProcessor
[ant:javac] 注意:Serializing Entity types
[ant:javac] 注意:Generating .... for [...]
[ant:javac] 注意:Running JPAAnnotationProcessor
[ant:javac] 注意:Running JPAAnnotationProcessor
:compileJava

BUILD SUCCESSFUL

また、変更がなければ UP-TO-DATEと判定され、実行されない。

$ gradle compileJava
:execProcessor UP-TO-DATE
:compileJava UP-TO-DATE

BUILD SUCCESSFUL
広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中