diff --git a/src/main/groovy/com/github/benmanes/gradle/versions/updates/Coordinate.groovy b/src/main/groovy/com/github/benmanes/gradle/versions/updates/Coordinate.groovy index 55487d51..961ee9cd 100644 --- a/src/main/groovy/com/github/benmanes/gradle/versions/updates/Coordinate.groovy +++ b/src/main/groovy/com/github/benmanes/gradle/versions/updates/Coordinate.groovy @@ -18,6 +18,7 @@ package com.github.benmanes.gradle.versions.updates import groovy.transform.EqualsAndHashCode import groovy.transform.TypeChecked import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.DependencyConstraint import org.gradle.api.artifacts.ModuleVersionIdentifier import org.gradle.api.artifacts.ModuleVersionSelector import org.gradle.api.artifacts.component.ModuleComponentIdentifier @@ -61,6 +62,10 @@ class Coordinate implements Comparable { return new Coordinate(dependency.group, dependency.name, dependency.version) } + static Coordinate from(DependencyConstraint dependency) { + return new Coordinate(dependency.group, dependency.name, dependency.version) + } + static Coordinate from(ModuleVersionIdentifier identifier) { return new Coordinate(identifier.group, identifier.name, identifier.version) } diff --git a/src/main/groovy/com/github/benmanes/gradle/versions/updates/Resolver.groovy b/src/main/groovy/com/github/benmanes/gradle/versions/updates/Resolver.groovy index 67579dff..1489faf5 100644 --- a/src/main/groovy/com/github/benmanes/gradle/versions/updates/Resolver.groovy +++ b/src/main/groovy/com/github/benmanes/gradle/versions/updates/Resolver.groovy @@ -25,6 +25,7 @@ import org.gradle.api.artifacts.ComponentMetadata import org.gradle.api.artifacts.ComponentSelection import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.DependencyConstraint import org.gradle.api.artifacts.ExternalDependency import org.gradle.api.artifacts.LenientConfiguration import org.gradle.api.artifacts.ModuleVersionIdentifier @@ -117,6 +118,15 @@ class Resolver { createQueryDependency(dependency, revision) } + // Common use case for dependency constraints is a java-platform BOM project. + try { + configuration.dependencyConstraints.each { dependency -> + latest.add(createQueryDependency(dependency, revision)) + } + } catch (MissingPropertyException e) { + // Skip if constraints not supported + } + Configuration copy = configuration.copyRecursive().setTransitive(false) // https://github.com/ben-manes/gradle-versions-plugin/issues/127 if (copy.metaClass.respondsTo(copy, "setCanBeResolved", Boolean)) { @@ -149,6 +159,19 @@ class Resolver { } } + /** Returns a variant of the provided dependency used for querying the latest version. */ + @TypeChecked(SKIP) + private Dependency createQueryDependency(DependencyConstraint dependency, String revision) { + String versionQuery = useSelectionRules ? '+' : "latest.${revision}" + + // If no version was specified then use 'none' to pass it through. + String version = dependency.version == null ? 'none' : versionQuery + + return project.dependencies.create("${dependency.group}:${dependency.name}:${version}") { + transitive = false + } + } + /** Adds a revision filter by rejecting candidates using a component selection rule. */ @TypeChecked(SKIP) private void addRevisionFilter(Configuration configuration, String revision) { @@ -184,12 +207,11 @@ class Resolver { /** Returns the coordinates for the current (declared) dependency versions. */ private Map getCurrentCoordinates(Configuration configuration) { - Map declared = configuration.dependencies.findAll { dependency -> - dependency instanceof ExternalDependency - }.collectEntries { - Coordinate coordinate = Coordinate.from(it) - return [coordinate.key, coordinate] - } + Map declared = + getResolvableDependencies(configuration).collectEntries { + return [it.key, it] + } + if (declared.isEmpty()) { return Collections.emptyMap() } @@ -217,6 +239,15 @@ class Resolver { coordinates.put(coordinate.key, declared.get(coordinate.key)) } + try { + for (DependencyConstraint constraint : copy.dependencyConstraints) { + Coordinate coordinate = Coordinate.from(constraint) + coordinates.put(coordinate.key, declared.get(coordinate.key)) + } + } catch (MissingPropertyException e) { + // Skip if constraints not supported + } + // Ignore undeclared (hidden) dependencies that appear when resolving a configuration coordinates.keySet().retainAll(declared.keySet()) @@ -333,6 +364,24 @@ class Resolver { return null } + private static List getResolvableDependencies(Configuration configuration) { + List coordinates = configuration.dependencies.findAll { dependency -> + dependency instanceof ExternalDependency + }.collect { dependency -> + Coordinate.from(dependency) + } + + try { + configuration.dependencyConstraints.each { + coordinates.add(Coordinate.from(it)) + } + } catch (MissingPropertyException e) { + // Skip + } + + return coordinates + } + private static final class ProjectUrl { boolean isResolved String url diff --git a/src/test/groovy/com/github/benmanes/gradle/versions/JavaLibrarySpec.groovy b/src/test/groovy/com/github/benmanes/gradle/versions/JavaLibrarySpec.groovy index fb199680..37d5172c 100644 --- a/src/test/groovy/com/github/benmanes/gradle/versions/JavaLibrarySpec.groovy +++ b/src/test/groovy/com/github/benmanes/gradle/versions/JavaLibrarySpec.groovy @@ -46,4 +46,40 @@ final class JavaLibrarySpec extends Specification { result.output.contains('com.google.inject:guice [2.0 -> 3.1]') result.task(':dependencyUpdates').outcome == SUCCESS } + + def "Show updates for an api dependency constraint in a java-library project"() { + given: + def mavenRepoUrl = getClass().getResource('/maven/').toURI() + buildFile = testProjectDir.newFile('build.gradle') + buildFile << + """ + plugins { + id 'java-library' + id 'com.github.ben-manes.versions' + } + + repositories { + maven { + url '${mavenRepoUrl}' + } + } + + dependencies { + constraints { + api 'com.google.inject:guice:2.0' + } + } + """.stripIndent() + + when: + def result = GradleRunner.create() + .withProjectDir(testProjectDir.root) + .withArguments('dependencyUpdates') + .withPluginClasspath() + .build() + + then: + result.output.contains('com.google.inject:guice [2.0 -> 3.1]') + result.task(':dependencyUpdates').outcome == SUCCESS + } }