gradleosgidependency-managementbnd

Converting a transitive dependency into a compileOnly dependency


I am trying to use OSGI to allow me to use two different versions of a transitive dependency. The plan is that one version (the newer version) will be hidden away inside an OSGI bundle, the other will be on the runtime classpath as normal.

I have built the bundle jar using Gradle (with the Groovy DSL), but the problem is its associated runtime dependencies are wrong - it brings along the newer version, which is supposed to be hidden inside the bundle. This is still true when I do, in the build.gradle file:

compileOnly deps.diffx
runtimeOnly(deps.diffx) {
    exclude group: 'com.propensive', module: 'magnolia_' + versions.scala_v
}

If I examine the dependencies with the Gradle dependencies task, it shows that magnolia is excluded from the runtimeOnly configuration, as expected - but is not excluded from the runtimeClasspath configuration.

If I then use ./gradlew dependencyInsight --dependency magnolia_2.12 --configuration runtime to try to find out where this dependency is coming from, it tells me that the newer version is coming from runtimeClasspath depending on diffx, and this is selected via conflict resolution. Well thanks - I already knew that. The question is, why is my exclusion not being applied to the derived configuration?

Basically I want to do the opposite of this question.

I also tried constraint versions, but they exhibited the same problem:

compileOnly deps.diffx
runtimeOnly(deps.diffx) {
    constraints {
        implementation('com.propensive:magnolia_' + versions.scala_v + ':0.10.0') {
            because 'this version is required by our other dependencies'
        }
    }
}

Solution

  • From the Gradle documentation:

    On the upside, Gradle’s exclude handling is, in contrast to Maven, taking the whole dependency graph into account. So if there are multiple dependencies on a library, excludes are only exercised if all dependencies agree on them. For example, if we add opencsv as another dependency to our project above, which also depends on commons-beanutils, commons-collection is no longer excluded as opencsv itself does not exclude it.

    So instead, we can use a dependency resolve rule:

    def magnoliaVersion = '0.10.0'
    
    configurations.runtimeClasspath {
        resolutionStrategy.eachDependency { DependencyResolveDetails details ->
            if (details.requested.group == 'com.propensive' && details.requested.name.startsWith("magnolia_") && details.requested.version != magnoliaVersion) {
                details.useVersion magnoliaVersion
                details.because 'this version is required by our other dependencies'
            }
        }
    }
    

    and then change the dependency back to a simple, single implementation one:

    implementation deps.diffx
    

    Unfortunately, as the documentation notes, this resolve rule isn't published, so it has to be applied again in any dependent Gradle modules.