From ef869e3cd120fb5f037a802d542417aceca14fbd Mon Sep 17 00:00:00 2001 From: Graeme Rocher Date: Fri, 13 Jun 2014 15:12:34 +0200 Subject: [PATCH] Implement support for the dependencyManagement concept as seen in Maven http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management This allows users to enforce versions of transitive dependencies. Fix for GRAILS-11501 --- .../aether/AetherDependencyManager.groovy | 36 +++++++++-- .../maven/aether/config/AetherDsl.groovy | 61 +++++++++++++++++-- .../config/DependenciesConfiguration.groovy | 41 +++++++------ .../DependencyManagementConfiguration.groovy | 52 ++++++++++++++++ .../aether/config/PluginConfiguration.groovy | 16 ++--- .../config/RepositoriesConfiguration.groovy | 20 +++--- .../maven/AetherDependencyManagerSpec.groovy | 23 +++++++ 7 files changed, 202 insertions(+), 47 deletions(-) create mode 100644 grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/DependencyManagementConfiguration.groovy diff --git a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/AetherDependencyManager.groovy b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/AetherDependencyManager.groovy index 3b6b8da9e19..84030b1a112 100644 --- a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/AetherDependencyManager.groovy +++ b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/AetherDependencyManager.groovy @@ -110,6 +110,7 @@ class AetherDependencyManager implements DependencyManager { protected Dependency jvmAgent protected DependencyReport jvmAgentReport protected List dependencies = [] + protected List managedDependencies = [] protected Set grailsPluginDependencies = [] protected List buildDependencies = [] protected Map> grailsDependenciesByScope = [:].withDefault { [] } @@ -309,7 +310,7 @@ class AetherDependencyManager implements DependencyManager { * * @param callable The DSL definition */ - void parseDependencies(Closure callable) { + void parseDependencies(@DelegatesTo(AetherDsl) Closure callable) { AetherDsl dsl = new AetherDsl(this) dsl.session = session callable.delegate = dsl @@ -515,29 +516,42 @@ class AetherDependencyManager implements DependencyManager { } protected void manageDependencies(CollectRequest collectRequest) { + def alreadyManagedArtefacts = managedDependencies.collect { Dependency d -> d.artifact } + if(managedDependencies) { + collectRequest.setManagedDependencies(managedDependencies) + } if(coreDependencies) { // ensure correct version of Spring is used for (springDep in ['spring-orm', 'spring-core', 'spring-tx', 'spring-context', 'spring-context-support', 'spring-bean', 'spring-web', 'spring-webmvc', 'spring-jms', 'spring-aop', 'spring-jdbc', 'spring-expression', 'spring-jdbc', 'spring-test']) { - collectRequest.addManagedDependency(new Dependency(new DefaultArtifact("org.springframework:${springDep}:${coreDependencies.springVersion}"), null)) + String id = "org.springframework:${springDep}:${coreDependencies.springVersion}" + registerManagedDependency(collectRequest, id, alreadyManagedArtefacts) } // ensure correct version of Groovy is used - collectRequest.addManagedDependency(new Dependency(new DefaultArtifact("org.codehaus.groovy:groovy-all:${coreDependencies.groovyVersion}"), null)) - collectRequest.addManagedDependency(new Dependency(new DefaultArtifact("org.codehaus.groovy:groovy:${coreDependencies.groovyVersion}"), null)) + registerManagedDependency(collectRequest, "org.codehaus.groovy:groovy-all:${coreDependencies.groovyVersion}", alreadyManagedArtefacts) + registerManagedDependency(collectRequest, "org.codehaus.groovy:groovy:${coreDependencies.groovyVersion}", alreadyManagedArtefacts) // ensure the correct versions of Grails jars are used for (grailsDep in ['grails-core', 'grails-bootstrap', 'grails-web', 'grails-async', 'grails-test']) { - collectRequest.addManagedDependency(new Dependency(new DefaultArtifact("org.grails:${grailsDep}:${coreDependencies.grailsVersion}"), null)) + String id = "org.grails:${grailsDep}:${coreDependencies.grailsVersion}" + registerManagedDependency(collectRequest, id,alreadyManagedArtefacts) } for(org.codehaus.groovy.grails.resolve.Dependency d in coreDependencies.compileDependencies) { if(d.group == 'org.grails') { - collectRequest.addManagedDependency(new Dependency(new DefaultArtifact(d.pattern), null)) + registerManagedDependency(collectRequest, d.pattern,alreadyManagedArtefacts) } } } } + private CollectRequest registerManagedDependency(CollectRequest collectRequest, String id, Collection alreadyManagedArtefacts) { + def artifact = new DefaultArtifact(id) + if(!alreadyManagedArtefacts.contains(artifact)) { + collectRequest.addManagedDependency(new Dependency(artifact, null)) + } + } + Proxy addProxy(String proxyHost, String proxyPort, String proxyUser, String proxyPass, String nonProxyHosts) { Proxy proxy if (proxyHost && proxyPort ) { @@ -558,6 +572,16 @@ class AetherDependencyManager implements DependencyManager { return proxy } + /** + * Adds a dependency to be used for dependency management. See http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management + * + * @param dependency The dependency to add + * @param configuration The configuration + */ + void addManagedDependency(Dependency dependency) { + managedDependencies << dependency + } + void addDependency(Dependency dependency, DependencyConfiguration configuration = null) { org.codehaus.groovy.grails.resolve.Dependency grailsDependency = createGrailsDependency(dependency, configuration) grailsDependencies << grailsDependency diff --git a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/AetherDsl.groovy b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/AetherDsl.groovy index ad7cede207e..9396be7a5ef 100644 --- a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/AetherDsl.groovy +++ b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/AetherDsl.groovy @@ -111,6 +111,11 @@ class AetherDsl { void useOrigin(boolean b) { GrailsConsole.getInstance().warn("BuildConfig: Method [useOrigin] not supported by Aether dependency manager") } + /** + * Configures the checksum policy to either fail or ignore + * + * @param enable If enabled fail if checksums are invalid + */ void checksums(boolean enable) { if (enable) { aetherDependencyManager.checksumPolicy = RepositoryPolicy.CHECKSUM_POLICY_FAIL @@ -119,10 +124,21 @@ class AetherDsl { aetherDependencyManager.checksumPolicy = RepositoryPolicy.CHECKSUM_POLICY_IGNORE } } - void checksums(String checksumConfig) { - aetherDependencyManager.checksumPolicy = checksumConfig + /** + * Uses an explicit checksum policy. + * + * @param checksumPolicy The checksum policy + * @see RepositoryPolicy + */ + void checksums(String checksumPolicy) { + aetherDependencyManager.checksumPolicy = checksumPolicy } + /** + * Configures the log level to use for Aether + * + * @param level The level, either "warn", "error", "info", "debug" or "verbose" + */ void log(String level) { switch(level) { case "warn": @@ -138,7 +154,13 @@ class AetherDsl { } } - void inherits(String name, Closure customizer = null) { + /** + * Whether to inherit dependenices from the framework or not + * + * @param name The named dependencies to inherit + * @param customizer The customizer to use if excluding dependencies from the framework + */ + void inherits(String name, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { final callable = aetherDependencyManager.inheritedDependencies[name] if (callable) { @@ -163,7 +185,12 @@ class AetherDsl { } } - void repositories(Closure callable) { + /** + * The repositories to configure + * + * @param callable A closure that defines the repositories + */ + void repositories(@DelegatesTo(RepositoriesConfiguration) Closure callable) { def rc = new RepositoriesConfiguration(aetherDependencyManager, session) callable.delegate = rc callable.call() @@ -171,14 +198,36 @@ class AetherDsl { this.aetherDependencyManager.repositories.addAll(rc.repositories) } - void dependencies(Closure callable) { + /** + * Defines the dependencies of the project + * + * @param callable A closure that delegate to {@link DependenciesConfiguration} + */ + void dependencies(@DelegatesTo(DependenciesConfiguration) Closure callable) { def dc = new DependenciesConfiguration(aetherDependencyManager) dc.exclusionDependencySelector = exclusionDependencySelector callable.delegate = dc callable.call() } - void plugins(Closure callable) { + /** + * Defines the managed dependencies of the project. See http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management + * + * @param callable A closure that delegate to {@link DependenciesConfiguration} + */ + void management(@DelegatesTo(DependencyManagementConfiguration) Closure callable) { + def dc = new DependencyManagementConfiguration(aetherDependencyManager) + dc.exclusionDependencySelector = exclusionDependencySelector + callable.delegate = dc + callable.call() + } + + /** + * Defines the plugin dependencies of the project + * + * @param callable A closure that delegate to {@link PluginConfiguration} + */ + void plugins(@DelegatesTo(PluginConfiguration) Closure callable) { def dc = new PluginConfiguration(aetherDependencyManager) callable.delegate = dc callable.call() diff --git a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/DependenciesConfiguration.groovy b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/DependenciesConfiguration.groovy index c42252d8cf6..f6f1ccaf51c 100644 --- a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/DependenciesConfiguration.groovy +++ b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/DependenciesConfiguration.groovy @@ -50,21 +50,28 @@ class DependenciesConfiguration { this.dependencyManager = dependencyManager } - void addDependency(Dependency dependency, Closure customizer = null) { + void addDependency(Dependency dependency, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { if (exclusionDependencySelector == null || exclusionDependencySelector.selectDependency(dependency)) { final dependencyConfig = customizeDependency(customizer, dependency) - dependency = dependencyConfig.dependency - dependencyManager.addDependency dependencyConfig.dependency, dependencyConfig + addDependencyToManager(dependencyConfig) } } - void addBuildDependency(Dependency dependency, Closure customizer = null) { + protected addDependencyToManager(DependencyConfiguration dependencyConfig) { + dependencyManager.addDependency dependencyConfig.dependency, dependencyConfig + } + + void addBuildDependency(Dependency dependency, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { if (exclusionDependencySelector == null || exclusionDependencySelector.selectDependency(dependency)) { final dependencyConfig = customizeDependency(customizer, dependency) - dependencyManager.addBuildDependency dependencyConfig.dependency, dependencyConfig + addBuildDependencyToManager(dependencyConfig) } } + protected addBuildDependencyToManager(DependencyConfiguration dependencyConfig) { + dependencyManager.addBuildDependency dependencyConfig.dependency, dependencyConfig + } + protected void addDependency(Map properties, String scope, Closure customizer = null) { Dependency d = createDependencyForProperties(properties, scope) addDependency(d, customizer) @@ -110,51 +117,51 @@ class DependenciesConfiguration { dependencyManager.setJvmAgent(new Dependency(new DefaultArtifact(pattern), SCOPE_COMPILE)) } - void build(String pattern, Closure customizer = null) { + void build(String pattern,@DelegatesTo(DependencyConfiguration) Closure customizer = null) { addBuildDependency new Dependency(new DefaultArtifact(pattern), SCOPE_COMPILE), customizer } - void build(Map properties, Closure customizer = null) { + void build(Map properties, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addBuildDependency(properties, SCOPE_COMPILE, customizer) } - void compile(String pattern, Closure customizer = null) { + void compile(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency new Dependency(new DefaultArtifact(pattern), SCOPE_COMPILE), customizer } - void compile(Map properties, Closure customizer = null) { + void compile(Map properties, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency(properties, SCOPE_COMPILE, customizer) } - void runtime(String pattern, Closure customizer = null) { + void runtime(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency new Dependency(new DefaultArtifact(pattern), SCOPE_RUNTIME), customizer } - void runtime(Map properties, Closure customizer = null) { + void runtime(Map properties, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency properties, SCOPE_RUNTIME, customizer } - void provided(String pattern, Closure customizer = null) { + void provided(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency new Dependency(new DefaultArtifact(pattern), SCOPE_PROVIDED), customizer } - void provided(Map properties, Closure customizer = null) { + void provided(Map properties, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency properties, SCOPE_PROVIDED, customizer } - void optional(String pattern, Closure customizer = null) { + void optional(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency new Dependency(new DefaultArtifact(pattern), SCOPE_OPTIONAL), customizer } - void optional(Map properties, Closure customizer = null) { + void optional(Map properties, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency properties, SCOPE_OPTIONAL, customizer } - void test(String pattern, Closure customizer = null) { + void test(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency new Dependency(new DefaultArtifact(pattern), SCOPE_TEST), customizer } - void test(Map properties, Closure customizer = null) { + void test(Map properties, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { addDependency properties, SCOPE_TEST, customizer } diff --git a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/DependencyManagementConfiguration.groovy b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/DependencyManagementConfiguration.groovy new file mode 100644 index 00000000000..fd270f6f8cb --- /dev/null +++ b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/DependencyManagementConfiguration.groovy @@ -0,0 +1,52 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.grails.resolve.maven.aether.config + +import org.codehaus.groovy.grails.resolve.maven.aether.AetherDependencyManager +import org.eclipse.aether.artifact.DefaultArtifact +import org.eclipse.aether.graph.Dependency + +/** + * Allows configuration of dependency management. + * + * @since 2.4.1 + * @author Graeme Rocher + */ +class DependencyManagementConfiguration extends DependenciesConfiguration { + DependencyManagementConfiguration(AetherDependencyManager dependencyManager) { + super(dependencyManager) + } + + @Override + protected addDependencyToManager(DependencyConfiguration dependencyConfig) { + dependencyManager.addManagedDependency(dependencyConfig.dependency) + } + + @Override + protected addBuildDependencyToManager(DependencyConfiguration dependencyConfig) { + dependencyManager.addManagedDependency(dependencyConfig.dependency) + } + + void dependency(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { + addDependency new Dependency(new DefaultArtifact(pattern), null), customizer + } + + void dependency(Map properties, @DelegatesTo(DependencyConfiguration) Closure customizer = null) { + addDependency(properties, null, customizer) + } + + +} diff --git a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/PluginConfiguration.groovy b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/PluginConfiguration.groovy index 57aeebd65c3..27fe195c2cb 100644 --- a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/PluginConfiguration.groovy +++ b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/PluginConfiguration.groovy @@ -32,42 +32,42 @@ class PluginConfiguration extends DependenciesConfiguration { } @Override - void addDependency(Dependency dependency, Closure customizer) { + void addDependency(Dependency dependency, @DelegatesTo(DependencyConfiguration) Closure customizer) { super.addDependency(dependency, customizer) } @Override - void addBuildDependency(Dependency dependency, Closure customizer) { + void addBuildDependency(Dependency dependency, @DelegatesTo(DependencyConfiguration) Closure customizer) { super.addBuildDependency(dependency, customizer) } @Override - void build(String pattern, Closure customizer) { + void build(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer) { super.build(extractDependencyProperties(pattern), customizer) } @Override - void compile(String pattern, Closure customizer) { + void compile(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer) { super.compile(extractDependencyProperties(pattern), customizer) } @Override - void runtime(String pattern, Closure customizer) { + void runtime(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer) { super.runtime(extractDependencyProperties(pattern), customizer) } @Override - void provided(String pattern, Closure customizer) { + void provided(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer) { super.provided(extractDependencyProperties(pattern), customizer) } @Override - void optional(String pattern, Closure customizer) { + void optional(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer) { super.optional(extractDependencyProperties(pattern), customizer) } @Override - void test(String pattern, Closure customizer) { + void test(String pattern, @DelegatesTo(DependencyConfiguration) Closure customizer) { super.test(extractDependencyProperties(pattern), customizer) } diff --git a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/RepositoriesConfiguration.groovy b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/RepositoriesConfiguration.groovy index 395bd4b531d..972de69b0d1 100644 --- a/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/RepositoriesConfiguration.groovy +++ b/grails-aether/src/main/groovy/org/codehaus/groovy/grails/resolve/maven/aether/config/RepositoriesConfiguration.groovy @@ -92,8 +92,8 @@ class RepositoriesConfiguration { aetherDependencyManager.cacheDir = location } - RemoteRepository mavenCentral(Closure configurer = null) { - final existing = repositories.find { ArtifactRepository ar -> ar.id == "mavenCentral" } + RemoteRepository mavenCentral(@DelegatesTo(RepositoryConfiguration) Closure configurer = null) { + final existing = repositories.find { RemoteRepository ar -> ar.id == "mavenCentral" } if (!existing) { final repositoryBuilder = new RemoteRepository.Builder("mavenCentral", "default", "http://repo1.maven.org/maven2/") @@ -107,8 +107,8 @@ class RepositoriesConfiguration { } } - RemoteRepository jcenter(Closure configurer = null) { - final existing = repositories.find { ArtifactRepository ar -> ar.id == "jcenter" } + RemoteRepository jcenter(@DelegatesTo(RepositoryConfiguration) Closure configurer = null) { + final existing = repositories.find { RemoteRepository ar -> ar.id == "jcenter" } if (!existing) { final repositoryBuilder = new RemoteRepository.Builder("jcenter", null, "http://jcenter.bintray.com") @@ -147,8 +147,8 @@ class RepositoriesConfiguration { } } - RemoteRepository grailsCentral(Closure configurer = null) { - final existing = repositories.find { ArtifactRepository ar -> ar.id == "grailsCentral" } + RemoteRepository grailsCentral(@DelegatesTo(RepositoryConfiguration) Closure configurer = null) { + final existing = repositories.find { RemoteRepository ar -> ar.id == "grailsCentral" } if (!existing) { final repositoryBuilder = new RemoteRepository.Builder("grailsCentral", "default", "http://repo.grails.org/grails/plugins") configureRepository(repositoryBuilder, configurer) @@ -162,8 +162,8 @@ class RepositoriesConfiguration { } } - RemoteRepository mavenRepo(String url, Closure configurer = null) { - final existing = repositories.find { ArtifactRepository ar -> ar.id == url } + RemoteRepository mavenRepo(String url, @DelegatesTo(RepositoryConfiguration) Closure configurer = null) { + final existing = repositories.find { RemoteRepository ar -> ar.id == url } if (!existing) { def u = new URL(url) def name = "${u.host.replace(".","_")}${u.path.replace("/", "_")}" @@ -178,12 +178,12 @@ class RepositoriesConfiguration { } } - RemoteRepository mavenRepo(Map properties, Closure configurer = null) { + RemoteRepository mavenRepo(Map properties, @DelegatesTo(RepositoryConfiguration) Closure configurer = null) { final url = properties.url def id = properties.id ?: properties.name ?: url if (id && properties.url) { - final existing = repositories.find { ArtifactRepository ar -> ar.id == url } + final existing = repositories.find { RemoteRepository ar -> ar.id == url } if (!existing) { final repositoryBuilder = new RemoteRepository.Builder(id, "default", url) configureRepository(repositoryBuilder, configurer) diff --git a/grails-aether/src/test/groovy/org/codehaus/groovy/grails/resolve/maven/AetherDependencyManagerSpec.groovy b/grails-aether/src/test/groovy/org/codehaus/groovy/grails/resolve/maven/AetherDependencyManagerSpec.groovy index b58073480d7..fb0f37d143d 100644 --- a/grails-aether/src/test/groovy/org/codehaus/groovy/grails/resolve/maven/AetherDependencyManagerSpec.groovy +++ b/grails-aether/src/test/groovy/org/codehaus/groovy/grails/resolve/maven/AetherDependencyManagerSpec.groovy @@ -36,6 +36,29 @@ import spock.lang.Unroll */ class AetherDependencyManagerSpec extends Specification { + void "Test that dependency management can be applied to dependencies"() { + given:"A dependency manager that applies management to transitive dependencies" + def dependencyManager = new AetherDependencyManager() + dependencyManager.parseDependencies { + repositories { + mavenCentral() + } + management { + dependency "commons-logging:commons-logging:1.1.3" + } + dependencies { + compile 'org.springframework:spring-core:3.2.0.RELEASE' + } + } + + when:"The dependencies are resolved" + def report = dependencyManager.resolve("compile") + + then:"The correct versions of transitives are resolved" + report.jarFiles.any { File f -> f.name.contains('commons-logging-1.1.3')} + + } + @Issue('GRAILS-11055') void "Test that a transitive dependency excluded with the map syntax is actually excluded"() { given:"A dependency with an exclusion"