diff --git a/pom.xml b/pom.xml index 030c6ce8..0d42cec0 100644 --- a/pom.xml +++ b/pom.xml @@ -108,10 +108,9 @@ under the License. org.eclipse.aether - aether-api + aether-util 1.0.0.v20140518 - provided org.slf4j diff --git a/src/it/projects/dependency-sets/massembly-1008/pom.xml b/src/it/projects/dependency-sets/massembly-1008/pom.xml new file mode 100644 index 00000000..82e605c5 --- /dev/null +++ b/src/it/projects/dependency-sets/massembly-1008/pom.xml @@ -0,0 +1,71 @@ + + + + 4.0.0 + + org.apache.maven.plugin.assembly.test + it-project-parent + 1 + + + test + massembly-1008 + 1 + + https://issues.apache.org/jira/browse/MASSEMBLY-1008 + + + UTF-8 + + + + com.google.inject + guice + 6.0.0 + + + com.google.guava + guava + 31.0.1-jre + test + + + + + + maven-assembly-plugin + + + assembly + package + + single + + + + src/main/assembly/bin.xml + + + + + + + + diff --git a/src/it/projects/dependency-sets/massembly-1008/src/main/assembly/bin.xml b/src/it/projects/dependency-sets/massembly-1008/src/main/assembly/bin.xml new file mode 100644 index 00000000..230d3c93 --- /dev/null +++ b/src/it/projects/dependency-sets/massembly-1008/src/main/assembly/bin.xml @@ -0,0 +1,33 @@ + + + bin + + dir + + false + + + false + runtime + + false + + + diff --git a/src/it/projects/dependency-sets/massembly-1008/verify.groovy b/src/it/projects/dependency-sets/massembly-1008/verify.groovy new file mode 100644 index 00000000..55d7b281 --- /dev/null +++ b/src/it/projects/dependency-sets/massembly-1008/verify.groovy @@ -0,0 +1,48 @@ +/* + * 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.*; + +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" +] + +File assemblyBasedir = new File( basedir, "target/massembly-1008-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 +} + +// defined set vs listed set: same cardinality and all present: OK + +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 9081af35..6bbd7b00 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 @@ -22,15 +22,24 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; +import org.apache.maven.RepositoryUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.model.DependencyManagement; import org.apache.maven.plugins.assembly.AssemblerConfigurationSource; import org.apache.maven.plugins.assembly.archive.ArchiveCreationException; import org.apache.maven.plugins.assembly.archive.phase.ModuleSetAssemblyPhase; @@ -40,6 +49,17 @@ import org.apache.maven.plugins.assembly.model.ModuleSet; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.StringUtils; +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; +import org.eclipse.aether.graph.DependencyVisitor; +import org.eclipse.aether.resolution.DependencyRequest; +import org.eclipse.aether.resolution.DependencyResult; +import org.eclipse.aether.util.filter.DependencyFilterUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +67,6 @@ /** * @author jdcasey - * */ @Singleton @Named @@ -56,9 +75,12 @@ public class DefaultDependencyResolver implements DependencyResolver { private final ArtifactHandlerManager artifactHandlerManager; + private final RepositorySystem repositorySystem; + @Inject - public DefaultDependencyResolver(ArtifactHandlerManager artifactHandlerManager) { + public DefaultDependencyResolver(ArtifactHandlerManager artifactHandlerManager, RepositorySystem repositorySystem) { this.artifactHandlerManager = requireNonNull(artifactHandlerManager); + this.repositorySystem = requireNonNull(repositorySystem); } @Override @@ -75,7 +97,8 @@ public Map> resolveDependencySets( final MavenProject currentProject = configSource.getProject(); final ResolutionManagementInfo info = new ResolutionManagementInfo(); - updateDependencySetResolutionRequirements(dependencySet, info, currentProject); + updateDependencySetResolutionRequirements( + configSource.getMavenSession().getRepositorySession(), dependencySet, info, currentProject); updateModuleSetResolutionRequirements(moduleSet, dependencySet, info, configSource); result.put(dependencySet, info.getArtifacts()); @@ -96,7 +119,8 @@ public Map> resolveDependencySets( final MavenProject currentProject = configSource.getProject(); final ResolutionManagementInfo info = new ResolutionManagementInfo(); - updateDependencySetResolutionRequirements(dependencySet, info, currentProject); + updateDependencySetResolutionRequirements( + configSource.getMavenSession().getRepositorySession(), dependencySet, info, currentProject); result.put(dependencySet, info.getArtifacts()); } @@ -127,7 +151,10 @@ void updateModuleSetResolutionRequirements( if (binaries.isIncludeDependencies()) { updateDependencySetResolutionRequirements( - dependencySet, requirements, projects.toArray(new MavenProject[0])); + configSource.getMavenSession().getRepositorySession(), + dependencySet, + requirements, + projects.toArray(new MavenProject[0])); } } } @@ -149,7 +176,10 @@ private Artifact createArtifact(String groupId, String artifactId, String versio } void updateDependencySetResolutionRequirements( - final DependencySet set, final ResolutionManagementInfo requirements, final MavenProject... projects) + RepositorySystemSession systemSession, + final DependencySet set, + final ResolutionManagementInfo requirements, + final MavenProject... projects) throws DependencyResolutionException { for (final MavenProject project : projects) { if (project == null) { @@ -158,14 +188,91 @@ void updateDependencySetResolutionRequirements( Set dependencyArtifacts = null; if (set.isUseTransitiveDependencies()) { - dependencyArtifacts = project.getArtifacts(); + try { + // we need resolve project again according to requested scope + dependencyArtifacts = resolveTransitive(systemSession, set.getScope(), project); + } catch (org.eclipse.aether.resolution.DependencyResolutionException e) { + throw new DependencyResolutionException(e.getMessage(), e); + } } else { + // FIXME remove using deprecated method dependencyArtifacts = project.getDependencyArtifacts(); } requirements.addArtifacts(dependencyArtifacts); - LOGGER.debug("Dependencies for project: " + project.getId() + " are:\n" - + StringUtils.join(dependencyArtifacts.iterator(), "\n")); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug( + "Dependencies for project: {} are:\n{}", + project.getId(), + StringUtils.join(dependencyArtifacts.iterator(), "\n")); + } } } + + private Set resolveTransitive( + RepositorySystemSession repositorySession, String scope, MavenProject project) + throws org.eclipse.aether.resolution.DependencyResolutionException { + + // scope dependency filter + DependencyFilter scoopeDependencyFilter = DependencyFilterUtils.classpathFilter(scope); + + // get project dependencies filtered by requested scope + 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()) + .map(DependencyManagement::getDependencies) + .map(list -> list.stream() + .map(d -> RepositoryUtils.toDependency(d, repositorySession.getArtifactTypeRegistry())) + .collect(Collectors.toList())) + .orElse(null); + + CollectRequest collectRequest = new CollectRequest(); + collectRequest.setManagedDependencies(managedDependencies); + collectRequest.setRepositories(project.getRemoteProjectRepositories()); + collectRequest.setDependencies(dependencies); + collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact())); + + DependencyRequest request = new DependencyRequest(collectRequest, scoopeDependencyFilter); + + DependencyResult dependencyResult = repositorySystem.resolveDependencies(repositorySession, request); + + // cache for artifact mapping + Map aetherToMavenArtifacts = new HashMap<>(); + Deque stack = new ArrayDeque<>(); + stack.push(project.getArtifact().getId()); + + Set artifacts = new HashSet<>(); + + // we need rebuild artifact dependencyTrail - it is used by useTransitiveFiltering + dependencyResult.getRoot().accept(new DependencyVisitor() { + @Override + public boolean visitEnter(DependencyNode node) { + if (node.getDependency() != null) { + stack.push(aetherToMavenArtifacts + .computeIfAbsent(node.getDependency().getArtifact(), RepositoryUtils::toArtifact) + .getId()); + } + return true; + } + + @Override + public boolean visitLeave(DependencyNode node) { + if (node.getDependency() != null) { + Artifact artifact = aetherToMavenArtifacts.computeIfAbsent( + node.getDependency().getArtifact(), RepositoryUtils::toArtifact); + List depTrail = new ArrayList<>(); + stack.descendingIterator().forEachRemaining(depTrail::add); + stack.pop(); + artifact.setDependencyTrail(depTrail); + artifacts.add(artifact); + } + return true; + } + }); + + return artifacts; + } } 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 dda82cb1..fb311754 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 @@ -27,55 +27,84 @@ import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.handler.DefaultArtifactHandler; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Model; import org.apache.maven.plugins.assembly.AssemblerConfigurationSource; import org.apache.maven.plugins.assembly.model.DependencySet; import org.apache.maven.plugins.assembly.model.ModuleBinaries; import org.apache.maven.plugins.assembly.model.ModuleSet; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.ContainerConfiguration; -import org.codehaus.plexus.PlexusConstants; -import org.codehaus.plexus.PlexusTestCase; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +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.Test; - +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class DefaultDependencyResolverTest extends PlexusTestCase { +@RunWith(MockitoJUnitRunner.class) +public class DefaultDependencyResolverTest { - private DefaultDependencyResolver resolver; + @Mock + private ArtifactHandlerManager artifactHandlerManager; - protected void customizeContainerConfiguration(ContainerConfiguration configuration) { - configuration.setClassPathScanning(PlexusConstants.SCANNING_CACHE).setAutoWiring(true); - } + @Mock + private RepositorySystem repositorySystem; - @Override - public void setUp() throws Exception { - super.setUp(); + @Mock + private RepositorySystemSession systemSession; - resolver = (DefaultDependencyResolver) lookup(DependencyResolver.class); - } + @InjectMocks + private DefaultDependencyResolver resolver; @Test - public void test_getDependencySetResolutionRequirements_transitive() throws DependencyResolutionException { + public void test_getDependencySetResolutionRequirements_transitive() throws Exception { final DependencySet ds = new DependencySet(); ds.setScope(Artifact.SCOPE_SYSTEM); ds.setUseTransitiveDependencies(true); final MavenProject project = createMavenProject("main-group", "main-artifact", "1", null); - Set dependencyArtifacts = new HashSet<>(); - dependencyArtifacts.add(newArtifact("g.id", "a-id", "1")); - Set artifacts = new HashSet<>(dependencyArtifacts); + Set artifacts = new HashSet<>(); + artifacts.add(newArtifact("g.id", "a-id", "1")); artifacts.add(newArtifact("g.id", "a-id-2", "2")); - project.setArtifacts(artifacts); - project.setDependencyArtifacts(dependencyArtifacts); + + DefaultDependencyNode node1 = new DefaultDependencyNode( + new Dependency(new org.eclipse.aether.artifact.DefaultArtifact("g.id:a-id:1"), "runtime")); + DefaultDependencyNode node2 = new DefaultDependencyNode( + new Dependency(new org.eclipse.aether.artifact.DefaultArtifact("g.id:a-id-2:2"), "runtime")); + + DependencyResult dependencyResult = new DependencyResult(new DependencyRequest()); + DefaultDependencyNode rootDependencyNode = new DefaultDependencyNode((Dependency) null); + rootDependencyNode.setChildren(Arrays.asList(node1, node2)); + dependencyResult.setRoot(rootDependencyNode); + + when(repositorySystem.resolveDependencies(eq(systemSession), any())).thenReturn(dependencyResult); final ResolutionManagementInfo info = new ResolutionManagementInfo(); - resolver.updateDependencySetResolutionRequirements(ds, info, project); + resolver.updateDependencySetResolutionRequirements(systemSession, ds, info, project); assertEquals(artifacts, info.getArtifacts()); + // dependencyTrail is set + info.getArtifacts().forEach(artifact -> { + assertEquals(2, artifact.getDependencyTrail().size()); + assertEquals( + project.getArtifact().getId(), artifact.getDependencyTrail().get(0)); + assertEquals(artifact.getId(), artifact.getDependencyTrail().get(1)); + }); } @Test @@ -94,7 +123,7 @@ public void test_getDependencySetResolutionRequirements_nonTransitive() throws D project.setDependencyArtifacts(dependencyArtifacts); final ResolutionManagementInfo info = new ResolutionManagementInfo(); - resolver.updateDependencySetResolutionRequirements(ds, info, project); + resolver.updateDependencySetResolutionRequirements(systemSession, ds, info, project); assertEquals(dependencyArtifacts, info.getArtifacts()); } @@ -118,7 +147,7 @@ public void test_getModuleSetResolutionRequirements_withoutBinaries() throws Dep } @Test - public void test_getModuleSetResolutionRequirements_includeDeps() throws DependencyResolutionException { + public void test_getModuleSetResolutionRequirements_includeDeps() throws Exception { final File rootDir = new File("root"); final MavenProject project = createMavenProject("main-group", "main-artifact", "1", rootDir); final MavenProject module1 = createMavenProject("main-group", "module-1", "1", new File(rootDir, "module-1")); @@ -135,6 +164,9 @@ public void test_getModuleSetResolutionRequirements_includeDeps() throws Depende final AssemblerConfigurationSource cs = mock(AssemblerConfigurationSource.class); when(cs.getReactorProjects()).thenReturn(Arrays.asList(project, module1, module2)); when(cs.getProject()).thenReturn(project); + MavenSession mavenSession = mock(MavenSession.class); + when(cs.getMavenSession()).thenReturn(mavenSession); + when(mavenSession.getRepositorySession()).thenReturn(systemSession); final ResolutionManagementInfo info = new ResolutionManagementInfo(); @@ -142,8 +174,19 @@ public void test_getModuleSetResolutionRequirements_includeDeps() throws Depende final ModuleBinaries mb = new ModuleBinaries(); mb.setIncludeDependencies(true); ms.setBinaries(mb); + // FIXME - this is not checked - because ms.UseAllReactorProjects is false ms.addInclude("*:module-1"); + DefaultDependencyNode node1 = new DefaultDependencyNode( + new Dependency(new org.eclipse.aether.artifact.DefaultArtifact("group.id:module-1-dep:1"), "runtime")); + + DependencyResult dependencyResult = new DependencyResult(new DependencyRequest()); + DefaultDependencyNode rootDependencyNode = new DefaultDependencyNode((Dependency) null); + rootDependencyNode.setChildren(Collections.singletonList(node1)); + dependencyResult.setRoot(rootDependencyNode); + + when(repositorySystem.resolveDependencies(eq(systemSession), any())).thenReturn(dependencyResult); + resolver.updateModuleSetResolutionRequirements(ms, new DependencySet(), info, cs); assertEquals(module1Artifacts, info.getArtifacts());