アプリが依存するライブラリと com.android.databinding:compiler
の依存先ライブラリがかぶっていると ProGuard が警告を吐いてビルドできなくなることがある。 多くのプロジェクトではこの問題は起こらないと思う。
最終的にどうするのがいいかまではわかってないし、詳しいこともわかっておらず原因について推測してる箇所が多いけど、メモ程度に書き残しておく。
発生する問題
例えば、compile
configuration の依存に com.google.guava:guava:19.0
が含まれていると、ProGuard の処理で以下のような警告が出て、:app:transformClassesAndResourcesWithProguardForRelease
タスクが失敗する。 (Android Plugin for Gradle 2.0.0 を使って、Data Binding 有効で、ProGuard 有効な場合。)
Warning: library class android.databinding.tool.util.SourceCodeEscapers$1 extends or implements program class com.google.common.escape.CharEscaper
Warning: library class android.databinding.tool.util.SourceCodeEscapers$JavaCharEscaper extends or implements program class com.google.common.escape.ArrayBasedCharEscaper
Warning: library class android.databinding.tool.util.SourceCodeEscapers$JavaCharEscaperWithOctal extends or implements program class com.google.common.escape.ArrayBasedCharEscaper
下記でも同様の問題が書かれている。
原因に関する前提
Data Binding ライブラリと provided
configuration への依存関係の追加
Data Binding ライブラリを有効にしている (build.gradle に android { dataBinding { enabled = true } }
を記述) と、provided
configuration に com.android.databinding:compiler
の依存が追加される。 これは、ビルド時にのみ使われるツールで、レイアウトファイルを基に YourLayoutBinding
クラスみたいなやつを生成するなどの処理に使われるっぽい。 (詳細は追いかけてない。)
Android Plugin for Gradle 1.5.0 のころは、以下のような依存関係になっていた。
$ ./gradlew :app:dependencies
(略)
provided - Classpath for only compiling the main sources.
\--- com.android.databinding:compiler:1.0-rc5
+--- com.android.databinding:baseLibrary:1.0-rc5
\--- com.googlecode.juniversalchardet:juniversalchardet:1.0.3
(略)
Android Plugin for Gradle 2.0.0 では、com.android.databinding:compiler
のバージョンも更新されており、以下のようになる。
$ ./gradlew :app:dependencies
(略)
provided - Classpath for only compiling the main sources.
\--- com.android.databinding:compiler:2.0.0
+--- com.android.databinding:compilerCommon:2.0.0
| +--- com.android.databinding:baseLibrary:2.0.0
| +--- com.tunnelvisionlabs:antlr4:4.5
| | +--- com.tunnelvisionlabs:antlr4-runtime:4.5
| | | +--- org.abego.treelayout:org.abego.treelayout.core:1.0.1
| | | \--- com.tunnelvisionlabs:antlr4-annotations:4.5
| | +--- com.tunnelvisionlabs:antlr4-annotations:4.5
| | +--- org.antlr:antlr-runtime:3.5.2
| | \--- org.antlr:ST4:4.0.8
| | \--- org.antlr:antlr-runtime:3.5.2
| +--- commons-io:commons-io:2.4
| +--- com.googlecode.juniversalchardet:juniversalchardet:1.0.3
| \--- com.google.guava:guava:17.0
+--- com.android.databinding:baseLibrary:2.0.0
+--- org.jetbrains.kotlin:kotlin-stdlib:1.0.0
| \--- org.jetbrains.kotlin:kotlin-runtime:1.0.0
+--- commons-io:commons-io:2.4
+--- commons-codec:commons-codec:1.10
+--- com.tunnelvisionlabs:antlr4:4.5 (*)
\--- com.googlecode.juniversalchardet:juniversalchardet:1.0.3
(略)
ProGuard と provided
configuration
ProGuard が入力として受け付ける JAR (や WAR、AAR など) ファイルには、input jar と library jar の 2 種類がある。
input jar は、アプリケーションに含まれるクラスファイルを含むもので、これらが処理されて出力される。
library jar の方は出力には含まれない。 「input jar の中のクラスがが依存するが、出力には含めない」というようなものを library jar として指定するようになっている。
さて、Android Plugin for Gradle には ProGuard の処理を行うタスクが定義されるが、その ProGuard の設定としては provided
configuration に含められている依存関係を ProGuard の library jar として渡すようになっているっぽい。
ProGuardTransform
クラス のコードをちょっと見たけど、追いかけられてない。
原因
上に書いたように、provided
configuration に含まれる com.android.databinding:compiler:2.0.0
は com.google.guava:guava:17.0
に依存するが、多分 compile
configuration の com.google.guava:guava
の方が新しくて、そっちを使うという風に ProGuard がみなすのだと思う。 そうすると、library jar 側の android.databinding.tool.util.SourceCodeEscapers
が input jar 側の Guava に依存することになって、『library class ... depends on program class ...』 の警告が発生するのだと思う。
対応
とりあえずの対応
com.android.databinding:compiler:2.0.0
はビルド時のみに使われるものなので、単に警告を無視してやれば良さそう。 うちのプロジェクトでは以下のような記述を proguard-rules.pro に追加した。
# Data Binding library
# ====================
# * Data Binding ライブラリを有効にすると、ビルド時に使われる
# (`com.android.databinding:compiler:2.0.0`) が `provided` configuration に追加される。
# * さらに `provided` configuration の依存が ProGuard の library jars に追加されるっぽい。
# * `com.android.databinding:compiler:2.0.0` は `com.google.guava:guava:17.0` などに依存して
# いるが、それらのライブラリのより新しいバージョンが `compile` configuration にも含まれて
# いると、ProGuard の処理時に 「library class ... depends on program class ...」 という
# ような警告が発生してビルドが止まってしまうっぽい。
-dontwarn android.databinding.tool.util.**
根本的には?
根本的には、以下のどちらかが必要そう?
- Data Binding ライブラリのビルド時用のツールを
provided
configuration の依存に含めない。 -
provided
configuration の依存を ProGuard の library jar に含めない。
provided
configuration がどういう目的のものかによって上記のどちらがいいかが違ってくると思うけど、本来の provided
configuration というのは 「実行環境により提供されるのでプログラム側には含めなくていいけどコンパイル時に参照する必要があるもの」 を含めるところだと思うので、それだとすると前者の対応ができるといいのかなぁと思う。