Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constraints checking and latest version determination issues with platforms #727

Closed
ianbrandt opened this issue Feb 10, 2023 · 42 comments
Closed

Comments

@ianbrandt
Copy link

ianbrandt commented Feb 10, 2023

I'm defining my own platform that in turn imports the BOMs for Spring and Spring Boot. The project is still on Java 11, so for the time being I need to constrain and align my Spring and Spring Boot versions to be less than 6.x and 3.x respectively, as those require Java 17.

This may be user error, but I'm unable to get the Gradle Versions Plugin to honor my platform constraints. Reproducer here:

https://github.com/ianbrandt/gradle-versions-plugin-platform-constraints/tree/f5b4360c2c5725cce61fa1ea0ed7d307ef3e8ed0

Constraint checking enabled:

https://github.com/ianbrandt/gradle-versions-plugin-platform-constraints/blob/f5b4360c2c5725cce61fa1ea0ed7d307ef3e8ed0/build.gradle.kts#L26

My platform, with Spring BOM import, and version alignment via a ComponentMetadataRule (I'm not sure if the rule can be declared in the platform, or if it should be declared in my convention plugin, but I tried it both ways and it didn't seem to make a difference):

https://github.com/ianbrandt/gradle-versions-plugin-platform-constraints/blob/f5b4360c2c5725cce61fa1ea0ed7d307ef3e8ed0/platforms/app-platform/build.gradle.kts

The version range constraints specified with strict version shorthand:

https://github.com/ianbrandt/gradle-versions-plugin-platform-constraints/blob/f5b4360c2c5725cce61fa1ea0ed7d307ef3e8ed0/gradle/libs.versions.toml#L13-L14

Toolchain declaration for Java 11 (via Kotlin):

https://github.com/ianbrandt/gradle-versions-plugin-platform-constraints/blob/main/build-logic/src/main/kotlin/com.ianbrandt.buildlogic.kotlin-project.gradle.kts#L13

With that I get the following running dependencyUpdates:

The following dependencies have later milestone versions:
 - org.jetbrains.kotlin:kotlin-reflect [1.7.10 -> 1.8.10]
     https://kotlinlang.org/
 - org.springframework.boot:org.springframework.boot.gradle.plugin [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot

Failed to determine the latest version for the following dependencies (use --info for details):
 - org.apache.logging.log4j:log4j-core
     2.19.0
 - org.springframework:spring-context
     6.0.4
 - org.springframework.boot:spring-boot
     3.0.2
 - org.springframework.boot:spring-boot-autoconfigure
     3.0.2
 - org.springframework.boot:spring-boot-starter
     3.0.2
 - org.springframework.boot:spring-boot-starter-log4j2
     3.0.2
 - org.springframework.boot:spring-boot-starter-test
     3.0.2
 - org.springframework.boot:spring-boot-starter-webflux
     3.0.2

When I run with --info I get:

Failed to determine the latest version for the following dependencies (use --info for details):
 - org.apache.logging.log4j:log4j-core
     2.19.0
The exception that is the cause of unresolved state: org.gradle.internal.resolve.ModuleVersionNotFoundException: Cannot resolve external dependency org.apache.logging.log4j:log4j-core:+ because no repositories are defined.
Required by:
    project :subprojects

 - org.springframework:spring-context
     6.0.4
The exception that is the cause of unresolved state: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve org.springframework:spring-context:+.
Required by:
    project :subprojects:app
Caused by: org.gradle.internal.component.NoMatchingConfigurationSelectionException: No matching variant of org.springframework:spring-context:6.0.4 was found. The consumer was configured to find a runtime of a library compatible with Java 11, packaged as a jar, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm' but:
  - Variant 'apiElements' capability org.springframework:spring-context:6.0.4 declares a library, packaged as a jar, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm':
      - Incompatible because this component declares an API of a component compatible with Java 17 and the consumer needed a runtime of a component compatible with Java 11
  - Variant 'runtimeElements' capability org.springframework:spring-context:6.0.4 declares a runtime of a library, packaged as a jar, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm':
      - Incompatible because this component declares a component compatible with Java 17 and the consumer needed a component compatible with Java 11
	at org.gradle.internal.component.model.AttributeConfigurationSelector.selectVariantsUsingAttributeMatching(AttributeConfigurationSelector.java:113)
[...]

 - org.springframework.boot:spring-boot
     3.0.2
The exception that is the cause of unresolved state: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve org.springframework.boot:spring-boot:+.
Required by:
    project :subprojects:app
Caused by: org.gradle.internal.component.NoMatchingConfigurationSelectionException: No matching variant of org.springframework.boot:spring-boot:3.0.2 was found. The consumer was configured to find a runtime of a library compatible with Java 11, packaged as a jar, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm' but:
  - Variant 'apiElements' capability org.springframework.boot:spring-boot:3.0.2 declares a library, packaged as a jar, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm':
      - Incompatible because this component declares an API of a component compatible with Java 17 and the consumer needed a runtime of a component compatible with Java 11
  - Variant 'javadocElements' capability org.springframework.boot:spring-boot:3.0.2 declares a runtime of a component, and its dependencies declared externally:
      - Incompatible because this component declares documentation and the consumer needed a library
      - Other compatible attributes:
          - Doesn't say anything about its target Java environment (preferred optimized for standard JVMs)
          - Doesn't say anything about its target Java version (required compatibility with Java 11)
          - Doesn't say anything about its elements (required them packaged as a jar)
          - Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'jvm')
  - Variant 'mavenOptionalApiElements' capability org.springframework.boot:spring-boot-maven-optional:3.0.2 declares a library, packaged as a jar, and its dependencies declared externally:
      - Incompatible because this component declares an API of a component compatible with Java 17 and the consumer needed a runtime of a component compatible with Java 11
      - Other compatible attributes:
          - Doesn't say anything about its target Java environment (preferred optimized for standard JVMs)
          - Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'jvm')
  - Variant 'mavenOptionalRuntimeElements' capability org.springframework.boot:spring-boot-maven-optional:3.0.2 declares a runtime of a library, packaged as a jar, and its dependencies declared externally:
      - Incompatible because this component declares a component compatible with Java 17 and the consumer needed a component compatible with Java 11
      - Other compatible attributes:
          - Doesn't say anything about its target Java environment (preferred optimized for standard JVMs)
          - Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'jvm')
  - Variant 'runtimeElements' capability org.springframework.boot:spring-boot:3.0.2 declares a runtime of a library, packaged as a jar, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm':
      - Incompatible because this component declares a component compatible with Java 17 and the consumer needed a component compatible with Java 11
  - Variant 'sourcesElements' capability org.springframework.boot:spring-boot:3.0.2 declares a runtime of a component, and its dependencies declared externally:
      - Incompatible because this component declares documentation and the consumer needed a library
      - Other compatible attributes:
          - Doesn't say anything about its target Java environment (preferred optimized for standard JVMs)
          - Doesn't say anything about its target Java version (required compatibility with Java 11)
          - Doesn't say anything about its elements (required them packaged as a jar)
          - Doesn't say anything about org.jetbrains.kotlin.platform.type (required 'jvm')
	at org.gradle.internal.component.model.AttributeConfigurationSelector.selectVariantsUsingAttributeMatching(AttributeConfigurationSelector.java:113)
[...]

What I'm hoping for here is that the plugin recommends updates for the Spring BOMs I'm importing only if they're within a specified version range, and to not get the "Failed to determine the latest version for the following dependencies" errors for dependency versions that don't have variants compatible with my toolchain.

Am I doing something wrong, or is the issue limitations or bugs in the plugin?

@ben-manes
Copy link
Owner

Unfortunately gradle module metadata and its usages are very flaky. This is somewhat on Gradle having to evolve with minimal compatibility breaks, but most often due to other plugins intentionally misbehaving. You are welcome to debug our Resolver to see if you can diagnose the root problem.

In similar cases that I have debugged, it has usually been the Kotlin or Android plugins which violate the Gradle APIs. They do hack to the internals to bypass restrictions, hard code configuration names instead of using metadata (e.g. breaking configuration copying or extends), reinvent basic features in brittle ways (e.g. default dependencies), and simply don't want to play nice or respect others in the community. Where possible we have some workarounds to fix their bugs, but often I don't know how to fix an issue when the other plugins refuse to correct their broken behavior. This plugin uses Gradle's apis in very simple ways that held true since Gradle 1.0, approaches approved by the core team, and other than slight compatibility changes between major versions it is rarely at fault.

For BOMs the Spring team was great when their plugin broke compatibility and fixed that mistake (spring-gradle-plugins/dependency-management-plugin#77). My expertise in Gradle is aging and the interactions are becoming complex, so unfortunately I'm probably not the best one to help you figure this out anymore. But you are welcome to dig in by using a local build of the plugin to try to see where the problem comes from and if something on our side, then PRs are always welcome.

@ianbrandt
Copy link
Author

I could start by converting the reproducer project to be Java only, and see if removing the Kotlin Gradle Plugin makes any difference.

Somewhat encouraging, I follow and vote for all the Gradle compatibility issues for Kotlin (e.g. https://youtrack.jetbrains.com/issue/KT-54231/Compatibility-with-Gradle-8.0-release, which links to similar meta issues for the earlier and later versions), and they seem to be making a significant push lately to catch up and correct things.

@ianbrandt
Copy link
Author

Done:

https://github.com/ianbrandt/gradle-versions-plugin-platform-constraints/tree/java-only

I get basically the same result:

The following dependencies have later milestone versions:
 - org.springframework.boot:org.springframework.boot.gradle.plugin [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot

Failed to determine the latest version for the following dependencies (use --info for details):
 - org.apache.logging.log4j:log4j-core
     2.19.0
 - org.springframework:spring-context
     6.0.4
 - org.springframework.boot:spring-boot
     3.0.2
 - org.springframework.boot:spring-boot-autoconfigure
     3.0.2
 - org.springframework.boot:spring-boot-starter
     3.0.2
 - org.springframework.boot:spring-boot-starter-log4j2
     3.0.2
 - org.springframework.boot:spring-boot-starter-test
 - org.springframework.boot:spring-boot-starter-webflux
     3.0.2

@ben-manes
Copy link
Owner

thanks! I'll try to spend some time this weekend and see if anything jumps out at me.

@mikand13
Copy link

Might be a regression here. Seeing the same on 0.45.0, no problems on 0.44.0. We are running against a nexus on AWS, if that helps.

@ben-manes
Copy link
Owner

ben-manes commented Feb 12, 2023

Thanks for that insight @mikand13! I can confirm that the sample project resolves fine in 0.44, with the small caveat of log4j which might be a configuration mistake. I'll bisect to see what might have caused this.

------------------------------------------------------------
: Project Dependency Updates (report to plain text file)
------------------------------------------------------------

The following dependencies are using the latest milestone version:
 - com.ianbrandt.platforms:app-platform:1.0-SNAPSHOT
 - com.ianbrandt.platforms:test-platform:1.0-SNAPSHOT
 - io.mockk:mockk:1.13.4
 - io.mockk:mockk-dsl-jvm:1.13.4
 - org.assertj:assertj-core:3.24.2
 - org.jetbrains:annotations:13.0
 - org.jetbrains.kotlin:kotlin-allopen:1.8.10
 - org.jetbrains.kotlin:kotlin-reflect:1.8.10
 - org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.8.10
 - org.jetbrains.kotlin:kotlin-stdlib:1.7.10
 - org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10
 - org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10
 - org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10
 - org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4
 - org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4
 - org.junit.jupiter:junit-jupiter:5.9.2
 - org.junit.jupiter:junit-jupiter-api:5.9.2
 - org.junit.jupiter:junit-jupiter-params:5.9.2

The following dependencies have later milestone versions:
 - com.github.ben-manes.versions:com.github.ben-manes.versions.gradle.plugin [0.44.0 -> 0.45.0]
 - org.jetbrains.kotlin:kotlin-reflect [1.7.10 -> 1.8.10]
     https://kotlinlang.org/
 - org.springframework:spring-context [5.3.25 -> 6.0.4]
     https://github.com/spring-projects/spring-framework
 - org.springframework.boot:org.springframework.boot.gradle.plugin [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot
 - org.springframework.boot:spring-boot [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot
 - org.springframework.boot:spring-boot-autoconfigure [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot
 - org.springframework.boot:spring-boot-starter [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot
 - org.springframework.boot:spring-boot-starter-log4j2 [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot
 - org.springframework.boot:spring-boot-starter-test [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot
 - org.springframework.boot:spring-boot-starter-webflux [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot

Failed to determine the latest version for the following dependencies (use --info for details):
 - org.apache.logging.log4j:log4j-core
     2.19.0
The exception that is the cause of unresolved state: org.gradle.internal.resolve.ModuleVersionNotFoundException: Cannot resolve external dependency org.apache.logging.log4j:log4j-core:{require 2.17.1; reject [2.0, 2.17.1)} because no repositories are defined.
Required by:
    project :subprojects

@ben-manes
Copy link
Owner

Hi @jvandort,

It seems that your change in #721 to support Gradle 9.0's changes is the cause of this failed resolution. Can you please take a look and advise? I was able to confirm by taking your commit and the one prior, publishing versions to the local maven repository, and specifying those in the sample java project above. With your changes we observe the reported errors. Thanks!

@jvandort
Copy link
Contributor

Thanks for letting me know. I'll take a look sometime this week and will let you know what I find

@jvandort
Copy link
Contributor

jvandort commented Feb 12, 2023

It seems the problem lies here:

Incompatible because this component declares an API of a component compatible with Java 17 and the consumer needed a runtime of a component compatible with Java 11

Its understandable that this error is hard to decode. We've heard a lot of complaints about the No matching variant of X was found error, for good reason. The error message is very wordy and we have some tentative plans to update the message, though we do not have a concrete timeline to update it.

However, the root of the problem seems to be that the project is java 11, but the newest version of spring is java 17 compatible only. Essentially, gradle is failing to resolve the newest version of spring since a java 11 project is not allowed to depend on java 17 dependencies.

I guess the real question is, what behavior should this plugin have when such a situation occurs? Should the plugin recommend the newest version which is compatible with the current java version? Thinking about that right now, that would seemingly be quite difficult with how things work now. Or, should the plugin recommend the newest version outright even if it is not compatible with the newest version? What is the current behavior?

I have not tested this yet, but you should be able to call JavaPluginExtension#disableAutoTargetJvm to disable the behavior where Gradle attempts to do this Java version detection. However, this could be risky, as it affects the entire gradle project.

Alternatively, you could try to unset the TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE attribute on the configuration being resolved so that we don't do the checking. For context, this is the attribute which the auto-target-jvm functionality is setting itself. Though, looking at the code, simply unsetting this may not work, as the attribute is only set when the configuration is locked from mutation -- which happens during resolution, after the plugin has executed. One option would be to set the attribute value to `0, since the auto-target-jvm functionality does not override a value for the attribute if it has been set already.

Context:

https://github.com/gradle/gradle/blob/da59fbd0db73df45e2c59b2aca7cda83d62bd89f/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/JavaEcosystemSupport.java#L112-L118
https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServices.java#L213-L236

@ben-manes
Copy link
Owner

should the plugin recommend the newest version outright even if it is not compatible with the newest version? What is the current behavior?

I believe that would be the least surprising result, as there are a variety of reasons why incompatibilities might occur (such as JavaEE => Jakarta, e.g Jetty 10 vs 11). A resolution rule can be applied to the report task to restrict expected incompatibilities until resolved.

Do you think that setting attributes, etc. can/should be done in this plugin or is that the responsibility of the consuming project? If the latter, what could we document for those who run into this problem?

@jvandort
Copy link
Contributor

Setting the attribute would be the responsibility of the plugin. You would only want to disable this auto-target-jvm behavior for the configurations that the plugin is resolving. During normal resolution in projects, this check is quite important. Otherwise, it would be very easy for a java 11 project to depend on java 17 bytecode -- which would break when actually running the code on a java 11 jvm.

I would presume it would make sense to add the attribute somewhere in createLatestConfiguration https://github.com/jvandort/gradle-versions-plugin/blob/ce2e5c1d6a264f5e71deb871bf928ddb4190fd6f/gradle-versions-plugin/src/main/kotlin/com/github/benmanes/gradle/versions/updates/Resolver.kt#L89

@mikand13
Copy link

We don't use spring, seeing the same behaviour so this might be a symptom of something else? Works without changing other deps. Only change is 0.44.0 -> 0.45.0.

@ben-manes
Copy link
Owner

Unfortunately using TARGET_JVM_VERSION_ATTRIBUTE, 0 did not work. See info log.

Incompatible because this component declares an API of a component compatible with Java 8 and the consumer needed a runtime of a component compatible with Java 0

Patch
diff --git a/gradle-versions-plugin/src/main/kotlin/com/github/benmanes/gradle/versions/updates/Resolver.kt b/gradle-versions-plugin/src/main/kotlin/com/github/benmanes/gradle/versions/updates/Resolver.kt
index f661a08..8877a40 100644
--- a/gradle-versions-plugin/src/main/kotlin/com/github/benmanes/gradle/versions/updates/Resolver.kt
+++ b/gradle-versions-plugin/src/main/kotlin/com/github/benmanes/gradle/versions/updates/Resolver.kt
@@ -25,6 +25,7 @@ import org.gradle.api.artifacts.repositories.MavenArtifactRepository
 import org.gradle.api.artifacts.result.ResolvedArtifactResult
 import org.gradle.api.attributes.Attribute
 import org.gradle.api.attributes.HasConfigurableAttributes
+import org.gradle.api.attributes.java.TargetJvmVersion;
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependencyConstraint
 import org.gradle.api.specs.Specs.SATISFIES_ALL
@@ -144,6 +145,7 @@ class Resolver(
     copy.dependencies.addAll(latest)
     copy.dependencies.addAll(inherited)
 
+    disableAutoTargetJvm(copy)
     addRevisionFilter(copy, revision)
     addAttributes(copy, configuration)
     addCustomResolutionStrategy(copy, currentCoordinates)
@@ -196,6 +198,12 @@ class Resolver(
     return nonTransitiveDependency
   }
 
+  private fun disableAutoTargetJvm(configuration: Configuration) {
+    // Disable the auto target jvm for the configuration
+    // https://github.com/ben-manes/gradle-versions-plugin/issues/727#issuecomment-1427132589
+    configuration.attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 0);
+  }
+
   /** Adds the attributes from the source to the target. */
   private fun addAttributes(
     target: HasConfigurableAttributes<*>,
@@ -260,6 +268,7 @@ class Resolver(
     val coordinates = hashMapOf<Coordinate.Key, Coordinate>()
     val copy = configuration.copyRecursive().setTransitive(transitive)
 
+    disableAutoTargetJvm(copy)
     val lenient = copy.resolvedConfiguration.lenientConfiguration
 
     val resolved = lenient.getFirstLevelModuleDependencies(SATISFIES_ALL)
diff --git a/gradle.properties b/gradle.properties
index dce12a0..da0df19 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
 GROUP=com.github.ben-manes
-VERSION_NAME=0.45.0
+VERSION_NAME=0.46.0
 
 POM_INCEPTION_YEAR=2012
 POM_PACKAGING=jar

@jvandort
Copy link
Contributor

@ben-manes Try Integer.MAX_VALUE instead of 0. I got mixed up

@jvandort
Copy link
Contributor

@mikand13 Can you post your error here?

@ben-manes
Copy link
Owner

In that case, it doesn't seem to be applied.

Incompatible because this component declares a component compatible with Java 17 and the consumer needed a component compatible with Java 11

info.txt

@jvandort
Copy link
Contributor

Interesting that it works with 0 but not max value. I don't think there's special logic for that but maybe there is. Try like 1000 or soemthing. If not i can investigate further

@ben-manes
Copy link
Owner

Nope, no luck with 1000 either.

ianbrandt added a commit to ianbrandt/gradle-versions-plugin-platform-constraints that referenced this issue Feb 16, 2023
@ianbrandt
Copy link
Author

ianbrandt commented Feb 16, 2023

Thanks all for looking into this. I downgraded to 0.44.0. Other than the noted one for log4j-core, I'm no longer getting the errors. I am still getting upgrade recommendations irrespective of my version range constraints.

Again adding a version alignment ComponentMetadataRule to either my platform build or project convention plugin didn't make a difference. My hypothesis there was that maybe the BOM constraints weren't being considered by the plugin for the individual dependencies, but that the version alignment rule would transitively do the trick.

As for variants, it would be great to get update recommendations constrained by what's applicable to my project. I understand there could be a myriad of different variants to consider, but the Java target version is a very common case. Perhaps that could receive special treatment. Something like the following output would be really handy:

 - org.springframework:spring-context [5.3.24 -> 5.3.25, 6.0.5 (1), 7.0.2 (2)]
(1) Incompatible because this component declares an API of a component compatible with Java 17 and the consumer needed a runtime of a component compatible with Java 11
(2) Incompatible because this component declares an API of a component compatible with Java 21 and the consumer needed a runtime of a component compatible with Java 11

Surely that's a separate feature request. For this issue I'm just hoping to get upgrade recommendations that are constrained by my explicitly specified platform and its imported BOMs, e.g. org.springframework:spring-context [5.3.24 -> 5.3.25] instead of [5.3.24 -> 6.0.5].

@ben-manes
Copy link
Owner

ben-manes commented Feb 16, 2023

My hypothesis there was that maybe the BOM constraints weren't being considered by the plugin for the individual dependencies.

The Spring dependency-management-plugin does not constrain the dynamic query (see #77). Otherwise it pinned the version so upgrades were not seen.

adding a version alignment ComponentMetadataRule to either my platform build or project convention plugin didn't make a difference

I'm unsure and am not familiar enough with all of the Gradle features, interactions, and implementation details. Generally since we use Configuration.copyRecursive(), all of the resolution constraints are carried over and applied. Then additional can be added to the plugin's resolution strategy to customize the report for more restrictions. By letting Gradle handle it, I too would have expected the conventions to be honored. Offhand, we have seen cases where copyRecursive() did not capture some settings and the Gradle team would fix these gaps when reported.

As for variants, it would be great to get update recommendations constrained by what's applicable to my project.

I suspect this would be hard because Gradle performs this logic and returns the resolved configuration, which we inspect to compare the dependency versions. We don't know why a version was rejected, but it is often captured in the info logs for users to debug from. The alternative is to do dependency resolution manually and then trying to emulate features like resolution strategies, constraints, variants, etc. from the maven pom and gradle module metadata. That's how Dependabot and similar work, which is useful but shallow. By letting Gradle handle it then you get more customized resolution and we have less duplication, but it does mean we are beholden to what the platform exposes.

@jvandort
Copy link
Contributor

I have not forgotten about this issue. I plan to take another look at this shortly

@jvandort
Copy link
Contributor

@ben-manes There was a slight issue with your patch. Your call to overwrite the TARGET_JVM_VERSION_ATTRIBUTE in createLatestConfiguration was made too early. And the call to addAttributes later in the method overwrote the value.

Adding copy.attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.MAX_VALUE) on line 150 after the addAttributes(copy, configuration) call (in addition to the change you made in getCurrentCoordinates) solves the problem for me.

For reference, I used the following minimal reproducer based off of the reproducer given in the original post:

        plugins {
          id 'java-library'
          id 'com.github.ben-manes.versions'
        }

        repositories {
            mavenCentral()
        }

        testing {
          suites {
            integrationTest(JvmTestSuite) {
              dependencies {
                implementation(project)
              }
            }
          }
        }

        dependencies {
          implementation("org.springframework.boot:spring-boot:2.7.8")
        }

@ben-manes
Copy link
Owner

oh thank you @jvandort!

@ben-manes
Copy link
Owner

I wasn't able to get the reproducer to work in a minimal project, as Gradle said implementation(project) was invalid (bad parameter). While it would be nice to have a unit test, I'll release it as is. Thanks for all of the help everyone!

@jvandort
Copy link
Contributor

@ben-manes try implementation(project())

@ben-manes
Copy link
Owner

That works, but it does not fail?

------------------------------------------------------------
: Project Dependency Updates (report to plain text file)
------------------------------------------------------------

The following dependencies are using the latest milestone version:
 - com.github.ben-manes:gradle-versions-plugin:0.45.0

The following dependencies have later milestone versions:
 - org.springframework.boot:spring-boot [2.7.8 -> 3.0.2]
     https://spring.io/projects/spring-boot

Gradle release-candidate updates:
 - Gradle: [8.0.1: UP-TO-DATE]

Generated report file build/dependencyUpdates/report.txt

@jvandort
Copy link
Contributor

I thought the whole point of all of this was so it wouldn't fail?

@ben-manes
Copy link
Owner

I thought the reproducer would fail on the current release (v45) and pass on the next one (v46)?

@ben-manes
Copy link
Owner

Released in v46

@ben-manes
Copy link
Owner

I did notice a small quirk in v46 that if I set checkConstraints = true then in Caffeine the resolution fails as below. If using v45 then it resolves fine and is listed as up-to-date.

Failed to determine the latest version for the following dependencies (use --info for details):
 - com.beust:jcommander
     1.82
The exception that is the cause of unresolved state: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve com.beust:jcommander:+.
Required by:
    project :
Caused by: org.gradle.internal.component.NoMatchingConfigurationSelectionException: No matching variant of com.beust:jcommander:1.82 was found. The consumer was configured to find attribute 'org.gradle.jvm.version' with value '2147483647' but:
  - Variant 'apiElements' capability com.beust:jcommander:1.82:
      - Incompatible because this component declares attribute 'org.gradle.jvm.version' with value '8' and the consumer needed attribute 'org.gradle.jvm.version' with value '2147483647'
  - Variant 'runtimeElements' capability com.beust:jcommander:1.82:
      - Incompatible because this component declares attribute 'org.gradle.jvm.version' with value '8' and the consumer needed attribute 'org.gradle.jvm.version' with value '2147483647'

@jvandort
Copy link
Contributor

I thought the reproducer would fail on the current release (v45) and pass on the next one (v46)?

What version of Java are you running gradle with? The reproducer does not specify a toolchain so if you're running with 17+ it should not error in v45

@jvandort
Copy link
Contributor

I'll see what I can figure out about caffeine

@ianbrandt
Copy link
Author

ianbrandt commented Feb 21, 2023

I've upgraded my reproducer to 0.46.0 (and Gradle 8.0.1):

https://github.com/ianbrandt/gradle-versions-plugin-platform-constraints/tree/cda96a1c29c50f326ad8e3e659ad2ff9407b5795.

I'm still getting "Failed to determine the latest version for the following dependencies" for log4j-core, and it's still recommending versions of Spring artifacts beyond my specified BOM constraints. Since this issue has been closed, should I file those as one or two separate issues?

ianbrandt pushed a commit to sdkotlin/sd-kotlin-talks that referenced this issue Feb 21, 2023
@jvandort
Copy link
Contributor

jvandort commented Mar 13, 2023

@ianbrandt The recommending of artifacts beyond your BOM constraints should be a separate issue.

I am looking into the log4j issue currently

@jvandort
Copy link
Contributor

@ianbrandt The log4j problem was present on 0.44.0 before these changes. It is related to trying to resolve the buildscript classpath configuration in the :subprojects project, which has a log4j dependency constraint. Though since the :subprojects project is empty with only a single subproject itself and no other configuration, it has no plugins.

You can either file a separate issue or add a repository to the :subprojects project.

@jvandort
Copy link
Contributor

jvandort commented Mar 13, 2023

@ben-manes If you can provide a short quick reproducer for the caffeine issue you're seeing I can take a look at that

EDIT: Never mind, I got Caffeine to reproduce it.

Apologies for the delay. Things have been busy here recently with multiple major version and patch releases going out the door

@ben-manes
Copy link
Owner

EDIT: Never mind, I got Caffeine to reproduce it.

haha, that's great because I'm having a hard time making a minimal reproduction. I'm sure its something dumb I did in my build. 😄

@jvandort
Copy link
Contributor

jvandort commented Mar 14, 2023

I found the problem. Your root buildscript is doing dependency resolution (caused by dependencyUpdates), but does not apply the jvm-ecosystem plugin which is the plugin which adds the attribute schema compatibility rules which makes a value of Integer.MAX_VALUE for TARGET_JVM_VERSION_ATTRIBUTE compatible with 8.

In general, any project which performs dependeny-resolution against JVM artifact must apply the jvm-ecosystem plugin.

To resolve this, the versions plugin should apply the jvm-ecosystem plugin to the current project.

For context, java-library applies java, which applies java-base, which applies jvm-ecosystem. Usually you do not need to worry about applying this plugin yourself. But, if you're plugin is setting attributes managed by the jvm-ecosystem plugin, of which TARGET_JVM_VERSION_ATTRIBUTE is one of, the plugin must be applied.

@ben-manes
Copy link
Owner

beautiful and thank you! Can you open an issue?

@jvandort
Copy link
Contributor

Will do

@ianbrandt
Copy link
Author

The recommending of artifacts beyond your BOM constraints should be a separate issue.

Filed #755.

@ianbrandt
Copy link
Author

The log4j problem was present on 0.44.0 before these changes. It is related to trying to resolve the buildscript classpath configuration in the :subprojects project, which has a log4j dependency constraint. Though since the :subprojects project is empty with only a single subproject itself and no other configuration, it has no plugins.

You can either file a separate issue or add a repository to the :subprojects project.

Filed #756.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants