From 98141525cba67e4d6bd662e3be6ebd676237ebdc Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Sat, 27 Apr 2019 12:26:34 +0200 Subject: [PATCH 01/55] Rework exclude rule merging As a follow-up to #9197, this commit properly fixes the exclude rule merging algorithm, by completely rewriting it. The new merging algorithm works by implementing the minimal set of algebra operations that make sense to minimize computation durations. In order to do this, this commit introduces a number of exclude specs (found in their own package) and factories to create actual implementation of those specs. Specs represent the different kind of excludes we can find: - excluding a group - excluding a module (no group defined) - excluding a group+module - excluding an artifact of a group+module - pattern-matching excludes - unions of excludes - intersections of excludes With all those minimal bricks, factories are responsible of generating consistent specs. The dumbest factory will just generate new instances for everything. This is the default factory. Minimally, this factory has to be backed by an optimizing factory, which will take care of handling special cases: - union or intersection of a single spec - union or intersection of 2 specs - when one of them is null - when both are equal Then we have a factory which performs the minimal algebra to minimize specs: - unions of unions - intersections of intersections - union of a union and individual specs - insection of an intersection and individual spec - ... This factory can be as smart as it can, but one must be careful that it's worth it: some previously implemented optimizations (like (A+B).A = A turned out to be costly to detect, and didn't make it the final cut. Last but not least, a caching factory is there to avoid recomputing the same intersections and unions of specs when we have already done the job. This is efficient if the underlying (delegate) specs are easily compared, which is the case thanks to the interning factory. All in all, the delegation chain allows us to make the algorithm fast and hopefully reliable, while making it easier to debug. --- .../gradlebuild/packaging/MinifyPlugin.kt | 1 + ...ndencyExcludeResolveIntegrationTest.groovy | 8 +- ...ependencyManagementBuildScopeServices.java | 4 +- .../ivyresolve/NoRepositoriesResolver.java | 4 +- .../RepositoryChainArtifactResolver.java | 4 +- .../ProjectDependencyResolver.java | 4 +- .../artifact/DefaultArtifactSet.java | 10 +- .../ResolvedArtifactsGraphVisitor.java | 4 +- .../excludes/AbstractCompositeExclusion.java | 102 --- .../excludes/AbstractModuleExclusion.java | 118 --- .../resolveengine/excludes/AllExclusion.java | 87 -- .../excludes/ArtifactExcludeSpec.java | 82 -- .../excludes/EitherExclusion.java | 107 --- .../excludes/GroupNameExcludeSpec.java | 58 -- .../excludes/ImmutableModuleExclusionSet.java | 187 ---- .../IvyPatternMatcherExcludeRuleSpec.java | 95 -- .../excludes/ModuleExclusion.java | 49 -- .../excludes/ModuleExclusions.java | 491 ++--------- .../excludes/ModuleIdExcludeSpec.java | 57 -- .../excludes/ModuleNameExcludeSpec.java | 57 -- .../excludes/PatternMatchers.java | 2 +- .../factories/CachingExcludeFactory.java | 149 ++++ .../factories/DelegatingExcludeFactory.java | 92 ++ .../factories/NormalizingExcludeFactory.java | 158 ++++ .../excludes/factories/Optimizations.java | 89 ++ .../factories/OptimizingExcludeFactory.java | 61 ++ .../excludes/factories/package-info.java | 20 + .../simple/DefaultCompositeExclude.java | 117 +++ .../excludes/simple/DefaultExcludeAllOf.java | 55 ++ .../excludes/simple/DefaultExcludeAnyOf.java | 54 ++ .../simple/DefaultExcludeEverything.java | 72 ++ .../simple/DefaultExcludeFactory.java | 88 ++ .../simple/DefaultExcludeNothing.java | 71 ++ .../excludes/simple/DefaultGroupExclude.java | 88 ++ ...faultIvyPatternMatcherExcludeRuleSpec.java | 118 +++ .../simple/DefaultModuleArtifactExclude.java | 85 ++ .../excludes/simple/DefaultModuleExclude.java | 88 ++ .../simple/DefaultModuleIdExclude.java | 88 ++ .../simple/DefaultModuleSetExclude.java | 83 ++ .../excludes/simple/package-info.java | 20 + .../ArtifactExclude.java} | 29 +- .../excludes/specs/CompositeExclude.java | 32 + .../excludes/specs/ExcludeAllOf.java | 19 + .../excludes/specs/ExcludeAnyOf.java | 19 + .../excludes/specs/ExcludeEverything.java | 19 + .../excludes/specs/ExcludeFactory.java | 48 + .../excludes/specs/ExcludeNothing.java | 19 + .../excludes/specs/ExcludeSpec.java | 43 + .../excludes/specs/GroupExclude.java | 20 + .../IvyPatternMatcherExcludeRuleSpec.java | 19 + .../excludes/specs/ModuleExclude.java | 20 + .../excludes/specs/ModuleIdExclude.java | 22 + .../ModuleSetExclude.java} | 32 +- .../excludes/specs/package-info.java | 20 + .../graph/DependencyGraphEdge.java | 4 +- .../graph/builder/EdgeState.java | 16 +- .../graph/builder/NodeState.java | 48 +- .../model/FixedComponentArtifacts.java | 4 +- .../MetadataSourcedComponentArtifacts.java | 4 +- .../component/model/ComponentArtifacts.java | 4 +- .../resolve/resolver/ArtifactSelector.java | 4 +- .../resolver/DefaultArtifactSelector.java | 10 +- .../resolver/OriginArtifactSelector.java | 4 +- ...RepositoryChainArtifactResolverTest.groovy | 6 +- .../DependencyGraphBuilderTest.groovy | 2 +- .../DefaultModuleExclusionTest.groovy | 818 ------------------ .../NormalizingExcludeFactoryTest.groovy | 137 +++ .../model/IvyDependencyDescriptorTest.groovy | 29 +- .../MavenDependencyDescriptorTest.groovy | 16 +- .../DefaultLocalComponentMetadataTest.groovy | 3 - ...ocalComponentDependencyMetadataTest.groovy | 9 +- .../LocalLibraryDependencyResolver.java | 4 +- ...mLocalLibraryDependencyResolverTest.groovy | 7 +- .../resolver/VcsDependencyResolver.java | 4 +- 74 files changed, 2219 insertions(+), 2402 deletions(-) delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AbstractCompositeExclusion.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AbstractModuleExclusion.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AllExclusion.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ArtifactExcludeSpec.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/EitherExclusion.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/GroupNameExcludeSpec.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ImmutableModuleExclusionSet.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/IvyPatternMatcherExcludeRuleSpec.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusion.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleIdExcludeSpec.java delete mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleNameExcludeSpec.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/package-info.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeEverything.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeNothing.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultIvyPatternMatcherExcludeRuleSpec.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleArtifactExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleIdExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleSetExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/package-info.java rename subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/{ExcludeNone.java => specs/ArtifactExclude.java} (62%) create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeAllOf.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeAnyOf.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeEverything.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeNothing.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeSpec.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/GroupExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/IvyPatternMatcherExcludeRuleSpec.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleIdExclude.java rename subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/{ExcludeAllModulesSpec.java => specs/ModuleSetExclude.java} (53%) create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/package-info.java delete mode 100644 subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/DefaultModuleExclusionTest.groovy create mode 100644 subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy diff --git a/buildSrc/subprojects/packaging/src/main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt b/buildSrc/subprojects/packaging/src/main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt index 25136dcaba5f..412753e83031 100644 --- a/buildSrc/subprojects/packaging/src/main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt +++ b/buildSrc/subprojects/packaging/src/main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt @@ -38,6 +38,7 @@ open class MinifyPlugin : Plugin { val keepPatterns = mapOf( "fastutil" to setOf( "it.unimi.dsi.fastutil.ints.IntOpenHashSet", + "it.unimi.dsi.fastutil.ints.IntRBTreeSet", "it.unimi.dsi.fastutil.ints.IntSets" ) ) diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ModuleDependencyExcludeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ModuleDependencyExcludeResolveIntegrationTest.groovy index 3ceb8f4c4ffe..5ee7c5f879dc 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ModuleDependencyExcludeResolveIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ModuleDependencyExcludeResolveIntegrationTest.groovy @@ -16,9 +16,11 @@ package org.gradle.integtests.resolve + import org.gradle.integtests.fixtures.executer.GradleContextualExecuter import spock.lang.IgnoreIf import spock.lang.Issue +import spock.lang.Unroll /** * Demonstrates the resolution of dependency excludes in published module metadata. @@ -109,7 +111,8 @@ task check(type: Sync) { * * Exclude is applied to dependency a->b */ - def "dependency exclude for group or module applies to child module of dependency"() { + @Unroll + def "dependency exclude for group or module applies to child module of dependency (#excluded)"() { given: def expectResolved = ['a', 'b', 'c', 'd', 'e'] - expectExcluded repository { @@ -202,7 +205,8 @@ task check(type: Sync) { * * Selective exclusions are applied to dependency a->b */ - def "can exclude transitive dependencies"() { + @Unroll + def "can exclude transitive dependencies (#condition)"() { repository { 'a:a:1.0' { dependsOn group: 'b', artifact: 'b', version: '1.0', exclusions: excludes diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java index d6e133f8525e..9d5d2dce8097 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java @@ -169,8 +169,8 @@ BuildCommencedTimeProvider createBuildTimeProvider() { return new BuildCommencedTimeProvider(); } - ModuleExclusions createModuleExclusions(ImmutableModuleIdentifierFactory moduleIdentifierFactory) { - return new ModuleExclusions(moduleIdentifierFactory); + ModuleExclusions createModuleExclusions() { + return new ModuleExclusions(); } MavenMutableModuleMetadataFactory createMutableMavenMetadataFactory(ImmutableModuleIdentifierFactory moduleIdentifierFactory, diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java index bfd43681d9a1..7de16f585f18 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java @@ -19,7 +19,7 @@ import org.gradle.api.artifacts.component.ComponentIdentifier; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.api.internal.component.ArtifactType; @@ -82,7 +82,7 @@ public boolean isFetchingMetadataCheap(ComponentIdentifier identifier) { @Nullable @Override - public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes) { + public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) { throw new UnsupportedOperationException(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java index 9e044fb8a93a..152fe0fbbc1c 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java @@ -17,7 +17,7 @@ import org.gradle.api.attributes.Category; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.repositories.metadata.MavenImmutableAttributesFactory; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.AttributeValue; @@ -60,7 +60,7 @@ public void resolveArtifactsWithType(ComponentResolveMetadata component, Artifac @Nullable @Override - public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes) { + public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) { if (component.getSource() == null) { // virtual components have no source return NO_ARTIFACTS; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java index bdb29c4ea06d..33b876d65a5b 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java @@ -26,7 +26,7 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.DefaultArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.api.internal.component.ArtifactType; @@ -131,7 +131,7 @@ public void resolveArtifactsWithType(ComponentResolveMetadata component, Artifac @Nullable @Override - public ArtifactSet resolveArtifacts(final ComponentResolveMetadata component, final ConfigurationMetadata configuration, final ArtifactTypeRegistry artifactTypeRegistry, final ModuleExclusion exclusions, final ImmutableAttributes overriddenAttributes) { + public ArtifactSet resolveArtifacts(final ComponentResolveMetadata component, final ConfigurationMetadata configuration, final ArtifactTypeRegistry artifactTypeRegistry, final ExcludeSpec exclusions, final ImmutableAttributes overriddenAttributes) { if (isProjectModule(component.getId())) { return DefaultArtifactSet.multipleVariants(component.getId(), component.getModuleVersionId(), component.getSource(), exclusions, configuration.getVariants(), component.getAttributesSchema(), self, allProjectArtifacts, artifactTypeRegistry, overriddenAttributes); } else { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSet.java index 852a6b5baf3f..e6b5b844aa64 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSet.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSet.java @@ -23,7 +23,7 @@ import org.gradle.api.artifacts.component.ComponentArtifactIdentifier; import org.gradle.api.artifacts.component.ComponentIdentifier; import org.gradle.api.internal.artifacts.DefaultResolvedArtifact; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.transform.VariantSelector; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.AttributesSchemaInternal; @@ -71,7 +71,7 @@ public ImmutableAttributes getOverriddenAttributes() { return selectionAttributes; } - public static ArtifactSet multipleVariants(ComponentIdentifier componentIdentifier, ModuleVersionIdentifier ownerId, ModuleSource moduleSource, ModuleExclusion exclusions, Set variants, AttributesSchemaInternal schema, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ImmutableAttributes selectionAttributes) { + public static ArtifactSet multipleVariants(ComponentIdentifier componentIdentifier, ModuleVersionIdentifier ownerId, ModuleSource moduleSource, ExcludeSpec exclusions, Set variants, AttributesSchemaInternal schema, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ImmutableAttributes selectionAttributes) { if (variants.size() == 1) { VariantResolveMetadata variantMetadata = variants.iterator().next(); ResolvedVariant resolvedVariant = toResolvedVariant(variantMetadata, ownerId, moduleSource, exclusions, artifactResolver, allResolvedArtifacts, artifactTypeRegistry); @@ -85,13 +85,13 @@ public static ArtifactSet multipleVariants(ComponentIdentifier componentIdentifi return new MultipleVariantArtifactSet(componentIdentifier, schema, result.build(), selectionAttributes); } - public static ArtifactSet singleVariant(ComponentIdentifier componentIdentifier, ModuleVersionIdentifier ownerId, DisplayName displayName, Collection artifacts, ModuleSource moduleSource, ModuleExclusion exclusions, AttributesSchemaInternal schema, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ImmutableAttributes selectionAttributes) { + public static ArtifactSet singleVariant(ComponentIdentifier componentIdentifier, ModuleVersionIdentifier ownerId, DisplayName displayName, Collection artifacts, ModuleSource moduleSource, ExcludeSpec exclusions, AttributesSchemaInternal schema, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ImmutableAttributes selectionAttributes) { VariantResolveMetadata variantMetadata = new DefaultVariantMetadata(displayName, ImmutableAttributes.EMPTY, ImmutableList.copyOf(artifacts), ImmutableCapabilities.EMPTY); ResolvedVariant resolvedVariant = toResolvedVariant(variantMetadata, ownerId, moduleSource, exclusions, artifactResolver, allResolvedArtifacts, artifactTypeRegistry); return new SingleVariantArtifactSet(componentIdentifier, schema, resolvedVariant, selectionAttributes); } - private static ResolvedVariant toResolvedVariant(VariantResolveMetadata variant, ModuleVersionIdentifier ownerId, ModuleSource moduleSource, ModuleExclusion exclusions, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry) { + private static ResolvedVariant toResolvedVariant(VariantResolveMetadata variant, ModuleVersionIdentifier ownerId, ModuleSource moduleSource, ExcludeSpec exclusions, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry) { List artifacts = variant.getArtifacts(); ImmutableSet.Builder resolvedArtifacts = ImmutableSet.builder(); @@ -100,7 +100,7 @@ private static ResolvedVariant toResolvedVariant(VariantResolveMetadata variant, for (ComponentArtifactMetadata artifact : artifacts) { IvyArtifactName artifactName = artifact.getName(); - if (exclusions.excludeArtifact(ownerId.getModule(), artifactName)) { + if (exclusions.excludesArtifact(ownerId.getModule(), artifactName)) { continue; } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactsGraphVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactsGraphVisitor.java index 96113fa4eb10..c0617d9d8af6 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactsGraphVisitor.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactsGraphVisitor.java @@ -17,7 +17,7 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact; import com.google.common.collect.Maps; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphEdge; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphSelector; @@ -96,7 +96,7 @@ private ArtifactsForNode getArtifacts(DependencyGraphEdge dependency, Dependency ArtifactsForNode configurationArtifactSet = artifactsByNodeId.get(toConfiguration.getNodeId()); if (configurationArtifactSet == null) { - ModuleExclusion exclusions = dependency.getExclusions(); + ExcludeSpec exclusions = dependency.getExclusions(); ArtifactSet nodeArtifacts = artifactSelector.resolveArtifacts(component, targetConfiguration, exclusions, overriddenAttributes); int id = nextId++; configurationArtifactSet = new ArtifactsForNode(id, nodeArtifacts); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AbstractCompositeExclusion.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AbstractCompositeExclusion.java deleted file mode 100644 index 830ccd8118e9..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AbstractCompositeExclusion.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import com.google.common.collect.Sets; - -import java.util.Collection; -import java.util.Set; - -abstract class AbstractCompositeExclusion extends AbstractModuleExclusion { - private int hashCode = -1; - - abstract Collection getFilters(); - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("{"); - builder.append(getClass().getSimpleName()); - for (AbstractModuleExclusion spec : getFilters()) { - builder.append(' '); - builder.append(spec); - } - builder.append("}"); - return builder.toString(); - } - - @Override - protected boolean doExcludesSameModulesAs(AbstractModuleExclusion other) { - AbstractCompositeExclusion spec = (AbstractCompositeExclusion) other; - Collection thisFilters = getFilters(); - Collection otherFilters = spec.getFilters(); - - // To make the comparison faster, we compute the sets of specs that exist in this exclusion, but not in the other - // and the set of specs that exist in the other and not in this one. Then we only need to check if the missing - // from one set have an equivalent in the missing of the other set, which is much faster than checking all of them - Set miss1 = Sets.newHashSetWithExpectedSize(thisFilters.size()); - Set miss2 = Sets.newHashSetWithExpectedSize(otherFilters.size()); - for (AbstractModuleExclusion exclusion : thisFilters) { - if (!otherFilters.contains(exclusion)) { - miss1.add(exclusion); - } - } - for (AbstractModuleExclusion otherFilter : otherFilters) { - if (!thisFilters.contains(otherFilter)) { - miss2.add(otherFilter); - } - } - return (miss1.isEmpty() && miss2.isEmpty()) || (implies(miss1, miss2) && implies(miss2, miss1)); - } - - @Override - protected boolean doEquals(Object obj) { - AbstractCompositeExclusion other = (AbstractCompositeExclusion) obj; - return hashCode() == other.hashCode() && getFilters().equals(other.getFilters()); - } - - @Override - protected int doHashCode() { - if (hashCode != -1) { - return hashCode; - } - hashCode = getFilters().hashCode(); - return hashCode; - } - - /** - * Returns true if for every spec in this spec, there is a corresponding spec in the given spec that excludesSameModulesAs(). - */ - protected boolean implies(Set miss1, Set miss2) { - if (miss1.isEmpty() || miss2.isEmpty()) { - return false; - } - for (AbstractModuleExclusion thisSpec : miss1) { - boolean found = false; - for (AbstractModuleExclusion otherSpec : miss2) { - if (thisSpec.excludesSameModulesAs(otherSpec)) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; - } -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AbstractModuleExclusion.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AbstractModuleExclusion.java deleted file mode 100644 index 1a1d54a24440..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AbstractModuleExclusion.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.internal.component.model.IvyArtifactName; - -import java.util.Collection; - -abstract class AbstractModuleExclusion implements ModuleExclusion { - private int hashCode = -1; - private ModuleExclusion lastCheck; - private boolean lastCheckResult; - - protected static boolean isWildcard(String attribute) { - return PatternMatchers.ANY_EXPRESSION.equals(attribute); - } - - public boolean excludeArtifact(ModuleIdentifier module, IvyArtifactName artifact) { - return false; - } - - public boolean mayExcludeArtifacts() { - return false; - } - - public final boolean excludesSameModulesAs(ModuleExclusion filter) { - if (filter == this) { - return true; - } - AbstractModuleExclusion other = (AbstractModuleExclusion) filter; - boolean thisExcludesNothing = excludesNoModules(); - boolean otherExcludesNothing = other.excludesNoModules(); - if (thisExcludesNothing && otherExcludesNothing) { - return true; - } - if (thisExcludesNothing || otherExcludesNothing) { - return false; - } - if (!other.getClass().equals(getClass())) { - return false; - } - synchronized (this) { - // This is an optimization, based on the fact that in a large amount of times - // a specific exclusion is checked against the same filter the next time so - // we don't need to recompute the result: we can cache the last query - if (filter == lastCheck) { - return lastCheckResult; - } - lastCheck = other; - lastCheckResult = doExcludesSameModulesAs(other); - return lastCheckResult; - } - } - - /** - * Only called when this and the other spec have the same class. - */ - protected boolean doExcludesSameModulesAs(AbstractModuleExclusion other) { - return false; - } - - protected boolean excludesNoModules() { - return false; - } - - /** - * Possibly unpack a composite spec into it's constituent parts, if those parts are applied as an intersection. - * @param specs - */ - protected void unpackEither(Collection specs) { - specs.add(this); - } - - /** - * Possibly unpack a composite spec into it's constituent parts, if those parts are applied as a union. - */ - protected void unpackAll(Collection specs) { - specs.add(this); - } - - @Override - public final boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj == null || obj.getClass() != getClass()) { - return false; - } - return doEquals(obj); - } - - protected abstract boolean doEquals(Object obj); - - @Override - public final int hashCode() { - if (hashCode != -1) { - return hashCode; - } - hashCode = doHashCode(); - return hashCode; - } - - protected abstract int doHashCode(); -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AllExclusion.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AllExclusion.java deleted file mode 100644 index 9dfc35884906..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/AllExclusion.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.internal.component.model.IvyArtifactName; - -import java.util.Collection; -import java.util.List; - -/** - * A filter that only excludes artifacts and modules that are excluded by _all_ of the supplied exclude rules. - * As such, this is a union of the separate exclude rule filters. - */ -class AllExclusion extends AbstractCompositeExclusion { - private final List filters; - - public AllExclusion(List filters) { - this.filters = filters; - } - - Collection getFilters() { - return filters; - } - - /** - * Can unpack into constituents when creating a larger union. - */ - @Override - protected void unpackAll(Collection specs) { - specs.addAll(this.filters); - } - - @Override - protected boolean excludesNoModules() { - for (AbstractModuleExclusion excludeSpec : filters) { - if (excludeSpec.excludesNoModules()) { - return true; - } - } - return false; - } - - public boolean excludeModule(ModuleIdentifier element) { - for (AbstractModuleExclusion spec : filters) { - if (!spec.excludeModule(element)) { - return false; - } - } - - return true; - } - - public boolean excludeArtifact(ModuleIdentifier module, IvyArtifactName artifact) { - for (AbstractModuleExclusion spec : filters) { - if (!spec.excludeArtifact(module, artifact)) { - return false; - } - } - - return true; - } - - public boolean mayExcludeArtifacts() { - for (AbstractModuleExclusion spec : filters) { - if (!spec.mayExcludeArtifacts()) { - return false; - } - } - - return true; - } -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ArtifactExcludeSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ArtifactExcludeSpec.java deleted file mode 100644 index c10b61cb1495..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ArtifactExcludeSpec.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.internal.component.model.IvyArtifactName; - -/** - * A ModuleResolutionFilter that excludes any artifact that matches supplied module and artifact details. - * Does not exclude any modules - */ -class ArtifactExcludeSpec extends AbstractModuleExclusion { - private final ModuleIdentifier moduleId; - private final IvyArtifactName ivyArtifactName; - - ArtifactExcludeSpec(ModuleIdentifier moduleId, IvyArtifactName artifact) { - this.moduleId = moduleId; - this.ivyArtifactName = artifact; - } - - @Override - public String toString() { - return "{artifact " + moduleId + ":" + ivyArtifactName + "}"; - } - - @Override - protected boolean doEquals(Object o) { - ArtifactExcludeSpec other = (ArtifactExcludeSpec) o; - return moduleId.equals(other.moduleId) && ivyArtifactName.equals(other.ivyArtifactName); - } - - @Override - protected int doHashCode() { - return moduleId.hashCode() ^ ivyArtifactName.hashCode(); - } - - @Override - protected boolean doExcludesSameModulesAs(AbstractModuleExclusion other) { - return true; - } - - @Override - protected boolean excludesNoModules() { - return true; - } - - @Override - public boolean excludeModule(ModuleIdentifier module) { - return false; - } - - @Override - public boolean excludeArtifact(ModuleIdentifier module, IvyArtifactName artifact) { - return matches(moduleId.getGroup(), module.getGroup()) - && matches(moduleId.getName(), module.getName()) - && matches(ivyArtifactName.getName(), artifact.getName()) - && matches(ivyArtifactName.getExtension(), artifact.getExtension()) - && matches(ivyArtifactName.getType(), artifact.getType()); - } - - public boolean mayExcludeArtifacts() { - return true; - } - - private boolean matches(String expression, String input) { - return isWildcard(expression) || expression.equals(input); - } -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/EitherExclusion.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/EitherExclusion.java deleted file mode 100644 index f8f39dedae10..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/EitherExclusion.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.internal.component.model.IvyArtifactName; - -import java.util.Collection; - -/** - * A spec that excludes modules or artifacts that are excluded by _any_ of the supplied exclusions. - * As such, this is an intersection of the separate exclude rule filters. - */ -class EitherExclusion extends AbstractCompositeExclusion { - private final ImmutableModuleExclusionSet excludeSpecs; - private final boolean mergeable; - - private Boolean excludesNoModules; - - public EitherExclusion(ImmutableModuleExclusionSet specs) { - this.excludeSpecs = specs; - boolean canMerge = true; - for (AbstractModuleExclusion spec : specs) { - if (!canMerge(spec)) { - canMerge = false; - break; - } - } - mergeable = canMerge; - } - - boolean canMerge() { - return mergeable; - } - - ImmutableModuleExclusionSet getFilters() { - return excludeSpecs; - } - - @Override - protected boolean excludesNoModules() { - if (excludesNoModules == null) { - // cache the result as we may be asked several times - excludesNoModules = doExcludeNoModules(); - } - return excludesNoModules; - } - - private boolean doExcludeNoModules() { - for (AbstractModuleExclusion excludeSpec : excludeSpecs) { - if (!excludeSpec.excludesNoModules()) { - return false; - } - } - return true; - } - - public boolean excludeModule(ModuleIdentifier element) { - return excludeSpecs.excludesModule(element); - } - - public boolean excludeArtifact(ModuleIdentifier module, IvyArtifactName artifact) { - return excludeSpecs.excludesArtifact(module, artifact); - } - - public boolean mayExcludeArtifacts() { - for (AbstractModuleExclusion spec : excludeSpecs) { - if (spec.mayExcludeArtifacts()) { - return true; - } - } - - return false; - } - - /** - * Can unpack into constituents when creating a larger intersection (since elements are applied as an intersection). - * @param specs - */ - @Override - protected void unpackEither(Collection specs) { - specs.addAll(excludeSpecs); - } - - private static boolean canMerge(AbstractModuleExclusion excludeSpec) { - return excludeSpec instanceof ExcludeAllModulesSpec - || excludeSpec instanceof ArtifactExcludeSpec - || excludeSpec instanceof GroupNameExcludeSpec - || excludeSpec instanceof ModuleNameExcludeSpec - || excludeSpec instanceof ModuleIdExcludeSpec; - } - -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/GroupNameExcludeSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/GroupNameExcludeSpec.java deleted file mode 100644 index 30f35daadbf8..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/GroupNameExcludeSpec.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import org.gradle.api.artifacts.ModuleIdentifier; - -/** - * A ModuleResolutionFilter that excludes any module with a matching group. - * Does not exclude any artifacts. - */ -class GroupNameExcludeSpec extends AbstractModuleExclusion { - final String group; - - GroupNameExcludeSpec(String group) { - this.group = group; - } - - @Override - public String toString() { - return "{group " + group + "}"; - } - - @Override - protected boolean doEquals(Object o) { - GroupNameExcludeSpec other = (GroupNameExcludeSpec) o; - return group.equals(other.group); - } - - @Override - protected int doHashCode() { - return group.hashCode(); - } - - @Override - public boolean doExcludesSameModulesAs(AbstractModuleExclusion other) { - GroupNameExcludeSpec groupNameExcludeSpec = (GroupNameExcludeSpec) other; - return group.equals(groupNameExcludeSpec.group); - } - - @Override - public boolean excludeModule(ModuleIdentifier module) { - return module.getGroup().equals(group); - } -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ImmutableModuleExclusionSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ImmutableModuleExclusionSet.java deleted file mode 100644 index eee88c589dfa..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ImmutableModuleExclusionSet.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2017 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterators; -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.internal.Cast; -import org.gradle.internal.component.model.IvyArtifactName; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; - -final class ImmutableModuleExclusionSet implements Set { - private final Set delegate; - - final AbstractModuleExclusion[] elements; - private final int hashCode; - - // the following fields are used as optimizations, to avoid iterating on the whole set of exclusions - private ImmutableSet excludedModules; - private ImmutableList moduleExcludes; - private ImmutableList artifactExcludes; - - ImmutableModuleExclusionSet(ImmutableSet delegate) { - this.delegate = delegate; - this.elements = delegate.toArray(new AbstractModuleExclusion[0]); - this.hashCode = delegate.hashCode(); - } - - private synchronized void precomputeCaches() { - if (excludedModules != null) { - return; - } - ImmutableSet.Builder moduleIds = ImmutableSet.builder(); - ImmutableList.Builder modules = ImmutableList.builder(); - ImmutableList.Builder artifacts = ImmutableList.builder(); - for (AbstractModuleExclusion exclusion : delegate) { - if (exclusion instanceof ModuleIdExcludeSpec) { - moduleIds.add(((ModuleIdExcludeSpec) exclusion).moduleId); - } else { - if (!exclusion.excludesNoModules()) { - modules.add(exclusion); - } - if (exclusion.mayExcludeArtifacts()) { - artifacts.add(exclusion); - } - } - } - excludedModules = moduleIds.build(); - moduleExcludes = modules.build(); - artifactExcludes = artifacts.build(); - } - - @Override - public int size() { - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return delegate.contains(o); - } - - @Override - public Iterator iterator() { - return Iterators.forArray(elements); - } - - /** - * This method optimizes module exclusion lookup, based on empirical data showing that the set can be very large, but would contain mostly direct module exclusion nodes. So instead of always - * iterating over all excluded modules, we can perform a fast lookup using a set of excluded modules first, then only iterate on the remaining exclusions. - * - * @param id the module to check - * @return true if it's excluded - */ - boolean excludesModule(ModuleIdentifier id) { - precomputeCaches(); - if (excludedModules.contains(id)) { - return true; - } - for (AbstractModuleExclusion excludeSpec : moduleExcludes) { - if (excludeSpec.excludeModule(id)) { - return true; - } - } - return false; - } - - boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifact) { - precomputeCaches(); - for (AbstractModuleExclusion excludeSpec : artifactExcludes) { - if (excludeSpec.excludeArtifact(module, artifact)) { - return true; - } - } - return false; - } - - @Override - public Object[] toArray() { - return elements; - } - - @Override - public T[] toArray(T[] a) { - return Cast.uncheckedCast(elements); - } - - @Override - public boolean add(AbstractModuleExclusion abstractModuleExclusion) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(Object o) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean containsAll(Collection c) { - return delegate.containsAll(c); - } - - @Override - public boolean addAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - ImmutableModuleExclusionSet that = (ImmutableModuleExclusionSet) o; - - if (hashCode != that.hashCode) { - return false; - } - - return delegate.equals(that.delegate); - } - - @Override - public int hashCode() { - return hashCode; - } -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/IvyPatternMatcherExcludeRuleSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/IvyPatternMatcherExcludeRuleSpec.java deleted file mode 100644 index 7e8ff90ddd3c..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/IvyPatternMatcherExcludeRuleSpec.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import com.google.common.base.Objects; -import org.apache.ivy.plugins.matcher.PatternMatcher; -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.internal.component.model.ExcludeMetadata; -import org.gradle.internal.component.model.IvyArtifactName; - -/** - * A ModuleResolutionFilter that excludes any module/artifact that matches the exclude rule, using an Ivy pattern matcher. - */ -class IvyPatternMatcherExcludeRuleSpec extends AbstractModuleExclusion { - private final ModuleIdentifier moduleId; - private final IvyArtifactName ivyArtifactName; - private final PatternMatcher matcher; - private final boolean isArtifactExclude; - - IvyPatternMatcherExcludeRuleSpec(ExcludeMetadata rule) { - this.moduleId = rule.getModuleId(); - this.ivyArtifactName = rule.getArtifact(); - this.matcher = PatternMatchers.getInstance().getMatcher(rule.getMatcher()); - isArtifactExclude = ivyArtifactName != null; - } - - @Override - public String toString() { - return "{exclude-rule " + moduleId + ":" + ivyArtifactName + " with matcher " + matcher.getName() + "}"; - } - - @Override - protected boolean doEquals(Object o) { - IvyPatternMatcherExcludeRuleSpec other = (IvyPatternMatcherExcludeRuleSpec) o; - return doExcludesSameModulesAs(other); - } - - @Override - protected int doHashCode() { - return Objects.hashCode(moduleId, ivyArtifactName); - } - - @Override - protected boolean doExcludesSameModulesAs(AbstractModuleExclusion other) { - IvyPatternMatcherExcludeRuleSpec otherSpec = (IvyPatternMatcherExcludeRuleSpec) other; - return moduleId.equals(otherSpec.moduleId) - && Objects.equal(ivyArtifactName, otherSpec.ivyArtifactName) - && matcher.getName().equals(otherSpec.matcher.getName()); - } - - @Override - protected boolean excludesNoModules() { - return isArtifactExclude; - } - - public boolean excludeModule(ModuleIdentifier module) { - if (isArtifactExclude) { - return false; - } - return matches(moduleId.getGroup(), module.getGroup()) && matches(moduleId.getName(), module.getName()); - } - - public boolean excludeArtifact(ModuleIdentifier module, IvyArtifactName artifact) { - if (!isArtifactExclude) { - return false; - } - return matches(moduleId.getGroup(), module.getGroup()) - && matches(moduleId.getName(), module.getName()) - && matches(ivyArtifactName.getName(), artifact.getName()) - && matches(ivyArtifactName.getExtension(), artifact.getExtension()) - && matches(ivyArtifactName.getType(), artifact.getType()); - } - - public boolean mayExcludeArtifacts() { - return isArtifactExclude; - } - - private boolean matches(String expression, String input) { - return matcher.getMatcher(expression).matches(input); - } -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusion.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusion.java deleted file mode 100644 index b7f54482de38..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusion.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.internal.component.model.IvyArtifactName; - -/** - * Manages sets of exclude rules, allowing union and intersection operations on the rules. - */ -public interface ModuleExclusion { - /** - * Should this module be excluded from the resolution result? - */ - boolean excludeModule(ModuleIdentifier module); - - /** - * Should this artifact be excluded from the resolution result? - */ - boolean excludeArtifact(ModuleIdentifier module, IvyArtifactName artifact); - - /** - * Could any artifacts be excluded by this filter? - * - * @return false if this filter could return false for {@link #excludeArtifact} for every provided artifact. - */ - boolean mayExcludeArtifacts(); - - /** - * Determines if this filter excludes the same set of modules as the other. - * - * @return true if the filters excludes the same set of modules. Returns false if they may not, or if it is unknown. - */ - boolean excludesSameModulesAs(ModuleExclusion other); -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java index fa10c3d46959..538674d8cdf8 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * 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. @@ -13,453 +13,88 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.DependencyGraphBuilder; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.CachingExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.NormalizingExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.OptimizingExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.internal.component.model.ExcludeMetadata; import org.gradle.internal.component.model.IvyArtifactName; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; -import java.util.Set; - -import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.AbstractModuleExclusion.isWildcard; +import java.util.stream.Collectors; -/** - * Manages sets of exclude rules, allowing union and intersection operations on the rules. - * - *

This class attempts to reduce execution time, by flattening union and intersection specs, at the cost of more analysis at construction time. This is taken advantage of by {@link - * DependencyGraphBuilder}, on the assumption that there are many more edges in the dependency graph than there are exclude rules (ie - * we evaluate the rules much more often that we construct them).

- * - *

Also, this class attempts to be quite accurate in determining if 2 specs will exclude exactly the same set of modules. {@link DependencyGraphBuilder} - * uses this to avoid traversing the dependency graph of a particular version that has already been traversed when a new incoming edge is added (eg a newly discovered dependency) and when an incoming - * edge is removed (eg a conflict evicts a version that depends on the given version).

- * - *
  • When a module dependency has multiple exclusions, then the resulting exclusion is the _intersection_ of those exclusions (module is excluded if excluded by _any_).
  • When a module - * is depended on via a transitive path, then the resulting exclusion is the _intersection_ of the exclusions on each leg of the path (module is excluded if excluded by _any_).
  • When a module - * is depended on via multiple paths in the graph, then the resulting exclusion is the _union_ of the exclusions on each of those paths (module is excluded if excluded by _all_).
- */ public class ModuleExclusions { - private static final ExcludeNone EXCLUDE_NONE = new ExcludeNone(); - private static final ExcludeAllModulesSpec EXCLUDE_ALL_MODULES_SPEC = new ExcludeAllModulesSpec(); - - private final ImmutableModuleIdentifierFactory moduleIdentifierFactory; - - private final Map mergeCache = Maps.newConcurrentMap(); - private final Map, AbstractModuleExclusion> excludeAnyCache = Maps.newConcurrentMap(); - private final Map, EitherExclusion> eitherCache = Maps.newConcurrentMap(); - private final Map> mergeOperationCache = Maps.newIdentityHashMap(); - private final Map moduleIdSpecs = Maps.newConcurrentMap(); - private final Map moduleNameSpecs = Maps.newConcurrentMap(); - private final Map groupNameSpecs = Maps.newConcurrentMap(); - - private final Object mergeOperationLock = new Object(); - - public ModuleExclusions(ImmutableModuleIdentifierFactory moduleIdentifierFactory) { - this.moduleIdentifierFactory = moduleIdentifierFactory; - } - - /** - * Returns a spec that excludes nothing. - */ - public static ModuleExclusion excludeNone() { - return EXCLUDE_NONE; - } - - /** - * Returns a spec that excludes those modules and artifacts that are excluded by _any_ of the given exclude rules. - */ - public ModuleExclusion excludeAny(ExcludeMetadata... excludes) { - if (excludes.length == 0) { - return EXCLUDE_NONE; - } - return excludeAny(ImmutableList.copyOf(excludes)); - } - - /** - * Returns a spec that excludes those modules and artifacts that are excluded by _any_ of the given exclude rules. - */ - public ModuleExclusion excludeAny(ImmutableList excludes) { - if (excludes.isEmpty()) { - return EXCLUDE_NONE; - } - AbstractModuleExclusion exclusion = excludeAnyCache.get(excludes); - if (exclusion != null) { - return exclusion; - } - ImmutableSet.Builder exclusions = ImmutableSet.builder(); - for (ExcludeMetadata exclude : excludes) { - exclusions.add(forExclude(exclude)); - } - exclusion = asEither(exclusions.build()); - excludeAnyCache.put(excludes, exclusion); - return exclusion; - } - - private AbstractModuleExclusion forExclude(ExcludeMetadata rule) { - // For custom ivy pattern matchers, don't inspect the rule any more deeply: this prevents us from doing smart merging later - if (!PatternMatchers.isExactMatcher(rule.getMatcher())) { - return new IvyPatternMatcherExcludeRuleSpec(rule); - } - - ModuleIdentifier moduleId = rule.getModuleId(); - IvyArtifactName artifact = rule.getArtifact(); - boolean anyOrganisation = isWildcard(moduleId.getGroup()); - boolean anyModule = isWildcard(moduleId.getName()); - - // Build a strongly typed (mergeable) exclude spec for each supplied rule - if (artifact == null) { - if (!anyOrganisation && !anyModule) { - return moduleIdExcludeSpec(moduleId); - } else if (!anyModule) { - return moduleNameExcludeSpec(moduleId.getName()); - } else if (!anyOrganisation) { - return groupNameExcludeSpec(moduleId.getGroup()); - } else { - return EXCLUDE_ALL_MODULES_SPEC; - } - } else { - return new ArtifactExcludeSpec(moduleId, artifact); - } - } - - private ModuleIdExcludeSpec moduleIdExcludeSpec(ModuleIdentifier id) { - ModuleIdExcludeSpec spec = moduleIdSpecs.get(id); - if (spec == null) { - spec = new ModuleIdExcludeSpec(id); - moduleIdSpecs.put(id, spec); - } - return spec; - } - - private ModuleNameExcludeSpec moduleNameExcludeSpec(String id) { - ModuleNameExcludeSpec spec = moduleNameSpecs.get(id); - if (spec == null) { - spec = new ModuleNameExcludeSpec(id); - moduleNameSpecs.put(id, spec); - } - return spec; - } - - private GroupNameExcludeSpec groupNameExcludeSpec(String id) { - GroupNameExcludeSpec spec = groupNameSpecs.get(id); - if (spec == null) { - spec = new GroupNameExcludeSpec(id); - groupNameSpecs.put(id, spec); - } - return spec; - } - - /** - * Returns a spec that excludes those modules and artifacts that are excluded by _either_ of the given exclude rules. - */ - public ModuleExclusion either(ModuleExclusion one, ModuleExclusion two) { - if (one == two) { - return one; - } - if (one == EXCLUDE_NONE) { - return two; - } - if (two == EXCLUDE_NONE) { - return one; - } - if (one.equals(two)) { - return one; - } - - if (one instanceof EitherExclusion && ((EitherExclusion) one).getFilters().contains(two)) { - return one; - } else if (two instanceof EitherExclusion && ((EitherExclusion) two).getFilters().contains(one)) { - return two; - } - - AbstractModuleExclusion aOne = (AbstractModuleExclusion) one; - AbstractModuleExclusion aTwo = (AbstractModuleExclusion) two; - - List builder = Lists.newArrayListWithExpectedSize(estimateSize(aOne) + estimateSize(aTwo)); - - aOne.unpackEither(builder); - aTwo.unpackEither(builder); - - return asEither(ImmutableSet.copyOf(builder)); - } - - private static int estimateSize(AbstractModuleExclusion ex) { - if (ex instanceof AbstractCompositeExclusion) { - return ((AbstractCompositeExclusion) ex).getFilters().size(); - } - return 1; - } - - /** - * Returns a spec that excludes only those modules and artifacts that are excluded by _both_ of the supplied exclude rules. - */ - public ModuleExclusion both(ModuleExclusion one, ModuleExclusion two) { - if (one == two) { - return one; - } - if (one == EXCLUDE_NONE || two == EXCLUDE_NONE) { - return EXCLUDE_NONE; - } - if (one.equals(two)) { - return one; - } - - List specs = new ArrayList(); - ((AbstractModuleExclusion) one).unpackAll(specs); - ((AbstractModuleExclusion) two).unpackAll(specs); - for (int i = 0; i < specs.size();) { - AbstractModuleExclusion spec = specs.get(i); - AbstractModuleExclusion merged = null; - // See if we can merge any of the following specs into one - for (int j = i + 1; j < specs.size(); j++) { - AbstractModuleExclusion other = specs.get(j); - merged = maybeMergeIntoAll(spec, other); - if (merged != null) { - specs.remove(j); - break; + // please keep the formatting below as it helps enabling or disabling stages + private final ExcludeFactory factory = new OptimizingExcludeFactory(// optimizes for nulls, 2-params, ... mandatory + new CachingExcludeFactory(// caches the result of operations + new NormalizingExcludeFactory(// performs algebra + new DefaultExcludeFactory()// the end of the chain, mandatory + ) + ) + ); + private final Map metadataToExcludeCache = Maps.newConcurrentMap(); + private final ExcludeSpec nothing; + + public ModuleExclusions() { + nothing = factory.nothing(); + } + + public ExcludeSpec excludeAny(ImmutableList excludes) { + return factory.anyOf(excludes.stream() + .map(this::forExclude) + .collect(Collectors.toList())); + } + + public ExcludeSpec nothing() { + return nothing; + } + + private ExcludeSpec forExclude(ExcludeMetadata r) { + return metadataToExcludeCache.computeIfAbsent(r, rule -> { + // For custom ivy pattern matchers, don't inspect the rule any more deeply: this prevents us from doing smart merging later + if (!PatternMatchers.isExactMatcher(rule.getMatcher())) { + return factory.ivyPatternExclude(rule.getModuleId(), rule.getArtifact(), rule.getMatcher()); + } + + ModuleIdentifier moduleId = rule.getModuleId(); + IvyArtifactName artifact = rule.getArtifact(); + boolean anyOrganisation = isWildcard(moduleId.getGroup()); + boolean anyModule = isWildcard(moduleId.getName()); + + // Build a strongly typed (mergeable) exclude spec for each supplied rule + if (artifact == null) { + if (!anyOrganisation && !anyModule) { + return factory.moduleId(moduleId); + } else if (!anyModule) { + return factory.module(moduleId.getName()); + } else if (!anyOrganisation) { + return factory.group(moduleId.getGroup()); + } else { + return factory.everything(); } - } - if (merged != null) { - specs.set(i, merged); } else { - i++; - } - } - if (specs.size() == 1) { - return specs.get(0); - } - return new AllExclusion(specs); - } - - /** - * Attempt to merge 2 exclusions into a single filter that is the union of both. - * Currently this is only implemented when both exclusions are `IntersectionExclusion`s. - */ - private AbstractModuleExclusion maybeMergeIntoAll(AbstractModuleExclusion one, AbstractModuleExclusion two) { - if (one.equals(two)) { - return one; - } - if (one instanceof EitherExclusion && two instanceof EitherExclusion) { - return maybeMergeIntoAll((EitherExclusion) one, (EitherExclusion) two); - } - return null; - } - - private AbstractModuleExclusion maybeMergeIntoAll(EitherExclusion one, EitherExclusion other) { - if (one.equals(other)) { - return one; - } - if (one.canMerge() && other.canMerge()) { - AbstractModuleExclusion[] oneFilters = one.getFilters().elements; - AbstractModuleExclusion[] otherFilters = other.getFilters().elements; - if (Arrays.equals(oneFilters, otherFilters)) { - return one; - } - - MergeOperation merge = mergeOperation(oneFilters, otherFilters); - AbstractModuleExclusion exclusion = mergeCache.get(merge); - if (exclusion != null) { - return exclusion; - } - return mergeAndCacheResult(merge, oneFilters, otherFilters); - } - return null; - } - - private MergeOperation mergeOperation(AbstractModuleExclusion[] one, AbstractModuleExclusion[] two) { - synchronized (mergeOperationLock) { - Map oneMap = mergeOperationCache.get(one); - if (oneMap == null) { - oneMap = Maps.newIdentityHashMap(); - mergeOperationCache.put(one, oneMap); - - } - MergeOperation mergeOperation = oneMap.get(two); - if (mergeOperation != null) { - return mergeOperation; - } - mergeOperation = new MergeOperation(one, two); - oneMap.put(two, mergeOperation); - return mergeOperation; - } - } - - private AbstractModuleExclusion mergeAndCacheResult(MergeOperation merge, AbstractModuleExclusion[] oneFilters, AbstractModuleExclusion[] otherFilters) { - AbstractModuleExclusion exclusion; // Merge the exclude rules from both specs into a single union spec. - final BitSet remaining = new BitSet(otherFilters.length); - remaining.set(0, otherFilters.length, true); - MergeSet merged = new MergeSet(remaining, oneFilters.length + otherFilters.length); - for (AbstractModuleExclusion thisSpec : oneFilters) { - if (!remaining.isEmpty()) { - for (int i = remaining.nextSetBit(0); i >= 0; i = remaining.nextSetBit(i+1)) { - AbstractModuleExclusion otherSpec = otherFilters[i]; - merged.current = otherSpec; - merged.idx = i; - mergeExcludeRules(thisSpec, otherSpec, merged); - } - } - } - if (merged.isEmpty()) { - exclusion = ModuleExclusions.EXCLUDE_NONE; - } else { - exclusion = asEither(ImmutableSet.copyOf(merged)); - } - mergeCache.put(merge, exclusion); - return exclusion; - } - - private EitherExclusion asEither(ImmutableSet excludes) { - EitherExclusion cached = eitherCache.get(excludes); - if (cached == null) { - cached = new EitherExclusion(new ImmutableModuleExclusionSet(excludes)); - eitherCache.put(excludes, cached); - } - return cached; - } - - // Add exclusions to the list that will exclude modules/artifacts that are excluded by _both_ of the candidate rules. - private void mergeExcludeRules(AbstractModuleExclusion spec1, AbstractModuleExclusion spec2, Set merged) { - if (spec1 == spec2) { - merged.add(spec1); - } else if (spec1 instanceof ExcludeAllModulesSpec) { - // spec1 excludes everything: use spec2 excludes - merged.add(spec2); - } else if (spec2 instanceof ExcludeAllModulesSpec) { - // spec2 excludes everything: use spec1 excludes - merged.add(spec1); - } else if (spec1 instanceof ArtifactExcludeSpec) { - // Excludes _no_ modules, may exclude some artifacts. - // This isn't right: We are losing the artifacts excluded by spec2 - // (2 artifact excludes should cancel out unless equal) - merged.add(spec1); - } else if (spec2 instanceof ArtifactExcludeSpec) { - // Excludes _no_ modules, may exclude some artifacts. - // This isn't right: We are losing the artifacts excluded by spec2 - merged.add(spec2); - } else if (spec1 instanceof GroupNameExcludeSpec) { - // Merge into a single exclusion for Group + Module - mergeExcludeRules((GroupNameExcludeSpec) spec1, spec2, merged); - } else if (spec2 instanceof GroupNameExcludeSpec) { - // Merge into a single exclusion for Group + Module - mergeExcludeRules((GroupNameExcludeSpec) spec2, spec1, merged); - } else if (spec1 instanceof ModuleNameExcludeSpec) { - // Merge into a single exclusion for Group + Module - mergeExcludeRules((ModuleNameExcludeSpec) spec1, spec2, merged); - } else if (spec2 instanceof ModuleNameExcludeSpec) { - // Merge into a single exclusion for Group + Module - mergeExcludeRules((ModuleNameExcludeSpec) spec2, spec1, merged); - } else if ((spec1 instanceof ModuleIdExcludeSpec) && (spec2 instanceof ModuleIdExcludeSpec)) { - // Excludes nothing if the excluded module ids don't match: in that case this rule contributes nothing to the union - ModuleIdExcludeSpec moduleSpec1 = (ModuleIdExcludeSpec) spec1; - ModuleIdExcludeSpec moduleSpec2 = (ModuleIdExcludeSpec) spec2; - if (moduleSpec1.moduleId.equals(moduleSpec2.moduleId)) { - merged.add(moduleSpec1); + return factory.ivyPatternExclude(moduleId, artifact, rule.getMatcher()); } - } else { - throw new UnsupportedOperationException(String.format("Cannot calculate intersection of exclude rules: %s, %s", spec1, spec2)); - } + }); } - private void mergeExcludeRules(GroupNameExcludeSpec spec1, AbstractModuleExclusion spec2, Set merged) { - if (spec2 instanceof GroupNameExcludeSpec) { - // Intersection of 2 group excludes does nothing unless excluded groups match - GroupNameExcludeSpec groupNameExcludeSpec = (GroupNameExcludeSpec) spec2; - if (spec1.group.equals(groupNameExcludeSpec.group)) { - merged.add(spec1); - } - } else if (spec2 instanceof ModuleNameExcludeSpec) { - // Intersection of group & module name exclude only excludes module with matching group + name - ModuleNameExcludeSpec moduleNameExcludeSpec = (ModuleNameExcludeSpec) spec2; - merged.add(moduleIdExcludeSpec(moduleIdentifierFactory.module(spec1.group, moduleNameExcludeSpec.module))); - } else if (spec2 instanceof ModuleIdExcludeSpec) { - // Intersection of group + module id exclude only excludes the module id if the excluded groups match - ModuleIdExcludeSpec moduleIdExcludeSpec = (ModuleIdExcludeSpec) spec2; - if (moduleIdExcludeSpec.moduleId.getGroup().equals(spec1.group)) { - merged.add(spec2); - } - } else { - throw new UnsupportedOperationException(String.format("Cannot calculate intersection of exclude rules: %s, %s", spec1, spec2)); - } + private static boolean isWildcard(String attribute) { + return PatternMatchers.ANY_EXPRESSION.equals(attribute); } - private static void mergeExcludeRules(ModuleNameExcludeSpec spec1, AbstractModuleExclusion spec2, Set merged) { - if (spec2 instanceof ModuleNameExcludeSpec) { - // Intersection of 2 module name excludes does nothing unless excluded module names match - ModuleNameExcludeSpec moduleNameExcludeSpec = (ModuleNameExcludeSpec) spec2; - if (spec1.module.equals(moduleNameExcludeSpec.module)) { - merged.add(spec1); - } - } else if (spec2 instanceof ModuleIdExcludeSpec) { - // Intersection of module name & module id exclude only excludes module if the excluded module names match - ModuleIdExcludeSpec moduleIdExcludeSpec = (ModuleIdExcludeSpec) spec2; - if (moduleIdExcludeSpec.moduleId.getName().equals(spec1.module)) { - merged.add(spec2); - } - } else { - throw new UnsupportedOperationException(String.format("Cannot calculate intersection of exclude rules: %s, %s", spec1, spec2)); - } + public ExcludeSpec excludeAny(ExcludeSpec one, ExcludeSpec two) { + return factory.anyOf(one, two); } - private static final class MergeOperation { - private final AbstractModuleExclusion[] one; - private final AbstractModuleExclusion[] two; - private final int hashCode; - - - private MergeOperation(AbstractModuleExclusion[] one, AbstractModuleExclusion[] two) { - this.one = one; - this.two = two; - this.hashCode = 31 * Arrays.hashCode(one) + Arrays.hashCode(two); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - MergeOperation that = (MergeOperation) o; - return Arrays.equals(one, that.one) && Arrays.equals(two, that.two); - } - - @Override - public int hashCode() { - return hashCode; - } + public ExcludeSpec excludeAll(ExcludeSpec one, ExcludeSpec two) { + return factory.allOf(one, two); } - private static final class MergeSet extends LinkedHashSet { - private final BitSet remaining; - private int idx; - private AbstractModuleExclusion current; - - private MergeSet(BitSet remaining, int size) { - super(size); - this.remaining = remaining; - } - - @Override - public boolean add(AbstractModuleExclusion abstractModuleExclusion) { - if (current == abstractModuleExclusion) { - remaining.clear(idx); - } - return super.add(abstractModuleExclusion); - } - } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleIdExcludeSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleIdExcludeSpec.java deleted file mode 100644 index 62dc730fa5b6..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleIdExcludeSpec.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import org.gradle.api.artifacts.ModuleIdentifier; - -/** - * Excludes any module that has a module id matching the one specified. - * Does not exclude artifacts. - */ -class ModuleIdExcludeSpec extends AbstractModuleExclusion { - final ModuleIdentifier moduleId; - - public ModuleIdExcludeSpec(ModuleIdentifier id) { - this.moduleId = id; - } - - @Override - public String toString() { - return "{module-id " + moduleId + "}"; - } - - @Override - protected boolean doEquals(Object o) { - ModuleIdExcludeSpec other = (ModuleIdExcludeSpec) o; - return moduleId.equals(other.moduleId); - } - - @Override - protected int doHashCode() { - return moduleId.hashCode(); - } - - @Override - protected boolean doExcludesSameModulesAs(AbstractModuleExclusion other) { - ModuleIdExcludeSpec moduleIdExcludeSpec = (ModuleIdExcludeSpec) other; - return moduleId.equals(moduleIdExcludeSpec.moduleId); - } - - public boolean excludeModule(ModuleIdentifier module) { - return module.equals(moduleId); - } -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleNameExcludeSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleNameExcludeSpec.java deleted file mode 100644 index 0a8effc3c4c9..000000000000 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleNameExcludeSpec.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes; - -import org.gradle.api.artifacts.ModuleIdentifier; - -/** - * Excludes any module that has a module name matching the one specified. - * Does not exclude artifacts. - */ -class ModuleNameExcludeSpec extends AbstractModuleExclusion { - final String module; - - ModuleNameExcludeSpec(String module) { - this.module = module; - } - - @Override - public String toString() { - return "{module " + module + "}"; - } - - @Override - protected boolean doEquals(Object o) { - ModuleNameExcludeSpec other = (ModuleNameExcludeSpec) o; - return module.equals(other.module); - } - - @Override - protected int doHashCode() { - return module.hashCode(); - } - - @Override - public boolean doExcludesSameModulesAs(AbstractModuleExclusion other) { - ModuleNameExcludeSpec moduleNameExcludeSpec = (ModuleNameExcludeSpec) other; - return module.equals(moduleNameExcludeSpec.module); - } - - public boolean excludeModule(ModuleIdentifier element) { - return element.getName().equals(module); - } -} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/PatternMatchers.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/PatternMatchers.java index 62f80b6410f1..be93559e78ba 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/PatternMatchers.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/PatternMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * 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. diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java new file mode 100644 index 000000000000..6d4936102d77 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java @@ -0,0 +1,149 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; + +import com.google.common.collect.Maps; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; + +import java.util.List; +import java.util.Map; + +/** + * This factory is responsible for caching merging queries. It delegates computations + * to another factory, so if the delegate returns the same instances for the same + * queries, caching will be faster. + */ +public class CachingExcludeFactory extends DelegatingExcludeFactory { + private final Map allOfPairCache = Maps.newConcurrentMap(); + private final Map anyOfPairCache = Maps.newConcurrentMap(); + private final Map allOfListCache = Maps.newConcurrentMap(); + private final Map anyOfListCache = Maps.newConcurrentMap(); + + public CachingExcludeFactory(ExcludeFactory delegate) { + super(delegate); + } + + @Override + public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { + return cachedAnyPair(one, two); + } + + private ExcludeSpec cachedAnyPair(ExcludeSpec left, ExcludeSpec right) { + return anyOfPairCache.computeIfAbsent(new ExcludePair(left, right), key -> delegate.anyOf(key.left, key.right)); + } + + @Override + public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { + return cachedAllPair(one, two); + } + + private ExcludeSpec cachedAllPair(ExcludeSpec left, ExcludeSpec right) { + return allOfPairCache.computeIfAbsent(new ExcludePair(left, right), key -> delegate.allOf(key.left, key.right)); + } + + @Override + public ExcludeSpec anyOf(List specs) { + return anyOfListCache.computeIfAbsent(new ExcludeList(specs), key -> delegate.anyOf(key.specs)); + } + + @Override + public ExcludeSpec allOf(List specs) { + return allOfListCache.computeIfAbsent(new ExcludeList(specs), key -> delegate.allOf(key.specs)); + } + + /** + * A special key which recognizes the fact union and intersection + * are commutative. + */ + private final static class ExcludePair { + private final ExcludeSpec left; + private final ExcludeSpec right; + private final int hashCode; + + private ExcludePair(ExcludeSpec left, ExcludeSpec right) { + this.left = left; + this.right = right; + this.hashCode = left.hashCode() ^ right.hashCode(); // must be symmetrical + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ExcludePair that = (ExcludePair) o; + + return (left.equals(that.left) && right.equals(that.right)) + // symmetry + || (left.equals(that.right) && right.equals(that.left)); + } + + @Override + public int hashCode() { + return hashCode; + } + } + + /** + * A special exclude spec list key which recognizes + * that union and intersection are commutative. + */ + private static class ExcludeList { + private final List specs; + private final int size; + private final int hashCode; + + private ExcludeList(List specs) { + this.specs = specs; + this.size = specs.size(); + this.hashCode = computeHashCode(); + } + + private int computeHashCode() { + int hash = 0; + for (ExcludeSpec spec : specs) { + hash += spec.hashCode(); + } + return hash; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ExcludeList that = (ExcludeList) o; + if (size != that.size) { + return false; + } + return specs.containsAll(that.specs); + } + + @Override + public int hashCode() { + return hashCode; + } + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java new file mode 100644 index 000000000000..3c5f35f0b7e6 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java @@ -0,0 +1,92 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.internal.component.model.IvyArtifactName; + +import java.util.List; +import java.util.Set; + +public abstract class DelegatingExcludeFactory implements ExcludeFactory { + protected final ExcludeFactory delegate; + + public DelegatingExcludeFactory(ExcludeFactory delegate) { + this.delegate = delegate; + } + + @Override + public ExcludeSpec nothing() { + return delegate.nothing(); + } + + @Override + public ExcludeSpec everything() { + return delegate.everything(); + } + + @Override + public ExcludeSpec group(String group) { + return delegate.group(group); + } + + @Override + public ExcludeSpec module(String module) { + return delegate.module(module); + } + + @Override + public ExcludeSpec moduleId(ModuleIdentifier id) { + return delegate.moduleId(id); + } + + @Override + public ExcludeSpec artifact(ModuleIdentifier id, IvyArtifactName artifact) { + return delegate.artifact(id, artifact); + } + + @Override + public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { + return delegate.anyOf(one, two); + } + + @Override + public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { + return delegate.allOf(one, two); + } + + @Override + public ExcludeSpec anyOf(List specs) { + return delegate.anyOf(specs); + } + + @Override + public ExcludeSpec allOf(List specs) { + return delegate.allOf(specs); + } + + @Override + public ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher) { + return delegate.ivyPatternExclude(moduleId, artifact, matcher); + } + + @Override + public ExcludeSpec moduleSet(Set modules) { + return delegate.moduleSet(modules); + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java new file mode 100644 index 000000000000..f3b4720c8fad --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -0,0 +1,158 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.CompositeExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAllOf; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAnyOf; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * This factory performs normalization of exclude rules. This is the smartest + * of all factories and is responsible for doing some basic algebra computations. + * It shouldn't be too slow, or the whole chain will pay the price. + */ +public class NormalizingExcludeFactory extends DelegatingExcludeFactory { + public NormalizingExcludeFactory(ExcludeFactory delegate) { + super(delegate); + } + + @Override + public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { + return doUnion(ImmutableList.of(one, two)); + } + + @Override + public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { + return doIntersect(ImmutableList.of(one, two)); + } + + @Override + public ExcludeSpec anyOf(List specs) { + return doUnion(specs); + } + + @Override + public ExcludeSpec allOf(List specs) { + return doIntersect(specs); + } + + private ExcludeSpec doUnion(List specs) { + Set simpleExcludes = null; + List moduleSets = null; + Set flattened = flatten(ExcludeAnyOf.class, specs, ExcludeEverything.class::isInstance, ExcludeNothing.class::isInstance); + if (flattened == null) { + return everything(); + } + for (Iterator it = flattened.iterator(); it.hasNext(); ) { + ExcludeSpec spec = it.next(); + if (spec instanceof ModuleIdExclude) { + if (simpleExcludes == null) { + simpleExcludes = Sets.newHashSetWithExpectedSize(specs.size()); + } + simpleExcludes.add((ModuleIdExclude) spec); + it.remove(); + } + // will allow merging module sets into a single one + if (spec instanceof ModuleSetExclude) { + if (moduleSets == null) { + moduleSets = Lists.newArrayList(); + } + moduleSets.add((ModuleSetExclude) spec); + it.remove(); + + } + } + // merge all single module id into an id set + if (simpleExcludes != null) { + if (simpleExcludes.size() > 1 || moduleSets != null) { + ModuleSetExclude e = (ModuleSetExclude) delegate.moduleSet(simpleExcludes.stream().map(ModuleIdExclude::getModuleId).collect(Collectors.toSet())); + if (moduleSets != null) { + moduleSets.add(e); + } else { + flattened.add(e); + } + } else { + flattened.add(simpleExcludes.iterator().next()); + } + } + if (moduleSets != null) { + if (moduleSets.size() == 1) { + flattened.add(moduleSets.get(0)); + } else { + // merge all module sets + flattened.add(delegate.moduleSet(moduleSets.stream().flatMap(e -> e.getModuleIds().stream()).collect(Collectors.toSet()))); + } + } + if (flattened.isEmpty()) { + return nothing(); + } + return Optimizations.optimizeList(this, ImmutableList.copyOf(flattened), delegate::anyOf); + } + + /** + * Flattens a collection of elements that are going to be joined or intersected. There + * are 3 possible outcomes: + * - null means that the fast exit condition was reached, meaning that the caller knows it's not worth computing more + * - empty list meaning that an easy simplification was reached and we directly know the result + * - flattened unions/intersections + */ + private Set flatten(Class flattenType, List specs, Predicate fastExit, Predicate filter) { + Set out = null; + for (ExcludeSpec spec : specs) { + if (fastExit.test(spec)) { + return null; + } + if (!filter.test(spec)) { + if (out == null) { + out = Sets.newHashSetWithExpectedSize(4 * specs.size()); + } + if (flattenType.isInstance(spec)) { + out.addAll(((CompositeExclude) spec).getComponents()); + } else { + out.add(spec); + } + } + } + return out == null ? Collections.emptySet() : out; + } + + private ExcludeSpec doIntersect(List specs) { + Set relevant = flatten(ExcludeAllOf.class, specs, ExcludeNothing.class::isInstance, ExcludeEverything.class::isInstance); + if (relevant == null) { + return nothing(); + } + if (relevant.isEmpty()) { + return everything(); + } + return Optimizations.optimizeList(this, ImmutableList.copyOf(relevant), delegate::allOf); + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.java new file mode 100644 index 000000000000..cf68477477ff --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.java @@ -0,0 +1,89 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; + +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +public abstract class Optimizations { + public static ExcludeSpec optimizeAnyOf(ExcludeSpec one, ExcludeSpec two, BiFunction onMiss) { + // fast path for two + if (one == null) { + return two; + } + if (two == null) { + return one; + } + if (one.equals(two)) { + return one; + } + if (one instanceof ExcludeEverything) { + return one; + } + if (one instanceof ExcludeNothing) { + return two; + } + if (two instanceof ExcludeEverything) { + return two; + } + if (two instanceof ExcludeNothing) { + return one; + } + return onMiss.apply(one, two); + } + + public static ExcludeSpec optimizeAllOf(ExcludeFactory factory, ExcludeSpec one, ExcludeSpec two, BiFunction onMiss) { + // fast path for two + if (one == null) { + return two; + } + if (two == null) { + return one; + } + if (one.equals(two)) { + return one; + } + if (one instanceof ExcludeEverything) { + return two; + } + if (one instanceof ExcludeNothing) { + return factory.nothing(); + } + if (two instanceof ExcludeEverything) { + return one; + } + if (two instanceof ExcludeNothing) { + return factory.nothing(); + } + return onMiss.apply(one, two); + } + + public static ExcludeSpec optimizeList(ExcludeFactory factory, List specs, Function, ExcludeSpec> onMiss) { + if (specs.isEmpty()) { + return factory.nothing(); + } + if (specs.size() == 1) { + return specs.get(0); + } + return onMiss.apply(specs); + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java new file mode 100644 index 000000000000..d1b277af5da7 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java @@ -0,0 +1,61 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; + +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; + +import java.util.List; + +/** + * This factory is responsible for optimizing in special cases: null parameters, + * list with 2 elements, ... and should be at the top of the delegation chain. + */ +public class OptimizingExcludeFactory extends DelegatingExcludeFactory { + public OptimizingExcludeFactory(ExcludeFactory delegate) { + super(delegate); + } + + @Override + public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { + return Optimizations.optimizeAnyOf(one, two, super::anyOf); + } + + @Override + public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { + return Optimizations.optimizeAllOf(this, one, two, super::allOf); + } + + @Override + public ExcludeSpec anyOf(List specs) { + return Optimizations.optimizeList(this, specs, list -> { + if (list.size() == 2) { + return delegate.anyOf(list.get(0), list.get(1)); + } + return delegate.anyOf(list); + }); + } + + @Override + public ExcludeSpec allOf(List specs) { + return Optimizations.optimizeList(this, specs, list -> { + if (list.size() == 2) { + return delegate.allOf(list.get(0), list.get(1)); + } + return delegate.allOf(list); + }); + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/package-info.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/package-info.java new file mode 100644 index 000000000000..5b3deca545d0 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * This package contains different accessory factories for excludes. + */ +package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java new file mode 100644 index 000000000000..fc694803d425 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java @@ -0,0 +1,117 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableSet; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.CompositeExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; + +import java.util.Iterator; +import java.util.stream.Stream; + +abstract class DefaultCompositeExclude implements CompositeExclude { + private final ImmutableSet components; + private final int hashCode; + + DefaultCompositeExclude(ImmutableSet components) { + this.components = components; + this.hashCode = components.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultCompositeExclude that = (DefaultCompositeExclude) o; + return hashCode == that.hashCode && Objects.equal(components, that.components); + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultCompositeExclude that = (DefaultCompositeExclude) o; + return equalsIgnoreArtifact(components, that.components); + } + + private boolean equalsIgnoreArtifact(ImmutableSet components, ImmutableSet other) { + if (components == other) { + return true; + } + if (components.size() != other.size()) { + return false; + } + return compareExcludingArtifacts(components, other); + } + + private boolean compareExcludingArtifacts(ImmutableSet components, ImmutableSet other) { + // The fast check iterator is there assuming that we have 2 collections with identical contents + // in which case we can perform a faster check for sets, as if they were lists + Iterator fastCheckIterator = other.iterator(); + for (ExcludeSpec component : components) { + boolean found = false; + if (fastCheckIterator != null && fastCheckIterator.next().equalsIgnoreArtifact(component)) { + break; + } + // we're unlucky, sets are either different, or in a different order + fastCheckIterator = null; + for (ExcludeSpec o : other) { + if (component.equalsIgnoreArtifact(o)) { + found = true; + break; + } + } + if (!found) { + // fast exit, when sets are actually different + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public final Stream components() { + return components.stream(); + } + + @Override + public ImmutableSet getComponents() { + return components; + } + + @Override + public String toString() { + return "{" + getDisplayName() + + " " + components + + '}'; + } + + protected abstract String getDisplayName(); +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java new file mode 100644 index 000000000000..3f027c46b8cf --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java @@ -0,0 +1,55 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import com.google.common.collect.ImmutableSet; +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAllOf; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.internal.component.model.IvyArtifactName; + +import java.util.List; + +final class DefaultExcludeAllOf extends DefaultCompositeExclude implements ExcludeAllOf { + public static ExcludeSpec of(List components) { + return new DefaultExcludeAllOf(ImmutableSet.copyOf(components)); + } + + private DefaultExcludeAllOf(ImmutableSet components) { + super(components); + } + + @Override + protected String getDisplayName() { + return "all of"; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return components().allMatch(e -> e.excludes(module)); + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return components().allMatch(e -> e.excludesArtifact(module, artifactName)); + } + + @Override + public boolean mayExcludeArtifacts() { + return components().allMatch(ExcludeSpec::mayExcludeArtifacts); + } + +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java new file mode 100644 index 000000000000..ab64e51b10aa --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java @@ -0,0 +1,54 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import com.google.common.collect.ImmutableSet; +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAnyOf; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.internal.component.model.IvyArtifactName; + +import java.util.List; + +final class DefaultExcludeAnyOf extends DefaultCompositeExclude implements ExcludeAnyOf { + public static ExcludeSpec of(List components) { + return new DefaultExcludeAnyOf(ImmutableSet.copyOf(components)); + } + + private DefaultExcludeAnyOf(ImmutableSet components) { + super(components); + } + + @Override + protected String getDisplayName() { + return "any of"; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return components().anyMatch(e -> e.excludes(module)); + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return components().anyMatch(e -> e.excludesArtifact(module, artifactName)); + } + + @Override + public boolean mayExcludeArtifacts() { + return components().anyMatch(ExcludeSpec::mayExcludeArtifacts); + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeEverything.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeEverything.java new file mode 100644 index 000000000000..d0fa7eab8725 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeEverything.java @@ -0,0 +1,72 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.internal.component.model.IvyArtifactName; + +final class DefaultExcludeEverything implements ExcludeEverything { + private static final ExcludeEverything INSTANCE = new DefaultExcludeEverything(); + + public static ExcludeSpec get() { + return INSTANCE; + } + + private DefaultExcludeEverything() { + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return true; + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + // everything excluded **only** applies to modules, not artifacts! + return false; + } + + @Override + public boolean mayExcludeArtifacts() { + return false; // an exclude all is for modules, not artifacts + } + + @Override + public String toString() { + return "{excludes everything}"; + } + + @Override + public int hashCode() { + return 1; + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec o) { + return this == o; + } + + private int size() { + return 1; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java new file mode 100644 index 000000000000..6ac063956f9f --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java @@ -0,0 +1,88 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import com.google.common.collect.ImmutableList; +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.Optimizations; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.internal.component.model.IvyArtifactName; + +import java.util.List; +import java.util.Set; + +public class DefaultExcludeFactory implements ExcludeFactory { + @Override + public ExcludeSpec nothing() { + return DefaultExcludeNothing.get(); + } + + @Override + public ExcludeSpec everything() { + return DefaultExcludeEverything.get(); + } + + @Override + public ExcludeSpec group(String group) { + return DefaultGroupExclude.of(group); + } + + @Override + public ExcludeSpec module(String module) { + return DefaultModuleExclude.of(module); + } + + @Override + public ExcludeSpec moduleId(ModuleIdentifier id) { + return DefaultModuleIdExclude.of(id); + } + + @Override + public ExcludeSpec artifact(ModuleIdentifier id, IvyArtifactName artifact) { + return DefaultModuleArtifactExclude.of(id, artifact); + } + + @Override + public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { + return Optimizations.optimizeAnyOf(one, two, (a, b) -> DefaultExcludeAnyOf.of(ImmutableList.of(a, b))); + } + + @Override + public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { + return Optimizations.optimizeAllOf(this, one, two, (a, b) -> DefaultExcludeAllOf.of(ImmutableList.of(a, b))); + } + + @Override + public ExcludeSpec anyOf(List specs) { + return DefaultExcludeAnyOf.of(specs); + } + + @Override + public ExcludeSpec allOf(List specs) { + return DefaultExcludeAllOf.of(specs); + } + + @Override + public ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher) { + return DefaultIvyPatternMatcherExcludeRuleSpec.of(moduleId, artifact, matcher); + } + + @Override + public ExcludeSpec moduleSet(Set modules) { + return DefaultModuleSetExclude.of(modules); + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeNothing.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeNothing.java new file mode 100644 index 000000000000..d86cc95a5e53 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeNothing.java @@ -0,0 +1,71 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.internal.component.model.IvyArtifactName; + +class DefaultExcludeNothing implements ExcludeNothing { + private static final ExcludeNothing INSTANCE = new DefaultExcludeNothing(); + + public static ExcludeSpec get() { + return INSTANCE; + } + + private DefaultExcludeNothing() { + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return false; + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return false; + } + + @Override + public boolean mayExcludeArtifacts() { + return false; + } + + @Override + public String toString() { + return "{excludes none}"; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec o) { + return this == o; + } + + private int size() { + return 1; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupExclude.java new file mode 100644 index 000000000000..9265f9d9f841 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupExclude.java @@ -0,0 +1,88 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude; +import org.gradle.internal.component.model.IvyArtifactName; + +final class DefaultGroupExclude implements GroupExclude { + private final String group; + private final int hashCode; + + private DefaultGroupExclude(String group) { + this.group = group; + this.hashCode = group.hashCode(); + } + + static ExcludeSpec of(String group) { + return new DefaultGroupExclude(group); + } + + @Override + public String getGroup() { + return group; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return group.equals(module.getGroup()); + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return false; + } + + @Override + public boolean mayExcludeArtifacts() { + return false; + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec other) { + return equals(other); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultGroupExclude that = (DefaultGroupExclude) o; + + if (hashCode != that.hashCode) { + return false; + } + return group.equals(that.group); + + } + + @Override + public int hashCode() { + return group.hashCode(); + } + + @Override + public String toString() { + return "{exclude group = '" + group + "'}"; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultIvyPatternMatcherExcludeRuleSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultIvyPatternMatcherExcludeRuleSpec.java new file mode 100644 index 000000000000..c8245cb56735 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultIvyPatternMatcherExcludeRuleSpec.java @@ -0,0 +1,118 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import com.google.common.base.Objects; +import org.apache.ivy.plugins.matcher.PatternMatcher; +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.PatternMatchers; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.IvyPatternMatcherExcludeRuleSpec; +import org.gradle.internal.component.model.IvyArtifactName; + +final class DefaultIvyPatternMatcherExcludeRuleSpec implements IvyPatternMatcherExcludeRuleSpec { + private final ModuleIdentifier moduleId; + private final IvyArtifactName ivyArtifactName; + private final PatternMatcher matcher; + private final boolean isArtifactExclude; + private final int hashCode; + + public static ExcludeSpec of(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher) { + return new DefaultIvyPatternMatcherExcludeRuleSpec(moduleId, artifact, matcher); + } + + private DefaultIvyPatternMatcherExcludeRuleSpec(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher) { + this.moduleId = moduleId; + this.ivyArtifactName = artifact; + this.matcher = PatternMatchers.getInstance().getMatcher(matcher); + isArtifactExclude = ivyArtifactName != null; + hashCode = Objects.hashCode(moduleId, ivyArtifactName, matcher, isArtifactExclude); + } + + @Override + public String toString() { + return "{exclude-rule " + moduleId + ":" + ivyArtifactName + " with matcher " + matcher.getName() + "}"; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + if (isArtifactExclude) { + return false; + } + return matches(moduleId.getGroup(), module.getGroup()) && matches(moduleId.getName(), module.getName()); + } + + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifact) { + if (!isArtifactExclude) { + return false; + } + return matches(moduleId.getGroup(), module.getGroup()) + && matches(moduleId.getName(), module.getName()) + && matches(ivyArtifactName.getName(), artifact.getName()) + && matches(ivyArtifactName.getExtension(), artifact.getExtension()) + && matches(ivyArtifactName.getType(), artifact.getType()); + } + + public boolean mayExcludeArtifacts() { + return isArtifactExclude; + } + + private boolean matches(String expression, String input) { + return matcher.getMatcher(expression).matches(input); + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultIvyPatternMatcherExcludeRuleSpec that = (DefaultIvyPatternMatcherExcludeRuleSpec) o; + return isArtifactExclude == that.isArtifactExclude && + Objects.equal(moduleId, that.moduleId) && + Objects.equal(matcher, that.matcher); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultIvyPatternMatcherExcludeRuleSpec that = (DefaultIvyPatternMatcherExcludeRuleSpec) o; + return hashCode == that.hashCode && + isArtifactExclude == that.isArtifactExclude && + Objects.equal(moduleId, that.moduleId) && + Objects.equal(ivyArtifactName, that.ivyArtifactName) && + Objects.equal(matcher, that.matcher); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public IvyArtifactName getArtifact() { + return ivyArtifactName; + } + +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleArtifactExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleArtifactExclude.java new file mode 100644 index 000000000000..69af6ce4af41 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleArtifactExclude.java @@ -0,0 +1,85 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ArtifactExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.internal.component.model.IvyArtifactName; + +final class DefaultModuleArtifactExclude implements ArtifactExclude { + private final ModuleIdentifier moduleId; + private final IvyArtifactName artifactName; + private final int hashCode; + + static ArtifactExclude of(ModuleIdentifier id, IvyArtifactName artifact) { + return new DefaultModuleArtifactExclude(id, artifact); + } + + private DefaultModuleArtifactExclude(ModuleIdentifier moduleId, IvyArtifactName artifactName) { + this.moduleId = moduleId; + this.artifactName = artifactName; + this.hashCode = 31 * moduleId.hashCode() + artifactName.hashCode(); + } + + @Override + public IvyArtifactName getArtifact() { + return artifactName; + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return moduleId.equals(module) && this.artifactName.equals(artifactName); + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + DefaultModuleArtifactExclude exc = (DefaultModuleArtifactExclude) other; + return moduleId.equals(exc.moduleId); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultModuleArtifactExclude that = (DefaultModuleArtifactExclude) o; + + if (hashCode != that.hashCode) { + return false; + } + if (!moduleId.equals(that.moduleId)) { + return false; + } + return artifactName.equals(that.artifactName); + + } + + @Override + public int hashCode() { + return hashCode; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleExclude.java new file mode 100644 index 000000000000..dab2af2adc97 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleExclude.java @@ -0,0 +1,88 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleExclude; +import org.gradle.internal.component.model.IvyArtifactName; + +final class DefaultModuleExclude implements ModuleExclude { + private final String module; + private final int hashCode; + + public static ExcludeSpec of(String module) { + return new DefaultModuleExclude(module); + } + + private DefaultModuleExclude(String module) { + this.module = module; + this.hashCode = module.hashCode(); + } + + @Override + public String getModule() { + return module; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return this.module.equals(module.getName()); + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return false; + } + + @Override + public boolean mayExcludeArtifacts() { + return false; + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec other) { + return equals(other); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultModuleExclude that = (DefaultModuleExclude) o; + + if (hashCode != that.hashCode) { + return false; + } + return module.equals(that.module); + + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + return "{exclude module = '" + module + "'}"; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleIdExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleIdExclude.java new file mode 100644 index 000000000000..fa33f5b7e90f --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleIdExclude.java @@ -0,0 +1,88 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdExclude; +import org.gradle.internal.component.model.IvyArtifactName; + +final class DefaultModuleIdExclude implements ModuleIdExclude { + private final ModuleIdentifier moduleId; + private final int hashCode; + + static ModuleIdExclude of(ModuleIdentifier id) { + return new DefaultModuleIdExclude(id); + } + + private DefaultModuleIdExclude(ModuleIdentifier moduleId) { + this.moduleId = moduleId; + this.hashCode = moduleId.hashCode(); + } + + @Override + public ModuleIdentifier getModuleId() { + return moduleId; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return moduleId.equals(module); + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return false; + } + + @Override + public boolean mayExcludeArtifacts() { + return false; + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec other) { + return equals(other); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultModuleIdExclude that = (DefaultModuleIdExclude) o; + + if (hashCode != that.hashCode) { + return false; + } + return moduleId.equals(that.moduleId); + + } + + @Override + public int hashCode() { + return moduleId.hashCode(); + } + + @Override + public String toString() { + return "{exclude module id = '" + moduleId + "'}"; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleSetExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleSetExclude.java new file mode 100644 index 000000000000..f4bf9f1c3407 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleSetExclude.java @@ -0,0 +1,83 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import com.google.common.collect.ImmutableSet; +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; +import org.gradle.internal.component.model.IvyArtifactName; + +import java.util.Set; + +final class DefaultModuleSetExclude implements ModuleSetExclude { + private final Set moduleIds; + private final int hashCode; + + static ModuleSetExclude of(Set ids) { + return new DefaultModuleSetExclude(ImmutableSet.copyOf(ids)); + } + + private DefaultModuleSetExclude(ImmutableSet moduleIds) { + this.moduleIds = moduleIds; + this.hashCode = moduleIds.hashCode(); + } + + @Override + public Set getModuleIds() { + return moduleIds; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return moduleIds.contains(module); + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return false; + } + + @Override + public boolean mayExcludeArtifacts() { + return false; + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec other) { + return equals(other); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultModuleSetExclude that = (DefaultModuleSetExclude) o; + + return moduleIds.equals(that.moduleIds); + + } + + @Override + public int hashCode() { + return hashCode; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/package-info.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/package-info.java new file mode 100644 index 000000000000..b6a89d979304 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * This package contains the leaf exclude type implementations. + */ +package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ExcludeNone.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ArtifactExclude.java similarity index 62% rename from subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ExcludeNone.java rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ArtifactExclude.java index 05295501a01b..c7ef4c4758ed 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ExcludeNone.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ArtifactExclude.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * 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. @@ -13,34 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes; +package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.internal.component.model.IvyArtifactName; -class ExcludeNone extends AbstractModuleExclusion { - @Override - public String toString() { - return "{exclude-none}"; - } - - @Override - protected boolean doEquals(Object o) { - return true; - } - - @Override - protected int doHashCode() { - return 0; - } - +public interface ArtifactExclude extends ExcludeSpec { @Override - public boolean excludeModule(ModuleIdentifier module) { + default boolean excludes(ModuleIdentifier module) { return false; } @Override - protected boolean excludesNoModules() { + default boolean mayExcludeArtifacts() { return true; } + + IvyArtifactName getArtifact(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java new file mode 100644 index 000000000000..85c0bc324e43 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java @@ -0,0 +1,32 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +import java.util.Collection; +import java.util.stream.Stream; + +public interface CompositeExclude extends ExcludeSpec { + @Override + boolean equals(Object o); + + Stream components(); + + Collection getComponents(); + + default boolean contains(ExcludeSpec spec) { + return components().anyMatch(spec::equals); + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeAllOf.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeAllOf.java new file mode 100644 index 000000000000..6109f3c099ea --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeAllOf.java @@ -0,0 +1,19 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +public interface ExcludeAllOf extends CompositeExclude { +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeAnyOf.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeAnyOf.java new file mode 100644 index 000000000000..5fa17734c077 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeAnyOf.java @@ -0,0 +1,19 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +public interface ExcludeAnyOf extends CompositeExclude { +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeEverything.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeEverything.java new file mode 100644 index 000000000000..f2c94d3b7da0 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeEverything.java @@ -0,0 +1,19 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +public interface ExcludeEverything extends ExcludeSpec { +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java new file mode 100644 index 000000000000..b7e35a51351f --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java @@ -0,0 +1,48 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.internal.component.model.IvyArtifactName; + +import java.util.List; +import java.util.Set; + +public interface ExcludeFactory { + ExcludeSpec nothing(); + + ExcludeSpec everything(); + + ExcludeSpec group(String group); + + ExcludeSpec module(String module); + + ExcludeSpec moduleId(ModuleIdentifier id); + + ExcludeSpec artifact(ModuleIdentifier id, IvyArtifactName artifact); + + ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two); + + ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two); + + ExcludeSpec anyOf(List specs); + + ExcludeSpec allOf(List specs); + + ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher); + + ExcludeSpec moduleSet(Set modules); +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeNothing.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeNothing.java new file mode 100644 index 000000000000..ca1d6c1d423a --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeNothing.java @@ -0,0 +1,19 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +public interface ExcludeNothing extends ExcludeSpec { +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeSpec.java new file mode 100644 index 000000000000..7cd89f75354d --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeSpec.java @@ -0,0 +1,43 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.internal.component.model.IvyArtifactName; + +public interface ExcludeSpec { + /** + * Determines if this exclude rule excludes the supplied module. + */ + boolean excludes(ModuleIdentifier module); + + /** + * Determines if this exclude rule excludes the supplied artifact, for the specified module. + */ + boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName); + + /** + * Tells if this rule may exclude some artifacts. This is used to optimize artifact resolution. + */ + boolean mayExcludeArtifacts(); + + /** + * This method is used to determine if 2 excludes are equivalent, only differing by the artifact they exclude. + * This is used by the resolution engine to figure out if it should invalidate a previous visit or not. + */ + boolean equalsIgnoreArtifact(ExcludeSpec other); + +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/GroupExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/GroupExclude.java new file mode 100644 index 000000000000..3ecd5e978341 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/GroupExclude.java @@ -0,0 +1,20 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +public interface GroupExclude extends ExcludeSpec { + String getGroup(); +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/IvyPatternMatcherExcludeRuleSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/IvyPatternMatcherExcludeRuleSpec.java new file mode 100644 index 000000000000..56f467fa59b9 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/IvyPatternMatcherExcludeRuleSpec.java @@ -0,0 +1,19 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +public interface IvyPatternMatcherExcludeRuleSpec extends ArtifactExclude { +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleExclude.java new file mode 100644 index 000000000000..1862bd502089 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleExclude.java @@ -0,0 +1,20 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +public interface ModuleExclude extends ExcludeSpec { + String getModule(); +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleIdExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleIdExclude.java new file mode 100644 index 000000000000..4ce47c8f368d --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleIdExclude.java @@ -0,0 +1,22 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +import org.gradle.api.artifacts.ModuleIdentifier; + +public interface ModuleIdExclude extends ExcludeSpec { + ModuleIdentifier getModuleId(); +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ExcludeAllModulesSpec.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleSetExclude.java similarity index 53% rename from subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ExcludeAllModulesSpec.java rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleSetExclude.java index 633e8e65000f..de8742d30477 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ExcludeAllModulesSpec.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleSetExclude.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * 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. @@ -13,34 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes; +package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; import org.gradle.api.artifacts.ModuleIdentifier; -class ExcludeAllModulesSpec extends AbstractModuleExclusion { - @Override - public String toString() { - return "{exclude-all}"; - } - - @Override - protected boolean doEquals(Object o) { - return true; - } - - @Override - protected int doHashCode() { - return 0; - } - - @Override - public boolean doExcludesSameModulesAs(AbstractModuleExclusion other) { - return true; - } +import java.util.Set; - @Override - public boolean excludeModule(ModuleIdentifier module) { - return true; - } +public interface ModuleSetExclude extends ExcludeSpec { + Set getModuleIds(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/package-info.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/package-info.java new file mode 100644 index 000000000000..1d7c9a15a78d --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * This package contains the base interfaces for module exclusion specs. + */ +package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphEdge.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphEdge.java index c6303c9c8dce..f36e84efaf00 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphEdge.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphEdge.java @@ -17,7 +17,7 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph; import org.gradle.api.artifacts.Dependency; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.internal.component.model.ComponentArtifactMetadata; import org.gradle.internal.component.model.ConfigurationMetadata; @@ -34,7 +34,7 @@ public interface DependencyGraphEdge extends ResolvedGraphDependency { DependencyGraphSelector getSelector(); - ModuleExclusion getExclusions(); + ExcludeSpec getExclusions(); boolean contributesArtifacts(); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java index 930eff01866f..49b04b209984 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java @@ -24,7 +24,8 @@ import org.gradle.api.artifacts.component.ComponentSelector; import org.gradle.api.artifacts.result.ComponentSelectionReason; import org.gradle.api.attributes.Attribute; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphEdge; import org.gradle.api.internal.attributes.AttributeMergingException; import org.gradle.api.internal.attributes.ImmutableAttributes; @@ -54,7 +55,7 @@ class EdgeState implements DependencyGraphEdge { private final NodeState from; private final SelectorState selector; private final ResolveState resolveState; - private final ModuleExclusion transitiveExclusions; + private final ExcludeSpec transitiveExclusions; private final List targetNodes = Lists.newLinkedList(); private final boolean isTransitive; private final boolean isConstraint; @@ -62,7 +63,7 @@ class EdgeState implements DependencyGraphEdge { private ModuleVersionResolveException targetNodeSelectionFailure; private ImmutableAttributes cachedAttributes; - EdgeState(NodeState from, DependencyState dependencyState, ModuleExclusion transitiveExclusions, ResolveState resolveState) { + EdgeState(NodeState from, DependencyState dependencyState, ExcludeSpec transitiveExclusions, ResolveState resolveState) { this.from = from; this.dependencyState = dependencyState; this.dependencyMetadata = dependencyState.getDependency(); @@ -243,16 +244,17 @@ private boolean isVirtualDependency() { } @Override - public ModuleExclusion getExclusions() { + public ExcludeSpec getExclusions() { List excludes = dependencyMetadata.getExcludes(); if (excludes.isEmpty()) { return transitiveExclusions; } - ModuleExclusion edgeExclusions = resolveState.getModuleExclusions().excludeAny(ImmutableList.copyOf(excludes)); - return resolveState.getModuleExclusions().either(edgeExclusions, transitiveExclusions); + ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); + ExcludeSpec edgeExclusions = moduleExclusions.excludeAny(ImmutableList.copyOf(excludes)); + return moduleExclusions.excludeAny(edgeExclusions, transitiveExclusions); } - public ModuleExclusion getEdgeExclusions() { + ExcludeSpec getEdgeExclusions() { List excludes = dependencyMetadata.getExcludes(); if (excludes.isEmpty()) { return null; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 5198e9a4b4bf..a4f32568876c 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -28,8 +28,7 @@ import org.gradle.api.internal.artifacts.DependencySubstitutionInternal; import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier; import org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution.DependencySubstitutionApplicator; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode; import org.gradle.api.internal.attributes.ImmutableAttributesFactory; import org.gradle.api.specs.Spec; @@ -76,7 +75,8 @@ public boolean isSatisfiedBy(EdgeState edge) { private final boolean isTransitive; private final boolean selectedByVariantAwareResolution; - ModuleExclusion previousTraversalExclusions; + ExcludeSpec previousTraversalExclusions; + // In opposite to outgoing edges, virtual edges are for now pretty rare, so they are created lazily private List virtualEdges; private boolean queued; @@ -222,7 +222,7 @@ public void visitOutgoingDependencies(Collection discoveredEdges) { } // Determine the net exclusion for this node, by inspecting all transitive incoming edges - ModuleExclusion resolutionFilter = getModuleResolutionFilter(incomingEdges); + ExcludeSpec resolutionFilter = getModuleResolutionFilter(incomingEdges); // Virtual platforms require their constraints to be recomputed each time as each module addition can cause a shift in versions if (!isVirtualPlatformNeedsRefresh()) { @@ -304,7 +304,7 @@ private void handleNonTransitiveNode(Collection discoveredEdges) { * Iterate over the dependencies originating in this node, adding them either as a 'pending' dependency * or adding them to the `discoveredEdges` collection (and `this.outgoingEdges`) */ - private void visitDependencies(ModuleExclusion resolutionFilter, Collection discoveredEdges) { + private void visitDependencies(ExcludeSpec resolutionFilter, Collection discoveredEdges) { PendingDependenciesVisitor pendingDepsVisitor = resolveState.newPendingDependenciesVisitor(); try { for (DependencyMetadata dependency : metaData.getDependencies()) { @@ -327,7 +327,7 @@ private void visitDependencies(ModuleExclusion resolutionFilter, Collection discoveredEdges, ModuleExclusion resolutionFilter, boolean deferSelection) { + private void createAndLinkEdgeState(DependencyState dependencyState, Collection discoveredEdges, ExcludeSpec resolutionFilter, boolean deferSelection) { EdgeState dependencyEdge = new EdgeState(this, dependencyState, resolutionFilter, resolveState); outgoingEdges.add(dependencyEdge); discoveredEdges.add(dependencyEdge); @@ -438,17 +438,17 @@ private List getTransitiveIncomingEdges() { return incomingEdges; } - private boolean isExcluded(ModuleExclusion selector, DependencyState dependencyState) { + private boolean isExcluded(ExcludeSpec selector, DependencyState dependencyState) { DependencyMetadata dependency = dependencyState.getDependency(); if (!resolveState.getEdgeFilter().isSatisfiedBy(dependency)) { LOGGER.debug("{} is filtered.", dependency); return true; } - if (selector == ModuleExclusions.excludeNone()) { + if (selector == resolveState.getModuleExclusions().nothing()) { return false; } ModuleIdentifier targetModuleId = dependencyState.getModuleIdentifier(); - if (selector.excludeModule(targetModuleId)) { + if (selector.excludes(targetModuleId)) { LOGGER.debug("{} is excluded from {}.", targetModuleId, this); return true; } @@ -479,14 +479,14 @@ public boolean shouldIncludedInGraphResult() { return isSelected() && !component.getModule().isVirtualPlatform(); } - private ModuleExclusion getModuleResolutionFilter(List incomingEdges) { - ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); - ModuleExclusion nodeExclusions = moduleExclusions.excludeAny(metaData.getExcludes()); + private ExcludeSpec getModuleResolutionFilter(List incomingEdges) { + org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); + ExcludeSpec nodeExclusions = moduleExclusions.excludeAny(metaData.getExcludes()); if (incomingEdges.isEmpty()) { return nodeExclusions; } - ModuleExclusion edgeExclusions = null; + ExcludeSpec edgeExclusions = null; for (EdgeState dependencyEdge : incomingEdges) { if (dependencyEdge.isTransitive()) { @@ -494,31 +494,19 @@ private ModuleExclusion getModuleResolutionFilter(List incomingEdges) edgeExclusions = excludedByBoth(edgeExclusions, dependencyEdge.getExclusions()); } else if (dependencyEdge.getDependencyMetadata().isConstraint()) { // Constraint: only consider explicit exclusions declared for this constraint - ModuleExclusion constraintExclusions = dependencyEdge.getEdgeExclusions(); + ExcludeSpec constraintExclusions = dependencyEdge.getEdgeExclusions(); nodeExclusions = excludedByEither(nodeExclusions, constraintExclusions); } } return excludedByEither(edgeExclusions, nodeExclusions); } - private ModuleExclusion excludedByBoth(ModuleExclusion one, ModuleExclusion two) { - if (one == null) { - return two; - } - if (two == null) { - return one; - } - return resolveState.getModuleExclusions().both(one, two); + private ExcludeSpec excludedByBoth(ExcludeSpec one, ExcludeSpec two) { + return resolveState.getModuleExclusions().excludeAll(one, two); } - private ModuleExclusion excludedByEither(ModuleExclusion one, ModuleExclusion two) { - if (one == null) { - return two; - } - if (two == null) { - return one; - } - return resolveState.getModuleExclusions().either(one, two); + private ExcludeSpec excludedByEither(ExcludeSpec one, ExcludeSpec two) { + return resolveState.getModuleExclusions().excludeAny(one, two); } private void removeOutgoingEdges() { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/FixedComponentArtifacts.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/FixedComponentArtifacts.java index d7b4e1b8f4b5..f19f3d939d7f 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/FixedComponentArtifacts.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/FixedComponentArtifacts.java @@ -21,7 +21,7 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.DefaultArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.internal.component.model.ComponentArtifactMetadata; @@ -48,7 +48,7 @@ public List getArtifacts() { } @Override - public ArtifactSet getArtifactsFor(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes) { + public ArtifactSet getArtifactsFor(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) { return DefaultArtifactSet.singleVariant(component.getId(), component.getModuleVersionId(), configuration.asDescribable(), artifacts, component.getSource(), exclusions, component.getAttributesSchema(), artifactResolver, allResolvedArtifacts, artifactTypeRegistry, overriddenAttributes); } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MetadataSourcedComponentArtifacts.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MetadataSourcedComponentArtifacts.java index 65ad6c4c5beb..dc3e8171b1d6 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MetadataSourcedComponentArtifacts.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MetadataSourcedComponentArtifacts.java @@ -20,7 +20,7 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.DefaultArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.internal.component.model.ComponentArtifacts; @@ -35,7 +35,7 @@ */ public class MetadataSourcedComponentArtifacts implements ComponentArtifacts { @Override - public ArtifactSet getArtifactsFor(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes) { + public ArtifactSet getArtifactsFor(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) { return DefaultArtifactSet.multipleVariants(component.getId(), component.getModuleVersionId(), component.getSource(), exclusions, configuration.getVariants(), component.getAttributesSchema(), artifactResolver, allResolvedArtifacts, artifactTypeRegistry, overriddenAttributes); } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifacts.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifacts.java index bbe40f6a04c2..d472253621dd 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifacts.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifacts.java @@ -19,7 +19,7 @@ import org.gradle.api.artifacts.component.ComponentArtifactIdentifier; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.internal.resolve.resolver.ArtifactResolver; @@ -33,5 +33,5 @@ public interface ComponentArtifacts { /** * Returns the variants for the given configuration. The values that are returned are retained for the life of the current build, so should reference as little state as possible. Should also be thread safe. */ - ArtifactSet getArtifactsFor(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes); + ArtifactSet getArtifactsFor(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactResolver artifactResolver, Map allResolvedArtifacts, ArtifactTypeRegistry artifactTypeRegistry, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactSelector.java index 0cfde84a1e6c..8315ea214182 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactSelector.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactSelector.java @@ -17,7 +17,7 @@ package org.gradle.internal.resolve.resolver; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.internal.component.local.model.LocalFileDependencyMetadata; import org.gradle.internal.component.model.ComponentArtifactMetadata; @@ -30,7 +30,7 @@ public interface ArtifactSelector { /** * Creates a set that will resolve the artifacts of the given configuration, minus those artifacts that are excluded. */ - ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes); + ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes); /** * Creates a set that will resolve the given artifacts of the given component. diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultArtifactSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultArtifactSelector.java index 4b014722c534..b2c6cdf564d6 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultArtifactSelector.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultArtifactSelector.java @@ -22,8 +22,8 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.DefaultArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.FileDependencyArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.internal.Describables; @@ -37,6 +37,8 @@ import java.util.Map; public class DefaultArtifactSelector implements ArtifactSelector { + private static final ExcludeSpec EXCLUDE_NONE = new DefaultExcludeFactory().nothing(); + private final Map allResolvedArtifacts = Maps.newHashMap(); private final List selectors; private final ArtifactTypeRegistry artifactTypeRegistry; @@ -54,7 +56,7 @@ public ArtifactSet resolveArtifacts(LocalFileDependencyMetadata fileDependencyMe } @Override - public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes) { + public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) { ArtifactSet artifacts = null; for (OriginArtifactSelector selector : selectors) { artifacts = selector.resolveArtifacts(component, configuration, artifactTypeRegistry, exclusions, overriddenAttributes); @@ -70,6 +72,6 @@ public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, Configur @Override public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, Collection artifacts, ImmutableAttributes overriddenAttributes) { - return DefaultArtifactSet.singleVariant(component.getId(), component.getModuleVersionId(), Describables.of(component.getId()), artifacts, component.getSource(), ModuleExclusions.excludeNone(), component.getAttributesSchema(), artifactResolver, allResolvedArtifacts, artifactTypeRegistry, overriddenAttributes); + return DefaultArtifactSet.singleVariant(component.getId(), component.getModuleVersionId(), Describables.of(component.getId()), artifacts, component.getSource(), EXCLUDE_NONE, component.getAttributesSchema(), artifactResolver, allResolvedArtifacts, artifactTypeRegistry, overriddenAttributes); } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/OriginArtifactSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/OriginArtifactSelector.java index 417f76df206b..e295d624140b 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/OriginArtifactSelector.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/OriginArtifactSelector.java @@ -17,7 +17,7 @@ package org.gradle.internal.resolve.resolver; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.internal.component.model.ComponentResolveMetadata; @@ -30,5 +30,5 @@ public interface OriginArtifactSelector { * Creates a set that will resolve the artifacts of the given configuration, minus those artifacts that are excluded. */ @Nullable - ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes); + ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes); } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy index 4c38b10d4b08..1c7bf8d8ae1b 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy @@ -17,7 +17,7 @@ package org.gradle.api.internal.artifacts.ivyservice.ivyresolve import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry import org.gradle.api.internal.attributes.ImmutableAttributes import org.gradle.internal.component.model.ComponentArtifactMetadata @@ -57,7 +57,7 @@ class RepositoryChainArtifactResolverTest extends Specification { def artifacts = Mock(ComponentArtifacts) def configuration = Stub(ConfigurationMetadata) def artifactTypeRegistry = Stub(ArtifactTypeRegistry) - def exclusion = Stub(ModuleExclusion) + def exclusion = Stub(ExcludeSpec) def artifactSet = Stub(ArtifactSet) when: @@ -82,7 +82,7 @@ class RepositoryChainArtifactResolverTest extends Specification { def artifacts = Mock(ComponentArtifacts) def configuration = Stub(ConfigurationMetadata) def artifactTypeRegistry = Stub(ArtifactTypeRegistry) - def exclusion = Stub(ModuleExclusion) + def exclusion = Stub(ExcludeSpec) def artifactSet = Stub(ArtifactSet) when: diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy index e21f95488187..aa8b52afebb9 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy @@ -92,7 +92,7 @@ class DependencyGraphBuilderTest extends Specification { DefaultModuleIdentifier.newId(*args) } } - def moduleExclusions = new ModuleExclusions(moduleIdentifierFactory) + def moduleExclusions = new ModuleExclusions() def buildOperationProcessor = Mock(BuildOperationExecutor) { def queue = Mock(BuildOperationQueue) { add(_) >> { args -> diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/DefaultModuleExclusionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/DefaultModuleExclusionTest.groovy deleted file mode 100644 index 01beda4ce5f0..000000000000 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/DefaultModuleExclusionTest.groovy +++ /dev/null @@ -1,818 +0,0 @@ -/* - * Copyright 2016 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.api.internal.artifacts.ivyservice.resolveengine.excludes - -import groovy.transform.NotYetImplemented -import org.gradle.api.internal.artifacts.DefaultModuleIdentifier -import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory -import org.gradle.internal.component.external.descriptor.DefaultExclude -import org.gradle.internal.component.model.DefaultIvyArtifactName -import org.gradle.internal.component.model.Exclude -import org.gradle.internal.component.model.IvyArtifactName -import spock.lang.Specification -import spock.lang.Unroll - -import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions.excludeNone - -class DefaultModuleExclusionTest extends Specification { - def moduleExclusions = new ModuleExclusions(Mock(ImmutableModuleIdentifierFactory) { - module(_, _) >> { args -> - DefaultModuleIdentifier.newId(*args) - } - }) - - def "accepts all modules default"() { - def spec = excludeAny() - - expect: - !spec.excludeModule(moduleId("org", "module")) - } - - def "accepts all artifacts by default"() { - def spec = excludeAny() - - expect: - !spec.excludeArtifact(moduleId("org", "module"), artifactName("test", "jar", "jar")) - !spec.mayExcludeArtifacts() - } - - def "spec with no rules excludes nothing"() { - expect: - excludeAny().is(excludeNone()) - } - - def "default specs accept the same modules as each other"() { - expect: - excludeAny().excludesSameModulesAs(excludeAny()) - excludeNone().excludesSameModulesAs(excludeNone()) - } - - def "specs are equal when they contain the same rules"() { - def rule1 = excludeRule("*", "*") - def rule2 = excludeRule("org", "*") - def rule3 = excludeRule("org", "module") - - expect: - excludeAny(rule1) == excludeAny(rule1) - excludeAny(rule1) != excludeAny(rule2) - excludeAny(rule1) != excludeAny(rule1, rule2) - - excludeAny(rule2, rule1) == excludeAny(rule1, rule2) - excludeAny(rule2, rule1) != excludeAny(rule1, rule3) - excludeAny(rule2, rule1) != excludeAny(rule1, rule2, rule3) - } - - @Unroll - def "does not accept module that matches single module exclude rule (#rule)"() { - when: - def spec = excludeAny(rule) - - then: - spec.excludeModule(moduleId('org', 'module')) - - where: - rule << [excludeRule('*', '*'), - excludeRule('org', 'module'), - excludeRule('org', '*'), - excludeRule('*', 'module'), - regexpExcludeRule('*', '*'), - regexpExcludeRule('or.*', 'module'), - regexpExcludeRule('org', 'mod.*'), - regexpExcludeRule('or.*', '*'), - regexpExcludeRule('*', 'mod.*')] - } - - @Unroll - def "accepts module that doesn't match single module exclude rule (#rule)"() { - when: - def spec = excludeAny(rule) - - then: - !spec.excludeModule(moduleId('org', 'module')) - - where: - rule << [excludeRule('org2', 'module2'), - excludeRule('*', 'module2'), - excludeRule('org2', '*'), - regexpExcludeRule('or.*2', "module"), - regexpExcludeRule('org', "mod.*2"), - regexpExcludeRule('or.*2', "*"), - regexpExcludeRule('*', "mod.*2")] - } - - @Unroll - def "module exclude rule selects the same modules as itself (#rule)"() { - when: - def spec = excludeAny(rule) - def same = excludeAny(rule) - def all = excludeAny() - def otherRule = excludeAny(excludeRule('*', 'other')) - def artifactRule = excludeAny(excludeRule('*', 'other', 'thing', '*', '*')) - - then: - spec.excludesSameModulesAs(spec) - spec.excludesSameModulesAs(same) - !spec.excludesSameModulesAs(all) - !spec.excludesSameModulesAs(otherRule) - !spec.excludesSameModulesAs(artifactRule) - - where: - rule << [excludeRule('*', '*'), - excludeRule('*', 'module'), - excludeRule('org', '*'), - excludeRule('org', 'module'), - regexpExcludeRule('or.*', "module"), - regexpExcludeRule('org', "mod.*")] - } - - @Unroll - def "accepts module for every artifact exclude rule (#rule)"() { - when: - def spec = excludeAny(rule) - - then: - !spec.excludeModule(moduleId('org', 'module')) - spec.mayExcludeArtifacts() - - where: - rule << [excludeRule('*', '*', 'artifact'), - excludeRule('org', '*', 'artifact'), - excludeRule('org', 'module', 'artifact'), - regexpExcludeRule('.*', "m.*", 'artifact')] - } - - @Unroll - def "accepts artifact for every module exclude rule (#rule)"() { - when: - def spec = excludeAny(rule) - - then: - !spec.excludeArtifact(moduleId('org', 'module'), artifactName('name', 'jar', 'jar')) - !spec.mayExcludeArtifacts() - - where: - rule << [excludeRule('*', '*'), - excludeRule('org', 'module'), - excludeRule('org', '*'), - excludeRule('*', 'module'), - regexpExcludeRule('*', '*'), - regexpExcludeRule('or.*', 'module'), - regexpExcludeRule('org', 'mod.*'), - regexpExcludeRule('or.*', '*'), - regexpExcludeRule('*', 'mod.*')] - } - - @Unroll - def "does not accept artifact that matches single artifact exclude rule (#rule)"() { - when: - def spec = excludeAny(rule) - - then: - spec.excludeArtifact(moduleId('org', 'module'), artifactName('mylib', 'jar', 'jar')) - spec.mayExcludeArtifacts() - - where: - rule << [excludeRule('org', 'module', 'mylib', 'jar', 'jar'), - excludeRule('org', 'module', '*', 'jar', 'jar'), - excludeRule('org', 'module', 'mylib', '*', 'jar'), - excludeRule('org', 'module', 'mylib', 'jar', '*'), - regexpExcludeRule('org', 'module', 'my.*', 'jar', 'jar'), - regexpExcludeRule('org', 'module', 'my.*', '*', '*'), - regexpExcludeRule('org', 'module', 'mylib', 'j.*', 'jar'), - regexpExcludeRule('org', 'module', '*', 'j.*', 'jar'), - regexpExcludeRule('org', 'module', 'mylib', 'jar', 'j.*'), - regexpExcludeRule('org', 'module', 'mylib', '*', 'j.*')] - } - - @Unroll - def "accepts artifact that doesn't match single artifact exclude rule (#rule)"() { - when: - def spec = excludeAny(rule) - - then: - !spec.excludeArtifact(moduleId('org', 'module'), artifactName('mylib', 'jar', 'jar')) - - where: - rule << [excludeRule('*', 'module'), - excludeRule('org', '*'), - excludeRule('org', 'module'), - excludeRule('*', 'module2'), - excludeRule('org2', '*'), - excludeRule('org2', 'module2'), - excludeRule('org', 'module', 'mylib', 'sources', 'jar'), - excludeRule('org', 'module', 'mylib', 'jar', 'war'), - excludeRule('org', 'module', 'otherlib', 'jar', 'jar'), - excludeRule('org', 'module', 'otherlib', '*', '*'), - excludeRule('org', 'module', 'otherlib', '*', 'jar'), - excludeRule('org', 'module', 'otherlib', 'jar', '*'), - excludeRule('org', 'module', '*', 'sources', 'jar'), - excludeRule('org', 'module', '*', 'sources', '*'), - excludeArtifactRule('mylib', 'sources', 'jar'), - excludeArtifactRule('mylib', 'jar', 'war'), - excludeArtifactRule('otherlib', 'jar', 'jar'), - excludeArtifactRule('otherlib', '*', '*'), - excludeArtifactRule('*', 'sources', 'jar'), - excludeArtifactRule('otherlib', '*', 'jar'), - excludeArtifactRule('otherlib', 'jar', '*'), - regexpExcludeRule('or.*2', 'module'), - regexpExcludeRule('org', 'mod.*2'), - regexpExcludeRule('org', 'module', 'my.*2', '*', '*'), - regexpExcludeRule('org', 'module', 'mylib', 'j.*2', '*'), - regexpExcludeRule('org', 'module', 'mylib', 'jar', 'j.*2'), - regexpExcludeArtifactRule('my.*2', '*', '*'), - regexpExcludeArtifactRule('mylib', 'j.*2', '*'), - regexpExcludeArtifactRule('mylib', 'jar', 'j.*2')] - } - - @Unroll - def "artifact exclude rule accepts the same modules as other rules that accept all modules (#rule)"() { - when: - def spec = excludeAny(rule) - def sameRule = excludeAny(rule) - def otherRule = excludeAny(excludeRule('*', '*', 'thing', '*', '*')) - def all = excludeNone() - def moduleRule = excludeAny(excludeRule('*', 'module')) - - then: - spec.excludesSameModulesAs(spec) - spec.excludesSameModulesAs(sameRule) - spec.excludesSameModulesAs(otherRule) - spec.excludesSameModulesAs(all) - all.excludesSameModulesAs(spec) - - !spec.excludesSameModulesAs(moduleRule) - !moduleRule.excludesSameModulesAs(spec) - - spec.excludesSameModulesAs(both(spec, otherRule)) - spec.excludesSameModulesAs(both(spec, moduleRule)) - spec.excludesSameModulesAs(either(spec, both(otherRule, sameRule))) - - where: - rule << [excludeRule('*', '*', '*', 'jar', 'jar'), - excludeRule('org', 'module', 'mylib', 'jar', 'jar'), - excludeRule('org', 'module', '*', 'jar', 'jar'), - excludeRule('org', 'module', 'mylib', '*', 'jar'), - excludeRule('org', 'module', 'mylib', 'jar', '*'), - regexpExcludeRule('org', "module", 'my.*', 'jar', 'jar'), - regexpExcludeRule('org', "module", 'mylib', 'j.*', 'jar'), - regexpExcludeRule('org', "module", 'mylib', 'jar', 'j.*')] - } - - def "does not accept module version that matches any exclude rule"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def rule3 = excludeRule("org2", "*") - def rule4 = excludeRule("*", "module4") - def rule5 = regexpExcludeRule("regexp-\\d+", "module\\d+") - def spec = excludeAny(rule1, rule2, rule3, rule4, rule5) - - expect: - spec.excludeModule(moduleId("org", "module")) - spec.excludeModule(moduleId("org", "module2")) - spec.excludeModule(moduleId("org2", "anything")) - spec.excludeModule(moduleId("other", "module4")) - spec.excludeModule(moduleId("regexp-72", "module12")) - !spec.excludeModule(moduleId("org", "other")) - !spec.excludeModule(moduleId("regexp-72", "other")) - !spec.excludeModule(moduleId("regexp", "module2")) - } - - def "specs with the same set of exclude rules accept the same modules as each other"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def rule3 = excludeRule("org2", "*") - def rule4 = excludeRule("*", "module4") - def rule5 = regexpExcludeRule("pattern1", "pattern2") - def exactMatchSpec = excludeAny(rule1) - def moduleWildcard = excludeAny(rule3) - def groupWildcard = excludeAny(rule4) - def regexp = excludeAny(rule5) - def manyRules = excludeAny(rule1, rule2, rule3, rule4, rule5) - - expect: - exactMatchSpec.excludesSameModulesAs(exactMatchSpec) - exactMatchSpec.excludesSameModulesAs(excludeAny(rule1)) - - !exactMatchSpec.excludesSameModulesAs(excludeAny(rule2)) - !exactMatchSpec.excludesSameModulesAs(excludeAny()) - !exactMatchSpec.excludesSameModulesAs(excludeAny(rule1, rule2)) - - moduleWildcard.excludesSameModulesAs(moduleWildcard) - moduleWildcard.excludesSameModulesAs(excludeAny(rule3)) - - !moduleWildcard.excludesSameModulesAs(excludeAny(rule1)) - !moduleWildcard.excludesSameModulesAs(excludeAny(rule1, rule3)) - !moduleWildcard.excludesSameModulesAs(excludeAny()) - !moduleWildcard.excludesSameModulesAs(excludeAny(excludeRule("org3", "*"))) - - groupWildcard.excludesSameModulesAs(groupWildcard) - groupWildcard.excludesSameModulesAs(excludeAny(rule4)) - - !groupWildcard.excludesSameModulesAs(excludeAny(rule1)) - !groupWildcard.excludesSameModulesAs(excludeAny(rule1, rule4)) - !groupWildcard.excludesSameModulesAs(excludeAny()) - !groupWildcard.excludesSameModulesAs(excludeAny(excludeRule("*", "module5"))) - - regexp.excludesSameModulesAs(regexp) - regexp.excludesSameModulesAs(excludeAny(rule5)) - - !regexp.excludesSameModulesAs(excludeAny(rule1)) - !regexp.excludesSameModulesAs(excludeAny(rule1, rule5)) - !regexp.excludesSameModulesAs(excludeAny()) - !regexp.excludesSameModulesAs(excludeAny(regexpExcludeRule("pattern", "other"))) - - manyRules.excludesSameModulesAs(manyRules) - manyRules.excludesSameModulesAs(excludeAny(rule1, rule2, rule3, rule4, rule5)) - - !manyRules.excludesSameModulesAs(excludeAny(rule1, rule3, rule4, rule5)) - !manyRules.excludesSameModulesAs(excludeAny(rule1, rule2, rule4, rule5)) - !manyRules.excludesSameModulesAs(excludeAny(rule1, rule2, rule3, rule5)) - !manyRules.excludesSameModulesAs(excludeAny(rule1, rule2, rule3, rule4)) - - !manyRules.excludesSameModulesAs(excludeAny(rule1, excludeRule("org", "module3"), rule3, rule4, rule5)) - !manyRules.excludesSameModulesAs(excludeAny(rule1, rule2, excludeRule("org3", "*"), rule4, rule5)) - !manyRules.excludesSameModulesAs(excludeAny(rule1, rule2, rule3, excludeRule("*", "module5"), rule5)) - !manyRules.excludesSameModulesAs(excludeAny(rule1, rule2, rule3, rule4, regexpExcludeRule("other", "other"))) - } - - def "union with empty spec is empty spec"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeArtifactRule("b", "jar", "jar") - def spec = excludeAny(rule1, rule2) - def spec2 = excludeNone() - - expect: - both(spec, spec2).is(spec2) - both(spec2, spec).is(spec2) - } - - def "union of a spec with itself returns the original spec"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def rule3 = excludeArtifactRule("a", "jar", "jar") - def spec = excludeAny(rule1, rule2, rule3) - - expect: - both(spec, spec).is(spec) - } - - def "union of two specs with the same exclude rule instances returns one of the original specs"() { - def rule1 = excludeRule("org", "module") - def rule2 = regexpExcludeRule("org", "module2") - def rule3 = excludeRule("org2", "*") - def rule4 = excludeRule("*", "module3") - def spec = excludeAny(rule1, rule2, rule3, rule4) - def spec2 = excludeAny(rule2, rule3, rule1, rule4) - - expect: - both(spec, spec2).is(spec) - } - - @NotYetImplemented - def "union of two specs where one spec contains a superset of rules returns the spec with the subset of rules"() { - def rule1 = excludeRule("org", "module") - def rule2 = regexpExcludeRule("org", "module2") - def rule3 = excludeRule("org2", "*") - def rule4 = excludeRule("*", "module3") - def spec = excludeAny(rule1, rule2, rule3, rule4) - def spec2 = excludeAny(rule2, rule1, rule4) - - expect: - both(spec, spec2).is(spec2) - both(spec2, spec).is(spec2) - } - - def "union of two specs with exact matching exclude rules uses the either of the exclude rules"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def rule3 = excludeRule("org", "module3") - def spec = excludeAny(rule1, rule2) - def spec2 = excludeAny(rule1, rule3) - - expect: - def union = both(spec, spec2) - union == excludeAny(rule1) - } - - def "union of spec with module wildcard uses the most specific matching exclude rules"() { - def rule1 = excludeRule("org", "*") - def rule2 = excludeRule("org", "module") - def rule3 = excludeRule("org", "module2") - def rule4 = excludeRule("other", "module") - def rule5 = excludeRule("*", "module3") - def rule6 = excludeRule("org2", "*") - def spec = excludeAny(rule1) - - expect: - def union1 = both(spec, excludeAny(rule2, rule3, rule4)) - union1 == excludeAny(rule2, rule3) - - def union2 = both(spec, excludeAny(rule5)) - union2 == excludeAny(excludeRule("org", "module3")) - - def union3 = both(spec, excludeAny(rule6, rule2)) - union3 == excludeAny(rule2) - } - - def "union of spec with group wildcard uses the most specific matching exclude rules"() { - def rule1 = excludeRule("*", "module") - def rule2 = excludeRule("org", "module") - def rule3 = excludeRule("org", "module2") - def rule4 = excludeRule("other", "module") - def rule5 = excludeRule("org", "*") - def rule6 = excludeRule("*", "module2") - def spec = excludeAny(rule1) - - expect: - def union1 = both(spec, excludeAny(rule2, rule3, rule4)) - union1 == excludeAny(rule2, rule4) - - def union2 = both(spec, excludeAny(rule5)) - union2 == excludeAny(excludeRule("org", "module")) - - def union3 = both(spec, excludeAny(rule6)) - union3 == excludeNone() - } - - def "union of two specs with disjoint exact matching exclude rules excludes no modules"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def spec = excludeAny(rule1) - def spec2 = excludeAny(rule2) - - expect: - def union = both(spec, spec2) - union == excludeNone() - } - - def "union of a spec with exclude-all spec returns the original spec"() { - def rule1 = excludeRule("*", "*") - def rule2 = excludeRule("org", "module2") - def spec1 = excludeAny(rule1) - def spec2 = excludeAny(rule2) - - expect: - both(spec1, spec2) == spec2 - both(spec2, spec1) == spec2 - } - - def "union of module spec and artifact spec uses the artifact spec"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("*", "module-2") - def rule3 = excludeRule("org", "*-2") - def artifactRule1 = excludeRule("org", "module", "art", "*", "*") - def artifactRule2 = excludeRule("*", "*", "*", "jar", "*") - def artifactSpec1 = excludeAny(artifactRule1) - def artifactSpec2 = excludeAny(artifactRule1, artifactRule2) - - expect: - def union1 = both(artifactSpec1, excludeAny(rule1)) - union1 == artifactSpec1 - - def union2 = both(artifactSpec1, excludeAny(rule1, rule2, rule3)) - union2 == artifactSpec1 - - def union3 = both(artifactSpec2, excludeAny(rule1, rule2, rule3)) - union3 == artifactSpec2 - } - - def "union of two specs with non-exact matching exclude rules is a union spec"() { - def rule1 = excludeRule("org", "module") - def rule2 = regexpExcludeRule("org", "module2") - def spec = excludeAny(rule1) - def spec2 = excludeAny(rule2) - - expect: - def union = both(spec, spec2) - def specs = [] - union.unpackAll(specs) - specs.size() == 2 - specs[0] == spec - specs[1] == spec2 - } - - def "union of union specs is the union of the original specs"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def rule3 = regexpExcludeRule("org", "module2") - def spec = excludeAny(rule1) - def spec2 = excludeAny(rule1, rule2) - def spec3 = excludeAny(rule3) - - expect: - def union = both(both(spec, spec3), spec2) - - union instanceof AllExclusion - union.filters.size() == 2 - union.filters.any { - it instanceof EitherExclusion && it.excludeSpecs == spec.excludeSpecs - } - union.filters.contains(spec3) - } - - // Regression test for GRADLE-3275, also exercises GRADLE-3434 - def "either propagates through child union rules"() { - def rule1 = excludeRule("org", "module") - def rule2 = regexpExcludeRule("org", "module2") - def rule3 = regexpExcludeRule("org", "module3") - def spec = excludeAny(rule1) - def spec2 = excludeAny(rule1, rule2) - def spec3 = excludeAny(rule3) - - def excludeBacked1 = either(spec, spec2); // module + module2 - def union1 = both(spec2, spec3); // module, module2, module3 - def excludeBacked2 = either(spec2, union1); // module, module2 - def finalUnion = both(spec3, excludeBacked2); // module - - expect: - // Sanity checks. - excludeBacked1 == spec2 - def specs = [] - union1.unpackAll(specs) - specs == [spec2, spec3]; - - // Verify test is exercising the function it's supposed to. - excludeBacked1 instanceof EitherExclusion - excludeBacked2 instanceof EitherExclusion - - union1 instanceof AllExclusion - finalUnion instanceof AllExclusion - - !spec2.excludeModule(moduleId("org", "module4")) - - // Verify that this function passes the either operation through to union2's rules. - !finalUnion.excludeModule(moduleId("org", "module")) - !finalUnion.excludeModule(moduleId("org", "module2")) - !finalUnion.excludeModule(moduleId("org", "module3")) - } - - def "union accept module that is accepted by any merged exclude rule"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def spec = excludeAny(rule1, rule2) - def spec2 = excludeAny(rule1) - - expect: - def union1 = both(spec, spec2) - - spec.excludeModule(moduleId("org", "module")) - union1.excludeModule(moduleId("org", "module")) - - spec.excludeModule(moduleId("org", "module2")) - !union1.excludeModule(moduleId("org", "module2")) - } - - def "union accepts artifact that is accepted by any merged exclude rule"() { - def moduleId = moduleId("org", "module") - def excludeA = excludeRule("org", "module", "a") - def excludeB = excludeRule("org", "module", "b") - def spec = excludeAny(excludeA) - def spec2 = excludeAny(excludeB) - - when: - def union1 = both(spec, spec2) - - then: - union1.excludeArtifact(moduleId, artifactName("a", "zip", "zip")) - !union1.excludeArtifact(moduleId, artifactName("b", "zip", "zip")) - !union1.excludeArtifact(moduleId, artifactName("c", "zip", "zip")) - - union1.mayExcludeArtifacts() - } - - def "unions accepts same modules when original specs accept same modules"() { - def rule1 = regexpExcludeRule("org", "module") - def rule2 = regexpExcludeRule("org", "module2") - def rule3 = regexpExcludeRule("org", "module3") - def spec1 = excludeAny(rule1) - def spec2 = excludeAny(rule2) - def spec3 = excludeAny(rule3) - - expect: - both(spec1, spec2).excludesSameModulesAs(both(spec2, spec1)) - - !both(spec1, spec2).excludesSameModulesAs(spec2) - !both(spec1, spec2).excludesSameModulesAs(spec1) - !both(spec1, spec2).excludesSameModulesAs(both(spec1, spec3)) - } - - def "either with empty spec is original spec"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeArtifactRule("b", "jar", "jar") - def spec = excludeAny(rule1, rule2) - def spec2 = excludeNone() - - expect: - either(spec, spec2).is(spec) - either(spec2, spec).is(spec) - } - - def "either of a spec with itself returns the original spec"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def rule3 = excludeArtifactRule("b", "jar", "jar") - def spec = excludeAny(rule1, rule2, rule3) - - expect: - either(spec, spec).is(spec) - } - - def "either of two specs with the same exclude rule instances returns one of the original specs"() { - def rule1 = excludeRule("org", "module") - def rule2 = regexpExcludeRule("org", "module2") - def rule3 = excludeRule("org2", "*") - def rule4 = excludeRule("*", "module3") - def spec = excludeAny(rule1, rule2, rule3, rule4) - def spec2 = excludeAny(rule2, rule3, rule1, rule4) - - expect: - either(spec, spec2).is(spec) - } - - def "either of two specs where one spec contains a superset of the rules of the other returns the spec containing the superset"() { - def rule1 = excludeRule("org", "module") - def rule2 = regexpExcludeRule("org", "module2") - def rule3 = excludeRule("org2", "*") - def rule4 = excludeRule("*", "module3") - def spec = excludeAny(rule1, rule2, rule3, rule4) - def spec2 = excludeAny(rule2, rule1, rule4) - - expect: - either(spec, spec2).is(spec) - either(spec2, spec).is(spec) - } - - def "either does not accept module that is not accepted by any merged exclude rules"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def spec = excludeAny(rule1, rule2) - def spec2 = excludeAny(rule1) - - expect: - def either = either(spec, spec2) - - spec.excludeModule(moduleId("org", "module")) - either.excludeModule(moduleId("org", "module")) - - spec.excludeModule(moduleId("org", "module2")) - either.excludeModule(moduleId("org", "module2")) - - !spec.excludeModule(moduleId("org", "module3")) - !spec2.excludeModule(moduleId("org", "module3")) - !either.excludeModule(moduleId("org", "module3")) - } - - def "either accepts artifact that is accepted by every merged exclude rule"() { - def moduleId = moduleId("org", "module") - def excludeA = excludeRule("org", "module", "a") - def excludeB = excludeRule("org", "module", "b") - def spec = excludeAny(excludeA, excludeB) - def spec2 = excludeAny(excludeA) - - expect: - def either = either(spec, spec2) - - either.excludeArtifact(moduleId, artifactName("a", "zip", "zip")) - either.excludeArtifact(moduleId, artifactName("b", "zip", "zip")) - !either.excludeArtifact(moduleId, artifactName("c", "zip", "zip")) - - either.mayExcludeArtifacts() - } - - def "either of two specs with exclude rules is the union of the exclude rules"() { - def rule1 = excludeRule("org", "module") - def rule2 = excludeRule("org", "module2") - def spec = excludeAny(rule1, rule2) - def spec2 = excludeAny(rule1) - - expect: - def either = either(spec, spec2) - either == excludeAny(rule1, rule2) - } - - def "eithers accepts same modules when original specs accept same modules"() { - def rule1 = regexpExcludeRule("org", "module") - def rule2 = regexpExcludeRule("org", "module2") - def rule3 = regexpExcludeRule("org", "module3") - def spec1 = both(excludeAny(rule1), excludeAny(rule2)) - def spec2 = both(excludeAny(rule2), excludeAny(rule1)) - def spec3 = excludeAny(rule3) - assert spec1.excludesSameModulesAs(spec2) - - expect: - either(spec1, spec2).excludesSameModulesAs(either(spec2, spec1)) - - !either(spec1, spec2).excludesSameModulesAs(spec1) - !either(spec1, spec2).excludesSameModulesAs(spec2) - !either(spec1, spec2).excludesSameModulesAs(either(spec1, spec3)) - } - - def "does not accept artifact that matches specific exclude rule"() { - def rule1 = excludeArtifactRule("a", "jar", "jar") - def rule2 = excludeArtifactRule("b", "jar", "jar") - def rule3 = excludeArtifactRule("c", "*", "*") - def rule4 = excludeArtifactRule("d", "*", "jar") - def rule5 = excludeArtifactRule("e", "sources", "jar") - def rule6 = excludeArtifactRule("f", "sources", "*") - def rule7 = excludeArtifactRule("g", "jar", "war") - def rule8 = regexpExcludeArtifactRule("regexp-\\d+", "jar", "jar") - def spec = excludeAny(rule1, rule2, rule3, rule4, rule5, rule6, rule7, rule8) - - expect: - spec.excludeArtifact(moduleId("org", "module"), artifactName("a", "jar", "jar")) - spec.excludeArtifact(moduleId("org", "module2"), artifactName("b", "jar", "jar")) - spec.excludeArtifact(moduleId("org2", "anything"), artifactName("c", "jar", "jar")) - spec.excludeArtifact(moduleId("other", "module4"), artifactName("d", "jar", "jar")) - spec.excludeArtifact(moduleId("some", "app"), artifactName("e", "sources", "jar")) - spec.excludeArtifact(moduleId("foo", "bar"), artifactName("f", "sources", "jar")) - spec.excludeArtifact(moduleId("well", "known"), artifactName("g", "jar", "war")) - spec.excludeArtifact(moduleId("other", "sample"), artifactName("regexp-99", "jar", "jar")) - !spec.excludeArtifact(moduleId("some", "app"), artifactName("e", "jar", "jar")) - !spec.excludeArtifact(moduleId("some", "app"), artifactName("e", "javadoc", "jar")) - !spec.excludeArtifact(moduleId("foo", "bar"), artifactName("f", "jar", "jar")) - !spec.excludeArtifact(moduleId("well", "known"), artifactName("g", "jar", "jar")) - !spec.excludeArtifact(moduleId("well", "known"), artifactName("g", "jar", "zip")) - !spec.excludeArtifact(moduleId("other", "sample"), artifactName("regexp", "jar", "jar")) - } - - def "can merge excludes with default and non-default ivy pattern matchers"() { - def simpleExclude = excludeAny(excludeModuleRule("module-exclude")) - def regexpExclude = excludeAny(regexpExcludeRule("regexp-match", "*")) - def unmergedUnion = both(simpleExclude, regexpExclude) - def either = either(unmergedUnion, simpleExclude) - - expect: - both(either, simpleExclude) - } - - ModuleExclusion both(ModuleExclusion spec, ModuleExclusion otherRule) { - moduleExclusions.both(spec, otherRule) - } - - ModuleExclusion either(ModuleExclusion spec, ModuleExclusion otherRule) { - moduleExclusions.either(spec, otherRule) - } - - ModuleExclusion excludeAny(Exclude... excludes) { - moduleExclusions.excludeAny(excludes) - } - - static specForRule(def spec, Exclude rule) { - return spec.moduleId.group == rule.moduleId.group && spec.moduleId.name == rule.moduleId.name - } - - def moduleId(String group, String name) { - return DefaultModuleIdentifier.newId(group, name); - } - - def artifactName(String name, String type, String ext) { - return new DefaultIvyArtifactName(name, type, ext) - } - - def excludeRule(String org, String module) { - new DefaultExclude(DefaultModuleIdentifier.newId(org, module), null, new String[0], PatternMatchers.EXACT) - } - - def excludeRule(String org, String module, String name, String type = "*", String ext = "*") { - IvyArtifactName artifactName = new DefaultIvyArtifactName(name, type, ext) - new DefaultExclude(DefaultModuleIdentifier.newId(org, module), artifactName, new String[0], PatternMatchers.EXACT) - } - - def excludeModuleRule(String module) { - new DefaultExclude(DefaultModuleIdentifier.newId("*", module)) - } - - def excludeGroupRule(String group) { - new DefaultExclude(DefaultModuleIdentifier.newId(group, "*")) - } - - def excludeArtifactRule(String name, String type, String ext) { - excludeRule("*", "*", name, type, ext) - } - - def regexpExcludeRule(String org, String module) { - new DefaultExclude(DefaultModuleIdentifier.newId(org, module), null, new String[0], "regexp") - } - - def regexpExcludeRule(String org, String module, String name, String type = "*", String ext = "*") { - IvyArtifactName ivyArtifactName = new DefaultIvyArtifactName(name, type, ext) - new DefaultExclude(DefaultModuleIdentifier.newId(org, module), ivyArtifactName, new String[0], "regexp") - } - - def regexpExcludeArtifactRule(String name, String type, String ext) { - regexpExcludeRule("*", "*", name, type, ext) - } -} diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy new file mode 100644 index 000000000000..57acacc8d6d7 --- /dev/null +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy @@ -0,0 +1,137 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.factories + +import com.google.common.collect.ImmutableList +import org.gradle.api.internal.artifacts.DefaultModuleIdentifier +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeFactory +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec +import org.gradle.internal.component.model.DefaultIvyArtifactName +import org.gradle.internal.component.model.IvyArtifactName +import spock.lang.Shared +import spock.lang.Specification +import spock.lang.Subject +import spock.lang.Unroll + +class NormalizingExcludeFactoryTest extends Specification { + + @Shared + private ExcludeFactory delegate = new DefaultExcludeFactory() + + @Subject + private NormalizingExcludeFactory factory = new NormalizingExcludeFactory(delegate) + + @Shared + private DefaultIvyArtifactName artifactName = new DefaultIvyArtifactName("a", "b", "c") + + @Unroll("#left ∪ #right = #expected") + def "union of two elements"() { + expect: + factory.anyOf(left, right) == expected + + and: "union is commutative" + factory.anyOf(right, left) == expected + + where: + left | right | expected +/* everything() | nothing() | everything() + everything() | everything() | everything() + nothing() | nothing() | nothing() + everything() | group("foo") | everything() + nothing() | group("foo") | group("foo") + group("foo") | group("bar") | anyOf(group("foo"), group("bar")) + group("foo") | module("bar") | anyOf(group("foo"), module("bar"))*/ + anyOf(group("foo"), group("bar")) | group("foo") | anyOf(group("foo"), group("bar")) + anyOf(group("foo"), module("bar")) | module("bar") | anyOf(module("bar"), group("foo")) + } + + @Unroll("#one ∪ #two ∪ #three = #expected") + def "union of three elements"() { + expect: + [one, two, three].combinations().each { list -> + assert factory.anyOf(list) == expected + } + + where: + one | two | three | expected + everything() | nothing() | nothing() | everything() + everything() | everything() | everything() | everything() + nothing() | nothing() | nothing() | nothing() + everything() | group("foo") | everything() | everything() + group("foo") | group("bar") | group("baz") | anyOf(group("foo"), group("bar"), group("baz")) + } + + @Unroll("#left ∩ #right = #expected") + def "intersection of two elements"() { + expect: + factory.allOf(left, right) == expected + + and: "intersection is commutative" + factory.allOf(right, left) == expected + + where: + left | right | expected + everything() | nothing() | nothing() + everything() | everything() | everything() + nothing() | nothing() | nothing() + everything() | group("foo") | group("foo") + nothing() | group("foo") | nothing() + group("foo") | group("foo") | group("foo") + allOf(group("foo"), group("foo2")) | module("bar") | allOf(group("foo2"), group("foo"), module("bar")) + } + + private ExcludeSpec nothing() { + delegate.nothing() + } + + private ExcludeSpec everything() { + delegate.everything() + } + + private ExcludeSpec group(String group) { + delegate.group(group) + } + + private ExcludeSpec module(String module) { + delegate.module(module) + } + + private ExcludeSpec module(String group, String name) { + delegate.moduleId(DefaultModuleIdentifier.newId(group, name)) + } + + private ExcludeSpec anyOf(ExcludeSpec... specs) { + delegate.anyOf(ImmutableList.copyOf(specs)) + } + + private ExcludeSpec allOf(ExcludeSpec... specs) { + delegate.allOf(ImmutableList.copyOf(specs)) + } + + private ExcludeSpec ivy(String group, String module, IvyArtifactName artifact, String matcher) { + delegate.ivyPatternExclude( + DefaultModuleIdentifier.newId(group, module), + artifact, + matcher + ) + } + + private static IvyArtifactName artifact(String name) { + new DefaultIvyArtifactName(name, "jar", "jar") + } +} diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/IvyDependencyDescriptorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/IvyDependencyDescriptorTest.groovy index 6fa3d7ce1f47..312f2a5a149f 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/IvyDependencyDescriptorTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/IvyDependencyDescriptorTest.groovy @@ -16,15 +16,16 @@ package org.gradle.internal.component.external.model +import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableListMultimap import com.google.common.collect.ImmutableSet import com.google.common.collect.LinkedHashMultimap import org.gradle.api.artifacts.component.ComponentIdentifier import org.gradle.api.artifacts.component.ModuleComponentSelector -import org.gradle.api.internal.artifacts.DefaultImmutableModuleIdentifierFactory import org.gradle.api.internal.artifacts.DefaultModuleIdentifier -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.PatternMatchers +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec import org.gradle.internal.component.external.descriptor.Artifact import org.gradle.internal.component.external.descriptor.DefaultExclude import org.gradle.internal.component.external.model.ivy.IvyDependencyDescriptor @@ -38,6 +39,8 @@ import static com.google.common.collect.ImmutableList.copyOf class IvyDependencyDescriptorTest extends ExternalDependencyDescriptorTest { + private final static ExcludeSpec NOTHING = new ModuleExclusions().nothing() + @Override ExternalDependencyDescriptor create(ModuleComponentSelector selector) { return new IvyDependencyDescriptor(selector, ImmutableListMultimap.of()) @@ -101,40 +104,40 @@ class IvyDependencyDescriptorTest extends ExternalDependencyDescriptorTest { def "excludes nothing when no exclude rules provided"() { def dep = createWithExcludes(requested, []) - def moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) + def moduleExclusions = new ModuleExclusions() expect: - moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration("from").hierarchy))) == ModuleExclusions.excludeNone() - moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration("anything").hierarchy))) == ModuleExclusions.excludeNone() + moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration("from").hierarchy))) == NOTHING + moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration("anything").hierarchy))) == NOTHING } def "excludes nothing when traversing a different configuration"() { def exclude = new DefaultExclude(DefaultModuleIdentifier.newId("group", "*"), ["from"] as String[], PatternMatchers.EXACT) def dep = createWithExcludes(requested, [exclude]) - def moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) + def moduleExclusions = new ModuleExclusions() expect: - moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration("anything").hierarchy))) == ModuleExclusions.excludeNone() + moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration("anything").hierarchy))) == NOTHING } def "applies exclude rules when traversing a configuration"() { def exclude = new DefaultExclude(DefaultModuleIdentifier.newId("group", "*"), ["from"] as String[], PatternMatchers.EXACT) def dep = createWithExcludes(requested, [exclude]) def configuration = configuration("from") - def moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) + def moduleExclusions = new ModuleExclusions() expect: - moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration.hierarchy))) == moduleExclusions.excludeAny(exclude) + moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration.hierarchy))) == moduleExclusions.excludeAny(ImmutableList.of(exclude)) } def "applies rules when traversing a child of specified configuration"() { def exclude = new DefaultExclude(DefaultModuleIdentifier.newId("group", "*"), ["from"] as String[], PatternMatchers.EXACT) def dep = createWithExcludes(requested, [exclude]) def configuration = configuration("child", "from") - def moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) + def moduleExclusions = new ModuleExclusions() expect: - moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration.hierarchy))) == moduleExclusions.excludeAny(exclude) + moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration.hierarchy))) == moduleExclusions.excludeAny(ImmutableList.of(exclude)) } def "applies matching exclude rules"() { @@ -143,10 +146,10 @@ class IvyDependencyDescriptorTest extends ExternalDependencyDescriptorTest { def exclude3 = new DefaultExclude(DefaultModuleIdentifier.newId("group3", "*"), ["other"] as String[], PatternMatchers.EXACT) def dep = createWithExcludes(requested, [exclude1, exclude2, exclude3]) def configuration = configuration("from") - def moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) + def moduleExclusions = new ModuleExclusions() expect: - moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration.hierarchy))) == moduleExclusions.excludeAny(exclude1, exclude2) + moduleExclusions.excludeAny(copyOf(dep.getConfigurationExcludes(configuration.hierarchy))) == moduleExclusions.excludeAny(ImmutableList.of(exclude1, exclude2)) } def "selects no configurations when no configuration mappings provided"() { diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/MavenDependencyDescriptorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/MavenDependencyDescriptorTest.groovy index 364c0797750f..98975bbe60bc 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/MavenDependencyDescriptorTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/MavenDependencyDescriptorTest.groovy @@ -31,13 +31,14 @@ */ package org.gradle.internal.component.external.model +import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableSet import org.gradle.api.artifacts.component.ComponentIdentifier import org.gradle.api.artifacts.component.ModuleComponentSelector -import org.gradle.api.internal.artifacts.DefaultImmutableModuleIdentifierFactory import org.gradle.api.internal.artifacts.DefaultModuleIdentifier -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.PatternMatchers +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec import org.gradle.internal.component.external.descriptor.DefaultExclude import org.gradle.internal.component.external.descriptor.MavenScope import org.gradle.internal.component.external.model.maven.MavenDependencyDescriptor @@ -47,11 +48,12 @@ import org.gradle.internal.component.model.ComponentResolveMetadata import org.gradle.internal.component.model.ConfigurationMetadata import org.gradle.internal.component.model.ConfigurationNotFoundException import org.gradle.internal.component.model.Exclude -import org.gradle.internal.component.model.ExcludeMetadata +import org.gradle.internal.component.model.ExcludeMetadata class MavenDependencyDescriptorTest extends ExternalDependencyDescriptorTest { - final ModuleExclusions moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) - + final ModuleExclusions moduleExclusions = new ModuleExclusions() + final ExcludeSpec nothing = moduleExclusions.nothing() + @Override ExternalDependencyDescriptor create(ModuleComponentSelector selector) { return mavenDependencyMetadata(MavenScope.Compile, selector, []) @@ -66,7 +68,7 @@ class MavenDependencyDescriptorTest extends ExternalDependencyDescriptorTest { expect: def exclusions = moduleExclusions.excludeAny(dep.allExcludes) - exclusions == ModuleExclusions.excludeNone() + exclusions == nothing exclusions.is(moduleExclusions.excludeAny(dep.allExcludes)) } @@ -77,7 +79,7 @@ class MavenDependencyDescriptorTest extends ExternalDependencyDescriptorTest { expect: def exclusions = moduleExclusions.excludeAny(dep.allExcludes) - exclusions == moduleExclusions.excludeAny(exclude1, exclude2) + exclusions == moduleExclusions.excludeAny(ImmutableList.of(exclude1, exclude2)) exclusions.is(moduleExclusions.excludeAny(dep.allExcludes)) } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetadataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetadataTest.groovy index 3e74a451defc..06ec41b3aef5 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetadataTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetadataTest.groovy @@ -18,12 +18,10 @@ package org.gradle.internal.component.local.model import com.google.common.collect.ImmutableSet import org.gradle.api.artifacts.PublishArtifact -import org.gradle.api.internal.artifacts.DefaultImmutableModuleIdentifierFactory import org.gradle.api.internal.artifacts.DefaultModuleIdentifier import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier import org.gradle.api.internal.artifacts.DefaultPublishArtifactSet import org.gradle.api.internal.artifacts.configurations.OutgoingVariant -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact import org.gradle.api.internal.attributes.AttributeContainerInternal import org.gradle.api.internal.attributes.AttributesSchemaInternal @@ -66,7 +64,6 @@ class DefaultLocalComponentMetadataTest extends Specification { } def "configuration has no dependencies or artifacts when none have been added"() { - def moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) when: metadata.addConfiguration("super", "description", [] as Set, ImmutableSet.of("super"), false, false, ImmutableAttributes.EMPTY, true, true, ImmutableCapabilities.EMPTY) metadata.addConfiguration("conf", "description", ["super"] as Set, ImmutableSet.of("super", "conf"), true, true, ImmutableAttributes.EMPTY, true, true, ImmutableCapabilities.EMPTY) diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/LocalComponentDependencyMetadataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/LocalComponentDependencyMetadataTest.groovy index adf654269986..f66bb71c10f5 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/LocalComponentDependencyMetadataTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/LocalComponentDependencyMetadataTest.groovy @@ -26,7 +26,6 @@ import org.gradle.api.artifacts.component.ProjectComponentSelector import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.AttributeCompatibilityRule import org.gradle.api.attributes.CompatibilityCheckDetails -import org.gradle.api.internal.artifacts.DefaultImmutableModuleIdentifierFactory import org.gradle.api.internal.artifacts.DefaultModuleIdentifier import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions @@ -351,24 +350,24 @@ Configuration 'bar': } def "excludes nothing when no exclude rules provided"() { - def moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) + def moduleExclusions = new ModuleExclusions() def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, "to", [] as List, [], false, false, true, false, null) expect: def exclusions = moduleExclusions.excludeAny(copyOf(dep.excludes)) - exclusions == ModuleExclusions.excludeNone() + exclusions == moduleExclusions.nothing() exclusions.is(moduleExclusions.excludeAny(copyOf(dep.excludes))) } def "applies exclude rules when traversing the from configuration"() { def exclude1 = new DefaultExclude(DefaultModuleIdentifier.newId("group1", "*")) def exclude2 = new DefaultExclude(DefaultModuleIdentifier.newId("group2", "*")) - def moduleExclusions = new ModuleExclusions(new DefaultImmutableModuleIdentifierFactory()) + def moduleExclusions = new ModuleExclusions() def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, "to", [] as List, [exclude1, exclude2], false, false, true, false, null) expect: def exclusions = moduleExclusions.excludeAny(copyOf(dep.excludes)) - exclusions == moduleExclusions.excludeAny(exclude1, exclude2) + exclusions == moduleExclusions.excludeAny(ImmutableList.of(exclude1, exclude2)) exclusions.is(moduleExclusions.excludeAny(copyOf(dep.excludes))) } diff --git a/subprojects/platform-base/src/main/java/org/gradle/api/internal/resolve/LocalLibraryDependencyResolver.java b/subprojects/platform-base/src/main/java/org/gradle/api/internal/resolve/LocalLibraryDependencyResolver.java index 001af7d81153..3783fa6cde97 100644 --- a/subprojects/platform-base/src/main/java/org/gradle/api/internal/resolve/LocalLibraryDependencyResolver.java +++ b/subprojects/platform-base/src/main/java/org/gradle/api/internal/resolve/LocalLibraryDependencyResolver.java @@ -26,7 +26,7 @@ import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.api.internal.component.ArtifactType; @@ -206,7 +206,7 @@ private boolean isLibrary(ComponentIdentifier identifier) { @Nullable @Override - public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes) { + public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) { ComponentIdentifier componentId = component.getId(); if (isLibrary(componentId)) { return new MetadataSourcedComponentArtifacts().getArtifactsFor(component, configuration, this, new ConcurrentHashMap(), artifactTypeRegistry, exclusions, overriddenAttributes); diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/resolve/JvmLocalLibraryDependencyResolverTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/resolve/JvmLocalLibraryDependencyResolverTest.groovy index af3bf172c1c8..c022ff26b397 100644 --- a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/resolve/JvmLocalLibraryDependencyResolverTest.groovy +++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/resolve/JvmLocalLibraryDependencyResolverTest.groovy @@ -22,6 +22,7 @@ import org.gradle.api.artifacts.component.LibraryBinaryIdentifier import org.gradle.api.artifacts.component.LibraryComponentSelector import org.gradle.api.artifacts.component.ModuleComponentIdentifier import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry import org.gradle.api.internal.attributes.ImmutableAttributes import org.gradle.api.internal.component.ArtifactType @@ -68,6 +69,8 @@ import static org.gradle.util.WrapUtil.toDomainObjectSet class JvmLocalLibraryDependencyResolverTest extends Specification { + public static final ExcludeSpec NOTHING = new ModuleExclusions().nothing() + Map projects ProjectRegistry projectRegistry ProjectModelResolver projectModelResolver @@ -210,7 +213,7 @@ class JvmLocalLibraryDependencyResolverTest extends Specification { result.hasResult() when: - def artifacts = resolver.resolveArtifacts(component, configuration, Stub(ArtifactTypeRegistry), ModuleExclusions.excludeNone(), ImmutableAttributes.EMPTY) + def artifacts = resolver.resolveArtifacts(component, configuration, Stub(ArtifactTypeRegistry), NOTHING, ImmutableAttributes.EMPTY) then: artifacts != null @@ -230,7 +233,7 @@ class JvmLocalLibraryDependencyResolverTest extends Specification { !result.hasResult() when: - def artifacts = resolver.resolveArtifacts(component, configuration, Stub(ArtifactTypeRegistry), ModuleExclusions.excludeNone(), ImmutableAttributes.EMPTY) + def artifacts = resolver.resolveArtifacts(component, configuration, Stub(ArtifactTypeRegistry), NOTHING, ImmutableAttributes.EMPTY) then: artifacts == null diff --git a/subprojects/version-control/src/main/java/org/gradle/vcs/internal/resolver/VcsDependencyResolver.java b/subprojects/version-control/src/main/java/org/gradle/vcs/internal/resolver/VcsDependencyResolver.java index b1c8f446b86e..ac0e6979e78f 100644 --- a/subprojects/version-control/src/main/java/org/gradle/vcs/internal/resolver/VcsDependencyResolver.java +++ b/subprojects/version-control/src/main/java/org/gradle/vcs/internal/resolver/VcsDependencyResolver.java @@ -25,7 +25,7 @@ import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; import org.gradle.api.internal.artifacts.ivyservice.projectmodule.LocalComponentRegistry; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusion; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry; import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.api.internal.component.ArtifactType; @@ -159,7 +159,7 @@ public OriginArtifactSelector getArtifactSelector() { @Nullable @Override - public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ModuleExclusion exclusions, ImmutableAttributes overriddenAttributes) { + public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata configuration, ArtifactTypeRegistry artifactTypeRegistry, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) { return null; } From 2b35007fa53e9d40e2bff32d8145033b53a6a182 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 28 Apr 2019 23:36:22 +0200 Subject: [PATCH 02/55] Remove indexed exclude factory It didn't prove as fast as it was intended to be. Instead, we performed the same optimization for single groups/modules as we did for module sets. --- .../factories/DelegatingExcludeFactory.java | 33 ++++- .../factories/NormalizingExcludeFactory.java | 128 +++++++++++++----- .../simple/DefaultExcludeEverything.java | 2 +- .../simple/DefaultExcludeFactory.java | 35 +++-- .../simple/DefaultExcludeNothing.java | 2 +- .../excludes/simple/DefaultGroupExclude.java | 2 +- .../simple/DefaultGroupSetExclude.java | 83 ++++++++++++ .../excludes/simple/DefaultModuleExclude.java | 2 +- .../simple/DefaultModuleIdSetExclude.java | 88 ++++++++++++ .../simple/DefaultModuleSetExclude.java | 56 ++++---- .../excludes/specs/ExcludeFactory.java | 18 ++- .../excludes/specs/GroupSetExclude.java | 22 +++ .../excludes/specs/ModuleIdSetExclude.java | 24 ++++ .../excludes/specs/ModuleSetExclude.java | 4 +- .../NormalizingExcludeFactoryTest.groovy | 10 +- 15 files changed, 410 insertions(+), 99 deletions(-) create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupSetExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleIdSetExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/GroupSetExclude.java create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleIdSetExclude.java diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java index 3c5f35f0b7e6..e51cc42bc17c 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java @@ -16,8 +16,17 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ArtifactExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupSetExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdSetExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; import org.gradle.internal.component.model.IvyArtifactName; import java.util.List; @@ -31,32 +40,32 @@ public DelegatingExcludeFactory(ExcludeFactory delegate) { } @Override - public ExcludeSpec nothing() { + public ExcludeNothing nothing() { return delegate.nothing(); } @Override - public ExcludeSpec everything() { + public ExcludeEverything everything() { return delegate.everything(); } @Override - public ExcludeSpec group(String group) { + public GroupExclude group(String group) { return delegate.group(group); } @Override - public ExcludeSpec module(String module) { + public ModuleExclude module(String module) { return delegate.module(module); } @Override - public ExcludeSpec moduleId(ModuleIdentifier id) { + public ModuleIdExclude moduleId(ModuleIdentifier id) { return delegate.moduleId(id); } @Override - public ExcludeSpec artifact(ModuleIdentifier id, IvyArtifactName artifact) { + public ArtifactExclude artifact(ModuleIdentifier id, IvyArtifactName artifact) { return delegate.artifact(id, artifact); } @@ -86,7 +95,17 @@ public ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName } @Override - public ExcludeSpec moduleSet(Set modules) { + public ModuleIdSetExclude moduleIdSet(Set modules) { + return delegate.moduleIdSet(modules); + } + + @Override + public GroupSetExclude groupSet(Set groups) { + return delegate.groupSet(groups); + } + + @Override + public ModuleSetExclude moduleSet(Set modules) { return delegate.moduleSet(modules); } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java index f3b4720c8fad..451ef67e02c7 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -16,7 +16,6 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.CompositeExclude; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAllOf; @@ -25,16 +24,23 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupSetExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleExclude; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdSetExclude; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; +import org.gradle.internal.Cast; import java.util.Collections; -import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toSet; + /** * This factory performs normalization of exclude rules. This is the smartest * of all factories and is responsible for doing some basic algebra computations. @@ -66,56 +72,75 @@ public ExcludeSpec allOf(List specs) { } private ExcludeSpec doUnion(List specs) { - Set simpleExcludes = null; - List moduleSets = null; Set flattened = flatten(ExcludeAnyOf.class, specs, ExcludeEverything.class::isInstance, ExcludeNothing.class::isInstance); if (flattened == null) { return everything(); } - for (Iterator it = flattened.iterator(); it.hasNext(); ) { - ExcludeSpec spec = it.next(); - if (spec instanceof ModuleIdExclude) { - if (simpleExcludes == null) { - simpleExcludes = Sets.newHashSetWithExpectedSize(specs.size()); + if (flattened.isEmpty()) { + return nothing(); + } + Map> byType = flattened.stream().collect(Collectors.groupingBy(UnionOf::typeOf)); + List moduleIdExcludes = UnionOf.MODULEID.fromMap(byType); + List moduleIdSetsExcludes = UnionOf.MODULEID_SET.fromMap(byType); + List groupExcludes = UnionOf.GROUP.fromMap(byType); + List groupSetExcludes = UnionOf.GROUP_SET.fromMap(byType); + List moduleExcludes = UnionOf.MODULE.fromMap(byType); + List moduleSetExcludes = UnionOf.MODULE_SET.fromMap(byType); + List other = UnionOf.NOT_JOINABLE.fromMap(byType); + if (!moduleIdExcludes.isEmpty()) { + if (moduleIdExcludes.size() > 1 || !moduleIdSetsExcludes.isEmpty()) { + ModuleIdSetExclude excludeSpec = delegate.moduleIdSet(moduleIdExcludes.stream().map(ModuleIdExclude::getModuleId).collect(toSet())); + if (moduleIdSetsExcludes.isEmpty()) { + moduleIdSetsExcludes = ImmutableList.of(excludeSpec); + } else { + moduleIdSetsExcludes.add(excludeSpec); } - simpleExcludes.add((ModuleIdExclude) spec); - it.remove(); + moduleIdExcludes = Collections.emptyList(); } - // will allow merging module sets into a single one - if (spec instanceof ModuleSetExclude) { - if (moduleSets == null) { - moduleSets = Lists.newArrayList(); + } + if (!groupExcludes.isEmpty()) { + if (groupExcludes.size() > 1 || !groupSetExcludes.isEmpty()) { + GroupSetExclude excludeSpec = delegate.groupSet(groupExcludes.stream().map(GroupExclude::getGroup).collect(toSet())); + if (groupSetExcludes.isEmpty()) { + groupSetExcludes = ImmutableList.of(excludeSpec); + } else { + groupSetExcludes.add(excludeSpec); } - moduleSets.add((ModuleSetExclude) spec); - it.remove(); - + groupExcludes = Collections.emptyList(); } } - // merge all single module id into an id set - if (simpleExcludes != null) { - if (simpleExcludes.size() > 1 || moduleSets != null) { - ModuleSetExclude e = (ModuleSetExclude) delegate.moduleSet(simpleExcludes.stream().map(ModuleIdExclude::getModuleId).collect(Collectors.toSet())); - if (moduleSets != null) { - moduleSets.add(e); + if (!moduleExcludes.isEmpty()) { + if (moduleExcludes.size() > 1 || !moduleSetExcludes.isEmpty()) { + ModuleSetExclude excludeSpec = delegate.moduleSet(moduleExcludes.stream().map(ModuleExclude::getModule).collect(toSet())); + if (moduleSetExcludes.isEmpty()) { + moduleSetExcludes = ImmutableList.of(excludeSpec); } else { - flattened.add(e); + moduleSetExcludes.add(excludeSpec); } - } else { - flattened.add(simpleExcludes.iterator().next()); + moduleExcludes = Collections.emptyList(); } } - if (moduleSets != null) { - if (moduleSets.size() == 1) { - flattened.add(moduleSets.get(0)); - } else { - // merge all module sets - flattened.add(delegate.moduleSet(moduleSets.stream().flatMap(e -> e.getModuleIds().stream()).collect(Collectors.toSet()))); - } + if (moduleIdSetsExcludes.size() > 1) { + moduleIdSetsExcludes = ImmutableList.of(delegate.moduleIdSet(moduleIdSetsExcludes.stream().flatMap(e -> e.getModuleIds().stream()).collect(toSet()))); } - if (flattened.isEmpty()) { - return nothing(); + if (groupSetExcludes.size() > 1) { + groupSetExcludes = ImmutableList.of(delegate.groupSet(groupSetExcludes.stream().flatMap(e -> e.getGroups().stream()).collect(toSet()))); + } + if (moduleSetExcludes.size() > 1) { + moduleSetExcludes = ImmutableList.of(delegate.moduleSet(moduleSetExcludes.stream().flatMap(e -> e.getModules().stream()).collect(toSet()))); } - return Optimizations.optimizeList(this, ImmutableList.copyOf(flattened), delegate::anyOf); + ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize( + moduleIdExcludes.size() + groupExcludes.size() + moduleExcludes.size() + + moduleIdSetsExcludes.size() + groupSetExcludes.size() + moduleSetExcludes.size() + other.size() + ); + builder.addAll(moduleIdExcludes); + builder.addAll(groupExcludes); + builder.addAll(moduleExcludes); + builder.addAll(moduleIdSetsExcludes); + builder.addAll(groupSetExcludes); + builder.addAll(moduleSetExcludes); + builder.addAll(other); + return Optimizations.optimizeList(this, builder.build(), delegate::anyOf); } /** @@ -155,4 +180,33 @@ private ExcludeSpec doIntersect(List specs) { } return Optimizations.optimizeList(this, ImmutableList.copyOf(relevant), delegate::allOf); } + + private enum UnionOf { + MODULEID(ModuleIdExclude.class), + GROUP(GroupExclude.class), + MODULE(ModuleExclude.class), + MODULEID_SET(ModuleIdSetExclude.class), + GROUP_SET(GroupSetExclude.class), + MODULE_SET(ModuleSetExclude.class), + NOT_JOINABLE(ExcludeSpec.class); + + private final Class excludeClass; + + UnionOf(Class excludeClass) { + this.excludeClass = excludeClass; + } + + public List fromMap(Map> from) { + return Cast.uncheckedCast(from.getOrDefault(this, Collections.emptyList())); + } + + public static UnionOf typeOf(ExcludeSpec spec) { + for (UnionOf unionOf : UnionOf.values()) { + if (unionOf.excludeClass.isInstance(spec)) { + return unionOf; + } + } + return null; + } + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeEverything.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeEverything.java index d0fa7eab8725..1066c4ce74d7 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeEverything.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeEverything.java @@ -23,7 +23,7 @@ final class DefaultExcludeEverything implements ExcludeEverything { private static final ExcludeEverything INSTANCE = new DefaultExcludeEverything(); - public static ExcludeSpec get() { + public static ExcludeEverything get() { return INSTANCE; } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java index 6ac063956f9f..041b1f69738c 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java @@ -18,8 +18,17 @@ import com.google.common.collect.ImmutableList; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.Optimizations; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ArtifactExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupSetExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdSetExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; import org.gradle.internal.component.model.IvyArtifactName; import java.util.List; @@ -27,32 +36,32 @@ public class DefaultExcludeFactory implements ExcludeFactory { @Override - public ExcludeSpec nothing() { + public ExcludeNothing nothing() { return DefaultExcludeNothing.get(); } @Override - public ExcludeSpec everything() { + public ExcludeEverything everything() { return DefaultExcludeEverything.get(); } @Override - public ExcludeSpec group(String group) { + public GroupExclude group(String group) { return DefaultGroupExclude.of(group); } @Override - public ExcludeSpec module(String module) { + public ModuleExclude module(String module) { return DefaultModuleExclude.of(module); } @Override - public ExcludeSpec moduleId(ModuleIdentifier id) { + public ModuleIdExclude moduleId(ModuleIdentifier id) { return DefaultModuleIdExclude.of(id); } @Override - public ExcludeSpec artifact(ModuleIdentifier id, IvyArtifactName artifact) { + public ArtifactExclude artifact(ModuleIdentifier id, IvyArtifactName artifact) { return DefaultModuleArtifactExclude.of(id, artifact); } @@ -82,7 +91,17 @@ public ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName } @Override - public ExcludeSpec moduleSet(Set modules) { - return DefaultModuleSetExclude.of(modules); + public ModuleIdSetExclude moduleIdSet(Set modules) { + return DefaultModuleIdSetExclude.of(modules); + } + + @Override + public GroupSetExclude groupSet(Set groups) { + return new DefaultGroupSetExclude(groups); + } + + @Override + public ModuleSetExclude moduleSet(Set modules) { + return new DefaultModuleSetExclude(modules); } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeNothing.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeNothing.java index d86cc95a5e53..7c0d067e3d80 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeNothing.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeNothing.java @@ -23,7 +23,7 @@ class DefaultExcludeNothing implements ExcludeNothing { private static final ExcludeNothing INSTANCE = new DefaultExcludeNothing(); - public static ExcludeSpec get() { + public static ExcludeNothing get() { return INSTANCE; } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupExclude.java index 9265f9d9f841..067cf9864fc5 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupExclude.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupExclude.java @@ -29,7 +29,7 @@ private DefaultGroupExclude(String group) { this.hashCode = group.hashCode(); } - static ExcludeSpec of(String group) { + static GroupExclude of(String group) { return new DefaultGroupExclude(group); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupSetExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupSetExclude.java new file mode 100644 index 000000000000..a87ffd3e66e9 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultGroupSetExclude.java @@ -0,0 +1,83 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupSetExclude; +import org.gradle.internal.component.model.IvyArtifactName; + +import java.util.Set; + +final class DefaultGroupSetExclude implements GroupSetExclude { + private final Set groups; + private int hashCode; + + DefaultGroupSetExclude(Set groups) { + this.groups = groups; + this.hashCode = groups.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultGroupSetExclude that = (DefaultGroupSetExclude) o; + + return groups.equals(that.groups); + + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public Set getGroups() { + return groups; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return groups.contains(module.getGroup()); + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return false; + } + + @Override + public boolean mayExcludeArtifacts() { + return false; + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec other) { + return equals(other); + } + + @Override + public String toString() { + return "{ groups = " + groups + '}'; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleExclude.java index dab2af2adc97..fc685f75a239 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleExclude.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleExclude.java @@ -24,7 +24,7 @@ final class DefaultModuleExclude implements ModuleExclude { private final String module; private final int hashCode; - public static ExcludeSpec of(String module) { + public static ModuleExclude of(String module) { return new DefaultModuleExclude(module); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleIdSetExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleIdSetExclude.java new file mode 100644 index 000000000000..827d17da95de --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleIdSetExclude.java @@ -0,0 +1,88 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; + +import com.google.common.collect.ImmutableSet; +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdSetExclude; +import org.gradle.internal.component.model.IvyArtifactName; + +import java.util.Set; + +final class DefaultModuleIdSetExclude implements ModuleIdSetExclude { + private final Set moduleIds; + private final int hashCode; + + static ModuleIdSetExclude of(Set ids) { + return new DefaultModuleIdSetExclude(ImmutableSet.copyOf(ids)); + } + + private DefaultModuleIdSetExclude(ImmutableSet moduleIds) { + this.moduleIds = moduleIds; + this.hashCode = moduleIds.hashCode(); + } + + @Override + public Set getModuleIds() { + return moduleIds; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return moduleIds.contains(module); + } + + @Override + public boolean excludesArtifact(ModuleIdentifier module, IvyArtifactName artifactName) { + return false; + } + + @Override + public boolean mayExcludeArtifacts() { + return false; + } + + @Override + public boolean equalsIgnoreArtifact(ExcludeSpec other) { + return equals(other); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultModuleIdSetExclude that = (DefaultModuleIdSetExclude) o; + + return moduleIds.equals(that.moduleIds); + + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + return "{ module ids = " + moduleIds + '}'; + } +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleSetExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleSetExclude.java index f4bf9f1c3407..0f32edcc8d4e 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleSetExclude.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultModuleSetExclude.java @@ -15,7 +15,6 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; -import com.google.common.collect.ImmutableSet; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; @@ -24,26 +23,42 @@ import java.util.Set; final class DefaultModuleSetExclude implements ModuleSetExclude { - private final Set moduleIds; - private final int hashCode; + private final Set modules; + private int hashCode; - static ModuleSetExclude of(Set ids) { - return new DefaultModuleSetExclude(ImmutableSet.copyOf(ids)); + DefaultModuleSetExclude(Set modules) { + this.modules = modules; + this.hashCode = modules.hashCode(); } - private DefaultModuleSetExclude(ImmutableSet moduleIds) { - this.moduleIds = moduleIds; - this.hashCode = moduleIds.hashCode(); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultModuleSetExclude that = (DefaultModuleSetExclude) o; + + return modules.equals(that.modules); + + } + + @Override + public int hashCode() { + return hashCode; } @Override - public Set getModuleIds() { - return moduleIds; + public Set getModules() { + return modules; } @Override public boolean excludes(ModuleIdentifier module) { - return moduleIds.contains(module); + return modules.contains(module.getName()); } @Override @@ -62,22 +77,7 @@ public boolean equalsIgnoreArtifact(ExcludeSpec other) { } @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - DefaultModuleSetExclude that = (DefaultModuleSetExclude) o; - - return moduleIds.equals(that.moduleIds); - - } - - @Override - public int hashCode() { - return hashCode; + public String toString() { + return "{ module names = " + modules + '}'; } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java index b7e35a51351f..2f887401138c 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java @@ -22,17 +22,17 @@ import java.util.Set; public interface ExcludeFactory { - ExcludeSpec nothing(); + ExcludeNothing nothing(); - ExcludeSpec everything(); + ExcludeEverything everything(); - ExcludeSpec group(String group); + GroupExclude group(String group); - ExcludeSpec module(String module); + ModuleExclude module(String module); - ExcludeSpec moduleId(ModuleIdentifier id); + ModuleIdExclude moduleId(ModuleIdentifier id); - ExcludeSpec artifact(ModuleIdentifier id, IvyArtifactName artifact); + ArtifactExclude artifact(ModuleIdentifier id, IvyArtifactName artifact); ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two); @@ -44,5 +44,9 @@ public interface ExcludeFactory { ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher); - ExcludeSpec moduleSet(Set modules); + ModuleIdSetExclude moduleIdSet(Set modules); + + GroupSetExclude groupSet(Set groups); + + ModuleSetExclude moduleSet(Set modules); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/GroupSetExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/GroupSetExclude.java new file mode 100644 index 000000000000..78c628bd5f93 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/GroupSetExclude.java @@ -0,0 +1,22 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +import java.util.Set; + +public interface GroupSetExclude extends ExcludeSpec { + Set getGroups(); +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleIdSetExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleIdSetExclude.java new file mode 100644 index 000000000000..ba4dc48d49e8 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleIdSetExclude.java @@ -0,0 +1,24 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; + +import org.gradle.api.artifacts.ModuleIdentifier; + +import java.util.Set; + +public interface ModuleIdSetExclude extends ExcludeSpec { + Set getModuleIds(); +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleSetExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleSetExclude.java index de8742d30477..064568f5f8e2 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleSetExclude.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleSetExclude.java @@ -15,10 +15,8 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; -import org.gradle.api.artifacts.ModuleIdentifier; - import java.util.Set; public interface ModuleSetExclude extends ExcludeSpec { - Set getModuleIds(); + Set getModules(); } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy index 57acacc8d6d7..1b3f23513ab6 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy @@ -49,14 +49,14 @@ class NormalizingExcludeFactoryTest extends Specification { where: left | right | expected -/* everything() | nothing() | everything() + everything() | nothing() | everything() everything() | everything() | everything() nothing() | nothing() | nothing() everything() | group("foo") | everything() nothing() | group("foo") | group("foo") - group("foo") | group("bar") | anyOf(group("foo"), group("bar")) - group("foo") | module("bar") | anyOf(group("foo"), module("bar"))*/ - anyOf(group("foo"), group("bar")) | group("foo") | anyOf(group("foo"), group("bar")) + //group("foo") | group("bar") | anyOf(group("foo"), group("bar")) + group("foo") | module("bar") | anyOf(group("foo"), module("bar")) + //anyOf(group("foo"), group("bar")) | group("foo") | anyOf(group("foo"), group("bar")) anyOf(group("foo"), module("bar")) | module("bar") | anyOf(module("bar"), group("foo")) } @@ -73,7 +73,7 @@ class NormalizingExcludeFactoryTest extends Specification { everything() | everything() | everything() | everything() nothing() | nothing() | nothing() | nothing() everything() | group("foo") | everything() | everything() - group("foo") | group("bar") | group("baz") | anyOf(group("foo"), group("bar"), group("baz")) + //group("foo") | group("bar") | group("baz") | anyOf(group("foo"), group("bar"), group("baz")) } @Unroll("#left ∩ #right = #expected") From b33a6eb50d44817a8311692b178fbd6fd098913f Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 29 Apr 2019 08:37:06 +0200 Subject: [PATCH 03/55] Optimize `ExcludePair` for comparisons --- .../excludes/factories/CachingExcludeFactory.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java index 6d4936102d77..9cc68e9fe111 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java @@ -75,9 +75,13 @@ private final static class ExcludePair { private final int hashCode; private ExcludePair(ExcludeSpec left, ExcludeSpec right) { - this.left = left; - this.right = right; - this.hashCode = left.hashCode() ^ right.hashCode(); // must be symmetrical + int lhc = left.hashCode(); + int rhc = right.hashCode(); + this.hashCode = lhc ^ rhc; // must be symmetrical + // Optimizes comparisons by making sure that the 2 elements of + // the pair are "sorted" by hashcode ascending + this.left = lhc Date: Mon, 29 Apr 2019 11:20:30 +0200 Subject: [PATCH 04/55] Reuse caching at lower level The merge cache can be reused at different stages. In particular, when the normalizing factory normalizes unions/intersections, it is possible that the normalized union is found in cache. --- .../excludes/ModuleExclusions.java | 11 ++- .../factories/CachingExcludeFactory.java | 99 +++++++++++++++---- 2 files changed, 87 insertions(+), 23 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java index 538674d8cdf8..7284cd9063c8 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java @@ -31,12 +31,17 @@ import java.util.stream.Collectors; public class ModuleExclusions { + private final CachingExcludeFactory.MergeCaches mergeCaches = new CachingExcludeFactory.MergeCaches(); // please keep the formatting below as it helps enabling or disabling stages private final ExcludeFactory factory = new OptimizingExcludeFactory(// optimizes for nulls, 2-params, ... mandatory - new CachingExcludeFactory(// caches the result of operations + new CachingExcludeFactory(// caches the result of TL operations new NormalizingExcludeFactory(// performs algebra - new DefaultExcludeFactory()// the end of the chain, mandatory - ) + new CachingExcludeFactory(// caches the result of optimization operations + new DefaultExcludeFactory(),// the end of the chain, mandatory + mergeCaches // shares the same caches as the top level one as after reducing we can find already cached merge operations + ) + ), + mergeCaches ) ); private final Map metadataToExcludeCache = Maps.newConcurrentMap(); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java index 9cc68e9fe111..9d5f5e71e4d0 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java @@ -21,6 +21,8 @@ import java.util.List; import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; /** * This factory is responsible for caching merging queries. It delegates computations @@ -28,13 +30,11 @@ * queries, caching will be faster. */ public class CachingExcludeFactory extends DelegatingExcludeFactory { - private final Map allOfPairCache = Maps.newConcurrentMap(); - private final Map anyOfPairCache = Maps.newConcurrentMap(); - private final Map allOfListCache = Maps.newConcurrentMap(); - private final Map anyOfListCache = Maps.newConcurrentMap(); + private final MergeCaches caches; - public CachingExcludeFactory(ExcludeFactory delegate) { + public CachingExcludeFactory(ExcludeFactory delegate, MergeCaches caches) { super(delegate); + this.caches = caches; } @Override @@ -43,26 +43,22 @@ public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { } private ExcludeSpec cachedAnyPair(ExcludeSpec left, ExcludeSpec right) { - return anyOfPairCache.computeIfAbsent(new ExcludePair(left, right), key -> delegate.anyOf(key.left, key.right)); + return caches.getAnyPair(ExcludePair.of(left, right), key -> delegate.anyOf(key.left, key.right)); } @Override public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { - return cachedAllPair(one, two); - } - - private ExcludeSpec cachedAllPair(ExcludeSpec left, ExcludeSpec right) { - return allOfPairCache.computeIfAbsent(new ExcludePair(left, right), key -> delegate.allOf(key.left, key.right)); + return caches.getAllPair(ExcludePair.of(one, two), key -> delegate.allOf(key.left, key.right)); } @Override public ExcludeSpec anyOf(List specs) { - return anyOfListCache.computeIfAbsent(new ExcludeList(specs), key -> delegate.anyOf(key.specs)); + return caches.getAnyOf(new ExcludeList(specs), key -> delegate.anyOf(key.specs)); } @Override public ExcludeSpec allOf(List specs) { - return allOfListCache.computeIfAbsent(new ExcludeList(specs), key -> delegate.allOf(key.specs)); + return caches.getAllOf(new ExcludeList(specs), key -> delegate.allOf(key.specs)); } /** @@ -74,14 +70,19 @@ private final static class ExcludePair { private final ExcludeSpec right; private final int hashCode; + // Optimizes comparisons by making sure that the 2 elements of + // the pair are "sorted" by hashcode ascending + private static ExcludePair of(ExcludeSpec left, ExcludeSpec right) { + if (left.hashCode() > right.hashCode()) { + return new ExcludePair(right, left); + } + return new ExcludePair(left, right); + } + private ExcludePair(ExcludeSpec left, ExcludeSpec right) { - int lhc = left.hashCode(); - int rhc = right.hashCode(); - this.hashCode = lhc ^ rhc; // must be symmetrical - // Optimizes comparisons by making sure that the 2 elements of - // the pair are "sorted" by hashcode ascending - this.left = lhc allOfPairCache = ConcurrentCache.of(); + private final ConcurrentCache anyOfPairCache = ConcurrentCache.of(); + private final ConcurrentCache allOfListCache = ConcurrentCache.of(); + private final ConcurrentCache anyOfListCache = ConcurrentCache.of(); + + ExcludeSpec getAnyPair(ExcludePair pair, Function onMiss) { + return anyOfPairCache.computeIfAbsent(pair, onMiss); + } + + ExcludeSpec getAllPair(ExcludePair pair, Function onMiss) { + return allOfPairCache.computeIfAbsent(pair, onMiss); + } + + ExcludeSpec getAnyOf(ExcludeList list, Function onMiss) { + return anyOfListCache.computeIfAbsent(list, onMiss); + } + + ExcludeSpec getAllOf(ExcludeList list, Function onMiss) { + return allOfListCache.computeIfAbsent(list, onMiss); + } + } + + private static class ConcurrentCache { + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private final Map backingMap = Maps.newHashMap(); + + static ConcurrentCache of() { + return new ConcurrentCache<>(); + } + + V computeIfAbsent(K key, Function producer) { + lock.readLock().lock(); + try { + V value = backingMap.get(key); + if (value != null) { + return value; + } + } finally { + lock.readLock().unlock(); + } + lock.writeLock().lock(); + try { + V value = producer.apply(key); + backingMap.put(key, value); + return value; + } finally { + lock.writeLock().unlock(); + } + } + } } From 0d168e6ba9007d5b7e1e6b25b662e2dcc3eb9413 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 29 Apr 2019 13:18:21 +0200 Subject: [PATCH 05/55] Optimize flattening This commit optimizes flattening by avoiding the creation of intermediate data structures. In particular using lists we were converting from and to sets unnecessarily. --- .../excludes/ModuleExclusions.java | 4 +- .../factories/CachingExcludeFactory.java | 29 +++--- .../factories/DelegatingExcludeFactory.java | 8 +- .../{specs => factories}/ExcludeFactory.java | 17 +++- .../factories/NormalizingExcludeFactory.java | 93 +++++++++++++------ .../excludes/factories/Optimizations.java | 7 +- .../factories/OptimizingExcludeFactory.java | 18 ++-- .../simple/DefaultCompositeExclude.java | 10 +- .../excludes/simple/DefaultExcludeAllOf.java | 6 +- .../excludes/simple/DefaultExcludeAnyOf.java | 6 +- .../simple/DefaultExcludeFactory.java | 17 ++-- .../excludes/specs/CompositeExclude.java | 6 +- .../NormalizingExcludeFactoryTest.groovy | 9 +- 13 files changed, 138 insertions(+), 92 deletions(-) rename subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/{specs => factories}/ExcludeFactory.java (58%) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java index 7284cd9063c8..a411af90f09e 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java @@ -22,7 +22,7 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.NormalizingExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.OptimizingExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeFactory; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.internal.component.model.ExcludeMetadata; import org.gradle.internal.component.model.IvyArtifactName; @@ -54,7 +54,7 @@ public ModuleExclusions() { public ExcludeSpec excludeAny(ImmutableList excludes) { return factory.anyOf(excludes.stream() .map(this::forExclude) - .collect(Collectors.toList())); + .collect(Collectors.toSet())); } public ExcludeSpec nothing() { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java index 9d5f5e71e4d0..91270978feea 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java @@ -16,11 +16,10 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; import com.google.common.collect.Maps; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; @@ -52,13 +51,13 @@ public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { } @Override - public ExcludeSpec anyOf(List specs) { - return caches.getAnyOf(new ExcludeList(specs), key -> delegate.anyOf(key.specs)); + public ExcludeSpec anyOf(Set specs) { + return caches.getAnyOf(new ExcludesKey(specs), key -> delegate.anyOf(key.specs)); } @Override - public ExcludeSpec allOf(List specs) { - return caches.getAllOf(new ExcludeList(specs), key -> delegate.allOf(key.specs)); + public ExcludeSpec allOf(Set specs) { + return caches.getAllOf(new ExcludesKey(specs), key -> delegate.allOf(key.specs)); } /** @@ -109,12 +108,12 @@ public int hashCode() { * A special exclude spec list key which recognizes * that union and intersection are commutative. */ - private static class ExcludeList { - private final List specs; + private static class ExcludesKey { + private final Set specs; private final int size; private final int hashCode; - private ExcludeList(List specs) { + private ExcludesKey(Set specs) { this.specs = specs; this.size = specs.size(); this.hashCode = computeHashCode(); @@ -137,11 +136,11 @@ public boolean equals(Object o) { return false; } - ExcludeList that = (ExcludeList) o; + ExcludesKey that = (ExcludesKey) o; if (size != that.size) { return false; } - return specs.containsAll(that.specs); + return specs.equals(that.specs); } @Override @@ -159,8 +158,8 @@ public int hashCode() { public static class MergeCaches { private final ConcurrentCache allOfPairCache = ConcurrentCache.of(); private final ConcurrentCache anyOfPairCache = ConcurrentCache.of(); - private final ConcurrentCache allOfListCache = ConcurrentCache.of(); - private final ConcurrentCache anyOfListCache = ConcurrentCache.of(); + private final ConcurrentCache allOfListCache = ConcurrentCache.of(); + private final ConcurrentCache anyOfListCache = ConcurrentCache.of(); ExcludeSpec getAnyPair(ExcludePair pair, Function onMiss) { return anyOfPairCache.computeIfAbsent(pair, onMiss); @@ -170,11 +169,11 @@ ExcludeSpec getAllPair(ExcludePair pair, Function onMi return allOfPairCache.computeIfAbsent(pair, onMiss); } - ExcludeSpec getAnyOf(ExcludeList list, Function onMiss) { + ExcludeSpec getAnyOf(ExcludesKey list, Function onMiss) { return anyOfListCache.computeIfAbsent(list, onMiss); } - ExcludeSpec getAllOf(ExcludeList list, Function onMiss) { + ExcludeSpec getAllOf(ExcludesKey list, Function onMiss) { return allOfListCache.computeIfAbsent(list, onMiss); } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java index e51cc42bc17c..821d0c2ae346 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java @@ -18,7 +18,6 @@ import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ArtifactExclude; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude; @@ -29,13 +28,12 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; import org.gradle.internal.component.model.IvyArtifactName; -import java.util.List; import java.util.Set; public abstract class DelegatingExcludeFactory implements ExcludeFactory { protected final ExcludeFactory delegate; - public DelegatingExcludeFactory(ExcludeFactory delegate) { + DelegatingExcludeFactory(ExcludeFactory delegate) { this.delegate = delegate; } @@ -80,12 +78,12 @@ public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { } @Override - public ExcludeSpec anyOf(List specs) { + public ExcludeSpec anyOf(Set specs) { return delegate.anyOf(specs); } @Override - public ExcludeSpec allOf(List specs) { + public ExcludeSpec allOf(Set specs) { return delegate.allOf(specs); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/ExcludeFactory.java similarity index 58% rename from subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/ExcludeFactory.java index 2f887401138c..2d0d667aa8f4 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/ExcludeFactory.java @@ -13,12 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; +package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ArtifactExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupSetExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdSetExclude; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; import org.gradle.internal.component.model.IvyArtifactName; -import java.util.List; import java.util.Set; public interface ExcludeFactory { @@ -38,9 +47,9 @@ public interface ExcludeFactory { ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two); - ExcludeSpec anyOf(List specs); + ExcludeSpec anyOf(Set specs); - ExcludeSpec allOf(List specs); + ExcludeSpec allOf(Set specs); ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java index 451ef67e02c7..487b1898fcdc 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -16,12 +16,11 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Sets; +import com.google.common.collect.ImmutableSet; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.CompositeExclude; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAllOf; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAnyOf; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude; @@ -53,33 +52,33 @@ public NormalizingExcludeFactory(ExcludeFactory delegate) { @Override public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { - return doUnion(ImmutableList.of(one, two)); + return doUnion(ImmutableSet.of(one, two)); } @Override public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { - return doIntersect(ImmutableList.of(one, two)); + return doIntersect(ImmutableSet.of(one, two)); } @Override - public ExcludeSpec anyOf(List specs) { + public ExcludeSpec anyOf(Set specs) { return doUnion(specs); } @Override - public ExcludeSpec allOf(List specs) { + public ExcludeSpec allOf(Set specs) { return doIntersect(specs); } - private ExcludeSpec doUnion(List specs) { - Set flattened = flatten(ExcludeAnyOf.class, specs, ExcludeEverything.class::isInstance, ExcludeNothing.class::isInstance); - if (flattened == null) { + private ExcludeSpec doUnion(Set specs) { + FlattenOperationResult flattened = flatten(ExcludeAnyOf.class, specs, ExcludeEverything.class::isInstance, ExcludeNothing.class::isInstance); + if (flattened.fastExit) { return everything(); } - if (flattened.isEmpty()) { + if (flattened.result.isEmpty()) { return nothing(); } - Map> byType = flattened.stream().collect(Collectors.groupingBy(UnionOf::typeOf)); + Map> byType = flattened.result.stream().collect(Collectors.groupingBy(UnionOf::typeOf)); List moduleIdExcludes = UnionOf.MODULEID.fromMap(byType); List moduleIdSetsExcludes = UnionOf.MODULEID_SET.fromMap(byType); List groupExcludes = UnionOf.GROUP.fromMap(byType); @@ -129,7 +128,7 @@ private ExcludeSpec doUnion(List specs) { if (moduleSetExcludes.size() > 1) { moduleSetExcludes = ImmutableList.of(delegate.moduleSet(moduleSetExcludes.stream().flatMap(e -> e.getModules().stream()).collect(toSet()))); } - ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize( + ImmutableSet.Builder builder = ImmutableSet.builderWithExpectedSize( moduleIdExcludes.size() + groupExcludes.size() + moduleExcludes.size() + moduleIdSetsExcludes.size() + groupSetExcludes.size() + moduleSetExcludes.size() + other.size() ); @@ -140,7 +139,7 @@ private ExcludeSpec doUnion(List specs) { builder.addAll(groupSetExcludes); builder.addAll(moduleSetExcludes); builder.addAll(other); - return Optimizations.optimizeList(this, builder.build(), delegate::anyOf); + return Optimizations.optimizeCollection(this, builder.build(), delegate::anyOf); } /** @@ -150,35 +149,56 @@ private ExcludeSpec doUnion(List specs) { * - empty list meaning that an easy simplification was reached and we directly know the result * - flattened unions/intersections */ - private Set flatten(Class flattenType, List specs, Predicate fastExit, Predicate filter) { - Set out = null; + private FlattenOperationResult flatten(Class flattenType, Set specs, Predicate fastExit, Predicate ignoreSpec) { + boolean filtered = false; + boolean flatten = false; + int size = 0; for (ExcludeSpec spec : specs) { if (fastExit.test(spec)) { - return null; + return FlattenOperationResult.FAST_EXIT; } - if (!filter.test(spec)) { - if (out == null) { - out = Sets.newHashSetWithExpectedSize(4 * specs.size()); - } - if (flattenType.isInstance(spec)) { - out.addAll(((CompositeExclude) spec).getComponents()); + if (ignoreSpec.test(spec)) { + filtered = true; + } + if (flattenType.isInstance(spec)) { + flatten = true; + size += ((CompositeExclude) spec).size(); + } else { + size++; + } + } + if (!filtered && !flatten) { + return FlattenOperationResult.of(specs); + } + if (filtered && !flatten) { + return FlattenOperationResult.of(specs.stream().filter(e -> !ignoreSpec.test(e)).collect(Collectors.toSet())); + } + // slowest path + final boolean processAll = !filtered; + ImmutableSet.Builder flattened = ImmutableSet.builderWithExpectedSize(size); + for (ExcludeSpec e : specs) { + if (processAll || !ignoreSpec.test(e)) { + if (flattenType.isInstance(e)) { + CompositeExclude compositeExclude = (CompositeExclude) e; + flattened.addAll(compositeExclude.getComponents()); } else { - out.add(spec); + flattened.add(e); } } } - return out == null ? Collections.emptySet() : out; + return FlattenOperationResult.of(flattened.build()); } - private ExcludeSpec doIntersect(List specs) { - Set relevant = flatten(ExcludeAllOf.class, specs, ExcludeNothing.class::isInstance, ExcludeEverything.class::isInstance); - if (relevant == null) { + private ExcludeSpec doIntersect(Set specs) { + FlattenOperationResult flattened = flatten(ExcludeAllOf.class, specs, ExcludeNothing.class::isInstance, ExcludeEverything.class::isInstance); + if (flattened.fastExit) { return nothing(); } - if (relevant.isEmpty()) { + Set result = flattened.result; + if (result.isEmpty()) { return everything(); } - return Optimizations.optimizeList(this, ImmutableList.copyOf(relevant), delegate::allOf); + return Optimizations.optimizeCollection(this, result, delegate::allOf); } private enum UnionOf { @@ -209,4 +229,19 @@ public static UnionOf typeOf(ExcludeSpec spec) { return null; } } + + private static class FlattenOperationResult { + private static final FlattenOperationResult FAST_EXIT = new FlattenOperationResult(null, true); + private final Set result; + private final boolean fastExit; + + private FlattenOperationResult(Set result, boolean fastExit) { + this.result = result; + this.fastExit = fastExit; + } + + public static FlattenOperationResult of(Set specs) { + return new FlattenOperationResult(specs, false); + } + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.java index cf68477477ff..ab6aa5254bbb 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.java @@ -16,11 +16,10 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; -import java.util.List; +import java.util.Collection; import java.util.function.BiFunction; import java.util.function.Function; @@ -77,12 +76,12 @@ public static ExcludeSpec optimizeAllOf(ExcludeFactory factory, ExcludeSpec one, return onMiss.apply(one, two); } - public static ExcludeSpec optimizeList(ExcludeFactory factory, List specs, Function, ExcludeSpec> onMiss) { + public static > ExcludeSpec optimizeCollection(ExcludeFactory factory, T specs, Function onMiss) { if (specs.isEmpty()) { return factory.nothing(); } if (specs.size() == 1) { - return specs.get(0); + return specs.iterator().next(); } return onMiss.apply(specs); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java index d1b277af5da7..0b7ffa6c7445 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java @@ -15,10 +15,10 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; -import java.util.List; +import java.util.Iterator; +import java.util.Set; /** * This factory is responsible for optimizing in special cases: null parameters, @@ -40,20 +40,22 @@ public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { } @Override - public ExcludeSpec anyOf(List specs) { - return Optimizations.optimizeList(this, specs, list -> { + public ExcludeSpec anyOf(Set specs) { + return Optimizations.optimizeCollection(this, specs, list -> { if (list.size() == 2) { - return delegate.anyOf(list.get(0), list.get(1)); + Iterator it = list.iterator(); + return delegate.anyOf(it.next(), it.next()); } return delegate.anyOf(list); }); } @Override - public ExcludeSpec allOf(List specs) { - return Optimizations.optimizeList(this, specs, list -> { + public ExcludeSpec allOf(Set specs) { + return Optimizations.optimizeCollection(this, specs, list -> { if (list.size() == 2) { - return delegate.allOf(list.get(0), list.get(1)); + Iterator it = list.iterator(); + return delegate.allOf(it.next(), it.next()); } return delegate.allOf(list); }); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java index fc694803d425..dbbad30e1580 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java @@ -21,15 +21,18 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import java.util.Iterator; +import java.util.Set; import java.util.stream.Stream; abstract class DefaultCompositeExclude implements CompositeExclude { private final ImmutableSet components; private final int hashCode; + private final int size; DefaultCompositeExclude(ImmutableSet components) { this.components = components; this.hashCode = components.hashCode(); + this.size = components.size(); } @Override @@ -102,10 +105,15 @@ public final Stream components() { } @Override - public ImmutableSet getComponents() { + public Set getComponents() { return components; } + @Override + public int size() { + return size; + } + @Override public String toString() { return "{" + getDisplayName() + diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java index 3f027c46b8cf..35bd6cbffe5f 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java @@ -21,11 +21,9 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.internal.component.model.IvyArtifactName; -import java.util.List; - final class DefaultExcludeAllOf extends DefaultCompositeExclude implements ExcludeAllOf { - public static ExcludeSpec of(List components) { - return new DefaultExcludeAllOf(ImmutableSet.copyOf(components)); + public static ExcludeAllOf of(ImmutableSet components) { + return new DefaultExcludeAllOf(components); } private DefaultExcludeAllOf(ImmutableSet components) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java index ab64e51b10aa..4510359fa16b 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java @@ -21,11 +21,9 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.internal.component.model.IvyArtifactName; -import java.util.List; - final class DefaultExcludeAnyOf extends DefaultCompositeExclude implements ExcludeAnyOf { - public static ExcludeSpec of(List components) { - return new DefaultExcludeAnyOf(ImmutableSet.copyOf(components)); + public static ExcludeSpec of(ImmutableSet components) { + return new DefaultExcludeAnyOf(components); } private DefaultExcludeAnyOf(ImmutableSet components) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java index 041b1f69738c..d080bdd0c370 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java @@ -15,12 +15,12 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.Optimizations; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ArtifactExclude; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude; @@ -31,7 +31,6 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; import org.gradle.internal.component.model.IvyArtifactName; -import java.util.List; import java.util.Set; public class DefaultExcludeFactory implements ExcludeFactory { @@ -67,22 +66,22 @@ public ArtifactExclude artifact(ModuleIdentifier id, IvyArtifactName artifact) { @Override public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { - return Optimizations.optimizeAnyOf(one, two, (a, b) -> DefaultExcludeAnyOf.of(ImmutableList.of(a, b))); + return Optimizations.optimizeAnyOf(one, two, (a, b) -> DefaultExcludeAnyOf.of(ImmutableSet.of(a, b))); } @Override public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { - return Optimizations.optimizeAllOf(this, one, two, (a, b) -> DefaultExcludeAllOf.of(ImmutableList.of(a, b))); + return Optimizations.optimizeAllOf(this, one, two, (a, b) -> DefaultExcludeAllOf.of(ImmutableSet.of(a, b))); } @Override - public ExcludeSpec anyOf(List specs) { - return DefaultExcludeAnyOf.of(specs); + public ExcludeSpec anyOf(Set specs) { + return DefaultExcludeAnyOf.of(ImmutableSet.copyOf(specs)); } @Override - public ExcludeSpec allOf(List specs) { - return DefaultExcludeAllOf.of(specs); + public ExcludeSpec allOf(Set specs) { + return DefaultExcludeAllOf.of(ImmutableSet.copyOf(specs)); } @Override diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java index 85c0bc324e43..daf09cd51515 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java @@ -15,7 +15,7 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs; -import java.util.Collection; +import java.util.Set; import java.util.stream.Stream; public interface CompositeExclude extends ExcludeSpec { @@ -24,9 +24,11 @@ public interface CompositeExclude extends ExcludeSpec { Stream components(); - Collection getComponents(); + Set getComponents(); default boolean contains(ExcludeSpec spec) { return components().anyMatch(spec::equals); } + + int size(); } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy index 1b3f23513ab6..f3d64480596b 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy @@ -16,10 +16,9 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories -import com.google.common.collect.ImmutableList +import com.google.common.collect.ImmutableSet import org.gradle.api.internal.artifacts.DefaultModuleIdentifier import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeFactory -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeFactory import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec import org.gradle.internal.component.model.DefaultIvyArtifactName import org.gradle.internal.component.model.IvyArtifactName @@ -64,7 +63,7 @@ class NormalizingExcludeFactoryTest extends Specification { def "union of three elements"() { expect: [one, two, three].combinations().each { list -> - assert factory.anyOf(list) == expected + assert factory.anyOf(list as Set) == expected } where: @@ -116,11 +115,11 @@ class NormalizingExcludeFactoryTest extends Specification { } private ExcludeSpec anyOf(ExcludeSpec... specs) { - delegate.anyOf(ImmutableList.copyOf(specs)) + delegate.anyOf(ImmutableSet.copyOf(specs)) } private ExcludeSpec allOf(ExcludeSpec... specs) { - delegate.allOf(ImmutableList.copyOf(specs)) + delegate.allOf(ImmutableSet.copyOf(specs)) } private ExcludeSpec ivy(String group, String module, IvyArtifactName artifact, String matcher) { From 5e59d336f2e6f73ff418d6b220663dbd9058c5e9 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 29 Apr 2019 14:27:50 +0200 Subject: [PATCH 06/55] Use coarce grained locking Measurements show it's significantly faster than using a read/write lock. --- .../excludes/ModuleExclusions.java | 2 +- .../factories/CachingExcludeFactory.java | 18 ++++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java index a411af90f09e..40b2e58f6bf1 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java @@ -37,7 +37,7 @@ public class ModuleExclusions { new CachingExcludeFactory(// caches the result of TL operations new NormalizingExcludeFactory(// performs algebra new CachingExcludeFactory(// caches the result of optimization operations - new DefaultExcludeFactory(),// the end of the chain, mandatory + new DefaultExcludeFactory(), // the end of the chain, mandatory mergeCaches // shares the same caches as the top level one as after reducing we can find already cached merge operations ) ), diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java index 91270978feea..1b58a7a94451 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java @@ -20,7 +20,6 @@ import java.util.Map; import java.util.Set; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; /** @@ -81,7 +80,7 @@ private static ExcludePair of(ExcludeSpec left, ExcludeSpec right) { private ExcludePair(ExcludeSpec left, ExcludeSpec right) { this.left = left; this.right = right; - this.hashCode = left.hashCode() ^ right.hashCode(); // must be symmetrical + this.hashCode = 31 * left.hashCode() + right.hashCode(); } @Override @@ -179,30 +178,21 @@ ExcludeSpec getAllOf(ExcludesKey list, Function onMiss } private static class ConcurrentCache { - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Map backingMap = Maps.newHashMap(); - static ConcurrentCache of() { + static ConcurrentCache of() { return new ConcurrentCache<>(); } V computeIfAbsent(K key, Function producer) { - lock.readLock().lock(); - try { + synchronized (backingMap) { V value = backingMap.get(key); if (value != null) { return value; } - } finally { - lock.readLock().unlock(); - } - lock.writeLock().lock(); - try { - V value = producer.apply(key); + value = producer.apply(key); backingMap.put(key, value); return value; - } finally { - lock.writeLock().unlock(); } } } From ee367ab724ab181d0c73e2d649bc4211d96f1355 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 29 Apr 2019 20:53:29 +0200 Subject: [PATCH 07/55] Remove redundant optimization --- .../resolveengine/excludes/simple/DefaultExcludeFactory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java index d080bdd0c370..838863d0cab2 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java @@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableSet; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.ExcludeFactory; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.Optimizations; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ArtifactExclude; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing; @@ -66,12 +65,12 @@ public ArtifactExclude artifact(ModuleIdentifier id, IvyArtifactName artifact) { @Override public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { - return Optimizations.optimizeAnyOf(one, two, (a, b) -> DefaultExcludeAnyOf.of(ImmutableSet.of(a, b))); + return DefaultExcludeAnyOf.of(ImmutableSet.of(one, two)); } @Override public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { - return Optimizations.optimizeAllOf(this, one, two, (a, b) -> DefaultExcludeAllOf.of(ImmutableSet.of(a, b))); + return DefaultExcludeAllOf.of(ImmutableSet.of(one, two)); } @Override From 90fa83636a0cc330a1a670e6bb4bf4a6f117791e Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 29 Apr 2019 22:43:30 +0200 Subject: [PATCH 08/55] Optimize 0 and 1 exclude metadata use cases --- .../resolveengine/excludes/ModuleExclusions.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java index 40b2e58f6bf1..9177bc30b687 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java @@ -52,6 +52,13 @@ public ModuleExclusions() { } public ExcludeSpec excludeAny(ImmutableList excludes) { + if (excludes.isEmpty()) { + // avoids creation of empty hashset + return nothing; + } + if (excludes.size() == 1) { + return forExclude(excludes.get(0)); + } return factory.anyOf(excludes.stream() .map(this::forExclude) .collect(Collectors.toSet())); From 03fc570923e9067418d85b232a512cb9dba3218f Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 30 Apr 2019 09:28:16 +0200 Subject: [PATCH 09/55] Avoid the creation of intermediate datastructures when merging This commit makes the resolution filter computation faster by avoiding an iterative construction, and avoiding the creation of intermediate sets, when it's not necessary. This speeds up merging a bit because we don't have to flatten multiple times the same thing. --- .../excludes/ModuleExclusions.java | 16 +++-- .../graph/builder/EdgeState.java | 2 +- .../graph/builder/NodeState.java | 64 ++++++++++++++----- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java index 9177bc30b687..57495d842667 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/ModuleExclusions.java @@ -15,19 +15,20 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.CachingExcludeFactory; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.NormalizingExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.OptimizingExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeFactory; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.ExcludeFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.internal.component.model.ExcludeMetadata; import org.gradle.internal.component.model.IvyArtifactName; +import java.util.Collection; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; public class ModuleExclusions { @@ -51,13 +52,13 @@ public ModuleExclusions() { nothing = factory.nothing(); } - public ExcludeSpec excludeAny(ImmutableList excludes) { + public ExcludeSpec excludeAny(Collection excludes) { if (excludes.isEmpty()) { // avoids creation of empty hashset return nothing; } if (excludes.size() == 1) { - return forExclude(excludes.get(0)); + return forExclude(excludes.iterator().next()); } return factory.anyOf(excludes.stream() .map(this::forExclude) @@ -109,4 +110,11 @@ public ExcludeSpec excludeAll(ExcludeSpec one, ExcludeSpec two) { return factory.allOf(one, two); } + public ExcludeSpec excludeAll(Set specs) { + return factory.allOf(specs); + } + + public ExcludeSpec excludeAny(Set specs) { + return factory.anyOf(specs); + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java index 49b04b209984..8fb583cd0d75 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java @@ -250,7 +250,7 @@ public ExcludeSpec getExclusions() { return transitiveExclusions; } ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); - ExcludeSpec edgeExclusions = moduleExclusions.excludeAny(ImmutableList.copyOf(excludes)); + ExcludeSpec edgeExclusions = moduleExclusions.excludeAny(excludes); return moduleExclusions.excludeAny(edgeExclusions, transitiveExclusions); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index a4f32568876c..b07dbc83d6d4 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -28,6 +28,7 @@ import org.gradle.api.internal.artifacts.DependencySubstitutionInternal; import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier; import org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution.DependencySubstitutionApplicator; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode; import org.gradle.api.internal.attributes.ImmutableAttributesFactory; @@ -72,6 +73,7 @@ public boolean isSatisfiedBy(EdgeState edge) { private final ConfigurationMetadata metaData; private final ResolveState resolveState; + private final ModuleExclusions moduleExclusions; private final boolean isTransitive; private final boolean selectedByVariantAwareResolution; @@ -93,6 +95,7 @@ public NodeState(Long resultId, ResolvedConfigurationIdentifier id, ComponentSta this.metaData = md; this.isTransitive = metaData.isTransitive(); this.selectedByVariantAwareResolution = md instanceof SelectedByVariantMatchingConfigurationMetadata; + this.moduleExclusions = resolveState == null ? null : resolveState.getModuleExclusions(); // can be null in tests, ResolveState cannot be mocked component.addConfiguration(this); } @@ -222,7 +225,7 @@ public void visitOutgoingDependencies(Collection discoveredEdges) { } // Determine the net exclusion for this node, by inspecting all transitive incoming edges - ExcludeSpec resolutionFilter = getModuleResolutionFilter(incomingEdges); + ExcludeSpec resolutionFilter = computeModuleResolutionFilter(incomingEdges); // Virtual platforms require their constraints to be recomputed each time as each module addition can cause a shift in versions if (!isVirtualPlatformNeedsRefresh()) { @@ -444,7 +447,7 @@ private boolean isExcluded(ExcludeSpec selector, DependencyState dependencyState LOGGER.debug("{} is filtered.", dependency); return true; } - if (selector == resolveState.getModuleExclusions().nothing()) { + if (selector == moduleExclusions.nothing()) { return false; } ModuleIdentifier targetModuleId = dependencyState.getModuleIdentifier(); @@ -479,34 +482,61 @@ public boolean shouldIncludedInGraphResult() { return isSelected() && !component.getModule().isVirtualPlatform(); } - private ExcludeSpec getModuleResolutionFilter(List incomingEdges) { - org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); - ExcludeSpec nodeExclusions = moduleExclusions.excludeAny(metaData.getExcludes()); + private ExcludeSpec computeModuleResolutionFilter(List incomingEdges) { + ExcludeSpec nodeExclusions = computeNodeExclusions(); if (incomingEdges.isEmpty()) { return nodeExclusions; } - ExcludeSpec edgeExclusions = null; + return computeExclusionFilter(incomingEdges, nodeExclusions); + } + private ExcludeSpec computeNodeExclusions() { + return moduleExclusions.excludeAny(metaData.getExcludes()); + } + + private ExcludeSpec computeExclusionFilter(List incomingEdges, ExcludeSpec nodeExclusions) { + ExcludeSpec edgeExclusions = null; + Set excludedByBoth = null; + Set excludedByEither = null; + ExcludeSpec nothing = moduleExclusions.nothing(); for (EdgeState dependencyEdge : incomingEdges) { if (dependencyEdge.isTransitive()) { // Transitive dependency - edgeExclusions = excludedByBoth(edgeExclusions, dependencyEdge.getExclusions()); + ExcludeSpec exclusions = dependencyEdge.getExclusions(); + if (edgeExclusions == null) { + edgeExclusions = exclusions; + } else if (exclusions != null && edgeExclusions != exclusions) { + if (excludedByBoth == null) { + excludedByBoth = Sets.newHashSet(); + } + excludedByBoth.add(exclusions); + } } else if (dependencyEdge.getDependencyMetadata().isConstraint()) { // Constraint: only consider explicit exclusions declared for this constraint ExcludeSpec constraintExclusions = dependencyEdge.getEdgeExclusions(); - nodeExclusions = excludedByEither(nodeExclusions, constraintExclusions); + if (constraintExclusions!= null && constraintExclusions != nothing && constraintExclusions != nodeExclusions) { + if (excludedByEither == null) { + excludedByEither = Sets.newHashSet(); + } + excludedByEither.add(constraintExclusions); + } } } - return excludedByEither(edgeExclusions, nodeExclusions); - } - - private ExcludeSpec excludedByBoth(ExcludeSpec one, ExcludeSpec two) { - return resolveState.getModuleExclusions().excludeAll(one, two); - } - - private ExcludeSpec excludedByEither(ExcludeSpec one, ExcludeSpec two) { - return resolveState.getModuleExclusions().excludeAny(one, two); + if (excludedByBoth != null) { + if (edgeExclusions != null) { + // do not believe what IJ says, it can be non null, see above + excludedByBoth.add(edgeExclusions); + } + edgeExclusions = moduleExclusions.excludeAll(excludedByBoth); + } + if (excludedByEither != null) { + if (nodeExclusions != null) { + excludedByEither.add(nodeExclusions); + nodeExclusions = moduleExclusions.excludeAny(excludedByEither); + } + } + return moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); } private void removeOutgoingEdges() { From 1b3b8aa106c2941e7b6fc0b46dffaadc83ad12ad Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 30 Apr 2019 11:12:05 +0200 Subject: [PATCH 10/55] Avoid computing the same exclusion filter multiple times A single edge can participate in different resolutions, so it's a good thing to cache the exclusion filter for this edge. Similarly, we may ask for the same node exclusions multiple times, and it's a waste of time to rebuild the filter, even if we have good optimizations below. --- .../resolveengine/graph/builder/EdgeState.java | 17 +++++++++++++---- .../resolveengine/graph/builder/NodeState.java | 10 +++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java index 8fb583cd0d75..360fe2c137ae 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java @@ -62,6 +62,7 @@ class EdgeState implements DependencyGraphEdge { private ModuleVersionResolveException targetNodeSelectionFailure; private ImmutableAttributes cachedAttributes; + private ExcludeSpec cachedExclusions; EdgeState(NodeState from, DependencyState dependencyState, ExcludeSpec transitiveExclusions, ResolveState resolveState) { this.from = from; @@ -245,13 +246,21 @@ private boolean isVirtualDependency() { @Override public ExcludeSpec getExclusions() { + if (cachedExclusions == null) { + computeExclusions(); + } + return cachedExclusions; + } + + private void computeExclusions() { List excludes = dependencyMetadata.getExcludes(); if (excludes.isEmpty()) { - return transitiveExclusions; + cachedExclusions = transitiveExclusions; + } else { + ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); + ExcludeSpec edgeExclusions = moduleExclusions.excludeAny(excludes); + cachedExclusions = moduleExclusions.excludeAny(edgeExclusions, transitiveExclusions); } - ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); - ExcludeSpec edgeExclusions = moduleExclusions.excludeAny(excludes); - return moduleExclusions.excludeAny(edgeExclusions, transitiveExclusions); } ExcludeSpec getEdgeExclusions() { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index b07dbc83d6d4..dd242d245bd9 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -85,7 +85,8 @@ public boolean isSatisfiedBy(EdgeState edge) { private boolean evicted; private Set upcomingNoLongerPendingConstraints; private boolean virtualPlatformNeedsRefresh; - private HashSet edgesToRecompute; + private Set edgesToRecompute; + private ExcludeSpec cachedNodeExclusions; public NodeState(Long resultId, ResolvedConfigurationIdentifier id, ComponentState component, ResolveState resolveState, ConfigurationMetadata md) { this.resultId = resultId; @@ -230,7 +231,7 @@ public void visitOutgoingDependencies(Collection discoveredEdges) { // Virtual platforms require their constraints to be recomputed each time as each module addition can cause a shift in versions if (!isVirtualPlatformNeedsRefresh()) { // Check if node was previously traversed with the same net exclusion when not a virtual platform - if (previousTraversalExclusions != null && previousTraversalExclusions.excludesSameModulesAs(resolutionFilter)) { + if (previousTraversalExclusions != null && previousTraversalExclusions.equalsIgnoreArtifact(resolutionFilter)) { boolean newConstraints = handleNewConstraints(discoveredEdges); boolean edgesToRecompute = handleEdgesToRecompute(discoveredEdges); if (!newConstraints && !edgesToRecompute) { @@ -492,7 +493,10 @@ private ExcludeSpec computeModuleResolutionFilter(List incomingEdges) } private ExcludeSpec computeNodeExclusions() { - return moduleExclusions.excludeAny(metaData.getExcludes()); + if (cachedNodeExclusions == null) { + cachedNodeExclusions = moduleExclusions.excludeAny(metaData.getExcludes()); + } + return cachedNodeExclusions; } private ExcludeSpec computeExclusionFilter(List incomingEdges, ExcludeSpec nodeExclusions) { From b8c8c3401eb1a838cf3d0356ced893d14a5006f4 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 30 Apr 2019 13:40:19 +0200 Subject: [PATCH 11/55] Remove unused class --- .../main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/buildSrc/subprojects/packaging/src/main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt b/buildSrc/subprojects/packaging/src/main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt index 412753e83031..25136dcaba5f 100644 --- a/buildSrc/subprojects/packaging/src/main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt +++ b/buildSrc/subprojects/packaging/src/main/kotlin/org/gradle/gradlebuild/packaging/MinifyPlugin.kt @@ -38,7 +38,6 @@ open class MinifyPlugin : Plugin { val keepPatterns = mapOf( "fastutil" to setOf( "it.unimi.dsi.fastutil.ints.IntOpenHashSet", - "it.unimi.dsi.fastutil.ints.IntRBTreeSet", "it.unimi.dsi.fastutil.ints.IntSets" ) ) From 9f7d23f9731aa64dc04614396a83f4bb8fc06ddc Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 30 Apr 2019 15:11:06 +0200 Subject: [PATCH 12/55] Add union/intersection simplification --- .../factories/NormalizingExcludeFactory.java | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java index 487b1898fcdc..920fada53dcc 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -31,10 +31,13 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude; import org.gradle.internal.Cast; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -52,12 +55,57 @@ public NormalizingExcludeFactory(ExcludeFactory delegate) { @Override public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { - return doUnion(ImmutableSet.of(one, two)); + return simplify(ExcludeAllOf.class, one, two, (left, right) -> doUnion(ImmutableSet.of(left, right))); } @Override public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { - return doIntersect(ImmutableSet.of(one, two)); + return simplify(ExcludeAnyOf.class, one, two, (left, right) -> doIntersect(ImmutableSet.of(left, right))); + } + + // Simplifies (A ∪ ...) ∩ A = A + // and (A ∩ ...) ∪ A = A + private ExcludeSpec simplify(Class clazz, ExcludeSpec one, ExcludeSpec two, BiFunction orElse) { + if (clazz.isInstance(one)) { + if (componentsOf(one).contains(two)) { + return two; + } + } + if (clazz.isInstance(two)) { + if (componentsOf(two).contains(one)) { + return one; + } + } + return orElse.apply(one, two); + } + + private Set simplifySet(Class clazz, Set specs) { + if (specs.size() < 3) { + return specs; + } + ExcludeSpec[] asArray = specs.toArray(new ExcludeSpec[0]); + boolean doDrop = false; + for (int i = 0; i < asArray.length; i++) { + ExcludeSpec excludeSpec = asArray[i]; + if (clazz.isInstance(excludeSpec)) { + Set components = componentsOf(excludeSpec); + for (int j = 0; j < asArray.length; j++) { + if (i != j && components.contains(asArray[j])) { + doDrop = true; + asArray[i] = null; + break; + } + } + } + } + if (doDrop) { + specs = Arrays.stream(asArray).filter(Objects::nonNull).collect(toSet()); + } + return specs; + } + + private Set componentsOf(ExcludeSpec spec) { + return ((CompositeExclude) spec).getComponents(); } @Override @@ -71,6 +119,7 @@ public ExcludeSpec allOf(Set specs) { } private ExcludeSpec doUnion(Set specs) { + specs = simplifySet(ExcludeAllOf.class, specs); FlattenOperationResult flattened = flatten(ExcludeAnyOf.class, specs, ExcludeEverything.class::isInstance, ExcludeNothing.class::isInstance); if (flattened.fastExit) { return everything(); @@ -87,6 +136,7 @@ private ExcludeSpec doUnion(Set specs) { List moduleSetExcludes = UnionOf.MODULE_SET.fromMap(byType); List other = UnionOf.NOT_JOINABLE.fromMap(byType); if (!moduleIdExcludes.isEmpty()) { + // If there's more than one module id, merge them into a module id set if (moduleIdExcludes.size() > 1 || !moduleIdSetsExcludes.isEmpty()) { ModuleIdSetExclude excludeSpec = delegate.moduleIdSet(moduleIdExcludes.stream().map(ModuleIdExclude::getModuleId).collect(toSet())); if (moduleIdSetsExcludes.isEmpty()) { @@ -98,6 +148,7 @@ private ExcludeSpec doUnion(Set specs) { } } if (!groupExcludes.isEmpty()) { + // If there's more than group, merge them into a group set if (groupExcludes.size() > 1 || !groupSetExcludes.isEmpty()) { GroupSetExclude excludeSpec = delegate.groupSet(groupExcludes.stream().map(GroupExclude::getGroup).collect(toSet())); if (groupSetExcludes.isEmpty()) { @@ -109,6 +160,7 @@ private ExcludeSpec doUnion(Set specs) { } } if (!moduleExcludes.isEmpty()) { + // If there's more than one module, merge them into a module set if (moduleExcludes.size() > 1 || !moduleSetExcludes.isEmpty()) { ModuleSetExclude excludeSpec = delegate.moduleSet(moduleExcludes.stream().map(ModuleExclude::getModule).collect(toSet())); if (moduleSetExcludes.isEmpty()) { @@ -190,6 +242,7 @@ private FlattenOperationResult flatten(Class flattenT } private ExcludeSpec doIntersect(Set specs) { + specs = simplifySet(ExcludeAnyOf.class, specs); FlattenOperationResult flattened = flatten(ExcludeAllOf.class, specs, ExcludeNothing.class::isInstance, ExcludeEverything.class::isInstance); if (flattened.fastExit) { return nothing(); From 61b9c741828d6418a51c95fcb4ad50506787fd43 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 30 Apr 2019 16:26:58 +0200 Subject: [PATCH 13/55] Give up a bit on immutability for the sake of performance Creating an immutable list with Guava has a slight performance overhead. Given we have to mitigate a performance regression, choice is to give up here, knowing we have enough guarantees (the structure is internal). --- .../factories/NormalizingExcludeFactory.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java index 920fada53dcc..f4a552685654 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -40,6 +40,7 @@ import java.util.function.BiFunction; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.util.stream.Collectors.toSet; @@ -226,19 +227,20 @@ private FlattenOperationResult flatten(Class flattenT return FlattenOperationResult.of(specs.stream().filter(e -> !ignoreSpec.test(e)).collect(Collectors.toSet())); } // slowest path - final boolean processAll = !filtered; - ImmutableSet.Builder flattened = ImmutableSet.builderWithExpectedSize(size); - for (ExcludeSpec e : specs) { - if (processAll || !ignoreSpec.test(e)) { + Stream stream = specs.stream(); + if (filtered) { + stream = stream.filter(e -> !ignoreSpec.test(e)); + } + return FlattenOperationResult.of(stream + .flatMap(e -> { if (flattenType.isInstance(e)) { CompositeExclude compositeExclude = (CompositeExclude) e; - flattened.addAll(compositeExclude.getComponents()); - } else { - flattened.add(e); + return compositeExclude.components(); } - } - } - return FlattenOperationResult.of(flattened.build()); + return Stream.of(e); + }) + .collect(toSet()) + ); } private ExcludeSpec doIntersect(Set specs) { From d8c428e63d138ae15785901bfbde97914be52f80 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 30 Apr 2019 17:11:14 +0200 Subject: [PATCH 14/55] Fix composite exclude hashcode The previous hashcode implementation would give the same hashcode for allOf(A,B) and anyOf(A,B), which was a problem when comparing pairs of specs which contain the same elements. --- .../resolveengine/excludes/simple/DefaultCompositeExclude.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java index dbbad30e1580..8ce20348ecce 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java @@ -31,8 +31,8 @@ abstract class DefaultCompositeExclude implements CompositeExclude { DefaultCompositeExclude(ImmutableSet components) { this.components = components; - this.hashCode = components.hashCode(); this.size = components.size(); + this.hashCode = 31 * components.hashCode() + this.size ^ this.getClass().hashCode(); } @Override From b0288f024f497e14d5fc03027f6efd1a7682a079 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Thu, 2 May 2019 09:53:05 +0200 Subject: [PATCH 15/55] Use hash set estimate size to avoid resizes --- .../excludes/factories/NormalizingExcludeFactory.java | 9 +++++++-- .../resolveengine/graph/builder/NodeState.java | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java index f4a552685654..69ffca2eee74 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.CompositeExclude; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAllOf; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAnyOf; @@ -224,13 +225,17 @@ private FlattenOperationResult flatten(Class flattenT return FlattenOperationResult.of(specs); } if (filtered && !flatten) { - return FlattenOperationResult.of(specs.stream().filter(e -> !ignoreSpec.test(e)).collect(Collectors.toSet())); + return FlattenOperationResult.of(specs.stream().filter(e -> !ignoreSpec.test(e)).collect(toSet())); } // slowest path Stream stream = specs.stream(); if (filtered) { stream = stream.filter(e -> !ignoreSpec.test(e)); } + return expensiveFlatten(flattenType, stream, size); + } + + private FlattenOperationResult expensiveFlatten(Class flattenType, Stream stream, int size) { return FlattenOperationResult.of(stream .flatMap(e -> { if (flattenType.isInstance(e)) { @@ -239,7 +244,7 @@ private FlattenOperationResult flatten(Class flattenT } return Stream.of(e); }) - .collect(toSet()) + .collect(Collectors.toCollection(() -> Sets.newHashSetWithExpectedSize(size))) ); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index dd242d245bd9..dbda67d49199 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -504,6 +504,7 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud Set excludedByBoth = null; Set excludedByEither = null; ExcludeSpec nothing = moduleExclusions.nothing(); + int incomingEdgeCount = incomingEdges.size(); for (EdgeState dependencyEdge : incomingEdges) { if (dependencyEdge.isTransitive()) { // Transitive dependency @@ -512,7 +513,7 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud edgeExclusions = exclusions; } else if (exclusions != null && edgeExclusions != exclusions) { if (excludedByBoth == null) { - excludedByBoth = Sets.newHashSet(); + excludedByBoth = Sets.newHashSetWithExpectedSize(incomingEdgeCount); } excludedByBoth.add(exclusions); } @@ -521,7 +522,7 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud ExcludeSpec constraintExclusions = dependencyEdge.getEdgeExclusions(); if (constraintExclusions!= null && constraintExclusions != nothing && constraintExclusions != nodeExclusions) { if (excludedByEither == null) { - excludedByEither = Sets.newHashSet(); + excludedByEither = Sets.newHashSetWithExpectedSize(incomingEdgeCount); } excludedByEither.add(constraintExclusions); } From d7989ee95f5dbc710e5bb6eff31cc7eec1194a93 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Thu, 2 May 2019 10:09:37 +0200 Subject: [PATCH 16/55] Perform simplification only if specs contain expected types --- .../excludes/factories/CachingExcludeFactory.java | 10 +--------- .../excludes/factories/NormalizingExcludeFactory.java | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java index 1b58a7a94451..370804a41ec8 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java @@ -115,15 +115,7 @@ private static class ExcludesKey { private ExcludesKey(Set specs) { this.specs = specs; this.size = specs.size(); - this.hashCode = computeHashCode(); - } - - private int computeHashCode() { - int hash = 0; - for (ExcludeSpec spec : specs) { - hash += spec.hashCode(); - } - return hash; + this.hashCode = specs.hashCode(); } @Override diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java index 69ffca2eee74..6eb5159686eb 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -82,7 +82,7 @@ private ExcludeSpec simplify(Class clazz, ExcludeSpe } private Set simplifySet(Class clazz, Set specs) { - if (specs.size() < 3) { + if (specs.stream().noneMatch(clazz::isInstance)) { return specs; } ExcludeSpec[] asArray = specs.toArray(new ExcludeSpec[0]); From b72718e5d8336ed601b0d92917a4e288e8091dee Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Thu, 2 May 2019 11:39:09 +0200 Subject: [PATCH 17/55] Minor refactoring for readability --- .../factories/NormalizingExcludeFactory.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java index 6eb5159686eb..7282f71d1a59 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -213,8 +213,7 @@ private FlattenOperationResult flatten(Class flattenT } if (ignoreSpec.test(spec)) { filtered = true; - } - if (flattenType.isInstance(spec)) { + } else if (flattenType.isInstance(spec)) { flatten = true; size += ((CompositeExclude) spec).size(); } else { @@ -225,14 +224,22 @@ private FlattenOperationResult flatten(Class flattenT return FlattenOperationResult.of(specs); } if (filtered && !flatten) { - return FlattenOperationResult.of(specs.stream().filter(e -> !ignoreSpec.test(e)).collect(toSet())); + return filterOnly(specs, ignoreSpec); } // slowest path + return expensiveFlatten(flattenType, maybeFilter(specs, ignoreSpec, filtered), size); + } + + private FlattenOperationResult filterOnly(Set specs, Predicate ignoreSpec) { + return FlattenOperationResult.of(specs.stream().filter(e -> !ignoreSpec.test(e)).collect(toSet())); + } + + private Stream maybeFilter(Set specs, Predicate ignoreSpec, boolean filtered) { Stream stream = specs.stream(); if (filtered) { stream = stream.filter(e -> !ignoreSpec.test(e)); } - return expensiveFlatten(flattenType, stream, size); + return stream; } private FlattenOperationResult expensiveFlatten(Class flattenType, Stream stream, int size) { From 3fc2b2e43259009ebb813dbbebe5e20adecd3aea Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Thu, 2 May 2019 12:01:22 +0200 Subject: [PATCH 18/55] Avoid recomputing the same exclude filter multiple times --- .../resolveengine/graph/builder/NodeState.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index dbda67d49199..634c071786fb 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -86,7 +86,12 @@ public boolean isSatisfiedBy(EdgeState edge) { private Set upcomingNoLongerPendingConstraints; private boolean virtualPlatformNeedsRefresh; private Set edgesToRecompute; + + // exclusions optimizations private ExcludeSpec cachedNodeExclusions; + private List previousIncoming; + private ExcludeSpec cachedExcludes; + public NodeState(Long resultId, ResolvedConfigurationIdentifier id, ComponentState component, ResolveState resolveState, ConfigurationMetadata md) { this.resultId = resultId; @@ -500,6 +505,12 @@ private ExcludeSpec computeNodeExclusions() { } private ExcludeSpec computeExclusionFilter(List incomingEdges, ExcludeSpec nodeExclusions) { + if (incomingEdges.equals(previousIncoming)) { + // if we reach this point it means the node selection was restarted, but + // effectively it has the same incoming edges as before, so we can return + // the result we computed last time + return cachedExcludes; + } ExcludeSpec edgeExclusions = null; Set excludedByBoth = null; Set excludedByEither = null; @@ -541,7 +552,10 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud nodeExclusions = moduleExclusions.excludeAny(excludedByEither); } } - return moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); + ExcludeSpec result = moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); + previousIncoming = ImmutableList.copyOf(incomingEdges); + cachedExcludes = result; + return result; } private void removeOutgoingEdges() { From a329efa5ea4bb16e4eef6f5141cf3dfd02924f31 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Thu, 2 May 2019 13:28:16 +0200 Subject: [PATCH 19/55] Cache `EdgeState` and `DependencyState` Because we may add and remove the same dependencies different times, it's worth caching both dependency states and edge states in case the same edge comes back into the selection process. This helps with exclude merging as it's likely to hit the exclude cache. --- .../resolveengine/graph/builder/NodeState.java | 13 +++++++++++-- .../resolveengine/graph/builder/ResolveState.java | 14 ++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 634c071786fb..2c625e09b0b1 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.gradle.api.Action; import org.gradle.api.artifacts.ModuleIdentifier; @@ -51,6 +52,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -87,6 +89,10 @@ public boolean isSatisfiedBy(EdgeState edge) { private boolean virtualPlatformNeedsRefresh; private Set edgesToRecompute; + // caches + private Map dependencyStateCache = Maps.newHashMap(); + private Map edgesCache = Maps.newHashMap(); + // exclusions optimizations private ExcludeSpec cachedNodeExclusions; private List previousIncoming; @@ -309,6 +315,10 @@ private void handleNonTransitiveNode(Collection discoveredEdges) { } } + private DependencyState createDependencyState(DependencyMetadata md) { + return new DependencyState(md, resolveState.getComponentSelectorConverter()); + } + /** * Iterate over the dependencies originating in this node, adding them either as a 'pending' dependency * or adding them to the `discoveredEdges` collection (and `this.outgoingEdges`) @@ -317,7 +327,7 @@ private void visitDependencies(ExcludeSpec resolutionFilter, Collection discoveredEdges) { for (DependencyMetadata dependency : metaData.getDependencies()) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java index ca53abe6a606..01c547f9ebc0 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java @@ -125,12 +125,7 @@ RootNode getRoot() { } public ModuleResolveState getModule(ModuleIdentifier id) { - ModuleResolveState module = modules.get(id); - if (module == null) { - module = new ModuleResolveState(idGenerator, id, metaDataResolver, attributesFactory, versionComparator, versionParser, selectorStateResolver, resolveOptimizations); - modules.put(id, module); - } - return module; + return modules.computeIfAbsent(id, mid -> new ModuleResolveState(idGenerator, id, metaDataResolver, attributesFactory, versionComparator, versionParser, selectorStateResolver, resolveOptimizations)); } @Override @@ -152,12 +147,7 @@ public int getNodeCount() { public NodeState getNode(ComponentState module, ConfigurationMetadata configurationMetadata) { ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier(module.getId(), configurationMetadata.getName()); - NodeState configuration = nodes.get(id); - if (configuration == null) { - configuration = new NodeState(idGenerator.generateId(), id, module, this, configurationMetadata); - nodes.put(id, configuration); - } - return configuration; + return nodes.computeIfAbsent(id, rci -> new NodeState(idGenerator.generateId(), id, module, this, configurationMetadata)); } public Collection getSelectors() { From 24d0c1b623f39b89225bb75964ccd3bb8e32d5ef Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Thu, 2 May 2019 17:46:52 +0200 Subject: [PATCH 20/55] De-duplicate dependencies before iterating This fixes a bug whenever the same dependency is seen multiple times on the same component: we're incorrectly tracking the number of hard edges to this dependency. By deduping, we avoid the creation of multiple edges to the same thing, which makes things faster and fixes this bug. Deduplication is done on `NodeState` because it's too expensive to do it ahead of time, on configuration metadata, in particular because of the lazy computation of dependencies and application of rules. --- .../ForcingPlatformAlignmentTest.groovy | 7 +-- .../graph/builder/NodeState.java | 52 +++++++++++++++++-- .../graph/builder/ResolveState.java | 16 +++--- .../graph/builder/SelectorState.java | 1 - 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy index c6ec2565d9ec..642509bb47e2 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy @@ -992,7 +992,8 @@ include 'other' @RequiredFeatures([ @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven"), - @RequiredFeature(feature = GradleMetadataResolveRunner.EXPERIMENTAL_RESOLVE_BEHAVIOR, value = "true") + @RequiredFeature(feature = GradleMetadataResolveRunner.EXPERIMENTAL_RESOLVE_BEHAVIOR, value = "true"), + @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true"), ]) @Issue("nebula-plugins/gradle-nebula-integration#51") @Unroll("force to lower patch version should bring the rest of aligned group up (notation=#forceNotation)") @@ -1040,9 +1041,9 @@ include 'other' where: forceNotation << [ - "configurations.all { resolutionStrategy { force 'org:databind:2.6.7.1' } }", +// "configurations.all { resolutionStrategy { force 'org:databind:2.6.7.1' } }", "dependencies { conf enforcedPlatform('org:platform:2.6.7.1') }", - "dependencies { conf('org:databind:2.6.7.1') { force = true } }", +// "dependencies { conf('org:databind:2.6.7.1') { force = true } }", ] } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 2c625e09b0b1..306b80254559 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -54,6 +54,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * Represents a node in the dependency graph. @@ -78,6 +79,7 @@ public boolean isSatisfiedBy(EdgeState edge) { private final ModuleExclusions moduleExclusions; private final boolean isTransitive; private final boolean selectedByVariantAwareResolution; + private final boolean dependenciesMayChange; ExcludeSpec previousTraversalExclusions; @@ -92,6 +94,7 @@ public boolean isSatisfiedBy(EdgeState edge) { // caches private Map dependencyStateCache = Maps.newHashMap(); private Map edgesCache = Maps.newHashMap(); + private List deduplicatedDependencies; // exclusions optimizations private ExcludeSpec cachedNodeExclusions; @@ -108,6 +111,7 @@ public NodeState(Long resultId, ResolvedConfigurationIdentifier id, ComponentSta this.isTransitive = metaData.isTransitive(); this.selectedByVariantAwareResolution = md instanceof SelectedByVariantMatchingConfigurationMetadata; this.moduleExclusions = resolveState == null ? null : resolveState.getModuleExclusions(); // can be null in tests, ResolveState cannot be mocked + this.dependenciesMayChange = component.getModule() != null && component.getModule().isVirtualPlatform(); // can be null in tests, ComponentState cannot be mocked component.addConfiguration(this); } @@ -326,7 +330,7 @@ private DependencyState createDependencyState(DependencyMetadata md) { private void visitDependencies(ExcludeSpec resolutionFilter, Collection discoveredEdges) { PendingDependenciesVisitor pendingDepsVisitor = resolveState.newPendingDependenciesVisitor(); try { - for (DependencyMetadata dependency : metaData.getDependencies()) { + for (DependencyMetadata dependency : deduplicatedDependencies()) { DependencyState dependencyState = dependencyStateCache.computeIfAbsent(dependency, this::createDependencyState); if (isExcluded(resolutionFilter, dependencyState)) { continue; @@ -346,19 +350,29 @@ private void visitDependencies(ExcludeSpec resolutionFilter, Collection deduplicatedDependencies() { + if (deduplicatedDependencies == null || dependenciesMayChange) { + deduplicatedDependencies = doDeduplicate(metaData.getDependencies()); + } + return deduplicatedDependencies; + } + + private static List doDeduplicate(List dependencies) { + return dependencies.stream().distinct().collect(Collectors.toList()); + } + private void createAndLinkEdgeState(DependencyState dependencyState, Collection discoveredEdges, ExcludeSpec resolutionFilter, boolean deferSelection) { EdgeState dependencyEdge = new EdgeState(this, dependencyState, resolutionFilter, resolveState); outgoingEdges.add(dependencyEdge); discoveredEdges.add(dependencyEdge); dependencyEdge.getSelector().use(deferSelection); } - /** * Iterate over the dependencies originating in this node, adding only the constraints listed * in upcomingNoLongerPendingConstraints */ private void visitAdditionalConstraints(Collection discoveredEdges) { - for (DependencyMetadata dependency : metaData.getDependencies()) { + for (DependencyMetadata dependency : deduplicatedDependencies()) { if (dependency.isConstraint()) { DependencyState dependencyState = new DependencyState(dependency, resolveState.getComponentSelectorConverter()); if (upcomingNoLongerPendingConstraints.contains(dependencyState.getModuleIdentifier())) { @@ -514,7 +528,7 @@ private ExcludeSpec computeNodeExclusions() { } private ExcludeSpec computeExclusionFilter(List incomingEdges, ExcludeSpec nodeExclusions) { - if (incomingEdges.equals(previousIncoming)) { + if (sameIncomingEdgesAsPreviousPass(incomingEdges)) { // if we reach this point it means the node selection was restarted, but // effectively it has the same incoming edges as before, so we can return // the result we computed last time @@ -540,7 +554,7 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud } else if (dependencyEdge.getDependencyMetadata().isConstraint()) { // Constraint: only consider explicit exclusions declared for this constraint ExcludeSpec constraintExclusions = dependencyEdge.getEdgeExclusions(); - if (constraintExclusions!= null && constraintExclusions != nothing && constraintExclusions != nodeExclusions) { + if (constraintExclusions != null && constraintExclusions != nothing && constraintExclusions != nodeExclusions) { if (excludedByEither == null) { excludedByEither = Sets.newHashSetWithExpectedSize(incomingEdgeCount); } @@ -562,11 +576,19 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud } } ExcludeSpec result = moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); + // We use a set here because for excludes, order of edges is irrelevant + // so we hit the cache more by using a set previousIncoming = ImmutableList.copyOf(incomingEdges); cachedExcludes = result; return result; } + private boolean sameIncomingEdgesAsPreviousPass(List incomingEdges) { + return previousIncoming != null + && previousIncoming.hashCode() == incomingEdges.hashCode() + && previousIncoming.equals(incomingEdges); + } + private void removeOutgoingEdges() { if (!outgoingEdges.isEmpty()) { for (EdgeState outgoingDependency : outgoingEdges) { @@ -714,4 +736,24 @@ boolean isSelectedByVariantAwareResolution() { // the order is strange logically but here for performance optimization return selectedByVariantAwareResolution && isSelected(); } + + private static class HashCodeCachingHashSet extends HashSet { + private final int hashCode; + + public HashCodeCachingHashSet(List incomingEdges, int hashCode) { + super(incomingEdges); + this.hashCode = hashCode; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object o) { + // to make checkstyle happy + return super.equals(o); + } + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java index 01c547f9ebc0..013ea61b47a2 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java @@ -97,8 +97,8 @@ public ResolveState(IdGenerator idGenerator, ComponentResolveResult rootRe this.versionComparator = versionComparator; this.versionParser = versionParser; this.modules = new LinkedHashMap(graphSize); - this.nodes = new LinkedHashMap(3*graphSize/2); - this.selectors = new LinkedHashMap(5*graphSize/2); + this.nodes = new LinkedHashMap(3 * graphSize / 2); + this.selectors = new LinkedHashMap(5 * graphSize / 2); this.queue = new ArrayDeque(graphSize); this.resolveOptimizations = new ResolveOptimizations(); ComponentState rootVersion = getRevision(rootResult.getId(), rootResult.getModuleVersionId(), rootResult.getMetadata()); @@ -155,15 +155,11 @@ public Collection getSelectors() { } public SelectorState getSelector(DependencyState dependencyState) { - ComponentSelector requested = dependencyState.getRequested(); - SelectorState selectorState = selectors.get(requested); - if (selectorState == null) { + SelectorState selectorState = selectors.computeIfAbsent(dependencyState.getRequested(), req -> { ModuleIdentifier moduleIdentifier = dependencyState.getModuleIdentifier(); - selectorState = new SelectorState(idGenerator.generateId(), dependencyState, idResolver, versionSelectorScheme, this, moduleIdentifier); - selectors.put(requested, selectorState); - } else { - selectorState.update(dependencyState); - } + return new SelectorState(idGenerator.generateId(), dependencyState, idResolver, versionSelectorScheme, this, moduleIdentifier); + }); + selectorState.update(dependencyState); return selectorState; } 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 4752fbf4c46a..1411b80da948 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 @@ -91,7 +91,6 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState this.versionSelectorScheme = versionSelectorScheme; this.targetModule = resolveState.getModule(targetModuleId); this.attributesFactory = resolveState.getAttributesFactory(); - update(dependencyState); this.dependencyState = dependencyState; this.firstSeenDependency = dependencyState.getDependency(); From 7cfb2ec16bac06d6d4cf10dd64c48cca241afbdf Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Fri, 3 May 2019 08:38:47 +0200 Subject: [PATCH 21/55] Cache edge exclusions for constraints --- .../graph/builder/EdgeState.java | 24 ++++++++++++------- .../graph/builder/NodeState.java | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java index 360fe2c137ae..83f8c50c2ef9 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java @@ -16,7 +16,6 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.gradle.api.Transformer; import org.gradle.api.artifacts.Dependency; @@ -62,6 +61,7 @@ class EdgeState implements DependencyGraphEdge { private ModuleVersionResolveException targetNodeSelectionFailure; private ImmutableAttributes cachedAttributes; + private ExcludeSpec cachedEdgeExclusions; private ExcludeSpec cachedExclusions; EdgeState(NodeState from, DependencyState dependencyState, ExcludeSpec transitiveExclusions, ResolveState resolveState) { @@ -257,18 +257,26 @@ private void computeExclusions() { if (excludes.isEmpty()) { cachedExclusions = transitiveExclusions; } else { - ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); - ExcludeSpec edgeExclusions = moduleExclusions.excludeAny(excludes); - cachedExclusions = moduleExclusions.excludeAny(edgeExclusions, transitiveExclusions); + computeExclusionsWhenExcludesPresent(excludes); } } + private void computeExclusionsWhenExcludesPresent(List excludes) { + ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); + ExcludeSpec edgeExclusions = moduleExclusions.excludeAny(excludes); + cachedExclusions = moduleExclusions.excludeAny(edgeExclusions, transitiveExclusions); + } + ExcludeSpec getEdgeExclusions() { - List excludes = dependencyMetadata.getExcludes(); - if (excludes.isEmpty()) { - return null; + if (cachedEdgeExclusions == null) { + List excludes = dependencyMetadata.getExcludes(); + ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); + if (excludes.isEmpty()) { + return moduleExclusions.nothing(); + } + cachedEdgeExclusions = moduleExclusions.excludeAny(excludes); } - return resolveState.getModuleExclusions().excludeAny(ImmutableList.copyOf(excludes)); + return cachedEdgeExclusions; } @Override diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 306b80254559..a63fc8cf63b0 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -507,7 +507,7 @@ public void evict() { restartIncomingEdges(); } - public boolean shouldIncludedInGraphResult() { + boolean shouldIncludedInGraphResult() { return isSelected() && !component.getModule().isVirtualPlatform(); } @@ -554,7 +554,7 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud } else if (dependencyEdge.getDependencyMetadata().isConstraint()) { // Constraint: only consider explicit exclusions declared for this constraint ExcludeSpec constraintExclusions = dependencyEdge.getEdgeExclusions(); - if (constraintExclusions != null && constraintExclusions != nothing && constraintExclusions != nodeExclusions) { + if (constraintExclusions != nothing && constraintExclusions != nodeExclusions) { if (excludedByEither == null) { excludedByEither = Sets.newHashSetWithExpectedSize(incomingEdgeCount); } From b948e0fef15db6f195de2a7acb9cd2e9f98fabe5 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Fri, 3 May 2019 10:44:37 +0200 Subject: [PATCH 22/55] Avoid recomputing the dependency states Given that a node may participate several times in resolution, in particular in case of conflict, and that its incoming edges may change, this commit caches the result of different operations: - the deduplicated dependencies on one end - the associated dependency states for each dependency - the filtered dependency states, in case the resolution filter hasn't changed --- .../graph/builder/NodeState.java | 122 ++++++++++++------ 1 file changed, 85 insertions(+), 37 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index a63fc8cf63b0..4e04cc0c1ab4 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -17,6 +17,7 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -50,11 +51,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; /** * Represents a node in the dependency graph. @@ -94,12 +93,22 @@ public boolean isSatisfiedBy(EdgeState edge) { // caches private Map dependencyStateCache = Maps.newHashMap(); private Map edgesCache = Maps.newHashMap(); - private List deduplicatedDependencies; + + // Caches the list of dependencies, deduplicated + private List deduplicatedDependencies; + + // Caches the list of dependency states for dependencies + private List cachedDependencyStates; + + // Caches the list of dependency states which are NOT excluded + private List cachedFilteredDependencyStates; // exclusions optimizations private ExcludeSpec cachedNodeExclusions; - private List previousIncoming; - private ExcludeSpec cachedExcludes; + private Set previousIncoming; + private int previousIncomingHash; + private int incomingHash; + private ExcludeSpec cachedModuleResolutionFilter; public NodeState(Long resultId, ResolvedConfigurationIdentifier id, ComponentState component, ResolveState resolveState, ConfigurationMetadata md) { @@ -330,11 +339,7 @@ private DependencyState createDependencyState(DependencyMetadata md) { private void visitDependencies(ExcludeSpec resolutionFilter, Collection discoveredEdges) { PendingDependenciesVisitor pendingDepsVisitor = resolveState.newPendingDependenciesVisitor(); try { - for (DependencyMetadata dependency : deduplicatedDependencies()) { - DependencyState dependencyState = dependencyStateCache.computeIfAbsent(dependency, this::createDependencyState); - if (isExcluded(resolutionFilter, dependencyState)) { - continue; - } + for (DependencyState dependencyState : dependencies(resolutionFilter)) { dependencyState = maybeSubstitute(dependencyState, resolveState.getDependencySubstitutionApplicator()); PendingDependenciesVisitor.PendingState pendingState = pendingDepsVisitor.maybeAddAsPendingDependency(this, dependencyState); if (!pendingState.isPending()) { @@ -353,12 +358,63 @@ private void visitDependencies(ExcludeSpec resolutionFilter, Collection deduplicatedDependencies() { if (deduplicatedDependencies == null || dependenciesMayChange) { deduplicatedDependencies = doDeduplicate(metaData.getDependencies()); + cachedDependencyStates = null; + cachedFilteredDependencyStates = null; } return deduplicatedDependencies; } - private static List doDeduplicate(List dependencies) { - return dependencies.stream().distinct().collect(Collectors.toList()); + private List dependencies(ExcludeSpec spec) { + List dependencies = deduplicatedDependencies(); + if (cachedDependencyStates == null) { + cachedDependencyStates = cacheDependencyStates(dependencies); + } + if (cachedFilteredDependencyStates == null) { + cachedFilteredDependencyStates = cacheFilteredDependencyStates(spec, cachedDependencyStates); + } + return cachedFilteredDependencyStates; + } + + private List cacheFilteredDependencyStates(ExcludeSpec spec, List from) { + List tmp = Lists.newArrayListWithCapacity(from.size()); + for (DependencyState dependencyState : from) { + if (!isExcluded(spec, dependencyState)) { + tmp.add(dependencyState); + } + } + return tmp; + } + + private List cacheDependencyStates(List dependencies) { + List tmp = Lists.newArrayListWithCapacity(dependencies.size()); + for (DependencyMetadata dependency : dependencies) { + tmp.add(cachedDependencyStateFor(dependency)); + } + return tmp; + } + + private DependencyState cachedDependencyStateFor(DependencyMetadata md) { + return dependencyStateCache.computeIfAbsent(md, this::createDependencyState); + } + + private static List doDeduplicate(List dependencies) { + int size = dependencies.size(); + if (size < 2) { + return dependencies; + } + return deduplicateWithExpectedSize(dependencies, size); + } + + private static List deduplicateWithExpectedSize(List dependencies, int size) { + // Not using Java 8 stream API as this one will be slightly faster + List result = Lists.newArrayListWithCapacity(size); + Set seen = Sets.newHashSetWithExpectedSize(size); + for (DependencyMetadata dependency : dependencies) { + if (seen.add(dependency)) { + result.add(dependency); + } + } + return result; } private void createAndLinkEdgeState(DependencyState dependencyState, Collection discoveredEdges, ExcludeSpec resolutionFilter, boolean deferSelection) { @@ -490,11 +546,13 @@ private boolean isExcluded(ExcludeSpec selector, DependencyState dependencyState public void addIncomingEdge(EdgeState dependencyEdge) { incomingEdges.add(dependencyEdge); + incomingHash += dependencyEdge.hashCode(); resolveState.onMoreSelected(this); } public void removeIncomingEdge(EdgeState dependencyEdge) { incomingEdges.remove(dependencyEdge); + incomingHash -= dependencyEdge.hashCode(); resolveState.onFewerSelected(this); } @@ -532,7 +590,7 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud // if we reach this point it means the node selection was restarted, but // effectively it has the same incoming edges as before, so we can return // the result we computed last time - return cachedExcludes; + return cachedModuleResolutionFilter; } ExcludeSpec edgeExclusions = null; Set excludedByBoth = null; @@ -578,15 +636,19 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud ExcludeSpec result = moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); // We use a set here because for excludes, order of edges is irrelevant // so we hit the cache more by using a set - previousIncoming = ImmutableList.copyOf(incomingEdges); - cachedExcludes = result; + previousIncoming = ImmutableSet.copyOf(incomingEdges); + previousIncomingHash = incomingHash; + cachedModuleResolutionFilter = result; + cachedFilteredDependencyStates = null; return result; } private boolean sameIncomingEdgesAsPreviousPass(List incomingEdges) { return previousIncoming != null - && previousIncoming.hashCode() == incomingEdges.hashCode() - && previousIncoming.equals(incomingEdges); + && previousIncomingHash == incomingHash + && previousIncoming.size() == incomingEdges.size() + // this comparison only stands because we know we work on deduplicated edges + && previousIncoming.containsAll(incomingEdges); } private void removeOutgoingEdges() { @@ -633,7 +695,12 @@ private void restartIncomingEdges() { dependency.restart(); } } + clearIncomingEdges(); + } + + private void clearIncomingEdges() { incomingEdges.clear(); + incomingHash = 0; } public void deselect() { @@ -675,7 +742,7 @@ public void clearConstraintEdges(PendingDependencies pendingDependencies, NodeSt } pendingDependencies.addNode(from); } - incomingEdges.clear(); + clearIncomingEdges(); } void forEachCapability(Action action) { @@ -737,23 +804,4 @@ boolean isSelectedByVariantAwareResolution() { return selectedByVariantAwareResolution && isSelected(); } - private static class HashCodeCachingHashSet extends HashSet { - private final int hashCode; - - public HashCodeCachingHashSet(List incomingEdges, int hashCode) { - super(incomingEdges); - this.hashCode = hashCode; - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public boolean equals(Object o) { - // to make checkstyle happy - return super.equals(o); - } - } } From 8d1a28cb306cd0b7ca84d3bba7b030a7bad122f9 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Fri, 3 May 2019 12:10:07 +0200 Subject: [PATCH 23/55] Restore test combinations --- .../resolve/alignment/ForcingPlatformAlignmentTest.groovy | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy index 642509bb47e2..c6ec2565d9ec 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy @@ -992,8 +992,7 @@ include 'other' @RequiredFeatures([ @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven"), - @RequiredFeature(feature = GradleMetadataResolveRunner.EXPERIMENTAL_RESOLVE_BEHAVIOR, value = "true"), - @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true"), + @RequiredFeature(feature = GradleMetadataResolveRunner.EXPERIMENTAL_RESOLVE_BEHAVIOR, value = "true") ]) @Issue("nebula-plugins/gradle-nebula-integration#51") @Unroll("force to lower patch version should bring the rest of aligned group up (notation=#forceNotation)") @@ -1041,9 +1040,9 @@ include 'other' where: forceNotation << [ -// "configurations.all { resolutionStrategy { force 'org:databind:2.6.7.1' } }", + "configurations.all { resolutionStrategy { force 'org:databind:2.6.7.1' } }", "dependencies { conf enforcedPlatform('org:platform:2.6.7.1') }", -// "dependencies { conf('org:databind:2.6.7.1') { force = true } }", + "dependencies { conf('org:databind:2.6.7.1') { force = true } }", ] } From 9863bb0647763820ab9de922dd1c38e2b9b1a131 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Fri, 3 May 2019 12:11:57 +0200 Subject: [PATCH 24/55] Avoid adding the same dependency reasons multiple times This can happen because re-selecting the same node may trigger adding selections for the same dependency state. --- .../resolveengine/graph/builder/DependencyState.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java index 1c788360d2a7..c872f2935f88 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java @@ -38,6 +38,7 @@ class DependencyState { private ModuleIdentifier moduleIdentifier; public ModuleVersionResolveException failure; + private boolean reasonsAlreadyAdded; DependencyState(DependencyMetadata dependency, ComponentSelectorConverter componentSelectorConverter) { this(dependency, dependency.getSelector(), null, componentSelectorConverter); @@ -96,7 +97,11 @@ public boolean isFromLock() { return dependency instanceof LocalOriginDependencyMetadata && ((LocalOriginDependencyMetadata) dependency).isFromLock(); } - public void addSelectionReasons(Set reasons) { + void addSelectionReasons(Set reasons) { + if (reasonsAlreadyAdded) { + return; + } + reasonsAlreadyAdded = true; String reason = dependency.getReason(); ComponentSelectionDescriptorInternal dependencyDescriptor = dependency.isConstraint() ? CONSTRAINT : REQUESTED; if (reason != null) { From f0c56e25dab7ecb181838b1cb3666c91c83c7883 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Fri, 3 May 2019 13:49:24 +0200 Subject: [PATCH 25/55] Make selector state creation cheaper This commit makes the selector state creation cheaper by caching the resolved version constraints. It's quite common to see the same version numbers in a graph, or in general the same version constraints. Because resolving the version constraint takes time, it's good to cache. --- .../graph/builder/ResolveState.java | 16 ++++++++++++++- .../graph/builder/SelectorState.java | 20 ++++--------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java index 013ea61b47a2..bea682f1a507 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java @@ -16,12 +16,17 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder; +import com.google.common.collect.Maps; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.ModuleVersionIdentifier; +import org.gradle.api.artifacts.VersionConstraint; import org.gradle.api.artifacts.component.ComponentIdentifier; import org.gradle.api.artifacts.component.ComponentSelector; +import org.gradle.api.artifacts.component.ModuleComponentSelector; import org.gradle.api.internal.artifacts.ComponentSelectorConverter; import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier; +import org.gradle.api.internal.artifacts.ResolvedVersionConstraint; +import org.gradle.api.internal.artifacts.dependencies.DefaultResolvedVersionConstraint; import org.gradle.api.internal.artifacts.dsl.ModuleReplacementsData; import org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution.DependencySubstitutionApplicator; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.Version; @@ -75,6 +80,7 @@ class ResolveState implements ComponentStateFactory { private final VersionParser versionParser; private final SelectorStateResolver selectorStateResolver; private final ResolveOptimizations resolveOptimizations; + private final Map resolvedVersionConstraints = Maps.newHashMap(); public ResolveState(IdGenerator idGenerator, ComponentResolveResult rootResult, String rootConfigurationName, DependencyToComponentIdResolver idResolver, ComponentMetaDataResolver metaDataResolver, Spec edgeFilter, AttributesSchemaInternal attributesSchema, @@ -157,7 +163,7 @@ public Collection getSelectors() { public SelectorState getSelector(DependencyState dependencyState) { SelectorState selectorState = selectors.computeIfAbsent(dependencyState.getRequested(), req -> { ModuleIdentifier moduleIdentifier = dependencyState.getModuleIdentifier(); - return new SelectorState(idGenerator.generateId(), dependencyState, idResolver, versionSelectorScheme, this, moduleIdentifier); + return new SelectorState(idGenerator.generateId(), dependencyState, idResolver, this, moduleIdentifier); }); selectorState.update(dependencyState); return selectorState; @@ -228,4 +234,12 @@ public DependencySubstitutionApplicator getDependencySubstitutionApplicator() { PendingDependenciesVisitor newPendingDependenciesVisitor() { return new DefaultPendingDependenciesVisitor(this); } + + ResolvedVersionConstraint resolveVersionConstraint(ComponentSelector selector) { + if (selector instanceof ModuleComponentSelector) { + VersionConstraint vc = ((ModuleComponentSelector) selector).getVersionConstraint(); + return resolvedVersionConstraints.computeIfAbsent(vc, key -> new DefaultResolvedVersionConstraint(key, versionSelectorScheme)); + } + return null; + } } 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 1411b80da948..c553decb986d 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 @@ -22,12 +22,9 @@ import org.gradle.api.Describable; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.component.ComponentSelector; -import org.gradle.api.artifacts.component.ModuleComponentSelector; import org.gradle.api.artifacts.result.ComponentSelectionCause; import org.gradle.api.internal.artifacts.ResolvedVersionConstraint; -import org.gradle.api.internal.artifacts.dependencies.DefaultResolvedVersionConstraint; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; -import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphSelector; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ResolvableSelectorState; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorInternal; @@ -35,6 +32,7 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons; import org.gradle.api.internal.attributes.ImmutableAttributesFactory; import org.gradle.internal.component.model.DependencyMetadata; +import org.gradle.internal.logging.text.TreeFormatter; import org.gradle.internal.resolve.ModuleVersionResolveException; import org.gradle.internal.resolve.RejectedByAttributesVersion; import org.gradle.internal.resolve.RejectedByRuleVersion; @@ -44,7 +42,6 @@ import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult; import org.gradle.internal.resolve.result.ComponentIdResolveResult; import org.gradle.internal.resolve.result.DefaultBuildableComponentIdResolveResult; -import org.gradle.internal.logging.text.TreeFormatter; import java.util.Collection; import java.util.Set; @@ -63,8 +60,7 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState private final DependencyState dependencyState; private final DependencyMetadata firstSeenDependency; private final DependencyToComponentIdResolver resolver; - private final DefaultResolvedVersionConstraint versionConstraint; - private final VersionSelectorScheme versionSelectorScheme; + private final ResolvedVersionConstraint versionConstraint; private final ImmutableAttributesFactory attributesFactory; private final Set dependencyReasons = Sets.newLinkedHashSet(); @@ -85,16 +81,15 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState // evicted, but it can still be reintegrated later in a different path. private int outgoingEdgeCount; - SelectorState(Long id, DependencyState dependencyState, DependencyToComponentIdResolver resolver, VersionSelectorScheme versionSelectorScheme, ResolveState resolveState, ModuleIdentifier targetModuleId) { + SelectorState(Long id, DependencyState dependencyState, DependencyToComponentIdResolver resolver, ResolveState resolveState, ModuleIdentifier targetModuleId) { this.id = id; this.resolver = resolver; - this.versionSelectorScheme = versionSelectorScheme; this.targetModule = resolveState.getModule(targetModuleId); this.attributesFactory = resolveState.getAttributesFactory(); update(dependencyState); this.dependencyState = dependencyState; this.firstSeenDependency = dependencyState.getDependency(); - this.versionConstraint = resolveVersionConstraint(firstSeenDependency.getSelector()); + this.versionConstraint = resolveState.resolveVersionConstraint(firstSeenDependency.getSelector()); } public void use(boolean deferSelection) { @@ -117,13 +112,6 @@ private void removeAndMarkSelectorForReuse() { resolved = false; } - private DefaultResolvedVersionConstraint resolveVersionConstraint(ComponentSelector selector) { - if (selector instanceof ModuleComponentSelector) { - return new DefaultResolvedVersionConstraint(((ModuleComponentSelector) selector).getVersionConstraint(), versionSelectorScheme); - } - return null; - } - @Override public Long getResultId() { return id; From 93b024c149021646124becff0ffccd7f5268eff2 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Fri, 3 May 2019 14:11:24 +0200 Subject: [PATCH 26/55] Small optimizations to make exclude computation faster --- .../external/model/ivy/IvyDependencyDescriptor.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyDependencyDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyDependencyDescriptor.java index efc25831df6b..4b8b55fcb9f5 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyDependencyDescriptor.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyDependencyDescriptor.java @@ -222,14 +222,17 @@ public List getAllExcludes() { @Override public List getConfigurationExcludes(Collection configurations) { - List rules = Lists.newArrayList(); + if (excludes.isEmpty()) { + return ImmutableList.of(); + } + ImmutableList.Builder rules = ImmutableList.builderWithExpectedSize(excludes.size()); for (Exclude exclude : excludes) { Set ruleConfigurations = exclude.getConfigurations(); if (include(ruleConfigurations, configurations)) { rules.add(exclude); } } - return rules; + return rules.build(); } public List getDependencyArtifacts() { From 206d9a342a34790d64633baa13541c8b0b8e9c99 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Fri, 3 May 2019 14:38:55 +0200 Subject: [PATCH 27/55] Fix merge conflict --- .../ivyservice/resolveengine/graph/builder/NodeState.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 4e04cc0c1ab4..86e3bd0ccf7f 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -281,7 +281,7 @@ public void visitOutgoingDependencies(Collection discoveredEdges) { private void prepareToRecomputeEdge(EdgeState edgeToRecompute) { if (edgesToRecompute == null) { - edgesToRecompute = new HashSet<>(); + edgesToRecompute = Sets.newLinkedHashSet(); } edgesToRecompute.add(edgeToRecompute); resolveState.onMoreSelected(this); @@ -418,7 +418,8 @@ private static List deduplicateWithExpectedSize(Li } private void createAndLinkEdgeState(DependencyState dependencyState, Collection discoveredEdges, ExcludeSpec resolutionFilter, boolean deferSelection) { - EdgeState dependencyEdge = new EdgeState(this, dependencyState, resolutionFilter, resolveState); + EdgeState dependencyEdge = edgesCache.computeIfAbsent(dependencyState, ds -> new EdgeState(this, ds, resolutionFilter, resolveState)); + dependencyEdge.getSelector().update(dependencyState); outgoingEdges.add(dependencyEdge); discoveredEdges.add(dependencyEdge); dependencyEdge.getSelector().use(deferSelection); From c41d6686e29e0a873ef550d869e6d766a2127d84 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sat, 4 May 2019 13:20:16 +0200 Subject: [PATCH 28/55] Reduce overhead of caching excludes This commit replaces the exact key match, which involved creation of a set of incoming edges, with a long hash computation (sum of hashes of incoming edges + length). This reduces the overhead of caching, especially in case there are _no_ excludes. --- .../artifacts/ResolvedVersionConstraint.java | 8 +++++ .../DefaultResolvedVersionConstraint.java | 6 ++++ .../ivyresolve/DefaultMetadataProvider.java | 22 +++++-------- .../strategy/ExactVersionSelector.java | 6 ++-- .../graph/builder/NodeState.java | 33 +++++++++++-------- .../SelectorStateResolverResults.java | 20 +++++++---- .../DefaultModuleComponentIdentifier.java | 3 ++ .../model/DefaultModuleComponentSelector.java | 4 +++ 8 files changed, 66 insertions(+), 36 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolvedVersionConstraint.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolvedVersionConstraint.java index 9482453897e9..aae119b691d3 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolvedVersionConstraint.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolvedVersionConstraint.java @@ -17,10 +17,18 @@ import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; +import javax.annotation.Nullable; + public interface ResolvedVersionConstraint { + @Nullable VersionSelector getPreferredSelector(); + + @Nullable VersionSelector getRequiredSelector(); + + @Nullable VersionSelector getRejectedSelector(); + boolean isRejectAll(); boolean isDynamic(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultResolvedVersionConstraint.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultResolvedVersionConstraint.java index 7b622be2aeef..e5d4f46558a4 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultResolvedVersionConstraint.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultResolvedVersionConstraint.java @@ -29,6 +29,7 @@ public class DefaultResolvedVersionConstraint implements ResolvedVersionConstrai private final VersionSelector requiredVersionSelector; private final VersionSelector rejectedVersionsSelector; private final boolean rejectAll; + private final boolean isDynamic; public DefaultResolvedVersionConstraint(VersionConstraint parent, VersionSelectorScheme scheme) { this(parent.getRequiredVersion(), parent.getPreferredVersion(), parent.getStrictVersion(), parent.getRejectedVersions(), scheme); @@ -53,6 +54,7 @@ public DefaultResolvedVersionConstraint(String requiredVersion, String preferred this.rejectedVersionsSelector = toRejectSelector(scheme, rejectedVersions); rejectAll = isRejectAll(version, rejectedVersions); } + this.isDynamic = doComputeIsDynamic(); } private VersionSelector getRejectionForStrict(String version, VersionSelectorScheme versionSelectorScheme) { @@ -89,6 +91,10 @@ public boolean isRejectAll() { @Override public boolean isDynamic() { + return isDynamic; + } + + private boolean doComputeIsDynamic() { if (requiredVersionSelector != null) { return requiredVersionSelector.isDynamic(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProvider.java index 014cb245bbb7..24e43a5a1f8e 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProvider.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProvider.java @@ -16,7 +16,6 @@ package org.gradle.api.internal.artifacts.ivyservice.ivyresolve; -import com.google.common.base.Optional; import com.google.common.collect.Lists; import org.gradle.api.Action; import org.gradle.api.InvalidUserDataException; @@ -40,12 +39,12 @@ import org.gradle.api.internal.attributes.ImmutableAttributesFactory; import org.gradle.api.internal.project.ProjectInternal; import org.gradle.internal.action.InstantiatingAction; -import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata; import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata; +import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata; import org.gradle.internal.component.model.ComponentResolveMetadata; +import org.gradle.internal.logging.text.TreeFormatter; import org.gradle.internal.reflect.Instantiator; import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult; -import org.gradle.internal.logging.text.TreeFormatter; import java.util.List; @@ -58,7 +57,8 @@ public ComponentMetadata transform(BuildableComponentMetadataSupplierDetails det }; private final ModuleComponentResolveState resolveState; private BuildableModuleComponentMetaDataResolveResult cachedResult; - private Optional cachedComponentMetadata; + private ComponentMetadata cachedComponentMetadata; + private boolean computedMetadata; DefaultMetadataProvider(ModuleComponentResolveState resolveState) { this.resolveState = resolveState; @@ -66,17 +66,13 @@ public ComponentMetadata transform(BuildableComponentMetadataSupplierDetails det @Override public ComponentMetadata getComponentMetadata() { - if (cachedComponentMetadata != null) { - return cachedComponentMetadata.orNull(); + if (computedMetadata) { + return cachedComponentMetadata; } - ComponentMetadata metadata = computeMetadata(); - if (metadata != null) { - cachedComponentMetadata = Optional.of(metadata); - } else { - cachedComponentMetadata = Optional.absent(); - } - return metadata; + cachedComponentMetadata = computeMetadata(); + computedMetadata = true; + return cachedComponentMetadata; } private ComponentMetadata computeMetadata() { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelector.java index 28d7ccb196b6..bdaa00e36cc3 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelector.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelector.java @@ -19,8 +19,11 @@ * Version matcher for "static" version selectors (1.0, 1.2.3, etc.). */ public class ExactVersionSelector extends AbstractStringVersionSelector { + private final String version; + public ExactVersionSelector(String selector) { super(selector); + this.version = selector; } public boolean isDynamic() { @@ -36,8 +39,7 @@ public boolean matchesUniqueVersion() { } public boolean accept(String candidate) { - String selector = getSelector(); - return selector.isEmpty() || selector.equals(candidate); + return version.isEmpty() || version.equals(candidate); } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 86e3bd0ccf7f..a93edec74fb2 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -17,7 +17,6 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -105,9 +104,9 @@ public boolean isSatisfiedBy(EdgeState edge) { // exclusions optimizations private ExcludeSpec cachedNodeExclusions; - private Set previousIncoming; - private int previousIncomingHash; - private int incomingHash; + private int previousIncomingEdgeCount; + private long previousIncomingHash; + private long incomingHash; private ExcludeSpec cachedModuleResolutionFilter; @@ -424,6 +423,7 @@ private void createAndLinkEdgeState(DependencyState dependencyState, Collection< discoveredEdges.add(dependencyEdge); dependencyEdge.getSelector().use(deferSelection); } + /** * Iterate over the dependencies originating in this node, adding only the constraints listed * in upcomingNoLongerPendingConstraints @@ -587,7 +587,8 @@ private ExcludeSpec computeNodeExclusions() { } private ExcludeSpec computeExclusionFilter(List incomingEdges, ExcludeSpec nodeExclusions) { - if (sameIncomingEdgesAsPreviousPass(incomingEdges)) { + int incomingEdgeCount = incomingEdges.size(); + if (sameIncomingEdgesAsPreviousPass(incomingEdgeCount)) { // if we reach this point it means the node selection was restarted, but // effectively it has the same incoming edges as before, so we can return // the result we computed last time @@ -597,14 +598,17 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud Set excludedByBoth = null; Set excludedByEither = null; ExcludeSpec nothing = moduleExclusions.nothing(); - int incomingEdgeCount = incomingEdges.size(); for (EdgeState dependencyEdge : incomingEdges) { if (dependencyEdge.isTransitive()) { // Transitive dependency ExcludeSpec exclusions = dependencyEdge.getExclusions(); - if (edgeExclusions == null) { + if (edgeExclusions == null || exclusions == nothing) { + // if exclusions == nothing, then the intersection will be "nothing" edgeExclusions = exclusions; - } else if (exclusions != null && edgeExclusions != exclusions) { + if (exclusions == nothing) { + excludedByBoth = null; + } + } else if (exclusions != null && edgeExclusions != exclusions && edgeExclusions != nothing) { if (excludedByBoth == null) { excludedByBoth = Sets.newHashSetWithExpectedSize(incomingEdgeCount); } @@ -637,19 +641,20 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud ExcludeSpec result = moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); // We use a set here because for excludes, order of edges is irrelevant // so we hit the cache more by using a set - previousIncoming = ImmutableSet.copyOf(incomingEdges); + previousIncomingEdgeCount = incomingEdgeCount; previousIncomingHash = incomingHash; cachedModuleResolutionFilter = result; cachedFilteredDependencyStates = null; return result; } - private boolean sameIncomingEdgesAsPreviousPass(List incomingEdges) { - return previousIncoming != null + private boolean sameIncomingEdgesAsPreviousPass(int incomingEdgeCount) { + // This is a heuristic, more than truth: it is possible that the 2 long hashs + // are identical AND that the sizes of collections are identical, but it's + // extremely unlikely (never happened on test cases even on large dependency graph) + return cachedModuleResolutionFilter != null && previousIncomingHash == incomingHash - && previousIncoming.size() == incomingEdges.size() - // this comparison only stands because we know we work on deduplicated edges - && previousIncoming.containsAll(incomingEdges); + && previousIncomingEdgeCount == incomingEdgeCount; } private void removeOutgoingEdges() { 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 12038d5cd915..548a3ce07c42 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 @@ -132,8 +132,10 @@ boolean replaceExistingResolutionsWithBetterResult(ComponentIdResolveResult reso // Check already-resolved dependencies and use this version if it's compatible boolean replaces = false; for (Registration registration : results) { - if (emptyVersion(registration.result) || sameVersion(registration.result, resolveResult) || - (included(registration.selector, resolveResult, isFromLock) && lowerVersion(registration.result, resolveResult))) { + ComponentIdResolveResult result = registration.result; + ResolvableSelectorState selector = registration.selector; + if (emptyVersion(result) || sameVersion(result, resolveResult) || + (included(selector, resolveResult, isFromLock) && lowerVersion(result, resolveResult))) { registration.result = resolveResult; replaces = true; } @@ -145,21 +147,21 @@ void register(ResolvableSelectorState selector, ComponentIdResolveResult resolve results.add(new Registration(selector, resolveResult)); } - private boolean emptyVersion(ComponentIdResolveResult existing) { + private static boolean emptyVersion(ComponentIdResolveResult existing) { if (existing.getFailure() == null) { return existing.getModuleVersionId().getVersion().isEmpty(); } return false; } - private boolean sameVersion(ComponentIdResolveResult existing, ComponentIdResolveResult resolveResult) { + private static boolean sameVersion(ComponentIdResolveResult existing, ComponentIdResolveResult resolveResult) { if (existing.getFailure() == null && resolveResult.getFailure() == null) { return existing.getId().equals(resolveResult.getId()); } return false; } - private boolean lowerVersion(ComponentIdResolveResult existing, ComponentIdResolveResult resolveResult) { + private static boolean lowerVersion(ComponentIdResolveResult existing, ComponentIdResolveResult resolveResult) { if (existing.getFailure() == null && resolveResult.getFailure() == null) { Version existingVersion = VERSION_PARSER.transform(existing.getModuleVersionId().getVersion()); Version candidateVersion = VERSION_PARSER.transform(resolveResult.getModuleVersionId().getVersion()); @@ -170,8 +172,8 @@ private boolean lowerVersion(ComponentIdResolveResult existing, ComponentIdResol return false; } - private boolean included(ResolvableSelectorState dep, ComponentIdResolveResult candidate, boolean candidateIsFromLock) { - if (candidate.getFailure() != null) { + private static boolean included(ResolvableSelectorState dep, ComponentIdResolveResult candidate, boolean candidateIsFromLock) { + if (hasFailure(candidate)) { return false; } ResolvedVersionConstraint versionConstraint = dep.getVersionConstraint(); @@ -192,6 +194,10 @@ private boolean included(ResolvableSelectorState dep, ComponentIdResolveResult c return false; } + private static boolean hasFailure(ComponentIdResolveResult candidate) { + return candidate.getFailure() != null; + } + public boolean isEmpty() { return results.isEmpty(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java index 07759970cf81..04ebd2d5e838 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java @@ -83,6 +83,9 @@ public boolean equals(Object o) { DefaultModuleComponentIdentifier that = (DefaultModuleComponentIdentifier) o; + if (hashCode != that.hashCode) { + return false; + } if (!moduleIdentifier.equals(that.moduleIdentifier)) { return false; } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java index 44adc91bae8f..e9298ba1c582 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java @@ -125,6 +125,10 @@ public boolean equals(Object o) { DefaultModuleComponentSelector that = (DefaultModuleComponentSelector) o; + if (hashCode != that.hashCode) { + return false; + } + if (!moduleIdentifier.equals(that.moduleIdentifier)) { return false; } From 4ed59f19962c2401a4c2aa4f7cf5f6a2fed60f45 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sat, 4 May 2019 17:49:55 +0200 Subject: [PATCH 29/55] Avoid de-duplicating dependencies De-duplicating dependencies can be expensive, especially when in most of cases there are no duplicates. Instead this commit reworks deduplication by checking when an incoming edge is added if it already contains the same incoming edge. --- .../graph/builder/NodeState.java | 53 ++++++------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index a93edec74fb2..b323b7d2dc33 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -93,9 +93,6 @@ public boolean isSatisfiedBy(EdgeState edge) { private Map dependencyStateCache = Maps.newHashMap(); private Map edgesCache = Maps.newHashMap(); - // Caches the list of dependencies, deduplicated - private List deduplicatedDependencies; - // Caches the list of dependency states for dependencies private List cachedDependencyStates; @@ -354,17 +351,16 @@ private void visitDependencies(ExcludeSpec resolutionFilter, Collection deduplicatedDependencies() { - if (deduplicatedDependencies == null || dependenciesMayChange) { - deduplicatedDependencies = doDeduplicate(metaData.getDependencies()); + private List dependencies() { + if (dependenciesMayChange) { cachedDependencyStates = null; cachedFilteredDependencyStates = null; } - return deduplicatedDependencies; + return metaData.getDependencies(); } private List dependencies(ExcludeSpec spec) { - List dependencies = deduplicatedDependencies(); + List dependencies = dependencies(); if (cachedDependencyStates == null) { cachedDependencyStates = cacheDependencyStates(dependencies); } @@ -396,26 +392,6 @@ private DependencyState cachedDependencyStateFor(DependencyMetadata md) { return dependencyStateCache.computeIfAbsent(md, this::createDependencyState); } - private static List doDeduplicate(List dependencies) { - int size = dependencies.size(); - if (size < 2) { - return dependencies; - } - return deduplicateWithExpectedSize(dependencies, size); - } - - private static List deduplicateWithExpectedSize(List dependencies, int size) { - // Not using Java 8 stream API as this one will be slightly faster - List result = Lists.newArrayListWithCapacity(size); - Set seen = Sets.newHashSetWithExpectedSize(size); - for (DependencyMetadata dependency : dependencies) { - if (seen.add(dependency)) { - result.add(dependency); - } - } - return result; - } - private void createAndLinkEdgeState(DependencyState dependencyState, Collection discoveredEdges, ExcludeSpec resolutionFilter, boolean deferSelection) { EdgeState dependencyEdge = edgesCache.computeIfAbsent(dependencyState, ds -> new EdgeState(this, ds, resolutionFilter, resolveState)); dependencyEdge.getSelector().update(dependencyState); @@ -429,7 +405,7 @@ private void createAndLinkEdgeState(DependencyState dependencyState, Collection< * in upcomingNoLongerPendingConstraints */ private void visitAdditionalConstraints(Collection discoveredEdges) { - for (DependencyMetadata dependency : deduplicatedDependencies()) { + for (DependencyMetadata dependency : dependencies()) { if (dependency.isConstraint()) { DependencyState dependencyState = new DependencyState(dependency, resolveState.getComponentSelectorConverter()); if (upcomingNoLongerPendingConstraints.contains(dependencyState.getModuleIdentifier())) { @@ -545,16 +521,19 @@ private boolean isExcluded(ExcludeSpec selector, DependencyState dependencyState return false; } - public void addIncomingEdge(EdgeState dependencyEdge) { - incomingEdges.add(dependencyEdge); - incomingHash += dependencyEdge.hashCode(); - resolveState.onMoreSelected(this); + void addIncomingEdge(EdgeState dependencyEdge) { + if (!incomingEdges.contains(dependencyEdge)) { + incomingEdges.add(dependencyEdge); + incomingHash += dependencyEdge.hashCode(); + resolveState.onMoreSelected(this); + } } - public void removeIncomingEdge(EdgeState dependencyEdge) { - incomingEdges.remove(dependencyEdge); - incomingHash -= dependencyEdge.hashCode(); - resolveState.onFewerSelected(this); + void removeIncomingEdge(EdgeState dependencyEdge) { + if (incomingEdges.remove(dependencyEdge)) { + incomingHash -= dependencyEdge.hashCode(); + resolveState.onFewerSelected(this); + } } public boolean isSelected() { From d5263cf6897c791ee679a6c90b1fe6eb8bf717c9 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sat, 4 May 2019 20:02:57 +0200 Subject: [PATCH 30/55] Optimize selection in case a variant provides the implicit capability This avoids iterating over capabilities when there's a single capability and that it is the shadowed, implicit capability. --- .../CachingModuleComponentRepository.java | 12 ++++----- .../external/model/ImmutableCapabilities.java | 16 ++++++++++++ .../model/ShadowedCapabilityOnly.java | 25 +++++++++++++++++++ .../model/AttributeConfigurationSelector.java | 19 +++++++++++--- 4 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ShadowedCapabilityOnly.java diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java index 281d15b7f1a3..2616f2126e6b 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java @@ -15,7 +15,6 @@ */ package org.gradle.api.internal.artifacts.ivyservice.ivyresolve; -import org.gradle.api.Transformer; import org.gradle.api.artifacts.ArtifactIdentifier; import org.gradle.api.artifacts.ComponentMetadataSupplierDetails; import org.gradle.api.artifacts.ModuleIdentifier; @@ -55,7 +54,6 @@ import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult; import org.gradle.internal.resolve.result.DefaultBuildableArtifactSetResolveResult; import org.gradle.util.BuildCommencedTimeProvider; -import org.gradle.util.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,6 +61,7 @@ import java.math.BigInteger; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * A ModuleComponentRepository that loads and saves resolution results in the dependency resolution cache. @@ -159,11 +158,10 @@ private void listModuleVersionsFromCache(ModuleDependencyMetadata dependency, Bu ModuleVersionsCache.CachedModuleVersionList cachedModuleVersionList = moduleVersionsCache.getCachedModuleResolution(delegate, moduleId); if (cachedModuleVersionList != null) { Set versionList = cachedModuleVersionList.getModuleVersions(); - Set versions = CollectionUtils.collect(versionList, new Transformer() { - public ModuleVersionIdentifier transform(String original) { - return DefaultModuleVersionIdentifier.newId(moduleId, original); - } - }); + Set versions = versionList + .stream() + .map(original -> DefaultModuleVersionIdentifier.newId(moduleId, original)) + .collect(Collectors.toSet()); if (cachePolicy.mustRefreshVersionList(moduleId, versions, cachedModuleVersionList.getAgeMillis())) { LOGGER.debug("Version listing in dynamic revision cache is expired: will perform fresh resolve of '{}' in '{}'", requested, delegate.getName()); } else { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapabilities.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapabilities.java index 8bef9ed4b7e2..817ef7a38db0 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapabilities.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapabilities.java @@ -27,6 +27,15 @@ public class ImmutableCapabilities implements CapabilitiesMetadata { private final ImmutableList capabilities; public static ImmutableCapabilities of(List capabilities) { + if (capabilities.isEmpty()) { + return EMPTY; + } + if (capabilities.size() == 1) { + Capability single = capabilities.get(0); + if (single instanceof ShadowedCapability) { + return new ShadowedSingleImmutableCapabilities(single); + } + } return new ImmutableCapabilities(ImmutableList.copyOf(capabilities)); } @@ -38,4 +47,11 @@ public ImmutableCapabilities(ImmutableList capabilities) { public List getCapabilities() { return capabilities; } + + private static class ShadowedSingleImmutableCapabilities extends ImmutableCapabilities implements ShadowedCapabilityOnly { + + public ShadowedSingleImmutableCapabilities(Capability single) { + super(ImmutableList.of(single)); + } + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ShadowedCapabilityOnly.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ShadowedCapabilityOnly.java new file mode 100644 index 000000000000..95dfbf026581 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ShadowedCapabilityOnly.java @@ -0,0 +1,25 @@ +/* + * 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.internal.component.external.model; + +import org.gradle.api.capabilities.CapabilitiesMetadata; + +/** + * A marker interface only that helps performance of selection by avoiding to + * check if a variant contains the implicit capability. + */ +public interface ShadowedCapabilityOnly extends CapabilitiesMetadata { +} diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeConfigurationSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeConfigurationSelector.java index 8534707a6513..a8257f96feb6 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeConfigurationSelector.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeConfigurationSelector.java @@ -22,6 +22,7 @@ import org.gradle.api.artifacts.ArtifactIdentifier; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ModuleVersionIdentifier; +import org.gradle.api.capabilities.CapabilitiesMetadata; import org.gradle.api.capabilities.Capability; import org.gradle.api.internal.attributes.AttributesSchemaInternal; import org.gradle.api.internal.attributes.ImmutableAttributes; @@ -30,6 +31,7 @@ import org.gradle.internal.component.NoMatchingConfigurationSelectionException; import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata; import org.gradle.internal.component.external.model.ShadowedCapability; +import org.gradle.internal.component.external.model.ShadowedCapabilityOnly; import java.util.Collection; import java.util.Collections; @@ -129,7 +131,8 @@ private static ImmutableList filterVariantsByRequestedCap ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(consumableConfigurations.size()); boolean explicitlyRequested = !explicitRequestedCapabilities.isEmpty(); for (ConfigurationMetadata configuration : consumableConfigurations) { - List capabilities = configuration.getCapabilities().getCapabilities(); + CapabilitiesMetadata capabilitiesMetadata = configuration.getCapabilities(); + List capabilities = capabilitiesMetadata.getCapabilities(); MatchResult result; if (explicitlyRequested) { // some capabilities are explicitly required (in other words, we're not _necessarily_ looking for the default capability @@ -137,7 +140,7 @@ private static ImmutableList filterVariantsByRequestedCap result = providesAllCapabilities(targetComponent, explicitRequestedCapabilities, capabilities); } else { // we need to make sure the variants we consider provide the implicit capability - result = containsImplicitCapability(capabilities, group, name); + result = containsImplicitCapability(capabilitiesMetadata, capabilities, group, name); } if (result.matches) { if (lenient || result == MatchResult.EXACT_MATCH) { @@ -148,6 +151,10 @@ private static ImmutableList filterVariantsByRequestedCap return builder.build(); } + private static boolean isShadowedCapabilityOnly(CapabilitiesMetadata capabilitiesMetadata) { + return capabilitiesMetadata instanceof ShadowedCapabilityOnly; + } + /** * Determines if a producer variant provides all the requested capabilities. When doing so it does * NOT consider capability versions, as they will be used later in the engine during conflict resolution. @@ -181,8 +188,8 @@ private static MatchResult providesAllCapabilities(ComponentResolveMetadata targ return exactMatch ? MatchResult.EXACT_MATCH : MatchResult.MATCHES_ALL; } - private static MatchResult containsImplicitCapability(Collection capabilities, String group, String name) { - if (capabilities.isEmpty()) { + private static MatchResult containsImplicitCapability(CapabilitiesMetadata capabilitiesMetadata, Collection capabilities, String group, String name) { + if (fastContainsImplicitCapability(capabilitiesMetadata, capabilities)) { // An empty capability list means that it's an implicit capability only return MatchResult.EXACT_MATCH; } @@ -196,6 +203,10 @@ private static MatchResult containsImplicitCapability(Collection capabilities) { + return capabilities.isEmpty() || isShadowedCapabilityOnly(capabilitiesMetadata); + } + private static Capability unwrap(Capability capability) { if (capability instanceof ShadowedCapability) { return ((ShadowedCapability) capability).getShadowedCapability(); From 0ba07b5595cf414d3ecc776bd25ea2c3a3d9c4b5 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sat, 4 May 2019 22:30:57 +0200 Subject: [PATCH 31/55] Avoid re-creating selection reason Selection reasons should be immutable. It wasn't because for forced platforms we were relying on it to determine if it was forced or not. This is now changed to use the selectors instead, and we can cache the selection reason. --- .../resolveengine/graph/builder/ComponentState.java | 13 +++++++++++++ .../graph/builder/VirtualPlatformState.java | 4 ++-- .../result/DefaultComponentSelectionDescriptor.java | 3 ++- 3 files changed, 17 insertions(+), 3 deletions(-) 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 819ab63b41ea..d882680e67aa 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 @@ -51,6 +51,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.stream.StreamSupport; /** * Resolution state for a given component @@ -229,11 +230,16 @@ public void addConfiguration(NodeState node) { nodes.add(node); } + private ComponentSelectionReason cachedReason; + @Override public ComponentSelectionReason getSelectionReason() { if (root) { return ComponentSelectionReasons.root(); } + if (cachedReason != null) { + return cachedReason; + } ComponentSelectionReasonInternal reason = ComponentSelectionReasons.empty(); for (final SelectorState selectorState : module.getSelectors()) { if (selectorState.getFailure() == null) { @@ -243,9 +249,16 @@ public ComponentSelectionReason getSelectionReason() { for (ComponentSelectionDescriptorInternal selectionCause : VersionConflictResolutionDetails.mergeCauses(selectionCauses)) { reason.addCause(selectionCause); } + cachedReason = reason; return reason; } + boolean isForced() { + return StreamSupport.stream(module.getSelectors().spliterator(), false) + .filter(s -> s.getFailure() == null) + .anyMatch(SelectorState::isForce); + } + @Override public void addCause(ComponentSelectionDescriptorInternal componentSelectionDescriptor) { selectionCauses.add(componentSelectionDescriptor); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/VirtualPlatformState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/VirtualPlatformState.java index ee6b1752582c..fce342e6bda2 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/VirtualPlatformState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/VirtualPlatformState.java @@ -112,7 +112,7 @@ boolean isForced() { } private boolean isSelectedPlatformForced() { - boolean forced = platformModule.getSelected().getSelectionReason().isForced(); + boolean forced = platformModule.getSelected().isForced(); if (forced) { resolveOptimizations.declareForcedPlatformInUse(); } @@ -121,7 +121,7 @@ private boolean isSelectedPlatformForced() { private boolean isParticipatingModuleForced(ModuleResolveState participatingModule) { ComponentState selected = participatingModule.getSelected(); - boolean forced = selected != null && selected.getSelectionReason().isForced(); + boolean forced = selected != null && selected.isForced(); if (forced) { resolveOptimizations.declareForcedPlatformInUse(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultComponentSelectionDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultComponentSelectionDescriptor.java index 876c5b48019b..aade86d06508 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultComponentSelectionDescriptor.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultComponentSelectionDescriptor.java @@ -76,7 +76,8 @@ public boolean equals(Object o) { return false; } DefaultComponentSelectionDescriptor that = (DefaultComponentSelectionDescriptor) o; - return cause == that.cause + return hashCode == that.hashCode + && cause == that.cause && isEquivalentToForce == that.isEquivalentToForce && Objects.equal(description, that.description); } From de69bef49036f1140d65d358a7ee32caee6c87e8 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 May 2019 13:14:07 +0200 Subject: [PATCH 32/55] Sort module selectors This commit reworks module selectors so that they are sorted in an order which reduces the cost of module selection. We make sure to put local (project) selectors first, then we use selectors from locks (if any). The next selectors are "latest" version selectors because even if they are dynamic, they are likely to "win" selection. Then, exact version selectors are sorted by version descending , and last we add dynamic version selectors. --- ...ncySubstitutionRulesIntegrationTest.groovy | 12 +- ...ependencyConstraintsIntegrationTest.groovy | 6 +- ...enciesBuildOperationIntegrationTest.groovy | 2 +- ...chVersionConstraintsIntegrationTest.groovy | 2 +- ...onConflictResolutionIntegrationTest.groovy | 10 +- .../alignment/AlignmentIntegrationTest.groovy | 12 +- .../ResolutionResultApiIntegrationTest.groovy | 14 +- ...ipleVariantSelectionIntegrationTest.groovy | 4 +- ...ResolutionStrategiesIntegrationTest.groovy | 2 +- ...ependencyConstraintsIntegrationTest.groovy | 10 +- ...ctsDependencyResolveIntegrationTest.groovy | 2 +- ...endencyMetadataRulesIntegrationTest.groovy | 2 +- .../graph/builder/ModuleSelectors.java | 200 +++++++----------- .../selectors/ResolvableSelectorState.java | 4 + .../SelectorStateResolverResults.java | 16 +- .../DependencyGraphBuilderTest.groovy | 28 +-- .../graph/builder/ModuleSelectorsTest.groovy | 85 +++++++- .../selectors/TestComponentSelector.java | 47 ++++ .../selectors/TestModuleSelectorState.java | 2 +- ...ncyInsightReportTaskIntegrationTest.groovy | 12 +- 20 files changed, 282 insertions(+), 190 deletions(-) create mode 100644 subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestComponentSelector.java diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy index a25899434a22..6ae2c53c974e 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy @@ -830,7 +830,7 @@ class DependencySubstitutionRulesIntegrationTest extends AbstractIntegrationSpec resolve.expectGraph { root(":impl", "depsub:impl:") { module("org.utils:api:2.0") - edge("project :api", "org.utils:api:2.0").byConflictResolution("between versions 1.6 and 2.0").selectedByRule() + edge("project :api", "org.utils:api:2.0").byConflictResolution("between versions 2.0 and 1.6").selectedByRule() } } } @@ -952,7 +952,7 @@ class DependencySubstitutionRulesIntegrationTest extends AbstractIntegrationSpec module("org.utils:b:1.3") { module("org.utils:a:1.3") } - edge("org.utils:a:1.2", "org.utils:a:1.3").byConflictResolution("between versions 1.2.1 and 1.3") + edge("org.utils:a:1.2", "org.utils:a:1.3").byConflictResolution("between versions 1.3 and 1.2.1") } } } @@ -1278,7 +1278,7 @@ Required by: resolve.expectGraph { root(":", ":depsub:") { edge("org:a:1.0", "org:a:2.0") { - byConflictResolution("between versions 1.0 and 2.0") + byConflictResolution("between versions 2.0 and 1.0") module("org:c:1.0") } edge("foo:b:1.0", "org:b:1.0") { @@ -1318,7 +1318,7 @@ Required by: resolve.expectGraph { root(":", ":depsub:") { edge("org:a:1.0", "org:a:2.0") { - byConflictResolution("between versions 1.0 and 2.0") + byConflictResolution("between versions 2.0 and 1.0") module("org:c:1.0") } edge("foo:bar:baz", "org:b:1.0") { @@ -1377,7 +1377,7 @@ Required by: resolve.expectGraph { root(":", ":depsub:") { edge("org:a:1.0", "org:c:2.0") { - byConflictResolution("between versions 1.1 and 2.0") + byConflictResolution("between versions 2.0 and 1.1") } module("org:a:2.0") { module("org:b:2.0") { @@ -1449,7 +1449,7 @@ Required by: resolve.expectGraph { root(":", ":depsub:") { edge("org:a:1.0", "org:a:2.0") { - byConflictResolution("between versions 1.0 and 2.0") + byConflictResolution("between versions 2.0 and 1.0") module("org:c:1.0") } edge("foo:bar:baz", "org:b:1.0") { diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/PublishedDependencyConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/PublishedDependencyConstraintsIntegrationTest.groovy index 4d6f2c112ecc..03bd30559e4c 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/PublishedDependencyConstraintsIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/PublishedDependencyConstraintsIntegrationTest.groovy @@ -156,7 +156,7 @@ class PublishedDependencyConstraintsIntegrationTest extends AbstractModuleDepend } } if (available) { - edge("org:foo:1.0","org:foo:1.1").byConflictResolution("between versions 1.0 and 1.1") + edge("org:foo:1.0","org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0") } else { module("org:foo:1.0") } @@ -221,7 +221,7 @@ class PublishedDependencyConstraintsIntegrationTest extends AbstractModuleDepend } module("org:first-level2:1.0") { if (available) { - edge("org:foo:1.0","org:foo:1.1").byConflictResolution("between versions 1.0 and 1.1") + edge("org:foo:1.0","org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0") } else { module("org:foo:1.0") } @@ -395,7 +395,7 @@ class PublishedDependencyConstraintsIntegrationTest extends AbstractModuleDepend module("org:first-level:1.0") { if (available) { constraint("org:bar:1.1", "org:foo:1.1").selectedByRule() - edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.0 and 1.1") + edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0") } else { module("org:foo:1.0") } diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationDependenciesBuildOperationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationDependenciesBuildOperationIntegrationTest.groovy index d6ee3b5cc63e..7fb4b5aa4a94 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationDependenciesBuildOperationIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationDependenciesBuildOperationIntegrationTest.groovy @@ -398,7 +398,7 @@ class ResolveConfigurationDependenciesBuildOperationIntegrationTest extends Abst op.details.configurationName == "compile" op.failure == "org.gradle.api.artifacts.ResolveException: Could not resolve all dependencies for configuration ':compile'." failure.assertHasCause("""Conflict(s) found for the following module(s): - - org:leaf between versions 1.0 and 2.0""") + - org:leaf between versions 2.0 and 1.0""") op.result != null op.result.resolvedDependenciesCount == 2 } diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RichVersionConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RichVersionConstraintsIntegrationTest.groovy index be29234a1632..1fbe6293d0b2 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RichVersionConstraintsIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RichVersionConstraintsIntegrationTest.groovy @@ -601,7 +601,7 @@ class RichVersionConstraintsIntegrationTest extends AbstractModuleDependencyReso Dependency path ':test:unspecified' --> 'org:foo:{strictly [0,1]}'""") } - void "should pass if strict version ranges overlap"() { + void "should pass if strict version ranges overlap"() { given: repository { 'org:foo' { diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy index 0823dc67908a..dc25640a86c7 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy @@ -160,11 +160,11 @@ project(':tool') { root(":tool", "test:tool:") { project(":api", "test:api:") { configuration = "runtimeElements" - edge("org:foo:1.3.3", "org:foo:1.4.4").byConflictResolution("between versions 1.3.3 and 1.4.4") + edge("org:foo:1.3.3", "org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3") } project(":impl", "test:impl:") { configuration = "runtimeElements" - module("org:foo:1.4.4").byConflictResolution("between versions 1.3.3 and 1.4.4") + module("org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3") } } } @@ -210,10 +210,10 @@ task resolve { resolve.expectGraph { root(":", "org:test:1.0") { module("org:bar:1.0") { - edge("org:foo:1.3.3", "org:foo:1.4.4").byConflictResolution("between versions 1.3.3 and 1.4.4") + edge("org:foo:1.3.3", "org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3") } module("org:baz:1.0") { - module("org:foo:1.4.4").byConflictResolution("between versions 1.3.3 and 1.4.4") + module("org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3") } } } @@ -262,7 +262,7 @@ dependencies { } module("org:two:1.0") { module("org:dep-2.0-bringer:1.0") { - edge("org:dep:2.0", "org:dep:2.5").byConflictResolution("between versions 2.0 and 2.5") + edge("org:dep:2.0", "org:dep:2.5").byConflictResolution("between versions 2.5 and 2.0") } module("org:control-1.2-bringer:1.0") { module("org:control:1.2") diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/AlignmentIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/AlignmentIntegrationTest.groovy index 4ce4ae9b748c..88bb5dbf5dbf 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/AlignmentIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/AlignmentIntegrationTest.groovy @@ -107,7 +107,7 @@ class AlignmentIntegrationTest extends AbstractAlignmentSpec { module('org:core:1.1') } module("outside:module:1.0") { - edge('org:core:1.0', 'org:core:1.1').byConflictResolution("between versions 1.0 and 1.1") + edge('org:core:1.0', 'org:core:1.1').byConflictResolution("between versions 1.1 and 1.0") } } } @@ -187,7 +187,7 @@ class AlignmentIntegrationTest extends AbstractAlignmentSpec { root(":", ":test:") { module("org:xml:1.0") { edge('org:core:1.0', 'org:core:1.1') - .byConflictResolution("between versions 1.0 and 1.1") + .byConflictResolution("between versions 1.1 and 1.0") .byConstraint("belongs to platform org:platform:1.1") } module("org:json:1.1") { @@ -529,7 +529,7 @@ class AlignmentIntegrationTest extends AbstractAlignmentSpec { } module('org2:foo:1.0') { edge('org4:a:1.0', 'org4:a:1.1') { - byConflictResolution("between versions 1.0 and 1.1") + byConflictResolution("between versions 1.1 and 1.0") } } module('org3:bar:1.0') { @@ -596,7 +596,7 @@ class AlignmentIntegrationTest extends AbstractAlignmentSpec { byConstraint("belongs to platform org:platform:1.1") // byReason("version 1.1 is buggy") // TODO CC: uncomment when we collect rejection from component selection rule edge('org:core:1.0', 'org:core:1.1') { - byConflictResolution("between versions 1.0 and 1.1") + byConflictResolution("between versions 1.1 and 1.0") } } module("org:json:1.1") { @@ -731,7 +731,7 @@ class AlignmentIntegrationTest extends AbstractAlignmentSpec { module("org.apache.groovy:core:2.5") } edge("org.springframework:core:1.0", "org.springframework:core:1.1") { - byConflictResolution("between versions 1.0 and 1.1") + byConflictResolution("between versions 1.1 and 1.0") } } } @@ -813,7 +813,7 @@ class AlignmentIntegrationTest extends AbstractAlignmentSpec { module("org.apache.groovy:core:2.5") } edge("org.springframework:core:1.0", "org.springframework:core:1.1") { - byConflictResolution("between versions 1.0 and 1.1") + byConflictResolution("between versions 1.1 and 1.0") } } } diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy index 81cb04500db5..44d72df7f066 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy @@ -78,7 +78,7 @@ class ResolutionResultApiIntegrationTest extends AbstractDependencyResolutionTes then: output.contains """ cool-project:5.0 root -foo:1.0 between versions 0.5 and 1.0 +foo:1.0 between versions 1.0 and 0.5 leaf:2.0 forced bar:1.0 requested baz:1.0 requested @@ -129,8 +129,8 @@ baz:1.0 requested descriptions.each { println "\$it.cause : \$it.description" } - def descriptor = descriptions.find { it.cause == ComponentSelectionCause.REQUESTED } - assert descriptor?.description == 'second reason' + def descriptors = descriptions.findAll { it.cause == ComponentSelectionCause.REQUESTED } + assert descriptors.description == ['first reason', 'second reason'] } } } @@ -200,8 +200,8 @@ baz:1.0 requested descriptions.each { println "\$it.cause : \$it.description" } - def descriptor = descriptions.find { it.cause == ComponentSelectionCause.REQUESTED } - assert descriptor?.description == 'second reason' + def descriptors = descriptions.findAll { it.cause == ComponentSelectionCause.REQUESTED } + assert descriptors.description == ['requested', 'second reason'] } } } @@ -217,14 +217,14 @@ baz:1.0 requested root(":", ":test:") { module('org.test:a:1.0:runtime') { edge('org.test:leaf:0.9', 'org.test:leaf:1.1') - .byConflictResolution("between versions 1.0 and 1.1") // conflict with the version requested by 'b' + .byConflictResolution("between versions 1.1 and 1.0") // conflict with the version requested by 'b' .byReason('second reason') // this comes from 'b' .selectedByRule("substitute 0.9 with 1.0") } module('org.test:b:1.0:runtime') { module('org.test:leaf:1.1') .selectedByRule("substitute 0.9 with 1.0") - .byConflictResolution("between versions 1.0 and 1.1") + .byConflictResolution("between versions 1.1 and 1.0") .byReason('second reason') } } diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/MultipleVariantSelectionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/MultipleVariantSelectionIntegrationTest.groovy index f95b4e992916..a0262ce4028c 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/MultipleVariantSelectionIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/MultipleVariantSelectionIntegrationTest.groovy @@ -355,7 +355,7 @@ class MultipleVariantSelectionIntegrationTest extends AbstractModuleDependencyRe resolve.expectGraph { root(":", ":test:") { edge('org:foo:1.0', 'org:foo:1.1') { - byConflictResolution('between versions 1.0 and 1.1') + byConflictResolution('between versions 1.1 and 1.0') // the following assertion is true but limitations to the test fixtures make it hard to check //variant('altruntime', [custom: 'c3', 'org.gradle.status': defaultStatus()]) variant('runtime', [custom: 'c2', 'org.gradle.status': defaultStatus(), 'org.gradle.usage': 'java-runtime-jars']) @@ -586,7 +586,7 @@ class MultipleVariantSelectionIntegrationTest extends AbstractModuleDependencyRe resolve.expectGraph { root(":", ":test:") { edge('org:foo:1.0', 'org:foo:1.1') { - byConflictResolution('between versions 1.0 and 1.1') + byConflictResolution('between versions 1.1 and 1.0') variant('runtime', [custom: 'c2', 'org.gradle.status': defaultStatus(), 'org.gradle.usage': 'java-runtime-jars']) artifact group: 'org', module: 'foo', version: '1.0', classifier: 'c2' } diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsAndResolutionStrategiesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsAndResolutionStrategiesIntegrationTest.groovy index 18f7f268b684..f6aff30d504a 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsAndResolutionStrategiesIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsAndResolutionStrategiesIntegrationTest.groovy @@ -91,7 +91,7 @@ class DependencyConstraintsAndResolutionStrategiesIntegrationTest extends Abstra then: failure.assertHasCause """Conflict(s) found for the following module(s): - - org:foo between versions 1.0 and 1.1""" + - org:foo between versions 1.1 and 1.0""" } void "dependency substitution rules are applied to dependency constraints"() { diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsIntegrationTest.groovy index 38855bddd05a..98eae0fd1c65 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsIntegrationTest.groovy @@ -135,7 +135,7 @@ class DependencyConstraintsIntegrationTest extends AbstractIntegrationSpec { resolve.expectGraph { root(":", ":test:") { module("org:bar:1.0") { - edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.0 and 1.1") + edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0") } constraint("org:foo:1.1", "org:foo:1.1") } @@ -296,7 +296,7 @@ class DependencyConstraintsIntegrationTest extends AbstractIntegrationSpec { resolve.expectGraph { root(":", ":test:") { constraint("org:bar:1.1", "org:foo:1.1").selectedByRule() - edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.0 and 1.1") + edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0") } } } @@ -326,7 +326,7 @@ class DependencyConstraintsIntegrationTest extends AbstractIntegrationSpec { then: resolve.expectGraph { root(":", ":test:") { - edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.0 and 1.1") + edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0") constraint("org:foo:1.1", "org:foo:1.1") } } @@ -369,7 +369,7 @@ class DependencyConstraintsIntegrationTest extends AbstractIntegrationSpec { then: resolve.expectGraph { root(":", ":test:") { - edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.0 and 1.1").byConstraint('transitive dependency constraint') + edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0").byConstraint('transitive dependency constraint') project(":b", "test:b:") { configuration = "conf" noArtifacts() @@ -417,7 +417,7 @@ class DependencyConstraintsIntegrationTest extends AbstractIntegrationSpec { then: resolve.expectGraph { root(":", ":test:") { - edge("org:foo:1.0", "org:foo:1.1:runtime").byConflictResolution("between versions 1.0 and 1.1") + edge("org:foo:1.0", "org:foo:1.1:runtime").byConflictResolution("between versions 1.1 and 1.0") edge("org:included:1.0", "project :included", "org:included:1.0") { noArtifacts() constraint("org:foo:1.1", "org:foo:1.1") diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRealProjectsDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRealProjectsDependencyResolveIntegrationTest.groovy index 6c73bb366bc9..7154486143fc 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRealProjectsDependencyResolveIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRealProjectsDependencyResolveIntegrationTest.groovy @@ -97,7 +97,7 @@ task check { } } edge('commons-collections:commons-collections:3.0', 'commons-collections:commons-collections:3.1') { - byConflictResolution("between versions 3.0 and 3.1") + byConflictResolution("between versions 3.1 and 3.0") } } } diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIntegrationTest.groovy index bc07919891f0..92d31fcebe22 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIntegrationTest.groovy @@ -1018,7 +1018,7 @@ class DependencyMetadataRulesIntegrationTest extends AbstractModuleDependencyRes edge("org.test:moduleC:1.0", "org.test:moduleC:1.1") byReason('can set a custom reason in a rule') } - constraint("org.test:moduleC:{strictly 1.1}", "org.test:moduleC:1.1").byConflictResolution("between versions 1.0 and 1.1") + constraint("org.test:moduleC:{strictly 1.1}", "org.test:moduleC:1.1").byConflictResolution("between versions 1.1 and 1.0") } } } 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 579d801c06c5..7ed46033bc10 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 @@ -16,23 +16,45 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder; -import com.google.common.collect.Iterators; import com.google.common.collect.Lists; +import org.gradle.api.internal.artifacts.ResolvedVersionConstraint; +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator; +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ExactVersionSelector; +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestVersionSelector; +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.Version; +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.resolveengine.graph.selectors.ResolvableSelectorState; +import org.gradle.internal.Cast; +import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; +import java.util.function.Function; public class ModuleSelectors implements Iterable { + private static final VersionParser VERSION_PARSER = new VersionParser(); + private static final Version EMPTY_VERSION = VERSION_PARSER.transform(""); + private static final Comparator VERSION_COMPARATOR = (new DefaultVersionComparator().asVersionComparator()).reversed(); - private static final Iterator EMPTY_ITERATOR = new EmptyIterator(); + private static > Comparator reverse( + Function keyExtractor) { + return Cast.uncheckedCast(Comparator.comparing(keyExtractor).reversed()); + } + + final static Comparator SELECTOR_COMPARATOR = + reverse(ResolvableSelectorState::isProject) + .thenComparing(reverse(ResolvableSelectorState::isFromLock)) + .thenComparing(reverse(ModuleSelectors::hasLatestSelector)) + .thenComparing(ModuleSelectors::isDynamicSelector) + .thenComparing(ModuleSelectors::requiredVersion, VERSION_COMPARATOR) + .thenComparing(ModuleSelectors::preferredVersion, VERSION_COMPARATOR); - private int selectorsCount = 0; - private T singleSelector; - private List selectors; - private List dynamicSelectors; + private final List selectors = Lists.newArrayList(); + private int size; private boolean deferSelection; + private boolean shouldSort; public boolean checkDeferSelection() { if (deferSelection) { @@ -45,147 +67,85 @@ public boolean checkDeferSelection() { @SuppressWarnings("unchecked") @Override public Iterator iterator() { - if (selectorsCount == 0) { - return EMPTY_ITERATOR; - } else if (selectorsCount == 1) { - return Iterators.singletonIterator(singleSelector); - } else { - if (selectors != null && dynamicSelectors != null) { - return Iterators.concat(selectors.iterator(), dynamicSelectors.iterator()); - } else if (selectors != null) { - return selectors.iterator(); - } else { - return dynamicSelectors.iterator(); - } + sort(); + return selectors.iterator(); + } + + private void sort() { + if (shouldSort) { + Collections.sort(selectors, SELECTOR_COMPARATOR); + shouldSort = false; } } public void add(T selector, boolean deferSelection) { - assert !contains(selector) : "Inconsistent call to add: should only be done if the selector isn't in use"; - if (selectorsCount == 0) { - singleSelector = selector; - this.deferSelection = deferSelection; - } else if (selectorsCount == 1) { - addSelector(singleSelector); - addSelector(selector); - singleSelector = null; - } else { - addSelector(selector); - } - selectorsCount++; + this.deferSelection = deferSelection; + selectors.add(selector); + size++; + shouldSort = size > 1; } - private void addSelector(T selector) { - if (isDynamicSelector(selector)) { - addDynamicSelector(selector); - } else { - addSimpleSelector(selector); + public boolean remove(T selector) { + boolean remove = selectors.remove(selector); + if (remove) { + size--; + shouldSort = size > 1; } + return remove; } - private boolean isDynamicSelector(T selector) { + private static boolean isDynamicSelector(ResolvableSelectorState selector) { return selector.getVersionConstraint() != null && selector.getVersionConstraint().isDynamic(); } - private void addSimpleSelector(T selector) { - if (selectors == null) { - selectors = Lists.newArrayListWithExpectedSize(3); - } - selectors.add(selector); + private static boolean hasLatestSelector(ResolvableSelectorState selector) { + return selector.getVersionConstraint() != null + && hasLatestSelector(selector.getVersionConstraint()); } - private void addDynamicSelector(T selector) { - if (dynamicSelectors == null) { - dynamicSelectors = Lists.newArrayListWithExpectedSize(3); - } - dynamicSelectors.add(selector); + private static boolean hasLatestSelector(ResolvedVersionConstraint vc) { + return hasLatestSelector(vc.getPreferredSelector()) || hasLatestSelector(vc.getRequiredSelector()); } - public boolean remove(T selector) { - assert contains(selector) : "Inconsistent call to remove: should only be done if the selector is in use"; - boolean removed = false; - if (selectorsCount == 0) { - return false; - } else if (selectorsCount == 1) { - removed = singleSelector.equals(selector); - if (removed) { - singleSelector = null; - } - } else { - if (isDynamicSelector(selector)) { - if (dynamicSelectors != null) { - removed = dynamicSelectors.remove(selector); - if (dynamicSelectors.isEmpty()) { - dynamicSelectors = null; - } - } - } else { - if (selectors != null) { - removed = selectors.remove(selector); - if (selectors.isEmpty()) { - selectors = null; - } - } - } - } - if (removed) { - selectorsCount--; - if (selectorsCount == 1) { - if (selectors != null) { - singleSelector = selectors.get(0); - selectors = null; - } else { - singleSelector = dynamicSelectors.get(0); - dynamicSelectors = null; - } - } - } - return removed; + private static boolean hasLatestSelector(VersionSelector versionSelector) { + return versionSelector instanceof LatestVersionSelector; } - public int size() { - return selectorsCount; + private static Version requiredVersion(ResolvableSelectorState selector) { + ResolvedVersionConstraint versionConstraint = selector.getVersionConstraint(); + if (versionConstraint == null) { + return EMPTY_VERSION; + } + return versionOf(versionConstraint.getRequiredSelector()); } - public T first() { - if (selectorsCount == 0) { - return null; - } else if (selectorsCount == 1) { - return singleSelector; - } else { - if (selectors != null) { - return selectors.get(0); - } else { - return dynamicSelectors.get(0); - } + private static Version preferredVersion(ResolvableSelectorState selector) { + ResolvedVersionConstraint versionConstraint = selector.getVersionConstraint(); + if (versionConstraint == null) { + return EMPTY_VERSION; } + return versionOf(versionConstraint.getPreferredSelector()); } - // Only used for assertions - private boolean contains(T selector) { - if (selectorsCount == 0) { - return false; - } else if (selectorsCount == 1) { - return singleSelector.equals(selector); - } else { - if (isDynamicSelector(selector)) { - return dynamicSelectors != null && dynamicSelectors.contains(selector); - } else { - return selectors != null && selectors.contains(selector); - } + private static Version versionOf(VersionSelector selector) { + if (!(selector instanceof ExactVersionSelector)) { + return EMPTY_VERSION; } + return VERSION_PARSER.transform(selector.getSelector()); } - private static class EmptyIterator implements Iterator { + public int size() { + return size; + } - @Override - public boolean hasNext() { - return false; + public T first() { + if (size == 0) { + return null; } - - @Override - public Object next() { - throw new NoSuchElementException("Empty iterator has no elements"); + if (size == 1) { + return selectors.get(0); } + sort(); + return selectors.get(0); } } 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 dbd168b851d2..fa9f6843fc1b 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 @@ -16,6 +16,7 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors; 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.resolve.ModuleVersionResolveException; @@ -63,4 +64,7 @@ public interface ResolvableSelectorState { boolean isFromLock(); + 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 548a3ce07c42..6054576f1eda 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 @@ -119,7 +119,7 @@ public static T componentForIdResolveResult boolean alreadyHaveResolutionForSelector(ResolvableSelectorState selector) { for (Registration registration : results) { ComponentIdResolveResult discovered = registration.result; - if (included(selector, discovered, registration.selector.isFromLock())) { + if (selectorAcceptsCandidate(selector, discovered, registration.selector.isFromLock())) { register(selector, discovered); selector.markResolved(); return true; @@ -128,15 +128,15 @@ boolean alreadyHaveResolutionForSelector(ResolvableSelectorState selector) { return false; } - boolean replaceExistingResolutionsWithBetterResult(ComponentIdResolveResult resolveResult, boolean isFromLock) { + boolean replaceExistingResolutionsWithBetterResult(ComponentIdResolveResult candidate, boolean isFromLock) { // Check already-resolved dependencies and use this version if it's compatible boolean replaces = false; for (Registration registration : results) { - ComponentIdResolveResult result = registration.result; - ResolvableSelectorState selector = registration.selector; - if (emptyVersion(result) || sameVersion(result, resolveResult) || - (included(selector, resolveResult, isFromLock) && lowerVersion(result, resolveResult))) { - registration.result = resolveResult; + ComponentIdResolveResult previous = registration.result; + ResolvableSelectorState previousSelector = registration.selector; + if (emptyVersion(previous) || sameVersion(previous, candidate) || + (selectorAcceptsCandidate(previousSelector, candidate, isFromLock) && lowerVersion(previous, candidate))) { + registration.result = candidate; replaces = true; } } @@ -172,7 +172,7 @@ private static boolean lowerVersion(ComponentIdResolveResult existing, Component return false; } - private static boolean included(ResolvableSelectorState dep, ComponentIdResolveResult candidate, boolean candidateIsFromLock) { + private static boolean selectorAcceptsCandidate(ResolvableSelectorState dep, ComponentIdResolveResult candidate, boolean candidateIsFromLock) { if (hasFailure(candidate)) { return false; } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy index aa8b52afebb9..6070fd03827f 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy @@ -192,8 +192,8 @@ class DependencyGraphBuilderTest extends Specification { traverses root, a traverses root, b traverses root, c - traverses a, d, revision: 'latest' - doesNotResolve b, d, revision: 'latest' + traverses a, d, revision: 'latest.release' + doesNotResolve b, d, revision: 'latest.release' doesNotResolve c, d when: @@ -259,7 +259,7 @@ class DependencyGraphBuilderTest extends Specification { 1 * conflictResolver.select(!null) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1.1', '1.2'] + assert candidates*.version == ['1.2', '1.1'] details.select(candidates.find { it.version == '1.2' }) } 0 * conflictResolver._ @@ -291,7 +291,7 @@ class DependencyGraphBuilderTest extends Specification { 1 * conflictResolver.select(!null) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1.1', '1.2'] + assert candidates*.version == ['1.2', '1.1'] details.select(candidates.find { it.version == '1.2' }) } 0 * conflictResolver._ @@ -353,7 +353,7 @@ class DependencyGraphBuilderTest extends Specification { 1 * conflictResolver.select(!null) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1.1', '1.2'] + assert candidates*.version == ['1.2', '1.1'] details.select(candidates.find { it.version == '1.2' }) } 0 * conflictResolver._ @@ -362,7 +362,7 @@ class DependencyGraphBuilderTest extends Specification { result.components == ids(root, selected, b, c, d) } - def "does not include evicted module required by another evicted module"() { + def"does not include evicted module required by another evicted module"() { given: def selectedA = revision('a', '1.2') def evictedA = revision('a', '1.1') @@ -386,13 +386,13 @@ class DependencyGraphBuilderTest extends Specification { 1 * conflictResolver.select(!null) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1.1', '1.2'] + assert candidates*.version == ['1.2', '1.1'] details.select(candidates.find { it.version == '1.2' }) } 1 * conflictResolver.select(!null) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['2.1', '2.2'] + assert candidates*.version == ['2.2', '2.1'] details.select(candidates.find { it.version == '2.2' }) } 0 * conflictResolver._ @@ -451,7 +451,7 @@ class DependencyGraphBuilderTest extends Specification { 1 * conflictResolver.select(!null) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1.1', '1.2'] + assert candidates*.version == ['1.2', '1.1'] details.select(candidates.find { it.version == '1.2' }) } 0 * conflictResolver._ @@ -483,19 +483,19 @@ class DependencyGraphBuilderTest extends Specification { 1 * conflictResolver.select(_) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1.1', '1.2'] + assert candidates*.version == ['1.2', '1.1'] details.select(candidates.find { it.version == '1.2' }) } 1 * conflictResolver.select(_) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['2.1', '2.2'] + assert candidates*.version == ['2.2', '2.1'] details.select(candidates.find { it.version == '2.2' }) } 1 * conflictResolver.select(_) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1.1', '1.2', '1.0'] + assert candidates*.version == ['1.2', '1.1', '1.0'] details.select(candidates.find { it.version == '1.2' }) } 0 * conflictResolver._ @@ -528,7 +528,7 @@ class DependencyGraphBuilderTest extends Specification { 1 * conflictResolver.select(_) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1', '2'] + assert candidates*.version == ['2', '1'] details.select(candidates.find { it.version == '2' }) } 0 * conflictResolver._ @@ -561,7 +561,7 @@ class DependencyGraphBuilderTest extends Specification { 1 * conflictResolver.select(_) >> { args -> def details = args[0] Collection candidates = details.candidates - assert candidates*.version == ['1', '2'] + assert candidates*.version == ['2', '1'] details.select(candidates.find { it.version == '2' }) } 0 * conflictResolver._ diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectorsTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectorsTest.groovy index 26d7d64c5813..4345a4f8fac0 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectorsTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectorsTest.groovy @@ -17,13 +17,18 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder import org.gradle.api.internal.artifacts.ResolvedVersionConstraint +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ExactVersionSelector +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestVersionSelector import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ResolvableSelectorState import spock.lang.Specification +import spock.lang.Subject import spock.lang.Unroll class ModuleSelectorsTest extends Specification { - def selectors = new ModuleSelectors() + @Subject + ModuleSelectors selectors = new ModuleSelectors() + int dynCount = 1 def 'empty by default'() { expect: @@ -127,7 +132,7 @@ class ModuleSelectorsTest extends Specification { @Unroll def 'can add and remove selectors in any order'() { given: - def selector1 =dynamicSelector() + def selector1 = dynamicSelector() def selector2 = Mock(ResolvableSelectorState) def selector3 = dynamicSelector() def selector4 = Mock(ResolvableSelectorState) @@ -150,6 +155,43 @@ class ModuleSelectorsTest extends Specification { } + def "sorts selectors for faster selection"() { + given: + def dyn1 = dynamicSelector() + def dyn2 = dynamicSelector() + def latest = latestSelector() + def v1 = prefer('1.0') + def v2 = prefer('2.0') + def v3 = prefer('3.0') + def v4 = require("2.0") + def v5 = require("3.0") + def fromLock = fromLock('1.0') + + expect: + sort([v3, v1, v2]) == [v3, v2, v1] + + and: + sort([v3, v1, v2, v4]) == [v4, v3, v2, v1] + + and: + sort([v3, v1, v2, v4, v5]) == [v5, v4, v3, v2, v1] + + and: + sort([v3, dyn1, v1]) == [v3, v1, dyn1] + + and: + sort([dyn1, v1, fromLock, dyn2]) == [fromLock, v1, dyn1, dyn2] + + and: + sort([v1, fromLock, dyn1, latest]) == [fromLock, latest, v1, dyn1] + + } + + private static List sort(List list) { + list.sort(ModuleSelectors.SELECTOR_COMPARATOR) + list + } + void verifyEmpty(ModuleSelectors selectors) { assert selectors.size() == 0 assert selectors.first() == null @@ -158,10 +200,49 @@ class ModuleSelectorsTest extends Specification { } ResolvableSelectorState dynamicSelector() { + int cpt = dynCount++ Mock(ResolvableSelectorState) { getVersionConstraint() >> Mock(ResolvedVersionConstraint) { isDynamic() >> true } + toString() >> "dynamic selector $cpt" + } + } + + ResolvableSelectorState latestSelector() { + Mock(ResolvableSelectorState) { + getVersionConstraint() >> Mock(ResolvedVersionConstraint) { + getPreferredSelector() >> new LatestVersionSelector("release") + } + toString() >> "latest" + } + } + + ResolvableSelectorState prefer(String version) { + Mock(ResolvableSelectorState) { + getVersionConstraint() >> Mock(ResolvedVersionConstraint) { + getPreferredSelector() >> new ExactVersionSelector(version) + } + toString() >> "prefer $version" + } + } + + ResolvableSelectorState require(String version) { + Mock(ResolvableSelectorState) { + getVersionConstraint() >> Mock(ResolvedVersionConstraint) { + getRequiredSelector() >> new ExactVersionSelector(version) + } + toString() >> "require $version" + } + } + + ResolvableSelectorState fromLock(String version) { + Mock(ResolvableSelectorState) { + getVersionConstraint() >> Mock(ResolvedVersionConstraint) { + getPreferredSelector() >> new ExactVersionSelector(version) + } + isFromLock() >> true + toString() >> "lock $version" } } } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestComponentSelector.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestComponentSelector.java new file mode 100644 index 000000000000..974a778179e8 --- /dev/null +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/TestComponentSelector.java @@ -0,0 +1,47 @@ +/* + * 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.api.internal.artifacts.ivyservice.resolveengine.graph.selectors; + +import com.google.common.collect.ImmutableList; +import org.gradle.api.artifacts.component.ComponentIdentifier; +import org.gradle.api.artifacts.component.ComponentSelector; +import org.gradle.api.attributes.AttributeContainer; +import org.gradle.api.capabilities.Capability; +import org.gradle.api.internal.attributes.ImmutableAttributes; + +import java.util.List; + +public class TestComponentSelector implements ComponentSelector { + @Override + public String getDisplayName() { + return "test"; + } + + @Override + public boolean matchesStrictly(ComponentIdentifier identifier) { + return false; + } + + @Override + public AttributeContainer getAttributes() { + return ImmutableAttributes.EMPTY; + } + + @Override + public List getRequestedCapabilities() { + return ImmutableList.of(); + } +} 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 af362f925e34..a6f0a9f9e8e4 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 @@ -55,7 +55,7 @@ public ResolvedVersionConstraint getVersionConstraint() { @Override public ComponentSelector getSelector() { - throw new UnsupportedOperationException(); + return new TestComponentSelector(); } @Override diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy index ca68037e6c3a..f53eaf224bbe 100644 --- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy +++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy @@ -206,7 +206,7 @@ org:leaf2:2.5 org.gradle.category = library (not requested) ] Selection reasons: - - By conflict resolution : between versions 1.5, 2.5 and 1.0 + - By conflict resolution : between versions 2.5, 1.5 and 1.0 org:leaf2:2.5 \\--- org:toplevel3:1.0 @@ -331,7 +331,7 @@ org:leaf:1.0 then: outputContains """Dependency resolution failed because of conflict(s) on the following module(s): - - org:leaf2 between versions 1.5, 2.5 and 1.0 + - org:leaf2 between versions 2.5, 1.5 and 1.0 org:leaf2:2.5 variant "runtime" [ @@ -340,7 +340,7 @@ org:leaf2:2.5 org.gradle.category = library (not requested) ] Selection reasons: - - By conflict resolution : between versions 1.5, 2.5 and 1.0 + - By conflict resolution : between versions 2.5, 1.5 and 1.0 org:leaf2:2.5 \\--- org:toplevel3:1.0 @@ -404,7 +404,7 @@ org:leaf2:1.5 -> 2.5 then: outputContains """Dependency resolution failed because of conflict(s) on the following module(s): - - org:leaf2 between versions 1.5, 2.5 and 1.0 + - org:leaf2 between versions 2.5, 1.5 and 1.0 org:leaf2:2.5 variant "runtime" [ @@ -413,7 +413,7 @@ org:leaf2:2.5 org.gradle.category = library (not requested) ] Selection reasons: - - By conflict resolution : between versions 1.5, 2.5 and 1.0 + - By conflict resolution : between versions 2.5, 1.5 and 1.0 org:leaf2:2.5 \\--- org:toplevel3:1.0 @@ -2990,7 +2990,7 @@ planet:venus:2.0.1 org.gradle.jvm.version = ${JavaVersion.current().majorVersion} ] Selection reasons: - - By conflict resolution : between versions 2.0.0, 2.0.1 and 1.0 + - By conflict resolution : between versions 2.0.1, 2.0.0 and 1.0 planet:venus:2.0.1 \\--- planet:mars:4.0.0 From bd90c10f7a6912f1134970644ba9dd44e9e11ed0 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 May 2019 18:27:00 +0200 Subject: [PATCH 33/55] Compute "hasTransitiveEdges" on the go Instead of iterating on edges and potentially creating a list, that we don't need given we only need to know if there's any transitive edge. --- .../graph/builder/NodeState.java | 87 ++++++++++--------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index b323b7d2dc33..9e6339be4338 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -43,7 +43,6 @@ import org.gradle.internal.component.model.DependencyMetadata; import org.gradle.internal.component.model.SelectedByVariantMatchingConfigurationMetadata; import org.gradle.internal.resolve.ModuleVersionResolveException; -import org.gradle.util.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,6 +84,7 @@ public boolean isSatisfiedBy(EdgeState edge) { private List virtualEdges; private boolean queued; private boolean evicted; + private int transitiveEdgeCount; private Set upcomingNoLongerPendingConstraints; private boolean virtualPlatformNeedsRefresh; private Set edgesToRecompute; @@ -236,11 +236,8 @@ public void visitOutgoingDependencies(Collection discoveredEdges) { return; } - // Check if this node is still included in the graph, by looking at incoming edges. - List transitiveIncoming = getTransitiveIncomingEdges(); - // Check if there are any transitive incoming edges at all. Don't traverse if not. - if (transitiveIncoming.isEmpty() && !isRoot()) { + if (transitiveEdgeCount == 0 && !isRoot()) { handleNonTransitiveNode(discoveredEdges); return; } @@ -486,21 +483,11 @@ static DependencyState maybeSubstitute(DependencyState dependencyState, Dependen return dependencyState; } - /** - * Returns the set of incoming edges that are transitive. Most edges are transitive, so the implementation is optimized for this case. - */ - private List getTransitiveIncomingEdges() { + private boolean hasAnyTransitiveEdge() { if (isRoot()) { - return incomingEdges; - } - for (EdgeState incomingEdge : incomingEdges) { - if (!incomingEdge.isTransitive()) { - // Have a non-transitive edge: return a filtered list - return CollectionUtils.filter(incomingEdges, TRANSITIVE_EDGES); - } + return true; } - // All edges are transitive, no need to construct a filtered list. - return incomingEdges; + return incomingEdges.stream().anyMatch(EdgeState::isTransitive); } private boolean isExcluded(ExcludeSpec selector, DependencyState dependencyState) { @@ -526,6 +513,9 @@ void addIncomingEdge(EdgeState dependencyEdge) { incomingEdges.add(dependencyEdge); incomingHash += dependencyEdge.hashCode(); resolveState.onMoreSelected(this); + if (dependencyEdge.isTransitive()) { + transitiveEdgeCount++; + } } } @@ -533,6 +523,9 @@ void removeIncomingEdge(EdgeState dependencyEdge) { if (incomingEdges.remove(dependencyEdge)) { incomingHash -= dependencyEdge.hashCode(); resolveState.onFewerSelected(this); + if (dependencyEdge.isTransitive()) { + transitiveEdgeCount--; + } } } @@ -594,29 +587,11 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud excludedByBoth.add(exclusions); } } else if (dependencyEdge.getDependencyMetadata().isConstraint()) { - // Constraint: only consider explicit exclusions declared for this constraint - ExcludeSpec constraintExclusions = dependencyEdge.getEdgeExclusions(); - if (constraintExclusions != nothing && constraintExclusions != nodeExclusions) { - if (excludedByEither == null) { - excludedByEither = Sets.newHashSetWithExpectedSize(incomingEdgeCount); - } - excludedByEither.add(constraintExclusions); - } - } - } - if (excludedByBoth != null) { - if (edgeExclusions != null) { - // do not believe what IJ says, it can be non null, see above - excludedByBoth.add(edgeExclusions); - } - edgeExclusions = moduleExclusions.excludeAll(excludedByBoth); - } - if (excludedByEither != null) { - if (nodeExclusions != null) { - excludedByEither.add(nodeExclusions); - nodeExclusions = moduleExclusions.excludeAny(excludedByEither); + excludedByEither = collectEdgeConstraint(nodeExclusions, excludedByEither, dependencyEdge, nothing, incomingEdgeCount); } } + edgeExclusions = intersectEdgeExclusions(edgeExclusions, excludedByBoth); + nodeExclusions = joinNodeExclusions(nodeExclusions, excludedByEither); ExcludeSpec result = moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); // We use a set here because for excludes, order of edges is irrelevant // so we hit the cache more by using a set @@ -627,6 +602,39 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud return result; } + private static Set collectEdgeConstraint(ExcludeSpec nodeExclusions, Set excludedByEither, EdgeState dependencyEdge, ExcludeSpec nothing, int incomingEdgeCount) { + // Constraint: only consider explicit exclusions declared for this constraint + ExcludeSpec constraintExclusions = dependencyEdge.getEdgeExclusions(); + if (constraintExclusions != nothing && constraintExclusions != nodeExclusions) { + if (excludedByEither == null) { + excludedByEither = Sets.newHashSetWithExpectedSize(incomingEdgeCount); + } + excludedByEither.add(constraintExclusions); + } + return excludedByEither; + } + + private ExcludeSpec joinNodeExclusions(ExcludeSpec nodeExclusions, Set excludedByEither) { + if (excludedByEither != null) { + if (nodeExclusions != null) { + excludedByEither.add(nodeExclusions); + nodeExclusions = moduleExclusions.excludeAny(excludedByEither); + } + } + return nodeExclusions; + } + + private ExcludeSpec intersectEdgeExclusions(ExcludeSpec edgeExclusions, Set excludedByBoth) { + if (excludedByBoth != null) { + if (edgeExclusions != null) { + // do not believe what IJ says, it can be non null, see above + excludedByBoth.add(edgeExclusions); + } + edgeExclusions = moduleExclusions.excludeAll(excludedByBoth); + } + return edgeExclusions; + } + private boolean sameIncomingEdgesAsPreviousPass(int incomingEdgeCount) { // This is a heuristic, more than truth: it is possible that the 2 long hashs // are identical AND that the sizes of collections are identical, but it's @@ -686,6 +694,7 @@ private void restartIncomingEdges() { private void clearIncomingEdges() { incomingEdges.clear(); incomingHash = 0; + transitiveEdgeCount = 0; } public void deselect() { From 8384412365e81f835e1f5e549e67c12427344511 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 May 2019 18:55:16 +0200 Subject: [PATCH 34/55] Minor refactoring for readability --- .../graph/builder/NodeState.java | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 9e6339be4338..98b1a64c8dd3 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -566,32 +566,42 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud // the result we computed last time return cachedModuleResolutionFilter; } + ExcludeSpec nothing = moduleExclusions.nothing(); ExcludeSpec edgeExclusions = null; Set excludedByBoth = null; Set excludedByEither = null; - ExcludeSpec nothing = moduleExclusions.nothing(); for (EdgeState dependencyEdge : incomingEdges) { if (dependencyEdge.isTransitive()) { - // Transitive dependency - ExcludeSpec exclusions = dependencyEdge.getExclusions(); - if (edgeExclusions == null || exclusions == nothing) { - // if exclusions == nothing, then the intersection will be "nothing" - edgeExclusions = exclusions; - if (exclusions == nothing) { - excludedByBoth = null; + if (edgeExclusions != nothing) { + // Transitive dependency + ExcludeSpec exclusions = dependencyEdge.getExclusions(); + if (edgeExclusions == null || exclusions == nothing) { + edgeExclusions = exclusions; + } else if (exclusions != null && edgeExclusions != exclusions) { + if (excludedByBoth == null) { + excludedByBoth = Sets.newHashSetWithExpectedSize(incomingEdgeCount); + } + excludedByBoth.add(exclusions); } - } else if (exclusions != null && edgeExclusions != exclusions && edgeExclusions != nothing) { - if (excludedByBoth == null) { - excludedByBoth = Sets.newHashSetWithExpectedSize(incomingEdgeCount); + if (edgeExclusions == nothing) { + // if exclusions == nothing, then the intersection will be "nothing" + excludedByBoth = null; } - excludedByBoth.add(exclusions); } - } else if (dependencyEdge.getDependencyMetadata().isConstraint()) { + } else if (isConstraint(dependencyEdge)) { excludedByEither = collectEdgeConstraint(nodeExclusions, excludedByEither, dependencyEdge, nothing, incomingEdgeCount); } } edgeExclusions = intersectEdgeExclusions(edgeExclusions, excludedByBoth); nodeExclusions = joinNodeExclusions(nodeExclusions, excludedByEither); + return joinEdgeAndNodeExclusionsThenCacheResult(nodeExclusions, incomingEdgeCount, edgeExclusions); + } + + private static boolean isConstraint(EdgeState dependencyEdge) { + return dependencyEdge.getDependencyMetadata().isConstraint(); + } + + private ExcludeSpec joinEdgeAndNodeExclusionsThenCacheResult(ExcludeSpec nodeExclusions, int incomingEdgeCount, ExcludeSpec edgeExclusions) { ExcludeSpec result = moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); // We use a set here because for excludes, order of edges is irrelevant // so we hit the cache more by using a set @@ -625,9 +635,11 @@ private ExcludeSpec joinNodeExclusions(ExcludeSpec nodeExclusions, Set excludedByBoth) { + if (edgeExclusions == moduleExclusions.nothing()) { + return edgeExclusions; + } if (excludedByBoth != null) { if (edgeExclusions != null) { - // do not believe what IJ says, it can be non null, see above excludedByBoth.add(edgeExclusions); } edgeExclusions = moduleExclusions.excludeAll(excludedByBoth); @@ -726,7 +738,7 @@ public ImmutableAttributesFactory getAttributesFactory() { */ public void clearConstraintEdges(PendingDependencies pendingDependencies, NodeState backToPendingSource) { for (EdgeState incomingEdge : incomingEdges) { - assert incomingEdge.getDependencyMetadata().isConstraint(); + assert isConstraint(incomingEdge); NodeState from = incomingEdge.getFrom(); if (from != backToPendingSource) { // Only remove edges that come from a different node than the source of the dependency going back to pending From 1dd6b51ae1820eb12082497d5a9dc7f8d0fc60d7 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 08:15:23 +0200 Subject: [PATCH 35/55] Add some missing `equals/hashCode` Some classes are used in hashmaps and relying on the default hashcode is not a good idea. --- .../graph/builder/ComponentState.java | 21 ++++++++++++++++ .../graph/builder/DependencyState.java | 22 ++++++++++++++++ .../graph/builder/EdgeState.java | 25 +++++++++++++++++++ .../VersionConflictResolutionDetails.java | 2 +- 4 files changed, 69 insertions(+), 1 deletion(-) 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 d882680e67aa..aa820623c32b 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 @@ -65,6 +65,7 @@ public class ComponentState implements ComponentResolutionState, DependencyGraph private final ModuleResolveState module; private final List selectionCauses = Lists.newArrayList(); private final ImmutableCapability implicitCapability; + private final int hashCode; private volatile ComponentResolveMetadata metadata; @@ -84,6 +85,7 @@ public class ComponentState implements ComponentResolutionState, DependencyGraph this.componentIdentifier = componentIdentifier; this.resolver = resolver; this.implicitCapability = new ImmutableCapability(id.getGroup(), id.getName(), id.getVersion()); + this.hashCode = 31 * id.hashCode() ^ resultId.hashCode(); } @Override @@ -474,4 +476,23 @@ boolean hasMoreThanOneSelectedNodeUsingVariantAwareResolution() { return false; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ComponentState that = (ComponentState) o; + + return that.resultId == resultId; + + } + + @Override + public int hashCode() { + return hashCode; + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java index c872f2935f88..bbb6c75355dc 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java @@ -35,6 +35,7 @@ class DependencyState { private final DependencyMetadata dependency; private final List ruleDescriptors; private final ComponentSelectorConverter componentSelectorConverter; + private final int hashCode; private ModuleIdentifier moduleIdentifier; public ModuleVersionResolveException failure; @@ -49,6 +50,13 @@ private DependencyState(DependencyMetadata dependency, ComponentSelector request this.requested = requested; this.ruleDescriptors = ruleDescriptors; this.componentSelectorConverter = componentSelectorConverter; + this.hashCode = computeHashCode(); + } + + private int computeHashCode() { + int hashCode = dependency.hashCode(); + hashCode = 31 * hashCode + requested.hashCode(); + return hashCode; } public ComponentSelector getRequested() { @@ -116,4 +124,18 @@ void addSelectionReasons(Set reasons) { reasons.add(FORCED); } } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + // This is a performance optimization, dependency states are deduplicated + return false; + } + + @Override + public int hashCode() { + return hashCode; + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java index 83f8c50c2ef9..e37e6db66c6e 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java @@ -58,6 +58,7 @@ class EdgeState implements DependencyGraphEdge { private final List targetNodes = Lists.newLinkedList(); private final boolean isTransitive; private final boolean isConstraint; + private final int hashCode; private ModuleVersionResolveException targetNodeSelectionFailure; private ImmutableAttributes cachedAttributes; @@ -74,6 +75,16 @@ class EdgeState implements DependencyGraphEdge { this.selector = resolveState.getSelector(dependencyState); this.isTransitive = from.isTransitive() && dependencyMetadata.isTransitive(); this.isConstraint = dependencyMetadata.isConstraint(); + this.hashCode = computeHashCode(); + } + + private int computeHashCode() { + int hashCode = from.hashCode(); + hashCode = 31 * hashCode + dependencyState.hashCode(); + if (transitiveExclusions != null) { + hashCode = 31 * hashCode + transitiveExclusions.hashCode(); + } + return hashCode; } @Override @@ -349,4 +360,18 @@ void maybeDecreaseHardEdgeCount(NodeState removalSource) { selector.getTargetModule().decreaseHardEdgeCount(removalSource); } } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + // Edge states are deduplicated, this is a performance optimization + return false; + } + + @Override + public int hashCode() { + return hashCode; + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/VersionConflictResolutionDetails.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/VersionConflictResolutionDetails.java index 64d1bbac16f5..c590b24e3361 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/VersionConflictResolutionDetails.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/VersionConflictResolutionDetails.java @@ -35,7 +35,7 @@ public class VersionConflictResolutionDetails implements Describable { public VersionConflictResolutionDetails(Collection candidates) { this.candidates = candidates; - this.hashCode = Objects.hashCode(candidates); + this.hashCode = candidates.hashCode(); } public Collection getCandidates() { From 50973ce79e7e20982e6cc50724170bf03e6222d5 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 09:30:43 +0200 Subject: [PATCH 36/55] Optimize sorting of module selectors - sorting when removing is not necessary - sorting when the list has been reduced to 0 or 1 is not needed --- .../resolveengine/graph/builder/ModuleSelectors.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 7ed46033bc10..55bddc4e1674 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 @@ -73,7 +73,10 @@ public Iterator iterator() { private void sort() { if (shouldSort) { - Collections.sort(selectors, SELECTOR_COMPARATOR); + if (size > 1) { + // There's a possibility we say sort but the list has been reduced to 0 or 1 + Collections.sort(selectors, SELECTOR_COMPARATOR); + } shouldSort = false; } } @@ -89,7 +92,6 @@ public boolean remove(T selector) { boolean remove = selectors.remove(selector); if (remove) { size--; - shouldSort = size > 1; } return remove; } From 067178db6e551629c84843861d8faa845573420e Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 09:52:17 +0200 Subject: [PATCH 37/55] Add module exclusion specialization for 1 incoming edge --- .../graph/builder/NodeState.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 98b1a64c8dd3..326f35482017 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -566,6 +566,13 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud // the result we computed last time return cachedModuleResolutionFilter; } + if (incomingEdgeCount == 1) { + return computeExclusionFilterSingleIncomingEdge(incomingEdges.get(0), nodeExclusions); + } + return computeModuleExclusionsManyEdges(incomingEdges, nodeExclusions, incomingEdgeCount); + } + + private ExcludeSpec computeModuleExclusionsManyEdges(List incomingEdges, ExcludeSpec nodeExclusions, int incomingEdgeCount) { ExcludeSpec nothing = moduleExclusions.nothing(); ExcludeSpec edgeExclusions = null; Set excludedByBoth = null; @@ -594,14 +601,27 @@ private ExcludeSpec computeExclusionFilter(List incomingEdges, Exclud } edgeExclusions = intersectEdgeExclusions(edgeExclusions, excludedByBoth); nodeExclusions = joinNodeExclusions(nodeExclusions, excludedByEither); - return joinEdgeAndNodeExclusionsThenCacheResult(nodeExclusions, incomingEdgeCount, edgeExclusions); + return joinEdgeAndNodeExclusionsThenCacheResult(nodeExclusions, edgeExclusions, incomingEdgeCount); + } + + private ExcludeSpec computeExclusionFilterSingleIncomingEdge(EdgeState dependencyEdge, ExcludeSpec nodeExclusions) { + ExcludeSpec exclusions = null; + if (dependencyEdge.isTransitive()) { + exclusions = dependencyEdge.getExclusions(); + } else if (isConstraint(dependencyEdge)) { + exclusions = dependencyEdge.getEdgeExclusions(); + } + if (exclusions == null) { + exclusions = moduleExclusions.nothing(); + } + return joinEdgeAndNodeExclusionsThenCacheResult(nodeExclusions, exclusions, 1); } private static boolean isConstraint(EdgeState dependencyEdge) { return dependencyEdge.getDependencyMetadata().isConstraint(); } - private ExcludeSpec joinEdgeAndNodeExclusionsThenCacheResult(ExcludeSpec nodeExclusions, int incomingEdgeCount, ExcludeSpec edgeExclusions) { + private ExcludeSpec joinEdgeAndNodeExclusionsThenCacheResult(ExcludeSpec nodeExclusions, ExcludeSpec edgeExclusions, int incomingEdgeCount) { ExcludeSpec result = moduleExclusions.excludeAny(edgeExclusions, nodeExclusions); // We use a set here because for excludes, order of edges is irrelevant // so we hit the cache more by using a set From 84a6b681851893b3c0194b75147827edd68685e4 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 10:01:59 +0200 Subject: [PATCH 38/55] Simplify `VersionParser` --- .../ivyresolve/strategy/VersionParser.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParser.java index ce744d69ae1f..61c3eb57566a 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParser.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParser.java @@ -32,17 +32,7 @@ public VersionParser() { @Override public Version transform(String original) { - Version version = cache.get(original); - if (version != null) { - return version; - } - return parseAndCache(original); - } - - private Version parseAndCache(String original) { - Version version = parse(original); - cache.put(original, version); - return version; + return cache.computeIfAbsent(original, this::parse); } private Version parse(String original) { From b0ca0a66502ed70e180abf1a25d1be282c873dd8 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 10:20:30 +0200 Subject: [PATCH 39/55] Only consider "required" latest selector When sorting selectors, we will only give priority to a "latest" selector if it's a require selector. --- .../resolveengine/graph/builder/ModuleSelectors.java | 3 ++- .../resolveengine/graph/builder/ModuleSelectorsTest.groovy | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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 55bddc4e1674..f150d63014aa 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 @@ -106,7 +106,8 @@ private static boolean hasLatestSelector(ResolvableSelectorState selector) { } private static boolean hasLatestSelector(ResolvedVersionConstraint vc) { - return hasLatestSelector(vc.getPreferredSelector()) || hasLatestSelector(vc.getRequiredSelector()); + // Latest is only given priority if it's in a require + return hasLatestSelector(vc.getRequiredSelector()); } private static boolean hasLatestSelector(VersionSelector versionSelector) { diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectorsTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectorsTest.groovy index 4345a4f8fac0..083c4b1f79e0 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectorsTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleSelectorsTest.groovy @@ -212,7 +212,7 @@ class ModuleSelectorsTest extends Specification { ResolvableSelectorState latestSelector() { Mock(ResolvableSelectorState) { getVersionConstraint() >> Mock(ResolvedVersionConstraint) { - getPreferredSelector() >> new LatestVersionSelector("release") + getRequiredSelector() >> new LatestVersionSelector("release") } toString() >> "latest" } From e231c772dfd6b6715f1cdd1fd335f9b4dac54ba0 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 10:50:49 +0200 Subject: [PATCH 40/55] Make selector sorting faster --- .../graph/builder/ModuleSelectors.java | 16 +++++++++++++++- .../graph/builder/SelectorState.java | 9 +++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) 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 f150d63014aa..600084d7a1dd 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 @@ -75,12 +75,26 @@ private void sort() { if (shouldSort) { if (size > 1) { // There's a possibility we say sort but the list has been reduced to 0 or 1 - Collections.sort(selectors, SELECTOR_COMPARATOR); + if (size == 2) { + sortTwoElements(); + } else { + Collections.sort(selectors, SELECTOR_COMPARATOR); + } } shouldSort = false; } } + private void sortTwoElements() { + // faster than performing a Tim sort + T first = selectors.get(0); + T second = selectors.get(1); + if (SELECTOR_COMPARATOR.compare(first, second) > 0) { + selectors.set(0, second); + selectors.set(1, first); + } + } + public void add(T selector, boolean deferSelection) { this.deferSelection = deferSelection; selectors.add(selector); 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 c553decb986d..48f78e65e6d7 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 @@ -22,6 +22,7 @@ import org.gradle.api.Describable; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.component.ComponentSelector; +import org.gradle.api.artifacts.component.ProjectComponentSelector; import org.gradle.api.artifacts.result.ComponentSelectionCause; import org.gradle.api.internal.artifacts.ResolvedVersionConstraint; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; @@ -63,6 +64,7 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState private final ResolvedVersionConstraint versionConstraint; private final ImmutableAttributesFactory attributesFactory; private final Set dependencyReasons = Sets.newLinkedHashSet(); + private final boolean isProjectSelector; private ComponentIdResolveResult preferResult; private ComponentIdResolveResult requireResult; @@ -90,6 +92,13 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState this.dependencyState = dependencyState; this.firstSeenDependency = dependencyState.getDependency(); this.versionConstraint = resolveState.resolveVersionConstraint(firstSeenDependency.getSelector()); + this.isProjectSelector = getSelector() instanceof ProjectComponentSelector; + } + + @Override + public boolean isProject() { + // this is cached because used very often in sorting selectors + return isProjectSelector; } public void use(boolean deferSelection) { From dbb2ca6c51aa3d9ed28b0a1306c14cecf9e6f0d2 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 11:56:10 +0200 Subject: [PATCH 41/55] Order selectors on the fly Sorting can be expensive, especially when the list is almost always sorted. This commit changes the implementation to use on the fly sorting, as we add selectors. --- ...evisionRemoteResolveIntegrationTest.groovy | 6 +- .../graph/builder/ModuleSelectors.java | 75 ++++++++++--------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy index f98f4af187fe..b0174933df39 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy @@ -603,9 +603,9 @@ dependencies { succeeds "checkDeps" resolve.expectGraph { root(":", ":test:") { - edge("org.test:projectA:1.+", "org.test:projectA:3.0").byConflictResolution("between versions 1.2, 2.1 and 3.0") - edge("org.test:projectA:2.+", "org.test:projectA:3.0").byConflictResolution("between versions 1.2, 2.1 and 3.0") - edge("org.test:projectA:3.+", "org.test:projectA:3.0").byConflictResolution("between versions 1.2, 2.1 and 3.0") + edge("org.test:projectA:1.+", "org.test:projectA:3.0").byConflictResolution("between versions 3.0, 1.2 and 2.1") + edge("org.test:projectA:2.+", "org.test:projectA:3.0").byConflictResolution("between versions 3.0, 1.2 and 2.1") + edge("org.test:projectA:3.+", "org.test:projectA:3.0").byConflictResolution("between versions 3.0, 1.2 and 2.1") } } } 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 600084d7a1dd..648b7cd36af1 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 @@ -52,9 +52,7 @@ private static > Comparator reverse( .thenComparing(ModuleSelectors::preferredVersion, VERSION_COMPARATOR); private final List selectors = Lists.newArrayList(); - private int size; private boolean deferSelection; - private boolean shouldSort; public boolean checkDeferSelection() { if (deferSelection) { @@ -67,47 +65,55 @@ public boolean checkDeferSelection() { @SuppressWarnings("unchecked") @Override public Iterator iterator() { - sort(); return selectors.iterator(); } - private void sort() { - if (shouldSort) { - if (size > 1) { - // There's a possibility we say sort but the list has been reduced to 0 or 1 - if (size == 2) { - sortTwoElements(); - } else { - Collections.sort(selectors, SELECTOR_COMPARATOR); - } - } - shouldSort = false; + public void add(T selector, boolean deferSelection) { + this.deferSelection = deferSelection; + if (selectors.isEmpty()) { + selectors.add(selector); + } else { + doAdd(selector); } } - private void sortTwoElements() { - // faster than performing a Tim sort - T first = selectors.get(0); - T second = selectors.get(1); - if (SELECTOR_COMPARATOR.compare(first, second) > 0) { - selectors.set(0, second); - selectors.set(1, first); + private void doAdd(T selector) { + int size = selectors.size(); + if (size == 1) { + doAddWhenListHasOneElement(selector); + } else { + doAddWhenListHasManyElements(selectors, selector, size); } } - public void add(T selector, boolean deferSelection) { - this.deferSelection = deferSelection; - selectors.add(selector); - size++; - shouldSort = size > 1; + private static void doAddWhenListHasManyElements(List selectors, T selector, int size) { + int insertionPoint = Collections.binarySearch(selectors, selector, SELECTOR_COMPARATOR); + insertionPoint = advanceToPreserveOrder(selectors, selector, size, insertionPoint); + if (insertionPoint < 0) { + insertionPoint = ~insertionPoint; + } + selectors.add(insertionPoint, selector); } - public boolean remove(T selector) { - boolean remove = selectors.remove(selector); - if (remove) { - size--; + private static int advanceToPreserveOrder(List selectors, T selector, int size, int insertionPoint) { + while (insertionPoint > 0 && insertionPoint < size && SELECTOR_COMPARATOR.compare(selectors.get(insertionPoint), selector) == 0) { + insertionPoint++; } - return remove; + return insertionPoint; + } + + private void doAddWhenListHasOneElement(T selector) { + T first = selectors.get(0); + int c = SELECTOR_COMPARATOR.compare(first, selector); + if (c <= 0) { + selectors.add(selector); + } else { + selectors.add(0, selector); + } + } + + public boolean remove(T selector) { + return selectors.remove(selector); } private static boolean isDynamicSelector(ResolvableSelectorState selector) { @@ -152,17 +158,16 @@ private static Version versionOf(VersionSelector selector) { } public int size() { - return size; + return selectors.size(); } public T first() { - if (size == 0) { + if (size() == 0) { return null; } - if (size == 1) { + if (size() == 1) { return selectors.get(0); } - sort(); return selectors.get(0); } } From 5fcbee82bfc2bb7ff1b5966d90df8a1edd1bb391 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 14:23:20 +0200 Subject: [PATCH 42/55] Avoid the use of `Objects.hashCode` This involves the creation of an array, which starts becoming relevant when we create lots of those objects. --- .../modulecache/ModuleComponentAtRepositoryKey.java | 3 +-- .../model/DefaultModuleComponentIdentifier.java | 3 +-- .../model/DefaultModuleComponentSelector.java | 11 +++++++++-- .../component/external/model/ImmutableCapability.java | 10 +++++++--- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleComponentAtRepositoryKey.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleComponentAtRepositoryKey.java index dccd16b5b073..659c1992a905 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleComponentAtRepositoryKey.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleComponentAtRepositoryKey.java @@ -16,7 +16,6 @@ package org.gradle.api.internal.artifacts.ivyservice.modulecache; -import com.google.common.base.Objects; import org.gradle.api.artifacts.component.ModuleComponentIdentifier; public class ModuleComponentAtRepositoryKey { @@ -27,7 +26,7 @@ public class ModuleComponentAtRepositoryKey { ModuleComponentAtRepositoryKey(String repositoryId, ModuleComponentIdentifier componentId) { this.repositoryId = repositoryId; this.componentId = componentId; - this.hashCode = Objects.hashCode(repositoryId, componentId); + this.hashCode = 31 * repositoryId.hashCode() + componentId.hashCode(); } @Override diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java index 04ebd2d5e838..cfc7b7ee7c67 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java @@ -15,7 +15,6 @@ */ package org.gradle.internal.component.external.model; -import com.google.common.base.Objects; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.ModuleVersionIdentifier; import org.gradle.api.artifacts.component.ModuleComponentIdentifier; @@ -35,7 +34,7 @@ public DefaultModuleComponentIdentifier(ModuleIdentifier module, String version) this.version = version; // Do NOT change the order of members used in hash code here, it's been empirically // tested to reduce the number of collisions on a large dependency graph (performance test) - this.hashCode = Objects.hashCode(version, module); + this.hashCode = 31 * version.hashCode() + module.hashCode(); } public String getDisplayName() { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java index e9298ba1c582..c768836b2e73 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java @@ -15,7 +15,6 @@ */ package org.gradle.internal.component.external.model; -import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.ModuleVersionSelector; @@ -51,7 +50,15 @@ private DefaultModuleComponentSelector(ModuleIdentifier module, ImmutableVersion this.requestedCapabilities = requestedCapabilities; // Do NOT change the order of members used in hash code here, it's been empirically // tested to reduce the number of collisions on a large dependency graph (performance test) - this.hashCode = Objects.hashCode(version, module, attributes, requestedCapabilities); + this.hashCode = computeHashcode(module, version, attributes, requestedCapabilities); + } + + private int computeHashcode(ModuleIdentifier module, ImmutableVersionConstraint version, ImmutableAttributes attributes, ImmutableList requestedCapabilities) { + int hashCode = version.hashCode(); + hashCode = 31 * hashCode + module.hashCode(); + hashCode = 31 * hashCode + attributes.hashCode(); + hashCode = 31 * hashCode + requestedCapabilities.hashCode(); + return hashCode; } public String getDisplayName() { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java index d839b2beebe6..4067e2300ea3 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java @@ -30,9 +30,7 @@ public ImmutableCapability(String group, String name, String version) { this.name = name; this.version = version; - // Do NOT change the order of members used in hash code here, it's been empirically - // tested to reduce the number of collisions on a large dependency graph (performance test) - this.hashCode = Objects.hashCode(version, name, group); + this.hashCode = computeHashcode(group, name, version); // Using a string instead of a plain ID here might look strange, but this turned out to be // the fastest of several experiments, including: @@ -46,6 +44,12 @@ public ImmutableCapability(String group, String name, String version) { this.cachedId = group + ":" + name; } + private int computeHashcode(String group, String name, String version) { + // Do NOT change the order of members used in hash code here, it's been empirically + // tested to reduce the number of collisions on a large dependency graph (performance test) + return 31 * (31 * version.hashCode() + name.hashCode()) + group.hashCode(); + } + @Override public String getGroup() { return group; From b4d8d0c5d8ed072842cb93fae09d5d4a035a33ab Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 14:32:22 +0200 Subject: [PATCH 43/55] Use `emptyList` instead of `ImmutableList.of()` While it's better to use ImmutableList for consistency, it is surprisingly expensive to have the answer to the question `isEmpty` on an empty immutable list. This is done only where it showed up as a hotspot in profiling: getting artifacts. --- .../AbstractIvyDependencyDescriptorFactory.java | 6 +++++- .../resolveengine/graph/builder/EdgeState.java | 15 +++++++-------- .../model/LocalComponentDependencyMetadata.java | 9 +++++++-- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java index 388e7e4d2dc3..f42cebeca921 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java @@ -25,6 +25,7 @@ import org.gradle.internal.component.model.IvyArtifactName; import org.gradle.util.CollectionUtils; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -48,7 +49,10 @@ public ExcludeMetadata transform(ExcludeRule excludeRule) { }); } - protected ImmutableList convertArtifacts(Set dependencyArtifacts) { + protected List convertArtifacts(Set dependencyArtifacts) { + if (dependencyArtifacts.isEmpty()) { + return Collections.emptyList(); + } ImmutableList.Builder names = ImmutableList.builder(); for (DependencyArtifact dependencyArtifact : dependencyArtifacts) { DefaultIvyArtifactName name = new DefaultIvyArtifactName(dependencyArtifact.getName(), dependencyArtifact.getType(), getExtension(dependencyArtifact), dependencyArtifact.getClassifier()); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java index e37e6db66c6e..d57a944b493e 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java @@ -17,7 +17,6 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder; import com.google.common.collect.Lists; -import org.gradle.api.Transformer; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.component.ComponentSelector; @@ -36,10 +35,11 @@ import org.gradle.internal.component.model.ExcludeMetadata; import org.gradle.internal.component.model.IvyArtifactName; import org.gradle.internal.resolve.ModuleVersionResolveException; -import org.gradle.util.CollectionUtils; import javax.annotation.Nullable; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * Represents the edges in the dependency graph. @@ -347,12 +347,11 @@ public Dependency getOriginalDependency() { @Override public List getArtifacts(final ConfigurationMetadata targetConfiguration) { - return CollectionUtils.collect(dependencyMetadata.getArtifacts(), new Transformer() { - @Override - public ComponentArtifactMetadata transform(IvyArtifactName ivyArtifactName) { - return targetConfiguration.artifact(ivyArtifactName); - } - }); + List artifacts = dependencyMetadata.getArtifacts(); + if (artifacts.isEmpty()) { + return Collections.emptyList(); + } + return artifacts.stream().map(targetConfiguration::artifact).collect(Collectors.toList()); } void maybeDecreaseHardEdgeCount(NodeState removalSource) { 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 4f60af19f970..83bf4913f43d 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 @@ -32,6 +32,7 @@ import org.gradle.util.GUtil; import java.util.Collection; +import java.util.Collections; import java.util.List; public class LocalComponentDependencyMetadata implements LocalOriginDependencyMetadata { @@ -40,7 +41,7 @@ public class LocalComponentDependencyMetadata implements LocalOriginDependencyMe private final String moduleConfiguration; private final String dependencyConfiguration; private final List excludes; - private final ImmutableList artifactNames; + private final List artifactNames; private final boolean force; private final boolean changing; private final boolean transitive; @@ -81,7 +82,7 @@ public LocalComponentDependencyMetadata(ComponentIdentifier componentId, this.moduleAttributes = moduleAttributes; this.dependencyAttributes = ((AttributeContainerInternal)dependencyAttributes).asImmutable(); this.dependencyConfiguration = dependencyConfiguration; - this.artifactNames = ImmutableList.copyOf(artifactNames); + this.artifactNames = asImmutable(artifactNames); this.excludes = excludes; this.force = force; this.changing = changing; @@ -91,6 +92,10 @@ public LocalComponentDependencyMetadata(ComponentIdentifier componentId, this.reason = reason; } + private static List asImmutable(List artifactNames) { + return artifactNames.isEmpty() ? Collections.emptyList() : ImmutableList.copyOf(artifactNames); + } + @Override public String toString() { return "dependency: " + selector + " from-conf: " + moduleConfiguration + " to-conf: " + dependencyConfiguration; From f7020115302e6ecf16e0770ebb11fac9774fc056 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 15:15:20 +0200 Subject: [PATCH 44/55] Use a list instead of a set for dependency reasons Deduplication is done when adding a reason. The rationale is that in most cases, we have a single reason, and it's extremely unlikely to have a large set of reasons. --- .../graph/builder/DependencyState.java | 45 ++++++++++++++----- .../graph/builder/SelectorState.java | 5 +-- .../DefaultComponentSelectionDescriptor.java | 4 +- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java index bbb6c75355dc..cd50740f2256 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java @@ -26,9 +26,10 @@ import org.gradle.internal.resolve.ModuleVersionResolveException; import java.util.List; -import java.util.Set; -import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons.*; +import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons.CONSTRAINT; +import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons.FORCED; +import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons.REQUESTED; class DependencyState { private final ComponentSelector requested; @@ -105,26 +106,50 @@ public boolean isFromLock() { return dependency instanceof LocalOriginDependencyMetadata && ((LocalOriginDependencyMetadata) dependency).isFromLock(); } - void addSelectionReasons(Set reasons) { + void addSelectionReasons(List reasons) { if (reasonsAlreadyAdded) { return; } reasonsAlreadyAdded = true; - String reason = dependency.getReason(); - ComponentSelectionDescriptorInternal dependencyDescriptor = dependency.isConstraint() ? CONSTRAINT : REQUESTED; - if (reason != null) { - dependencyDescriptor = dependencyDescriptor.withDescription(Describables.of(reason)); - } - reasons.add(dependencyDescriptor); + addMainReason(reasons); if (ruleDescriptors != null) { - reasons.addAll(ruleDescriptors); + addRuleDescriptors(reasons); } if (isDependencyForced()) { reasons.add(FORCED); } } + private void addRuleDescriptors(List reasons) { + for (ComponentSelectionDescriptorInternal descriptor : ruleDescriptors) { + maybeAddReason(reasons, descriptor); + } + reasons.addAll(ruleDescriptors); + } + + private void addMainReason(List reasons) { + ComponentSelectionDescriptorInternal dependencyDescriptor = dependency.isConstraint() ? CONSTRAINT : REQUESTED; + String reason = dependency.getReason(); + if (reason != null) { + dependencyDescriptor = dependencyDescriptor.withDescription(Describables.of(reason)); + } + maybeAddReason(reasons, dependencyDescriptor); + } + + private static void maybeAddReason(List reasons, ComponentSelectionDescriptorInternal reason) { + if (reasons.isEmpty()) { + reasons.add(reason); + } else if (isNewReason(reasons, reason)) { + reasons.add(reason); + } + } + + private static boolean isNewReason(List reasons, ComponentSelectionDescriptorInternal reason) { + return (reasons.size() == 1 && !reason.equals(reasons.get(0))) + || !reasons.contains(reason); + } + @Override public boolean equals(Object o) { if (this == o) { 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 48f78e65e6d7..d1990f176ac2 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 @@ -18,7 +18,6 @@ import com.google.common.base.Joiner; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import org.gradle.api.Describable; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.component.ComponentSelector; @@ -45,7 +44,7 @@ import org.gradle.internal.resolve.result.DefaultBuildableComponentIdResolveResult; import java.util.Collection; -import java.util.Set; +import java.util.List; /** * Resolution state for a given module version selector. @@ -63,7 +62,7 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState private final DependencyToComponentIdResolver resolver; private final ResolvedVersionConstraint versionConstraint; private final ImmutableAttributesFactory attributesFactory; - private final Set dependencyReasons = Sets.newLinkedHashSet(); + private final List dependencyReasons = Lists.newArrayListWithExpectedSize(4); private final boolean isProjectSelector; private ComponentIdResolveResult preferResult; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultComponentSelectionDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultComponentSelectionDescriptor.java index aade86d06508..2cad8155c6b1 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultComponentSelectionDescriptor.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultComponentSelectionDescriptor.java @@ -41,9 +41,9 @@ private DefaultComponentSelectionDescriptor(ComponentSelectionCause cause, Descr this.hasCustomDescription = hasCustomDescription; this.isEquivalentToForce = isEquivalentToForce; if (hasCustomDescription) { - this.hashCode = Objects.hashCode(cause, description, isEquivalentToForce); + this.hashCode = 31 * (31 * cause.hashCode() + description.hashCode()) + (isEquivalentToForce ? 1 : 0); } else { - this.hashCode = Objects.hashCode(cause, isEquivalentToForce); + this.hashCode = 31 * cause.hashCode() + (isEquivalentToForce ? 1 : 0); } } From fbf39053094992100f7a0897b807094c70635188 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 17:27:47 +0200 Subject: [PATCH 45/55] Fix NPE: version of capability may be null --- .../component/external/model/ImmutableCapability.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java index 4067e2300ea3..0d894fa50054 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java @@ -47,7 +47,14 @@ public ImmutableCapability(String group, String name, String version) { private int computeHashcode(String group, String name, String version) { // Do NOT change the order of members used in hash code here, it's been empirically // tested to reduce the number of collisions on a large dependency graph (performance test) - return 31 * (31 * version.hashCode() + name.hashCode()) + group.hashCode(); + int hash = safeHash(version); + hash = 31 * hash + name.hashCode(); + hash = 31 * hash + group.hashCode(); + return hash; + } + + private static int safeHash(String o) { + return o == null ? 0 : o.hashCode(); } @Override From 0f5a8654743075261618e18513e6afd4619feb1d Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 18:11:16 +0200 Subject: [PATCH 46/55] Fix duplicate rule descriptors --- .../resolveengine/graph/builder/DependencyState.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java index cd50740f2256..d061f5be1591 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java @@ -117,7 +117,7 @@ void addSelectionReasons(List reasons) { addRuleDescriptors(reasons); } if (isDependencyForced()) { - reasons.add(FORCED); + maybeAddReason(reasons, FORCED); } } @@ -125,7 +125,6 @@ private void addRuleDescriptors(List reaso for (ComponentSelectionDescriptorInternal descriptor : ruleDescriptors) { maybeAddReason(reasons, descriptor); } - reasons.addAll(ruleDescriptors); } private void addMainReason(List reasons) { From 1cc7ed01181cfdbff780272c75d891e170737c1e Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Mon, 6 May 2019 23:08:13 +0200 Subject: [PATCH 47/55] Use cached dependency states for additional constraints --- .../ivyservice/resolveengine/graph/builder/NodeState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 326f35482017..f7115a4a6d02 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -404,7 +404,7 @@ private void createAndLinkEdgeState(DependencyState dependencyState, Collection< private void visitAdditionalConstraints(Collection discoveredEdges) { for (DependencyMetadata dependency : dependencies()) { if (dependency.isConstraint()) { - DependencyState dependencyState = new DependencyState(dependency, resolveState.getComponentSelectorConverter()); + DependencyState dependencyState = dependencyStateCache.computeIfAbsent(dependency, d -> new DependencyState(d, resolveState.getComponentSelectorConverter())); if (upcomingNoLongerPendingConstraints.contains(dependencyState.getModuleIdentifier())) { dependencyState = maybeSubstitute(dependencyState, resolveState.getDependencySubstitutionApplicator()); createAndLinkEdgeState(dependencyState, discoveredEdges, previousTraversalExclusions, false); From be092aa184bd075c3223e5788a2f1014a6a097f3 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Wed, 8 May 2019 14:58:30 +0200 Subject: [PATCH 48/55] Store potentially later activated constraints This is an attempt to optimize performance: instead of iterating over all dependencies, findind constraints, then checking if they belong to the list of newly activated constraints, which can be expensive in case of dependency locking, we now reverse the lookup and for activated constraints, get the list of dependency states which are effectively affected. --- .../graph/builder/NodeState.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index f7115a4a6d02..4a3d398e55f7 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -16,9 +16,11 @@ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import org.gradle.api.Action; import org.gradle.api.artifacts.ModuleIdentifier; @@ -88,6 +90,7 @@ public boolean isSatisfiedBy(EdgeState edge) { private Set upcomingNoLongerPendingConstraints; private boolean virtualPlatformNeedsRefresh; private Set edgesToRecompute; + private Multimap potentiallyActivatedConstraints; // caches private Map dependencyStateCache = Maps.newHashMap(); @@ -266,6 +269,7 @@ public void visitOutgoingDependencies(Collection discoveredEdges) { removeOutgoingEdges(); upcomingNoLongerPendingConstraints = null; edgesToRecompute = null; + potentiallyActivatedConstraints = null; } visitDependencies(resolutionFilter, discoveredEdges); @@ -335,7 +339,12 @@ private void visitDependencies(ExcludeSpec resolutionFilter, Collection discoveredEdges) { - for (DependencyMetadata dependency : dependencies()) { - if (dependency.isConstraint()) { - DependencyState dependencyState = dependencyStateCache.computeIfAbsent(dependency, d -> new DependencyState(d, resolveState.getComponentSelectorConverter())); - if (upcomingNoLongerPendingConstraints.contains(dependencyState.getModuleIdentifier())) { + if (potentiallyActivatedConstraints == null) { + return; + } + for (ModuleIdentifier module : upcomingNoLongerPendingConstraints) { + Collection dependencyStates = potentiallyActivatedConstraints.get(module); + if (!dependencyStates.isEmpty()) { + for (DependencyState dependencyState : dependencyStates) { dependencyState = maybeSubstitute(dependencyState, resolveState.getDependencySubstitutionApplicator()); createAndLinkEdgeState(dependencyState, discoveredEdges, previousTraversalExclusions, false); } + potentiallyActivatedConstraints.removeAll(module); } } upcomingNoLongerPendingConstraints = null; @@ -735,7 +748,7 @@ public void deselect() { void prepareForConstraintNoLongerPending(ModuleIdentifier moduleIdentifier) { if (upcomingNoLongerPendingConstraints == null) { - upcomingNoLongerPendingConstraints = Sets.newHashSet(); + upcomingNoLongerPendingConstraints = Sets.newLinkedHashSet(); } upcomingNoLongerPendingConstraints.add(moduleIdentifier); // Trigger a replay on this node, to add new constraints to graph From 59d2b1fb3b5c94477b413221a3c8e03443c19475 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Thu, 9 May 2019 18:22:39 +0200 Subject: [PATCH 49/55] Fix constraint not being reactivated --- .../resolveengine/graph/builder/EdgeState.java | 6 +++++- .../resolveengine/graph/builder/NodeState.java | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java index d57a944b493e..5d2c4afd8262 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java @@ -138,7 +138,7 @@ public void attachToTargetConfigurations() { ModuleResolveState module = targetComponent.getModule(); if (module.isPending()) { selector.getTargetModule().removeUnattachedDependency(this); - from.getOutgoingEdges().remove(this); + from.makePending(this); module.addPendingNode(from); return; } @@ -373,4 +373,8 @@ public boolean equals(Object o) { public int hashCode() { return hashCode; } + + DependencyState getDependencyState() { + return dependencyState; + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 4a3d398e55f7..3612d9d556be 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -340,10 +340,7 @@ private void visitDependencies(ExcludeSpec resolutionFilter, Collection dependencies() { if (dependenciesMayChange) { cachedDependencyStates = null; @@ -843,4 +847,8 @@ boolean isSelectedByVariantAwareResolution() { return selectedByVariantAwareResolution && isSelected(); } + void makePending(EdgeState edgeState) { + outgoingEdges.remove(edgeState); + registerActivatingConstraint(edgeState.getDependencyState()); + } } From a54fcebffb7dbefa861082d48145f02bdc3c8c49 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Fri, 10 May 2019 18:59:40 +0200 Subject: [PATCH 50/55] Optimize variant transform selection This should reduce the amount of garbage created during selection of artifact transforms. --- .../artifacts/VariantTransformRegistry.java | 4 +- .../AttributeMatchingVariantSelector.java | 3 +- .../ConsumerProvidedVariantFinder.java | 25 ++++--- .../transform/ConsumerVariantMatchResult.java | 18 +++-- .../DefaultVariantTransformRegistry.java | 2 +- ...ttributeMatchingVariantSelectorSpec.groovy | 71 ++++++++++--------- .../ConsumerProvidedVariantFinderTest.groovy | 36 ++++------ .../DefaultArtifactTransformsTest.groovy | 22 +++--- 8 files changed, 95 insertions(+), 86 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/VariantTransformRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/VariantTransformRegistry.java index 50fcb3e30b9a..061de4570efb 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/VariantTransformRegistry.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/VariantTransformRegistry.java @@ -22,6 +22,8 @@ import org.gradle.api.artifacts.transform.TransformSpec; import org.gradle.api.artifacts.transform.VariantTransform; +import java.util.List; + public interface VariantTransformRegistry { /** @@ -33,5 +35,5 @@ public interface VariantTransformRegistry { void registerTransform(Class> actionType, Action> registrationAction); - Iterable getTransforms(); + List getTransforms(); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelector.java index 1f3bde6fc471..9e9a66a01adb 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelector.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelector.java @@ -95,8 +95,7 @@ private ResolvedArtifactSet doSelect(ResolvedVariantSet producer) { List> candidates = new ArrayList>(); for (ResolvedVariant variant : producer.getVariants()) { AttributeContainerInternal variantAttributes = variant.getAttributes().asImmutable(); - ConsumerVariantMatchResult matchResult = new ConsumerVariantMatchResult(); - consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, componentRequested, matchResult); + ConsumerVariantMatchResult matchResult = consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, componentRequested); for (ConsumerVariantMatchResult.ConsumerVariant consumerVariant : matchResult.getMatches()) { candidates.add(Pair.of(variant, consumerVariant)); } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinder.java index f889a2b72cd4..1cd4de98908f 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinder.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinder.java @@ -50,21 +50,20 @@ public ConsumerProvidedVariantFinder(VariantTransformRegistry variantTransforms, this.attributesFactory = attributesFactory; } - public void collectConsumerVariants(AttributeContainerInternal actual, AttributeContainerInternal requested, ConsumerVariantMatchResult result) { + public ConsumerVariantMatchResult collectConsumerVariants(AttributeContainerInternal actual, AttributeContainerInternal requested) { AttributeSpecificCache toCache = getCache(requested); - ConsumerVariantMatchResult cachedResult = toCache.transforms.get(actual); - if (cachedResult == null) { - cachedResult = new ConsumerVariantMatchResult(); - findProducersFor(actual, requested, cachedResult); - toCache.transforms.put(actual, cachedResult); - } - cachedResult.applyTo(result); + return toCache.transforms.computeIfAbsent(actual, attrs -> { + return findProducersFor(actual, requested).asImmutable(); + }); } - private void findProducersFor(AttributeContainerInternal actual, AttributeContainerInternal requested, ConsumerVariantMatchResult result) { + private ConsumerVariantMatchResult findProducersFor(AttributeContainerInternal actual, AttributeContainerInternal requested) { // Prefer direct transformation over indirect transformation List candidates = new ArrayList(); - for (ArtifactTransformRegistration registration : variantTransforms.getTransforms()) { + List transforms = variantTransforms.getTransforms(); + int nbOfTransforms = transforms.size(); + ConsumerVariantMatchResult result = new ConsumerVariantMatchResult(nbOfTransforms * nbOfTransforms); + for (ArtifactTransformRegistration registration : transforms) { if (matchAttributes(registration.getTo(), requested)) { if (matchAttributes(actual, registration.getFrom())) { ImmutableAttributes variantAttributes = attributesFactory.concat(actual.asImmutable(), registration.getTo().asImmutable()); @@ -76,13 +75,12 @@ private void findProducersFor(AttributeContainerInternal actual, AttributeContai } } if (result.hasMatches()) { - return; + return result; } for (ArtifactTransformRegistration candidate : candidates) { - ConsumerVariantMatchResult inputVariants = new ConsumerVariantMatchResult(); AttributeContainerInternal requestedPrevious = computeRequestedAttributes(requested, candidate); - collectConsumerVariants(actual, requestedPrevious, inputVariants); + ConsumerVariantMatchResult inputVariants = collectConsumerVariants(actual, requestedPrevious); if (!inputVariants.hasMatches()) { continue; } @@ -92,6 +90,7 @@ private void findProducersFor(AttributeContainerInternal actual, AttributeContai result.matched(variantAttributes, transformation, inputVariant.depth + 1); } } + return result; } private AttributeContainerInternal computeRequestedAttributes(AttributeContainerInternal result, ArtifactTransformRegistration transform) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerVariantMatchResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerVariantMatchResult.java index 7a225970b930..e58014fe2159 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerVariantMatchResult.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerVariantMatchResult.java @@ -16,19 +16,25 @@ package org.gradle.api.internal.artifacts.transform; +import com.google.common.collect.Lists; import org.gradle.api.internal.attributes.AttributeContainerInternal; import org.gradle.api.internal.attributes.ImmutableAttributes; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; public class ConsumerVariantMatchResult { private int minDepth; - private final List matches = new ArrayList(); + private final List matches; - public void applyTo(ConsumerVariantMatchResult result) { - result.matches.addAll(this.matches); + ConsumerVariantMatchResult(int estimateSize) { + matches = Lists.newArrayListWithExpectedSize(estimateSize); + } + + private ConsumerVariantMatchResult(ConsumerVariantMatchResult other) { + this.minDepth = other.minDepth; + this.matches = Collections.unmodifiableList(other.matches); } public void matched(ImmutableAttributes output, Transformation transformation, int depth) { @@ -52,6 +58,10 @@ public Collection getMatches() { return matches; } + public ConsumerVariantMatchResult asImmutable() { + return new ConsumerVariantMatchResult(this); + } + public static class ConsumerVariant { final AttributeContainerInternal attributes; final Transformation transformation; diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistry.java index 625dffc61b3b..39302d34c59e 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistry.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistry.java @@ -121,7 +121,7 @@ private static void validateAttributes(RecordingRegistration registration) { } } - public Iterable getTransforms() { + public List getTransforms() { return transforms; } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelectorSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelectorSpec.groovy index 3d42c36a43e2..3696ef3b7c42 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelectorSpec.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelectorSpec.groovy @@ -21,6 +21,7 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.Resol import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariantSet import org.gradle.api.internal.attributes.AttributesSchemaInternal +import org.gradle.api.internal.attributes.ImmutableAttributes import org.gradle.api.internal.attributes.ImmutableAttributesFactory import org.gradle.api.internal.tasks.TaskDependencyResolveContext import org.gradle.internal.Describables @@ -137,8 +138,8 @@ class AttributeMatchingVariantSelectorSpec extends Specification { } }) 1 * attributeMatcher.matches(_, _) >> Collections.emptyList() - 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, Mock(Transformation), 1) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, Mock(Transformation), 1) } } @@ -185,11 +186,11 @@ class AttributeMatchingVariantSelectorSpec extends Specification { }) 1 * attributeMatcher.matches(_, _) >> Collections.emptyList() 1 * attributeMatcher.isMatching(requestedAttributes, requestedAttributes) >> true - 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform1, 2) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform1, 2) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform2, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform2, 3) } 1 * attributeMatcher.matches(_, _) >> { args -> args[0] } } @@ -237,11 +238,11 @@ class AttributeMatchingVariantSelectorSpec extends Specification { }) 1 * attributeMatcher.matches(_, _) >> Collections.emptyList() 1 * attributeMatcher.isMatching(requestedAttributes, requestedAttributes) >> true - 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform1, 2) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform1, 2) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform2, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform2, 3) } 1 * attributeMatcher.matches(_, _) >> { args -> args[0] } } @@ -288,11 +289,11 @@ class AttributeMatchingVariantSelectorSpec extends Specification { } }) 1 * attributeMatcher.matches(_, _) >> Collections.emptyList() - 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(otherVariantAttributes, transform1, 2) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes) >> { args -> + match(otherVariantAttributes, transform1, 2) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(variantAttributes, transform2, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes) >> { args -> + match(variantAttributes, transform2, 3) } 1 * attributeMatcher.matches(_, _) >> { args -> [args[0].get(0)] } } @@ -346,14 +347,14 @@ class AttributeMatchingVariantSelectorSpec extends Specification { }) 1 * attributeMatcher.matches(_, _) >> Collections.emptyList() 2 * attributeMatcher.isMatching(requestedAttributes, requestedAttributes) >> true - 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform1, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform1, 3) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform2, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform2, 3) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(yetAnotherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform3, 2) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(yetAnotherVariantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform3, 2) } 1 * attributeMatcher.matches(_, _) >> { args -> args[0] } } @@ -406,14 +407,14 @@ class AttributeMatchingVariantSelectorSpec extends Specification { } }) 1 * attributeMatcher.matches(_, _) >> Collections.emptyList() - 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform1, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform1, 3) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform2, 2) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform2, 2) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(yetAnotherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform3, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(yetAnotherVariantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform3, 3) } 2 * attributeMatcher.isMatching(requestedAttributes, requestedAttributes) >> true 1 * attributeMatcher.matches(_, _) >> { args -> args[0] } @@ -466,16 +467,22 @@ class AttributeMatchingVariantSelectorSpec extends Specification { } }) 1 * attributeMatcher.matches(_, _) >> Collections.emptyList() - 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform1, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(variantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform1, 3) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform2, 2) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(otherVariantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform2, 2) } - 1 * consumerProvidedVariantFinder.collectConsumerVariants(yetAnotherVariantAttributes, requestedAttributes, _) >> { args -> - args[2].matched(requestedAttributes, transform3, 3) + 1 * consumerProvidedVariantFinder.collectConsumerVariants(yetAnotherVariantAttributes, requestedAttributes) >> { args -> + match(requestedAttributes, transform3, 3) } 1 * attributeMatcher.matches(_, _) >> { args -> args[0] } 3 * attributeMatcher.isMatching(requestedAttributes, requestedAttributes) >>> [false, false, true] } + + static ConsumerVariantMatchResult match(ImmutableAttributes output, Transformation trn, int depth) { + def result = new ConsumerVariantMatchResult(2) + result.matched(output, trn, depth) + result + } } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinderTest.groovy index f2445927be05..2fa80c70b26f 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinderTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinderTest.groovy @@ -72,8 +72,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1, reg2, reg3] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: result.matches.size() == 1 @@ -101,8 +100,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1, reg2, reg3] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: result.matches.size() == 2 @@ -130,8 +128,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1, reg2] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: def match = result.matches.first() @@ -147,8 +144,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { 0 * matcher._ when: - def result2 = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result2) + def result2 = matchingCache.collectConsumerVariants(source, requested) then: def match2 = result2.matches.first() @@ -172,8 +168,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1, reg2, reg3] when: - def matchResult = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, matchResult) + def matchResult = matchingCache.collectConsumerVariants(source, requested) then: def transformer = matchResult.matches.first() @@ -216,8 +211,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1, reg2, reg3] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: result.matches.first().transformation.is(reg3.transformationStep) @@ -256,8 +250,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [registrations[registrationsIndex[0]], registrations[registrationsIndex[1]], registrations[registrationsIndex[2]], registrations[registrationsIndex[3]]] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: result.matches.size() == 1 @@ -308,8 +301,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1, reg2, reg3] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: result.matches.size() == 1 @@ -342,8 +334,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1, reg2] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: result.matches.empty @@ -366,8 +357,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1, reg2] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: result.matches.empty @@ -380,8 +370,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { 0 * matcher._ when: - def result2 = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result2) + def result2 = matchingCache.collectConsumerVariants(source, requested) then: result2.matches.empty @@ -406,8 +395,7 @@ class ConsumerProvidedVariantFinderTest extends Specification { transformRegistrations.transforms >> [reg1] when: - def result = new ConsumerVariantMatchResult() - matchingCache.collectConsumerVariants(source, requested, result) + def result = matchingCache.collectConsumerVariants(source, requested) then: result.matches.empty diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformsTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformsTest.groovy index 357bce597aa4..18a84d23db67 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformsTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformsTest.groovy @@ -151,10 +151,10 @@ class DefaultArtifactTransformsTest extends Specification { consumerSchema.withProducer(producerSchema) >> attributeMatcher attributeMatcher.matches(_, _) >> [] - matchingCache.collectConsumerVariants(typeAttributes("jar"), targetAttributes, _) >> { AttributeContainerInternal from, AttributeContainerInternal to, ConsumerVariantMatchResult result -> - result.matched(to, transformation, 1) + matchingCache.collectConsumerVariants(typeAttributes("jar"), targetAttributes) >> { AttributeContainerInternal from, AttributeContainerInternal to -> + match(to, transformation, 1) } - matchingCache.collectConsumerVariants(typeAttributes("dll"), targetAttributes, _) >> { } + matchingCache.collectConsumerVariants(typeAttributes("dll"), targetAttributes) >> { new ConsumerVariantMatchResult(0) } def result = transforms.variantSelector(targetAttributes, true, dependenciesResolver).select(set) when: @@ -207,8 +207,8 @@ class DefaultArtifactTransformsTest extends Specification { consumerSchema.withProducer(producerSchema) >> attributeMatcher attributeMatcher.matches(_, _) >> [] - matchingCache.collectConsumerVariants(_, _, _) >> { AttributeContainerInternal from, AttributeContainerInternal to, ConsumerVariantMatchResult result -> - result.matched(to, Stub(Transformation), 1) + matchingCache.collectConsumerVariants(_, _) >> { AttributeContainerInternal from, AttributeContainerInternal to -> + match(to, Stub(Transformation), 1) } def selector = transforms.variantSelector(typeAttributes("dll"), true, dependenciesResolver) @@ -247,8 +247,7 @@ Found the following transforms: consumerSchema.withProducer(producerSchema) >> attributeMatcher attributeMatcher.matches(_, _) >> [] - matchingCache.collectConsumerVariants(typeAttributes("dll"), typeAttributes("jar"), _) >> null - matchingCache.collectConsumerVariants(typeAttributes("dll"), typeAttributes("classes"), _) >> null + matchingCache.collectConsumerVariants(_, _) >> new ConsumerVariantMatchResult(0) expect: def result = transforms.variantSelector(typeAttributes("dll"), true, dependenciesResolver).select(set) @@ -273,8 +272,7 @@ Found the following transforms: consumerSchema.withProducer(producerSchema) >> attributeMatcher attributeMatcher.matches(_, _) >> [] - matchingCache.collectConsumerVariants(typeAttributes("dll"), typeAttributes("jar"), _) >> null - matchingCache.collectConsumerVariants(typeAttributes("dll"), typeAttributes("classes"), _) >> null + matchingCache.collectConsumerVariants(_, _) >> new ConsumerVariantMatchResult(0) when: def result = transforms.variantSelector(typeAttributes("dll"), false, dependenciesResolver).select(set) @@ -303,5 +301,11 @@ Found the following transforms: attributeContainer.asImmutable() } + static ConsumerVariantMatchResult match(ImmutableAttributes output, Transformation trn, int depth) { + def result = new ConsumerVariantMatchResult(2) + result.matched(output, trn, depth) + result + } + interface TestArtifact extends ResolvableArtifact, Buildable {} } From a9ec4b4afa2a2b2ef208ed7b6d16fa72a6f0c757 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 14 May 2019 10:20:58 +0200 Subject: [PATCH 51/55] Deduplicate attributes when writing result to disk Serializing attributes (and reading them) can be quite expensive. In project like Android projects, there are many attributes serialized and they often have the same values. This prevents overhead by keeping the serialized values de-duplicated. --- .../result/ComponentSelectorSerializer.java | 54 ++++++++++++++++++- .../ComponentSelectorSerializerTest.groovy | 46 ++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.java index 53619b514756..f6138b35159e 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import org.gradle.api.artifacts.VersionConstraint; import org.gradle.api.artifacts.component.BuildIdentifier; import org.gradle.api.artifacts.component.ComponentSelector; @@ -42,13 +43,14 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; public class ComponentSelectorSerializer extends AbstractSerializer { private final AttributeContainerSerializer attributeContainerSerializer; private final BuildIdentifierSerializer buildIdentifierSerializer; public ComponentSelectorSerializer(AttributeContainerSerializer attributeContainerSerializer) { - this.attributeContainerSerializer = attributeContainerSerializer; + this.attributeContainerSerializer = new OptimizingAttributeContainerSerializer(attributeContainerSerializer); this.buildIdentifierSerializer = new BuildIdentifierSerializer(); } @@ -226,4 +228,54 @@ byte getId() { return id; } } + + private static class OptimizingAttributeContainerSerializer implements AttributeContainerSerializer { + // We use an identity hashmap for performance, because we know that our attributes + // are generated by a factory which guarantees same instances + private final Map writeIndex = Maps.newIdentityHashMap(); + private final List readIndex = Lists.newArrayList(); + private final AttributeContainerSerializer delegate; + + private OptimizingAttributeContainerSerializer(AttributeContainerSerializer delegate) { + this.delegate = delegate; + } + + @Override + public ImmutableAttributes read(Decoder decoder) throws IOException { + boolean empty = decoder.readBoolean(); + if (empty) { + return ImmutableAttributes.EMPTY; + } + int idx = decoder.readSmallInt(); + ImmutableAttributes attributes; + if (idx == readIndex.size()) { + // new entry + attributes = delegate.read(decoder); + readIndex.add(attributes); + } else { + attributes = readIndex.get(idx); + } + return attributes; + } + + @Override + public void write(Encoder encoder, AttributeContainer container) throws IOException { + if (container.isEmpty()) { + encoder.writeBoolean(true); + return; + } else { + encoder.writeBoolean(false); + } + Integer idx = writeIndex.get(container); + if (idx == null) { + // new value + encoder.writeSmallInt(writeIndex.size()); + writeIndex.put(container, writeIndex.size()); + delegate.write(encoder, container); + } else { + // known value, only write index + encoder.writeSmallInt(idx); + } + } + } } diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy index 7ee5d26075e0..bb91e328c90d 100644 --- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy @@ -211,6 +211,52 @@ class ComponentSelectorSerializerTest extends SerializerSpec { result.versionConstraint.rejectedVersions == ['rej'] } + def "serializes attributes"() { + given: + + ModuleComponentSelector selector1 = DefaultModuleComponentSelector.newSelector( + DefaultModuleIdentifier.newId('group1', 'name1'), constraint('1.0'), AttributeTestUtil.attributes("foo": "val1"), []) + ModuleComponentSelector selector2 = DefaultModuleComponentSelector.newSelector( + DefaultModuleIdentifier.newId('group2', 'name2'), constraint('1.0'), AttributeTestUtil.attributes("foo": "val2"), []) + ModuleComponentSelector selector3 = DefaultModuleComponentSelector.newSelector( + DefaultModuleIdentifier.newId('group3', 'name3'), constraint('1.0'), AttributeTestUtil.attributes("foo": "val1"), []) + + when: + ModuleComponentSelector result1 = serialize(selector1, serializer) + ModuleComponentSelector result2 = serialize(selector2, serializer) + ModuleComponentSelector result3 = serialize(selector3, serializer) + + then: + result1.attributes == AttributeTestUtil.attributes("foo": "val1") + result2.attributes == AttributeTestUtil.attributes("foo": "val2") + result3.attributes == AttributeTestUtil.attributes("foo": "val1") + + } + + def "de-duplicates attributes"() { + def factory = AttributeTestUtil.attributesFactory() + def attr = Attribute.of("foo", String) + + given: + ModuleComponentSelector selector1 = DefaultModuleComponentSelector.newSelector( + DefaultModuleIdentifier.newId('group1', 'name1'), constraint('1.0'), factory.of(attr, "val1"), []) + ModuleComponentSelector selector2 = DefaultModuleComponentSelector.newSelector( + DefaultModuleIdentifier.newId('group2', 'name2'), constraint('1.0'), factory.of(attr, "val2"), []) + ModuleComponentSelector selector3 = DefaultModuleComponentSelector.newSelector( + DefaultModuleIdentifier.newId('group3', 'name3'), constraint('1.0'), factory.of(attr, "val1"), []) + + when: + byte[] result1 = toBytes(selector1, serializer) + byte[] result2 = toBytes(selector2, serializer) + byte[] result3 = toBytes(selector3, serializer) + + then: + result2.length == result1.length // different attributes + result3.length < result1.length // already seen + + } + + private static List capabilities() { [new ImmutableCapability("org", "foo", "${Math.random()}"), new ImmutableCapability("org", "bar", "${Math.random()}")] } From 5a1ba040964eeec5d1372742c3c241f0ab96565c Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 14 May 2019 14:09:17 +0200 Subject: [PATCH 52/55] Cache desugaring of attributes Desugaring can be expensive, and this commit reduces the cost by making sure we don't desugar the same instance of attributes twice. It makes use of the fact we have an immutable attributes factory which returns always the same instance of attributes. --- .../graph/builder/AttributeDesugaring.java | 45 ++++++++++++------- .../graph/builder/ComponentState.java | 2 +- .../graph/builder/DependencyGraphBuilder.java | 2 +- .../graph/builder/EdgeState.java | 2 +- .../graph/builder/NodeState.java | 13 +++--- .../graph/builder/ResolveState.java | 24 ++++++---- .../graph/builder/SelectorState.java | 11 ++--- 7 files changed, 55 insertions(+), 44 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/AttributeDesugaring.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/AttributeDesugaring.java index 8b3d5da6ced7..fe9e78c31955 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/AttributeDesugaring.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/AttributeDesugaring.java @@ -15,6 +15,7 @@ */ package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder; +import com.google.common.collect.Maps; import org.gradle.api.artifacts.component.ComponentSelector; import org.gradle.api.artifacts.component.ModuleComponentSelector; import org.gradle.api.attributes.Attribute; @@ -26,40 +27,50 @@ import org.gradle.internal.component.external.model.DefaultModuleComponentSelector; import org.gradle.internal.component.local.model.DefaultProjectComponentSelector; +import java.util.Map; import java.util.Set; -abstract class AttributeDesugaring { +class AttributeDesugaring { + private final Map desugared = Maps.newIdentityHashMap(); + private final ImmutableAttributesFactory attributesFactory; + + AttributeDesugaring(ImmutableAttributesFactory attributesFactory) { + this.attributesFactory = attributesFactory; + } + /** * Desugars attributes so that what we're going to serialize consists only of String or Boolean attributes, * and not their original types. * @return desugared attributes */ - static ImmutableAttributes desugar(ImmutableAttributes attributes, ImmutableAttributesFactory attributesFactory) { + ImmutableAttributes desugar(ImmutableAttributes attributes) { if (attributes.isEmpty()) { return attributes; } - AttributeContainerInternal mutable = attributesFactory.mutable(); - Set> keySet = attributes.keySet(); - for (Attribute attribute : keySet) { - Object value = attributes.getAttribute(attribute); - Attribute desugared = Cast.uncheckedCast(attribute); - if (attribute.getType() == Boolean.class || attribute.getType() == String.class) { - mutable.attribute(desugared, value); - } else { - desugared = Cast.uncheckedCast(Attribute.of(attribute.getName(), String.class)); - mutable.attribute(desugared, value.toString()); + return desugared.computeIfAbsent(attributes, key -> { + AttributeContainerInternal mutable = attributesFactory.mutable(); + Set> keySet = key.keySet(); + for (Attribute attribute : keySet) { + Object value = key.getAttribute(attribute); + Attribute desugared = Cast.uncheckedCast(attribute); + if (attribute.getType() == Boolean.class || attribute.getType() == String.class) { + mutable.attribute(desugared, value); + } else { + desugared = Cast.uncheckedCast(Attribute.of(attribute.getName(), String.class)); + mutable.attribute(desugared, value.toString()); + } } - } - return mutable.asImmutable(); + return mutable.asImmutable(); + }); } - static ComponentSelector desugarSelector(ComponentSelector selector, ImmutableAttributesFactory attributesFactory) { + ComponentSelector desugarSelector(ComponentSelector selector) { if (selector instanceof ModuleComponentSelector) { ModuleComponentSelector module = (ModuleComponentSelector) selector; AttributeContainer moduleAttributes = module.getAttributes(); if (!moduleAttributes.isEmpty()) { ImmutableAttributes attributes = ((AttributeContainerInternal) moduleAttributes).asImmutable(); - return DefaultModuleComponentSelector.newSelector(module.getModuleIdentifier(), module.getVersionConstraint(), desugar(attributes, attributesFactory), module.getRequestedCapabilities()); + return DefaultModuleComponentSelector.newSelector(module.getModuleIdentifier(), module.getVersionConstraint(), desugar(attributes), module.getRequestedCapabilities()); } } if (selector instanceof DefaultProjectComponentSelector) { @@ -67,7 +78,7 @@ static ComponentSelector desugarSelector(ComponentSelector selector, ImmutableAt AttributeContainer projectAttributes = project.getAttributes(); if (!projectAttributes.isEmpty()) { ImmutableAttributes attributes = ((AttributeContainerInternal) projectAttributes).asImmutable(); - return new DefaultProjectComponentSelector(project.getBuildIdentifier(), project.getIdentityPath(), project.projectPath(), project.getProjectName(), desugar(attributes, attributesFactory), project.getRequestedCapabilities()); + return new DefaultProjectComponentSelector(project.getBuildIdentifier(), project.getIdentityPath(), project.projectPath(), project.getProjectName(), desugar(attributes), project.getRequestedCapabilities()); } } return selector; 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 aa820623c32b..ef98ddcb1a40 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 @@ -279,7 +279,7 @@ public List getResolvedVariants() { ConfigurationMetadata metadata = node.getMetadata(); DisplayName name = Describables.of(metadata.getName()); List capabilities = metadata.getCapabilities().getCapabilities(); - AttributeContainer attributes = AttributeDesugaring.desugar(metadata.getAttributes(), node.getAttributesFactory()); + AttributeContainer attributes = node.desugar(metadata.getAttributes()); List resolvedVariantCapabilities = capabilities.isEmpty() ? Collections.singletonList(implicitCapability) : ImmutableList.copyOf(capabilities); ResolvedVariantDetails details = new DefaultVariantDetails(name, attributes, resolvedVariantCapabilities); if (result != null) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyGraphBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyGraphBuilder.java index d4a44fed8b44..7ce21245ef21 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyGraphBuilder.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyGraphBuilder.java @@ -135,7 +135,7 @@ public void resolve(final ResolveContext resolveContext, final DependencyGraphVi moduleResolver.resolve(resolveContext, rootModule); int graphSize = estimateSize(resolveContext); - final ResolveState resolveState = new ResolveState(idGenerator, rootModule, resolveContext.getName(), idResolver, metaDataResolver, edgeFilter, attributesSchema, moduleExclusions, moduleReplacementsData, componentSelectorConverter, attributesFactory, dependencySubstitutionApplicator, versionSelectorScheme, versionComparator, versionParser, moduleConflictHandler.getResolver(), graphSize); + final ResolveState resolveState = new ResolveState(idGenerator, rootModule, resolveContext.getName(), idResolver, metaDataResolver, edgeFilter, attributesSchema, moduleExclusions, componentSelectorConverter, attributesFactory, dependencySubstitutionApplicator, versionSelectorScheme, versionComparator, versionParser, moduleConflictHandler.getResolver(), graphSize); Map componentIdentifierCache = Maps.newHashMapWithExpectedSize(graphSize/2); traverseGraph(resolveState, componentIdentifierCache); diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java index 5d2c4afd8262..1425a9796c1a 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java @@ -297,7 +297,7 @@ public boolean contributesArtifacts() { @Override public ComponentSelector getRequested() { - return AttributeDesugaring.desugarSelector(dependencyState.getRequested(), from.getAttributesFactory()); + return resolveState.desugarSelector(dependencyState.getRequested()); } @Override diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 3612d9d556be..11a8bac58c58 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -34,8 +34,8 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode; +import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.api.internal.attributes.ImmutableAttributesFactory; -import org.gradle.api.specs.Spec; import org.gradle.internal.component.external.model.DefaultModuleComponentSelector; import org.gradle.internal.component.external.model.ShadowedCapability; import org.gradle.internal.component.local.model.LocalConfigurationMetadata; @@ -60,13 +60,6 @@ */ public class NodeState implements DependencyGraphNode { private static final Logger LOGGER = LoggerFactory.getLogger(DependencyGraphBuilder.class); - private static final Spec TRANSITIVE_EDGES = new Spec() { - @Override - public boolean isSatisfiedBy(EdgeState edge) { - return edge.isTransitive(); - } - }; - private final Long resultId; private final ComponentState component; private final List incomingEdges = Lists.newArrayList(); @@ -851,4 +844,8 @@ void makePending(EdgeState edgeState) { outgoingEdges.remove(edgeState); registerActivatingConstraint(edgeState.getDependencyState()); } + + ImmutableAttributes desugar(ImmutableAttributes attributes) { + return resolveState.desugar(attributes); + } } diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java index bea682f1a507..2bffd6420025 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java @@ -27,7 +27,6 @@ import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier; import org.gradle.api.internal.artifacts.ResolvedVersionConstraint; import org.gradle.api.internal.artifacts.dependencies.DefaultResolvedVersionConstraint; -import org.gradle.api.internal.artifacts.dsl.ModuleReplacementsData; import org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution.DependencySubstitutionApplicator; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.Version; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser; @@ -37,6 +36,7 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ComponentStateFactory; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolver; import org.gradle.api.internal.attributes.AttributesSchemaInternal; +import org.gradle.api.internal.attributes.ImmutableAttributes; import org.gradle.api.internal.attributes.ImmutableAttributesFactory; import org.gradle.api.specs.Spec; import org.gradle.internal.component.model.ComponentResolveMetadata; @@ -71,7 +71,6 @@ class ResolveState implements ComponentStateFactory { private final ModuleExclusions moduleExclusions; private final DeselectVersionAction deselectVersionAction = new DeselectVersionAction(this); private final ReplaceSelectionWithConflictResultAction replaceSelectionWithConflictResultAction; - private final ModuleReplacementsData moduleReplacementsData; private final ComponentSelectorConverter componentSelectorConverter; private final ImmutableAttributesFactory attributesFactory; private final DependencySubstitutionApplicator dependencySubstitutionApplicator; @@ -81,10 +80,11 @@ class ResolveState implements ComponentStateFactory { private final SelectorStateResolver selectorStateResolver; private final ResolveOptimizations resolveOptimizations; private final Map resolvedVersionConstraints = Maps.newHashMap(); + private final AttributeDesugaring attributeDesugaring; public ResolveState(IdGenerator idGenerator, ComponentResolveResult rootResult, String rootConfigurationName, DependencyToComponentIdResolver idResolver, ComponentMetaDataResolver metaDataResolver, Spec edgeFilter, AttributesSchemaInternal attributesSchema, - ModuleExclusions moduleExclusions, ModuleReplacementsData moduleReplacementsData, + ModuleExclusions moduleExclusions, ComponentSelectorConverter componentSelectorConverter, ImmutableAttributesFactory attributesFactory, DependencySubstitutionApplicator dependencySubstitutionApplicator, VersionSelectorScheme versionSelectorScheme, Comparator versionComparator, VersionParser versionParser, ModuleConflictResolver conflictResolver, @@ -95,7 +95,6 @@ public ResolveState(IdGenerator idGenerator, ComponentResolveResult rootRe this.edgeFilter = edgeFilter; this.attributesSchema = attributesSchema; this.moduleExclusions = moduleExclusions; - this.moduleReplacementsData = moduleReplacementsData; this.componentSelectorConverter = componentSelectorConverter; this.attributesFactory = attributesFactory; this.dependencySubstitutionApplicator = dependencySubstitutionApplicator; @@ -107,6 +106,7 @@ public ResolveState(IdGenerator idGenerator, ComponentResolveResult rootRe this.selectors = new LinkedHashMap(5 * graphSize / 2); this.queue = new ArrayDeque(graphSize); this.resolveOptimizations = new ResolveOptimizations(); + this.attributeDesugaring = new AttributeDesugaring(attributesFactory); ComponentState rootVersion = getRevision(rootResult.getId(), rootResult.getModuleVersionId(), rootResult.getMetadata()); final ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier(rootVersion.getId(), rootConfigurationName); ConfigurationMetadata configurationMetadata = rootVersion.getMetadata().getConfiguration(id.getConfiguration()); @@ -215,10 +215,6 @@ public ReplaceSelectionWithConflictResultAction getReplaceSelectionWithConflictR return replaceSelectionWithConflictResultAction; } - public ModuleReplacementsData getModuleReplacementsData() { - return moduleReplacementsData; - } - public ComponentSelectorConverter getComponentSelectorConverter() { return componentSelectorConverter; } @@ -242,4 +238,16 @@ ResolvedVersionConstraint resolveVersionConstraint(ComponentSelector selector) { } return null; } + + ImmutableAttributes desugar(ImmutableAttributes attributes) { + return attributeDesugaring.desugar(attributes); + } + + ComponentSelector desugarSelector(ComponentSelector requested) { + return attributeDesugaring.desugarSelector(requested); + } + + AttributeDesugaring getAttributeDesugaring() { + return attributeDesugaring; + } } 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 d1990f176ac2..9f1478ec7445 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 @@ -30,7 +30,6 @@ 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; -import org.gradle.api.internal.attributes.ImmutableAttributesFactory; import org.gradle.internal.component.model.DependencyMetadata; import org.gradle.internal.logging.text.TreeFormatter; import org.gradle.internal.resolve.ModuleVersionResolveException; @@ -61,9 +60,9 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState private final DependencyMetadata firstSeenDependency; private final DependencyToComponentIdResolver resolver; private final ResolvedVersionConstraint versionConstraint; - private final ImmutableAttributesFactory attributesFactory; private final List dependencyReasons = Lists.newArrayListWithExpectedSize(4); private final boolean isProjectSelector; + private final AttributeDesugaring attributeDesugaring; private ComponentIdResolveResult preferResult; private ComponentIdResolveResult requireResult; @@ -86,12 +85,12 @@ class SelectorState implements DependencyGraphSelector, ResolvableSelectorState this.id = id; this.resolver = resolver; this.targetModule = resolveState.getModule(targetModuleId); - this.attributesFactory = resolveState.getAttributesFactory(); update(dependencyState); this.dependencyState = dependencyState; this.firstSeenDependency = dependencyState.getDependency(); this.versionConstraint = resolveState.resolveVersionConstraint(firstSeenDependency.getSelector()); this.isProjectSelector = getSelector() instanceof ProjectComponentSelector; + this.attributeDesugaring = resolveState.getAttributeDesugaring(); } @Override @@ -132,7 +131,7 @@ public String toString() { @Override public ComponentSelector getRequested() { - return selectorWithDesugaredAttributes(dependencyState.getRequested()); + return attributeDesugaring.desugarSelector(dependencyState.getRequested()); } public ModuleResolveState getTargetModule() { @@ -312,10 +311,6 @@ public boolean isFromLock() { return fromLock; } - private ComponentSelector selectorWithDesugaredAttributes(ComponentSelector selector) { - return AttributeDesugaring.desugarSelector(selector, attributesFactory); - } - public void update(DependencyState dependencyState) { if (dependencyState != this.dependencyState) { if (!forced && dependencyState.isForced()) { From 6181cf10f40ac1b1ad35db68d82709dcd815149d Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 14 May 2019 15:24:41 +0200 Subject: [PATCH 53/55] Cache more than one query in component attribute matching Component attribute matching is the most expensive part of attribute selection whenever there's an ambiguity. In Android, that's very often the case. This commit changes the caching code to use more than one (actually cache all) query cache in order to try to reduce selection duration. --- .../model/ComponentAttributeMatcher.java | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentAttributeMatcher.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentAttributeMatcher.java index 7a9e59411855..440b04bfff02 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentAttributeMatcher.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentAttributeMatcher.java @@ -15,10 +15,10 @@ */ package org.gradle.internal.component.model; -import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import org.gradle.api.attributes.Attribute; import org.gradle.api.attributes.HasAttributes; import org.gradle.api.internal.attributes.AttributeContainerInternal; @@ -33,6 +33,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; /** * An attribute matcher, which optimizes for the case of only comparing 0 or 1 candidates and delegates to {@link MultipleCandidateMatcher} for all other cases. @@ -45,7 +46,7 @@ public class ComponentAttributeMatcher { * cache the result of the query, because it's often the case that we ask for the same * disambiguation of attributes several times in a row (but with different candidates). */ - private CachedQuery lastQuery; + private final Map cachedQueries = Maps.newConcurrentMap(); /** * Determines whether the given candidate is compatible with the requested criteria, according to the given schema. @@ -130,10 +131,9 @@ public List match(AttributeSelectionSchema schema, ImmutableAttributes requestedAttributes = requested.asImmutable(); CachedQuery query = CachedQuery.of(schema, requestedAttributes, candidates); - // Copy to local variable because the component attribute matcher can be accessed concurrently - CachedQuery lastQuery = this.lastQuery; - if (query.equals(lastQuery)) { - return lastQuery.select(candidates); + int[] index = cachedQueries.get(query); + if (index != null) { + return CachedQuery.select(index, candidates); } List matches = new MultipleCandidateMatcher(schema, candidates, requestedAttributes).getMatches(); if (LOGGER.isDebugEnabled()) { @@ -166,21 +166,29 @@ private synchronized void cacheMatchingResult(Collecti j++; } } - query.index = queryResult; - lastQuery = query; + cachedQueries.put(query, queryResult); } private static class CachedQuery { private final AttributeSelectionSchema schema; private final ImmutableAttributes requestedAttributes; private final ImmutableAttributes[] candidates; - - private volatile int[] index; + private final int hashCode; private CachedQuery(AttributeSelectionSchema schema, ImmutableAttributes requestedAttributes, ImmutableAttributes[] candidates) { this.schema = schema; this.requestedAttributes = requestedAttributes; this.candidates = candidates; + this.hashCode = computeHashCode(schema, requestedAttributes, candidates); + } + + private int computeHashCode(AttributeSelectionSchema schema, ImmutableAttributes requestedAttributes, ImmutableAttributes[] candidates) { + int hash = schema.hashCode(); + hash = 31 * hash + requestedAttributes.hashCode(); + for (ImmutableAttributes candidate : candidates) { + hash = 31 * hash + candidate.hashCode(); + } + return hash; } public static CachedQuery of(AttributeSelectionSchema schema, ImmutableAttributes requestedAttributes, Collection candidates) { @@ -192,7 +200,7 @@ public static CachedQuery of(AttributeSelectionSchema return new CachedQuery(schema, requestedAttributes, attributes); } - public List select(Collection unfiltered) { + public static List select(int[] index, Collection unfiltered) { if (index.length == 0) { return Collections.emptyList(); } @@ -222,14 +230,23 @@ public boolean equals(Object o) { return false; } CachedQuery that = (CachedQuery) o; - return schema.equals(that.schema) && + return hashCode == that.hashCode && + schema.equals(that.schema) && requestedAttributes.equals(that.requestedAttributes) && Arrays.equals(candidates, that.candidates); } @Override public int hashCode() { - return Objects.hashCode(schema, requestedAttributes, candidates); + return hashCode; + } + + @Override + public String toString() { + return "CachedQuery{" + + "requestedAttributes=" + requestedAttributes + + ", candidates=" + Arrays.toString(candidates) + + '}'; } } } From 867de479f1c3b7df3c77f363e874ad297b9aca00 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 14 May 2019 19:06:43 +0200 Subject: [PATCH 54/55] Optimize zip/tar visit Instead of checking for **every** file entry if the file is new, and then copy it if it's missing, we only need to do this once, at the top-level exploded directory: this saves a lot of "file.exist" calls, which can be quite expensive. --- .../gradle/api/internal/file/archive/TarFileTree.java | 11 +++++++---- .../gradle/api/internal/file/archive/ZipFileTree.java | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/TarFileTree.java b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/TarFileTree.java index 899b53c0b962..36e13757826c 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/TarFileTree.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/TarFileTree.java @@ -104,11 +104,12 @@ private void visitImpl(FileVisitor visitor, InputStream inputStream) throws IOEx NoCloseTarInputStream tar = new NoCloseTarInputStream(inputStream); TarEntry entry; File expandedDir = getExpandedDir(); + boolean isFirstTimeVisit = !expandedDir.exists(); while (!stopFlag.get() && (entry = tar.getNextEntry()) != null) { if (entry.isDirectory()) { - visitor.visitDir(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod)); + visitor.visitDir(new DetailsImpl(resource, isFirstTimeVisit, expandedDir, entry, tar, stopFlag, chmod)); } else { - visitor.visitFile(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod)); + visitor.visitFile(new DetailsImpl(resource, isFirstTimeVisit, expandedDir, entry, tar, stopFlag, chmod)); } } } @@ -189,13 +190,15 @@ private static class DetailsImpl extends AbstractFileTreeElement implements File private final NoCloseTarInputStream tar; private final AtomicBoolean stopFlag; private final ReadableResourceInternal resource; + private final boolean isFirstTimeVisit; private final File expandedDir; private File file; private boolean read; - public DetailsImpl(ReadableResourceInternal resource, File expandedDir, TarEntry entry, NoCloseTarInputStream tar, AtomicBoolean stopFlag, Chmod chmod) { + public DetailsImpl(ReadableResourceInternal resource, boolean isFirstTimeVisit, File expandedDir, TarEntry entry, NoCloseTarInputStream tar, AtomicBoolean stopFlag, Chmod chmod) { super(chmod); this.resource = resource; + this.isFirstTimeVisit = isFirstTimeVisit; this.expandedDir = expandedDir; this.entry = entry; this.tar = tar; @@ -213,7 +216,7 @@ public void stopVisiting() { public File getFile() { if (file == null) { file = new File(expandedDir, entry.getName()); - if (!file.exists()) { + if (isFirstTimeVisit) { copyTo(file); } } diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipFileTree.java b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipFileTree.java index 2ea5236c6318..a70c1b55ac3f 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipFileTree.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipFileTree.java @@ -79,6 +79,7 @@ public void visit(FileVisitor visitor) { try { ZipFile zip = new ZipFile(zipFile); File expandedDir = getExpandedDir(); + boolean isFirstTimeVisit = !expandedDir.exists(); try { // The iteration order of zip.getEntries() is based on the hash of the zip entry. This isn't much use // to us. So, collect the entries in a map and iterate over them in alphabetical order. @@ -92,9 +93,9 @@ public void visit(FileVisitor visitor) { while (!stopFlag.get() && sortedEntries.hasNext()) { ZipEntry entry = sortedEntries.next(); if (entry.isDirectory()) { - visitor.visitDir(new DetailsImpl(zipFile, expandedDir, entry, zip, stopFlag, chmod)); + visitor.visitDir(new DetailsImpl(zipFile, isFirstTimeVisit, expandedDir, entry, zip, stopFlag, chmod)); } else { - visitor.visitFile(new DetailsImpl(zipFile, expandedDir, entry, zip, stopFlag, chmod)); + visitor.visitFile(new DetailsImpl(zipFile, isFirstTimeVisit, expandedDir, entry, zip, stopFlag, chmod)); } } } finally { @@ -116,15 +117,17 @@ private File getExpandedDir() { private static class DetailsImpl extends AbstractFileTreeElement implements FileVisitDetails { private final File originalFile; + private final boolean isFirstTimeVisit; private final File expandedDir; private final ZipEntry entry; private final ZipFile zip; private final AtomicBoolean stopFlag; private File file; - public DetailsImpl(File originalFile, File expandedDir, ZipEntry entry, ZipFile zip, AtomicBoolean stopFlag, Chmod chmod) { + public DetailsImpl(File originalFile, boolean isFirstTimeVisit, File expandedDir, ZipEntry entry, ZipFile zip, AtomicBoolean stopFlag, Chmod chmod) { super(chmod); this.originalFile = originalFile; + this.isFirstTimeVisit = isFirstTimeVisit; this.expandedDir = expandedDir; this.entry = entry; this.zip = zip; @@ -142,7 +145,7 @@ public void stopVisiting() { public File getFile() { if (file == null) { file = new File(expandedDir, entry.getName()); - if (!file.exists()) { + if (isFirstTimeVisit) { copyTo(file); } } From a6577d6cd76775b9d557de31725f8d551d37047d Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 14 May 2019 23:07:59 +0200 Subject: [PATCH 55/55] Always add constraints to potentially activating list This fixes the Gradleception build. --- .../ivyservice/resolveengine/graph/builder/NodeState.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java index 11a8bac58c58..b01bc7cbc134 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java @@ -332,9 +332,10 @@ private void visitDependencies(ExcludeSpec resolutionFilter, Collection discoveredEdges) { dependencyState = maybeSubstitute(dependencyState, resolveState.getDependencySubstitutionApplicator()); createAndLinkEdgeState(dependencyState, discoveredEdges, previousTraversalExclusions, false); } - potentiallyActivatedConstraints.removeAll(module); } } upcomingNoLongerPendingConstraints = null;