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

Baseline task reports a failure due to a circular resolution #6445

Open
ben-manes opened this issue Jan 25, 2025 · 3 comments
Open

Baseline task reports a failure due to a circular resolution #6445

ben-manes opened this issue Jan 25, 2025 · 3 comments

Comments

@ben-manes
Copy link

I noticed in Gradle's Problem Report this error, which I have narrowed down to coming from bnd.

Image

When using --debug the error is from the baseline task trying to resolve the project itself as a dependency.

2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Realize task :caffeine:baseline' started
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Execute container callback action' started
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Execute container callback action'
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Execute container callback action' completed
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Realize task :caffeine:baseline'
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Realize task :caffeine:baseline' completed
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.api.Task] Searching for default baseline com.github.ben-manes.caffeine:caffeine:(0,3.2.1-SNAPSHOT[
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Resolve dependencies of :caffeine:detachedConfiguration1' started
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.modulecache.ResolvedArtifactCaches] Reusing in-memory cache for repo 'MavenRepo' [26c913274550a0b2221f47a0fe2d2358].
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.DependencyGraphBuilder] Visiting configuration com.github.ben-manes.caffeine:caffeine:3.2.1-SNAPSHOT(detachedConfiguration1).
2025-01-25T13:09:44.440-0800 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DynamicVersionResolver] Attempting to resolve version for com.github.ben-manes.caffeine:caffeine:(0,3.2.1-SNAPSHOT[ using repositories [MavenRepo]
2025-01-25T13:09:44.441-0800 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.CachingModuleComponentRepository] Using cached module metadata for module 'com.github.ben-manes.caffeine:caffeine:3.2.0' in 'MavenRepo'
2025-01-25T13:09:44.441-0800 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DynamicVersionResolver] Using com.github.ben-manes.caffeine:caffeine:3.2.0 from Maven repository 'MavenRepo'
2025-01-25T13:09:44.441-0800 [INFO] [org.gradle.internal.component.resolution.failure.exception.AbstractResolutionFailureException] Variant Selection Exception: org.gradle.internal.component.resolution.failure.exception.VariantSelectionByAttributesException caused by Resolution Failure: org.gradle.internal.component.resolution.failure.type.NoCompatibleVariantsFailure
2025-01-25T13:09:44.441-0800 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResultsBuilder] Flushing resolved configuration data in Binary store in /Users/ben/.gradle/.tmp/gradle10705468451962382484.bin. Wrote root 4236.
2025-01-25T13:09:44.441-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Resolve dependencies of :caffeine:detachedConfiguration1'
2025-01-25T13:09:44.441-0800 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Resolve dependencies of :caffeine:detachedConfiguration1' completed
2025-01-25T13:09:44.441-0800 [DEBUG] [org.gradle.api.Task] Baseline configuration resolve error org.gradle.api.internal.artifacts.ivyservice.TypedResolveException: Could not resolve all dependencies for configuration ':caffeine:detachedConfiguration1'., adding /Users/ben/projects/caffeine/caffeine/build/libs/caffeine-3.2.1-SNAPSHOT.jar as baseline
org.gradle.api.internal.artifacts.ivyservice.TypedResolveException: Could not resolve all dependencies for configuration ':caffeine:detachedConfiguration1'.
        at org.gradle.api.internal.artifacts.ResolveExceptionMapper.mapFailure(ResolveExceptionMapper.java:68)
        at org.gradle.api.internal.artifacts.ResolveExceptionMapper.mapFailures(ResolveExceptionMapper.java:60)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$DefaultResolutionHost.consolidateFailures(DefaultConfiguration.java:1974)
        at org.gradle.api.internal.artifacts.configurations.ResolutionHost.rethrowFailuresAndReportProblems(ResolutionHost.java:75)
        at org.gradle.api.internal.artifacts.ivyservice.DefaultResolvedConfiguration.rethrowFailure(DefaultResolvedConfiguration.java:66)
        at aQute.bnd.gradle.BndBuilderPlugin.lambda$apply$5(BndBuilderPlugin.java:119)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.lambda$defaultDependencies$1(DefaultConfiguration.java:492)
        at org.gradle.internal.ImmutableActionSet$SingletonSet.execute(ImmutableActionSet.java:225)
        ...
Caused by: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve com.github.ben-manes.caffeine:caffeine:(0,3.2.1-SNAPSHOT[.
Required by:
    project :caffeine
Caused by: org.gradle.internal.component.resolution.failure.exception.VariantSelectionByAttributesException: Unable to find a matching variant of project :caffeine:
  - No variants exist.
        at org.gradle.internal.component.resolution.failure.describer.NoCompatibleVariantsFailureDescriber.describeFailure(NoCompatibleVariantsFailureDescriber.java:58)
        at org.gradle.internal.component.resolution.failure.describer.NoCompatibleVariantsFailureDescriber.describeFailure(NoCompatibleVariantsFailureDescriber.java:37)
        at org.gradle.internal.component.resolution.failure.ResolutionFailureHandler.lambda$describeFailure$3(ResolutionFailureHandler.java:275)
        at java.base@21/java.util.Optional.map(Optional.java:260)
        at org.gradle.internal.component.resolution.failure.ResolutionFailureHandler.describeFailure(ResolutionFailureHandler.java:275)
        at org.gradle.internal.component.resolution.failure.ResolutionFailureHandler.noVariantsFailure(ResolutionFailureHandler.java:160)
        at org.gradle.internal.component.model.GraphVariantSelector.selectLegacyVariant(GraphVariantSelector.java:167)
        at org.gradle.internal.component.model.LocalComponentDependencyMetadata.selectVariants(LocalComponentDependencyMetadata.java:121)
        at org.gradle.internal.component.model.DelegatingDependencyMetadata.selectVariants(DelegatingDependencyMetadata.java:46)
        at org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.EdgeState.calculateTargetNodes(EdgeState.java:257)
        at org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.EdgeState.attachToTargetNodes(EdgeState.java:148)
        at org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.DependencyGraphBuilder.attachToTargetRevisionsSerially(DependencyGraphBuilder.java:387)
        at org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.DependencyGraphBuilder.resolveEdges(DependencyGraphBuilder.java:277)
        at org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.DependencyGraphBuilder.traverseGraph(DependencyGraphBuilder.java:202)
        at org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.DependencyGraphBuilder.resolve(DependencyGraphBuilder.java:161)
        at org.gradle.api.internal.artifacts.ivyservice.resolveengine.DependencyGraphResolver.resolve(DependencyGraphResolver.java:117)
        at org.gradle.api.internal.artifacts.ivyservice.ResolutionExecutor.doResolve(ResolutionExecutor.java:519)
        at org.gradle.api.internal.artifacts.ivyservice.ResolutionExecutor.resolveGraph(ResolutionExecutor.java:374)
        at org.gradle.api.internal.artifacts.ivyservice.ShortCircuitingResolutionExecutor.resolveGraph(ShortCircuitingResolutionExecutor.java:96)
        at org.gradle.api.internal.artifacts.ivyservice.DefaultConfigurationResolver.resolveGraph(DefaultConfigurationResolver.java:105)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$1.call(DefaultConfiguration.java:777)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$1.call(DefaultConfiguration.java:769)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveGraphInBuildOperation(DefaultConfiguration.java:769)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.lambda$resolveExclusivelyIfRequired$5(DefaultConfiguration.java:761)
        at org.gradle.api.internal.project.DefaultProjectStateRegistry$CalculatedModelValueImpl.update(DefaultProjectStateRegistry.java:509)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveExclusivelyIfRequired(DefaultConfiguration.java:756)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveGraphIfRequired(DefaultConfiguration.java:749)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.access$900(DefaultConfiguration.java:159)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ResolverResultsResolutionResultProvider.getValue(DefaultConfiguration.java:722)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ResolverResultsResolutionResultProvider.getValue(DefaultConfiguration.java:708)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.getResolvedConfiguration(DefaultConfiguration.java:662)
        at org.gradle.api.internal.artifacts.configurations.DefaultUnlockedConfiguration_Decorated.getResolvedConfiguration(Unknown Source)
        at aQute.bnd.gradle.BndBuilderPlugin.lambda$apply$5(BndBuilderPlugin.java:118)
        at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.lambda$defaultDependencies$1(DefaultConfiguration.java:492)
        ...

The detachedConfiguration appears to be run before the task is executed but rather when it is being added to the execution graph ("realizing" the task). The error is ignored at configuration time and, due to the jar task dependency, at execution time this works fine. I believe the problem is your usage of defaultDependencies where you try to resolve the project prematurely. Typically a default dependency is for an external tool version and letting the user override that, e.g. if a 3rd party plugin for a tool defaults to an older version because it is not released in tandem. I'm unsure why the defaults would be needed here, rather than resolving them as part of your task execution. Or maybe at least filter out the current project itself? It just doesn't make sense to me to have circular logic where the baseline task depends on the project's release jar in order to build the release jar.

baseline.defaultDependencies(deps -> {
Baseline task = unwrap(baselineTask);
Jar bundleTask = task.getBundleTask();
if (Objects.nonNull(bundleTask)) {
String archiveBaseName = unwrap(bundleTask.getArchiveBaseName());
String archiveVersion = unwrapOptional(bundleTask.getArchiveVersion()).orElse(null);
String group = project.getGroup()
.toString();
task.getLogger()
.debug("Searching for default baseline {}:{}:(0,{}[", group, archiveBaseName, archiveVersion);
Dependency baselineDep = project.getDependencies()
.create(Maps.of("group", group, "name", archiveBaseName));
((ExternalDependency) baselineDep)
.version(mvc -> mvc.strictly(String.format("(0,%s[", archiveVersion)));
((ExternalDependency) baselineDep).setTransitive(false);
try {
Configuration detached = project.getConfigurations()
.detachedConfiguration(baselineDep);
detached.getResolvedConfiguration()
.rethrowFailure();
} catch (ResolveException e) {
task.getLogger()
.debug("Baseline configuration resolve error {}, adding {} as baseline", e,
unwrapFile(task.getBundle()), e);
baselineDep = project.getDependencies()
.create(project.getObjects()
.fileCollection()
.from(task.getBundle()));
}
deps.add(baselineDep);
}
});

@ben-manes
Copy link
Author

Thinking about this and the JavaDoc shows a misunderstanding which I have encountered before.

* This plugin also defines a "baseline" configuration and a baseline task of
* type {@link Baseline}. The baseline task will be set up with the default of
* baselining the output of the jar task using the baseline configuration. The
* baseline configuration default dependency will use the prior version of the
* jar.

For some reason, it tries to add the current project as a default dependency, expecting to obtain the previous release's jar as an artifact to resolve against. However, since this is a dependency within the gradle subproject that produces that maven coordinate, Gradle rewrites it to a project dependency. The exact version is ignored since Gradle always selects the most recent version in the dependency set, which is the current project. Therefore the variant selection fails because it cannot satisfy that version range with the project dependency.

You might assume this would not happen on the build plugin phase, since the compile phase should be independent from the project's own dependency set. That sadly is not the case, as I found with the ErrorProne static analyzer which replaces the java compiler with its extension. As that library depends on Caffeine for its AST cache, this is rewritten to a project dependency and causes a failure since Caffeine cannot be compiled without having itself be built for the compiler. The workaround, as acknowledged by the Gradle team, is to exclude the dependency and instead put an opaque file dependency by downloading the jar manually to satisfy it at execution time. Therefore, Caffeine includes a download phase to bootstrap the javac compiler with the previous version.

I believe a similar issue is happening here by believing that the current project's buildScript phase can resolve the prior version. It won't and will transparently be rewritten to a project dependency, and then fail the strictly constraint. When it fails, presumably the baseline just can't leverage the prior version as an optimization. The simplest and correct solution would be to remove this optimization attempt and not depend on a prior release.

@pkriens
Copy link
Member

pkriens commented Jan 30, 2025

You have any concrete advice how to make this work for you?

@ben-manes
Copy link
Author

Yes, I believe the defaultDependencies can never succeed and does not help this plugin. If removed it would avoid an error appearing in the problem report.

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

2 participants