diff --git a/src/it/projects/flatten-dependency-all-both-test-and-transitive/pom.xml b/src/it/projects/flatten-dependency-all-both-test-and-transitive/pom.xml
new file mode 100644
index 00000000..8ca9f3a6
--- /dev/null
+++ b/src/it/projects/flatten-dependency-all-both-test-and-transitive/pom.xml
@@ -0,0 +1,35 @@
+
+ 4.0.0
+ org.codehaus.mojo.flatten.its
+ flatten-dependency-all-both-test-and-transitive
+ 0.0.1-SNAPSHOT
+
+
+ verify
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+
+ oss
+ all
+
+
+
+
+
+
+
+ org.codehaus.mojo.flatten.its
+ core
+
+ 3.2.1
+
+
+ org.codehaus.mojo.flatten.its
+ dep
+ 3.2.1
+ test
+
+
+
\ No newline at end of file
diff --git a/src/it/projects/flatten-dependency-all-both-test-and-transitive/verify.groovy b/src/it/projects/flatten-dependency-all-both-test-and-transitive/verify.groovy
new file mode 100644
index 00000000..8a1426c4
--- /dev/null
+++ b/src/it/projects/flatten-dependency-all-both-test-and-transitive/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.
+ */
+File originalPom = new File( basedir, 'pom.xml' )
+assert originalPom.exists()
+
+def originalProject = new XmlSlurper().parse( originalPom )
+assert 2 == originalProject.dependencies.dependency.size()
+assert "dep" == originalProject.dependencies.dependency[1].artifactId.text()
+assert "3.2.1" == originalProject.dependencies.dependency[1].version.text()
+assert "test" == originalProject.dependencies.dependency[1].scope.text()
+
+File flattenedPom = new File( basedir, '.flattened-pom.xml' )
+assert flattenedPom.exists()
+
+def flattenedProject = new XmlSlurper().parse( flattenedPom )
+
+// core and dep should be there. It's because while the test-scope dep (the
+// direct dependency), core declares dep as compile-scope (default) dependency.
+assert 2 == flattenedProject.dependencies.dependency.size()
+
+assert "core" == flattenedProject.dependencies.dependency[0].artifactId.text()
+assert "3.2.1" == flattenedProject.dependencies.dependency[0].version.text()
+assert "compile" == flattenedProject.dependencies.dependency[0].scope.text()
+
+// The flattened pom.xml should declare the dep under core as compile scope.
+// It's ok to ignore the one in the test-scope dependency.
+assert "dep" == flattenedProject.dependencies.dependency[1].artifactId.text()
+assert "3.2.1" == flattenedProject.dependencies.dependency[1].version.text()
+assert "compile" == flattenedProject.dependencies.dependency[1].scope.text()
diff --git a/src/main/java/org/codehaus/mojo/flatten/FlattenMojo.java b/src/main/java/org/codehaus/mojo/flatten/FlattenMojo.java
index 918c53d5..a5c10d8b 100644
--- a/src/main/java/org/codehaus/mojo/flatten/FlattenMojo.java
+++ b/src/main/java/org/codehaus/mojo/flatten/FlattenMojo.java
@@ -87,6 +87,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
@@ -1094,11 +1095,11 @@ private void createFlattenedDependenciesDirect( List projectDependen
* The collected dependencies are stored in order, so that the leaf dependencies are prioritized in front of direct dependencies.
* In addition, every non-leaf dependencies will exclude its own direct dependency, since all transitive dependencies
* will be collected.
- *
+ *
* Transitive dependencies are all going to be collected and become a direct dependency. Maven should already resolve
* versions properly because now the transitive dependencies are closer to the artifact. However, when this artifact is
* being consumed, Maven Enforcer Convergence rule will fail because there may be multiple versions for the same transitive dependency.
- *
+ *
* Typically, exclusion can be done by using the wildcard. However, a known Maven issue prevents convergence enforcer from
* working properly w/ wildcard exclusions. Thus, this will exclude each dependencies explicitly rather than using the wildcard.
*
@@ -1116,7 +1117,7 @@ private void createFlattenedDependenciesAll( List projectDependencie
final Artifact projectArtifact = this.project.getArtifact();
ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
- buildingRequest.setProject( project );
+ buildingRequest.setProject( cloneProjectWithoutTestDependencies( project ) );
final DependencyNode dependencyNode = this.dependencyGraphBuilder.buildDependencyGraph( buildingRequest, null);
@@ -1205,6 +1206,43 @@ private void createFlattenedDependenciesAll( List projectDependencie
}
}
+ /**
+ * Returns a cloned project that does not have direct test-scope dependencies.
+ *
+ * Test-scope project dependencies may hinder transitive dependencies by marking them as 'omitted for duplicate' when
+ * building dependency tree. This was a problem when the transitive dependency is actually needed by another non-test dependency
+ * of the project (See https://github.com/mojohaus/flatten-maven-plugin/issues/185). To avoid this interference of
+ * test-scope project dependencies, this plugin builds a dependency tree of the project without direct, test-scope dependencies.
+ *
+ * Removal of test scope dependencies is safe because these dependencies do not appear in library users' class path in
+ * any case.
+ *
+ * @param project is the original project to clone.
+ * @return a cloned project without direct test-scope dependencies.
+ */
+ private static MavenProject cloneProjectWithoutTestDependencies( MavenProject project )
+ {
+ final Set testScopeProjectDependencyKeys = new HashSet<>();
+ for ( Dependency projectDependency : project.getDependencies() )
+ {
+ if ( "test".equals( projectDependency.getScope() ) )
+ {
+ testScopeProjectDependencyKeys.add( projectDependency.getManagementKey() );
+ }
+ }
+ // LinkedHashSet preserves the order.
+ final Set dependencyArtifactsWithoutTest = new LinkedHashSet<>( project.getDependencyArtifacts() );
+ dependencyArtifactsWithoutTest.removeIf(artifact -> {
+ // The same logic as org.apache.maven.model.Dependency.getManagementKey()
+ String managementKey = artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType()
+ + ( artifact.getClassifier() != null ? ":" + artifact.getClassifier() : "" );
+ return testScopeProjectDependencyKeys.contains( managementKey );
+ });
+ final MavenProject projectWithoutTestScopeDeps = project.clone();
+ projectWithoutTestScopeDeps.setDependencyArtifacts( dependencyArtifactsWithoutTest );
+ return projectWithoutTestScopeDeps;
+ }
+
/**
* Collects the resolved {@link Dependency dependencies} from the given effectiveModel
.
*