View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.shade.mojo;
20  
21  import javax.inject.Inject;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.io.Writer;
28  import java.nio.file.Files;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Collections;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.LinkedHashSet;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  
39  import org.apache.maven.RepositoryUtils;
40  import org.apache.maven.artifact.Artifact;
41  import org.apache.maven.artifact.DefaultArtifact;
42  import org.apache.maven.execution.MavenSession;
43  import org.apache.maven.model.Dependency;
44  import org.apache.maven.model.Exclusion;
45  import org.apache.maven.model.Model;
46  import org.apache.maven.plugin.AbstractMojo;
47  import org.apache.maven.plugin.MojoExecutionException;
48  import org.apache.maven.plugins.annotations.LifecyclePhase;
49  import org.apache.maven.plugins.annotations.Mojo;
50  import org.apache.maven.plugins.annotations.Parameter;
51  import org.apache.maven.plugins.annotations.ResolutionScope;
52  import org.apache.maven.plugins.shade.ShadeRequest;
53  import org.apache.maven.plugins.shade.Shader;
54  import org.apache.maven.plugins.shade.filter.Filter;
55  import org.apache.maven.plugins.shade.filter.MinijarFilter;
56  import org.apache.maven.plugins.shade.filter.SimpleFilter;
57  import org.apache.maven.plugins.shade.pom.PomWriter;
58  import org.apache.maven.plugins.shade.relocation.Relocator;
59  import org.apache.maven.plugins.shade.relocation.SimpleRelocator;
60  import org.apache.maven.plugins.shade.resource.ManifestResourceTransformer;
61  import org.apache.maven.plugins.shade.resource.ResourceTransformer;
62  import org.apache.maven.project.DefaultProjectBuildingRequest;
63  import org.apache.maven.project.MavenProject;
64  import org.apache.maven.project.MavenProjectHelper;
65  import org.apache.maven.project.ProjectBuilder;
66  import org.apache.maven.project.ProjectBuildingException;
67  import org.apache.maven.project.ProjectBuildingRequest;
68  import org.apache.maven.project.ProjectBuildingResult;
69  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
70  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
71  import org.apache.maven.shared.dependency.graph.DependencyNode;
72  import org.codehaus.plexus.util.IOUtil;
73  import org.codehaus.plexus.util.WriterFactory;
74  import org.eclipse.aether.RepositorySystem;
75  import org.eclipse.aether.resolution.ArtifactRequest;
76  import org.eclipse.aether.resolution.ArtifactResolutionException;
77  import org.eclipse.aether.resolution.ArtifactResult;
78  
79  import static org.apache.maven.plugins.shade.resource.UseDependencyReducedPom.createPomReplaceTransformers;
80  
81  /**
82   * Mojo that performs shading delegating to the Shader component.
83   *
84   * @author Jason van Zyl
85   * @author Mauro Talevi
86   * @author David Blevins
87   * @author Hiram Chirino
88   */
89  // CHECKSTYLE_OFF: LineLength
90  @Mojo(
91          name = "shade",
92          defaultPhase = LifecyclePhase.PACKAGE,
93          threadSafe = true,
94          requiresDependencyResolution = ResolutionScope.RUNTIME)
95  // CHECKSTYLE_ON: LineLength
96  public class ShadeMojo extends AbstractMojo {
97      /**
98       * The current Maven session.
99       */
100     @Parameter(defaultValue = "${session}", readonly = true, required = true)
101     private MavenSession session;
102 
103     /**
104      * The current Maven project.
105      */
106     @Parameter(defaultValue = "${project}", readonly = true, required = true)
107     private MavenProject project;
108 
109     /**
110      * Artifacts to include/exclude from the final artifact. Artifacts are denoted by composite identifiers of the
111      * general form <code>groupId:artifactId:type:classifier</code>. Since version 1.3, the wildcard characters '*' and
112      * '?' can be used within the sub parts of those composite identifiers to do pattern matching. For convenience, the
113      * syntax <code>groupId</code> is equivalent to <code>groupId:*:*:*</code>, <code>groupId:artifactId</code> is
114      * equivalent to <code>groupId:artifactId:*:*</code> and <code>groupId:artifactId:classifier</code> is equivalent to
115      * <code>groupId:artifactId:*:classifier</code>. For example:
116      *
117      * <pre>
118      * &lt;artifactSet&gt;
119      *   &lt;includes&gt;
120      *     &lt;include&gt;org.apache.maven:*&lt;/include&gt;
121      *   &lt;/includes&gt;
122      *   &lt;excludes&gt;
123      *     &lt;exclude&gt;*:maven-core&lt;/exclude&gt;
124      *   &lt;/excludes&gt;
125      * &lt;/artifactSet&gt;
126      * </pre>
127      */
128     @Parameter
129     private ArtifactSet artifactSet;
130 
131     /**
132      * Packages to be relocated. For example:
133      *
134      * <pre>
135      * &lt;relocations&gt;
136      *   &lt;relocation&gt;
137      *     &lt;pattern&gt;org.apache&lt;/pattern&gt;
138      *     &lt;shadedPattern&gt;hidden.org.apache&lt;/shadedPattern&gt;
139      *     &lt;includes&gt;
140      *       &lt;include&gt;org.apache.maven.*&lt;/include&gt;
141      *     &lt;/includes&gt;
142      *     &lt;excludes&gt;
143      *       &lt;exclude&gt;org.apache.maven.Public*&lt;/exclude&gt;
144      *     &lt;/excludes&gt;
145      *   &lt;/relocation&gt;
146      * &lt;/relocations&gt;
147      * </pre>
148      *
149      * <em>Note:</em> Support for includes exists only since version 1.4.
150      */
151     @SuppressWarnings("MismatchedReadAndWriteOfArray")
152     @Parameter
153     private PackageRelocation[] relocations;
154 
155     /**
156      * Resource transformers to be used. Please see the "Examples" section for more information on available
157      * transformers and their configuration.
158      */
159     @Parameter
160     private ResourceTransformer[] transformers;
161 
162     /**
163      * Archive Filters to be used. Allows you to specify an artifact in the form of a composite identifier as used by
164      * {@link #artifactSet} and a set of include/exclude file patterns for filtering which contents of the archive are
165      * added to the shaded jar. From a logical perspective, includes are processed before excludes, thus it's possible
166      * to use an include to collect a set of files from the archive then use excludes to further reduce the set. By
167      * default, all files are included and no files are excluded. If multiple filters apply to an artifact, the
168      * intersection of the matched files will be included in the final JAR. For example:
169      *
170      * <pre>
171      * &lt;filters&gt;
172      *   &lt;filter&gt;
173      *     &lt;artifact&gt;junit:junit&lt;/artifact&gt;
174      *     &lt;includes&gt;
175      *       &lt;include&gt;org/junit/**&lt;/include&gt;
176      *     &lt;/includes&gt;
177      *     &lt;excludes&gt;
178      *       &lt;exclude&gt;org/junit/experimental/**&lt;/exclude&gt;
179      *     &lt;/excludes&gt;
180      *   &lt;/filter&gt;
181      * &lt;/filters&gt;
182      * </pre>
183      */
184     @SuppressWarnings("MismatchedReadAndWriteOfArray")
185     @Parameter
186     private ArchiveFilter[] filters;
187 
188     /**
189      * The destination directory for the shaded artifact.
190      */
191     @Parameter(defaultValue = "${project.build.directory}")
192     private File outputDirectory;
193 
194     /**
195      * The name of the shaded artifactId.
196      * <p/>
197      * If you like to change the name of the native artifact, you may use the &lt;build>&lt;finalName> setting. If this
198      * is set to something different than &lt;build>&lt;finalName>, no file replacement will be performed, even if
199      * shadedArtifactAttached is being used.
200      */
201     @Parameter
202     private String finalName;
203 
204     /**
205      * The name of the shaded artifactId. So you may want to use a different artifactId and keep the standard version.
206      * If the original artifactId was "foo" then the final artifact would be something like foo-1.0.jar. So if you
207      * change the artifactId you might have something like foo-special-1.0.jar.
208      */
209     @Parameter(defaultValue = "${project.artifactId}")
210     private String shadedArtifactId;
211 
212     /**
213      * If specified, this will include only artifacts which have groupIds which start with this.
214      */
215     @Parameter
216     private String shadedGroupFilter;
217 
218     /**
219      * Defines whether the shaded artifact should be attached as classifier to the original artifact. If false, the
220      * shaded jar will be the main artifact of the project
221      */
222     @Parameter
223     private boolean shadedArtifactAttached;
224 
225     /**
226      * Flag whether to generate a simplified POM for the shaded artifact. If set to <code>true</code>, dependencies that
227      * have been included into the uber JAR will be removed from the <code>&lt;dependencies&gt;</code> section of the
228      * generated POM. The reduced POM will be named <code>dependency-reduced-pom.xml</code> and is stored into the same
229      * directory as the shaded artifact. Unless you also specify dependencyReducedPomLocation, the plugin will create a
230      * temporary file named <code>dependency-reduced-pom.xml</code> in the project basedir.
231      */
232     @Parameter(defaultValue = "true")
233     private boolean createDependencyReducedPom;
234 
235     /**
236      * Where to put the dependency reduced pom. Note: setting a value for this parameter with a directory other than
237      * ${basedir} will change the value of ${basedir} for all executions that come after the shade execution. This is
238      * often not what you want. This is considered an open issue with this plugin.
239      *
240      * @since 1.7
241      */
242     @Parameter(defaultValue = "${basedir}/dependency-reduced-pom.xml")
243     private File dependencyReducedPomLocation;
244 
245     /**
246      * Create a dependency-reduced POM in ${basedir}/drp-UNIQUE.pom. This avoids build collisions of parallel builds
247      * without moving the dependency-reduced POM to a different directory. The property
248      * maven.shade.dependency-reduced-pom is set to the generated filename.
249      *
250      * @since 1.7.2
251      */
252     @Parameter(defaultValue = "false")
253     private boolean generateUniqueDependencyReducedPom;
254 
255     /**
256      * Add dependency reduced POM to the JAR instead of the original one provided by the project.
257      * If {@code createDependencyReducedPom} is {@code false} this parameter will be ignored.
258      *
259      * @since 3.3.0
260      */
261     @Parameter(defaultValue = "false")
262     private boolean useDependencyReducedPomInJar;
263 
264     /**
265      * When true, dependencies are kept in the pom but with scope 'provided'; when false, the dependency is removed.
266      */
267     @Parameter
268     private boolean keepDependenciesWithProvidedScope;
269 
270     /**
271      * When true, transitive deps of removed dependencies are promoted to direct dependencies. This should allow the
272      * drop in replacement of the removed deps with the new shaded jar and everything should still work.
273      */
274     @Parameter
275     private boolean promoteTransitiveDependencies;
276 
277     /**
278      * The name of the classifier used in case the shaded artifact is attached.
279      */
280     @Parameter(defaultValue = "shaded")
281     private String shadedClassifierName;
282 
283     /**
284      * When true, it will attempt to create a sources jar as well
285      */
286     @Parameter
287     private boolean createSourcesJar;
288 
289     /**
290      * When true, it will attempt to create a test sources jar.
291      */
292     @Parameter
293     private boolean createTestSourcesJar;
294 
295     /**
296      * When true, it will attempt to shade the contents of Java source files when creating the sources JAR. When false,
297      * it will just relocate the Java source files to the shaded paths, but will not modify the actual source file
298      * contents.
299      * <p>
300      * <b>Please note:</b> This feature uses a heuristic search & replace approach which covers many, but definitely not
301      * all possible cases of source code shading and its excludes. There is no full Java parser behind this
302      * functionality, which would be the only way to get this right for Java language elements. As for matching within
303      * Java string constants, this is next to impossible to get 100% right, trying to guess if they are used in
304      * reflection or not.
305      * <p>
306      * Please understand that the source shading feature is not meant as a source code generator anyway, merely as a
307      * tool creating reasonably plausible source code when navigating to a relocated library class from an IDE,
308      * hopefully displaying source code which makes 95% sense - no more, no less.
309      */
310     @Parameter(property = "shadeSourcesContent", defaultValue = "false")
311     private boolean shadeSourcesContent;
312 
313     /**
314      * When true, dependencies will be stripped down on the class level to only the transitive hull required for the
315      * artifact. See also {@link #entryPoints}, if you wish to further optimize JAR minimization.
316      * <p>
317      * <em>Note:</em> This feature requires Java 1.8 or higher due to its use of
318      * <a href="https://github.com/tcurdt/jdependency">jdependency</a>. Its accuracy therefore also depends on
319      * jdependency's limitations.
320      *
321      * @since 1.4
322      */
323     @Parameter
324     private boolean minimizeJar;
325 
326     /**
327      * Use this option in order to fine-tune {@link #minimizeJar}: By default, all of the target module's classes are
328      * kept and used as entry points for JAR minimization. By explicitly limiting the set of entry points, you can
329      * further minimize the set of classes kept in the shaded JAR. This affects both classes in the module itself and
330      * dependency classes. If {@link #minimizeJar} is inactive, this option has no effect either.
331      * <p>
332      * <em>Note:</em> This feature requires Java 1.8 or higher due to its use of
333      * <a href="https://github.com/tcurdt/jdependency">jdependency</a>. Its accuracy therefore also depends on
334      * jdependency's limitations.
335      * <p>
336      * Configuration example:
337      * <pre>{@code
338      * <minimizeJar>true</minimizeJar>
339      * <entryPoints>
340      *   <entryPoint>org.acme.Application</entryPoint>
341      *   <entryPoint>org.acme.OtherEntryPoint</entryPoint>
342      * </entryPoints>
343      * }</pre>
344      *
345      * @since 3.5.0
346      */
347     @Parameter
348     private Set<String> entryPoints;
349 
350     /**
351      * The path to the output file for the shaded artifact. When this parameter is set, the created archive will neither
352      * replace the project's main artifact nor will it be attached. Hence, this parameter causes the parameters
353      * {@link #finalName}, {@link #shadedArtifactAttached}, {@link #shadedClassifierName} and
354      * {@link #createDependencyReducedPom} to be ignored when used.
355      *
356      * @since 1.3
357      */
358     @Parameter
359     private File outputFile;
360 
361     /**
362      * You can pass here the roleHint about your own Shader implementation plexus component.
363      *
364      * @since 1.6
365      */
366     @Parameter
367     private String shaderHint;
368 
369     /**
370      * When true, the version of each dependency of the reduced pom will be based on the baseVersion of the original
371      * dependency instead of its resolved version. For example, if the original pom (transitively) depends on
372      * a:a:2.7-SNAPSHOT, if useBaseVersion is set to false, the reduced pom will depend on a:a:2.7-20130312.222222-12
373      * whereas if useBaseVersion is set to true, the reduced pom will depend on a:a:2.7-SNAPSHOT
374      *
375      * @since 3.0
376      */
377     @Parameter(defaultValue = "false")
378     private boolean useBaseVersion;
379 
380     /**
381      * When true, creates a shaded test-jar artifact as well.
382      */
383     @Parameter(defaultValue = "false")
384     private boolean shadeTestJar;
385 
386     /**
387      * When true, skips the execution of this MOJO.
388      * @since 3.3.0
389      */
390     @Parameter(defaultValue = "false")
391     private boolean skip;
392 
393     @Inject
394     private MavenProjectHelper projectHelper;
395 
396     @Inject
397     private Shader shader;
398 
399     @Inject
400     private RepositorySystem repositorySystem;
401 
402     /**
403      * The dependency graph builder to use.
404      */
405     @Inject
406     private DependencyGraphBuilder dependencyGraphBuilder;
407 
408     /**
409      * ProjectBuilder, needed to create projects from the artifacts.
410      */
411     @Inject
412     private ProjectBuilder projectBuilder;
413 
414     /**
415      * All the present Shaders.
416      */
417     @Inject
418     private Map<String, Shader> shaders;
419 
420     /**
421      * @throws MojoExecutionException in case of an error.
422      */
423     @Override
424     public void execute() throws MojoExecutionException {
425         if (skip) {
426             getLog().info("Shading has been skipped.");
427             return;
428         }
429 
430         setupHintedShader();
431 
432         Set<File> artifacts = new LinkedHashSet<>();
433         Set<String> artifactIds = new LinkedHashSet<>();
434         Set<File> sourceArtifacts = new LinkedHashSet<>();
435         Set<File> testArtifacts = new LinkedHashSet<>();
436         Set<File> testSourceArtifacts = new LinkedHashSet<>();
437 
438         ArtifactSelector artifactSelector = new ArtifactSelector(project.getArtifact(), artifactSet, shadedGroupFilter);
439 
440         if (artifactSelector.isSelected(project.getArtifact())
441                 && !"pom".equals(project.getArtifact().getType())) {
442             if (invalidMainArtifact()) {
443                 createErrorOutput();
444                 throw new MojoExecutionException(
445                         "Failed to create shaded artifact, " + "project main artifact does not exist.");
446             }
447 
448             artifacts.add(project.getArtifact().getFile());
449 
450             if (createSourcesJar) {
451                 File file = shadedSourcesArtifactFile();
452                 if (file.isFile()) {
453                     sourceArtifacts.add(file);
454                 }
455             }
456 
457             if (shadeTestJar) {
458                 File file = shadedTestArtifactFile();
459                 if (file.isFile()) {
460                     testArtifacts.add(file);
461                 }
462             }
463 
464             if (createTestSourcesJar) {
465                 File file = shadedTestSourcesArtifactFile();
466                 if (file.isFile()) {
467                     testSourceArtifacts.add(file);
468                 }
469             }
470         }
471 
472         processArtifactSelectors(
473                 artifacts, artifactIds, sourceArtifacts, testArtifacts, testSourceArtifacts, artifactSelector);
474 
475         File outputJar = (outputFile != null) ? outputFile : shadedArtifactFileWithClassifier();
476         File sourcesJar = shadedSourceArtifactFileWithClassifier();
477         File testJar = shadedTestArtifactFileWithClassifier();
478         File testSourcesJar = shadedTestSourceArtifactFileWithClassifier();
479 
480         // Now add our extra resources
481         try {
482             List<Filter> filters = getFilters();
483 
484             List<Relocator> relocators = getRelocators();
485 
486             List<ResourceTransformer> resourceTransformers = getResourceTransformers();
487 
488             if (createDependencyReducedPom) {
489                 createDependencyReducedPom(artifactIds);
490 
491                 if (useDependencyReducedPomInJar) {
492                     // In some cases the used implementation of the resourceTransformers is immutable.
493                     resourceTransformers = new ArrayList<>(resourceTransformers);
494                     resourceTransformers.addAll(createPomReplaceTransformers(project, dependencyReducedPomLocation));
495                 }
496             }
497 
498             ShadeRequest shadeRequest =
499                     shadeRequest("jar", artifacts, outputJar, filters, relocators, resourceTransformers);
500 
501             shader.shade(shadeRequest);
502 
503             if (createSourcesJar) {
504                 ShadeRequest shadeSourcesRequest = createShadeSourcesRequest(
505                         "sources-jar", sourceArtifacts, sourcesJar, filters, relocators, resourceTransformers);
506 
507                 shader.shade(shadeSourcesRequest);
508             }
509 
510             if (shadeTestJar) {
511                 ShadeRequest shadeTestRequest =
512                         shadeRequest("test-jar", testArtifacts, testJar, filters, relocators, resourceTransformers);
513 
514                 shader.shade(shadeTestRequest);
515             }
516 
517             if (createTestSourcesJar) {
518                 ShadeRequest shadeTestSourcesRequest = createShadeSourcesRequest(
519                         "test-sources-jar",
520                         testSourceArtifacts,
521                         testSourcesJar,
522                         filters,
523                         relocators,
524                         resourceTransformers);
525 
526                 shader.shade(shadeTestSourcesRequest);
527             }
528 
529             if (outputFile == null) {
530                 boolean renamed = false;
531 
532                 // rename the output file if a specific finalName is set
533                 // but don't rename if the finalName is the <build><finalName>
534                 // because this will be handled implicitly later
535                 if (finalName != null
536                         && finalName.length() > 0 //
537                         && !finalName.equals(project.getBuild().getFinalName())) {
538                     String finalFileName = finalName + "."
539                             + project.getArtifact().getArtifactHandler().getExtension();
540                     File finalFile = new File(outputDirectory, finalFileName);
541                     replaceFile(finalFile, outputJar);
542                     outputJar = finalFile;
543 
544                     // Also support the sources JAR
545                     if (createSourcesJar) {
546                         finalFileName = finalName + "-sources.jar";
547                         finalFile = new File(outputDirectory, finalFileName);
548                         replaceFile(finalFile, sourcesJar);
549                         sourcesJar = finalFile;
550                     }
551 
552                     // Also support the test JAR
553                     if (shadeTestJar) {
554                         finalFileName = finalName + "-tests.jar";
555                         finalFile = new File(outputDirectory, finalFileName);
556                         replaceFile(finalFile, testJar);
557                         testJar = finalFile;
558                     }
559 
560                     if (createTestSourcesJar) {
561                         finalFileName = finalName + "-test-sources.jar";
562                         finalFile = new File(outputDirectory, finalFileName);
563                         replaceFile(finalFile, testSourcesJar);
564                         testSourcesJar = finalFile;
565                     }
566 
567                     renamed = true;
568                 }
569 
570                 if (shadedArtifactAttached) {
571                     getLog().info("Attaching shaded artifact.");
572                     projectHelper.attachArtifact(
573                             project, project.getArtifact().getType(), shadedClassifierName, outputJar);
574                     if (createSourcesJar) {
575                         projectHelper.attachArtifact(
576                                 project, "java-source", shadedClassifierName + "-sources", sourcesJar);
577                     }
578 
579                     if (shadeTestJar) {
580                         projectHelper.attachArtifact(project, "test-jar", shadedClassifierName + "-tests", testJar);
581                     }
582 
583                     if (createTestSourcesJar) {
584                         projectHelper.attachArtifact(
585                                 project, "java-source", shadedClassifierName + "-test-sources", testSourcesJar);
586                     }
587                 } else if (!renamed) {
588                     getLog().info("Replacing original artifact with shaded artifact.");
589                     File originalArtifact = project.getArtifact().getFile();
590                     if (originalArtifact != null) {
591                         replaceFile(originalArtifact, outputJar);
592 
593                         if (createSourcesJar) {
594                             getLog().info("Replacing original source artifact with shaded source artifact.");
595                             File shadedSources = shadedSourcesArtifactFile();
596 
597                             replaceFile(shadedSources, sourcesJar);
598 
599                             projectHelper.attachArtifact(project, "java-source", "sources", shadedSources);
600                         }
601 
602                         if (shadeTestJar) {
603                             getLog().info("Replacing original test artifact with shaded test artifact.");
604                             File shadedTests = shadedTestArtifactFile();
605 
606                             replaceFile(shadedTests, testJar);
607 
608                             projectHelper.attachArtifact(project, "test-jar", shadedTests);
609                         }
610 
611                         if (createTestSourcesJar) {
612                             getLog().info("Replacing original test source artifact "
613                                     + "with shaded test source artifact.");
614                             File shadedTestSources = shadedTestSourcesArtifactFile();
615 
616                             replaceFile(shadedTestSources, testSourcesJar);
617 
618                             projectHelper.attachArtifact(project, "java-source", "test-sources", shadedTestSources);
619                         }
620                     }
621                 }
622             }
623         } catch (Exception e) {
624             throw new MojoExecutionException("Error creating shaded jar: " + e.getMessage(), e);
625         }
626     }
627 
628     private void createErrorOutput() {
629         getLog().error("The project main artifact does not exist. This could have the following");
630         getLog().error("reasons:");
631         getLog().error("- You have invoked the goal directly from the command line. This is not");
632         getLog().error("  supported. Please add the goal to the default lifecycle via an");
633         getLog().error("  <execution> element in your POM and use \"mvn package\" to have it run.");
634         getLog().error("- You have bound the goal to a lifecycle phase before \"package\". Please");
635         getLog().error("  remove this binding from your POM such that the goal will be run in");
636         getLog().error("  the proper phase.");
637         getLog().error("- You removed the configuration of the maven-jar-plugin that produces the main artifact.");
638     }
639 
640     private ShadeRequest shadeRequest(
641             String shade,
642             Set<File> artifacts,
643             File outputJar,
644             List<Filter> filters,
645             List<Relocator> relocators,
646             List<ResourceTransformer> resourceTransformers) {
647         ShadeRequest shadeRequest = new ShadeRequest();
648         shadeRequest.setJars(artifacts);
649         shadeRequest.setUberJar(outputJar);
650         shadeRequest.setFilters(filters);
651         shadeRequest.setRelocators(relocators);
652         shadeRequest.setResourceTransformers(toResourceTransformers(shade, resourceTransformers));
653         return shadeRequest;
654     }
655 
656     private ShadeRequest createShadeSourcesRequest(
657             String shade,
658             Set<File> testArtifacts,
659             File testJar,
660             List<Filter> filters,
661             List<Relocator> relocators,
662             List<ResourceTransformer> resourceTransformers) {
663         ShadeRequest shadeSourcesRequest =
664                 shadeRequest(shade, testArtifacts, testJar, filters, relocators, resourceTransformers);
665         shadeSourcesRequest.setShadeSourcesContent(shadeSourcesContent);
666         return shadeSourcesRequest;
667     }
668 
669     private void setupHintedShader() throws MojoExecutionException {
670         if (shaderHint != null) {
671             shader = shaders.get(shaderHint);
672 
673             if (shader == null) {
674                 throw new MojoExecutionException(
675                         "unable to lookup own Shader implementation with hint: '" + shaderHint + "'");
676             }
677         }
678     }
679 
680     private void processArtifactSelectors(
681             Set<File> artifacts,
682             Set<String> artifactIds,
683             Set<File> sourceArtifacts,
684             Set<File> testArtifacts,
685             Set<File> testSourceArtifacts,
686             ArtifactSelector artifactSelector) {
687 
688         List<String> excludedArtifacts = new ArrayList<>();
689         List<String> pomArtifacts = new ArrayList<>();
690         List<String> emptySourceArtifacts = new ArrayList<>();
691         List<String> emptyTestArtifacts = new ArrayList<>();
692         List<String> emptyTestSourceArtifacts = new ArrayList<>();
693 
694         for (Artifact artifact : project.getArtifacts()) {
695             if (!artifactSelector.isSelected(artifact)) {
696                 excludedArtifacts.add(artifact.getId());
697 
698                 continue;
699             }
700 
701             if ("pom".equals(artifact.getType())) {
702                 pomArtifacts.add(artifact.getId());
703                 continue;
704             }
705 
706             getLog().info("Including " + artifact.getId() + " in the shaded jar.");
707 
708             artifacts.add(artifact.getFile());
709             artifactIds.add(getId(artifact));
710 
711             if (createSourcesJar) {
712                 File file = resolveArtifactForClassifier(artifact, "sources");
713                 if (file != null) {
714                     if (file.length() > 0) {
715                         sourceArtifacts.add(file);
716                     } else {
717                         emptySourceArtifacts.add(artifact.getArtifactId());
718                     }
719                 }
720             }
721 
722             if (shadeTestJar) {
723                 File file = resolveArtifactForClassifier(artifact, "tests");
724                 if (file != null) {
725                     if (file.length() > 0) {
726                         testArtifacts.add(file);
727                     } else {
728                         emptyTestArtifacts.add(artifact.getId());
729                     }
730                 }
731             }
732 
733             if (createTestSourcesJar) {
734                 File file = resolveArtifactForClassifier(artifact, "test-sources");
735                 if (file != null) {
736                     testSourceArtifacts.add(file);
737                 } else {
738                     emptyTestSourceArtifacts.add(artifact.getId());
739                 }
740             }
741         }
742 
743         for (String artifactId : excludedArtifacts) {
744             getLog().info("Excluding " + artifactId + " from the shaded jar.");
745         }
746         for (String artifactId : pomArtifacts) {
747             getLog().info("Skipping pom dependency " + artifactId + " in the shaded jar.");
748         }
749         for (String artifactId : emptySourceArtifacts) {
750             getLog().warn("Skipping empty source jar " + artifactId + ".");
751         }
752         for (String artifactId : emptyTestArtifacts) {
753             getLog().warn("Skipping empty test jar " + artifactId + ".");
754         }
755         for (String artifactId : emptyTestSourceArtifacts) {
756             getLog().warn("Skipping empty test source jar " + artifactId + ".");
757         }
758     }
759 
760     private boolean invalidMainArtifact() {
761         return project.getArtifact().getFile() == null
762                 || !project.getArtifact().getFile().isFile();
763     }
764 
765     private void replaceFile(File oldFile, File newFile) throws MojoExecutionException {
766         getLog().info("Replacing " + oldFile + " with " + newFile);
767 
768         File origFile = new File(outputDirectory, "original-" + oldFile.getName());
769         if (oldFile.exists() && !oldFile.renameTo(origFile)) {
770             // try a gc to see if an unclosed stream needs garbage collecting
771             System.gc();
772             System.gc();
773 
774             if (!oldFile.renameTo(origFile)) {
775                 // Still didn't work. We'll do a copy
776                 try {
777                     copyFiles(oldFile, origFile);
778                 } catch (IOException ex) {
779                     // kind of ignorable here. We're just trying to save the original
780                     getLog().warn(ex);
781                 }
782             }
783         }
784         if (!newFile.renameTo(oldFile)) {
785             // try a gc to see if an unclosed stream needs garbage collecting
786             System.gc();
787             System.gc();
788 
789             if (!newFile.renameTo(oldFile)) {
790                 // Still didn't work. We'll do a copy
791                 try {
792                     copyFiles(newFile, oldFile);
793                 } catch (IOException ex) {
794                     throw new MojoExecutionException("Could not replace original artifact with shaded artifact!", ex);
795                 }
796             }
797         }
798     }
799 
800     private void copyFiles(File source, File target) throws IOException {
801         try (InputStream in = Files.newInputStream(source.toPath());
802                 OutputStream out = Files.newOutputStream(target.toPath())) {
803             IOUtil.copy(in, out);
804         }
805     }
806 
807     private File resolveArtifactForClassifier(Artifact artifact, String classifier) {
808         org.eclipse.aether.artifact.Artifact coordinate = RepositoryUtils.toArtifact(new DefaultArtifact(
809                 artifact.getGroupId(),
810                 artifact.getArtifactId(),
811                 artifact.getVersionRange(),
812                 artifact.getScope(),
813                 artifact.getType(),
814                 classifier,
815                 artifact.getArtifactHandler(),
816                 artifact.isOptional()));
817 
818         ArtifactRequest request = new ArtifactRequest(
819                 coordinate, RepositoryUtils.toRepos(project.getRemoteArtifactRepositories()), "shade");
820 
821         Artifact resolvedArtifact;
822         try {
823             ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
824             resolvedArtifact = RepositoryUtils.toArtifact(result.getArtifact());
825         } catch (ArtifactResolutionException e) {
826             getLog().warn("Could not get " + classifier + " for " + artifact);
827             return null;
828         }
829 
830         if (resolvedArtifact.isResolved()) {
831             return resolvedArtifact.getFile();
832         }
833         return null;
834     }
835 
836     private List<Relocator> getRelocators() {
837         List<Relocator> relocators = new ArrayList<>();
838 
839         if (relocations == null) {
840             return relocators;
841         }
842 
843         for (PackageRelocation r : relocations) {
844             relocators.add(new SimpleRelocator(
845                     r.getPattern(), r.getShadedPattern(), r.getIncludes(), r.getExcludes(), r.isRawString()));
846         }
847 
848         return relocators;
849     }
850 
851     private List<ResourceTransformer> getResourceTransformers() {
852         if (transformers == null) {
853             return Collections.emptyList();
854         }
855 
856         return Arrays.asList(transformers);
857     }
858 
859     private List<Filter> getFilters() throws MojoExecutionException {
860         List<Filter> filters = new ArrayList<>();
861         List<SimpleFilter> simpleFilters = new ArrayList<>();
862 
863         if (this.filters != null && this.filters.length > 0) {
864             Map<Artifact, ArtifactId> artifacts = new HashMap<>();
865 
866             artifacts.put(project.getArtifact(), new ArtifactId(project.getArtifact()));
867 
868             for (Artifact artifact : project.getArtifacts()) {
869                 artifacts.put(artifact, new ArtifactId(artifact));
870             }
871 
872             for (ArchiveFilter filter : this.filters) {
873                 ArtifactId pattern = new ArtifactId(filter.getArtifact());
874 
875                 Set<File> jars = new HashSet<>();
876 
877                 for (Map.Entry<Artifact, ArtifactId> entry : artifacts.entrySet()) {
878                     if (entry.getValue().matches(pattern)) {
879                         Artifact artifact = entry.getKey();
880 
881                         jars.add(artifact.getFile());
882 
883                         if (createSourcesJar) {
884                             File file = resolveArtifactForClassifier(artifact, "sources");
885                             if (file != null) {
886                                 jars.add(file);
887                             }
888                         }
889 
890                         if (shadeTestJar) {
891                             File file = resolveArtifactForClassifier(artifact, "tests");
892                             if (file != null) {
893                                 jars.add(file);
894                             }
895                         }
896                     }
897                 }
898 
899                 if (jars.isEmpty()) {
900                     getLog().info("No artifact matching filter " + filter.getArtifact());
901 
902                     continue;
903                 }
904 
905                 simpleFilters.add(new SimpleFilter(jars, filter));
906             }
907         }
908 
909         filters.addAll(simpleFilters);
910 
911         if (minimizeJar) {
912             if (entryPoints == null) {
913                 entryPoints = new HashSet<>();
914             }
915             getLog().info("Minimizing jar " + project.getArtifact()
916                     + (entryPoints.isEmpty() ? "" : " with entry points"));
917 
918             try {
919                 filters.add(new MinijarFilter(project, getLog(), simpleFilters, entryPoints));
920             } catch (IOException e) {
921                 throw new MojoExecutionException("Failed to analyze class dependencies", e);
922             }
923         }
924 
925         return filters;
926     }
927 
928     private File shadedArtifactFileWithClassifier() {
929         Artifact artifact = project.getArtifact();
930         final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "."
931                 + artifact.getArtifactHandler().getExtension();
932         return new File(outputDirectory, shadedName);
933     }
934 
935     private File shadedSourceArtifactFileWithClassifier() {
936         return shadedArtifactFileWithClassifier("sources");
937     }
938 
939     private File shadedTestSourceArtifactFileWithClassifier() {
940         return shadedArtifactFileWithClassifier("test-sources");
941     }
942 
943     private File shadedArtifactFileWithClassifier(String classifier) {
944         Artifact artifact = project.getArtifact();
945         final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "-"
946                 + classifier + "." + artifact.getArtifactHandler().getExtension();
947         return new File(outputDirectory, shadedName);
948     }
949 
950     private File shadedTestArtifactFileWithClassifier() {
951         return shadedArtifactFileWithClassifier("tests");
952     }
953 
954     private File shadedSourcesArtifactFile() {
955         return shadedArtifactFile("sources");
956     }
957 
958     private File shadedTestSourcesArtifactFile() {
959         return shadedArtifactFile("test-sources");
960     }
961 
962     private File shadedArtifactFile(String classifier) {
963         Artifact artifact = project.getArtifact();
964 
965         String shadedName;
966 
967         if (project.getBuild().getFinalName() != null) {
968             shadedName = project.getBuild().getFinalName() + "-" + classifier + "."
969                     + artifact.getArtifactHandler().getExtension();
970         } else {
971             shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + classifier + "."
972                     + artifact.getArtifactHandler().getExtension();
973         }
974 
975         return new File(outputDirectory, shadedName);
976     }
977 
978     private File shadedTestArtifactFile() {
979         return shadedArtifactFile("tests");
980     }
981 
982     // We need to find the direct dependencies that have been included in the uber JAR so that we can modify the
983     // POM accordingly.
984     private void createDependencyReducedPom(Set<String> artifactsToRemove)
985             throws IOException, DependencyGraphBuilderException, ProjectBuildingException {
986         List<Dependency> transitiveDeps = new ArrayList<>();
987 
988         // NOTE: By using the getArtifacts() we get the completely evaluated artifacts
989         // including the system scoped artifacts with expanded values of properties used.
990         for (Artifact artifact : project.getArtifacts()) {
991             if ("pom".equals(artifact.getType())) {
992                 // don't include pom type dependencies in dependency reduced pom
993                 continue;
994             }
995 
996             // promote
997             Dependency dep = createDependency(artifact);
998 
999             // we'll figure out the exclusions in a bit.
1000             transitiveDeps.add(dep);
1001         }
1002 
1003         Model model = project.getOriginalModel();
1004 
1005         // MSHADE-413: Must not use objects (for example `Model` or `Dependency`) that are "owned
1006         // by Maven" and being used by other projects/plugins. Modifying those will break the
1007         // correctness of the build - or cause an endless loop.
1008         List<Dependency> origDeps = new ArrayList<>();
1009         List<Dependency> source = promoteTransitiveDependencies ? transitiveDeps : project.getDependencies();
1010         for (Dependency d : source) {
1011             origDeps.add(d.clone());
1012         }
1013         model = model.clone();
1014 
1015         // MSHADE-185: We will remove all system scoped dependencies which usually
1016         // have some kind of property usage. At this time the properties within
1017         // such things are already evaluated.
1018         List<Dependency> originalDependencies = model.getDependencies();
1019         removeSystemScopedDependencies(artifactsToRemove, originalDependencies);
1020 
1021         List<Dependency> dependencies = new ArrayList<>();
1022         boolean modified = false;
1023         for (Dependency d : origDeps) {
1024             if (artifactsToRemove.contains(getId(d))) {
1025                 if (keepDependenciesWithProvidedScope) {
1026                     if (!"provided".equals(d.getScope())) {
1027                         modified = true;
1028                         d.setScope("provided");
1029                     }
1030                 } else {
1031                     modified = true;
1032                     continue;
1033                 }
1034             }
1035 
1036             dependencies.add(d);
1037         }
1038 
1039         // MSHADE-155
1040         model.setArtifactId(shadedArtifactId);
1041 
1042         // MSHADE-185: We will add those system scoped dependencies
1043         // from the non interpolated original pom file. So we keep
1044         // things like this: <systemPath>${tools.jar}</systemPath> intact.
1045         addSystemScopedDependencyFromNonInterpolatedPom(dependencies, originalDependencies);
1046 
1047         // Check to see if we have a reduction and if so rewrite the POM.
1048         rewriteDependencyReducedPomIfWeHaveReduction(dependencies, modified, transitiveDeps, model);
1049     }
1050 
1051     private void rewriteDependencyReducedPomIfWeHaveReduction(
1052             List<Dependency> dependencies, boolean modified, List<Dependency> transitiveDeps, Model model)
1053             throws IOException, ProjectBuildingException, DependencyGraphBuilderException {
1054         if (modified) {
1055             for (int loopCounter = 0; modified; loopCounter++) {
1056 
1057                 model.setDependencies(dependencies);
1058 
1059                 if (generateUniqueDependencyReducedPom) {
1060                     dependencyReducedPomLocation = Files.createTempFile(
1061                                     project.getBasedir().toPath(), "dependency-reduced-pom-", ".xml")
1062                             .toFile();
1063                     project.getProperties()
1064                             .setProperty(
1065                                     "maven.shade.dependency-reduced-pom",
1066                                     dependencyReducedPomLocation.getAbsolutePath());
1067                 } else {
1068                     if (dependencyReducedPomLocation == null) {
1069                         // MSHADE-123: We can't default to 'target' because it messes up uses of ${project.basedir}
1070                         dependencyReducedPomLocation = new File(project.getBasedir(), "dependency-reduced-pom.xml");
1071                     }
1072                 }
1073 
1074                 File f = dependencyReducedPomLocation;
1075                 // MSHADE-225
1076                 // Works for now, maybe there's a better algorithm where no for-loop is required
1077                 if (loopCounter == 0) {
1078                     getLog().info("Dependency-reduced POM written at: " + f.getAbsolutePath());
1079                 }
1080 
1081                 if (f.exists()) {
1082                     // noinspection ResultOfMethodCallIgnored
1083                     f.delete();
1084                 }
1085 
1086                 Writer w = WriterFactory.newXmlWriter(f);
1087 
1088                 String replaceRelativePath = null;
1089                 if (model.getParent() != null) {
1090                     replaceRelativePath = model.getParent().getRelativePath();
1091                 }
1092 
1093                 if (model.getParent() != null) {
1094                     File parentFile =
1095                             new File(project.getBasedir(), model.getParent().getRelativePath()).getCanonicalFile();
1096                     if (!parentFile.isFile()) {
1097                         parentFile = new File(parentFile, "pom.xml");
1098                     }
1099 
1100                     parentFile = parentFile.getCanonicalFile();
1101 
1102                     String relPath = RelativizePath.convertToRelativePath(parentFile, f);
1103                     model.getParent().setRelativePath(relPath);
1104                 }
1105 
1106                 try {
1107                     PomWriter.write(w, model, true);
1108                 } finally {
1109                     if (model.getParent() != null) {
1110                         model.getParent().setRelativePath(replaceRelativePath);
1111                     }
1112                     w.close();
1113                 }
1114 
1115                 ProjectBuildingRequest projectBuildingRequest =
1116                         new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
1117                 projectBuildingRequest.setLocalRepository(session.getLocalRepository());
1118                 projectBuildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories());
1119 
1120                 ProjectBuildingResult result = projectBuilder.build(f, projectBuildingRequest);
1121 
1122                 getLog().debug("updateExcludesInDeps()");
1123                 modified = updateExcludesInDeps(result.getProject(), dependencies, transitiveDeps);
1124             }
1125 
1126             project.setFile(dependencyReducedPomLocation);
1127         }
1128     }
1129 
1130     private void removeSystemScopedDependencies(Set<String> artifactsToRemove, List<Dependency> originalDependencies) {
1131         for (Dependency dependency : originalDependencies) {
1132             if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) {
1133                 artifactsToRemove.add(getId(dependency));
1134             }
1135         }
1136     }
1137 
1138     private void addSystemScopedDependencyFromNonInterpolatedPom(
1139             List<Dependency> dependencies, List<Dependency> originalDependencies) {
1140         for (Dependency dependency : originalDependencies) {
1141             if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) {
1142                 dependencies.add(dependency);
1143             }
1144         }
1145     }
1146 
1147     private Dependency createDependency(Artifact artifact) {
1148         Dependency dep = new Dependency();
1149         dep.setArtifactId(artifact.getArtifactId());
1150         if (artifact.hasClassifier()) {
1151             dep.setClassifier(artifact.getClassifier());
1152         }
1153         dep.setGroupId(artifact.getGroupId());
1154         dep.setOptional(artifact.isOptional());
1155         dep.setScope(artifact.getScope());
1156         dep.setType(artifact.getType());
1157         if (useBaseVersion) {
1158             dep.setVersion(artifact.getBaseVersion());
1159         } else {
1160             dep.setVersion(artifact.getVersion());
1161         }
1162         return dep;
1163     }
1164 
1165     private String getId(Artifact artifact) {
1166         return getId(artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier());
1167     }
1168 
1169     private String getId(Dependency dependency) {
1170         return getId(
1171                 dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(), dependency.getClassifier());
1172     }
1173 
1174     private String getId(String groupId, String artifactId, String type, String classifier) {
1175         return groupId + ":" + artifactId + ":" + type + ":" + ((classifier != null) ? classifier : "");
1176     }
1177 
1178     public boolean updateExcludesInDeps(
1179             MavenProject project, List<Dependency> dependencies, List<Dependency> transitiveDeps)
1180             throws DependencyGraphBuilderException {
1181         MavenProject original = session.getProjectBuildingRequest().getProject();
1182         try {
1183             session.getProjectBuildingRequest().setProject(project);
1184             DependencyNode node =
1185                     dependencyGraphBuilder.buildDependencyGraph(session.getProjectBuildingRequest(), null);
1186             boolean modified = false;
1187             for (DependencyNode n2 : node.getChildren()) {
1188                 String artifactId2 = getId(n2.getArtifact());
1189 
1190                 for (DependencyNode n3 : n2.getChildren()) {
1191                     Artifact artifact3 = n3.getArtifact();
1192                     String artifactId3 = getId(artifact3);
1193 
1194                     // check if it really isn't in the list of original dependencies. Maven
1195                     // prior to 2.0.8 may grab versions from transients instead of
1196                     // from the direct deps in which case they would be marked included
1197                     // instead of OMITTED_FOR_DUPLICATE
1198 
1199                     // also, if not promoting the transitives, level 2's would be included
1200                     boolean found = false;
1201                     for (Dependency dep : transitiveDeps) {
1202                         if (getId(dep).equals(artifactId3)) {
1203                             found = true;
1204                             break;
1205                         }
1206                     }
1207 
1208                     // MSHADE-311: do not add exclusion for provided transitive dep
1209                     //       note: MSHADE-31 introduced the exclusion logic for promoteTransitiveDependencies=true,
1210                     //             but as of 3.2.1 promoteTransitiveDependencies has no effect for provided deps,
1211                     //             which makes this fix even possible (see also MSHADE-181)
1212                     if (!found && !"provided".equals(artifact3.getScope())) {
1213                         getLog().debug(String.format(
1214                                 "dependency %s (scope %s) not found in transitive dependencies",
1215                                 artifactId3, artifact3.getScope()));
1216                         for (Dependency dep : dependencies) {
1217                             if (getId(dep).equals(artifactId2)) {
1218                                 // MSHADE-413: First check whether the exclusion has already been added,
1219                                 // because it's meaningless to add it more than once. Certain cases
1220                                 // can end up adding the exclusion "forever" and cause an endless loop
1221                                 // rewriting the whole dependency-reduced-pom.xml file.
1222                                 if (!dependencyHasExclusion(dep, artifact3)) {
1223                                     getLog().debug(String.format(
1224                                             "Adding exclusion for dependency %s (scope %s) " + "to %s (scope %s)",
1225                                             artifactId3, artifact3.getScope(), getId(dep), dep.getScope()));
1226                                     Exclusion exclusion = new Exclusion();
1227                                     exclusion.setArtifactId(artifact3.getArtifactId());
1228                                     exclusion.setGroupId(artifact3.getGroupId());
1229                                     dep.addExclusion(exclusion);
1230                                     modified = true;
1231                                     break;
1232                                 }
1233                             }
1234                         }
1235                     }
1236                 }
1237             }
1238             return modified;
1239         } finally {
1240             // restore it
1241             session.getProjectBuildingRequest().setProject(original);
1242         }
1243     }
1244 
1245     private boolean dependencyHasExclusion(Dependency dep, Artifact exclusionToCheck) {
1246         boolean containsExclusion = false;
1247         for (Exclusion existingExclusion : dep.getExclusions()) {
1248             if (existingExclusion.getGroupId().equals(exclusionToCheck.getGroupId())
1249                     && existingExclusion.getArtifactId().equals(exclusionToCheck.getArtifactId())) {
1250                 containsExclusion = true;
1251                 break;
1252             }
1253         }
1254         return containsExclusion;
1255     }
1256 
1257     private List<ResourceTransformer> toResourceTransformers(
1258             String shade, List<ResourceTransformer> resourceTransformers) {
1259         List<ResourceTransformer> forShade = new ArrayList<>();
1260         ManifestResourceTransformer lastMt = null;
1261         for (ResourceTransformer transformer : resourceTransformers) {
1262             if (!(transformer instanceof ManifestResourceTransformer)) {
1263                 forShade.add(transformer);
1264             } else if (((ManifestResourceTransformer) transformer).isForShade(shade)) {
1265                 final ManifestResourceTransformer mt = (ManifestResourceTransformer) transformer;
1266                 if (mt.isUsedForDefaultShading() && lastMt != null && !lastMt.isUsedForDefaultShading()) {
1267                     continue; // skip, we already have a specific transformer
1268                 }
1269                 if (!mt.isUsedForDefaultShading() && lastMt != null && lastMt.isUsedForDefaultShading()) {
1270                     forShade.remove(lastMt);
1271                 } else if (!mt.isUsedForDefaultShading() && lastMt != null) {
1272                     getLog().warn("Ambiguous manifest transformer definition for '" + shade + "': " + mt + " / "
1273                             + lastMt);
1274                 }
1275                 if (lastMt == null || !mt.isUsedForDefaultShading()) {
1276                     lastMt = mt;
1277                 }
1278                 forShade.add(transformer);
1279             }
1280         }
1281         return forShade;
1282     }
1283 }