diff --git a/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/CustomVersionManagementTests.java b/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/CustomVersionManagementTests.java new file mode 100644 index 0000000000..26f8c01bba --- /dev/null +++ b/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/CustomVersionManagementTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-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.springframework.boot.gradle; + +import java.io.IOException; + +import org.gradle.tooling.ProjectConnection; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.boot.dependency.tools.ManagedDependencies; + +/** + * Tests for using the Gradle plugin's support for custom version management + * + * @author Andy Wilkinson + */ +public class CustomVersionManagementTests { + + private static final String BOOT_VERSION = ManagedDependencies.get() + .find("spring-boot").getVersion(); + + private static ProjectConnection project; + + @BeforeClass + public static void createProject() throws IOException { + project = new ProjectCreator().createProject("custom-version-management"); + } + + @Test + public void exclusionsAreStillInPlace() { + project.newBuild().forTasks("checkExclusions") + .withArguments("-PbootVersion=" + BOOT_VERSION).run(); + } + + @Test + public void customSpringVersionIsUsed() { + project.newBuild().forTasks("checkSpringVersion") + .withArguments("-PbootVersion=" + BOOT_VERSION).run(); + } + +} diff --git a/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/ProjectCreator.java b/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/ProjectCreator.java new file mode 100644 index 0000000000..7a34a1be8a --- /dev/null +++ b/spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/ProjectCreator.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-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.springframework.boot.gradle; + +import java.io.File; +import java.io.IOException; + +import org.gradle.tooling.GradleConnector; +import org.gradle.tooling.ProjectConnection; +import org.gradle.tooling.internal.consumer.DefaultGradleConnector; +import org.springframework.util.FileCopyUtils; + +/** + * @author Andy Wilkinson + */ +public class ProjectCreator { + + public ProjectConnection createProject(String name) throws IOException { + File projectDirectory = new File("target/" + name); + projectDirectory.mkdirs(); + + File gradleScript = new File(projectDirectory, "build.gradle"); + FileCopyUtils.copy(new File("src/test/resources/" + name + ".gradle"), + gradleScript); + + GradleConnector gradleConnector = GradleConnector.newConnector(); + ((DefaultGradleConnector) gradleConnector).embedded(true); + return gradleConnector.forProjectDirectory(projectDirectory).connect(); + } +} diff --git a/spring-boot-integration-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java b/spring-boot-integration-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java index f6874e66d3..b2548b5d0b 100644 --- a/spring-boot-integration-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java +++ b/spring-boot-integration-tests/src/test/java/org/springframework/boot/starter/StarterDependenciesIntegrationTests.java @@ -23,9 +23,7 @@ import java.util.Arrays; import java.util.List; import org.gradle.tooling.BuildException; -import org.gradle.tooling.GradleConnector; import org.gradle.tooling.ProjectConnection; -import org.gradle.tooling.internal.consumer.DefaultGradleConnector; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -33,14 +31,14 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.springframework.boot.dependency.tools.ManagedDependencies; -import org.springframework.util.FileCopyUtils; +import org.springframework.boot.gradle.ProjectCreator; import static org.junit.Assert.fail; /** * Tests for the various starter projects to check that they don't pull in unwanted * transitive dependencies when used with Gradle - * + * * @author Andy Wilkinson */ @RunWith(Parameterized.class) @@ -76,15 +74,7 @@ public class StarterDependenciesIntegrationTests { @BeforeClass public static void createProject() throws IOException { - File projectDirectory = new File("target/starter-dependencies"); - projectDirectory.mkdirs(); - - File gradleScript = new File(projectDirectory, "build.gradle"); - FileCopyUtils.copy(new File("src/test/resources/build.gradle"), gradleScript); - - GradleConnector gradleConnector = GradleConnector.newConnector(); - ((DefaultGradleConnector) gradleConnector).embedded(true); - project = gradleConnector.forProjectDirectory(projectDirectory).connect(); + project = new ProjectCreator().createProject("starter-dependencies"); } @BeforeClass diff --git a/spring-boot-integration-tests/src/test/resources/custom-version-management.gradle b/spring-boot-integration-tests/src/test/resources/custom-version-management.gradle new file mode 100644 index 0000000000..d19e2d209b --- /dev/null +++ b/spring-boot-integration-tests/src/test/resources/custom-version-management.gradle @@ -0,0 +1,63 @@ +import org.gradle.api.artifacts.result.UnresolvedDependencyResult; + +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.bootVersion}") + } +} + +repositories { + mavenLocal() + mavenCentral() +} + +configurations { + exclusions + springVersion +} + +apply plugin: 'spring-boot' + +dependencies { + exclusions "org.springframework.boot:spring-boot-starter" + springVersion "org.springframework:spring-core" + versionManagement files('../../src/test/resources/custom-versions.properties') +} + +task checkExclusions { + doFirst { + def commonsLogging = getResolvedDependencies(configurations.exclusions) + .find { it.selected.id.group == 'commons-logging' } + if (commonsLogging) { + throw new GradleException("commong-logging is a transitive dependency") + } + } +} + +task checkSpringVersion { + doFirst { + def springVersions = getResolvedDependencies(configurations.springVersion) + .findAll{it.selected.id.group == 'org.springframework'} + .collect {it.selected.id.version} + if (springVersions.size() != 1 || !springVersions.contains("4.0.3.RELEASE")) { + throw new GradleException("Spring version was not 4.0.3.RELEASE") + } + } +} + +def getResolvedDependencies(def configuration) { + def allDependencies = configuration.incoming + .resolutionResult.allDependencies + .split { it instanceof UnresolvedDependencyResult } + + def unresolved = allDependencies.first() + def resolved = allDependencies.last() + if (unresolved) { + throw new GradleException("Resolution failed: ${unresolved}") + } + resolved +} diff --git a/spring-boot-integration-tests/src/test/resources/custom-versions.properties b/spring-boot-integration-tests/src/test/resources/custom-versions.properties new file mode 100644 index 0000000000..20ac076407 --- /dev/null +++ b/spring-boot-integration-tests/src/test/resources/custom-versions.properties @@ -0,0 +1,2 @@ +org.springframework\:spring-core=4.0.3.RELEASE +org.springframework.boot\:spring-boot-starter=1.1.0.RELEASE \ No newline at end of file diff --git a/spring-boot-integration-tests/src/test/resources/build.gradle b/spring-boot-integration-tests/src/test/resources/starter-dependencies.gradle similarity index 100% rename from spring-boot-integration-tests/src/test/resources/build.gradle rename to spring-boot-integration-tests/src/test/resources/starter-dependencies.gradle diff --git a/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/AbstractDependencies.java b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/AbstractDependencies.java index 0f8be49c18..32693cd0e9 100644 --- a/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/AbstractDependencies.java +++ b/spring-boot-tools/spring-boot-dependency-tools/src/main/java/org/springframework/boot/dependency/tools/AbstractDependencies.java @@ -16,14 +16,19 @@ package org.springframework.boot.dependency.tools; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import org.springframework.boot.dependency.tools.Dependency.Exclusion; + /** * Abstract base implementation for {@link Dependencies}. - * + * * @author Phillip Webb + * @author Andy Wilkinson * @since 1.1.0 */ abstract class AbstractDependencies implements Dependencies { @@ -53,10 +58,24 @@ abstract class AbstractDependencies implements Dependencies { } protected void add(ArtifactAndGroupId artifactAndGroupId, Dependency dependency) { + Dependency existing = this.byArtifactAndGroupId.get(artifactAndGroupId); + if (existing != null) { + dependency = mergeDependencies(existing, dependency); + } this.byArtifactAndGroupId.put(artifactAndGroupId, dependency); this.byArtifactId.put(dependency.getArtifactId(), dependency); } + private Dependency mergeDependencies(Dependency existingDependency, + Dependency newDependency) { + List combinedExclusions = new ArrayList(); + combinedExclusions.addAll(existingDependency.getExclusions()); + combinedExclusions.addAll(newDependency.getExclusions()); + + return new Dependency(newDependency.getGroupId(), newDependency.getArtifactId(), + newDependency.getVersion(), combinedExclusions); + } + /** * Simple holder for an artifact+group ID. */ diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/exclude/ApplyExcludeRules.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/exclude/ApplyExcludeRules.java index e1dcea1e98..8b493c6e59 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/exclude/ApplyExcludeRules.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/exclude/ApplyExcludeRules.java @@ -21,6 +21,7 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.ResolvableDependencies; import org.gradle.api.internal.artifacts.DefaultExcludeRule; import org.gradle.api.logging.Logger; import org.springframework.boot.dependency.tools.Dependency.Exclusion; @@ -45,12 +46,19 @@ public class ApplyExcludeRules implements Action { @Override public void execute(Configuration configuration) { - configuration.getDependencies().all(new Action() { - @Override - public void execute(Dependency dependency) { - applyExcludeRules(dependency); - } - }); + if (!VersionManagedDependencies.CONFIGURATION.equals(configuration.getName())) { + configuration.getIncoming().beforeResolve(new Action() { + @Override + public void execute(ResolvableDependencies resolvableDependencies) { + resolvableDependencies.getDependencies().all(new Action() { + @Override + public void execute(Dependency dependency) { + applyExcludeRules(dependency); + } + }); + } + }); + } } private void applyExcludeRules(Dependency dependency) {