Book Review: Heads You Win

Dynamic versions in build.gradle and consequence

"Raghu, I am getting this error from this morning", says our android developer, on a Thursday morning.

Execution failed for task ':app:transformClassesWithJarMergingForDebug'.

> duplicate entry: org/apache/commons/io/CopyUtils.class

"It was working yesterday", added the developer.

"Hmm! what did you change since yesterday?  Did you update Android Studio?  Or build.gradle?"

"Neither", said he.

"I also did a clean build".

"Let me try on my system", said me.  I updated the project and opened Android Studio.  I got the exact same error in my console.

"Are you sure it was working yesterday?", asked I.

"Yes, I checked in the source code.  It also built in jenkins", he said referring to our continuous integration tool, running on a linux server.

"Hmm!  is it possible that it works on linux but not Mac?", wondered I though it didn't make any logical sense.

"Let me check if command-line build works - it could be an Android Studio thingy", I said.  I did that. It didn't make any difference.

We tried to build the software on a linux system, but got the same error.

"Why don't we find out what is causing the duplicate entry for CopyUtils?",  I said.   So we ran

./gradlew -q dependencies app:dependencies

and searched for occurrences of commons-io (based on the package name of the erring class).  The only match was

Well!  the problem was evident here.  Two different commons-io libraries were being used.   But why were we seeing this error today and not previously?

The answer lies in the way we had defined the dependency version - we had used "2.+" to define the version of aws-android-sdk-s3 project.  2.4 version of the dependency had released Wednesday night, which contained the above issue.   Gradle build had updated to this version of dependency when run on Thursday morning.

By explicitly specifying the previous version number (2.3.9) of the dependency, the application could be built and run with no errors.  There is already a bug logged for this issue (with a suggested workaround).

Moral of the story:  Specify dynamic versions ("+") in dependencies at your own peril.

I also liked another blog post on this.