1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
84
85
86
87
88
89
90
91 @Mojo(
92 name = "shade",
93 defaultPhase = LifecyclePhase.PACKAGE,
94 threadSafe = true,
95 requiresDependencyResolution = ResolutionScope.RUNTIME)
96
97 public class ShadeMojo extends AbstractMojo {
98
99
100
101 @Parameter(defaultValue = "${session}", readonly = true, required = true)
102 private MavenSession session;
103
104
105
106
107 @Parameter(defaultValue = "${project}", readonly = true, required = true)
108 private MavenProject project;
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 @Parameter
130 private ArtifactSet artifactSet;
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 @SuppressWarnings("MismatchedReadAndWriteOfArray")
153 @Parameter
154 private PackageRelocation[] relocations;
155
156
157
158
159
160 @Parameter
161 private ResourceTransformer[] transformers;
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 @SuppressWarnings("MismatchedReadAndWriteOfArray")
186 @Parameter
187 private ArchiveFilter[] filters;
188
189
190
191
192 @Parameter(defaultValue = "${project.build.directory}")
193 private File outputDirectory;
194
195
196
197
198
199
200
201
202 @Parameter
203 private String finalName;
204
205
206
207
208
209
210 @Parameter(defaultValue = "${project.artifactId}")
211 private String shadedArtifactId;
212
213
214
215
216 @Parameter
217 private String shadedGroupFilter;
218
219
220
221
222
223 @Parameter
224 private boolean shadedArtifactAttached;
225
226
227
228
229
230
231
232
233 @Parameter(defaultValue = "true")
234 private boolean createDependencyReducedPom;
235
236
237
238
239
240
241
242
243 @Parameter(defaultValue = "${basedir}/dependency-reduced-pom.xml")
244 private File dependencyReducedPomLocation;
245
246
247
248
249
250
251
252
253 @Parameter(defaultValue = "false")
254 private boolean generateUniqueDependencyReducedPom;
255
256
257
258
259
260
261
262 @Parameter(defaultValue = "false")
263 private boolean useDependencyReducedPomInJar;
264
265
266
267
268 @Parameter
269 private boolean keepDependenciesWithProvidedScope;
270
271
272
273
274
275 @Parameter
276 private boolean promoteTransitiveDependencies;
277
278
279
280
281 @Parameter(defaultValue = "shaded")
282 private String shadedClassifierName;
283
284
285
286
287 @Parameter
288 private boolean createSourcesJar;
289
290
291
292
293 @Parameter
294 private boolean createTestSourcesJar;
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311 @Parameter(property = "shadeSourcesContent", defaultValue = "false")
312 private boolean shadeSourcesContent;
313
314
315
316
317
318
319
320
321
322
323
324 @Parameter
325 private boolean minimizeJar;
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348 @Parameter
349 private Set<String> entryPoints;
350
351
352
353
354
355
356
357
358
359 @Parameter
360 private File outputFile;
361
362
363
364
365
366
367 @Parameter
368 private String shaderHint;
369
370
371
372
373
374
375
376
377
378 @Parameter(defaultValue = "false")
379 private boolean useBaseVersion;
380
381
382
383
384 @Parameter(defaultValue = "false")
385 private boolean shadeTestJar;
386
387
388
389
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
405
406 @Inject
407 private DependencyGraphBuilder dependencyGraphBuilder;
408
409
410
411
412 @Inject
413 private ProjectBuilder projectBuilder;
414
415
416
417
418 @Inject
419 private Map<String, Shader> shaders;
420
421
422
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
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
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
534
535
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
546 if (createSourcesJar) {
547 finalFileName = finalName + "-sources.jar";
548 finalFile = new File(outputDirectory, finalFileName);
549 replaceFile(finalFile, sourcesJar);
550 sourcesJar = finalFile;
551 }
552
553
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
772 System.gc();
773 System.gc();
774
775 if (!oldFile.renameTo(origFile)) {
776
777 try {
778 copyFiles(oldFile, origFile);
779 } catch (IOException ex) {
780
781 getLog().warn(ex);
782 }
783 }
784 }
785 if (!newFile.renameTo(oldFile)) {
786
787 System.gc();
788 System.gc();
789
790 if (!newFile.renameTo(oldFile)) {
791
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
986
987 private void createDependencyReducedPom(Set<String> artifactsToRemove)
988 throws IOException, DependencyGraphBuilderException, ProjectBuildingException {
989 List<Dependency> transitiveDeps = new ArrayList<>();
990
991
992
993 for (Artifact artifact : project.getArtifacts()) {
994 if ("pom".equals(artifact.getType())) {
995
996 continue;
997 }
998
999
1000 Dependency dep = createDependency(artifact);
1001
1002
1003 transitiveDeps.add(dep);
1004 }
1005
1006 Model model = project.getOriginalModel();
1007
1008
1009
1010
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
1019
1020
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
1043 model.setArtifactId(shadedArtifactId);
1044
1045
1046
1047
1048 addSystemScopedDependencyFromNonInterpolatedPom(dependencies, originalDependencies);
1049
1050
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
1073 dependencyReducedPomLocation = new File(project.getBasedir(), "dependency-reduced-pom.xml");
1074 }
1075 }
1076
1077 File f = dependencyReducedPomLocation;
1078
1079
1080 if (loopCounter == 0) {
1081 getLog().info("Dependency-reduced POM written at: " + f.getAbsolutePath());
1082 }
1083
1084 if (f.exists()) {
1085
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()) {
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
1200
1201
1202
1203
1204
1205 boolean found = false;
1206 for (Dependency dep : transitiveDeps) {
1207 if (getId(dep).equals(artifactId3)) {
1208 found = true;
1209 break;
1210 }
1211 }
1212
1213
1214
1215
1216
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
1224
1225
1226
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
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;
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 }