Skip to content

Commit

Permalink
Consider changes to a dependency's target when managing its version
Browse files Browse the repository at this point in the history
Previously, dependency management was determined used the requested
selector. This meant that any changes made to the target selector
were ignored. This would cause a problem if an earlier resolution
strategy had changed the group ID or artifact ID of the dependency
as the managed version would be determined using the original
coordinates not the updated coordinates.

This commit applies managed versions using the target selector,
thereby ensuring that any earlier changes to the target are taken
into account when determining the managed version.

Fixes gh-383
  • Loading branch information
wilkinsona committed May 2, 2024
1 parent 19824b8 commit e722715
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2023 the original author or authors.
* Copyright 2014-2024 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.
Expand All @@ -24,6 +24,7 @@
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.DependencyResolveDetails;
import org.gradle.api.artifacts.ModuleVersionSelector;
import org.gradle.api.artifacts.ResolutionStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -59,44 +60,44 @@ class VersionConfiguringAction implements Action<DependencyResolveDetails> {

@Override
public void execute(DependencyResolveDetails details) {
logger.debug("Processing dependency '{}'", details.getRequested());
if (isDependencyOnLocalProject(this.project, details)) {
logger.debug("'{}' is a local project dependency. Dependency management has not been applied",
details.getRequested());
ModuleVersionSelector target = details.getTarget();
logger.debug("Processing requested dependency '{}' with target '{}", details.getRequested(), target);
if (isDependencyOnLocalProject(this.project, target)) {
logger.debug("'{}' is a local project dependency. Dependency management has not been applied", target);
return;
}
if (isDirectDependency(details) && Versions.isDynamic(details.getRequested().getVersion())) {
if (isDirectDependency(target) && Versions.isDynamic(target.getVersion())) {
logger.debug("'{}' is a direct dependency and has a dynamic version. "
+ "Dependency management has not been applied", details.getRequested());
+ "Dependency management has not been applied", target);
return;
}
String version = this.dependencyManagementContainer.getManagedVersion(this.configuration,
details.getRequested().getGroup(), details.getRequested().getName());
String version = this.dependencyManagementContainer.getManagedVersion(this.configuration, target.getGroup(),
target.getName());
if (version != null) {
logger.debug("Using version '{}' for dependency '{}'", version, details.getRequested());
logger.debug("Using version '{}' for dependency '{}'", version, target);
details.useVersion(version);
return;
}
logger.debug("No dependency management for dependency '{}'", details.getRequested());
logger.debug("No dependency management for dependency '{}'", target);
}

private boolean isDirectDependency(DependencyResolveDetails details) {
private boolean isDirectDependency(ModuleVersionSelector selector) {
if (this.directDependencies == null) {
Set<String> directDependencies = new HashSet<>();
for (Dependency dependency : this.configuration.getAllDependencies()) {
directDependencies.add(dependency.getGroup() + ":" + dependency.getName());
}
this.directDependencies = directDependencies;
}
return this.directDependencies.contains(getId(details));
return this.directDependencies.contains(getId(selector));
}

private boolean isDependencyOnLocalProject(Project project, DependencyResolveDetails details) {
return this.localProjects.getNames().contains(getId(details));
private boolean isDependencyOnLocalProject(Project project, ModuleVersionSelector selector) {
return this.localProjects.getNames().contains(getId(selector));
}

private String getId(DependencyResolveDetails details) {
return details.getRequested().getGroup() + ":" + details.getRequested().getName();
private String getId(ModuleVersionSelector selector) {
return selector.getGroup() + ":" + selector.getName();
}

ResolutionStrategy applyTo(Configuration c) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2023 the original author or authors.
* Copyright 2014-2024 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.
Expand Down Expand Up @@ -524,6 +524,12 @@ void platformConstrainingATransitiveDependencyDoesNotAccidentallyExcludeThatDepe
"kotlin-stdlib-common-1.9.10.jar", "annotations-13.0.jar");
}

@Test
void whenDependencyIsSubstitutedNewCoordinatesAreUsedForDependencyManagement() {
this.gradleBuild.runner().withArguments("resolve").build();
assertThat(readLines("resolved.txt")).containsExactly("bcprov-jdk18on-1.78.1.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,41 @@
buildscript {
project.configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "org.bouncycastle" && requested.name == "bcprov-jdk15on") {
useTarget("org.bouncycastle:bcprov-jdk18on:1.78.1")
}
}
}
}

plugins {
id "java"
id "io.spring.dependency-management"
}

/*
configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "org.bouncycastle" && requested.name == "bcprov-jdk15on") {
useTarget("org.bouncycastle:bcprov-jdk18on:1.78.1")
}
}
}
*/

repositories {
mavenCentral()
}

dependencies {
implementation("org.bouncycastle:bcprov-jdk15on:1.70")
}

task 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" }
}
}

0 comments on commit e722715

Please sign in to comment.