Posted at

Gradle 5.0 に備えて annotationProcessor について調べる


非推奨の警告

最近の Gradle (自分の環境は、 4.10.2)で注釈処理を利用していると、次のようなメッセージが出る。

> gradle compileJava

...

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/4.10.2/userguide/command_line_interface.html#sec:command_line_warnings

Gradle 5.0 で非互換になる機能を利用しているので、詳細を確認したければ --warning-mode all オプションを指定しろとのこと。

オプションを指定して実行すると、次のように出力される。

> gradle compileJava --warning-mode all

...

Detecting annotation processors on the compile classpath has been deprecated.
Gradle 5.0 will ignore annotation processors on the compile classpath.
The following annotation processors were detected on the compile classpath:
'combined.apt.CombinedProcessor' and
'separated.apt.SeparatedProcessor'.
Please add them to the annotation processor path instead.
If you did not intend to use annotation processors, you can use the '-proc:none' compiler argument to ignore them.

なんでも、 Gradle 5.0 からはアノテーションプロセッサをコンパイル時のクラスパスから取得する方法がサポートされなくなるらしい。

試しに 5.0 の RC 版 で実行したら、確かに無視されるようになっていた。

代わりに、 annotation processor path に指定しろとのこと。

もしくは、別に注釈処理を使っていないプロジェクトであれば、 -proc:none をコンパイルオプションに追加しろとのこと。

Gradle 5.0: Planned behaviour of ignoring jars with annotation processors not okay. · Issue #5056 · gradle/gradle

こちらの Issue に書き込まれているコメントによると、インクリメンタルな注釈処理(incremental annotation processors)をサポートするのが理由らしい(よくわかってない)。


対応方法

Convenient declaration of annotation processor dependencies | Gradle 4.6 Release Notes

Gradle 4.6 の時点で Java プラグインを入れると annotationProcessor という configuration が設定できるようになっている。

Dependency configurations | The Java Plugin

注釈処理を提供する依存ライブラリは、 implementation ではなく annotationProcessor に指定すれば良い。

ちなみに、 annotationProcessor は main のコード用の configuration なので、 test のコードに対して注釈処理を適用したい場合は testAnnotationProcessor に指定する。

Android の annotationProcessor

ググってると、Android Plugin for Gradle 3.0.0 へのマイグレーションの話 でも同じように annotationProcessor が出てくるけど、これはまた別物?(Android 事情に疎い)

少なくとも、 Gradle の JavaPlugin に annotationProcessor が入ったのは 2017年12月15日のコミットっぽい。Android の方は 3.0.0 のリリースが 2017年10月っぽいので、 Android の方が先?


注釈処理の提供方法によって対応方法が変わる(と思う)

実際にいろいろ試してみて気づいたが、単に注釈処理といっても、その提供方法などによって、ちょっと指定方法が変わってくると思う(個人的な試行錯誤の結果たどり着いた結論なので、正しさの保証はなし)。

分類すると、次のような感じで分かれる気がする。


  • ライブラリ本体と注釈処理が別々の jar で提供されている

  • ライブラリ本体と注釈処理が同じ jar で提供されている


検証用の実装


フォルダ構成

|-build.gradle

|-settings.gradle
|-combined/
| `-src/main/
| |-java/
| | `-combined/
| | |-apt/
| | | `-CombinedProcessor.java
| | `-core/
| | `-Combined.java
| `-resources/
| `-META-INF/services/
| `-javax.annotation.processing.Processor
|
|-separated/
| |-apt/
| | `src/main/
| | |-java/
| | | `-separated/apt/
| | | `-SeparatedProcessor.java
| | `-resources/
| | `-META-INF/services/
| | `-javax.annotation.processing.Processor
| `-core/
| `-src/main/java/
| `-separated/core/
| `-Separated.java
|
`-main/
`-src/
|-main/java/
| `-main/
| `-Foo.java
`-test/java/
`-main/
`-FooTest.java


  • 3つのマルチプロジェクトから成る



    • combined


      • 本体と注釈処理を1つの jar で提供するプロジェクト




    • separated


      • 本体と注釈処理を異なる jar で提供するプロジェクト




    • main


      • それぞれの注釈処理を利用しているプロジェクト






  • CombinedProcessor.javaSeparatedProcessor.java が、それぞれ注釈処理を実装したクラス


  • Combined.javaSeparated.java が、それぞれ処理対象となるアノテーション


  • Foo.java は、両方のアノテーションを設定して処理対象になるようにしている


Foo.java

package main;

import combined.core.Combined;
import separated.core.Separated;

@Combined
@Separated
public class Foo {}




  • build.gradle は次のような感じ


build.gradle

subprojects {

apply plugin: "java"
sourceCompatibility = 11
targetCompatibility = 11
compileJava.options.encoding = "UTF-8"
}

project(":separated:apt") {
dependencies {
implementation project(":separated:core")
}
}

project(":main") {
dependencies {
... // ここの設定は後述
}
}



ライブラリ本体と注釈処理が別々の jar で提供されている

この場合は、 implementation 1 に本体の jar を、 annotationProcessor に注釈処理用の jar を指定すればいい。


build.gradle

project(":main") {

dependencies {
implementation project(":separated:core")
annotationProcessor project(":separated:apt")
}
}


検証

> gradle :main:build --warning-mode all

...

> Task :main:compileJava
注意:SeparatedProcessor!!
注意:SeparatedProcessor!!

BUILD SUCCESSFUL in 4s


警告なく完了する。

CombinedProcessor の方は一時的に依存や実装を外している

このパターンに該当するライブラリとしては、公式ドキュメントで紹介されている Dagger などが該当する。


ライブラリ本体と注釈処理が同じ jar で提供されている

この場合は、 implementationannotationProcessor の両方に同じ依存対象を指定する。


build.gradle

project(":main") {

dependencies {
implementation project(":combined")
annotationProcessor project(":combined")

...
}
}



実行結果

> gradle :main:build --warning-mode all

...

> Task :main:compileJava
注意:CombinedProcessor!!
注意:SeparatedProcessor!!
注意:CombinedProcessor!!
注意:SeparatedProcessor!!

> Task :main:compileTestJava
Detecting annotation processors on the compile classpath has been deprecated. Gradle 5.0 will ignore annotation processors on the compile cl
asspath. The following annotation processors were detected on the compile classpath: 'combined.apt.CombinedProcessor'. Please add them to t
he annotation processor path instead. If you did not intend to use annotation processors, you can use the '-proc:none' compiler argument to
ignore them.

BUILD SUCCESSFUL in 4s


警告が出た。

よく見ると、 compileTestJava のタスク内で警告が出力されている(compileJava の方は警告なしで完了している)。

Java プラグインのドキュメント に、各 configuration の依存関係が図解されている。

今回関係する部分だけを抽出すると、次のようになっている(赤いのは非推奨の configuration)。

annotationProcessor.jpg

implementation に指定した依存関係は、そのまま testCompileClasspath でも利用される。

従って、テストのときにクラスパスから注釈処理が検出されて警告が出てしまっているっぽい。

どうすべきか?


コンパイル時だけ必要な場合

jar が必要なのがコンパイル時のみであれば、 implementation の代わりに compileOnly を使うことで警告表示を回避できる(というか、その方が設定として正しい気がする)。

例えば、 Lombok がこれに該当する。


build.gradle

dependencies {

compileOnly project(":combined")
annotationProcessor project(":combined")

...
}



実行結果

> gradle :main:build --warning-mode all

...

> Task :main:compileJava
注意:CombinedProcessor!!
注意:SeparatedProcessor!!
注意:CombinedProcessor!!
注意:SeparatedProcessor!!

BUILD SUCCESSFUL in 5s


警告は出なくなった。


無視する

テストコードで注釈処理を利用していないのであれば、警告は無視するのも選択肢としてありかなとも思う。

結局、 5.0 からは無視されるのであれば、 5.0 に上げた時点で警告も出なくなり、動きとしても問題ない状態に落ち着きそうな気がする。

一応 5.0 の RC 版で試したら、警告はなくなり main の方の注釈処理だけが動いてくれた。


5.0RCで動かした結果

> gradle --version

------------------------------------------------------------
Gradle 5.0-rc-1
------------------------------------------------------------

...

> gradle :main:build
...

> Task :main:compileJava
注意:CombinedProcessor!!
注意:SeparatedProcessor!!
注意:CombinedProcessor!!
注意:SeparatedProcessor!!

BUILD SUCCESSFUL in 4s



実行時も必要な場合

実行時も依存対象が必要な場合は、 compileOnly の指定は利用できない。

例えば、 Doma2 がこれに該当する。

こうなると、テストコードで出る警告は無視するのが一番良い気もする。

しかし、どうしても無視できないのであれば、最初の警告メッセージで出ていたコンパイルオプションにアノテーションプロセッサを無効にする指定を追加する方法を取ることになると思う。


build.gradle

dependencies {

implementation project(":combined")
annotationProcessor project(":combined")

...
}

compileTestJava {
// テスト時のコンパイルオプションに `-proc:none` を追加
options.compilerArgs += ["-proc:none"]
}



実行結果

> gradle :main:build --warning-mode all

...

> Task :main:compileJava
注意:CombinedProcessor!!
注意:SeparatedProcessor!!
注意:CombinedProcessor!!
注意:SeparatedProcessor!!

BUILD SUCCESSFUL in 5s


警告は出ない。





  1. コンパイル時のみ必要なライブラリの場合は compileOnly に設定する