Virtual platforms, publishing, and transitive resolution problems

Using Gradle 5.2.1. I have declared a virtual platform (https://docs.gradle.org/current/userguide/managing_transitive_dependencies.html#sec:virtual_platform) in a build plugin to enforce dependency version alignment for a transitive dependency (jackson) via a declaration like this:

implementation enforcedPlatform('com.fasterxml.jackson:jackson-platform:2.9.8')

I use this build plugin in a java-library project which transitively depends on jackson. This works fine. I have a consumer of this library that includes it via a SNAPSHOT dependency in an internal repo. The virtual platform appears to be part of the published POM of the library, specifically in a dependencyManagement block:

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.fasterxml.jackson</groupId>
        <artifactId>jackson-platform</artifactId>
        <version>2.9.8</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

The build for the consumer fails:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':copyMessageScript'.
> Could not resolve all files for configuration ':runtimeClasspath'.
   > Could not resolve com.myorg.lib-mylib:lib-foo:1.5-SNAPSHOT.
     Required by:
         project :
      > Could not resolve com.myorg.lib-mylib:lib-foo:1.5-SNAPSHOT.
         > Could not parse POM /home/raman/.m2/repository/com/myorg/lib-mylib/lib-foo/1.5-SNAPSHOT/lib-foo-1.5-SNAPSHOT.pom
            > Could not find com.fasterxml.jackson:jackson-platform:2.9.8.
              Searched in the following locations:
                - file:/home/raman/.m2/repository/com/fasterxml/jackson/jackson-platform/2.9.8/jackson-platform-2.9.8.pom
                - file:/home/raman/.m2/repository/com/fasterxml/jackson/jackson-platform/2.9.8/jackson-platform-2.9.8.jar
                - https://nexus.myorg.com/repository/maven-public/com/fasterxml/jackson/jackson-platform/2.9.8/jackson-platform-2.9.8.pom
                - https://nexus.myorg.com/repository/maven-public/com/fasterxml/jackson/jackson-platform/2.9.8/jackson-platform-2.9.8.jar
      > Could not resolve com.myorg.lib-mylib:lib-foo:1.5-SNAPSHOT.
         > Could not parse POM https://nexus.myorg.com/repository/maven-public/com/myorg/lib-mylib/lib-foo/1.5-SNAPSHOT/lib-foo-1.5-20190304.185038-109.pom
            > Could not find com.fasterxml.jackson:jackson-platform:2.9.8.

A workaround appears to be to include the library build as a composite build via includeBuild. However, this is not ideal as these particular projects are not in a mono-repo, and thus not always colocated. Plus, the fact that this works seems to be another bug – should be behavior of the build be this different simply by using a composite build?

Another workaround is given in https://github.com/gradle/gradle/issues/8020#issuecomment-451488606, which is to explicitly remove the platform declaration from the published artifacts:

publishing {
    publications {
        maven(MavenPublication) {
            from components.java
            pom.withXml {
                asNode().dependencyManagement.dependencies.dependency.findAll { node ->
                    node.groupId[0].text().equals('org.test') &&
                    node.artifactId[0].text().equals('platform') &&
                    node.scope[0].text().equals('import')
                }.each { node -> node.replaceNode {} }
            }
        }
    }
}

Some other comments:

The consuming application does also import the same build plugin as the library, and therefore should have a local declaration for the virtual platform, which one might think would resolve the issue.

I see some possibly related issues but nothing exactly like this situation, though one of the workarounds above is from a comment in the first issue here:


The pom.withXml workaround is really messy to do in a Kotlin build plugin also, as its using Groovy XML and duck-typing… found this related issue but argh, I feel like I’m going down a time-wasting rabbit hole here: https://github.com/gradle/kotlin-dsl/issues/225.

UPDATE: Here is the equivalent Kotlin code, which requires the kotlinx.dom dependency:

pom.withXml {
    it.asElement()
    .firstChildElement("dependencyManagement")
    ?.firstChildElement("dependencies")
    ?.elements("dependency")
    ?.filter { dep ->
        dep.firstChildElement("groupId")?.textContent == "com.fasterxml.jackson" &&
        dep.firstChildElement("artifactId")?.textContent == "jackson-platform" &&
        dep.firstChildElement("scope")?.textContent == "import"
    }
    ?.forEach { node -> node.removeFromParent() }
}