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