From 45e6c12cb6af9a0159b52c5cd1c1be4c77c0b2d1 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 10 Feb 2021 10:18:30 +0000 Subject: [PATCH] Consider transitives when identifying project dependencies Previously, when building a layered jar, the Gradle plugin only considered a configuration's direct dependencies when identifying project dependencies. This resulted in transitive project dependencies being missed when deciding which dependencies belong in the application layer. This commit updates ResolvedDependencies to consider all projects from the root project when collecting the IDs of local projects. This ensures that any project dependency, no matter where it appears in the dependency graph, is successfully identified. Fixes gh-25163 --- .../boot/gradle/tasks/bundling/BootJar.java | 11 ++++++----- .../tasks/bundling/ResolvedDependencies.java | 16 +++++++++------- .../tasks/bundling/BootJarIntegrationTests.java | 8 ++++++-- .../boot/gradle/tasks/bundling/BootJarTests.java | 2 +- ...tegrationTests-multiModuleCustomLayers.gradle | 5 +++++ ...grationTests-multiModuleImplicitLayers.gradle | 5 +++++ 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java index a853e245bab..665cef7d848 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -74,15 +74,16 @@ public class BootJar extends Jar implements BootArchive { */ public BootJar() { this.support = new BootArchiveSupport(LAUNCHER, new LibrarySpec(), new ZipCompressionResolver()); - this.bootInfSpec = getProject().copySpec().into("BOOT-INF"); - this.mainClass = getProject().getObjects().property(String.class); + Project project = getProject(); + this.bootInfSpec = project.copySpec().into("BOOT-INF"); + this.mainClass = project.getObjects().property(String.class); configureBootInfSpec(this.bootInfSpec); getMainSpec().with(this.bootInfSpec); - getProject().getConfigurations().all((configuration) -> { + project.getConfigurations().all((configuration) -> { ResolvableDependencies incoming = configuration.getIncoming(); incoming.afterResolve((resolvableDependencies) -> { if (resolvableDependencies == incoming) { - this.resolvedDependencies.processConfiguration(configuration); + this.resolvedDependencies.processConfiguration(project, configuration); } }); }); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ResolvedDependencies.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ResolvedDependencies.java index 62548749b92..fc7bdb89dba 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ResolvedDependencies.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/ResolvedDependencies.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -22,9 +22,9 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ModuleVersionIdentifier; -import org.gradle.api.artifacts.ProjectDependency; import org.gradle.api.artifacts.ResolvedArtifact; import org.gradle.api.artifacts.ResolvedConfiguration; @@ -44,13 +44,15 @@ class ResolvedDependencies { private final Map configurationDependencies = new LinkedHashMap<>(); - void processConfiguration(Configuration configuration) { - Set projectDependencyIds = configuration.getAllDependencies().withType(ProjectDependency.class).stream() - .map((projectDependency) -> projectDependency.getGroup() + ":" + projectDependency.getName() + ":" - + projectDependency.getVersion()) + private String projectId(Project project) { + return project.getGroup() + ":" + project.getName() + ":" + project.getVersion(); + } + + void processConfiguration(Project project, Configuration configuration) { + Set localProjectIds = project.getRootProject().getAllprojects().stream().map(this::projectId) .collect(Collectors.toSet()); this.configurationDependencies.put(configuration, - new ResolvedConfigurationDependencies(projectDependencyIds, configuration.getResolvedConfiguration())); + new ResolvedConfigurationDependencies(localProjectIds, configuration.getResolvedConfiguration())); } DependencyDescriptor find(File file) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java index 70ce20fdc8b..a8ddd80abe1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java @@ -149,6 +149,7 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests { assertThat(jarFile.getEntry(layerToolsJar)).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/alpha-1.2.3.jar")).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/bravo-1.2.3.jar")).isNotNull(); + assertThat(jarFile.getEntry("BOOT-INF/lib/charlie-1.2.3.jar")).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/commons-lang3-3.9.jar")).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/spring-core-5.2.5.RELEASE.jar")).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/spring-jcl-5.2.5.RELEASE.jar")).isNotNull(); @@ -171,7 +172,8 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests { assertThat(indexedLayers.get("spring-boot-loader")).containsExactly("org/"); assertThat(indexedLayers.get("snapshot-dependencies")).containsExactlyElementsOf(expectedSnapshotDependencies); assertThat(indexedLayers.get("application")).containsExactly("BOOT-INF/classes/", "BOOT-INF/classpath.idx", - "BOOT-INF/layers.idx", "BOOT-INF/lib/alpha-1.2.3.jar", "BOOT-INF/lib/bravo-1.2.3.jar", "META-INF/"); + "BOOT-INF/layers.idx", "BOOT-INF/lib/alpha-1.2.3.jar", "BOOT-INF/lib/bravo-1.2.3.jar", + "BOOT-INF/lib/charlie-1.2.3.jar", "META-INF/"); BuildResult listLayers = this.gradleBuild.build("listLayers"); assertThat(listLayers.task(":listLayers").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); String listLayersOutput = listLayers.getOutput(); @@ -244,6 +246,7 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests { assertThat(jarFile.getEntry(layerToolsJar)).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/alpha-1.2.3.jar")).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/bravo-1.2.3.jar")).isNotNull(); + assertThat(jarFile.getEntry("BOOT-INF/lib/charlie-1.2.3.jar")).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/commons-lang3-3.9.jar")).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/spring-core-5.2.5.RELEASE.jar")).isNotNull(); assertThat(jarFile.getEntry("BOOT-INF/lib/spring-jcl-5.2.5.RELEASE.jar")).isNotNull(); @@ -259,6 +262,7 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests { Set expectedSubprojectDependencies = new TreeSet<>(); expectedSubprojectDependencies.add("BOOT-INF/lib/alpha-1.2.3.jar"); expectedSubprojectDependencies.add("BOOT-INF/lib/bravo-1.2.3.jar"); + expectedSubprojectDependencies.add("BOOT-INF/lib/charlie-1.2.3.jar"); Set expectedDependencies = new TreeSet<>(); expectedDependencies.add("BOOT-INF/lib/spring-core-5.2.5.RELEASE.jar"); expectedDependencies.add("BOOT-INF/lib/spring-jcl-5.2.5.RELEASE.jar"); @@ -348,7 +352,7 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests { private void writeSettingsGradle() { try (PrintWriter writer = new PrintWriter( new FileWriter(new File(this.gradleBuild.getProjectDir(), "settings.gradle")))) { - writer.println("include 'alpha', 'bravo'"); + writer.println("include 'alpha', 'bravo', 'charlie'"); } catch (IOException ex) { throw new RuntimeException(ex); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java index 795c16e3e60..933282e2f89 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java @@ -323,7 +323,7 @@ class BootJarTests extends AbstractBootArchiveTests { return null; }).given(resolvableDependencies).afterResolve(any(Action.class)); given(configuration.getIncoming()).willReturn(resolvableDependencies); - bootJar.getResolvedDependencies().processConfiguration(configuration); + bootJar.getResolvedDependencies().processConfiguration(bootJar.getProject(), configuration); } private ResolvedArtifact mockLibraryArtifact(String fileName, String group, String module, String version) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-multiModuleCustomLayers.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-multiModuleCustomLayers.gradle index 29cc5a3687a..f8127b2f3db 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-multiModuleCustomLayers.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-multiModuleCustomLayers.gradle @@ -7,6 +7,11 @@ subprojects { apply plugin: 'java' group = 'org.example.projects' version = '1.2.3' + if (it.name == 'bravo') { + dependencies { + implementation(project(':charlie')) + } + } } bootJar { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-multiModuleImplicitLayers.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-multiModuleImplicitLayers.gradle index bd78e7ce97d..ba34cf4d1d6 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-multiModuleImplicitLayers.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests-multiModuleImplicitLayers.gradle @@ -7,6 +7,11 @@ subprojects { apply plugin: 'java' group = 'org.example.projects' version = '1.2.3' + if (it.name == 'bravo') { + dependencies { + implementation(project(':charlie')) + } + } } bootJar {