Gradle-duplicates-plugin
From Evgeny Goldin
Contents |
Introduction
Similarly to Maven "duplicates-finder-plugin" this plugin locates duplicate libraries in Gradle configurations. Duplicate libraries have different coordinates (group or name) but contain an identically named classes, i.e., classes with identical fully qualified names.
For example, configurations below contain a number of legal Gradle dependencies. Yet, some of them are duplicates, can you see them right away?:
dependencies { compile 'org.springframework:spring-core:3.0.5.RELEASE', 'org.springframework:spring:2.5.6', 'commons-beanutils:commons-beanutils:1.8.3', 'commons-collections:commons-collections:3.2.1' testCompile 'org.hamcrest:hamcrest-core:1.2', 'junit:junit:4.8.2' }
-
"spring-core"is a subset of"spring" -
"commons-beanutils"repacks some of"commons-collections"classes -
"junit"repacks a portion of the Hamcrest library
One source of possible duplicates are changes done to library coordinates in Maven repositories, like in Spring example above. When libraries evolve, migrate, grow up and split up they may start naming their artifacts differently. And then having an old and new version of the same library in Gradle configuration causes a duplicate problem.
While developers normally don't add duplicate libraries deliberately, this may still happen due to transitive dependencies brought by another project. Situations when outdated versions of Spring or Apache projects "leak" through transitive dependencies are not uncommon, especially when dependencies are not managed tightly. Once those duplicates found, one should normally exclude an older version from configuration.
Another possible source of duplicate classes in the same configuration are repacking portions of one library in another library, as demonstrated by JUnit and Commons BeanUtils. I believe it's a terrible thing to do and have no simple advice for how to overcome situations when eliminating duplicates is absolutely necessary but both libraries are required. One available option is to remove duplicate classes from offending library manually and deploy new artifact under a different name.
Example
Running "gradle duplicates" for this "build.gradle" script:
apply plugin: 'groovy' apply plugin: 'duplicates' buildscript { repositories { mavenRepo url: 'http://evgenyg.artifactoryonline.com/evgenyg/repo/' } dependencies { classpath 'com.github.goldin.plugins:gradle:0.1.3' } } repositories { mavenRepo urls: 'http://evgenyg.artifactoryonline.com/evgenyg/repo/' } duplicates { configurations = [ 'compile', 'testCompile' ] } dependencies { compile 'org.springframework:spring-core:3.0.5.RELEASE', 'org.springframework:spring:2.5.6', 'commons-beanutils:commons-beanutils:1.8.3', 'commons-collections:commons-collections:3.2.1' testCompile 'org.hamcrest:hamcrest-core:1.2', 'junit:junit:4.8.2' }
causes the following build error:
>gradle -q duplicates FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':duplicates'. Cause: Configuration [compile] - duplicates found in: -=-= [org.springframework:spring-core:3.0.5.RELEASE, org.springframework:spring:2.5.6] =-=- -=-= [org.springframework:spring-asm:3.0.5.RELEASE, org.springframework:spring:2.5.6] =-=- -=-= [commons-beanutils:commons-beanutils:1.8.3, commons-collections:commons-collections:3.2.1] =-=- Configuration [testCompile] - duplicates found in: -=-= [org.hamcrest:hamcrest-core:1.2, junit:junit:4.8.2] =-=- -=-= [org.springframework:spring-core:3.0.5.RELEASE, org.springframework:spring:2.5.6] =-=- -=-= [org.springframework:spring-asm:3.0.5.RELEASE, org.springframework:spring:2.5.6] =-=- -=-= [commons-beanutils:commons-beanutils:1.8.3, commons-collections:commons-collections:3.2.1] =-=-
When "verbose" is turned on all duplicate classes are printed:
>gradle -q duplicates FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':duplicates'. Cause: Configuration [compile] - duplicates found in: -=-= [org.springframework:spring-core:3.0.5.RELEASE, org.springframework:spring:2.5.6] =-=- --- [org.springframework.core.AliasRegistry] --- [org.springframework.core.AttributeAccessor] ... -=-= [org.springframework:spring-asm:3.0.5.RELEASE, org.springframework:spring:2.5.6] =-=- --- [org.springframework.asm.AnnotationVisitor] --- [org.springframework.asm.AnnotationWriter] ... -=-= [commons-beanutils:commons-beanutils:1.8.3, commons-collections:commons-collections:3.2.1] =-=- --- [org.apache.commons.collections.ArrayStack] --- [org.apache.commons.collections.Buffer] ... Configuration [testCompile] - duplicates found in: -=-= [org.hamcrest:hamcrest-core:1.2, junit:junit:4.8.2] =-=- --- [org.hamcrest.BaseDescription] --- [org.hamcrest.BaseMatcher] ... -=-= [org.springframework:spring-core:3.0.5.RELEASE, org.springframework:spring:2.5.6] =-=- --- [org.springframework.core.AliasRegistry] --- [org.springframework.core.AttributeAccessor] ... -=-= [org.springframework:spring-asm:3.0.5.RELEASE, org.springframework:spring:2.5.6] =-=- --- [org.springframework.asm.AnnotationVisitor] --- [org.springframework.asm.AnnotationWriter] ... -=-= [commons-beanutils:commons-beanutils:1.8.3, commons-collections:commons-collections:3.2.1] =-=- --- [org.apache.commons.collections.ArrayStack] --- [org.apache.commons.collections.Buffer] ...
Here are two more Gradle scripts using this plugin:
- http://github.com/evgeny-goldin/gcommons/blob/master/build.gradle -
"duplicates"is run manually - http://github.com/evgeny-goldin/teamcity-plugins/blob/master/build.gradle -
"duplicates"runs automatically
Details
| Provided By | ||
|---|---|---|
| Source Code | GitHub | |
| Issue Tracker | YouTrack | |
| Build Server | Gradle TeamCity | |
| Repository, Coordinates | Artifactory |
buildscript { repositories { mavenRepo url: 'http://evgenyg.artifactoryonline.com/evgenyg/repo/' } dependencies { classpath 'com.github.goldin.plugins:gradle:0.1.3' } } |
Settings
| Name | Type | Default value | Description |
|---|---|---|---|
configurations
| List<String>
| null
| List of configurations to test. All configurations are tested if list is null or empty.
|
verbose
| boolean
| false
| Whether all duplicating classes should be printed. |
fail
| boolean
| true
| Whether build should fail when duplicate libraries are found. |
