Improve mapping of resolved artifacts to dependency coordinates

Closes gh-31216
This commit is contained in:
Andy Wilkinson
2023-05-05 11:04:39 +01:00
parent 414c260bc0
commit fd864c35aa
9 changed files with 147 additions and 176 deletions

View File

@@ -144,6 +144,8 @@ final class JavaPluginAction implements PluginApplicationAction {
.getByName(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
Configuration productionRuntimeClasspath = project.getConfigurations()
.getByName(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
Configuration runtimeClasspath = project.getConfigurations()
.getByName(mainSourceSet.getRuntimeClasspathConfigurationName());
Callable<FileCollection> classpath = () -> mainSourceSet.getRuntimeClasspath()
.minus((developmentOnly.minus(productionRuntimeClasspath)))
.filter(new JarTypeFileSpec());
@@ -159,6 +161,7 @@ final class JavaPluginAction implements PluginApplicationAction {
? manifestStartClass : resolveMainClassName.get().readMainClassName()));
bootJar.getTargetJavaVersion()
.set(project.provider(() -> javaPluginExtension(project).getTargetCompatibility()));
bootJar.resolvedArtifacts(runtimeClasspath.getIncoming().getArtifacts().getResolvedArtifacts());
});
}

View File

@@ -74,10 +74,12 @@ class WarPluginAction implements PluginApplicationAction {
.getByName(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
Configuration productionRuntimeClasspath = project.getConfigurations()
.getByName(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
Callable<FileCollection> classpath = () -> project.getExtensions()
SourceSet mainSourceSet = project.getExtensions()
.getByType(SourceSetContainer.class)
.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
.getRuntimeClasspath()
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
Configuration runtimeClasspath = project.getConfigurations()
.getByName(mainSourceSet.getRuntimeClasspathConfigurationName());
Callable<FileCollection> classpath = () -> mainSourceSet.getRuntimeClasspath()
.minus(providedRuntimeConfiguration(project))
.minus((developmentOnly.minus(productionRuntimeClasspath)))
.filter(new JarTypeFileSpec());
@@ -97,6 +99,7 @@ class WarPluginAction implements PluginApplicationAction {
? manifestStartClass : resolveMainClassName.get().readMainClassName()));
bootWar.getTargetJavaVersion()
.set(project.provider(() -> javaPluginExtension(project).getTargetCompatibility()));
bootWar.resolvedArtifacts(runtimeClasspath.getIncoming().getArtifacts().getResolvedArtifacts());
});
bootWarProvider.map(War::getClasspath);
return bootWarProvider;

View File

@@ -16,13 +16,17 @@
package org.springframework.boot.gradle.tasks.bundling;
import java.util.Set;
import org.gradle.api.Action;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileTreeElement;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.specs.Spec;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Input;
@@ -120,4 +124,13 @@ public interface BootArchive extends Task {
@Optional
Property<JavaVersion> getTargetJavaVersion();
/**
* Registers the given lazily provided {@code resolvedArtifacts}. They are used to map
* from the files in the {@link #getClasspath classpath} to their dependency
* coordinates.
* @param resolvedArtifacts the lazily provided resolved artifacts
* @since 3.0.7
*/
void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts);
}

View File

@@ -18,12 +18,13 @@ package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Function;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ResolvableDependencies;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.file.CopySpec;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileCopyDetails;
@@ -58,8 +59,6 @@ public abstract class BootJar extends Jar implements BootArchive {
private static final String CLASSPATH_INDEX = "BOOT-INF/classpath.idx";
private final ResolvedDependencies resolvedDependencies = new ResolvedDependencies();
private final BootArchiveSupport support;
private final CopySpec bootInfSpec;
@@ -70,6 +69,8 @@ public abstract class BootJar extends Jar implements BootArchive {
private final Provider<Object> projectVersion;
private final ResolvedDependencies resolvedDependencies;
private FileCollection classpath;
/**
@@ -82,16 +83,9 @@ public abstract class BootJar extends Jar implements BootArchive {
this.layered = project.getObjects().newInstance(LayeredSpec.class);
configureBootInfSpec(this.bootInfSpec);
getMainSpec().with(this.bootInfSpec);
project.getConfigurations().all((configuration) -> {
ResolvableDependencies incoming = configuration.getIncoming();
incoming.afterResolve((resolvableDependencies) -> {
if (resolvableDependencies == incoming) {
this.resolvedDependencies.processConfiguration(project, configuration);
}
});
});
this.projectName = project.provider(project::getName);
this.projectVersion = project.provider(project::getVersion);
this.resolvedDependencies = new ResolvedDependencies(project);
}
private void configureBootInfSpec(CopySpec bootInfSpec) {
@@ -123,6 +117,16 @@ public abstract class BootJar extends Jar implements BootArchive {
});
}
@Override
public void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts) {
this.resolvedDependencies.resolvedArtifacts(resolvedArtifacts);
}
@Nested
ResolvedDependencies getResolvedDependencies() {
return this.resolvedDependencies;
}
@Override
public void copy() {
this.support.configureManifest(getManifest(), getMainClass().get(), CLASSES_DIRECTORY, LIB_DIRECTORY,
@@ -291,11 +295,6 @@ public abstract class BootJar extends Jar implements BootArchive {
return callable;
}
@Internal
ResolvedDependencies getResolvedDependencies() {
return this.resolvedDependencies;
}
private final class LibrarySpec implements Spec<FileCopyDetails> {
@Override

View File

@@ -17,12 +17,13 @@
package org.springframework.boot.gradle.tasks.bundling;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Function;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ResolvableDependencies;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.file.CopySpec;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileCopyDetails;
@@ -31,7 +32,6 @@ import org.gradle.api.internal.file.copy.CopyAction;
import org.gradle.api.provider.Provider;
import org.gradle.api.specs.Spec;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.bundling.War;
@@ -62,14 +62,14 @@ public abstract class BootWar extends War implements BootArchive {
private final BootArchiveSupport support;
private final ResolvedDependencies resolvedDependencies = new ResolvedDependencies();
private final LayeredSpec layered;
private final Provider<String> projectName;
private final Provider<Object> projectVersion;
private final ResolvedDependencies resolvedDependencies;
private FileCollection providedClasspath;
/**
@@ -82,22 +82,25 @@ public abstract class BootWar extends War implements BootArchive {
getWebInf().into("lib-provided", fromCallTo(this::getProvidedLibFiles));
this.support.moveModuleInfoToRoot(getRootSpec());
getRootSpec().eachFile(this.support::excludeNonZipLibraryFiles);
project.getConfigurations().all((configuration) -> {
ResolvableDependencies incoming = configuration.getIncoming();
incoming.afterResolve((resolvableDependencies) -> {
if (resolvableDependencies == incoming) {
this.resolvedDependencies.processConfiguration(project, configuration);
}
});
});
this.projectName = project.provider(project::getName);
this.projectVersion = project.provider(project::getVersion);
this.resolvedDependencies = new ResolvedDependencies(project);
}
private Object getProvidedLibFiles() {
return (this.providedClasspath != null) ? this.providedClasspath : Collections.emptyList();
}
@Override
public void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts) {
this.resolvedDependencies.resolvedArtifacts(resolvedArtifacts);
}
@Nested
ResolvedDependencies getResolvedDependencies() {
return this.resolvedDependencies;
}
@Override
public void copy() {
this.support.configureManifest(getManifest(), getMainClass().get(), CLASSES_DIRECTORY, LIB_DIRECTORY,
@@ -240,11 +243,6 @@ public abstract class BootWar extends War implements BootArchive {
return launchScript;
}
@Internal
ResolvedDependencies getResolvedDependencies() {
return this.resolvedDependencies;
}
/**
* Syntactic sugar that makes {@link CopySpec#into} calls a little easier to read.
* @param <T> the result type

View File

@@ -17,23 +17,29 @@
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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.LenientConfiguration;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedConfiguration;
import org.gradle.api.artifacts.component.ComponentArtifactIdentifier;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Input;
import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
import org.springframework.boot.loader.tools.LibraryCoordinates;
/**
* Tracks and provides details of resolved dependencies in the project so we can find
* {@link LibraryCoordinates}.
* Maps from {@link File} to {@link ComponentArtifactIdentifier}.
*
* @author Madhura Bhave
* @author Scott Frederick
@@ -43,93 +49,76 @@ import org.springframework.boot.loader.tools.LibraryCoordinates;
*/
class ResolvedDependencies {
private final Map<Configuration, ResolvedConfigurationDependencies> configurationDependencies = new LinkedHashMap<>();
private final Map<String, LibraryCoordinates> projectCoordinatesByPath;
private String projectId(Project project) {
return project.getGroup() + ":" + project.getName() + ":" + project.getVersion();
private final ListProperty<ComponentArtifactIdentifier> artifactIds;
private final ListProperty<File> artifactFiles;
ResolvedDependencies(Project project) {
this.artifactIds = project.getObjects().listProperty(ComponentArtifactIdentifier.class);
this.artifactFiles = project.getObjects().listProperty(File.class);
this.projectCoordinatesByPath = projectCoordinatesByPath(project);
}
void processConfiguration(Project project, Configuration configuration) {
Set<String> localProjectIds = project.getRootProject()
private static Map<String, LibraryCoordinates> projectCoordinatesByPath(Project project) {
return project.getRootProject()
.getAllprojects()
.stream()
.map(this::projectId)
.collect(Collectors.toSet());
this.configurationDependencies.put(configuration,
new ResolvedConfigurationDependencies(localProjectIds, configuration.getResolvedConfiguration()));
.collect(Collectors.toMap(Project::getPath, ResolvedDependencies::libraryCoordinates));
}
private static LibraryCoordinates libraryCoordinates(Project project) {
return LibraryCoordinates.of(Objects.toString(project.getGroup()), project.getName(),
Objects.toString(project.getVersion()));
}
@Input
ListProperty<ComponentArtifactIdentifier> getArtifactIds() {
return this.artifactIds;
}
@Classpath
ListProperty<File> getArtifactFiles() {
return this.artifactFiles;
}
void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts) {
this.artifactFiles.addAll(resolvedArtifacts
.map((artifacts) -> artifacts.stream().map(ResolvedArtifactResult::getFile).collect(Collectors.toList())));
this.artifactIds.addAll(resolvedArtifacts
.map((artifacts) -> artifacts.stream().map(ResolvedArtifactResult::getId).collect(Collectors.toList())));
}
DependencyDescriptor find(File file) {
for (ResolvedConfigurationDependencies dependencies : this.configurationDependencies.values()) {
DependencyDescriptor dependency = dependencies.find(file);
if (dependency != null) {
return dependency;
ComponentArtifactIdentifier id = findArtifactIdentifier(file);
if (id == null) {
return null;
}
if (id instanceof ModuleComponentArtifactIdentifier moduleComponentId) {
ModuleComponentIdentifier moduleId = moduleComponentId.getComponentIdentifier();
return new DependencyDescriptor(
LibraryCoordinates.of(moduleId.getGroup(), moduleId.getModule(), moduleId.getVersion()), false);
}
ComponentIdentifier componentIdentifier = id.getComponentIdentifier();
if (componentIdentifier instanceof ProjectComponentIdentifier projectComponentId) {
String projectPath = projectComponentId.getProjectPath();
LibraryCoordinates projectCoordinates = this.projectCoordinatesByPath.get(projectPath);
if (projectCoordinates != null) {
return new DependencyDescriptor(projectCoordinates, true);
}
}
return null;
}
/**
* Stores details of resolved configuration dependencies.
*/
private static class ResolvedConfigurationDependencies {
private final Map<File, DependencyDescriptor> dependencies = new LinkedHashMap<>();
ResolvedConfigurationDependencies(Set<String> projectDependencyIds,
ResolvedConfiguration resolvedConfiguration) {
if (!resolvedConfiguration.hasError()) {
LenientConfiguration lenientConfiguration = resolvedConfiguration.getLenientConfiguration();
// Ensure that all files are resolved, allowing Gradle to resolve in
// parallel if they are not
lenientConfiguration.getFiles();
for (ResolvedArtifact resolvedArtifact : lenientConfiguration.getArtifacts()) {
ModuleVersionIdentifier id = resolvedArtifact.getModuleVersion().getId();
boolean projectDependency = projectDependencyIds
.contains(id.getGroup() + ":" + id.getName() + ":" + id.getVersion());
this.dependencies.put(resolvedArtifact.getFile(), new DependencyDescriptor(
new ModuleVersionIdentifierLibraryCoordinates(id), projectDependency));
}
private ComponentArtifactIdentifier findArtifactIdentifier(File file) {
List<File> files = this.artifactFiles.get();
for (int i = 0; i < files.size(); i++) {
if (file.equals(files.get(i))) {
return this.artifactIds.get().get(i);
}
}
DependencyDescriptor find(File file) {
return this.dependencies.get(file);
}
}
/**
* Adapts a {@link ModuleVersionIdentifier} to {@link LibraryCoordinates}.
*/
private static class ModuleVersionIdentifierLibraryCoordinates implements LibraryCoordinates {
private final ModuleVersionIdentifier identifier;
ModuleVersionIdentifierLibraryCoordinates(ModuleVersionIdentifier identifier) {
this.identifier = identifier;
}
@Override
public String getGroupId() {
return this.identifier.getGroup();
}
@Override
public String getArtifactId() {
return this.identifier.getName();
}
@Override
public String getVersion() {
return this.identifier.getVersion();
}
@Override
public String toString() {
return this.identifier.toString();
}
return null;
}
/**