Build: Effectively remove transitive deps from generated maven poms

With gradle, deploying to maven means first generating poms. These are
filled in based on dependencies of the project. Recently, we started
disallowing transitive dependencies. However, this configuration does
not translate to maven poms because maven has no concept of excluding
all transitive dependencies.

This change adds exclusions for each of the transitive deps of each
dependency being added to the maven pom. It does so by creating dummy
configurations for each direct dependency (which does not have
transitive deps excluded), so that we can iterate the transitive deps
when building the pom.

Note, this should be simpler (just modifying maven's pom model), but
gradle tries to hide that from their api, causing us to need to
manipulate the xml directly.
https://discuss.gradle.org/t/modifying-maven-pom-generation-to-add-excludes/12744
This commit is contained in:
Ryan Ernst 2015-11-13 14:05:50 -08:00
parent a846a257c5
commit 837c593ec2
5 changed files with 89 additions and 25 deletions

View File

@ -20,15 +20,6 @@
import com.bmuschko.gradle.nexus.NexusPlugin
import org.gradle.plugins.ide.eclipse.model.SourceFolder
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.bmuschko:gradle-nexus-plugin:2.3.1'
}
}
// common maven publishing configuration
subprojects {
group = 'org.elasticsearch'
@ -110,8 +101,6 @@ subprojects {
configurations {
all {
resolutionStrategy {
failOnVersionConflict()
dependencySubstitution {
substitute module("org.elasticsearch:rest-api-spec:${version}") with project("${projectsPrefix}:rest-api-spec")
substitute module("org.elasticsearch:elasticsearch:${version}") with project("${projectsPrefix}:core")

View File

@ -53,6 +53,7 @@ dependencies {
compile 'org.eclipse.jgit:org.eclipse.jgit:3.2.0.201312181205-r'
compile 'com.perforce:p4java:2012.3.551082' // THIS IS SUPPOSED TO BE OPTIONAL IN THE FUTURE....
compile 'de.thetaphi:forbiddenapis:2.0'
compile 'com.bmuschko:gradle-nexus-plugin:2.3.1'
}
processResources {

View File

@ -18,14 +18,25 @@
*/
package org.elasticsearch.gradle
import groovy.xml.Namespace
import groovy.xml.QName
import nebula.plugin.extraconfigurations.ProvidedBasePlugin
import org.apache.maven.model.Exclusion
import org.elasticsearch.gradle.precommit.PrecommitTasks
import org.gradle.api.XmlProvider
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.GradleException
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.maven.MavenPom
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.internal.jvm.Jvm
@ -48,6 +59,8 @@ class BuildPlugin implements Plugin<Project> {
project.pluginManager.apply('nebula.info-java')
project.pluginManager.apply('nebula.info-scm')
project.pluginManager.apply('nebula.info-jar')
project.pluginManager.apply('com.bmuschko.nexus')
project.pluginManager.apply(ProvidedBasePlugin)
globalBuildInfo(project)
configureRepositories(project)
@ -103,16 +116,77 @@ class BuildPlugin implements Plugin<Project> {
/** Makes dependencies non-transitive by default */
static void configureConfigurations(Project project) {
// fail on any conflicting dependency versions
project.configurations.all({ Configuration configuration ->
if (configuration.name.startsWith('_transitive_')) {
// don't force transitive configurations to not conflict with themselves, since
// we just have them to find *what* transitive deps exist
return
}
configuration.resolutionStrategy.failOnVersionConflict()
})
// force all dependencies added directly to compile/testCompile to be non-transitive, except for ES itself
project.configurations.compile.dependencies.all { dep ->
Closure disableTransitiveDeps = { ModuleDependency dep ->
if (!(dep instanceof ProjectDependency) && dep.getGroup() != 'org.elasticsearch') {
dep.transitive = false
// also create a configuration just for this dependency version, so that later
// we can determine which dependencies it has (for pom generation excludes)
String depId = "${dep.getGroup()}:${dep.getName()}:${dep.getVersion()}"
String depConfig = "_transitive_${depId}"
if (project.configurations.findByName(depConfig) == null) {
project.configurations.create(depConfig)
project.dependencies.add(depConfig, depId)
}
}
}
project.configurations.testCompile.dependencies.all { dep ->
if (!(dep instanceof ProjectDependency) && dep.getGroup() != 'org.elasticsearch') {
dep.transitive = false
project.configurations.compile.dependencies.all(disableTransitiveDeps)
project.configurations.testCompile.dependencies.all(disableTransitiveDeps)
project.configurations.provided.dependencies.all(disableTransitiveDeps)
// add exclusions to the pom directly, for each of the transitive deps of this project's deps
project.modifyPom { MavenPom pom ->
pom.withXml { XmlProvider xml ->
// first find if we have dependencies at all, and grab the node
NodeList depsNodes = xml.asNode().get('dependencies')
if (depsNodes.isEmpty()) {
return
}
// check each dependency for any transitive deps
for (Node depNode : depsNodes.get(0).children()) {
String groupId = depNode.get('groupId').get(0).text()
String artifactId = depNode.get('artifactId').get(0).text()
String version = depNode.get('version').get(0).text()
// collect the transitive deps now that we know what this dependency is
String depId = "${groupId}:${artifactId}:${version}"
String depConfig = "_transitive_${depId}"
Configuration configuration = project.configurations.findByName(depConfig)
println ("Inspecting dep: ${depId}")
if (configuration == null) {
continue // we did not make this dep non-transitive
}
Set<ResolvedArtifact> artifacts = configuration.resolvedConfiguration.resolvedArtifacts
if (artifacts.size() <= 1) {
// this dep has no transitive deps (or the only artifact is itself)
continue
}
// we now know we have something to exclude, so add the exclusion elements
Node exclusions = depNode.appendNode('exclusions')
for (ResolvedArtifact transitiveArtifact : artifacts) {
ModuleVersionIdentifier transitiveDep = transitiveArtifact.moduleVersion.id
if (transitiveDep.group == groupId && transitiveDep.name == artifactId) {
continue; // don't exclude the dependency itself!
}
Node exclusion = exclusions.appendNode('exclusion')
exclusion.appendNode('groupId', transitiveDep.group)
exclusion.appendNode('artifactId', transitiveDep.name)
}
}
}
}
}
@ -120,6 +194,7 @@ class BuildPlugin implements Plugin<Project> {
/** Adds repositores used by ES dependencies */
static void configureRepositories(Project project) {
RepositoryHandler repos = project.repositories
repos.mavenLocal() // nocommit: remove
repos.mavenCentral()
repos.maven {
name 'sonatype-snapshots'

View File

@ -33,7 +33,6 @@ class PluginBuildPlugin extends BuildPlugin {
@Override
void apply(Project project) {
super.apply(project)
project.pluginManager.apply(ProvidedBasePlugin)
// TODO: add target compatibility (java version) to elasticsearch properties and set for the project
configureDependencies(project)
// this afterEvaluate must happen before the afterEvaluate added by integTest configure,

View File

@ -23,15 +23,15 @@ apply plugin: 'com.bmuschko.nexus'
dependencies {
compile "org.elasticsearch:elasticsearch:${version}"
compile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}"
compile "junit:junit:${versions.junit}"
compile 'org.hamcrest:hamcrest-all:1.3'
compile "org.apache.lucene:lucene-test-framework:${versions.lucene}"
compile "org.apache.lucene:lucene-codecs:${versions.lucene}"
compile "org.apache.httpcomponents:httpclient:${versions.httpclient}"
compile "org.apache.httpcomponents:httpcore:${versions.httpcore}"
compile "commons-logging:commons-logging:${versions.commonslogging}"
compile "commons-codec:commons-codec:${versions.commonscodec}"
compile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}@jar"
compile "junit:junit:${versions.junit}@jar"
compile 'org.hamcrest:hamcrest-all:1.3@jar'
compile "org.apache.lucene:lucene-test-framework:${versions.lucene}@jar"
compile "org.apache.lucene:lucene-codecs:${versions.lucene}@jar"
compile "org.apache.httpcomponents:httpclient:${versions.httpclient}@jar"
compile "org.apache.httpcomponents:httpcore:${versions.httpcore}@jar"
compile "commons-logging:commons-logging:${versions.commonslogging}@jar"
compile "commons-codec:commons-codec:${versions.commonscodec}@jar"
}
compileJava.options.compilerArgs << '-Xlint:-cast,-deprecation,-fallthrough,-overrides,-rawtypes,-serial,-try,-unchecked'