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 a853e245bab9..665cef7d848d 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 62548749b92a..fc7bdb89dba7 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.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 70ce20fdc8b2..a8ddd80abe1c 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 @@ void multiModuleImplicitLayers() throws IOException { 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 @@ void multiModuleImplicitLayers() throws IOException { 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 @@ void multiModuleCustomLayers() throws IOException { 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 @@ void multiModuleCustomLayers() throws IOException { 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 @@ private boolean isInIndex(List index, String file) { 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 795c16e3e609..933282e2f899 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 @@ private void addContent() throws IOException { 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 29cc5a3687a2..f8127b2f3db5 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 bd78e7ce97d4..ba34cf4d1d6c 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 {