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.execution.MavenSession;
43 import org.apache.maven.model.Dependency;
44 import org.apache.maven.model.Exclusion;
45 import org.apache.maven.model.Model;
46 import org.apache.maven.plugin.AbstractMojo;
47 import org.apache.maven.plugin.MojoExecutionException;
48 import org.apache.maven.plugins.annotations.LifecyclePhase;
49 import org.apache.maven.plugins.annotations.Mojo;
50 import org.apache.maven.plugins.annotations.Parameter;
51 import org.apache.maven.plugins.annotations.ResolutionScope;
52 import org.apache.maven.plugins.shade.ShadeRequest;
53 import org.apache.maven.plugins.shade.Shader;
54 import org.apache.maven.plugins.shade.filter.Filter;
55 import org.apache.maven.plugins.shade.filter.MinijarFilter;
56 import org.apache.maven.plugins.shade.filter.SimpleFilter;
57 import org.apache.maven.plugins.shade.pom.PomWriter;
58 import org.apache.maven.plugins.shade.relocation.Relocator;
59 import org.apache.maven.plugins.shade.relocation.SimpleRelocator;
60 import org.apache.maven.plugins.shade.resource.ManifestResourceTransformer;
61 import org.apache.maven.plugins.shade.resource.ResourceTransformer;
62 import org.apache.maven.project.DefaultProjectBuildingRequest;
63 import org.apache.maven.project.MavenProject;
64 import org.apache.maven.project.MavenProjectHelper;
65 import org.apache.maven.project.ProjectBuilder;
66 import org.apache.maven.project.ProjectBuildingException;
67 import org.apache.maven.project.ProjectBuildingRequest;
68 import org.apache.maven.project.ProjectBuildingResult;
69 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
70 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
71 import org.apache.maven.shared.dependency.graph.DependencyNode;
72 import org.codehaus.plexus.util.IOUtil;
73 import org.codehaus.plexus.util.WriterFactory;
74 import org.eclipse.aether.RepositorySystem;
75 import org.eclipse.aether.resolution.ArtifactRequest;
76 import org.eclipse.aether.resolution.ArtifactResolutionException;
77 import org.eclipse.aether.resolution.ArtifactResult;
78
79 import static org.apache.maven.plugins.shade.resource.UseDependencyReducedPom.createPomReplaceTransformers;
80
81
82
83
84
85
86
87
88
89
90 @Mojo(
91 name = "shade",
92 defaultPhase = LifecyclePhase.PACKAGE,
93 threadSafe = true,
94 requiresDependencyResolution = ResolutionScope.RUNTIME)
95
96 public class ShadeMojo extends AbstractMojo {
97
98
99
100 @Parameter(defaultValue = "${session}", readonly = true, required = true)
101 private MavenSession session;
102
103
104
105
106 @Parameter(defaultValue = "${project}", readonly = true, required = true)
107 private MavenProject project;
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 @Parameter
129 private ArtifactSet artifactSet;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 @SuppressWarnings("MismatchedReadAndWriteOfArray")
152 @Parameter
153 private PackageRelocation[] relocations;
154
155
156
157
158
159 @Parameter
160 private ResourceTransformer[] transformers;
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 @SuppressWarnings("MismatchedReadAndWriteOfArray")
185 @Parameter
186 private ArchiveFilter[] filters;
187
188
189
190
191 @Parameter(defaultValue = "${project.build.directory}")
192 private File outputDirectory;
193
194
195
196
197
198
199
200
201 @Parameter
202 private String finalName;
203
204
205
206
207
208
209 @Parameter(defaultValue = "${project.artifactId}")
210 private String shadedArtifactId;
211
212
213
214
215 @Parameter
216 private String shadedGroupFilter;
217
218
219
220
221
222 @Parameter
223 private boolean shadedArtifactAttached;
224
225
226
227
228
229
230
231
232 @Parameter(defaultValue = "true")
233 private boolean createDependencyReducedPom;
234
235
236
237
238
239
240
241
242 @Parameter(defaultValue = "${basedir}/dependency-reduced-pom.xml")
243 private File dependencyReducedPomLocation;
244
245
246
247
248
249
250
251
252 @Parameter(defaultValue = "false")
253 private boolean generateUniqueDependencyReducedPom;
254
255
256
257
258
259
260
261 @Parameter(defaultValue = "false")
262 private boolean useDependencyReducedPomInJar;
263
264
265
266
267 @Parameter
268 private boolean keepDependenciesWithProvidedScope;
269
270
271
272
273
274 @Parameter
275 private boolean promoteTransitiveDependencies;
276
277
278
279
280 @Parameter(defaultValue = "shaded")
281 private String shadedClassifierName;
282
283
284
285
286 @Parameter
287 private boolean createSourcesJar;
288
289
290
291
292 @Parameter
293 private boolean createTestSourcesJar;
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310 @Parameter(property = "shadeSourcesContent", defaultValue = "false")
311 private boolean shadeSourcesContent;
312
313
314
315
316
317
318
319
320
321
322
323 @Parameter
324 private boolean minimizeJar;
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347 @Parameter
348 private Set<String> entryPoints;
349
350
351
352
353
354
355
356
357
358 @Parameter
359 private File outputFile;
360
361
362
363
364
365
366 @Parameter
367 private String shaderHint;
368
369
370
371
372
373
374
375
376
377 @Parameter(defaultValue = "false")
378 private boolean useBaseVersion;
379
380
381
382
383 @Parameter(defaultValue = "false")
384 private boolean shadeTestJar;
385
386
387
388
389
390 @Parameter(defaultValue = "false")
391 private boolean skip;
392
393 @Inject
394 private MavenProjectHelper projectHelper;
395
396 @Inject
397 private Shader shader;
398
399 @Inject
400 private RepositorySystem repositorySystem;
401
402
403
404
405 @Inject
406 private DependencyGraphBuilder dependencyGraphBuilder;
407
408
409
410
411 @Inject
412 private ProjectBuilder projectBuilder;
413
414
415
416
417 @Inject
418 private Map<String, Shader> shaders;
419
420
421
422
423 @Override
424 public void execute() throws MojoExecutionException {
425 if (skip) {
426 getLog().info("Shading has been skipped.");
427 return;
428 }
429
430 setupHintedShader();
431
432 Set<File> artifacts = new LinkedHashSet<>();
433 Set<String> artifactIds = new LinkedHashSet<>();
434 Set<File> sourceArtifacts = new LinkedHashSet<>();
435 Set<File> testArtifacts = new LinkedHashSet<>();
436 Set<File> testSourceArtifacts = new LinkedHashSet<>();
437
438 ArtifactSelector artifactSelector = new ArtifactSelector(project.getArtifact(), artifactSet, shadedGroupFilter);
439
440 if (artifactSelector.isSelected(project.getArtifact())
441 && !"pom".equals(project.getArtifact().getType())) {
442 if (invalidMainArtifact()) {
443 createErrorOutput();
444 throw new MojoExecutionException(
445 "Failed to create shaded artifact, " + "project main artifact does not exist.");
446 }
447
448 artifacts.add(project.getArtifact().getFile());
449
450 if (createSourcesJar) {
451 File file = shadedSourcesArtifactFile();
452 if (file.isFile()) {
453 sourceArtifacts.add(file);
454 }
455 }
456
457 if (shadeTestJar) {
458 File file = shadedTestArtifactFile();
459 if (file.isFile()) {
460 testArtifacts.add(file);
461 }
462 }
463
464 if (createTestSourcesJar) {
465 File file = shadedTestSourcesArtifactFile();
466 if (file.isFile()) {
467 testSourceArtifacts.add(file);
468 }
469 }
470 }
471
472 processArtifactSelectors(
473 artifacts, artifactIds, sourceArtifacts, testArtifacts, testSourceArtifacts, artifactSelector);
474
475 File outputJar = (outputFile != null) ? outputFile : shadedArtifactFileWithClassifier();
476 File sourcesJar = shadedSourceArtifactFileWithClassifier();
477 File testJar = shadedTestArtifactFileWithClassifier();
478 File testSourcesJar = shadedTestSourceArtifactFileWithClassifier();
479
480
481 try {
482 List<Filter> filters = getFilters();
483
484 List<Relocator> relocators = getRelocators();
485
486 List<ResourceTransformer> resourceTransformers = getResourceTransformers();
487
488 if (createDependencyReducedPom) {
489 createDependencyReducedPom(artifactIds);
490
491 if (useDependencyReducedPomInJar) {
492
493 resourceTransformers = new ArrayList<>(resourceTransformers);
494 resourceTransformers.addAll(createPomReplaceTransformers(project, dependencyReducedPomLocation));
495 }
496 }
497
498 ShadeRequest shadeRequest =
499 shadeRequest("jar", artifacts, outputJar, filters, relocators, resourceTransformers);
500
501 shader.shade(shadeRequest);
502
503 if (createSourcesJar) {
504 ShadeRequest shadeSourcesRequest = createShadeSourcesRequest(
505 "sources-jar", sourceArtifacts, sourcesJar, filters, relocators, resourceTransformers);
506
507 shader.shade(shadeSourcesRequest);
508 }
509
510 if (shadeTestJar) {
511 ShadeRequest shadeTestRequest =
512 shadeRequest("test-jar", testArtifacts, testJar, filters, relocators, resourceTransformers);
513
514 shader.shade(shadeTestRequest);
515 }
516
517 if (createTestSourcesJar) {
518 ShadeRequest shadeTestSourcesRequest = createShadeSourcesRequest(
519 "test-sources-jar",
520 testSourceArtifacts,
521 testSourcesJar,
522 filters,
523 relocators,
524 resourceTransformers);
525
526 shader.shade(shadeTestSourcesRequest);
527 }
528
529 if (outputFile == null) {
530 boolean renamed = false;
531
532
533
534
535 if (finalName != null
536 && finalName.length() > 0
537 && !finalName.equals(project.getBuild().getFinalName())) {
538 String finalFileName = finalName + "."
539 + project.getArtifact().getArtifactHandler().getExtension();
540 File finalFile = new File(outputDirectory, finalFileName);
541 replaceFile(finalFile, outputJar);
542 outputJar = finalFile;
543
544
545 if (createSourcesJar) {
546 finalFileName = finalName + "-sources.jar";
547 finalFile = new File(outputDirectory, finalFileName);
548 replaceFile(finalFile, sourcesJar);
549 sourcesJar = finalFile;
550 }
551
552
553 if (shadeTestJar) {
554 finalFileName = finalName + "-tests.jar";
555 finalFile = new File(outputDirectory, finalFileName);
556 replaceFile(finalFile, testJar);
557 testJar = finalFile;
558 }
559
560 if (createTestSourcesJar) {
561 finalFileName = finalName + "-test-sources.jar";
562 finalFile = new File(outputDirectory, finalFileName);
563 replaceFile(finalFile, testSourcesJar);
564 testSourcesJar = finalFile;
565 }
566
567 renamed = true;
568 }
569
570 if (shadedArtifactAttached) {
571 getLog().info("Attaching shaded artifact.");
572 projectHelper.attachArtifact(
573 project, project.getArtifact().getType(), shadedClassifierName, outputJar);
574 if (createSourcesJar) {
575 projectHelper.attachArtifact(
576 project, "java-source", shadedClassifierName + "-sources", sourcesJar);
577 }
578
579 if (shadeTestJar) {
580 projectHelper.attachArtifact(project, "test-jar", shadedClassifierName + "-tests", testJar);
581 }
582
583 if (createTestSourcesJar) {
584 projectHelper.attachArtifact(
585 project, "java-source", shadedClassifierName + "-test-sources", testSourcesJar);
586 }
587 } else if (!renamed) {
588 getLog().info("Replacing original artifact with shaded artifact.");
589 File originalArtifact = project.getArtifact().getFile();
590 if (originalArtifact != null) {
591 replaceFile(originalArtifact, outputJar);
592
593 if (createSourcesJar) {
594 getLog().info("Replacing original source artifact with shaded source artifact.");
595 File shadedSources = shadedSourcesArtifactFile();
596
597 replaceFile(shadedSources, sourcesJar);
598
599 projectHelper.attachArtifact(project, "java-source", "sources", shadedSources);
600 }
601
602 if (shadeTestJar) {
603 getLog().info("Replacing original test artifact with shaded test artifact.");
604 File shadedTests = shadedTestArtifactFile();
605
606 replaceFile(shadedTests, testJar);
607
608 projectHelper.attachArtifact(project, "test-jar", shadedTests);
609 }
610
611 if (createTestSourcesJar) {
612 getLog().info("Replacing original test source artifact "
613 + "with shaded test source artifact.");
614 File shadedTestSources = shadedTestSourcesArtifactFile();
615
616 replaceFile(shadedTestSources, testSourcesJar);
617
618 projectHelper.attachArtifact(project, "java-source", "test-sources", shadedTestSources);
619 }
620 }
621 }
622 }
623 } catch (Exception e) {
624 throw new MojoExecutionException("Error creating shaded jar: " + e.getMessage(), e);
625 }
626 }
627
628 private void createErrorOutput() {
629 getLog().error("The project main artifact does not exist. This could have the following");
630 getLog().error("reasons:");
631 getLog().error("- You have invoked the goal directly from the command line. This is not");
632 getLog().error(" supported. Please add the goal to the default lifecycle via an");
633 getLog().error(" <execution> element in your POM and use \"mvn package\" to have it run.");
634 getLog().error("- You have bound the goal to a lifecycle phase before \"package\". Please");
635 getLog().error(" remove this binding from your POM such that the goal will be run in");
636 getLog().error(" the proper phase.");
637 getLog().error("- You removed the configuration of the maven-jar-plugin that produces the main artifact.");
638 }
639
640 private ShadeRequest shadeRequest(
641 String shade,
642 Set<File> artifacts,
643 File outputJar,
644 List<Filter> filters,
645 List<Relocator> relocators,
646 List<ResourceTransformer> resourceTransformers) {
647 ShadeRequest shadeRequest = new ShadeRequest();
648 shadeRequest.setJars(artifacts);
649 shadeRequest.setUberJar(outputJar);
650 shadeRequest.setFilters(filters);
651 shadeRequest.setRelocators(relocators);
652 shadeRequest.setResourceTransformers(toResourceTransformers(shade, resourceTransformers));
653 return shadeRequest;
654 }
655
656 private ShadeRequest createShadeSourcesRequest(
657 String shade,
658 Set<File> testArtifacts,
659 File testJar,
660 List<Filter> filters,
661 List<Relocator> relocators,
662 List<ResourceTransformer> resourceTransformers) {
663 ShadeRequest shadeSourcesRequest =
664 shadeRequest(shade, testArtifacts, testJar, filters, relocators, resourceTransformers);
665 shadeSourcesRequest.setShadeSourcesContent(shadeSourcesContent);
666 return shadeSourcesRequest;
667 }
668
669 private void setupHintedShader() throws MojoExecutionException {
670 if (shaderHint != null) {
671 shader = shaders.get(shaderHint);
672
673 if (shader == null) {
674 throw new MojoExecutionException(
675 "unable to lookup own Shader implementation with hint: '" + shaderHint + "'");
676 }
677 }
678 }
679
680 private void processArtifactSelectors(
681 Set<File> artifacts,
682 Set<String> artifactIds,
683 Set<File> sourceArtifacts,
684 Set<File> testArtifacts,
685 Set<File> testSourceArtifacts,
686 ArtifactSelector artifactSelector) {
687
688 List<String> excludedArtifacts = new ArrayList<>();
689 List<String> pomArtifacts = new ArrayList<>();
690 List<String> emptySourceArtifacts = new ArrayList<>();
691 List<String> emptyTestArtifacts = new ArrayList<>();
692 List<String> emptyTestSourceArtifacts = new ArrayList<>();
693
694 for (Artifact artifact : project.getArtifacts()) {
695 if (!artifactSelector.isSelected(artifact)) {
696 excludedArtifacts.add(artifact.getId());
697
698 continue;
699 }
700
701 if ("pom".equals(artifact.getType())) {
702 pomArtifacts.add(artifact.getId());
703 continue;
704 }
705
706 getLog().info("Including " + artifact.getId() + " in the shaded jar.");
707
708 artifacts.add(artifact.getFile());
709 artifactIds.add(getId(artifact));
710
711 if (createSourcesJar) {
712 File file = resolveArtifactForClassifier(artifact, "sources");
713 if (file != null) {
714 if (file.length() > 0) {
715 sourceArtifacts.add(file);
716 } else {
717 emptySourceArtifacts.add(artifact.getArtifactId());
718 }
719 }
720 }
721
722 if (shadeTestJar) {
723 File file = resolveArtifactForClassifier(artifact, "tests");
724 if (file != null) {
725 if (file.length() > 0) {
726 testArtifacts.add(file);
727 } else {
728 emptyTestArtifacts.add(artifact.getId());
729 }
730 }
731 }
732
733 if (createTestSourcesJar) {
734 File file = resolveArtifactForClassifier(artifact, "test-sources");
735 if (file != null) {
736 testSourceArtifacts.add(file);
737 } else {
738 emptyTestSourceArtifacts.add(artifact.getId());
739 }
740 }
741 }
742
743 for (String artifactId : excludedArtifacts) {
744 getLog().info("Excluding " + artifactId + " from the shaded jar.");
745 }
746 for (String artifactId : pomArtifacts) {
747 getLog().info("Skipping pom dependency " + artifactId + " in the shaded jar.");
748 }
749 for (String artifactId : emptySourceArtifacts) {
750 getLog().warn("Skipping empty source jar " + artifactId + ".");
751 }
752 for (String artifactId : emptyTestArtifacts) {
753 getLog().warn("Skipping empty test jar " + artifactId + ".");
754 }
755 for (String artifactId : emptyTestSourceArtifacts) {
756 getLog().warn("Skipping empty test source jar " + artifactId + ".");
757 }
758 }
759
760 private boolean invalidMainArtifact() {
761 return project.getArtifact().getFile() == null
762 || !project.getArtifact().getFile().isFile();
763 }
764
765 private void replaceFile(File oldFile, File newFile) throws MojoExecutionException {
766 getLog().info("Replacing " + oldFile + " with " + newFile);
767
768 File origFile = new File(outputDirectory, "original-" + oldFile.getName());
769 if (oldFile.exists() && !oldFile.renameTo(origFile)) {
770
771 System.gc();
772 System.gc();
773
774 if (!oldFile.renameTo(origFile)) {
775
776 try {
777 copyFiles(oldFile, origFile);
778 } catch (IOException ex) {
779
780 getLog().warn(ex);
781 }
782 }
783 }
784 if (!newFile.renameTo(oldFile)) {
785
786 System.gc();
787 System.gc();
788
789 if (!newFile.renameTo(oldFile)) {
790
791 try {
792 copyFiles(newFile, oldFile);
793 } catch (IOException ex) {
794 throw new MojoExecutionException("Could not replace original artifact with shaded artifact!", ex);
795 }
796 }
797 }
798 }
799
800 private void copyFiles(File source, File target) throws IOException {
801 try (InputStream in = Files.newInputStream(source.toPath());
802 OutputStream out = Files.newOutputStream(target.toPath())) {
803 IOUtil.copy(in, out);
804 }
805 }
806
807 private File resolveArtifactForClassifier(Artifact artifact, String classifier) {
808 org.eclipse.aether.artifact.Artifact coordinate = RepositoryUtils.toArtifact(new DefaultArtifact(
809 artifact.getGroupId(),
810 artifact.getArtifactId(),
811 artifact.getVersionRange(),
812 artifact.getScope(),
813 artifact.getType(),
814 classifier,
815 artifact.getArtifactHandler(),
816 artifact.isOptional()));
817
818 ArtifactRequest request = new ArtifactRequest(
819 coordinate, RepositoryUtils.toRepos(project.getRemoteArtifactRepositories()), "shade");
820
821 Artifact resolvedArtifact;
822 try {
823 ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
824 resolvedArtifact = RepositoryUtils.toArtifact(result.getArtifact());
825 } catch (ArtifactResolutionException e) {
826 getLog().warn("Could not get " + classifier + " for " + artifact);
827 return null;
828 }
829
830 if (resolvedArtifact.isResolved()) {
831 return resolvedArtifact.getFile();
832 }
833 return null;
834 }
835
836 private List<Relocator> getRelocators() {
837 List<Relocator> relocators = new ArrayList<>();
838
839 if (relocations == null) {
840 return relocators;
841 }
842
843 for (PackageRelocation r : relocations) {
844 relocators.add(new SimpleRelocator(
845 r.getPattern(), r.getShadedPattern(), r.getIncludes(), r.getExcludes(), r.isRawString()));
846 }
847
848 return relocators;
849 }
850
851 private List<ResourceTransformer> getResourceTransformers() {
852 if (transformers == null) {
853 return Collections.emptyList();
854 }
855
856 return Arrays.asList(transformers);
857 }
858
859 private List<Filter> getFilters() throws MojoExecutionException {
860 List<Filter> filters = new ArrayList<>();
861 List<SimpleFilter> simpleFilters = new ArrayList<>();
862
863 if (this.filters != null && this.filters.length > 0) {
864 Map<Artifact, ArtifactId> artifacts = new HashMap<>();
865
866 artifacts.put(project.getArtifact(), new ArtifactId(project.getArtifact()));
867
868 for (Artifact artifact : project.getArtifacts()) {
869 artifacts.put(artifact, new ArtifactId(artifact));
870 }
871
872 for (ArchiveFilter filter : this.filters) {
873 ArtifactId pattern = new ArtifactId(filter.getArtifact());
874
875 Set<File> jars = new HashSet<>();
876
877 for (Map.Entry<Artifact, ArtifactId> entry : artifacts.entrySet()) {
878 if (entry.getValue().matches(pattern)) {
879 Artifact artifact = entry.getKey();
880
881 jars.add(artifact.getFile());
882
883 if (createSourcesJar) {
884 File file = resolveArtifactForClassifier(artifact, "sources");
885 if (file != null) {
886 jars.add(file);
887 }
888 }
889
890 if (shadeTestJar) {
891 File file = resolveArtifactForClassifier(artifact, "tests");
892 if (file != null) {
893 jars.add(file);
894 }
895 }
896 }
897 }
898
899 if (jars.isEmpty()) {
900 getLog().info("No artifact matching filter " + filter.getArtifact());
901
902 continue;
903 }
904
905 simpleFilters.add(new SimpleFilter(jars, filter));
906 }
907 }
908
909 filters.addAll(simpleFilters);
910
911 if (minimizeJar) {
912 if (entryPoints == null) {
913 entryPoints = new HashSet<>();
914 }
915 getLog().info("Minimizing jar " + project.getArtifact()
916 + (entryPoints.isEmpty() ? "" : " with entry points"));
917
918 try {
919 filters.add(new MinijarFilter(project, getLog(), simpleFilters, entryPoints));
920 } catch (IOException e) {
921 throw new MojoExecutionException("Failed to analyze class dependencies", e);
922 }
923 }
924
925 return filters;
926 }
927
928 private File shadedArtifactFileWithClassifier() {
929 Artifact artifact = project.getArtifact();
930 final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "."
931 + artifact.getArtifactHandler().getExtension();
932 return new File(outputDirectory, shadedName);
933 }
934
935 private File shadedSourceArtifactFileWithClassifier() {
936 return shadedArtifactFileWithClassifier("sources");
937 }
938
939 private File shadedTestSourceArtifactFileWithClassifier() {
940 return shadedArtifactFileWithClassifier("test-sources");
941 }
942
943 private File shadedArtifactFileWithClassifier(String classifier) {
944 Artifact artifact = project.getArtifact();
945 final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "-"
946 + classifier + "." + artifact.getArtifactHandler().getExtension();
947 return new File(outputDirectory, shadedName);
948 }
949
950 private File shadedTestArtifactFileWithClassifier() {
951 return shadedArtifactFileWithClassifier("tests");
952 }
953
954 private File shadedSourcesArtifactFile() {
955 return shadedArtifactFile("sources");
956 }
957
958 private File shadedTestSourcesArtifactFile() {
959 return shadedArtifactFile("test-sources");
960 }
961
962 private File shadedArtifactFile(String classifier) {
963 Artifact artifact = project.getArtifact();
964
965 String shadedName;
966
967 if (project.getBuild().getFinalName() != null) {
968 shadedName = project.getBuild().getFinalName() + "-" + classifier + "."
969 + artifact.getArtifactHandler().getExtension();
970 } else {
971 shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + classifier + "."
972 + artifact.getArtifactHandler().getExtension();
973 }
974
975 return new File(outputDirectory, shadedName);
976 }
977
978 private File shadedTestArtifactFile() {
979 return shadedArtifactFile("tests");
980 }
981
982
983
984 private void createDependencyReducedPom(Set<String> artifactsToRemove)
985 throws IOException, DependencyGraphBuilderException, ProjectBuildingException {
986 List<Dependency> transitiveDeps = new ArrayList<>();
987
988
989
990 for (Artifact artifact : project.getArtifacts()) {
991 if ("pom".equals(artifact.getType())) {
992
993 continue;
994 }
995
996
997 Dependency dep = createDependency(artifact);
998
999
1000 transitiveDeps.add(dep);
1001 }
1002
1003 Model model = project.getOriginalModel();
1004
1005
1006
1007
1008 List<Dependency> origDeps = new ArrayList<>();
1009 List<Dependency> source = promoteTransitiveDependencies ? transitiveDeps : project.getDependencies();
1010 for (Dependency d : source) {
1011 origDeps.add(d.clone());
1012 }
1013 model = model.clone();
1014
1015
1016
1017
1018 List<Dependency> originalDependencies = model.getDependencies();
1019 removeSystemScopedDependencies(artifactsToRemove, originalDependencies);
1020
1021 List<Dependency> dependencies = new ArrayList<>();
1022 boolean modified = false;
1023 for (Dependency d : origDeps) {
1024 if (artifactsToRemove.contains(getId(d))) {
1025 if (keepDependenciesWithProvidedScope) {
1026 if (!"provided".equals(d.getScope())) {
1027 modified = true;
1028 d.setScope("provided");
1029 }
1030 } else {
1031 modified = true;
1032 continue;
1033 }
1034 }
1035
1036 dependencies.add(d);
1037 }
1038
1039
1040 model.setArtifactId(shadedArtifactId);
1041
1042
1043
1044
1045 addSystemScopedDependencyFromNonInterpolatedPom(dependencies, originalDependencies);
1046
1047
1048 rewriteDependencyReducedPomIfWeHaveReduction(dependencies, modified, transitiveDeps, model);
1049 }
1050
1051 private void rewriteDependencyReducedPomIfWeHaveReduction(
1052 List<Dependency> dependencies, boolean modified, List<Dependency> transitiveDeps, Model model)
1053 throws IOException, ProjectBuildingException, DependencyGraphBuilderException {
1054 if (modified) {
1055 for (int loopCounter = 0; modified; loopCounter++) {
1056
1057 model.setDependencies(dependencies);
1058
1059 if (generateUniqueDependencyReducedPom) {
1060 dependencyReducedPomLocation = Files.createTempFile(
1061 project.getBasedir().toPath(), "dependency-reduced-pom-", ".xml")
1062 .toFile();
1063 project.getProperties()
1064 .setProperty(
1065 "maven.shade.dependency-reduced-pom",
1066 dependencyReducedPomLocation.getAbsolutePath());
1067 } else {
1068 if (dependencyReducedPomLocation == null) {
1069
1070 dependencyReducedPomLocation = new File(project.getBasedir(), "dependency-reduced-pom.xml");
1071 }
1072 }
1073
1074 File f = dependencyReducedPomLocation;
1075
1076
1077 if (loopCounter == 0) {
1078 getLog().info("Dependency-reduced POM written at: " + f.getAbsolutePath());
1079 }
1080
1081 if (f.exists()) {
1082
1083 f.delete();
1084 }
1085
1086 Writer w = WriterFactory.newXmlWriter(f);
1087
1088 String replaceRelativePath = null;
1089 if (model.getParent() != null) {
1090 replaceRelativePath = model.getParent().getRelativePath();
1091 }
1092
1093 if (model.getParent() != null) {
1094 File parentFile =
1095 new File(project.getBasedir(), model.getParent().getRelativePath()).getCanonicalFile();
1096 if (!parentFile.isFile()) {
1097 parentFile = new File(parentFile, "pom.xml");
1098 }
1099
1100 parentFile = parentFile.getCanonicalFile();
1101
1102 String relPath = RelativizePath.convertToRelativePath(parentFile, f);
1103 model.getParent().setRelativePath(relPath);
1104 }
1105
1106 try {
1107 PomWriter.write(w, model, true);
1108 } finally {
1109 if (model.getParent() != null) {
1110 model.getParent().setRelativePath(replaceRelativePath);
1111 }
1112 w.close();
1113 }
1114
1115 ProjectBuildingRequest projectBuildingRequest =
1116 new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
1117 projectBuildingRequest.setLocalRepository(session.getLocalRepository());
1118 projectBuildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories());
1119
1120 ProjectBuildingResult result = projectBuilder.build(f, projectBuildingRequest);
1121
1122 getLog().debug("updateExcludesInDeps()");
1123 modified = updateExcludesInDeps(result.getProject(), dependencies, transitiveDeps);
1124 }
1125
1126 project.setFile(dependencyReducedPomLocation);
1127 }
1128 }
1129
1130 private void removeSystemScopedDependencies(Set<String> artifactsToRemove, List<Dependency> originalDependencies) {
1131 for (Dependency dependency : originalDependencies) {
1132 if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) {
1133 artifactsToRemove.add(getId(dependency));
1134 }
1135 }
1136 }
1137
1138 private void addSystemScopedDependencyFromNonInterpolatedPom(
1139 List<Dependency> dependencies, List<Dependency> originalDependencies) {
1140 for (Dependency dependency : originalDependencies) {
1141 if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) {
1142 dependencies.add(dependency);
1143 }
1144 }
1145 }
1146
1147 private Dependency createDependency(Artifact artifact) {
1148 Dependency dep = new Dependency();
1149 dep.setArtifactId(artifact.getArtifactId());
1150 if (artifact.hasClassifier()) {
1151 dep.setClassifier(artifact.getClassifier());
1152 }
1153 dep.setGroupId(artifact.getGroupId());
1154 dep.setOptional(artifact.isOptional());
1155 dep.setScope(artifact.getScope());
1156 dep.setType(artifact.getType());
1157 if (useBaseVersion) {
1158 dep.setVersion(artifact.getBaseVersion());
1159 } else {
1160 dep.setVersion(artifact.getVersion());
1161 }
1162 return dep;
1163 }
1164
1165 private String getId(Artifact artifact) {
1166 return getId(artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier());
1167 }
1168
1169 private String getId(Dependency dependency) {
1170 return getId(
1171 dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(), dependency.getClassifier());
1172 }
1173
1174 private String getId(String groupId, String artifactId, String type, String classifier) {
1175 return groupId + ":" + artifactId + ":" + type + ":" + ((classifier != null) ? classifier : "");
1176 }
1177
1178 public boolean updateExcludesInDeps(
1179 MavenProject project, List<Dependency> dependencies, List<Dependency> transitiveDeps)
1180 throws DependencyGraphBuilderException {
1181 MavenProject original = session.getProjectBuildingRequest().getProject();
1182 try {
1183 session.getProjectBuildingRequest().setProject(project);
1184 DependencyNode node =
1185 dependencyGraphBuilder.buildDependencyGraph(session.getProjectBuildingRequest(), null);
1186 boolean modified = false;
1187 for (DependencyNode n2 : node.getChildren()) {
1188 String artifactId2 = getId(n2.getArtifact());
1189
1190 for (DependencyNode n3 : n2.getChildren()) {
1191 Artifact artifact3 = n3.getArtifact();
1192 String artifactId3 = getId(artifact3);
1193
1194
1195
1196
1197
1198
1199
1200 boolean found = false;
1201 for (Dependency dep : transitiveDeps) {
1202 if (getId(dep).equals(artifactId3)) {
1203 found = true;
1204 break;
1205 }
1206 }
1207
1208
1209
1210
1211
1212 if (!found && !"provided".equals(artifact3.getScope())) {
1213 getLog().debug(String.format(
1214 "dependency %s (scope %s) not found in transitive dependencies",
1215 artifactId3, artifact3.getScope()));
1216 for (Dependency dep : dependencies) {
1217 if (getId(dep).equals(artifactId2)) {
1218
1219
1220
1221
1222 if (!dependencyHasExclusion(dep, artifact3)) {
1223 getLog().debug(String.format(
1224 "Adding exclusion for dependency %s (scope %s) " + "to %s (scope %s)",
1225 artifactId3, artifact3.getScope(), getId(dep), dep.getScope()));
1226 Exclusion exclusion = new Exclusion();
1227 exclusion.setArtifactId(artifact3.getArtifactId());
1228 exclusion.setGroupId(artifact3.getGroupId());
1229 dep.addExclusion(exclusion);
1230 modified = true;
1231 break;
1232 }
1233 }
1234 }
1235 }
1236 }
1237 }
1238 return modified;
1239 } finally {
1240
1241 session.getProjectBuildingRequest().setProject(original);
1242 }
1243 }
1244
1245 private boolean dependencyHasExclusion(Dependency dep, Artifact exclusionToCheck) {
1246 boolean containsExclusion = false;
1247 for (Exclusion existingExclusion : dep.getExclusions()) {
1248 if (existingExclusion.getGroupId().equals(exclusionToCheck.getGroupId())
1249 && existingExclusion.getArtifactId().equals(exclusionToCheck.getArtifactId())) {
1250 containsExclusion = true;
1251 break;
1252 }
1253 }
1254 return containsExclusion;
1255 }
1256
1257 private List<ResourceTransformer> toResourceTransformers(
1258 String shade, List<ResourceTransformer> resourceTransformers) {
1259 List<ResourceTransformer> forShade = new ArrayList<>();
1260 ManifestResourceTransformer lastMt = null;
1261 for (ResourceTransformer transformer : resourceTransformers) {
1262 if (!(transformer instanceof ManifestResourceTransformer)) {
1263 forShade.add(transformer);
1264 } else if (((ManifestResourceTransformer) transformer).isForShade(shade)) {
1265 final ManifestResourceTransformer mt = (ManifestResourceTransformer) transformer;
1266 if (mt.isUsedForDefaultShading() && lastMt != null && !lastMt.isUsedForDefaultShading()) {
1267 continue;
1268 }
1269 if (!mt.isUsedForDefaultShading() && lastMt != null && lastMt.isUsedForDefaultShading()) {
1270 forShade.remove(lastMt);
1271 } else if (!mt.isUsedForDefaultShading() && lastMt != null) {
1272 getLog().warn("Ambiguous manifest transformer definition for '" + shade + "': " + mt + " / "
1273 + lastMt);
1274 }
1275 if (lastMt == null || !mt.isUsedForDefaultShading()) {
1276 lastMt = mt;
1277 }
1278 forShade.add(transformer);
1279 }
1280 }
1281 return forShade;
1282 }
1283 }