diff --git a/src/it/projects/dependency-sets/massembly-1008/pom.xml b/src/it/projects/dependency-sets/massembly-1008/pom.xml
index 4cb15892..a7674575 100644
--- a/src/it/projects/dependency-sets/massembly-1008/pom.xml
+++ b/src/it/projects/dependency-sets/massembly-1008/pom.xml
@@ -23,6 +23,7 @@ under the License.
org.apache.maven.plugin.assembly.test
it-project-parent
1
+ ../../../it-project-parent/pom.xml
test
diff --git a/src/it/projects/dependency-sets/massembly-1008/verify.groovy b/src/it/projects/dependency-sets/massembly-1008/verify.groovy
index 55d7b281..5828daf7 100644
--- a/src/it/projects/dependency-sets/massembly-1008/verify.groovy
+++ b/src/it/projects/dependency-sets/massembly-1008/verify.groovy
@@ -19,18 +19,14 @@
import java.io.*;
+// guava is absent from the runtime dependencySet: the nearer test-scoped guava 32.0.0-jre wins
+// version mediation and is then excluded by scope, while guice's transitive guava 31.0.1-jre is
+// omitted for the conflict. This is the runtime classpath Maven resolves.
def expectedFilenames = [
"aopalliance-1.0.jar",
- "checker-qual-3.12.0.jar",
- "error_prone_annotations-2.7.1.jar",
- "failureaccess-1.0.1.jar",
- "guava-31.0.1-jre.jar",
"guice-6.0.0.jar",
- "j2objc-annotations-1.3.jar",
"jakarta.inject-api-2.0.1.jar",
- "javax.inject-1.jar",
- "jsr305-3.0.2.jar",
- "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar"
+ "javax.inject-1.jar"
]
File assemblyBasedir = new File( basedir, "target/massembly-1008-1-bin/" )
diff --git a/src/it/projects/dependency-sets/massembly-1026/pom.xml b/src/it/projects/dependency-sets/massembly-1026/pom.xml
new file mode 100644
index 00000000..9b925873
--- /dev/null
+++ b/src/it/projects/dependency-sets/massembly-1026/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+ 4.0.0
+
+ org.apache.maven.plugin.assembly.test
+ it-project-parent
+ 1
+ ../../../it-project-parent/pom.xml
+
+
+ test
+ massembly-1026
+ 1
+
+ https://issues.apache.org/jira/browse/MASSEMBLY-1026
+
+
+ UTF-8
+
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-guava
+ 2.7.3
+ test
+
+
+ com.google.inject
+ guice
+ 6.0.0
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assembly
+ package
+
+ single
+
+
+
+ src/main/assembly/bin.xml
+
+
+
+
+
+
+
+
diff --git a/src/it/projects/dependency-sets/massembly-1026/src/main/assembly/bin.xml b/src/it/projects/dependency-sets/massembly-1026/src/main/assembly/bin.xml
new file mode 100644
index 00000000..230d3c93
--- /dev/null
+++ b/src/it/projects/dependency-sets/massembly-1026/src/main/assembly/bin.xml
@@ -0,0 +1,33 @@
+
+
+ bin
+
+ dir
+
+ false
+
+
+ false
+ runtime
+
+ false
+
+
+
diff --git a/src/it/projects/dependency-sets/massembly-1026/verify.groovy b/src/it/projects/dependency-sets/massembly-1026/verify.groovy
new file mode 100644
index 00000000..acb93fdd
--- /dev/null
+++ b/src/it/projects/dependency-sets/massembly-1026/verify.groovy
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.*;
+
+// guava 16.0 (from the test-scoped jackson-datatype-guava) wins version mediation over guice's
+// guava 31.0.1-jre and stays on the runtime classpath via guice.
+def expectedFilenames = [
+ "aopalliance-1.0.jar",
+ "guava-16.0.jar",
+ "guice-6.0.0.jar",
+ "jakarta.inject-api-2.0.1.jar",
+ "javax.inject-1.jar"
+]
+
+File assemblyBasedir = new File( basedir, "target/massembly-1026-1-bin/" )
+
+assert assemblyBasedir.listFiles().length == expectedFilenames.size()
+
+for ( fileName in expectedFilenames )
+{
+ File file = new File( assemblyBasedir, fileName )
+ assert file.isFile() // exists and is file
+}
+
+// the losing version must not be assembled
+assert !new File( assemblyBasedir, "guava-31.0.1-jre.jar" ).exists()
+
+return true
diff --git a/src/it/projects/dependency-sets/massembly-1031/pom.xml b/src/it/projects/dependency-sets/massembly-1031/pom.xml
new file mode 100644
index 00000000..777fdcaa
--- /dev/null
+++ b/src/it/projects/dependency-sets/massembly-1031/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+ 4.0.0
+
+ org.apache.maven.plugin.assembly.test
+ it-project-parent
+ 1
+ ../../../it-project-parent/pom.xml
+
+
+ test
+ massembly-1031
+ 1
+
+ https://issues.apache.org/jira/browse/MASSEMBLY-1031
+
+
+ UTF-8
+
+
+
+
+ org.apache.commons
+ commons-text
+ 1.12.0
+
+
+ org.apache.commons
+ commons-lang3
+ 3.14.0
+ provided
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assembly
+ package
+
+ single
+
+
+
+ src/main/assembly/bin.xml
+
+
+
+
+
+
+
+
diff --git a/src/it/projects/dependency-sets/massembly-1031/src/main/assembly/bin.xml b/src/it/projects/dependency-sets/massembly-1031/src/main/assembly/bin.xml
new file mode 100644
index 00000000..0ad3ec58
--- /dev/null
+++ b/src/it/projects/dependency-sets/massembly-1031/src/main/assembly/bin.xml
@@ -0,0 +1,32 @@
+
+
+ bin
+
+ dir
+
+ false
+
+
+ false
+
+ false
+
+
+
diff --git a/src/it/projects/dependency-sets/massembly-1031/verify.groovy b/src/it/projects/dependency-sets/massembly-1031/verify.groovy
new file mode 100644
index 00000000..9af6d9ab
--- /dev/null
+++ b/src/it/projects/dependency-sets/massembly-1031/verify.groovy
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.*;
+
+// commons-lang3 is excluded: the direct provided declaration wins mediation over the transitive
+// compile one pulled in by commons-text, so only commons-text remains on the runtime classpath.
+def expectedFilenames = [
+ "commons-text-1.12.0.jar"
+]
+
+File assemblyBasedir = new File( basedir, "target/massembly-1031-1-bin/" )
+
+assert assemblyBasedir.listFiles().length == expectedFilenames.size()
+
+for ( fileName in expectedFilenames )
+{
+ File file = new File( assemblyBasedir, fileName )
+ assert file.isFile() // exists and is file
+}
+
+// commons-lang3 is provided and must be excluded
+assert !new File( assemblyBasedir, "commons-lang3-3.14.0.jar" ).exists()
+
+return true
diff --git a/src/main/java/org/apache/maven/plugins/assembly/artifact/DefaultDependencyResolver.java b/src/main/java/org/apache/maven/plugins/assembly/artifact/DefaultDependencyResolver.java
index e9eef03b..a26d3628 100644
--- a/src/main/java/org/apache/maven/plugins/assembly/artifact/DefaultDependencyResolver.java
+++ b/src/main/java/org/apache/maven/plugins/assembly/artifact/DefaultDependencyResolver.java
@@ -52,7 +52,6 @@
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.collection.CollectRequest;
-import org.eclipse.aether.graph.DefaultDependencyNode;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
@@ -213,13 +212,14 @@ private Set resolveTransitive(
RepositorySystemSession repositorySession, String scope, MavenProject project)
throws org.eclipse.aether.resolution.DependencyResolutionException {
- // scope dependency filter
- DependencyFilter scoopeDependencyFilter = DependencyFilterUtils.classpathFilter(scope);
+ DependencyFilter scopeDependencyFilter = DependencyFilterUtils.classpathFilter(scope);
- // get project dependencies filtered by requested scope
+ // Collect every declared dependency and apply the scope filter only when resolving below.
+ // Filtering declared dependencies by scope first would drop ones that win mediation - a
+ // nearer version, or a direct provided dependency that also appears transitively - leaving
+ // a graph that disagrees with the resolved compile/runtime classpath.
List dependencies = project.getDependencies().stream()
.map(d -> RepositoryUtils.toDependency(d, repositorySession.getArtifactTypeRegistry()))
- .filter(d -> scoopeDependencyFilter.accept(new DefaultDependencyNode(d), null))
.collect(Collectors.toList());
List managedDependencies = Optional.ofNullable(project.getDependencyManagement())
@@ -235,7 +235,7 @@ private Set resolveTransitive(
collectRequest.setDependencies(dependencies);
collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
- DependencyRequest request = new DependencyRequest(collectRequest, scoopeDependencyFilter);
+ DependencyRequest request = new DependencyRequest(collectRequest, scopeDependencyFilter);
DependencyResult dependencyResult = repositorySystem.resolveDependencies(repositorySession, request);
diff --git a/src/test/java/org/apache/maven/plugins/assembly/artifact/DefaultDependencyResolverTest.java b/src/test/java/org/apache/maven/plugins/assembly/artifact/DefaultDependencyResolverTest.java
index 8ed0e7d9..38d461b3 100644
--- a/src/test/java/org/apache/maven/plugins/assembly/artifact/DefaultDependencyResolverTest.java
+++ b/src/test/java/org/apache/maven/plugins/assembly/artifact/DefaultDependencyResolverTest.java
@@ -22,7 +22,9 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
@@ -38,12 +40,14 @@
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.ArtifactTypeRegistry;
import org.eclipse.aether.graph.DefaultDependencyNode;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResult;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@@ -110,6 +114,40 @@ void getDependencySetResolutionRequirementsTransitive() throws Exception {
});
}
+ @Test
+ void transitiveResolutionCollectsDependenciesOfEveryScope() throws Exception {
+ // Declared dependencies must reach the CollectRequest regardless of scope, so that
+ // full-graph mediation runs before the scope filter selects leaves.
+ final DependencySet ds = new DependencySet();
+ ds.setScope(Artifact.SCOPE_RUNTIME);
+ ds.setUseTransitiveDependencies(true);
+
+ final MavenProject project = createMavenProject("main-group", "main-artifact", "1", null);
+ project.getModel().addDependency(newModelDependency("g.id", "compile-dep", "1", Artifact.SCOPE_COMPILE));
+ project.getModel().addDependency(newModelDependency("g.id", "provided-dep", "1", Artifact.SCOPE_PROVIDED));
+ project.getModel().addDependency(newModelDependency("g.id", "test-dep", "1", Artifact.SCOPE_TEST));
+
+ when(systemSession.getArtifactTypeRegistry()).thenReturn(mock(ArtifactTypeRegistry.class));
+
+ DependencyResult dependencyResult = new DependencyResult(new DependencyRequest());
+ dependencyResult.setRoot(new DefaultDependencyNode((Dependency) null));
+
+ ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(DependencyRequest.class);
+ when(repositorySystem.resolveDependencies(eq(systemSession), requestCaptor.capture()))
+ .thenReturn(dependencyResult);
+
+ resolver.updateDependencySetResolutionRequirements(systemSession, ds, new ResolutionManagementInfo(), project);
+
+ List collected =
+ requestCaptor.getValue().getCollectRequest().getDependencies();
+ Set collectedScopes =
+ collected.stream().map(Dependency::getScope).collect(Collectors.toSet());
+ assertEquals(3, collected.size());
+ assertEquals(
+ new HashSet<>(Arrays.asList(Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_TEST)),
+ collectedScopes);
+ }
+
@Test
void getDependencySetResolutionRequirementsNonTransitive() throws Exception {
final DependencySet ds = new DependencySet();
@@ -220,6 +258,16 @@ private MavenProject createMavenProject(
return project;
}
+ private org.apache.maven.model.Dependency newModelDependency(
+ final String groupId, final String artifactId, final String version, final String scope) {
+ final org.apache.maven.model.Dependency dependency = new org.apache.maven.model.Dependency();
+ dependency.setGroupId(groupId);
+ dependency.setArtifactId(artifactId);
+ dependency.setVersion(version);
+ dependency.setScope(scope);
+ return dependency;
+ }
+
private Artifact newArtifact(final String groupId, final String artifactId, final String version) {
return new DefaultArtifact(
groupId,