diff --git a/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java b/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java index 09e3956fb..89a8594ca 100644 --- a/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java +++ b/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java @@ -1230,8 +1230,17 @@ private void rewriteDependencyReducedPomIfWeHaveReduction( parentFile = parentFile.getCanonicalFile(); - String relPath = RelativizePath.convertToRelativePath(parentFile, f); - model.getParent().setRelativePath(relPath); + File actualParentFile = + project.getParent() != null ? project.getParent().getFile() : null; + if (parentFileIsTheConfiguredRelativePath(parentFile, actualParentFile)) { + String relPath = RelativizePath.convertToRelativePath(parentFile, f); + model.getParent().setRelativePath(relPath); + } else { + // ../pom.xml is a different GAV (or missing). An empty + // relativePath stops Maven 4 from walking into that POM + // and reporting a false parent cycle. + model.getParent().setRelativePath(""); + } } try { @@ -1260,6 +1269,23 @@ private void rewriteDependencyReducedPomIfWeHaveReduction( } } + /** + * Keep a local relativePath only when it really points at the parent POM. + * Otherwise Maven 4 treats the default {@code ..} as the parent and reports + * a cycle against a sibling POM that happens to share the same parent GAV. + */ + static boolean parentFileIsTheConfiguredRelativePath(File configured, File actual) { + try { + return actual != null + && configured != null + && actual.isFile() + && configured.isFile() + && actual.getCanonicalFile().equals(configured.getCanonicalFile()); + } catch (IOException e) { + return false; + } + } + private void removeSystemScopedDependencies(Set artifactsToRemove, List originalDependencies) { for (Dependency dependency : originalDependencies) { if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) { diff --git a/src/main/java/org/apache/maven/plugins/shade/pom/MavenJDOMWriter.java b/src/main/java/org/apache/maven/plugins/shade/pom/MavenJDOMWriter.java index 5d3db0472..9bf9f0da8 100644 --- a/src/main/java/org/apache/maven/plugins/shade/pom/MavenJDOMWriter.java +++ b/src/main/java/org/apache/maven/plugins/shade/pom/MavenJDOMWriter.java @@ -1525,7 +1525,13 @@ protected void updateParent(Parent value, String xmlTag, Counter counter, Elemen findAndReplaceSimpleElement(innerCount, root, "artifactId", value.getArtifactId(), null); findAndReplaceSimpleElement(innerCount, root, "groupId", value.getGroupId(), null); findAndReplaceSimpleElement(innerCount, root, "version", value.getVersion(), null); - findAndReplaceSimpleElement(innerCount, root, "relativePath", value.getRelativePath(), "../pom.xml"); + String relativePath = value.getRelativePath(); + if (relativePath != null && relativePath.isEmpty()) { + Element rel = updateElement(innerCount, root, "relativePath", true); + rel.setText(""); + } else { + findAndReplaceSimpleElement(innerCount, root, "relativePath", relativePath, "../pom.xml"); + } } } diff --git a/src/test/java/org/apache/maven/plugins/shade/mojo/ShadeMojoParentPathTest.java b/src/test/java/org/apache/maven/plugins/shade/mojo/ShadeMojoParentPathTest.java new file mode 100644 index 000000000..ec5e66bcc --- /dev/null +++ b/src/test/java/org/apache/maven/plugins/shade/mojo/ShadeMojoParentPathTest.java @@ -0,0 +1,56 @@ +/* + * 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. + */ +package org.apache.maven.plugins.shade.mojo; + +import java.io.File; +import java.nio.file.Files; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ShadeMojoParentPathTest { + @TempDir + File temporaryFolder; + + @Test + public void matchingParentFilesKeepTheRelativePath() throws Exception { + File pom = new File(temporaryFolder, "pom.xml"); + Files.write(pom.toPath(), "".getBytes("UTF-8")); + assertTrue(ShadeMojo.parentFileIsTheConfiguredRelativePath(pom, pom)); + } + + @Test + public void aSiblingPomIsNotTheParent() throws Exception { + File parentPom = new File(temporaryFolder, "real-parent.xml"); + File siblingPom = new File(temporaryFolder, "pom.xml"); + Files.write(parentPom.toPath(), "".getBytes("UTF-8")); + Files.write(siblingPom.toPath(), "".getBytes("UTF-8")); + assertFalse(ShadeMojo.parentFileIsTheConfiguredRelativePath(siblingPom, parentPom)); + } + + @Test + public void missingActualParentDropsTheRelativePath() throws Exception { + File siblingPom = new File(temporaryFolder, "pom.xml"); + Files.write(siblingPom.toPath(), "".getBytes("UTF-8")); + assertFalse(ShadeMojo.parentFileIsTheConfiguredRelativePath(siblingPom, null)); + } +} diff --git a/src/test/java/org/apache/maven/plugins/shade/pom/PomWriterRelativePathTest.java b/src/test/java/org/apache/maven/plugins/shade/pom/PomWriterRelativePathTest.java new file mode 100644 index 000000000..be780889d --- /dev/null +++ b/src/test/java/org/apache/maven/plugins/shade/pom/PomWriterRelativePathTest.java @@ -0,0 +1,72 @@ +/* + * 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. + */ +package org.apache.maven.plugins.shade.pom; + +import java.io.StringWriter; + +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PomWriterRelativePathTest { + @Test + public void emptyRelativePathIsWritten() throws Exception { + Model model = new Model(); + model.setModelVersion("4.0.0"); + model.setGroupId("g"); + model.setArtifactId("a"); + model.setVersion("1.0"); + Parent parent = new Parent(); + parent.setGroupId("pg"); + parent.setArtifactId("pa"); + parent.setVersion("1.0"); + parent.setRelativePath(""); + model.setParent(parent); + + StringWriter writer = new StringWriter(); + PomWriter.write(writer, model, true); + String xml = writer.toString(); + + assertTrue(xml.contains("../pom.xml"), xml); + } + + @Test + public void defaultRelativePathIsOmitted() throws Exception { + Model model = new Model(); + model.setModelVersion("4.0.0"); + model.setGroupId("g"); + model.setArtifactId("a"); + model.setVersion("1.0"); + Parent parent = new Parent(); + parent.setGroupId("pg"); + parent.setArtifactId("pa"); + parent.setVersion("1.0"); + model.setParent(parent); + + StringWriter writer = new StringWriter(); + PomWriter.write(writer, model, true); + String xml = writer.toString(); + + assertFalse(xml.contains("relativePath"), xml); + } +}