Skip to content

Commit

Permalink
Fix accidental exclusion of a platform's dependencies
Browse files Browse the repository at this point in the history
Closes gh-368
  • Loading branch information
wilkinsona committed Oct 19, 2023
1 parent f8c80a1 commit aa1f91d
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

Expand All @@ -39,11 +38,7 @@
import org.gradle.api.artifacts.result.ResolutionResult;
import org.gradle.api.artifacts.result.ResolvedComponentResult;
import org.gradle.api.artifacts.result.ResolvedDependencyResult;
import org.gradle.api.artifacts.result.ResolvedVariantResult;
import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.attributes.AttributeContainer;
import org.gradle.api.attributes.Category;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -107,7 +102,7 @@ private Set<DependencyCandidate> findExcludedDependencies() {
resolutionResult.allDependencies((dependencyResult) -> {
if (dependencyResult instanceof ResolvedDependencyResult) {
ResolvedDependencyResult resolved = (ResolvedDependencyResult) dependencyResult;
if (!isPlatform(resolved.getSelected())) {
if (!resolved.isConstraint()) {
excludedDependencies.add(new DependencyCandidate(resolved.getSelected().getModuleVersion()));
}
}
Expand Down Expand Up @@ -137,21 +132,19 @@ private Configuration copyConfiguration() {
private Set<DependencyCandidate> determineIncludedComponents(ResolvedComponentResult root,
Map<String, Exclusions> pomExclusionsById) {
LinkedList<Node> queue = new LinkedList<>();
queue.add(new Node(root, getId(root), new HashSet<>(), false));
queue.add(new Node(root, getId(root), new HashSet<>()));
Set<ResolvedComponentResult> seen = new HashSet<>();
Set<DependencyCandidate> includedComponents = new HashSet<>();
while (!queue.isEmpty()) {
Node node = queue.remove();
includedComponents.add(new DependencyCandidate(node.component.getModuleVersion()));
if (!node.platform) {
for (DependencyResult dependency : node.component.getDependencies()) {
if (dependency instanceof ResolvedDependencyResult) {
handleResolvedDependency((ResolvedDependencyResult) dependency, node, pomExclusionsById, queue,
seen);
}
else if (dependency instanceof UnresolvedDependencyResult) {
handleUnresolvedDependency((UnresolvedDependencyResult) dependency, node, includedComponents);
}
for (DependencyResult dependency : node.component.getDependencies()) {
if (dependency instanceof ResolvedDependencyResult) {
handleResolvedDependency((ResolvedDependencyResult) dependency, node, pomExclusionsById, queue,
seen);
}
else if (dependency instanceof UnresolvedDependencyResult) {
handleUnresolvedDependency((UnresolvedDependencyResult) dependency, node, includedComponents);
}
}
}
Expand All @@ -162,25 +155,11 @@ private void handleResolvedDependency(ResolvedDependencyResult dependency, Node
Map<String, Exclusions> pomExclusionsById, LinkedList<Node> queue, Set<ResolvedComponentResult> seen) {
ResolvedComponentResult child = dependency.getSelected();
String childId = getId(child);
if (!node.excluded(childId) && seen.add(child)) {
boolean platform = isPlatform(child);
queue.add(new Node(child, childId, getChildExclusions(node, childId, pomExclusionsById), platform));
if (!node.excluded(childId) && seen.add(child) && !dependency.isConstraint()) {
queue.add(new Node(child, childId, getChildExclusions(node, childId, pomExclusionsById)));
}
}

private boolean isPlatform(ResolvedComponentResult component) {
List<ResolvedVariantResult> variants = component.getVariants();
for (ResolvedVariantResult variant : variants) {
AttributeContainer attributes = variant.getAttributes();
String category = attributes.getAttribute(Attribute.of("org.gradle.category", String.class));
if (category == null
|| (!Category.REGULAR_PLATFORM.equals(category) && !Category.ENFORCED_PLATFORM.equals(category))) {
return false;
}
}
return true;
}

private void handleUnresolvedDependency(UnresolvedDependencyResult dependency, Node node,
Set<DependencyCandidate> includedComponents) {
DependencyCandidate dependencyCandidate = toDependencyCandidate(dependency);
Expand Down Expand Up @@ -227,13 +206,10 @@ private static final class Node {

private final Set<Exclusion> exclusions;

private final boolean platform;

private Node(ResolvedComponentResult component, String id, Set<Exclusion> exclusions, boolean platform) {
private Node(ResolvedComponentResult component, String id, Set<Exclusion> exclusions) {
this.component = component;
this.id = id;
this.exclusions = exclusions;
this.platform = platform;
}

private boolean excluded(String id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,12 @@ void resolutionSucceedsWhenDependencyHasAnInvalidPom() {
.contains("Building this project requires a newer version of Maven");
}

@Test
void dependenciesDeclaredInAPlatformAreNotAccidentallyExcluded() {
this.gradleBuild.runner().withArguments("resolve").build();
assertThat(readLines("resolved.txt")).containsExactly("spring-core-5.3.27.jar", "spring-jcl-5.3.27.jar");
}

private void writeLines(Path path, String... lines) {
try {
Path resolvedPath = this.gradleBuild.runner().getProjectDir().toPath().resolve(path);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
plugins {
id "java"
id "io.spring.dependency-management"
}

repositories {
maven {
url file("maven-repo")
}
mavenCentral()
}

dependencies {
implementation(platform("test:platform-with-dependencies:1.0"))
}

tasks.register("resolve") {
doFirst {
def files = project.configurations.compileClasspath.resolve()
def output = new File("${buildDir}/resolved.txt")
output.parentFile.mkdirs()
files.collect { it.name }.each { output << "${it}\n" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"formatVersion": "1.1",
"component": {
"group": "test",
"module": "platform-with-dependencies",
"version": "1.0",
"attributes": {
"org.gradle.status": "release"
}
},
"createdBy": {
"gradle": {
"version": "8.1.1"
}
},
"variants": [
{
"name": "apiElements",
"attributes": {
"org.gradle.category": "platform",
"org.gradle.usage": "java-api"
},
"dependencies": [
{
"group": "org.springframework",
"module": "spring-core",
"version": {
"requires": "5.3.27"
}
}
]
},
{
"name": "runtimeElements",
"attributes": {
"org.gradle.category": "platform",
"org.gradle.usage": "java-runtime"
},
"dependencies": [
{
"group": "org.springframework",
"module": "spring-core",
"version": {
"requires": "6.0.12"
}
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>platform-with-dependencies</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.27</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

0 comments on commit aa1f91d

Please sign in to comment.