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

Allow to omit the version number in target module of a dependency substitution #12459

Open
mauromol opened this issue Mar 6, 2020 · 7 comments
Labels
a:feature A new functionality in:dependency-declarations variant notation attributes capability substitution in:jvm-ecosystem

Comments

@mauromol
Copy link

mauromol commented Mar 6, 2020

Expected Behavior

Using Spring Boot. I wish I could specify this:

configurations.all {
	resolutionStrategy.dependencySubstitution {
		// replace Logback implementation with Log4j2
		substitute module('org.springframework.boot:spring-boot-starter-logging') with module('org.springframework.boot:spring-boot-starter-log4j2')
	}
}

to replace the default Logback Spring Boot starter with the Log4j2 one.

Current Behavior

This produces an error:

Could not run phased build action using Gradle distribution 'https://services.gradle.org/distributions/
 gradle-6.2.2-bin.zip'. Build file 'D:\co\myapp\myapp-core\build.gradle' line: 29 A problem occurred 
 evaluating project ':myapp-core'. Must specify version for target of dependency substitution

I don't want to specify the version of the target module, it's determined by the BOM I'm also referencing in my build script:

dependencies {
	implementation platform('org.springframework.boot:spring-boot-dependencies:2.2.5.RELEASE')
}

So, right now I must use a much more verbose alternative:

configurations.all {
	resolutionStrategy.dependencySubstitution.all { DependencySubstitution dependency ->
		if (dependency.requested instanceof ModuleComponentSelector && dependency.requested.module == 'spring-boot-starter-logging')
			dependency.useTarget('org.springframework.boot:spring-boot-starter-log4j2', 'we use Log4j2 instead of Logback')
	}
}

or use a global exclusion rule for org.springframework.boot:spring-boot-starter-logging in addition to an explicit dependency declaration on org.springframework.boot:spring-boot-starter-log4j2 (which IMHO is desirable anyway).

Context

In Spring Boot it's quite "normal" that some starter dependencies are alternative with each other. Dependency substitution rules sounds like the best way to declare the use of an alternative starter by replacing the default one. Official Spring Boot documentation, right now, suggests the use of exclusion rules, that I know you don't like so much because are poor in semantics. The syntax substitute module with module would be perfect for this, but requiring the target module version is very limiting in this context.

Found this while creating spring-projects/spring-boot#20408.

@mauromol
Copy link
Author

mauromol commented Jun 24, 2020

By the way, the workaround I found seems to have its own problems. In fact, even though I can setup my Eclipse project with Buildship successfully, when Gradle 6.5 tries to resolve the runtimeClasspath configuration (for instance when I run the build task), it returns the following error:

./gradlew myproject:dependencyInsight --dependency spring-boot-starter-logging --configuration runtimeClasspath

> Task :myproject:dependencyInsight
org.springframework.boot:spring-boot-starter-logging:2.3.1.RELEASE (by constraint) FAILED
   Failures:
      - Could not resolve org.springframework.boot:spring-boot-starter-logging:2.3.1.RELEASE.
          - Invalid format: 'org.springframework.boot:spring-boot-starter-log4j2'. Group, name and version cannot be empty. Correct example: 'org.gradle:gradle-core:1.0'

org.springframework.boot:spring-boot-starter-logging:2.3.1.RELEASE FAILED
[...]

So, it seems like version is needed in any case, although there would be a dependency constraint that should give it the hint of which version should be used, even for the replacement module org.springframework.boot:spring-boot-starter-log4j2:

implementation platform('org.springframework.boot:spring-boot-dependencies:2.3.1.RELEASE')

In this specific case, I should be able to easily solve because the version of the dependency I want to substitute is the same of the one I want to use, but in general it may not be the case.

@ianbrandt
Copy link

As a use case for this, I thought I could use the new withClassifier(String) API to workaround #8561, as hinted at in the docs:

Alternatively, it’s possible to select a dependency with a specific classifier or, for more specific use cases, substitute with a very specific artifact (type, extension and classifier).

The idea would be to append a ZIP classifier to a dependency with its version still being specified in a platform BOM. Unless I'm missing something, that's a no-go with the inability to omit the version for dependency substitution rules.

@jiasonwang
Copy link

try to use version "0.0.0"

configurations.all {
	resolutionStrategy.dependencySubstitution {
		// replace Logback implementation with Log4j2
		substitute module('org.springframework.boot:spring-boot-starter-logging') with module('org.springframework.boot:spring-boot-starter-log4j2:0.0.0')
	}
}

@stale
Copy link

stale bot commented Apr 21, 2022

This issue has been automatically marked as stale because it has not had recent activity. Given the limited bandwidth of the team, it will be automatically closed if no further activity occurs. If you're interested in how we try to keep the backlog in a healthy state, please read our blog post on how we refine our backlog. If you feel this is something you could contribute, please have a look at our Contributor Guide. Thank you for your contribution.

@stale stale bot added the stale label Apr 21, 2022
@mauromol
Copy link
Author

This is still desirable.

@stale stale bot removed the stale label Apr 21, 2022
@ins1der1211
Copy link

I've used "*" wildcard as version and it worked out.

@yzhaoa
Copy link

yzhaoa commented Mar 13, 2024

As of Gradle version 7.4, Neither "0.0.0" nor "*" work correctly to resolve a version through platform (BOM).

Both "0.0.0" and "*" will cause Gradle to request either the versions "0.0.0" or literally "*" from Maven.

A functional workaround would be to perform a manual version resolution using the BOM inside of a resolutionStrategy.eachDependency closure, e.g.

configurations.named("implementation").configure {
  resolutionStrategy.eachDependency {
    if (requested.group == "org.springframework.boot" and requested.name == "spring-boot-starter-logging") {
      def resolver = project.detachedConfiguration(
        project.dependencies.platform('org.springframework.boot:spring-boot-dependencies:2.2.5.RELEASE'),
        project.dependencies.create('org.springframework.boot:spring-boot-starter-log4j2')
      )
      def resolvedName = resolver.resolvedConfiguration.firstLevelModuleDependencies.last().name
      useTarget(resolvedName)
    }
  }
}

@big-guy big-guy added the in:dependency-declarations variant notation attributes capability substitution label May 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:feature A new functionality in:dependency-declarations variant notation attributes capability substitution in:jvm-ecosystem
Projects
None yet
Development

No branches or pull requests

8 participants