You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sl...@apache.org on 2026/10/06 21:43:49 UTC

(maven-dependency-plugin) branch master updated: [MDEP-803] Preserve parent POMs when copying classified dependencies (#1696)

This is an automated email from the ASF dual-hosted git repository.

slachiewicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-dependency-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new 6291daab [MDEP-803] Preserve parent POMs when copying classified dependencies (#1696)
6291daab is described below

commit 6291daabc5198d5fb3debbf1b83666b506cb14b3
Author: Václav Haisman <vh...@gmail.com>
AuthorDate: Tue Oct 6 23:43:37 2026 +0200

    [MDEP-803] Preserve parent POMs when copying classified dependencies (#1696)
    
    * [MDEP-803] Add regression for parent POMs with a sources classifier
    
    * [MDEP-803] Preserve parent POMs when copying classified dependencies
    
    Collect and filter parent POMs separately so classifier and type translation only applies to dependencies. Preserve explicit classified POMs, including artifacts that are also used as parents, and document the parameter interaction.
    
    Fixes #1312.
---
 .../mdep-803-parent-poms-with-classifier/pom.xml   | 108 +++++++++++++++++++++
 .../setup.groovy                                   |  60 ++++++++++++
 .../verify.groovy                                  |  48 +++++++++
 .../AbstractDependencyFilterMojo.java              |  14 ++-
 .../fromDependencies/CopyDependenciesMojo.java     |   2 +
 5 files changed, 228 insertions(+), 4 deletions(-)

diff --git a/src/it/projects/mdep-803-parent-poms-with-classifier/pom.xml b/src/it/projects/mdep-803-parent-poms-with-classifier/pom.xml
new file mode 100644
index 00000000..552ecc52
--- /dev/null
+++ b/src/it/projects/mdep-803-parent-poms-with-classifier/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.maven.its.mdep803</groupId>
+    <artifactId>project-parent</artifactId>
+    <version>1.0</version>
+    <relativePath />
+  </parent>
+  <artifactId>copy-parent-poms-with-classifier</artifactId>
+  <version>1.0</version>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.its.mdep803</groupId>
+      <artifactId>library</artifactId>
+      <version>1.0</version>
+    </dependency>
+    <!-- A parent can also be an explicit dependency whose attachment must still be copied. -->
+    <dependency>
+      <groupId>org.apache.maven.its.mdep803</groupId>
+      <artifactId>grandparent</artifactId>
+      <version>1.0</version>
+      <type>pom</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.its.mdep803</groupId>
+      <artifactId>classified</artifactId>
+      <version>1.0</version>
+      <type>pom</type>
+      <classifier>sources</classifier>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>@project.version@</version>
+        <configuration>
+          <classifier>sources</classifier>
+          <includeScope>runtime</includeScope>
+          <includeGroupIds>org.apache.maven.its.mdep803</includeGroupIds>
+          <copyPom>true</copyPom>
+          <addParentPoms>true</addParentPoms>
+        </configuration>
+        <executions>
+          <execution>
+            <id>sources</id>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/sources</outputDirectory>
+            </configuration>
+          </execution>
+          <execution>
+            <id>sources-jar</id>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/sources-jar</outputDirectory>
+              <type>jar</type>
+            </configuration>
+          </execution>
+          <execution>
+            <id>excluded-parent</id>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/excluded-parent</outputDirectory>
+              <excludeArtifactIds>dependency-parent</excludeArtifactIds>
+            </configuration>
+          </execution>
+          <execution>
+            <id>repository-layout</id>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/repository-layout</outputDirectory>
+              <useRepositoryLayout>true</useRepositoryLayout>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/projects/mdep-803-parent-poms-with-classifier/setup.groovy b/src/it/projects/mdep-803-parent-poms-with-classifier/setup.groovy
new file mode 100644
index 00000000..24808cdc
--- /dev/null
+++ b/src/it/projects/mdep-803-parent-poms-with-classifier/setup.groovy
@@ -0,0 +1,60 @@
+/*
+ * 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.util.jar.JarOutputStream
+import org.codehaus.plexus.util.FileUtils
+
+// Install a self-contained fixture into the Invoker's local repository.
+FileUtils.deleteDirectory(new File(localRepositoryPath, 'org/apache/maven/its/mdep803'))
+def groupId = 'org.apache.maven.its.mdep803'
+def artifactDir = { String artifactId ->
+    def dir = new File(localRepositoryPath, "org/apache/maven/its/mdep803/${artifactId}/1.0")
+    dir.mkdirs()
+    dir
+}
+def pom = { String artifactId, String parent, String packaging ->
+    def parentXml = parent == null ? '' : """
+  <parent>
+    <groupId>${groupId}</groupId><artifactId>${parent}</artifactId><version>1.0</version><relativePath/>
+  </parent>"""
+    new File(artifactDir(artifactId), "${artifactId}-1.0.pom").text = """<project>
+  <modelVersion>4.0.0</modelVersion>${parentXml}
+  <groupId>${groupId}</groupId><artifactId>${artifactId}</artifactId><version>1.0</version>
+  <packaging>${packaging}</packaging>
+</project>
+"""
+}
+pom('grandparent', null, 'pom')
+pom('project-parent', 'grandparent', 'pom')
+pom('dependency-parent', 'grandparent', 'pom')
+pom('library', 'dependency-parent', 'jar')
+pom('classified', null, 'pom')
+
+// Only explicit dependencies have sources attachments. Automatically added parents
+// must be copied as ordinary POMs even when classifier or type is specified.
+['library', 'grandparent', 'classified'].each { artifactId ->
+    new JarOutputStream(new FileOutputStream(new File(artifactDir(artifactId), "${artifactId}-1.0-sources.jar"))).close()
+}
+new JarOutputStream(new FileOutputStream(new File(artifactDir('library'), 'library-1.0.jar'))).close()
+['grandparent', 'classified'].each { artifactId ->
+    new File(artifactDir(artifactId), "${artifactId}-1.0-sources.pom").text =
+            new File(artifactDir(artifactId), "${artifactId}-1.0.pom").text.replace(
+                    '</project>', '<description>sources attachment</description></project>')
+}
+return true
diff --git a/src/it/projects/mdep-803-parent-poms-with-classifier/verify.groovy b/src/it/projects/mdep-803-parent-poms-with-classifier/verify.groovy
new file mode 100644
index 00000000..01c44878
--- /dev/null
+++ b/src/it/projects/mdep-803-parent-poms-with-classifier/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.
+ */
+
+def parents = ['project-parent-1.0.pom', 'dependency-parent-1.0.pom', 'grandparent-1.0.pom']
+def dependencyPoms = ['library-1.0.pom', 'classified-1.0.pom']
+def sources = ['library-1.0-sources.jar', 'grandparent-1.0-sources.pom', 'classified-1.0-sources.pom']
+
+['sources', 'sources-jar', 'excluded-parent'].each { name ->
+    def expected = parents + dependencyPoms + sources
+    if (name == 'sources-jar') {
+        expected = expected.collect { it.endsWith('-sources.pom') ? it.replace('-sources.pom', '-sources.jar') : it }
+    }
+    if (name == 'excluded-parent') {
+        expected = expected - 'dependency-parent-1.0.pom'
+    }
+    def output = new File(basedir, "target/${name}")
+    def actual = output.list().toList().sort()
+    assert actual == expected.sort() : "Unexpected copied artifacts in ${name}: ${actual}; expected ${expected}"
+}
+
+// Explicit classified POMs must not be replaced with their primary POMs.
+['grandparent', 'classified'].each { artifactId ->
+    assert new File(basedir, "target/sources/${artifactId}-1.0-sources.pom").text.contains('sources attachment')
+}
+
+// Repository layout does not add a primary POM for an artifact whose type is already pom.
+(parents + ['library-1.0.pom'] + sources).each { name ->
+    def artifactId = name.substring(0, name.indexOf('-1.0'))
+    assert new File(basedir, "target/repository-layout/org/apache/maven/its/mdep803/${artifactId}/1.0/${name}").isFile()
+}
+assert !new File(basedir, 'target/repository-layout/org/apache/maven/its/mdep803/project-parent/1.0/project-parent-1.0-sources.pom').exists()
+return true
diff --git a/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/AbstractDependencyFilterMojo.java b/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/AbstractDependencyFilterMojo.java
index a0a23ca4..a483519a 100644
--- a/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/AbstractDependencyFilterMojo.java
+++ b/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/AbstractDependencyFilterMojo.java
@@ -21,7 +21,6 @@ package org.apache.maven.plugins.dependency.fromDependencies;
 import javax.inject.Inject;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -349,19 +348,22 @@ public abstract class AbstractDependencyFilterMojo extends AbstractDependencyMoj
             throw new MojoExecutionException("Failed to collect artifacts", e);
         }
 
+        // A POM can be both a dependency to translate and a parent to copy unchanged.
+        Set<Artifact> parentArtifacts = new LinkedHashSet<>();
         if (includeParents) {
             // add dependencies parents
-            for (Artifact dep : new ArrayList<>(artifacts)) {
-                addParentArtifacts(buildProjectFromArtifact(dep), artifacts);
+            for (Artifact dep : artifacts) {
+                addParentArtifacts(buildProjectFromArtifact(dep), parentArtifacts);
             }
 
             // add current project parent
-            addParentArtifacts(getProject(), artifacts);
+            addParentArtifacts(getProject(), parentArtifacts);
         }
 
         // perform filtering
         try {
             artifacts = filter.filter(artifacts);
+            parentArtifacts = filter.filter(parentArtifacts);
         } catch (ArtifactFilterException e) {
             throw new MojoExecutionException(e.getMessage(), e);
         }
@@ -370,7 +372,11 @@ public abstract class AbstractDependencyFilterMojo extends AbstractDependencyMoj
         DependencyStatusSets status;
         if (classifier != null && !classifier.isEmpty()) {
             status = getClassifierTranslatedDependencies(artifacts, stopOnFailure);
+            DependencyStatusSets parentStatus = filterMarkedDependencies(parentArtifacts);
+            status.getResolvedDependencies().addAll(parentStatus.getResolvedDependencies());
+            status.getSkippedDependencies().addAll(parentStatus.getSkippedDependencies());
         } else {
+            artifacts.addAll(parentArtifacts);
             status = filterMarkedDependencies(artifacts);
         }
 
diff --git a/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/CopyDependenciesMojo.java b/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/CopyDependenciesMojo.java
index 064f79fd..c12d7287 100644
--- a/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/CopyDependenciesMojo.java
+++ b/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/CopyDependenciesMojo.java
@@ -86,6 +86,8 @@ public class CopyDependenciesMojo extends AbstractFromDependenciesMojo {
 
     /**
      * Add parent poms to the list of copied dependencies (both current project pom parents and dependencies parents).
+     * The {@code classifier} and {@code type} parameters do not change parent POM coordinates.
+     * Inclusion and exclusion filters still apply.
      *
      * @since 2.8
      */