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); } } 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/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/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/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/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/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/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/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/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/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/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/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/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/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) { 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/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/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..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 @@ -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,108 @@ * 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.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.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.Collection; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; -import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.AbstractModuleExclusion.isWildcard; - -/** - * 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) { + 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 TL operations + new NormalizingExcludeFactory(// performs algebra + 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(); + private final ExcludeSpec nothing; + + public ModuleExclusions() { + nothing = factory.nothing(); + } + + public ExcludeSpec excludeAny(Collection 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; + // avoids creation of empty hashset + return nothing; } - if (one == EXCLUDE_NONE) { - return two; + if (excludes.size() == 1) { + return forExclude(excludes.iterator().next()); } - 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)); + return factory.anyOf(excludes.stream() + .map(this::forExclude) + .collect(Collectors.toSet())); } - private static int estimateSize(AbstractModuleExclusion ex) { - if (ex instanceof AbstractCompositeExclusion) { - return ((AbstractCompositeExclusion) ex).getFilters().size(); - } - return 1; + public ExcludeSpec nothing() { + return nothing; } - /** - * 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; - } - } - 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; + 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()); } - 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); + ModuleIdentifier moduleId = rule.getModuleId(); + IvyArtifactName artifact = rule.getArtifact(); + boolean anyOrganisation = isWildcard(moduleId.getGroup()); + boolean anyModule = isWildcard(moduleId.getName()); - } - 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); + // 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(); } + } else { + return factory.ivyPatternExclude(moduleId, artifact, rule.getMatcher()); } - } - 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; + private static boolean isWildcard(String attribute) { + return PatternMatchers.ANY_EXPRESSION.equals(attribute); } - // 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); - } - } 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 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)); - } + public ExcludeSpec excludeAll(ExcludeSpec one, ExcludeSpec two) { + return factory.allOf(one, two); } - 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 excludeAll(Set specs) { + return factory.allOf(specs); } - 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; - } - } - - 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); - } + 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/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..370804a41ec8 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/CachingExcludeFactory.java @@ -0,0 +1,191 @@ +/* + * 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.ExcludeSpec; + +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +/** + * 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 MergeCaches caches; + + public CachingExcludeFactory(ExcludeFactory delegate, MergeCaches caches) { + super(delegate); + this.caches = caches; + } + + @Override + public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { + return cachedAnyPair(one, two); + } + + private ExcludeSpec cachedAnyPair(ExcludeSpec left, ExcludeSpec right) { + return caches.getAnyPair(ExcludePair.of(left, right), key -> delegate.anyOf(key.left, key.right)); + } + + @Override + public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { + return caches.getAllPair(ExcludePair.of(one, two), key -> delegate.allOf(key.left, key.right)); + } + + @Override + public ExcludeSpec anyOf(Set specs) { + return caches.getAnyOf(new ExcludesKey(specs), key -> delegate.anyOf(key.specs)); + } + + @Override + public ExcludeSpec allOf(Set specs) { + return caches.getAllOf(new ExcludesKey(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; + + // 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) { + this.left = left; + this.right = right; + this.hashCode = 31 * left.hashCode() + right.hashCode(); + } + + @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); + } + + @Override + public int hashCode() { + return hashCode; + } + } + + /** + * A special exclude spec list key which recognizes + * that union and intersection are commutative. + */ + private static class ExcludesKey { + private final Set specs; + private final int size; + private final int hashCode; + + private ExcludesKey(Set specs) { + this.specs = specs; + this.size = specs.size(); + this.hashCode = specs.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ExcludesKey that = (ExcludesKey) o; + if (size != that.size) { + return false; + } + return specs.equals(that.specs); + } + + @Override + public int hashCode() { + return hashCode; + } + } + + /** + * A shareable backing cache for different caching exclude factories. + * Synchronization is ad-hoc, since `computeIfAbsent` on a concurrent hash map + * will not allow for recursion, which is the case for us whenever a cache is + * found at different levels. + */ + 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(); + + ExcludeSpec getAnyPair(ExcludePair pair, Function onMiss) { + return anyOfPairCache.computeIfAbsent(pair, onMiss); + } + + ExcludeSpec getAllPair(ExcludePair pair, Function onMiss) { + return allOfPairCache.computeIfAbsent(pair, onMiss); + } + + ExcludeSpec getAnyOf(ExcludesKey list, Function onMiss) { + return anyOfListCache.computeIfAbsent(list, onMiss); + } + + ExcludeSpec getAllOf(ExcludesKey list, Function onMiss) { + return allOfListCache.computeIfAbsent(list, onMiss); + } + } + + private static class ConcurrentCache { + private final Map backingMap = Maps.newHashMap(); + + static ConcurrentCache of() { + return new ConcurrentCache<>(); + } + + V computeIfAbsent(K key, Function producer) { + synchronized (backingMap) { + V value = backingMap.get(key); + if (value != null) { + return value; + } + value = producer.apply(key); + backingMap.put(key, value); + return value; + } + } + } +} 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..821d0c2ae346 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/DelegatingExcludeFactory.java @@ -0,0 +1,109 @@ +/* + * 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.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.Set; + +public abstract class DelegatingExcludeFactory implements ExcludeFactory { + protected final ExcludeFactory delegate; + + DelegatingExcludeFactory(ExcludeFactory delegate) { + this.delegate = delegate; + } + + @Override + public ExcludeNothing nothing() { + return delegate.nothing(); + } + + @Override + public ExcludeEverything everything() { + return delegate.everything(); + } + + @Override + public GroupExclude group(String group) { + return delegate.group(group); + } + + @Override + public ModuleExclude module(String module) { + return delegate.module(module); + } + + @Override + public ModuleIdExclude moduleId(ModuleIdentifier id) { + return delegate.moduleId(id); + } + + @Override + public ArtifactExclude 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(Set specs) { + return delegate.anyOf(specs); + } + + @Override + public ExcludeSpec allOf(Set specs) { + return delegate.allOf(specs); + } + + @Override + public ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher) { + return delegate.ivyPatternExclude(moduleId, artifact, matcher); + } + + @Override + 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/ExcludeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/ExcludeFactory.java new file mode 100644 index 000000000000..2d0d667aa8f4 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/ExcludeFactory.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.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.Set; + +public interface ExcludeFactory { + ExcludeNothing nothing(); + + ExcludeEverything everything(); + + GroupExclude group(String group); + + ModuleExclude module(String module); + + ModuleIdExclude moduleId(ModuleIdentifier id); + + ArtifactExclude artifact(ModuleIdentifier id, IvyArtifactName artifact); + + ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two); + + ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two); + + ExcludeSpec anyOf(Set specs); + + ExcludeSpec allOf(Set specs); + + ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher); + + 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/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..7282f71d1a59 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactory.java @@ -0,0 +1,314 @@ +/* + * 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.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; +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.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; +import java.util.stream.Stream; + +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. + * 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 simplify(ExcludeAllOf.class, one, two, (left, right) -> doUnion(ImmutableSet.of(left, right))); + } + + @Override + public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec 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.stream().noneMatch(clazz::isInstance)) { + 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 + public ExcludeSpec anyOf(Set specs) { + return doUnion(specs); + } + + @Override + public ExcludeSpec allOf(Set specs) { + return doIntersect(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(); + } + if (flattened.result.isEmpty()) { + return nothing(); + } + 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); + 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 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()) { + moduleIdSetsExcludes = ImmutableList.of(excludeSpec); + } else { + moduleIdSetsExcludes.add(excludeSpec); + } + moduleIdExcludes = Collections.emptyList(); + } + } + 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()) { + groupSetExcludes = ImmutableList.of(excludeSpec); + } else { + groupSetExcludes.add(excludeSpec); + } + groupExcludes = Collections.emptyList(); + } + } + 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()) { + moduleSetExcludes = ImmutableList.of(excludeSpec); + } else { + moduleSetExcludes.add(excludeSpec); + } + moduleExcludes = Collections.emptyList(); + } + } + if (moduleIdSetsExcludes.size() > 1) { + moduleIdSetsExcludes = ImmutableList.of(delegate.moduleIdSet(moduleIdSetsExcludes.stream().flatMap(e -> e.getModuleIds().stream()).collect(toSet()))); + } + 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()))); + } + ImmutableSet.Builder builder = ImmutableSet.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.optimizeCollection(this, builder.build(), 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 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 FlattenOperationResult.FAST_EXIT; + } + if (ignoreSpec.test(spec)) { + filtered = true; + } else if (flattenType.isInstance(spec)) { + flatten = true; + size += ((CompositeExclude) spec).size(); + } else { + size++; + } + } + if (!filtered && !flatten) { + return FlattenOperationResult.of(specs); + } + if (filtered && !flatten) { + 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 stream; + } + + private FlattenOperationResult expensiveFlatten(Class flattenType, Stream stream, int size) { + return FlattenOperationResult.of(stream + .flatMap(e -> { + if (flattenType.isInstance(e)) { + CompositeExclude compositeExclude = (CompositeExclude) e; + return compositeExclude.components(); + } + return Stream.of(e); + }) + .collect(Collectors.toCollection(() -> Sets.newHashSetWithExpectedSize(size))) + ); + } + + 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(); + } + Set result = flattened.result; + if (result.isEmpty()) { + return everything(); + } + return Optimizations.optimizeCollection(this, result, 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; + } + } + + 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 new file mode 100644 index 000000000000..ab6aa5254bbb --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Optimizations.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.factories; + +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 java.util.Collection; +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 optimizeCollection(ExcludeFactory factory, T specs, Function onMiss) { + if (specs.isEmpty()) { + return factory.nothing(); + } + if (specs.size() == 1) { + 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 new file mode 100644 index 000000000000..0b7ffa6c7445 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/OptimizingExcludeFactory.java @@ -0,0 +1,63 @@ +/* + * 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.ExcludeSpec; + +import java.util.Iterator; +import java.util.Set; + +/** + * 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(Set specs) { + return Optimizations.optimizeCollection(this, specs, list -> { + if (list.size() == 2) { + Iterator it = list.iterator(); + return delegate.anyOf(it.next(), it.next()); + } + return delegate.anyOf(list); + }); + } + + @Override + public ExcludeSpec allOf(Set specs) { + return Optimizations.optimizeCollection(this, specs, list -> { + if (list.size() == 2) { + 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/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..8ce20348ecce --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultCompositeExclude.java @@ -0,0 +1,125 @@ +/* + * 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.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.size = components.size(); + this.hashCode = 31 * components.hashCode() + this.size ^ this.getClass().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 Set getComponents() { + return components; + } + + @Override + public int size() { + return size; + } + + @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..35bd6cbffe5f --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAllOf.java @@ -0,0 +1,53 @@ +/* + * 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; + +final class DefaultExcludeAllOf extends DefaultCompositeExclude implements ExcludeAllOf { + public static ExcludeAllOf of(ImmutableSet components) { + return new DefaultExcludeAllOf(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..4510359fa16b --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeAnyOf.java @@ -0,0 +1,52 @@ +/* + * 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; + +final class DefaultExcludeAnyOf extends DefaultCompositeExclude implements ExcludeAnyOf { + public static ExcludeSpec of(ImmutableSet components) { + return new DefaultExcludeAnyOf(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..1066c4ce74d7 --- /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 ExcludeEverything 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..838863d0cab2 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/simple/DefaultExcludeFactory.java @@ -0,0 +1,105 @@ +/* + * 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.factories.ExcludeFactory; +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.Set; + +public class DefaultExcludeFactory implements ExcludeFactory { + @Override + public ExcludeNothing nothing() { + return DefaultExcludeNothing.get(); + } + + @Override + public ExcludeEverything everything() { + return DefaultExcludeEverything.get(); + } + + @Override + public GroupExclude group(String group) { + return DefaultGroupExclude.of(group); + } + + @Override + public ModuleExclude module(String module) { + return DefaultModuleExclude.of(module); + } + + @Override + public ModuleIdExclude moduleId(ModuleIdentifier id) { + return DefaultModuleIdExclude.of(id); + } + + @Override + public ArtifactExclude artifact(ModuleIdentifier id, IvyArtifactName artifact) { + return DefaultModuleArtifactExclude.of(id, artifact); + } + + @Override + public ExcludeSpec anyOf(ExcludeSpec one, ExcludeSpec two) { + return DefaultExcludeAnyOf.of(ImmutableSet.of(one, two)); + } + + @Override + public ExcludeSpec allOf(ExcludeSpec one, ExcludeSpec two) { + return DefaultExcludeAllOf.of(ImmutableSet.of(one, two)); + } + + @Override + public ExcludeSpec anyOf(Set specs) { + return DefaultExcludeAnyOf.of(ImmutableSet.copyOf(specs)); + } + + @Override + public ExcludeSpec allOf(Set specs) { + return DefaultExcludeAllOf.of(ImmutableSet.copyOf(specs)); + } + + @Override + public ExcludeSpec ivyPatternExclude(ModuleIdentifier moduleId, IvyArtifactName artifact, String matcher) { + return DefaultIvyPatternMatcherExcludeRuleSpec.of(moduleId, artifact, matcher); + } + + @Override + 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 new file mode 100644 index 000000000000..7c0d067e3d80 --- /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 ExcludeNothing 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..067cf9864fc5 --- /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 GroupExclude 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/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/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..fc685f75a239 --- /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 ModuleExclude 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/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 new file mode 100644 index 000000000000..0f32edcc8d4e --- /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 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 modules; + private int hashCode; + + DefaultModuleSetExclude(Set modules) { + this.modules = modules; + this.hashCode = modules.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 getModules() { + return modules; + } + + @Override + public boolean excludes(ModuleIdentifier module) { + return modules.contains(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 String toString() { + return "{ module names = " + modules + '}'; + } +} 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..daf09cd51515 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/CompositeExclude.java @@ -0,0 +1,34 @@ +/* + * 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; +import java.util.stream.Stream; + +public interface CompositeExclude extends ExcludeSpec { + @Override + boolean equals(Object o); + + Stream components(); + + Set getComponents(); + + default boolean contains(ExcludeSpec spec) { + return components().anyMatch(spec::equals); + } + + int size(); +} 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/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/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/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/ModuleIdSetExclude.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/ModuleIdSetExclude.java index 633e8e65000f..ba4dc48d49e8 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/ModuleIdSetExclude.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 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 new file mode 100644 index 000000000000..064568f5f8e2 --- /dev/null +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/specs/ModuleSetExclude.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 ModuleSetExclude extends ExcludeSpec { + Set getModules(); +} 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/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 819ab63b41ea..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 @@ -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 @@ -64,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; @@ -83,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 @@ -229,11 +232,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 +251,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); @@ -264,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) { @@ -461,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/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/DependencyState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java index 1c788360d2a7..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 @@ -26,18 +26,21 @@ 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; private final DependencyMetadata dependency; private final List ruleDescriptors; private final ComponentSelectorConverter componentSelectorConverter; + private final int hashCode; private ModuleIdentifier moduleIdentifier; public ModuleVersionResolveException failure; + private boolean reasonsAlreadyAdded; DependencyState(DependencyMetadata dependency, ComponentSelectorConverter componentSelectorConverter) { this(dependency, dependency.getSelector(), null, componentSelectorConverter); @@ -48,6 +51,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() { @@ -96,19 +106,60 @@ public boolean isFromLock() { return dependency instanceof LocalOriginDependencyMetadata && ((LocalOriginDependencyMetadata) dependency).isFromLock(); } - public void addSelectionReasons(Set reasons) { - String reason = dependency.getReason(); + void addSelectionReasons(List reasons) { + if (reasonsAlreadyAdded) { + return; + } + reasonsAlreadyAdded = true; + addMainReason(reasons); + + if (ruleDescriptors != null) { + addRuleDescriptors(reasons); + } + if (isDependencyForced()) { + maybeAddReason(reasons, FORCED); + } + } + + private void addRuleDescriptors(List reasons) { + for (ComponentSelectionDescriptorInternal descriptor : ruleDescriptors) { + maybeAddReason(reasons, descriptor); + } + } + + private void addMainReason(List reasons) { ComponentSelectionDescriptorInternal dependencyDescriptor = dependency.isConstraint() ? CONSTRAINT : REQUESTED; + String reason = dependency.getReason(); if (reason != null) { dependencyDescriptor = dependencyDescriptor.withDescription(Describables.of(reason)); } - reasons.add(dependencyDescriptor); + maybeAddReason(reasons, dependencyDescriptor); + } - if (ruleDescriptors != null) { - reasons.addAll(ruleDescriptors); + private static void maybeAddReason(List reasons, ComponentSelectionDescriptorInternal reason) { + if (reasons.isEmpty()) { + reasons.add(reason); + } else if (isNewReason(reasons, reason)) { + reasons.add(reason); } - if (isDependencyForced()) { - reasons.add(FORCED); + } + + 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) { + 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 930eff01866f..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 @@ -16,15 +16,14 @@ 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; import org.gradle.api.artifacts.ModuleIdentifier; 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; @@ -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. @@ -54,15 +54,18 @@ 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; + private final int hashCode; private ModuleVersionResolveException targetNodeSelectionFailure; private ImmutableAttributes cachedAttributes; + private ExcludeSpec cachedEdgeExclusions; + private ExcludeSpec cachedExclusions; - 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(); @@ -72,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 @@ -125,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; } @@ -243,21 +256,38 @@ private boolean isVirtualDependency() { } @Override - public ModuleExclusion getExclusions() { - List excludes = dependencyMetadata.getExcludes(); - if (excludes.isEmpty()) { - return transitiveExclusions; + public ExcludeSpec getExclusions() { + if (cachedExclusions == null) { + computeExclusions(); } - ModuleExclusion edgeExclusions = resolveState.getModuleExclusions().excludeAny(ImmutableList.copyOf(excludes)); - return resolveState.getModuleExclusions().either(edgeExclusions, transitiveExclusions); + return cachedExclusions; } - public ModuleExclusion getEdgeExclusions() { + private void computeExclusions() { List excludes = dependencyMetadata.getExcludes(); if (excludes.isEmpty()) { - return null; + cachedExclusions = transitiveExclusions; + } else { + computeExclusionsWhenExcludesPresent(excludes); + } + } + + private void computeExclusionsWhenExcludesPresent(List excludes) { + ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); + ExcludeSpec edgeExclusions = moduleExclusions.excludeAny(excludes); + cachedExclusions = moduleExclusions.excludeAny(edgeExclusions, transitiveExclusions); + } + + ExcludeSpec getEdgeExclusions() { + 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 @@ -267,7 +297,7 @@ public boolean contributesArtifacts() { @Override public ComponentSelector getRequested() { - return AttributeDesugaring.desugarSelector(dependencyState.getRequested(), from.getAttributesFactory()); + return resolveState.desugarSelector(dependencyState.getRequested()); } @Override @@ -317,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) { @@ -330,4 +359,22 @@ 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; + } + + DependencyState getDependencyState() { + return dependencyState; + } } 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..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 @@ -16,22 +16,42 @@ 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 boolean deferSelection; public boolean checkDeferSelection() { @@ -45,147 +65,109 @@ 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(); - } - } + return selectors.iterator(); } 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; + this.deferSelection = deferSelection; + if (selectors.isEmpty()) { + selectors.add(selector); } else { - addSelector(selector); + doAdd(selector); } - selectorsCount++; } - private void addSelector(T selector) { - if (isDynamicSelector(selector)) { - addDynamicSelector(selector); + private void doAdd(T selector) { + int size = selectors.size(); + if (size == 1) { + doAddWhenListHasOneElement(selector); } else { - addSimpleSelector(selector); + doAddWhenListHasManyElements(selectors, selector, size); } } - private boolean isDynamicSelector(T selector) { - return selector.getVersionConstraint() != null && selector.getVersionConstraint().isDynamic(); + 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); } - private void addSimpleSelector(T selector) { - if (selectors == null) { - selectors = Lists.newArrayListWithExpectedSize(3); + 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++; } - selectors.add(selector); + return insertionPoint; } - private void addDynamicSelector(T selector) { - if (dynamicSelectors == null) { - dynamicSelectors = Lists.newArrayListWithExpectedSize(3); + 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); } - dynamicSelectors.add(selector); } 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; + return selectors.remove(selector); } - public int size() { - return selectorsCount; + private static boolean isDynamicSelector(ResolvableSelectorState selector) { + return selector.getVersionConstraint() != null && selector.getVersionConstraint().isDynamic(); } - 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 boolean hasLatestSelector(ResolvableSelectorState selector) { + return selector.getVersionConstraint() != null + && hasLatestSelector(selector.getVersionConstraint()); } - // 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 boolean hasLatestSelector(ResolvedVersionConstraint vc) { + // Latest is only given priority if it's in a require + return hasLatestSelector(vc.getRequiredSelector()); + } + + private static boolean hasLatestSelector(VersionSelector versionSelector) { + return versionSelector instanceof LatestVersionSelector; + } + + private static Version requiredVersion(ResolvableSelectorState selector) { + ResolvedVersionConstraint versionConstraint = selector.getVersionConstraint(); + if (versionConstraint == null) { + return EMPTY_VERSION; } + return versionOf(versionConstraint.getRequiredSelector()); } - private static class EmptyIterator implements Iterator { + private static Version preferredVersion(ResolvableSelectorState selector) { + ResolvedVersionConstraint versionConstraint = selector.getVersionConstraint(); + if (versionConstraint == null) { + return EMPTY_VERSION; + } + return versionOf(versionConstraint.getPreferredSelector()); + } - @Override - public boolean hasNext() { - return false; + private static Version versionOf(VersionSelector selector) { + if (!(selector instanceof ExactVersionSelector)) { + return EMPTY_VERSION; } + return VERSION_PARSER.transform(selector.getSelector()); + } + + public int size() { + return selectors.size(); + } - @Override - public Object next() { - throw new NoSuchElementException("Empty iterator has no elements"); + public T first() { + if (size() == 0) { + return null; + } + if (size() == 1) { + return selectors.get(0); } + return selectors.get(0); } } 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..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 @@ -16,8 +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; @@ -28,11 +31,11 @@ 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.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; @@ -42,15 +45,14 @@ 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; 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; /** @@ -58,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(); @@ -73,17 +68,40 @@ 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; + private final boolean dependenciesMayChange; + + ExcludeSpec previousTraversalExclusions; - ModuleExclusion previousTraversalExclusions; // In opposite to outgoing edges, virtual edges are for now pretty rare, so they are created lazily private List virtualEdges; private boolean queued; private boolean evicted; + private int transitiveEdgeCount; private Set upcomingNoLongerPendingConstraints; private boolean virtualPlatformNeedsRefresh; - private HashSet edgesToRecompute; + private Set edgesToRecompute; + private Multimap potentiallyActivatedConstraints; + + // caches + private Map dependencyStateCache = Maps.newHashMap(); + private Map edgesCache = Maps.newHashMap(); + + // 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 int previousIncomingEdgeCount; + private long previousIncomingHash; + private long incomingHash; + private ExcludeSpec cachedModuleResolutionFilter; + public NodeState(Long resultId, ResolvedConfigurationIdentifier id, ComponentState component, ResolveState resolveState, ConfigurationMetadata md) { this.resultId = resultId; @@ -93,6 +111,8 @@ 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 + this.dependenciesMayChange = component.getModule() != null && component.getModule().isVirtualPlatform(); // can be null in tests, ComponentState cannot be mocked component.addConfiguration(this); } @@ -212,22 +232,19 @@ 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; } // Determine the net exclusion for this node, by inspecting all transitive incoming edges - ModuleExclusion 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()) { // 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) { @@ -245,6 +262,7 @@ public void visitOutgoingDependencies(Collection discoveredEdges) { removeOutgoingEdges(); upcomingNoLongerPendingConstraints = null; edgesToRecompute = null; + potentiallyActivatedConstraints = null; } visitDependencies(resolutionFilter, discoveredEdges); @@ -253,7 +271,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); @@ -300,20 +318,23 @@ 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`) */ - private void visitDependencies(ModuleExclusion resolutionFilter, Collection discoveredEdges) { + private void visitDependencies(ExcludeSpec resolutionFilter, Collection discoveredEdges) { PendingDependenciesVisitor pendingDepsVisitor = resolveState.newPendingDependenciesVisitor(); try { - for (DependencyMetadata dependency : metaData.getDependencies()) { - DependencyState dependencyState = new DependencyState(dependency, resolveState.getComponentSelectorConverter()); - if (isExcluded(resolutionFilter, dependencyState)) { - continue; - } + for (DependencyState dependencyState : dependencies(resolutionFilter)) { dependencyState = maybeSubstitute(dependencyState, resolveState.getDependencySubstitutionApplicator()); PendingDependenciesVisitor.PendingState pendingState = pendingDepsVisitor.maybeAddAsPendingDependency(this, dependencyState); + if (dependencyState.getDependency().isConstraint()) { + registerActivatingConstraint(dependencyState); + } if (!pendingState.isPending()) { createAndLinkEdgeState(dependencyState, discoveredEdges, resolutionFilter, pendingState == PendingDependenciesVisitor.PendingState.NOT_PENDING_ACTIVATING); } @@ -327,8 +348,57 @@ private void visitDependencies(ModuleExclusion resolutionFilter, Collection discoveredEdges, ModuleExclusion resolutionFilter, boolean deferSelection) { - EdgeState dependencyEdge = new EdgeState(this, dependencyState, resolutionFilter, resolveState); + private void registerActivatingConstraint(DependencyState dependencyState) { + if (potentiallyActivatedConstraints == null) { + potentiallyActivatedConstraints = ArrayListMultimap.create(); + } + potentiallyActivatedConstraints.put(dependencyState.getModuleIdentifier(), dependencyState); + } + + private List dependencies() { + if (dependenciesMayChange) { + cachedDependencyStates = null; + cachedFilteredDependencyStates = null; + } + return metaData.getDependencies(); + } + + private List dependencies(ExcludeSpec spec) { + List dependencies = dependencies(); + 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 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); outgoingEdges.add(dependencyEdge); discoveredEdges.add(dependencyEdge); dependencyEdge.getSelector().use(deferSelection); @@ -337,13 +407,15 @@ private void createAndLinkEdgeState(DependencyState dependencyState, Collection< /** * Iterate over the dependencies originating in this node, adding only the constraints listed * in upcomingNoLongerPendingConstraints - * @param discoveredEdges */ private void visitAdditionalConstraints(Collection discoveredEdges) { - for (DependencyMetadata dependency : metaData.getDependencies()) { - if (dependency.isConstraint()) { - DependencyState dependencyState = new DependencyState(dependency, 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); } @@ -421,34 +493,24 @@ 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(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 == moduleExclusions.nothing()) { return false; } ModuleIdentifier targetModuleId = dependencyState.getModuleIdentifier(); - if (selector.excludeModule(targetModuleId)) { + if (selector.excludes(targetModuleId)) { LOGGER.debug("{} is excluded from {}.", targetModuleId, this); return true; } @@ -456,14 +518,25 @@ private boolean isExcluded(ModuleExclusion selector, DependencyState dependencyS return false; } - public void addIncomingEdge(EdgeState dependencyEdge) { - incomingEdges.add(dependencyEdge); - resolveState.onMoreSelected(this); + void addIncomingEdge(EdgeState dependencyEdge) { + if (!incomingEdges.contains(dependencyEdge)) { + incomingEdges.add(dependencyEdge); + incomingHash += dependencyEdge.hashCode(); + resolveState.onMoreSelected(this); + if (dependencyEdge.isTransitive()) { + transitiveEdgeCount++; + } + } } - public void removeIncomingEdge(EdgeState dependencyEdge) { - incomingEdges.remove(dependencyEdge); - resolveState.onFewerSelected(this); + void removeIncomingEdge(EdgeState dependencyEdge) { + if (incomingEdges.remove(dependencyEdge)) { + incomingHash -= dependencyEdge.hashCode(); + resolveState.onFewerSelected(this); + if (dependencyEdge.isTransitive()) { + transitiveEdgeCount--; + } + } } public boolean isSelected() { @@ -475,50 +548,142 @@ public void evict() { restartIncomingEdges(); } - public boolean shouldIncludedInGraphResult() { + boolean shouldIncludedInGraphResult() { return isSelected() && !component.getModule().isVirtualPlatform(); } - private ModuleExclusion getModuleResolutionFilter(List incomingEdges) { - ModuleExclusions moduleExclusions = resolveState.getModuleExclusions(); - ModuleExclusion nodeExclusions = moduleExclusions.excludeAny(metaData.getExcludes()); + private ExcludeSpec computeModuleResolutionFilter(List incomingEdges) { + ExcludeSpec nodeExclusions = computeNodeExclusions(); if (incomingEdges.isEmpty()) { return nodeExclusions; } - ModuleExclusion edgeExclusions = null; + return computeExclusionFilter(incomingEdges, nodeExclusions); + } + + private ExcludeSpec computeNodeExclusions() { + if (cachedNodeExclusions == null) { + cachedNodeExclusions = moduleExclusions.excludeAny(metaData.getExcludes()); + } + return cachedNodeExclusions; + } + private ExcludeSpec computeExclusionFilter(List incomingEdges, ExcludeSpec nodeExclusions) { + 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 + 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; + Set excludedByEither = null; for (EdgeState dependencyEdge : incomingEdges) { if (dependencyEdge.isTransitive()) { - // Transitive dependency - edgeExclusions = excludedByBoth(edgeExclusions, dependencyEdge.getExclusions()); - } else if (dependencyEdge.getDependencyMetadata().isConstraint()) { - // Constraint: only consider explicit exclusions declared for this constraint - ModuleExclusion constraintExclusions = dependencyEdge.getEdgeExclusions(); - nodeExclusions = excludedByEither(nodeExclusions, constraintExclusions); + 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); + } + if (edgeExclusions == nothing) { + // if exclusions == nothing, then the intersection will be "nothing" + excludedByBoth = null; + } + } + } else if (isConstraint(dependencyEdge)) { + excludedByEither = collectEdgeConstraint(nodeExclusions, excludedByEither, dependencyEdge, nothing, incomingEdgeCount); } } - return excludedByEither(edgeExclusions, nodeExclusions); + edgeExclusions = intersectEdgeExclusions(edgeExclusions, excludedByBoth); + nodeExclusions = joinNodeExclusions(nodeExclusions, excludedByEither); + return joinEdgeAndNodeExclusionsThenCacheResult(nodeExclusions, edgeExclusions, incomingEdgeCount); } - private ModuleExclusion excludedByBoth(ModuleExclusion one, ModuleExclusion two) { - if (one == null) { - return two; + private ExcludeSpec computeExclusionFilterSingleIncomingEdge(EdgeState dependencyEdge, ExcludeSpec nodeExclusions) { + ExcludeSpec exclusions = null; + if (dependencyEdge.isTransitive()) { + exclusions = dependencyEdge.getExclusions(); + } else if (isConstraint(dependencyEdge)) { + exclusions = dependencyEdge.getEdgeExclusions(); } - if (two == null) { - return one; + if (exclusions == null) { + exclusions = moduleExclusions.nothing(); } - return resolveState.getModuleExclusions().both(one, two); + return joinEdgeAndNodeExclusionsThenCacheResult(nodeExclusions, exclusions, 1); + } + + private static boolean isConstraint(EdgeState dependencyEdge) { + return dependencyEdge.getDependencyMetadata().isConstraint(); } - private ModuleExclusion excludedByEither(ModuleExclusion one, ModuleExclusion two) { - if (one == null) { - return two; + 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 + previousIncomingEdgeCount = incomingEdgeCount; + previousIncomingHash = incomingHash; + cachedModuleResolutionFilter = result; + cachedFilteredDependencyStates = null; + 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 (edgeExclusions == moduleExclusions.nothing()) { + return edgeExclusions; } - if (two == null) { - return one; + if (excludedByBoth != null) { + if (edgeExclusions != null) { + excludedByBoth.add(edgeExclusions); + } + edgeExclusions = moduleExclusions.excludeAll(excludedByBoth); } - return resolveState.getModuleExclusions().either(one, two); + 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 + // extremely unlikely (never happened on test cases even on large dependency graph) + return cachedModuleResolutionFilter != null + && previousIncomingHash == incomingHash + && previousIncomingEdgeCount == incomingEdgeCount; } private void removeOutgoingEdges() { @@ -565,7 +730,13 @@ private void restartIncomingEdges() { dependency.restart(); } } + clearIncomingEdges(); + } + + private void clearIncomingEdges() { incomingEdges.clear(); + incomingHash = 0; + transitiveEdgeCount = 0; } public void deselect() { @@ -574,7 +745,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 @@ -597,7 +768,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 @@ -607,7 +778,7 @@ public void clearConstraintEdges(PendingDependencies pendingDependencies, NodeSt } pendingDependencies.addNode(from); } - incomingEdges.clear(); + clearIncomingEdges(); } void forEachCapability(Action action) { @@ -668,4 +839,13 @@ boolean isSelectedByVariantAwareResolution() { // the order is strange logically but here for performance optimization return selectedByVariantAwareResolution && isSelected(); } + + 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 ca53abe6a606..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 @@ -16,13 +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.dsl.ModuleReplacementsData; +import org.gradle.api.internal.artifacts.ResolvedVersionConstraint; +import org.gradle.api.internal.artifacts.dependencies.DefaultResolvedVersionConstraint; 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; @@ -32,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; @@ -66,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; @@ -75,10 +79,12 @@ class ResolveState implements ComponentStateFactory { private final VersionParser versionParser; 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, @@ -89,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; @@ -97,10 +102,11 @@ 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(); + 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()); @@ -125,12 +131,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 +153,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() { @@ -165,15 +161,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, this, moduleIdentifier); + }); + selectorState.update(dependencyState); return selectorState; } @@ -223,10 +215,6 @@ public ReplaceSelectionWithConflictResultAction getReplaceSelectionWithConflictR return replaceSelectionWithConflictResultAction; } - public ModuleReplacementsData getModuleReplacementsData() { - return moduleReplacementsData; - } - public ComponentSelectorConverter getComponentSelectorConverter() { return componentSelectorConverter; } @@ -242,4 +230,24 @@ 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; + } + + 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 4752fbf4c46a..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 @@ -18,23 +18,20 @@ 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; -import org.gradle.api.artifacts.component.ModuleComponentSelector; +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.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; 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; import org.gradle.internal.resolve.RejectedByAttributesVersion; import org.gradle.internal.resolve.RejectedByRuleVersion; @@ -44,10 +41,9 @@ 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; +import java.util.List; /** * Resolution state for a given module version selector. @@ -63,10 +59,10 @@ 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 ImmutableAttributesFactory attributesFactory; - private final Set dependencyReasons = Sets.newLinkedHashSet(); + private final ResolvedVersionConstraint versionConstraint; + private final List dependencyReasons = Lists.newArrayListWithExpectedSize(4); + private final boolean isProjectSelector; + private final AttributeDesugaring attributeDesugaring; private ComponentIdResolveResult preferResult; private ComponentIdResolveResult requireResult; @@ -85,17 +81,22 @@ 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()); + this.isProjectSelector = getSelector() instanceof ProjectComponentSelector; + this.attributeDesugaring = resolveState.getAttributeDesugaring(); + } + + @Override + public boolean isProject() { + // this is cached because used very often in sorting selectors + return isProjectSelector; } public void use(boolean deferSelection) { @@ -118,13 +119,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; @@ -137,7 +131,7 @@ public String toString() { @Override public ComponentSelector getRequested() { - return selectorWithDesugaredAttributes(dependencyState.getRequested()); + return attributeDesugaring.desugarSelector(dependencyState.getRequested()); } public ModuleResolveState getTargetModule() { @@ -317,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()) { 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/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() { 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 12038d5cd915..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,13 +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) { - if (emptyVersion(registration.result) || sameVersion(registration.result, resolveResult) || - (included(registration.selector, resolveResult, isFromLock) && lowerVersion(registration.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; } } @@ -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 selectorAcceptsCandidate(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/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/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..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); } } @@ -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); } 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/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..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() { @@ -83,6 +82,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..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() { @@ -125,6 +132,10 @@ public boolean equals(Object o) { DefaultModuleComponentSelector that = (DefaultModuleComponentSelector) 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/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/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/ImmutableCapability.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ImmutableCapability.java index d839b2beebe6..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 @@ -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,19 @@ 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) + 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 public String getGroup() { return group; 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/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/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() { 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(); 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/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) + + '}'; } } } 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; 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..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 @@ -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 -> @@ -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/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..f3d64480596b --- /dev/null +++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/NormalizingExcludeFactoryTest.groovy @@ -0,0 +1,136 @@ +/* + * 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.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.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 as Set) == 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(ImmutableSet.copyOf(specs)) + } + + private ExcludeSpec allOf(ExcludeSpec... specs) { + delegate.allOf(ImmutableSet.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/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..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 @@ -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) { + getRequiredSelector() >> 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/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()}")] } 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 {} } 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/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 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; }