diff --git a/src/it/projects/MSHADE-413-parallel/invoker.properties b/src/it/projects/MSHADE-413-parallel/invoker.properties new file mode 100644 index 00000000..7eb2f06c --- /dev/null +++ b/src/it/projects/MSHADE-413-parallel/invoker.properties @@ -0,0 +1,19 @@ +# 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. + +invoker.timeoutInSeconds=60 +invoker.goals = clean install -T2 -pl p1/ -pl p2/ diff --git a/src/it/projects/MSHADE-413-parallel/p1/pom.xml b/src/it/projects/MSHADE-413-parallel/p1/pom.xml new file mode 100644 index 00000000..cab97d13 --- /dev/null +++ b/src/it/projects/MSHADE-413-parallel/p1/pom.xml @@ -0,0 +1,87 @@ + + + + + + 4.0.0 + + + org.apache.maven.its.shade.parallel + mshade413-parent + 1.0 + + + mshade413-p1 + 1.0 + + MSHADE-413-p1 + + + + org.projectnessie + nessie-spark-extensions-base + 0.22.0 + + + org.projectnessie + nessie-spark-extensions-base + 0.22.0 + tests + test + + + org.apache.iceberg + iceberg-spark3-runtime + 0.13.1 + test + + + org.junit.jupiter + junit-jupiter-api + 5.8.2 + test + + + + + + + org.apache.maven.plugins + maven-shade-plugin + @project.version@ + + + + org.projectnessie + + + + + + package + + shade + + + + + + + diff --git a/src/it/projects/MSHADE-413-parallel/p2/pom.xml b/src/it/projects/MSHADE-413-parallel/p2/pom.xml new file mode 100644 index 00000000..f598df6b --- /dev/null +++ b/src/it/projects/MSHADE-413-parallel/p2/pom.xml @@ -0,0 +1,87 @@ + + + + + + 4.0.0 + + + org.apache.maven.its.shade.parallel + mshade413-parent + 1.0 + + + mshade413-p2 + 1.0 + + MSHADE-413-p2 + + + + org.projectnessie + nessie-spark-extensions-base + 0.22.0 + + + org.projectnessie + nessie-spark-extensions-base + 0.22.0 + tests + test + + + org.apache.iceberg + iceberg-spark3-runtime + 0.13.1 + test + + + org.junit.jupiter + junit-jupiter-api + 5.8.2 + test + + + + + + + org.apache.maven.plugins + maven-shade-plugin + @project.version@ + + + + org.projectnessie + + + + + + package + + shade + + + + + + + diff --git a/src/it/projects/MSHADE-413-parallel/pom.xml b/src/it/projects/MSHADE-413-parallel/pom.xml new file mode 100644 index 00000000..7089c0a9 --- /dev/null +++ b/src/it/projects/MSHADE-413-parallel/pom.xml @@ -0,0 +1,39 @@ + + + + + + 4.0.0 + + org.apache.maven.its.shade.parallel + mshade413-parent + 1.0 + pom + + MSHADE-413 + + Test that shade works in two parallel project builds. + + + + p1 + p2 + + 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 d717253f..10b1be24 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 @@ -1061,10 +1061,6 @@ private File shadedTestArtifactFile() private void createDependencyReducedPom( Set artifactsToRemove ) throws IOException, DependencyGraphBuilderException, ProjectBuildingException { - List dependencies = new ArrayList<>(); - - boolean modified = false; - List transitiveDeps = new ArrayList<>(); // NOTE: By using the getArtifacts() we get the completely evaluated artifacts @@ -1083,39 +1079,48 @@ private void createDependencyReducedPom( Set artifactsToRemove ) // we'll figure out the exclusions in a bit. transitiveDeps.add( dep ); } - List origDeps = project.getDependencies(); - if ( promoteTransitiveDependencies ) + Model model = project.getOriginalModel(); + + // MSHADE-413: Must not use objects (for example `Model` or `Dependency`) that are "owned + // by Maven" and being used by other projects/plugins. Modifying those will break the + // correctness of the build - or cause an endless loop. + List origDeps = new ArrayList<>(); + List source = promoteTransitiveDependencies ? transitiveDeps : project.getDependencies(); + for ( Dependency d : source ) { - origDeps = transitiveDeps; + origDeps.add( d.clone() ); } + model = model.clone(); - Model model = project.getOriginalModel(); // MSHADE-185: We will remove all system scoped dependencies which usually // have some kind of property usage. At this time the properties within // such things are already evaluated. List originalDependencies = model.getDependencies(); removeSystemScopedDependencies( artifactsToRemove, originalDependencies ); + List dependencies = new ArrayList<>(); + boolean modified = false; for ( Dependency d : origDeps ) { - dependencies.add( d ); - - String id = getId( d ); - - if ( artifactsToRemove.contains( id ) ) + if ( artifactsToRemove.contains( getId( d ) ) ) { - modified = true; - if ( keepDependenciesWithProvidedScope ) { - d.setScope( "provided" ); + if ( !"provided".equals( d.getScope() ) ) + { + modified = true; + d.setScope( "provided" ); + } } else { - dependencies.remove( d ); + modified = true; + continue; } } + + dependencies.add( d ); } // MSHADE-155 @@ -1299,8 +1304,13 @@ public boolean updateExcludesInDeps( MavenProject project, List depe boolean modified = false; for ( DependencyNode n2 : node.getChildren() ) { + String artifactId2 = getId( n2.getArtifact() ); + for ( DependencyNode n3 : n2.getChildren() ) { + Artifact artifact3 = n3.getArtifact(); + String artifactId3 = getId( artifact3 ); + // check if it really isn't in the list of original dependencies. Maven // prior to 2.0.8 may grab versions from transients instead of // from the direct deps in which case they would be marked included @@ -1310,7 +1320,7 @@ public boolean updateExcludesInDeps( MavenProject project, List depe boolean found = false; for ( Dependency dep : transitiveDeps ) { - if ( getId( dep ).equals( getId( n3.getArtifact() ) ) ) + if ( getId( dep ).equals( artifactId3 ) ) { found = true; break; @@ -1321,18 +1331,31 @@ public boolean updateExcludesInDeps( MavenProject project, List depe // note: MSHADE-31 introduced the exclusion logic for promoteTransitiveDependencies=true, // but as of 3.2.1 promoteTransitiveDependencies has no effect for provided deps, // which makes this fix even possible (see also MSHADE-181) - if ( !found && !"provided".equals( n3.getArtifact().getScope() ) ) + if ( !found && !"provided".equals( artifact3.getScope() ) ) { + getLog().debug( String.format( "dependency %s (scope %s) not found in transitive dependencies", + artifactId3, artifact3.getScope() ) ); for ( Dependency dep : dependencies ) { - if ( getId( dep ).equals( getId( n2.getArtifact() ) ) ) + if ( getId( dep ).equals( artifactId2 ) ) { - Exclusion exclusion = new Exclusion(); - exclusion.setArtifactId( n3.getArtifact().getArtifactId() ); - exclusion.setGroupId( n3.getArtifact().getGroupId() ); - dep.addExclusion( exclusion ); - modified = true; - break; + // MSHADE-413: First check whether the exclusion has already been added, + // because it's meaningless to add it more than once. Certain cases + // can end up adding the exclusion "forever" and cause an endless loop + // rewriting the whole dependency-reduced-pom.xml file. + if ( !dependencyHasExclusion( dep, artifact3 ) ) + { + getLog().debug( String.format( "Adding exclusion for dependency %s (scope %s) " + + "to %s (scope %s)", + artifactId3, artifact3.getScope(), + getId( dep ), dep.getScope() ) ); + Exclusion exclusion = new Exclusion(); + exclusion.setArtifactId( artifact3.getArtifactId() ); + exclusion.setGroupId( artifact3.getGroupId() ); + dep.addExclusion( exclusion ); + modified = true; + break; + } } } } @@ -1347,6 +1370,21 @@ public boolean updateExcludesInDeps( MavenProject project, List depe } } + private boolean dependencyHasExclusion( Dependency dep, Artifact exclusionToCheck ) + { + boolean containsExclusion = false; + for ( Exclusion existingExclusion : dep.getExclusions() ) + { + if ( existingExclusion.getGroupId().equals( exclusionToCheck.getGroupId() ) + && existingExclusion.getArtifactId().equals( exclusionToCheck.getArtifactId() ) ) + { + containsExclusion = true; + break; + } + } + return containsExclusion; + } + private List toResourceTransformers( String shade, List resourceTransformers ) {