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 @Override
451 public void execute() throws MojoExecutionException {
452 if (skip) {
453 getLog().info("Shading has been skipped.");
454 return;
455 }
456
457 setupHintedShader();
458
459 Set<File> artifacts = new LinkedHashSet<>();
460 Set<String> artifactIds = new LinkedHashSet<>();
461 Set<File> sourceArtifacts = new LinkedHashSet<>();
462 Set<File> testArtifacts = new LinkedHashSet<>();
463 Set<File> testSourceArtifacts = new LinkedHashSet<>();
464
465 ArtifactSelector artifactSelector = new ArtifactSelector(project.getArtifact(), artifactSet, shadedGroupFilter);
466
467 if (artifactSelector.isSelected(project.getArtifact())
468 && !"pom".equals(project.getArtifact().getType())) {
469 if (invalidMainArtifact()) {
470 createErrorOutput();
471 throw new MojoExecutionException(
472 "Failed to create shaded artifact, " + "project main artifact does not exist.");
473 }
474
475 artifacts.add(project.getArtifact().getFile());
476
477 if (extraJars != null && !extraJars.isEmpty()) {
478 for (File extraJar : extraJars) {
479 if (!Files.isRegularFile(extraJar.toPath())) {
480 throw new MojoExecutionException(
481 "Failed to create shaded artifact: parameter extraJars contains path " + extraJar
482 + " that is not a file (does not exist or is not a file)");
483 }
484 artifacts.add(extraJar);
485 }
486 }
487
488 if (createSourcesJar) {
489 File file = shadedSourcesArtifactFile();
490 if (file.isFile()) {
491 sourceArtifacts.add(file);
492 }
493 }
494
495 if (shadeTestJar) {
496 File file = shadedTestArtifactFile();
497 if (file.isFile()) {
498 testArtifacts.add(file);
499 }
500 }
501
502 if (createTestSourcesJar) {
503 File file = shadedTestSourcesArtifactFile();
504 if (file.isFile()) {
505 testSourceArtifacts.add(file);
506 }
507 }
508 }
509
510 processArtifactSelectors(
511 artifacts, artifactIds, sourceArtifacts, testArtifacts, testSourceArtifacts, artifactSelector);
512
513 File outputJar = (outputFile != null) ? outputFile : shadedArtifactFileWithClassifier();
514 File sourcesJar = shadedSourceArtifactFileWithClassifier();
515 File testJar = shadedTestArtifactFileWithClassifier();
516 File testSourcesJar = shadedTestSourceArtifactFileWithClassifier();
517
518
519 try {
520 List<Filter> filters = getFilters();
521
522 List<Relocator> relocators = getRelocators();
523
524 List<ResourceTransformer> resourceTransformers = getResourceTransformers();
525
526 if (createDependencyReducedPom) {
527 createDependencyReducedPom(artifactIds);
528
529 if (useDependencyReducedPomInJar) {
530
531 resourceTransformers = new ArrayList<>(resourceTransformers);
532 resourceTransformers.addAll(createPomReplaceTransformers(project, dependencyReducedPomLocation));
533 }
534 }
535
536 ShadeRequest shadeRequest =
537 shadeRequest("jar", artifacts, outputJar, filters, relocators, resourceTransformers);
538
539 shader.shade(shadeRequest);
540
541 if (createSourcesJar) {
542 ShadeRequest shadeSourcesRequest = createShadeSourcesRequest(
543 "sources-jar", sourceArtifacts, sourcesJar, filters, relocators, resourceTransformers);
544
545 shader.shade(shadeSourcesRequest);
546 }
547
548 if (shadeTestJar) {
549 ShadeRequest shadeTestRequest =
550 shadeRequest("test-jar", testArtifacts, testJar, filters, relocators, resourceTransformers);
551
552 shader.shade(shadeTestRequest);
553 }
554
555 if (createTestSourcesJar) {
556 ShadeRequest shadeTestSourcesRequest = createShadeSourcesRequest(
557 "test-sources-jar",
558 testSourceArtifacts,
559 testSourcesJar,
560 filters,
561 relocators,
562 resourceTransformers);
563
564 shader.shade(shadeTestSourcesRequest);
565 }
566
567 if (outputFile == null) {
568 boolean renamed = false;
569
570
571
572
573 if (finalName != null
574 && finalName.length() > 0
575 && !finalName.equals(project.getBuild().getFinalName())) {
576 String finalFileName = finalName + "."
577 + project.getArtifact().getArtifactHandler().getExtension();
578 File finalFile = new File(outputDirectory, finalFileName);
579 replaceFile(finalFile, outputJar);
580 outputJar = finalFile;
581
582
583 if (createSourcesJar) {
584 finalFileName = finalName + "-sources.jar";
585 finalFile = new File(outputDirectory, finalFileName);
586 replaceFile(finalFile, sourcesJar);
587 sourcesJar = finalFile;
588 }
589
590
591 if (shadeTestJar) {
592 finalFileName = finalName + "-tests.jar";
593 finalFile = new File(outputDirectory, finalFileName);
594 replaceFile(finalFile, testJar);
595 testJar = finalFile;
596 }
597
598 if (createTestSourcesJar) {
599 finalFileName = finalName + "-test-sources.jar";
600 finalFile = new File(outputDirectory, finalFileName);
601 replaceFile(finalFile, testSourcesJar);
602 testSourcesJar = finalFile;
603 }
604
605 renamed = true;
606 }
607
608 if (shadedArtifactAttached) {
609 getLog().info("Attaching shaded artifact.");
610 projectHelper.attachArtifact(
611 project, project.getArtifact().getType(), shadedClassifierName, outputJar);
612 if (createSourcesJar) {
613 projectHelper.attachArtifact(
614 project, "java-source", shadedClassifierName + "-sources", sourcesJar);
615 }
616
617 if (shadeTestJar) {
618 projectHelper.attachArtifact(project, "test-jar", shadedClassifierName + "-tests", testJar);
619 }
620
621 if (createTestSourcesJar) {
622 projectHelper.attachArtifact(
623 project, "java-source", shadedClassifierName + "-test-sources", testSourcesJar);
624 }
625 } else if (!renamed) {
626 getLog().info("Replacing original artifact with shaded artifact.");
627 File originalArtifact = project.getArtifact().getFile();
628 if (originalArtifact != null) {
629 replaceFile(originalArtifact, outputJar);
630
631 if (createSourcesJar) {
632 getLog().info("Replacing original source artifact with shaded source artifact.");
633 File shadedSources = shadedSourcesArtifactFile();
634
635 replaceFile(shadedSources, sourcesJar);
636
637 projectHelper.attachArtifact(project, "java-source", "sources", shadedSources);
638 }
639
640 if (shadeTestJar) {
641 getLog().info("Replacing original test artifact with shaded test artifact.");
642 File shadedTests = shadedTestArtifactFile();
643
644 replaceFile(shadedTests, testJar);
645
646 projectHelper.attachArtifact(project, "test-jar", shadedTests);
647 }
648
649 if (createTestSourcesJar) {
650 getLog().info("Replacing original test source artifact "
651 + "with shaded test source artifact.");
652 File shadedTestSources = shadedTestSourcesArtifactFile();
653
654 replaceFile(shadedTestSources, testSourcesJar);
655
656 projectHelper.attachArtifact(project, "java-source", "test-sources", shadedTestSources);
657 }
658 }
659 }
660 }
661 } catch (Exception e) {
662 throw new MojoExecutionException("Error creating shaded jar: " + e.getMessage(), e);
663 }
664 }
665
666 private void createErrorOutput() {
667 getLog().error("The project main artifact does not exist. This could have the following");
668 getLog().error("reasons:");
669 getLog().error("- You have invoked the goal directly from the command line. This is not");
670 getLog().error(" supported. Please add the goal to the default lifecycle via an");
671 getLog().error(" <execution> element in your POM and use \"mvn package\" to have it run.");
672 getLog().error("- You have bound the goal to a lifecycle phase before \"package\". Please");
673 getLog().error(" remove this binding from your POM such that the goal will be run in");
674 getLog().error(" the proper phase.");
675 getLog().error("- You removed the configuration of the maven-jar-plugin that produces the main artifact.");
676 }
677
678 private ShadeRequest shadeRequest(
679 String shade,
680 Set<File> artifacts,
681 File outputJar,
682 List<Filter> filters,
683 List<Relocator> relocators,
684 List<ResourceTransformer> resourceTransformers) {
685 ShadeRequest shadeRequest = new ShadeRequest();
686 shadeRequest.setJars(artifacts);
687 shadeRequest.setUberJar(outputJar);
688 shadeRequest.setFilters(filters);
689 shadeRequest.setRelocators(relocators);
690 shadeRequest.setResourceTransformers(toResourceTransformers(shade, resourceTransformers));
691 return shadeRequest;
692 }
693
694 private ShadeRequest createShadeSourcesRequest(
695 String shade,
696 Set<File> testArtifacts,
697 File testJar,
698 List<Filter> filters,
699 List<Relocator> relocators,
700 List<ResourceTransformer> resourceTransformers) {
701 ShadeRequest shadeSourcesRequest =
702 shadeRequest(shade, testArtifacts, testJar, filters, relocators, resourceTransformers);
703 shadeSourcesRequest.setShadeSourcesContent(shadeSourcesContent);
704 return shadeSourcesRequest;
705 }
706
707 private void setupHintedShader() throws MojoExecutionException {
708 if (shaderHint != null) {
709 shader = shaders.get(shaderHint);
710
711 if (shader == null) {
712 throw new MojoExecutionException(
713 "unable to lookup own Shader implementation with hint: '" + shaderHint + "'");
714 }
715 }
716 }
717
718 private void processArtifactSelectors(
719 Set<File> artifacts,
720 Set<String> artifactIds,
721 Set<File> sourceArtifacts,
722 Set<File> testArtifacts,
723 Set<File> testSourceArtifacts,
724 ArtifactSelector artifactSelector)
725 throws MojoExecutionException {
726
727 List<String> excludedArtifacts = new ArrayList<>();
728 List<String> pomArtifacts = new ArrayList<>();
729 List<String> emptySourceArtifacts = new ArrayList<>();
730 List<String> emptyTestArtifacts = new ArrayList<>();
731 List<String> emptyTestSourceArtifacts = new ArrayList<>();
732
733 ArrayList<Artifact> processedArtifacts = new ArrayList<>();
734 if (extraArtifacts != null && !extraArtifacts.isEmpty()) {
735 processedArtifacts.addAll(extraArtifacts.stream()
736 .map(org.eclipse.aether.artifact.DefaultArtifact::new)
737 .map(RepositoryUtils::toArtifact)
738 .collect(Collectors.toList()));
739
740 for (Artifact artifact : processedArtifacts) {
741 try {
742 org.eclipse.aether.artifact.Artifact resolved =
743 resolveArtifact(RepositoryUtils.toArtifact(artifact));
744 if (resolved.getFile() != null) {
745 artifact.setFile(resolved.getFile());
746 }
747 } catch (ArtifactResolutionException e) {
748 throw new MojoExecutionException(
749 "Failed to create shaded artifact: parameter extraArtifacts contains artifact "
750 + artifact.getId() + " that is not resolvable",
751 e);
752 }
753 }
754 }
755 processedArtifacts.addAll(project.getArtifacts());
756
757 for (Artifact artifact : processedArtifacts) {
758 if (!artifactSelector.isSelected(artifact)) {
759 excludedArtifacts.add(artifact.getId());
760
761 continue;
762 }
763
764 if ("pom".equals(artifact.getType())) {
765 pomArtifacts.add(artifact.getId());
766 continue;
767 }
768
769 getLog().info("Including " + artifact.getId() + " in the shaded jar.");
770
771 artifacts.add(artifact.getFile());
772 artifactIds.add(getId(artifact));
773
774 if (createSourcesJar) {
775 File file = resolveArtifactForClassifier(artifact, "sources");
776 if (file != null) {
777 if (file.length() > 0) {
778 sourceArtifacts.add(file);
779 } else {
780 emptySourceArtifacts.add(artifact.getArtifactId());
781 }
782 }
783 }
784
785 if (shadeTestJar) {
786 File file = resolveArtifactForClassifier(artifact, "tests");
787 if (file != null) {
788 if (file.length() > 0) {
789 testArtifacts.add(file);
790 } else {
791 emptyTestArtifacts.add(artifact.getId());
792 }
793 }
794 }
795
796 if (createTestSourcesJar) {
797 File file = resolveArtifactForClassifier(artifact, "test-sources");
798 if (file != null) {
799 testSourceArtifacts.add(file);
800 } else {
801 emptyTestSourceArtifacts.add(artifact.getId());
802 }
803 }
804 }
805
806 for (String artifactId : excludedArtifacts) {
807 getLog().info("Excluding " + artifactId + " from the shaded jar.");
808 }
809 for (String artifactId : pomArtifacts) {
810 getLog().info("Skipping pom dependency " + artifactId + " in the shaded jar.");
811 }
812 for (String artifactId : emptySourceArtifacts) {
813 getLog().warn("Skipping empty source jar " + artifactId + ".");
814 }
815 for (String artifactId : emptyTestArtifacts) {
816 getLog().warn("Skipping empty test jar " + artifactId + ".");
817 }
818 for (String artifactId : emptyTestSourceArtifacts) {
819 getLog().warn("Skipping empty test source jar " + artifactId + ".");
820 }
821 }
822
823 private boolean invalidMainArtifact() {
824 return project.getArtifact().getFile() == null
825 || !project.getArtifact().getFile().isFile();
826 }
827
828 private void replaceFile(File oldFile, File newFile) throws MojoExecutionException {
829 getLog().info("Replacing " + oldFile + " with " + newFile);
830
831 File origFile = new File(outputDirectory, "original-" + oldFile.getName());
832 if (oldFile.exists() && !oldFile.renameTo(origFile)) {
833
834 System.gc();
835 System.gc();
836
837 if (!oldFile.renameTo(origFile)) {
838
839 try {
840 copyFiles(oldFile, origFile);
841 } catch (IOException ex) {
842
843 getLog().warn(ex);
844 }
845 }
846 }
847 if (!newFile.renameTo(oldFile)) {
848
849 System.gc();
850 System.gc();
851
852 if (!newFile.renameTo(oldFile)) {
853
854 try {
855 copyFiles(newFile, oldFile);
856 } catch (IOException ex) {
857 throw new MojoExecutionException("Could not replace original artifact with shaded artifact!", ex);
858 }
859 }
860 }
861 }
862
863 private void copyFiles(File source, File target) throws IOException {
864 try (InputStream in = Files.newInputStream(source.toPath());
865 OutputStream out = Files.newOutputStream(target.toPath())) {
866 IOUtil.copy(in, out);
867 }
868 }
869
870 private File resolveArtifactForClassifier(Artifact artifact, String classifier) {
871 Artifact toResolve = new DefaultArtifact(
872 artifact.getGroupId(),
873 artifact.getArtifactId(),
874 artifact.getVersionRange() == null
875 ? VersionRange.createFromVersion(artifact.getVersion())
876 : artifact.getVersionRange(),
877 artifact.getScope(),
878 artifact.getType(),
879 classifier,
880 artifact.getArtifactHandler(),
881 artifact.isOptional());
882 try {
883 org.eclipse.aether.artifact.Artifact resolved = resolveArtifact(RepositoryUtils.toArtifact(toResolve));
884 if (resolved.getFile() != null) {
885 return resolved.getFile();
886 }
887 return null;
888 } catch (ArtifactResolutionException e) {
889 getLog().warn("Could not get " + classifier + " for " + artifact);
890 return null;
891 }
892 }
893
894 private org.eclipse.aether.artifact.Artifact resolveArtifact(org.eclipse.aether.artifact.Artifact artifact)
895 throws ArtifactResolutionException {
896 return repositorySystem
897 .resolveArtifact(
898 session.getRepositorySession(),
899 new ArtifactRequest(artifact, project.getRemoteProjectRepositories(), "shade"))
900 .getArtifact();
901 }
902
903 private List<Relocator> getRelocators() {
904 List<Relocator> relocators = new ArrayList<>();
905
906 if (relocations == null) {
907 return relocators;
908 }
909
910 for (PackageRelocation r : relocations) {
911 relocators.add(new SimpleRelocator(
912 r.getPattern(), r.getShadedPattern(), r.getIncludes(), r.getExcludes(), r.isRawString()));
913 }
914
915 return relocators;
916 }
917
918 private List<ResourceTransformer> getResourceTransformers() throws MojoExecutionException {
919 if (transformers == null) {
920 return Collections.emptyList();
921 }
922 for (ResourceTransformer transformer : transformers) {
923 if (transformer == null) {
924 throw new MojoExecutionException(
925 "Failed to create shaded artifact: parameter transformers contains null (double-check XML attribute)");
926 }
927 }
928 return Arrays.asList(transformers);
929 }
930
931 private List<Filter> getFilters() throws MojoExecutionException {
932 List<Filter> filters = new ArrayList<>();
933 List<SimpleFilter> simpleFilters = new ArrayList<>();
934
935 if (this.filters != null && this.filters.length > 0) {
936 Map<Artifact, ArtifactId> artifacts = new HashMap<>();
937
938 artifacts.put(project.getArtifact(), new ArtifactId(project.getArtifact()));
939
940 for (Artifact artifact : project.getArtifacts()) {
941 artifacts.put(artifact, new ArtifactId(artifact));
942 }
943
944 for (ArchiveFilter filter : this.filters) {
945 ArtifactId pattern = new ArtifactId(filter.getArtifact());
946
947 Set<File> jars = new HashSet<>();
948
949 for (Map.Entry<Artifact, ArtifactId> entry : artifacts.entrySet()) {
950 if (entry.getValue().matches(pattern)) {
951 Artifact artifact = entry.getKey();
952
953 jars.add(artifact.getFile());
954
955 if (createSourcesJar) {
956 File file = resolveArtifactForClassifier(artifact, "sources");
957 if (file != null) {
958 jars.add(file);
959 }
960 }
961
962 if (shadeTestJar) {
963 File file = resolveArtifactForClassifier(artifact, "tests");
964 if (file != null) {
965 jars.add(file);
966 }
967 }
968 }
969 }
970
971 if (jars.isEmpty()) {
972 getLog().info("No artifact matching filter " + filter.getArtifact());
973
974 continue;
975 }
976
977 simpleFilters.add(new SimpleFilter(jars, filter));
978 }
979 }
980
981 filters.addAll(simpleFilters);
982
983 if (minimizeJar) {
984 if (entryPoints == null) {
985 entryPoints = new HashSet<>();
986 }
987 getLog().info("Minimizing jar " + project.getArtifact()
988 + (entryPoints.isEmpty() ? "" : " with entry points"));
989
990 try {
991 filters.add(new MinijarFilter(project, getLog(), simpleFilters, entryPoints));
992 } catch (IOException e) {
993 throw new MojoExecutionException("Failed to analyze class dependencies", e);
994 }
995 }
996
997 return filters;
998 }
999
1000 private File shadedArtifactFileWithClassifier() {
1001 Artifact artifact = project.getArtifact();
1002 final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "."
1003 + artifact.getArtifactHandler().getExtension();
1004 return new File(outputDirectory, shadedName);
1005 }
1006
1007 private File shadedSourceArtifactFileWithClassifier() {
1008 return shadedArtifactFileWithClassifier("sources");
1009 }
1010
1011 private File shadedTestSourceArtifactFileWithClassifier() {
1012 return shadedArtifactFileWithClassifier("test-sources");
1013 }
1014
1015 private File shadedArtifactFileWithClassifier(String classifier) {
1016 Artifact artifact = project.getArtifact();
1017 final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "-"
1018 + classifier + "." + artifact.getArtifactHandler().getExtension();
1019 return new File(outputDirectory, shadedName);
1020 }
1021
1022 private File shadedTestArtifactFileWithClassifier() {
1023 return shadedArtifactFileWithClassifier("tests");
1024 }
1025
1026 private File shadedSourcesArtifactFile() {
1027 return shadedArtifactFile("sources");
1028 }
1029
1030 private File shadedTestSourcesArtifactFile() {
1031 return shadedArtifactFile("test-sources");
1032 }
1033
1034 private File shadedArtifactFile(String classifier) {
1035 Artifact artifact = project.getArtifact();
1036
1037 String shadedName;
1038
1039 if (project.getBuild().getFinalName() != null) {
1040 shadedName = project.getBuild().getFinalName() + "-" + classifier + "."
1041 + artifact.getArtifactHandler().getExtension();
1042 } else {
1043 shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + classifier + "."
1044 + artifact.getArtifactHandler().getExtension();
1045 }
1046
1047 return new File(outputDirectory, shadedName);
1048 }
1049
1050 private File shadedTestArtifactFile() {
1051 return shadedArtifactFile("tests");
1052 }
1053
1054
1055
1056 private void createDependencyReducedPom(Set<String> artifactsToRemove)
1057 throws IOException, ProjectBuildingException, DependencyCollectionException {
1058 List<Dependency> transitiveDeps = new ArrayList<>();
1059
1060
1061
1062 for (Artifact artifact : project.getArtifacts()) {
1063 if ("pom".equals(artifact.getType())) {
1064
1065 continue;
1066 }
1067
1068
1069 Dependency dep = createDependency(artifact);
1070
1071
1072 transitiveDeps.add(dep);
1073 }
1074
1075 Model model = project.getOriginalModel();
1076
1077
1078
1079
1080 List<Dependency> origDeps = new ArrayList<>();
1081 List<Dependency> source = promoteTransitiveDependencies ? transitiveDeps : project.getDependencies();
1082 for (Dependency d : source) {
1083 origDeps.add(d.clone());
1084 }
1085 model = model.clone();
1086
1087
1088
1089
1090 List<Dependency> originalDependencies = model.getDependencies();
1091 removeSystemScopedDependencies(artifactsToRemove, originalDependencies);
1092
1093 List<Dependency> dependencies = new ArrayList<>();
1094 boolean modified = false;
1095 for (Dependency d : origDeps) {
1096 if (artifactsToRemove.contains(getId(d))) {
1097 if (keepDependenciesWithProvidedScope) {
1098 if (!"provided".equals(d.getScope())) {
1099 modified = true;
1100 d.setScope("provided");
1101 }
1102 } else {
1103 modified = true;
1104 continue;
1105 }
1106 }
1107
1108 dependencies.add(d);
1109 }
1110
1111
1112 model.setArtifactId(shadedArtifactId);
1113
1114
1115
1116
1117 addSystemScopedDependencyFromNonInterpolatedPom(dependencies, originalDependencies);
1118
1119
1120 rewriteDependencyReducedPomIfWeHaveReduction(dependencies, modified, transitiveDeps, model);
1121 }
1122
1123 private void rewriteDependencyReducedPomIfWeHaveReduction(
1124 List<Dependency> dependencies, boolean modified, List<Dependency> transitiveDeps, Model model)
1125 throws IOException, ProjectBuildingException, DependencyCollectionException {
1126 if (modified) {
1127 for (int loopCounter = 0; modified; loopCounter++) {
1128
1129 model.setDependencies(dependencies);
1130
1131 if (generateUniqueDependencyReducedPom) {
1132 dependencyReducedPomLocation = Files.createTempFile(
1133 project.getBasedir().toPath(), "dependency-reduced-pom-", ".xml")
1134 .toFile();
1135 project.getProperties()
1136 .setProperty(
1137 "maven.shade.dependency-reduced-pom",
1138 dependencyReducedPomLocation.getAbsolutePath());
1139 } else {
1140 if (dependencyReducedPomLocation == null) {
1141
1142 dependencyReducedPomLocation = new File(project.getBasedir(), "dependency-reduced-pom.xml");
1143 }
1144 }
1145
1146 File f = dependencyReducedPomLocation;
1147
1148
1149 if (loopCounter == 0) {
1150 getLog().info("Dependency-reduced POM written at: " + f.getAbsolutePath());
1151 }
1152
1153 if (f.exists()) {
1154
1155 f.delete();
1156 }
1157
1158 Writer w = WriterFactory.newXmlWriter(f);
1159
1160 String replaceRelativePath = null;
1161 if (model.getParent() != null) {
1162 replaceRelativePath = model.getParent().getRelativePath();
1163 }
1164
1165 if (model.getParent() != null) {
1166 File parentFile =
1167 new File(project.getBasedir(), model.getParent().getRelativePath()).getCanonicalFile();
1168 if (!parentFile.isFile()) {
1169 parentFile = new File(parentFile, "pom.xml");
1170 }
1171
1172 parentFile = parentFile.getCanonicalFile();
1173
1174 String relPath = RelativizePath.convertToRelativePath(parentFile, f);
1175 model.getParent().setRelativePath(relPath);
1176 }
1177
1178 try {
1179 PomWriter.write(w, model, true);
1180 } finally {
1181 if (model.getParent() != null) {
1182 model.getParent().setRelativePath(replaceRelativePath);
1183 }
1184 w.close();
1185 }
1186
1187 synchronized (session.getProjectBuildingRequest()) {
1188 ProjectBuildingRequest projectBuildingRequest =
1189 new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
1190 projectBuildingRequest.setLocalRepository(session.getLocalRepository());
1191 projectBuildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories());
1192
1193 ProjectBuildingResult result = projectBuilder.build(f, projectBuildingRequest);
1194
1195 getLog().debug("updateExcludesInDeps()");
1196 modified = updateExcludesInDeps(result.getProject(), dependencies, transitiveDeps);
1197 }
1198 }
1199
1200 project.setFile(dependencyReducedPomLocation);
1201 }
1202 }
1203
1204 private void removeSystemScopedDependencies(Set<String> artifactsToRemove, List<Dependency> originalDependencies) {
1205 for (Dependency dependency : originalDependencies) {
1206 if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) {
1207 artifactsToRemove.add(getId(dependency));
1208 }
1209 }
1210 }
1211
1212 private void addSystemScopedDependencyFromNonInterpolatedPom(
1213 List<Dependency> dependencies, List<Dependency> originalDependencies) {
1214 for (Dependency dependency : originalDependencies) {
1215 if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) {
1216 dependencies.add(dependency);
1217 }
1218 }
1219 }
1220
1221 private Dependency createDependency(Artifact artifact) {
1222 Dependency dep = new Dependency();
1223 dep.setArtifactId(artifact.getArtifactId());
1224 if (artifact.hasClassifier()) {
1225 dep.setClassifier(artifact.getClassifier());
1226 }
1227 dep.setGroupId(artifact.getGroupId());
1228 dep.setOptional(artifact.isOptional());
1229 dep.setScope(artifact.getScope());
1230 dep.setType(artifact.getType());
1231 if (useBaseVersion) {
1232 dep.setVersion(artifact.getBaseVersion());
1233 } else {
1234 dep.setVersion(artifact.getVersion());
1235 }
1236 return dep;
1237 }
1238
1239 private String getId(Artifact artifact) {
1240 return getId(artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier());
1241 }
1242
1243 private String getId(Dependency dependency) {
1244 return getId(
1245 dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(), dependency.getClassifier());
1246 }
1247
1248 private String getId(String groupId, String artifactId, String type, String classifier) {
1249 return groupId + ":" + artifactId + ":" + type + ":" + ((classifier != null) ? classifier : "");
1250 }
1251
1252 public boolean updateExcludesInDeps(
1253 MavenProject project, List<Dependency> dependencies, List<Dependency> transitiveDeps)
1254 throws DependencyCollectionException {
1255 CollectRequest collectRequest = new CollectRequest();
1256 collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
1257 collectRequest.setRepositories(project.getRemoteProjectRepositories());
1258 collectRequest.setDependencies(project.getDependencies().stream()
1259 .map(d -> RepositoryUtils.toDependency(
1260 d, session.getRepositorySession().getArtifactTypeRegistry()))
1261 .collect(Collectors.toList()));
1262 if (project.getDependencyManagement() != null) {
1263 collectRequest.setManagedDependencies(project.getDependencyManagement().getDependencies().stream()
1264 .map(d -> RepositoryUtils.toDependency(
1265 d, session.getRepositorySession().getArtifactTypeRegistry()))
1266 .collect(Collectors.toList()));
1267 }
1268 CollectResult result = repositorySystem.collectDependencies(session.getRepositorySession(), collectRequest);
1269 boolean modified = false;
1270 if (result.getRoot() != null) {
1271 for (DependencyNode n2 : result.getRoot().getChildren()) {
1272 String artifactId2 = getId(RepositoryUtils.toArtifact(n2.getArtifact()));
1273
1274 for (DependencyNode n3 : n2.getChildren()) {
1275
1276 Artifact artifact3 = RepositoryUtils.toArtifact(n3.getArtifact());
1277 artifact3.setScope(n3.getDependency().getScope());
1278 String artifactId3 = getId(artifact3);
1279
1280
1281
1282
1283
1284
1285
1286 boolean found = false;
1287 for (Dependency dep : transitiveDeps) {
1288 if (getId(dep).equals(artifactId3)) {
1289 found = true;
1290 break;
1291 }
1292 }
1293
1294
1295
1296
1297
1298 if (!found && !"provided".equals(artifact3.getScope())) {
1299 getLog().debug(String.format(
1300 "dependency %s (scope %s) not found in transitive dependencies",
1301 artifactId3, artifact3.getScope()));
1302 for (Dependency dep : dependencies) {
1303 if (getId(dep).equals(artifactId2)) {
1304
1305
1306
1307
1308 if (!dependencyHasExclusion(dep, artifact3)) {
1309 getLog().debug(String.format(
1310 "Adding exclusion for dependency %s (scope %s) " + "to %s (scope %s)",
1311 artifactId3, artifact3.getScope(), getId(dep), dep.getScope()));
1312 Exclusion exclusion = new Exclusion();
1313 exclusion.setArtifactId(artifact3.getArtifactId());
1314 exclusion.setGroupId(artifact3.getGroupId());
1315 dep.addExclusion(exclusion);
1316 modified = true;
1317 break;
1318 }
1319 }
1320 }
1321 }
1322 }
1323 }
1324 }
1325 return modified;
1326 }
1327
1328 private boolean dependencyHasExclusion(Dependency dep, Artifact exclusionToCheck) {
1329 boolean containsExclusion = false;
1330 for (Exclusion existingExclusion : dep.getExclusions()) {
1331 if (existingExclusion.getGroupId().equals(exclusionToCheck.getGroupId())
1332 && existingExclusion.getArtifactId().equals(exclusionToCheck.getArtifactId())) {
1333 containsExclusion = true;
1334 break;
1335 }
1336 }
1337 return containsExclusion;
1338 }
1339
1340 private List<ResourceTransformer> toResourceTransformers(
1341 String shade, List<ResourceTransformer> resourceTransformers) {
1342 List<ResourceTransformer> forShade = new ArrayList<>();
1343 ManifestResourceTransformer lastMt = null;
1344 for (ResourceTransformer transformer : resourceTransformers) {
1345 if (!(transformer instanceof ManifestResourceTransformer)) {
1346 forShade.add(transformer);
1347 } else if (((ManifestResourceTransformer) transformer).isForShade(shade)) {
1348 final ManifestResourceTransformer mt = (ManifestResourceTransformer) transformer;
1349 if (mt.isUsedForDefaultShading() && lastMt != null && !lastMt.isUsedForDefaultShading()) {
1350 continue;
1351 }
1352 if (!mt.isUsedForDefaultShading() && lastMt != null && lastMt.isUsedForDefaultShading()) {
1353 forShade.remove(lastMt);
1354 } else if (!mt.isUsedForDefaultShading() && lastMt != null) {
1355 getLog().warn("Ambiguous manifest transformer definition for '" + shade + "': " + mt + " / "
1356 + lastMt);
1357 }
1358 if (lastMt == null || !mt.isUsedForDefaultShading()) {
1359 lastMt = mt;
1360 }
1361 forShade.add(transformer);
1362 }
1363 }
1364 return forShade;
1365 }
1366 }