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