This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/maven-4.0.x by this push:
new 5ec059c2d7 Fix false parent cycle detection with flatten-maven-plugin
(fixes #11399) (#11400)
5ec059c2d7 is described below
commit 5ec059c2d75393aaada2d2c1d0fe0100a1f79554
Author: Guillaume Nodet <[email protected]>
AuthorDate: Thu Nov 6 16:35:52 2025 +0100
Fix false parent cycle detection with flatten-maven-plugin (fixes #11399)
(#11400)
When using flatten-maven-plugin with updatePomFile=true and parent
expansion,
Maven incorrectly detected a parent cycle during the install phase. The
error
occurred because the consumer POM builder was using Path instead of
ModelSource
when reading the flattened POM.
This change updates the PomArtifactTransformer API to use ModelSource
instead
of Path. ModelSource includes the necessary context (base directory,
ModelLocator)
to properly resolve parent POMs and avoid false cycle detection.
Changes:
- Updated PomArtifactTransformer.transform() to accept ModelSource instead
of Path
- Modified ConsumerPomArtifactTransformer to create ModelSource with proper
resolution context
- Updated DefaultConsumerPomBuilder and related classes to work with
ModelSource
- Added integration test to verify the fix
Fixes #11399
---
.../transformation/PomArtifactTransformer.java | 3 +-
.../impl/ConsumerPomArtifactTransformer.java | 43 +++++++++++++-
.../impl/DefaultConsumerPomBuilder.java | 19 +++---
.../internal/transformation/impl/PomBuilder.java | 4 +-
.../transformation/impl/TransformedArtifact.java | 14 +++--
.../transformation/impl/TransformerSupport.java | 3 +-
.../impl/ConsumerPomArtifactTransformerTest.java | 9 +--
.../impl/ConsumerPomBuilderTest.java | 4 +-
...MavenITgh11399FlattenPluginParentCycleTest.java | 65 +++++++++++++++++++++
.../gh-11399-flatten-plugin-parent-cycle/pom.xml | 68 ++++++++++++++++++++++
10 files changed, 205 insertions(+), 27 deletions(-)
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/PomArtifactTransformer.java
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/PomArtifactTransformer.java
index dbccd89858..30b62384a4 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/PomArtifactTransformer.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/PomArtifactTransformer.java
@@ -24,6 +24,7 @@
import java.nio.file.Path;
import org.apache.maven.api.services.ModelBuilderException;
+import org.apache.maven.api.services.ModelSource;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.deployment.DeployRequest;
@@ -41,6 +42,6 @@ public interface PomArtifactTransformer {
void injectTransformedArtifacts(RepositorySystemSession session,
MavenProject currentProject) throws IOException;
- void transform(MavenProject project, RepositorySystemSession session, Path
src, Path tgt)
+ void transform(MavenProject project, RepositorySystemSession session,
ModelSource src, Path tgt)
throws ModelBuilderException, XMLStreamException, IOException;
}
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java
index 4b2722405c..adaf72c7ba 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java
@@ -23,7 +23,9 @@
import javax.inject.Singleton;
import javax.xml.stream.XMLStreamException;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -36,6 +38,9 @@
import org.apache.maven.api.feature.Features;
import org.apache.maven.api.model.Model;
import org.apache.maven.api.services.ModelBuilderException;
+import org.apache.maven.api.services.ModelSource;
+import org.apache.maven.api.services.Source;
+import org.apache.maven.api.services.Sources;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.artifact.ProjectArtifact;
import org.eclipse.aether.RepositorySystemSession;
@@ -93,19 +98,53 @@ public void
injectTransformedArtifacts(RepositorySystemSession session, MavenPro
TransformedArtifact createConsumerPomArtifact(
MavenProject project, Path consumer, RepositorySystemSession
session) {
+ Path actual = project.getFile().toPath();
+ Path parent = project.getBaseDirectory();
+ ModelSource source = new ModelSource() {
+ @Override
+ public Path getPath() {
+ return actual;
+ }
+
+ @Override
+ public InputStream openStream() throws IOException {
+ return Files.newInputStream(actual);
+ }
+
+ @Override
+ public String getLocation() {
+ return actual.toString();
+ }
+
+ @Override
+ public Source resolve(String relative) {
+ return Sources.buildSource(actual.resolve(relative));
+ }
+
+ @Override
+ public ModelSource resolve(ModelLocator modelLocator, String
relative) {
+ String norm = relative.replace('\\',
File.separatorChar).replace('/', File.separatorChar);
+ Path path = parent.resolve(norm);
+ Path relatedPom = modelLocator.locateExistingPom(path);
+ if (relatedPom != null) {
+ return Sources.buildSource(relatedPom);
+ }
+ return null;
+ }
+ };
return new TransformedArtifact(
this,
project,
consumer,
session,
new ProjectArtifact(project),
- () -> project.getFile().toPath(),
+ () -> source,
CONSUMER_POM_CLASSIFIER,
"pom");
}
@Override
- public void transform(MavenProject project, RepositorySystemSession
session, Path src, Path tgt)
+ public void transform(MavenProject project, RepositorySystemSession
session, ModelSource src, Path tgt)
throws ModelBuilderException, XMLStreamException, IOException {
Model model = builder.build(session, project, src);
write(model, tgt);
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
index d88abdc37d..9d92f84828 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
@@ -21,7 +21,6 @@
import javax.inject.Inject;
import javax.inject.Named;
-import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -45,7 +44,7 @@
import org.apache.maven.api.services.ModelBuilderException;
import org.apache.maven.api.services.ModelBuilderRequest;
import org.apache.maven.api.services.ModelBuilderResult;
-import org.apache.maven.api.services.Sources;
+import org.apache.maven.api.services.ModelSource;
import org.apache.maven.api.services.model.LifecycleBindingsInjector;
import org.apache.maven.impl.InternalSession;
import org.apache.maven.model.v4.MavenModelVersion;
@@ -71,7 +70,8 @@ class DefaultConsumerPomBuilder implements PomBuilder {
}
@Override
- public Model build(RepositorySystemSession session, MavenProject project,
Path src) throws ModelBuilderException {
+ public Model build(RepositorySystemSession session, MavenProject project,
ModelSource src)
+ throws ModelBuilderException {
Model model = project.getModel().getDelegate();
boolean flattenEnabled =
Features.consumerPomFlatten(session.getConfigProperties());
@@ -95,27 +95,27 @@ public Model build(RepositorySystemSession session,
MavenProject project, Path s
}
}
- protected Model buildPom(RepositorySystemSession session, MavenProject
project, Path src)
+ protected Model buildPom(RepositorySystemSession session, MavenProject
project, ModelSource src)
throws ModelBuilderException {
ModelBuilderResult result = buildModel(session, src);
Model model = result.getRawModel();
return transformPom(model, project);
}
- protected Model buildBom(RepositorySystemSession session, MavenProject
project, Path src)
+ protected Model buildBom(RepositorySystemSession session, MavenProject
project, ModelSource src)
throws ModelBuilderException {
ModelBuilderResult result = buildModel(session, src);
Model model = result.getEffectiveModel();
return transformBom(model, project);
}
- protected Model buildNonPom(RepositorySystemSession session, MavenProject
project, Path src)
+ protected Model buildNonPom(RepositorySystemSession session, MavenProject
project, ModelSource src)
throws ModelBuilderException {
Model model = buildEffectiveModel(session, src);
return transformNonPom(model, project);
}
- private Model buildEffectiveModel(RepositorySystemSession session, Path
src) throws ModelBuilderException {
+ private Model buildEffectiveModel(RepositorySystemSession session,
ModelSource src) throws ModelBuilderException {
InternalSession iSession = InternalSession.from(session);
ModelBuilderResult result = buildModel(session, src);
Model model = result.getEffectiveModel();
@@ -222,12 +222,13 @@ private static String getDependencyKey(Dependency
dependency) {
+ (dependency.getClassifier() != null ?
dependency.getClassifier() : "");
}
- private ModelBuilderResult buildModel(RepositorySystemSession session,
Path src) throws ModelBuilderException {
+ private ModelBuilderResult buildModel(RepositorySystemSession session,
ModelSource src)
+ throws ModelBuilderException {
InternalSession iSession = InternalSession.from(session);
ModelBuilderRequest.ModelBuilderRequestBuilder request =
ModelBuilderRequest.builder();
request.requestType(ModelBuilderRequest.RequestType.BUILD_CONSUMER);
request.session(iSession);
- request.source(Sources.buildSource(src));
+ request.source(src);
request.locationTracking(false);
request.systemProperties(session.getSystemProperties());
request.userProperties(session.getUserProperties());
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/PomBuilder.java
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/PomBuilder.java
index 4a62e4c6a3..86f9bed73a 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/PomBuilder.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/PomBuilder.java
@@ -21,10 +21,10 @@
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
-import java.nio.file.Path;
import org.apache.maven.api.model.Model;
import org.apache.maven.api.services.ModelBuilderException;
+import org.apache.maven.api.services.ModelSource;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystemSession;
@@ -33,6 +33,6 @@
* of {@link ConsumerPomArtifactTransformer}.
*/
interface PomBuilder {
- Model build(RepositorySystemSession session, MavenProject project, Path
src)
+ Model build(RepositorySystemSession session, MavenProject project,
ModelSource src)
throws ModelBuilderException, IOException, XMLStreamException;
}
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifact.java
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifact.java
index bcaa67f52d..b3ddb2c5fa 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifact.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifact.java
@@ -30,6 +30,7 @@
import java.util.function.Supplier;
import org.apache.maven.api.services.ModelBuilderException;
+import org.apache.maven.api.services.ModelSource;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.internal.transformation.PomArtifactTransformer;
import org.apache.maven.internal.transformation.TransformationFailedException;
@@ -48,7 +49,7 @@ class TransformedArtifact extends DefaultArtifact {
private static final int SHA1_BUFFER_SIZE = 8192;
private final PomArtifactTransformer pomArtifactTransformer;
private final MavenProject project;
- private final Supplier<Path> sourcePathProvider;
+ private final Supplier<ModelSource> sourcePathProvider;
private final Path target;
private final RepositorySystemSession session;
private final AtomicReference<String> sourceState;
@@ -60,7 +61,7 @@ class TransformedArtifact extends DefaultArtifact {
Path target,
RepositorySystemSession session,
org.apache.maven.artifact.Artifact source,
- Supplier<Path> sourcePathProvider,
+ Supplier<ModelSource> sourcePathProvider,
String classifier,
String extension) {
super(
@@ -105,20 +106,21 @@ public synchronized File getFile() {
private String mayUpdate() throws IOException, XMLStreamException,
ModelBuilderException {
String result;
- Path src = sourcePathProvider.get();
+ ModelSource src = sourcePathProvider.get();
if (src == null) {
Files.deleteIfExists(target);
result = null;
- } else if (!Files.exists(src)) {
+ } else if (!Files.exists(src.getPath())) {
Files.deleteIfExists(target);
result = "";
} else {
- String current = ChecksumAlgorithmHelper.calculate(src,
List.of(new Sha1ChecksumAlgorithmFactory()))
+ String current = ChecksumAlgorithmHelper.calculate(
+ src.getPath(), List.of(new
Sha1ChecksumAlgorithmFactory()))
.get(Sha1ChecksumAlgorithmFactory.NAME);
String existing = sourceState.get();
if (!Files.exists(target) || !Objects.equals(current, existing)) {
pomArtifactTransformer.transform(project, session, src,
target);
- Files.setLastModifiedTime(target,
Files.getLastModifiedTime(src));
+ Files.setLastModifiedTime(target,
Files.getLastModifiedTime(src.getPath()));
}
result = current;
}
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformerSupport.java
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformerSupport.java
index 84e721225f..3c4149120d 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformerSupport.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformerSupport.java
@@ -28,6 +28,7 @@
import org.apache.maven.api.model.Model;
import org.apache.maven.api.services.ModelBuilderException;
+import org.apache.maven.api.services.ModelSource;
import org.apache.maven.internal.transformation.PomArtifactTransformer;
import org.apache.maven.model.v4.MavenStaxReader;
import org.apache.maven.model.v4.MavenStaxWriter;
@@ -58,7 +59,7 @@ public DeployRequest
remapDeployArtifacts(RepositorySystemSession session, Deplo
public void injectTransformedArtifacts(RepositorySystemSession session,
MavenProject project) throws IOException {}
@Override
- public void transform(MavenProject project, RepositorySystemSession
session, Path src, Path tgt)
+ public void transform(MavenProject project, RepositorySystemSession
session, ModelSource src, Path tgt)
throws ModelBuilderException, XMLStreamException, IOException {
throw new IllegalStateException("This transformer does not use this
call.");
}
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java
b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java
index ec08041f24..4bdc242e01 100644
---
a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java
+++
b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java
@@ -24,6 +24,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
+import org.apache.maven.api.services.Sources;
import org.apache.maven.model.Model;
import org.apache.maven.model.v4.MavenStaxReader;
import org.apache.maven.project.MavenProject;
@@ -55,12 +56,12 @@ void transform() throws Exception {
MavenProject project = new MavenProject(model);
project.setOriginalModel(model);
ConsumerPomArtifactTransformer t = new
ConsumerPomArtifactTransformer((s, p, f) -> {
- try (InputStream is = Files.newInputStream(f)) {
+ try (InputStream is = f.openStream()) {
return DefaultConsumerPomBuilder.transformPom(new
MavenStaxReader().read(is), project);
}
});
- t.transform(project, systemSessionMock, beforePomFile, tempFile);
+ t.transform(project, systemSessionMock,
Sources.buildSource(beforePomFile), tempFile);
}
XmlAssert.assertThat(tempFile.toFile()).and(afterPomFile.toFile()).areIdentical();
}
@@ -82,12 +83,12 @@ void transformJarConsumerPom() throws Exception {
MavenProject project = new MavenProject(model);
project.setOriginalModel(model);
ConsumerPomArtifactTransformer t = new
ConsumerPomArtifactTransformer((s, p, f) -> {
- try (InputStream is = Files.newInputStream(f)) {
+ try (InputStream is = f.openStream()) {
return DefaultConsumerPomBuilder.transformNonPom(new
MavenStaxReader().read(is), project);
}
});
- t.transform(project, systemSessionMock, beforePomFile, tempFile);
+ t.transform(project, systemSessionMock,
Sources.buildSource(beforePomFile), tempFile);
}
XmlAssert.assertThat(afterPomFile.toFile()).and(tempFile.toFile()).areIdentical();
}
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
index 62f17df33b..11dc8cd9c7 100644
---
a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
+++
b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
@@ -108,7 +108,7 @@ void testTrivialConsumer() throws Exception {
MavenProject project = new MavenProject(orgModel);
project.setOriginalModel(new org.apache.maven.model.Model(orgModel));
- Model model = builder.build(session, project, file);
+ Model model = builder.build(session, project,
Sources.buildSource(file));
assertNotNull(model);
}
@@ -135,7 +135,7 @@ void testSimpleConsumer() throws Exception {
MavenProject project = new MavenProject(orgModel);
project.setOriginalModel(new org.apache.maven.model.Model(orgModel));
request.setRootDirectory(Paths.get("src/test/resources/consumer/simple"));
- Model model = builder.build(session, project, file);
+ Model model = builder.build(session, project,
Sources.buildSource(file));
assertNotNull(model);
assertTrue(model.getProfiles().isEmpty());
diff --git
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11399FlattenPluginParentCycleTest.java
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11399FlattenPluginParentCycleTest.java
new file mode 100644
index 0000000000..bb4bd624a4
--- /dev/null
+++
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11399FlattenPluginParentCycleTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.it;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is a test set for <a
href="https://github.com/apache/maven/issues/11399">GH-11399</a>.
+ *
+ * Verifies that using flatten-maven-plugin with updatePomFile=true does not
cause a false
+ * parent cycle detection error during install phase. The issue occurred when
the plugin
+ * updated the POM file reference, causing the consumer POM builder to
incorrectly detect
+ * a cycle between the project and its parent.
+ *
+ * @see <a
href="https://github.com/mojohaus/flatten-maven-plugin">flatten-maven-plugin</a>
+ */
+public class MavenITgh11399FlattenPluginParentCycleTest extends
AbstractMavenIntegrationTestCase {
+
+ public MavenITgh11399FlattenPluginParentCycleTest() {
+ super("(4.0.0-rc-3,)");
+ }
+
+ /**
+ * Verify that flatten-maven-plugin with updatePomFile=true and parent
expansion
+ * does not cause a false parent cycle detection error during install.
+ *
+ * The error was:
+ * "The parents form a cycle: org.apache:apache:35 -> /path/to/pom.xml ->
org.apache:apache:35"
+ *
+ * @throws Exception in case of failure
+ */
+ @Test
+ public void testFlattenPluginWithParentExpansionDoesNotCauseCycle() throws
Exception {
+ File testDir =
extractResources("/gh-11399-flatten-plugin-parent-cycle");
+
+ Verifier verifier = newVerifier(testDir.getAbsolutePath());
+ verifier.setAutoclean(false);
+ verifier.deleteArtifacts("org.apache.maven.its.mng8750");
+ verifier.addCliArgument("install");
+ verifier.execute();
+ verifier.verifyErrorFreeLog();
+
+ // Verify that the flattened POM was created
+ verifier.verifyFilePresent("target/.flattened-pom.xml");
+ }
+}
+
diff --git
a/its/core-it-suite/src/test/resources/gh-11399-flatten-plugin-parent-cycle/pom.xml
b/its/core-it-suite/src/test/resources/gh-11399-flatten-plugin-parent-cycle/pom.xml
new file mode 100644
index 0000000000..36aee9c658
--- /dev/null
+++
b/its/core-it-suite/src/test/resources/gh-11399-flatten-plugin-parent-cycle/pom.xml
@@ -0,0 +1,68 @@
+<?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</groupId>
+ <artifactId>apache</artifactId>
+ <version>35</version>
+ </parent>
+
+ <groupId>org.apache.maven.its.gh11399</groupId>
+ <artifactId>test-project</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>GH-11399 Flatten Plugin Parent Cycle Test</name>
+ <description>
+ Test project to verify that flatten-maven-plugin with
updatePomFile=true
+ does not cause a false parent cycle detection error.
+ </description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>flatten-maven-plugin</artifactId>
+ <version>1.7.3</version>
+ <executions>
+ <execution>
+ <id>flatten</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>flatten</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>target</outputDirectory>
+ <updatePomFile>true</updatePomFile>
+ <pomElements>
+ <parent>expand</parent>
+ </pomElements>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
+