Prevent substituting dependency with project

I have a project that builds a java library. The project’s build script uses a plugin that creates a configuration that depends on the artifact built by the project.

When the plugin’s configuration is resolved, this dependency is substituted with the project:

 > gradlew dependencies --configuration myPluginConfig
...
myPluginConfig
+--- org.example:mylib:2.4 -> project : (*)
\--- com.acme:transitive-dependency:3.25.0
...

which I guess is the expected behaviour according to
https://docs.gradle.org/current/userguide/composite_builds.html#included_build_declaring_substitutions

I’m trying to prevent this substitution, but cannot figure out how to do this. The example in the documentation https://docs.gradle.org/current/userguide/composite_builds.html#deactivate_included_build_substitutions, i.e.

configurations.myPluginConfig {
    resolutionStrategy.useGlobalDependencySubstitutionRules = false
    canBeConsumed = false
    attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
}

does not work for me, the dependency on the artifact created by the project is still substituted with the project.

The documentation refers to included builds, and the project is not a composite build, perhaps that is why the above doesn’t work.

Is there another way to prevent the substitution in the main build?

1 Like

Iirc the project is just handled like any other version and the project probably has higher version than 2.4 and thus is used, normal conflict resolution, not dependency substitution.
I think you can only configure to always use the project even if version is not higher but not the other way if I remember correctly.
Maybe you can use a strict version constraint on that configuration to enforce the usage of 2.4. :man_shrugging:

Thanks for the response.

Correct, the project has a higher version than 2.4, so without any other settings the conflict resolution is expected.

Unfortunately, using the strict constraint doesn’t prevent the artifact from being resolved into the project, neither does using resolutionStrategy.forcedModules.

Are you sure you or some plugin is not calling this: ResolutionStrategy (Gradle API 8.10) ?
Because its docs say

Gradle can resolve conflicts purely by version number or prioritize project dependencies over binary. The default is by version number .

Hm, no, I quickly tried and played a bit.

It seems if that configuration is in the project that is substituted, you have no choice, or at least I found no way besides lowering the project version below the published version.

If it is in another project of the build, there you could use all the usual resolutionStrategy tricks, forced version, forced module, substitution rules, eachDependency, …

Thanks for looking into this.

I’ll do some testing with moving the resolution of the configuration to a subproject.

The configuration is used as classpath for the plugin, so one work-around could be to explicitly specify that classpath’s jar files instead of using the configuration to define the classpath.

If the consumer of the plugin should not be able to modify that configuration, the plugin should probably use a detached configuration instead, then I guess this problem would also not appear.

Having such a consumer-space configuration is typically done when the plugin adds some default dependencies but lets the consumer overwrite what is in the configuration, for example to use a different version or fork, and so on.

Yes, the plugin uses a consumer-space configuration for exactly that reason.

I’ll test with a detached configuration and see how that turns out.

Well, maybe some special-casing, so that you use detached configuration in that one project and consumer-space configuration everywhere else. :man_shrugging:

Thanks for the suggestion! Using a detached configuration (not in the plugin, but as a special case in the project that experienced this problem) seems to work.

I ended up with configuring the plugin’s classpath like this:
myPlugin.classpath = configurations.detachedConfiguration(project.dependencies.create('org.example:mylib:2.4'))

and that configuration is resolved without replacing the module dependency with the project.

I first tried this with Gradle 7.6.4, but then got the error

> Could not resolve all task dependencies for configuration ':detachedConfiguration1'.
   > Could not resolve org.example:mylib:2.4.
     Required by:
         project :
      > Project : declares a dependency from configuration 'detachedConfiguration1' to configuration 'default' which is not declared in the descriptor for project :.

Upgrading to Gradle 8.10 solves this problem (it is present in Gradle 8.9)

1 Like