diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependency.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependency.java index fb6acbd5226a..2cbfae2be9d6 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependency.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependency.java @@ -36,7 +36,7 @@ import javax.annotation.Nullable; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -49,7 +49,7 @@ public abstract class AbstractModuleDependency extends AbstractDependency implem private ImmutableAttributesFactory attributesFactory; private NotationParser capabilityNotationParser; private DefaultExcludeRuleContainer excludeRuleContainer = new DefaultExcludeRuleContainer(); - private Set artifacts = new HashSet(); + private Set artifacts = new LinkedHashSet<>(); private ImmutableActionSet onMutate = ImmutableActionSet.empty(); private AttributeContainerInternal attributes; private ModuleDependencyCapabilitiesInternal moduleDependencyCapabilities; @@ -142,7 +142,7 @@ public DependencyArtifact artifact(Action configureA protected void copyTo(AbstractModuleDependency target) { super.copyTo(target); - target.setArtifacts(new HashSet(getArtifacts())); + target.setArtifacts(new LinkedHashSet<>(getArtifacts())); target.setExcludeRuleContainer(new DefaultExcludeRuleContainer(getExcludeRules())); target.setTransitive(isTransitive()); if (attributes != null) { diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/ClientModuleDependencySpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/ClientModuleDependencySpec.groovy index 25b49e836845..edd9e081c7da 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/ClientModuleDependencySpec.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/ClientModuleDependencySpec.groovy @@ -27,14 +27,14 @@ class ClientModuleDependencySpec extends AbstractModuleDependencySpec { new DefaultClientModule(group, name, version, configuration) } - def "equals but content not equal with different module dependencies"() { + def "not equal with different module dependencies"() { when: def dep1 = createDependency("group1", "name1", "version1", null) def dep2 = createDependency("group1", "name1", "version1", null) dep2.addDependency(Mock(ModuleDependency)) then: - dep1 == dep2 + dep1 != dep2 !dep1.contentEquals(dep2) !dep2.contentEquals(dep1) } diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/override/ComponentOverrideMetadataResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/override/ComponentOverrideMetadataResolveIntegrationTest.groovy new file mode 100644 index 000000000000..03fbc41aaf61 --- /dev/null +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/override/ComponentOverrideMetadataResolveIntegrationTest.groovy @@ -0,0 +1,252 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradle.integtests.resolve.override + +import org.gradle.integtests.fixtures.GradleMetadataResolveRunner +import org.gradle.integtests.fixtures.RequiredFeature +import org.gradle.integtests.fixtures.RequiredFeatures +import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest +import spock.lang.Unroll + +/** + * There is more test coverage for 'dependency artifacts' in ArtifactDependenciesIntegrationTest (old test setup). + */ +@RequiredFeatures( + // This test bypasses all metadata using 'artifact()' metadata sources. It is sufficient to test with one metadata setup. + @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true") +) +class ComponentOverrideMetadataResolveIntegrationTest extends AbstractModuleDependencyResolveTest { + + def "can combine artifact notation and constraints"() { + resolve.expectDefaultConfiguration(useMaven() ? 'runtime' : 'default') + + given: + repository { + 'org:foo:1.0' { + withModule { + undeclaredArtifact(type: 'distribution-tgz') + } + } + } + + buildFile << """ + repositories.all { + metadataSources { artifact() } + } + dependencies { + conf('org:foo@distribution-tgz') + + constraints { + conf('org:foo:1.0') + } + } + """ + + when: + repositoryInteractions { + 'org:foo:1.0' { + // expectHeadArtifact(type: 'distribution-tgz') <- head request can happen once or twice depending on timing + maybeHeadOrGetArtifact(type: 'distribution-tgz') + } + } + succeeds 'checkDeps' + + then: + resolve.expectGraph { + root(":", ":test:") { + edge('org:foo', 'org:foo:1.0') { + artifact(type: 'distribution-tgz') + } + constraint('org:foo:1.0') + } + } + } + + @Unroll + def "The first artifact is used as replacement for metadata if multiple artifacts are declared using #declaration"() { + resolve.expectDefaultConfiguration(useMaven() ? 'runtime' : 'default') + + given: + repository { + 'org:foo:1.0' { + withModule { + undeclaredArtifact(name: artifactName, type: 'distribution-tgz') + undeclaredArtifact(name: artifactName, type: 'zip') + } + } + } + + buildFile << """ + repositories.all { + metadataSources { artifact() } + } + dependencies { + $declaration + constraints { + conf('org:foo:1.0') + } + } + """ + + when: + repositoryInteractions { + 'org:foo:1.0' { + // HEAD requests happen in parallel when Gradle downloads metadata. + // In this cases "downloading metadata" means testing for the artifact. + // Each declaration is treated separately with it's own "consumer provided" metadata + // Depending on the timing, this can lead to multiple parallel requests (one for each declaration) + maybeHeadOrGetArtifact(name: artifactName, type: 'distribution-tgz') + expectGetArtifact(name: artifactName, type: 'zip') + } + } + succeeds 'checkDeps' + + then: + resolve.expectGraph { + root(":", ":test:") { + edge('org:foo', 'org:foo:1.0') { + artifact(name: artifactName, type: 'distribution-tgz') + artifact(name: artifactName, type: 'zip') + } + constraint('org:foo:1.0') + } + } + + where: + notation | artifactName | declaration + 'multiple dependency declarations (AT notation)' | 'foo' | "conf('org:foo@distribution-tgz'); conf('org:foo@zip')" + 'multiple dependency declarations (artifact notation)' | 'bar' | "conf('org:foo') { artifact { name = 'bar'; type = 'distribution-tgz' } }; conf('org:foo') { artifact { name = 'bar'; type = 'zip' } }" + 'multiple artifact declarations' | 'bar' | "conf('org:foo') { artifact { name = 'bar'; type = 'distribution-tgz' }; artifact { name = 'bar'; type = 'zip' } }" + } + + @Unroll + def "client module for version 1.0 is selected over other dependency with version #otherVersion"() { + resolve.expectDefaultConfiguration('default') + + given: + repository { + 'org:foo:0.9' { + dependsOn 'org:bar:1.0' + } + 'org:foo:1.0' { + dependsOn 'org:bar:1.0' + } + 'org:bar:1.0'() + } + + buildFile << """ + dependencies { + conf 'org:foo:$otherVersion' + conf module('org:foo:1.0') { + // no dependencies + } + } + + """ + + when: + repositoryInteractions { + 'org:foo:1.0' { + expectResolve() + } + } + succeeds 'checkDeps' + + then: + def notSelectedModule = "org:foo:$otherVersion" + resolve.expectGraph { + root(":", ":test:") { + module('org:foo:1.0') + if (notSelectedModule != 'org:foo:1.0') { + edge(notSelectedModule, 'org:foo:1.0') + } + } + } + + where: + otherVersion << ['0.9', '1.0'] + } + + def "client module for a not selected version or range is ignored"() { + given: + repository { + 'org:foo:1.1' { + dependsOn 'org:bar:1.0' + } + 'org:foo:1.0' { + dependsOn 'org:bar:1.0' + } + 'org:bar:1.0'() + } + + buildFile << """ + dependencies { + conf 'org:foo:1.1' + conf module('org:foo:[1.0,1.1]') { + // no dependencies + } + conf module('org:foo:1.0') { + // no dependencies + } + } + """ + + when: + repositoryInteractions { + 'org:foo:1.1' { + expectResolve() + } + 'org:bar:1.0' { + expectResolve() + } + } + succeeds 'checkDeps' + + then: + resolve.expectGraph { + root(":", ":test:") { + module('org:foo:1.1') { + module('org:bar:1.0') + } + edge('org:foo:[1.0,1.1]', 'org:foo:1.1') + edge('org:foo:1.0', 'org:foo:1.1') + } + } + } + + def "clashing client modules fail the build"() { + given: + buildFile << """ + configurations { + conf + } + dependencies { + conf module('org:foo:1.0') { + } + conf module('org:foo:1.0') { + dependency("org:baz:1.0") + } + } + """ + + when: + fails 'checkDeps' + + then: + failure.assertHasCause("org:foo:1.0 has more than one client module definitions.") + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/AbstractExternalModuleDependency.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/AbstractExternalModuleDependency.java index 9221dca7a584..62f1c8cb5abc 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/AbstractExternalModuleDependency.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/AbstractExternalModuleDependency.java @@ -49,7 +49,7 @@ protected void copyTo(AbstractExternalModuleDependency target) { } protected boolean isContentEqualsFor(ExternalModuleDependency dependencyRhs) { - if (!isKeyEquals(dependencyRhs) || !isCommonContentEquals(dependencyRhs)) { + if (!isCommonContentEquals(dependencyRhs)) { return false; } return force == dependencyRhs.isForce() && changing == dependencyRhs.isChanging(); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultClientModule.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultClientModule.java index acd01cc71cce..28b816195c4f 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultClientModule.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultClientModule.java @@ -64,6 +64,19 @@ public ClientModule copy() { return copiedClientModule; } + @Override + public boolean equals(Object o) { + if (!(o instanceof Dependency)) { + return false; + } + return contentEquals((Dependency) o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + @Override public boolean contentEquals(Dependency dependency) { if (this == dependency) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java index d4edecbf4869..35d160445902 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java @@ -41,6 +41,7 @@ import org.gradle.internal.component.external.model.ModuleDependencyMetadata; import org.gradle.internal.component.model.DefaultComponentOverrideMetadata; import org.gradle.internal.component.model.DependencyMetadata; +import org.gradle.internal.component.model.IvyArtifactName; import org.gradle.internal.resolve.ModuleVersionNotFoundException; import org.gradle.internal.resolve.ModuleVersionResolveException; import org.gradle.internal.resolve.RejectedByAttributesVersion; @@ -477,7 +478,8 @@ public CachePolicy getCachePolicy() { private void process(ModuleComponentRepositoryAccess access, DefaultBuildableModuleComponentMetaDataResolveResult result) { DependencyMetadata dependency = dependencyMetadata.withRequestedVersion(new DefaultImmutableVersionConstraint(version.getSource())); - access.resolveComponentMetaData(identifier, DefaultComponentOverrideMetadata.forDependency(dependency), result); + IvyArtifactName firstArtifact = dependency.getArtifacts().isEmpty() ? null : dependency.getArtifacts().get(0); + access.resolveComponentMetaData(identifier, DefaultComponentOverrideMetadata.forDependency(dependency.isChanging(), firstArtifact, DefaultComponentOverrideMetadata.extractClientModule(dependency)), result); attemptCollector.execute(result); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolutionState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolutionState.java index 8111daa1c59c..cad3549266a0 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolutionState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolutionState.java @@ -19,7 +19,6 @@ import org.gradle.api.artifacts.component.ComponentIdentifier; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.StringVersioned; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.VirtualPlatformState; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ResolvableSelectorState; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorInternal; import org.gradle.internal.component.model.ComponentResolveMetadata; @@ -31,8 +30,6 @@ public interface ComponentResolutionState extends StringVersioned { ModuleVersionIdentifier getId(); - void selectedBy(ResolvableSelectorState selectorState); - /** * Returns the meta-data for the component. Resolves if not already resolved. * diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ComponentState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ComponentState.java index cce3a617eb4f..a733a72620ac 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ComponentState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ComponentState.java @@ -28,7 +28,6 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphComponent; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.VersionConflictResolutionDetails; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ResolvableSelectorState; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorInternal; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasonInternal; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons; @@ -66,7 +65,7 @@ public class ComponentState implements ComponentResolutionState, DependencyGraph private ComponentSelectionState state = ComponentSelectionState.Selectable; private ModuleVersionResolveException metadataResolveFailure; - private SelectorState firstSelectedBy; + private ModuleSelectors selectors; private DependencyGraphBuilder.VisitState visitState = DependencyGraphBuilder.VisitState.NotSeen; private boolean rejected; @@ -165,11 +164,8 @@ public void restartIncomingEdges(ComponentState selected) { } } - @Override - public void selectedBy(ResolvableSelectorState selectorState) { - if (firstSelectedBy == null) { - firstSelectedBy = (SelectorState) selectorState; - } + public void setSelectors(ModuleSelectors selectors) { + this.selectors = selectors; } /** @@ -186,9 +182,15 @@ public void resolve() { return; } - // Any metadata overrides (e.g classifier/artifacts/client-module) will be taken from the first dependency that referenced this component - ComponentOverrideMetadata componentOverrideMetadata = DefaultComponentOverrideMetadata.forDependency(firstSelectedBy.getDependencyMetadata()); - + ComponentOverrideMetadata componentOverrideMetadata; + if (selectors != null && selectors.size() > 0) { + // Taking the first selector here to determine the 'changing' status and 'client module' is our best bet to get the selector that will most likely be chosen in the end. + // As selectors are sorted accordingly (see ModuleSelectors.SELECTOR_COMPARATOR). + SelectorState firstSelector = selectors.first(); + componentOverrideMetadata = DefaultComponentOverrideMetadata.forDependency(firstSelector.isChanging(), selectors.getFirstDependencyArtifact(), firstSelector.getClientModule()); + } else { + componentOverrideMetadata = DefaultComponentOverrideMetadata.EMPTY; + } DefaultBuildableComponentResolveResult result = new DefaultBuildableComponentResolveResult(); if (tryResolveVirtualPlatform()) { return; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleResolveState.java index a8aed8770094..37b008efc240 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleResolveState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleResolveState.java @@ -370,6 +370,7 @@ public boolean maybeUpdateSelection() { return false; } ComponentState newSelected = selectorStateResolver.selectBest(getId(), selectors); + newSelected.setSelectors(selectors); if (selected == null) { select(newSelected); return true; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectors.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectors.java index cc438a07f1ce..aa81a6a6054b 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectors.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectors.java @@ -26,6 +26,7 @@ import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ResolvableSelectorState; import org.gradle.internal.Cast; +import org.gradle.internal.component.model.IvyArtifactName; import java.util.Collections; import java.util.Comparator; @@ -172,4 +173,13 @@ public T first() { } return selectors.get(0); } + + public IvyArtifactName getFirstDependencyArtifact() { + for (T selector: selectors) { + if (selector.getFirstDependencyArtifact() != null) { + return selector.getFirstDependencyArtifact(); + } + } + return null; + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/PotentialEdge.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/PotentialEdge.java index 598854b399ad..623db9fc9ab3 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/PotentialEdge.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/PotentialEdge.java @@ -54,8 +54,6 @@ static PotentialEdge of(ResolveState resolveState, NodeState from, ModuleCompone edge.computeSelector(); ModuleVersionIdentifier toModuleVersionId = DefaultModuleVersionIdentifier.newId(toSelector.getModuleIdentifier(), toSelector.getVersion()); ComponentState version = resolveState.getModule(toSelector.getModuleIdentifier()).getVersion(toModuleVersionId, toComponent); - SelectorState selector = edge.getSelector(); - version.selectedBy(selector); // We need to check if the target version exists. For this, we have to try to get metadata for the aligned version. // If it's there, it means we can align, otherwise, we must NOT add the edge, or resolution would fail ComponentResolveMetadata metadata = version.getMetadata(); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/SelectorState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/SelectorState.java index 01d33fc4e750..958fdbf21635 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/SelectorState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/SelectorState.java @@ -19,6 +19,8 @@ import com.google.common.base.Joiner; import com.google.common.collect.Lists; import org.gradle.api.Describable; +import org.gradle.api.InvalidUserDataException; +import org.gradle.api.artifacts.ClientModule; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.component.ComponentSelector; import org.gradle.api.artifacts.component.ProjectComponentSelector; @@ -32,7 +34,9 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasonInternal; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons; import org.gradle.api.internal.attributes.AttributeDesugaring; +import org.gradle.internal.component.model.DefaultComponentOverrideMetadata; import org.gradle.internal.component.model.DependencyMetadata; +import org.gradle.internal.component.model.IvyArtifactName; import org.gradle.internal.logging.text.TreeFormatter; import org.gradle.internal.resolve.ModuleVersionResolveException; import org.gradle.internal.resolve.RejectedByAttributesVersion; @@ -59,7 +63,6 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState { private final Long id; private final DependencyState dependencyState; - private final DependencyMetadata firstSeenDependency; private final DependencyToComponentIdResolver resolver; private final ResolvedVersionConstraint versionConstraint; private final List dependencyReasons = Lists.newArrayListWithExpectedSize(4); @@ -75,6 +78,11 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState private boolean softForced; private boolean fromLock; + // The following state needs to be tracked to consistently construct `ComponentOverrideMetadata` independent of the order dependencies are visited + private IvyArtifactName firstDependencyArtifact; + private ClientModule clientModule; + private boolean changing; + // An internal counter used to track the number of outgoing edges // that use this selector. Since a module resolve state tracks all selectors // for this module, when considering selectors that need to be used when @@ -92,10 +100,9 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState } update(dependencyState); this.dependencyState = dependencyState; - this.firstSeenDependency = dependencyState.getDependency(); this.versionConstraint = versionByAncestor ? resolveState.resolveVersionConstraint(DefaultImmutableVersionConstraint.of()) : - resolveState.resolveVersionConstraint(firstSeenDependency.getSelector()); + resolveState.resolveVersionConstraint(dependencyState.getDependency().getSelector()); this.isProjectSelector = getSelector() instanceof ProjectComponentSelector; this.attributeDesugaring = resolveState.getAttributeDesugaring(); } @@ -133,7 +140,7 @@ public Long getResultId() { @Override public String toString() { - return firstSeenDependency.toString(); + return dependencyState.getDependency().toString(); } @Override @@ -181,7 +188,7 @@ private ComponentIdResolveResult resolve(VersionSelector selector, VersionSelect if (dependencyState.failure != null) { idResolveResult.failed(dependencyState.failure); } else { - resolver.resolve(firstSeenDependency, selector, rejector, idResolveResult); + resolver.resolve(dependencyState.getDependency(), selector, rejector, idResolveResult); } if (idResolveResult.getFailure() != null) { @@ -291,7 +298,22 @@ public void addReasonsForSelector(ComponentSelectionReasonInternal selectionReas } public DependencyMetadata getDependencyMetadata() { - return firstSeenDependency; + return dependencyState.getDependency(); + } + + @Override + public IvyArtifactName getFirstDependencyArtifact() { + return firstDependencyArtifact; + } + + @Override + public ClientModule getClientModule() { + return clientModule; + } + + @Override + public boolean isChanging() { + return changing; } @Override @@ -338,7 +360,26 @@ public void update(DependencyState dependencyState) { resolved = false; // when a selector changes from non lock to lock, we must reselect } dependencyState.addSelectionReasons(dependencyReasons); + trackDetailsForOverrideMetadata(dependencyState); + } + } + + private void trackDetailsForOverrideMetadata(DependencyState dependencyState) { + if (firstDependencyArtifact == null) { + List artifacts = dependencyState.getDependency().getArtifacts(); + if (!artifacts.isEmpty()) { + firstDependencyArtifact = artifacts.get(0); + } + } + ClientModule nextClientModule = DefaultComponentOverrideMetadata.extractClientModule(dependencyState.getDependency()); + if (nextClientModule != null && !nextClientModule.equals(clientModule)) { + if (clientModule == null) { + clientModule = nextClientModule; + } else { + throw new InvalidUserDataException(dependencyState.getDependency().getSelector().getDisplayName() + " has more than one client module definitions."); + } } + changing = changing || dependencyState.getDependency().isChanging(); } private class UnmatchedVersionsReason implements Describable { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/ResolvableSelectorState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/ResolvableSelectorState.java index 8ba95d1ac099..f83f2fcfdc60 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/ResolvableSelectorState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/ResolvableSelectorState.java @@ -15,10 +15,12 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors; +import org.gradle.api.artifacts.ClientModule; import org.gradle.api.artifacts.component.ComponentSelector; import org.gradle.api.artifacts.component.ProjectComponentSelector; import org.gradle.api.internal.artifacts.ResolvedVersionConstraint; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; +import org.gradle.internal.component.model.IvyArtifactName; import org.gradle.internal.resolve.ModuleVersionResolveException; import org.gradle.internal.resolve.result.ComponentIdResolveResult; @@ -66,6 +68,12 @@ public interface ResolvableSelectorState { boolean hasStrongOpinion(); + IvyArtifactName getFirstDependencyArtifact(); + + ClientModule getClientModule(); + + boolean isChanging(); + default boolean isProject() { return getSelector() instanceof ProjectComponentSelector; } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/SelectorStateResolverResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/SelectorStateResolverResults.java index 899501aa493d..5784f46a549a 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/SelectorStateResolverResults.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/SelectorStateResolverResults.java @@ -107,7 +107,6 @@ private boolean hasSoftForce() { public static T componentForIdResolveResult(ComponentStateFactory componentFactory, ComponentIdResolveResult idResolveResult, ResolvableSelectorState selector) { T component = componentFactory.getRevision(idResolveResult.getId(), idResolveResult.getModuleVersionId(), idResolveResult.getMetadata()); - component.selectedBy(selector); if (idResolveResult.isRejected()) { component.reject(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java index 5fc56677327a..ec3af0d264c9 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java @@ -169,7 +169,7 @@ private ComponentIdentifier validateComponentIdentifier(ComponentIdentifier comp private ComponentArtifactsResult buildComponentResult(ComponentIdentifier componentId, ComponentMetaDataResolver componentMetaDataResolver, ArtifactResolver artifactResolver) { BuildableComponentResolveResult moduleResolveResult = new DefaultBuildableComponentResolveResult(); - componentMetaDataResolver.resolve(componentId, new DefaultComponentOverrideMetadata(), moduleResolveResult); + componentMetaDataResolver.resolve(componentId, DefaultComponentOverrideMetadata.EMPTY, moduleResolveResult); ComponentResolveMetadata component = moduleResolveResult.getMetadata(); DefaultComponentArtifactsResult componentResult = new DefaultComponentArtifactsResult(component.getId()); for (Class artifactType : artifactTypes) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java index 4bba2e0957e3..5ab32a12f8a9 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java @@ -68,7 +68,7 @@ public LocallyAvailableExternalResource getMetaDataArtifact(ModuleDependencyMeta private File resolveMetaDataArtifactFile(ModuleComponentIdentifier moduleComponentIdentifier, ComponentMetaDataResolver componentResolver, ArtifactResolver artifactResolver, ArtifactType artifactType) { BuildableComponentResolveResult moduleVersionResolveResult = new DefaultBuildableComponentResolveResult(); - componentResolver.resolve(moduleComponentIdentifier, new DefaultComponentOverrideMetadata(), moduleVersionResolveResult); + componentResolver.resolve(moduleComponentIdentifier, DefaultComponentOverrideMetadata.EMPTY, moduleVersionResolveResult); BuildableArtifactSetResolveResult moduleArtifactsResolveResult = new DefaultBuildableArtifactSetResolveResult(); artifactResolver.resolveArtifactsWithType(moduleVersionResolveResult.getMetadata(), artifactType, moduleArtifactsResolveResult); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentOverrideMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentOverrideMetadata.java index a90fa3cff13f..ab88bc454ee8 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentOverrideMetadata.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentOverrideMetadata.java @@ -25,25 +25,30 @@ import java.util.List; public class DefaultComponentOverrideMetadata implements ComponentOverrideMetadata { + public static final ComponentOverrideMetadata EMPTY = new DefaultComponentOverrideMetadata(false, (IvyArtifactName) null, null); + private final boolean changing; private final List artifacts; private final ClientModule clientModule; - public static ComponentOverrideMetadata forDependency(DependencyMetadata dependencyMetadata) { - return new DefaultComponentOverrideMetadata(dependencyMetadata.isChanging(), dependencyMetadata.getArtifacts(), extractClientModule(dependencyMetadata)); + public static ComponentOverrideMetadata forDependency(boolean changing, IvyArtifactName mainArtifact, ClientModule clientModule) { + if (!changing && mainArtifact == null && clientModule == null) { + return EMPTY; + } + return new DefaultComponentOverrideMetadata(changing, mainArtifact, clientModule); } - public DefaultComponentOverrideMetadata() { - this(false, Collections.emptyList(), null); + private DefaultComponentOverrideMetadata(boolean changing, IvyArtifactName artifact, ClientModule clientModule) { + this(changing, artifact == null ? Collections.emptyList() : ImmutableList.of(artifact), clientModule); } private DefaultComponentOverrideMetadata(boolean changing, List artifacts, ClientModule clientModule) { this.changing = changing; - this.artifacts = ImmutableList.copyOf(artifacts); + this.artifacts = artifacts; this.clientModule = clientModule; } - private static ClientModule extractClientModule(DependencyMetadata dependencyMetadata) { + public static ClientModule extractClientModule(DependencyMetadata dependencyMetadata) { if (dependencyMetadata instanceof DslOriginDependencyMetadata) { Dependency source = ((DslOriginDependencyMetadata) dependencyMetadata).getSource(); if (source instanceof ClientModule) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LocalComponentDependencyMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LocalComponentDependencyMetadata.java index 8a3a8a0b4418..eaf3fcec18e3 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LocalComponentDependencyMetadata.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LocalComponentDependencyMetadata.java @@ -96,7 +96,7 @@ public LocalComponentDependencyMetadata(ComponentIdentifier componentId, } private static List asImmutable(List artifactNames) { - return artifactNames.isEmpty() ? Collections.emptyList() : ImmutableList.copyOf(artifactNames); + return artifactNames.isEmpty() ? Collections.emptyList() : artifactNames instanceof ImmutableList ? artifactNames : ImmutableList.copyOf(artifactNames); } @Override diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy index 28dee823f59a..781a26da947f 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy @@ -71,7 +71,7 @@ class ProjectDependencyResolverTest extends Specification { def projectComponentId = newProjectId(":projectPath") when: - resolver.resolve(projectComponentId, new DefaultComponentOverrideMetadata(), result) + resolver.resolve(projectComponentId, DefaultComponentOverrideMetadata.EMPTY, result) then: 1 * registry.getComponent(projectComponentId) >> componentMetaData diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/AbstractConflictResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/AbstractConflictResolverTest.groovy index 3e0099ed0bbb..c63105a4d1ea 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/AbstractConflictResolverTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/AbstractConflictResolverTest.groovy @@ -23,7 +23,6 @@ import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.VirtualPlatformState import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.DefaultConflictResolverDetails -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ResolvableSelectorState import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorInternal import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier import org.gradle.internal.component.model.ComponentResolveMetadata @@ -123,11 +122,6 @@ abstract class AbstractConflictResolverTest extends Specification { String toString() { id } - @Override - void selectedBy(ResolvableSelectorState selectorState) { - - } - @Override Set getPlatformOwners() { Collections.emptySet() diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestComponentResolutionState.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestComponentResolutionState.java index 5d4549fc0263..e7ab368a2228 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestComponentResolutionState.java +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestComponentResolutionState.java @@ -57,11 +57,6 @@ public ModuleVersionIdentifier getId() { return id; } - @Override - public void selectedBy(ResolvableSelectorState selectorState) { - - } - @Nullable @Override public ComponentResolveMetadata getMetadata() { diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestModuleSelectorState.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestModuleSelectorState.java index 0a8dee82c6dd..dd9ab274d4f6 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestModuleSelectorState.java +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestModuleSelectorState.java @@ -15,6 +15,7 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors; +import org.gradle.api.artifacts.ClientModule; import org.gradle.api.artifacts.VersionConstraint; import org.gradle.api.artifacts.component.ComponentSelector; import org.gradle.api.internal.artifacts.ResolvedVersionConstraint; @@ -24,6 +25,7 @@ import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme; +import org.gradle.internal.component.model.IvyArtifactName; import org.gradle.internal.resolve.ModuleVersionResolveException; import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver; import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult; @@ -117,4 +119,19 @@ public boolean isFromLock() { public boolean hasStrongOpinion() { return false; } + + @Override + public IvyArtifactName getFirstDependencyArtifact() { + return null; + } + + @Override + public ClientModule getClientModule() { + return null; + } + + @Override + public boolean isChanging() { + return false; + } } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestProjectSelectorState.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestProjectSelectorState.groovy index 1fd545cb65f6..f15281fdbc1a 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestProjectSelectorState.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestProjectSelectorState.groovy @@ -16,12 +16,14 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors +import org.gradle.api.artifacts.ClientModule import org.gradle.api.artifacts.component.ComponentSelector import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier import org.gradle.api.internal.artifacts.ResolvedVersionConstraint import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector import org.gradle.internal.component.local.model.DefaultProjectComponentSelector +import org.gradle.internal.component.model.IvyArtifactName import org.gradle.internal.resolve.ModuleVersionResolveException import org.gradle.internal.resolve.result.ComponentIdResolveResult import org.gradle.internal.resolve.result.DefaultBuildableComponentIdResolveResult @@ -59,16 +61,16 @@ class TestProjectSelectorState implements ResolvableSelectorState { @Override void failed(ModuleVersionResolveException failure) { - throw new UnsupportedOperationException("To be implemented"); + throw new UnsupportedOperationException("To be implemented") } @Override - public void markResolved() { + void markResolved() { } @Override - public boolean isForce() { - return false; + boolean isForce() { + return false } @Override @@ -85,5 +87,20 @@ class TestProjectSelectorState implements ResolvableSelectorState { boolean hasStrongOpinion() { return false } + + @Override + IvyArtifactName getFirstDependencyArtifact() { + return null + } + + @Override + ClientModule getClientModule() { + return null + } + + @Override + boolean isChanging() { + return false + } } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy index 3b25186f397b..5d20c3d4eeed 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy @@ -75,7 +75,7 @@ class IvyResolverTest extends Specification { @Unroll def "remote access fails directly for module id #moduleId with layout #layoutPattern"() { given: - def overrideMetadata = new DefaultComponentOverrideMetadata() + def overrideMetadata = DefaultComponentOverrideMetadata.EMPTY def result = new DefaultBuildableModuleComponentMetaDataResolveResult() when: @@ -105,7 +105,7 @@ class IvyResolverTest extends Specification { @Unroll def "remote access attempts to access metadata for id #moduleId with layout #layoutPattern"() { given: - def overrideMetadata = new DefaultComponentOverrideMetadata() + def overrideMetadata = DefaultComponentOverrideMetadata.EMPTY def result = new DefaultBuildableModuleComponentMetaDataResolveResult() when: diff --git a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/fixtures/publish/ModuleVersionSpec.groovy b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/fixtures/publish/ModuleVersionSpec.groovy index d912946578d8..2bb99f92ded1 100644 --- a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/fixtures/publish/ModuleVersionSpec.groovy +++ b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/fixtures/publish/ModuleVersionSpec.groovy @@ -105,6 +105,10 @@ class ModuleVersionSpec { expectGetArtifact << new ArtifactExpectation(InteractionExpectation.HEAD_MISSING, artifact) } + void maybeHeadOrGetArtifact(Map artifact) { + expectGetArtifact << new ArtifactExpectation(InteractionExpectation.MAYBE, artifact) + } + void maybeGetMetadata() { expectGetMetadata << InteractionExpectation.MAYBE } diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/AbstractMavenModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/AbstractMavenModule.groovy index d1da9c5f3db2..288077e5e4ee 100644 --- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/AbstractMavenModule.groovy +++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/AbstractMavenModule.groovy @@ -421,12 +421,13 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule ModuleArtifact getArtifact(Map options) { def artifact = toArtifact(options) def suffix = (artifact.classifier ? "-${artifact.classifier}" : "") + (artifact.type ? ".${artifact.type}" : "") + def artifactName = artifact.name ? artifact.name : artifactId return new ModuleArtifact() { String getFileName() { if (version.endsWith("-SNAPSHOT") && !metaDataFile.exists() && uniqueSnapshots) { - return "${artifactId}-${version}${suffix}" + return "${artifactName}-${version}${suffix}" } else { - return "$artifactId-${publishArtifactVersion}${suffix}" + return "$artifactName-${publishArtifactVersion}${suffix}" } } @@ -450,7 +451,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule protected Map toArtifact(Map options) { options = new HashMap(options) - def artifact = [type: options.containsKey('type') ? options.remove('type') : type, classifier: options.remove('classifier') ?: null] + def artifact = [type: options.containsKey('type') ? options.remove('type') : type, classifier: options.remove('classifier') ?: null, name: options.containsKey('name') ? options.remove('name') : null] assert options.isEmpty(): "Unknown options : ${options.keySet()}" return artifact } diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ExpectOne.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ExpectOne.groovy index 0534688000e6..7bd9f7c5dacf 100644 --- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ExpectOne.groovy +++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ExpectOne.groovy @@ -16,8 +16,14 @@ package org.gradle.test.fixtures.server +import java.util.concurrent.atomic.AtomicBoolean + abstract class ExpectOne implements ServerExpectation { - boolean run + AtomicBoolean atomicRun = new AtomicBoolean() + + boolean isRun() { + return atomicRun.get() + } void assertMet() { if (!run) { diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpServer.groovy index c0a71d0f67e1..1a7dd5bf0e12 100755 --- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpServer.groovy +++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpServer.groovy @@ -624,10 +624,9 @@ class HttpServer extends ServerWithExpectations implements HttpServerFixture { expectations << expectation add(path, matchPrefix, methods, new AbstractHandler() { void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) { - if (expectation.run) { + if (expectation.atomicRun.getAndSet(true)) { return } - expectation.run = true action.handle(request, response) request.handled = true } diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SFTPServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SFTPServer.groovy index 7c8382371965..b7448a9b0378 100644 --- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SFTPServer.groovy +++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SFTPServer.groovy @@ -382,7 +382,7 @@ class SFTPServer extends ServerWithExpectations implements RepositoryServer { boolean matches(Buffer buffer, int type, int id) { if (!run && type == expectedType) { int originalBufferPosition = buffer.rpos() - run = bufferMatches(buffer, id) + atomicRun.set(bufferMatches(buffer, id)) buffer.rpos(originalBufferPosition) return run } else { diff --git a/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/fixtures/GcsServer.groovy b/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/fixtures/GcsServer.groovy index 526ec99460ef..8b7ec11fb20c 100644 --- a/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/fixtures/GcsServer.groovy +++ b/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/fixtures/GcsServer.groovy @@ -456,7 +456,7 @@ class GcsServer extends HttpServer implements RepositoryServer { return } if (!((Request) request).isHandled()) { - expectation.run = true + expectation.atomicRun.set(true) action.handle(request, response) ((Request) request).setHandled(true) } diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Server.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Server.groovy index b48685cdaf21..33e4b86cb6b1 100644 --- a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Server.groovy +++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Server.groovy @@ -480,7 +480,7 @@ class S3Server extends HttpServer implements RepositoryServer { return } if (!((Request) request).isHandled()) { - expectation.run = true + expectation.atomicRun.set(true) action.handle(request, response) ((Request) request).setHandled(true) }