1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package ru.akman.maven.plugins.jlink;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.nio.file.FileSystems;
22 import java.nio.file.Files;
23 import java.nio.file.Path;
24 import java.nio.file.PathMatcher;
25 import java.text.MessageFormat;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Objects;
32 import java.util.Set;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35 import java.util.stream.Collectors;
36 import java.util.stream.Stream;
37 import org.apache.commons.lang3.JavaVersion;
38 import org.apache.commons.lang3.StringUtils;
39 import org.apache.commons.text.StringSubstitutor;
40 import org.apache.maven.artifact.Artifact;
41 import org.apache.maven.plugin.MojoExecutionException;
42 import org.apache.maven.plugins.annotations.Component;
43
44
45 import org.apache.maven.plugins.annotations.Mojo;
46 import org.apache.maven.plugins.annotations.Parameter;
47 import org.apache.maven.plugins.annotations.ResolutionScope;
48 import org.apache.maven.shared.model.fileset.FileSet;
49 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
50 import org.codehaus.plexus.languages.java.jpms.LocationManager;
51 import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
52 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
53 import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
54 import org.codehaus.plexus.util.FileUtils;
55 import org.codehaus.plexus.util.cli.CommandLineException;
56 import org.codehaus.plexus.util.cli.Commandline;
57 import ru.akman.maven.plugins.BaseToolMojo;
58 import ru.akman.maven.plugins.CommandLineBuilder;
59 import ru.akman.maven.plugins.CommandLineOption;
60
61
62
63
64
65
66
67
68
69
70
71
72 @Mojo(
73 name = "jlink",
74 requiresDependencyResolution = ResolutionScope.RUNTIME
75
76
77
78
79
80
81
82
83
84
85
86 )
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 public class JlinkMojo extends BaseToolMojo {
103
104
105
106
107 private static final String TOOL_HOME_BIN = "bin";
108
109
110
111
112 private static final String TOOL_NAME = "jlink";
113
114
115
116
117 private static final String OPTS_FILE = TOOL_NAME + ".opts";
118
119
120
121
122 private static final String ERROR_RESOLVE =
123 "Error: Unable to resolve file path for {0} [{1}]";
124
125
126
127
128 private static final String DESCRIPTOR_NAME = "module-info.class";
129
130
131
132
133 private JavaVersion toolJavaVersion;
134
135
136
137
138 private ResolvePathsResult<File> projectDependencies;
139
140
141
142
143 private JavaModuleDescriptor mainModuleDescriptor;
144
145
146
147
148 @Component
149 private LocationManager locationManager;
150
151
152
153
154 @Parameter
155 private File toolhome;
156
157
158
159
160 @Parameter(
161 defaultValue = "${project.build.directory}/jlink/mods"
162 )
163 private File modsdir;
164
165
166
167
168 @Parameter(
169 defaultValue = "${project.build.directory}/jlink/libs"
170 )
171 private File libsdir;
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 @Parameter
247 private ModulePath modulepath;
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 @Parameter
263 private List<String> addmodules;
264
265
266
267
268
269
270 @Parameter(
271 defaultValue = "${project.build.directory}/jlink/image"
272 )
273 private File output;
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 @Parameter
294 private List<String> limitmodules;
295
296
297
298
299
300
301
302
303
304
305
306
307
308 @Parameter
309 private List<String> suggestproviders;
310
311
312
313
314
315
316 @Parameter
317 private File saveopts;
318
319
320
321
322
323
324 @Parameter
325 private String resourceslastsorter;
326
327
328
329
330
331
332 @Parameter
333 private File postprocesspath;
334
335
336
337
338
339
340 @Parameter(
341 defaultValue = "false"
342 )
343 private boolean verbose;
344
345
346
347
348
349
350 @Parameter(
351 defaultValue = "false"
352 )
353 private boolean bindservices;
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369 @Parameter
370 private Launcher launcher;
371
372
373
374
375
376
377 @Parameter(
378 defaultValue = "false"
379 )
380 private boolean noheaderfiles;
381
382
383
384
385
386
387 @Parameter(
388 defaultValue = "false"
389 )
390 private boolean nomanpages;
391
392
393
394
395
396
397 @Parameter(
398 defaultValue = "NATIVE"
399 )
400 private Endian endian;
401
402
403
404
405
406
407
408
409 @Parameter(
410 defaultValue = "false"
411 )
412 private boolean ignoresigninginformation;
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428 @Parameter
429 private List<String> disableplugins;
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465 @Parameter
466 private Compress compress;
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485 @Parameter
486 private List<String> includelocales;
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502 @Parameter
503 private List<String> orderresources;
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519 @Parameter
520 private List<String> excluderesources;
521
522
523
524
525
526
527 @Parameter(
528 defaultValue = "false"
529 )
530 private boolean stripdebug;
531
532
533
534
535
536
537 @Parameter(
538 defaultValue = "false"
539 )
540 private boolean stripjavadebugattributes;
541
542
543
544
545
546
547 @Parameter(
548 defaultValue = "false"
549 )
550 private boolean stripnativecommands;
551
552
553
554
555
556
557
558
559
560 @Parameter(
561 defaultValue = "false"
562 )
563 private boolean deduplegalnotices;
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579 @Parameter
580 private List<String> excludefiles;
581
582
583
584
585
586
587 @Parameter
588 private Section excludejmodsection;
589
590
591
592
593
594
595
596
597
598
599
600 @Parameter
601 private File generatejliclasses;
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627 @Parameter
628 private ReleaseInfo releaseinfo;
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650 @Parameter
651 private HotSpot vm;
652
653
654
655
656
657
658
659
660
661 private ResolvePathsResult<File> resolveDependencies()
662 throws MojoExecutionException {
663
664
665
666 final Set<Artifact> artifacts = getProject().getArtifacts();
667 if (getLog().isDebugEnabled()) {
668 getLog().debug(PluginUtils.getArtifactSetDebugInfo(artifacts));
669 }
670
671
672 final List<File> paths = new ArrayList<>();
673
674
675 paths.add(getOutputDir());
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702 paths.addAll(artifacts.stream()
703 .filter(a -> a != null && !Artifact.SCOPE_TEST.equals(a.getScope()))
704 .map(a -> a.getFile())
705 .collect(Collectors.toList()));
706
707
708
709 paths.addAll(getProject().getDependencies().stream()
710 .filter(d -> d != null && !StringUtils.isBlank(d.getSystemPath()))
711 .map(d -> new File(StringUtils.stripToEmpty(d.getSystemPath())))
712 .collect(Collectors.toList()));
713
714
715
716 final ResolvePathsRequest<File> request =
717 ResolvePathsRequest.ofFiles(paths);
718
719
720 final File descriptorFile =
721 getOutputDir().toPath().resolve(DESCRIPTOR_NAME).toFile();
722 if (descriptorFile.exists() && !descriptorFile.isDirectory()) {
723 request.setMainModuleDescriptor(descriptorFile);
724 }
725
726
727 if (getToolHomeDirectory() != null) {
728 request.setJdkHome(getToolHomeDirectory());
729 }
730
731
732 try {
733 return locationManager.resolvePaths(request);
734 } catch (IOException ex) {
735 throw new MojoExecutionException(
736 "Error: Unable to resolve project dependencies", ex);
737 }
738
739 }
740
741
742
743
744
745
746 private JavaModuleDescriptor fetchMainModuleDescriptor() {
747 final JavaModuleDescriptor descriptor =
748 projectDependencies.getMainModuleDescriptor();
749 if (descriptor == null) {
750
751 if (getLog().isWarnEnabled()) {
752 getLog().warn("The main module descriptor not found");
753 }
754 } else {
755 if (getLog().isDebugEnabled()) {
756 getLog().debug(MessageFormat.format(
757 "Found the main module descriptor: [{0}]", descriptor.name()));
758 }
759 }
760 return descriptor;
761 }
762
763
764
765
766
767
768 private Map<File, String> fetchPathExceptions() {
769 return projectDependencies.getPathExceptions()
770 .entrySet().stream()
771 .filter(entry -> entry != null && entry.getKey() != null)
772 .collect(Collectors.toMap(
773 entry -> entry.getKey(),
774 entry -> PluginUtils.getThrowableCause(entry.getValue())
775 ));
776 }
777
778
779
780
781
782
783 private List<File> fetchClasspathElements() {
784 final List<File> result = projectDependencies.getClasspathElements()
785 .stream()
786 .filter(Objects::nonNull)
787 .collect(Collectors.toList());
788 if (getLog().isDebugEnabled()) {
789 getLog().debug("Found classpath elements: " + result.size()
790 + System.lineSeparator()
791 + result.stream()
792 .map(file -> file.toString())
793 .collect(Collectors.joining(System.lineSeparator())));
794 }
795 return result;
796 }
797
798
799
800
801
802
803 private List<File> fetchModulepathElements() {
804 final List<File> result = projectDependencies.getModulepathElements()
805 .keySet()
806 .stream()
807 .filter(Objects::nonNull)
808 .collect(Collectors.toList());
809 if (getLog().isDebugEnabled()) {
810 getLog().debug("Found modulepath elements: " + result.size()
811 + System.lineSeparator()
812 + projectDependencies.getModulepathElements().entrySet().stream()
813 .filter(entry -> entry != null && entry.getKey() != null)
814 .map(entry -> entry.getKey().toString()
815 + (ModuleNameSource.FILENAME.equals(entry.getValue())
816 ? System.lineSeparator()
817 + "[!] Detected 'requires' filename based "
818 + "automatic module"
819 + System.lineSeparator()
820 + "[!] Please don't publish this project to "
821 + "a public artifact repository"
822 + System.lineSeparator()
823 + (mainModuleDescriptor != null
824 && mainModuleDescriptor.exports().isEmpty()
825 ? "[!] APPLICATION"
826 : "[!] LIBRARY")
827 : ""))
828 .collect(Collectors.joining(System.lineSeparator())));
829 }
830 return result;
831 }
832
833
834
835
836
837
838 private String getPathElements() {
839 String result = null;
840 if (modulepath != null) {
841 final List<File> pathelements = modulepath.getPathElements();
842 if (pathelements != null && !pathelements.isEmpty()) {
843 result = pathelements.stream()
844 .filter(Objects::nonNull)
845 .map(file -> file.toString())
846 .collect(Collectors.joining(File.pathSeparator));
847 if (getLog().isDebugEnabled()) {
848 getLog().debug(PluginUtils.getPathElementsDebugInfo("PATHELEMENTS",
849 pathelements));
850 getLog().debug(result);
851 }
852 }
853 }
854 return result;
855 }
856
857
858
859
860
861
862
863
864
865 private String getFileSets() throws MojoExecutionException {
866 String result = null;
867 if (modulepath != null) {
868 final List<FileSet> filesets = modulepath.getFileSets();
869 if (filesets != null && !filesets.isEmpty()) {
870 for (final FileSet fileSet : filesets) {
871 final File fileSetDir;
872 try {
873 fileSetDir =
874 PluginUtils.normalizeFileSetBaseDir(getBaseDir(), fileSet);
875 } catch (IOException ex) {
876 throw new MojoExecutionException(
877 "Error: Unable to resolve fileset", ex);
878 }
879 result = Stream.of(getFileSetManager().getIncludedFiles(fileSet))
880 .filter(fileName -> !StringUtils.isBlank(fileName))
881 .map(fileName -> fileSetDir.toPath().resolve(
882 StringUtils.stripToEmpty(fileName)).toString())
883 .collect(Collectors.joining(File.pathSeparator));
884 if (getLog().isDebugEnabled()) {
885 getLog().debug(PluginUtils.getFileSetDebugInfo("FILESET",
886 fileSet, result));
887 }
888 }
889 }
890 }
891 return result;
892 }
893
894
895
896
897
898
899
900
901
902 private String getDirSets() throws MojoExecutionException {
903 String result = null;
904 if (modulepath != null) {
905 final List<FileSet> dirsets = modulepath.getDirSets();
906 if (dirsets != null && !dirsets.isEmpty()) {
907 for (final FileSet dirSet : dirsets) {
908 final File dirSetDir;
909 try {
910 dirSetDir =
911 PluginUtils.normalizeFileSetBaseDir(getBaseDir(), dirSet);
912 } catch (IOException ex) {
913 throw new MojoExecutionException(
914 "Error: Unable to resolve dirset", ex);
915 }
916 result = Stream.of(getFileSetManager().getIncludedDirectories(dirSet))
917 .filter(dirName -> !StringUtils.isBlank(dirName))
918 .map(dirName -> dirSetDir.toPath().resolve(
919 StringUtils.stripToEmpty(dirName)).toString())
920 .collect(Collectors.joining(File.pathSeparator));
921 if (getLog().isDebugEnabled()) {
922 getLog().debug(PluginUtils.getFileSetDebugInfo("DIRSET",
923 dirSet, result));
924 }
925 }
926 }
927 }
928 return result;
929 }
930
931
932
933
934
935
936 private String getDependencySets() {
937 String result = null;
938 if (modulepath != null) {
939 final List<DependencySet> dependencysets =
940 modulepath.getDependencySets();
941 if (dependencysets != null && !dependencysets.isEmpty()) {
942 for (final DependencySet dependencySet : dependencysets) {
943 result = getIncludedDependencies(dependencySet)
944 .stream()
945 .collect(Collectors.joining(File.pathSeparator));
946 if (getLog().isDebugEnabled()) {
947 getLog().debug(PluginUtils.getDependencySetDebugInfo(
948 "DEPENDENCYSET", dependencySet, result));
949 }
950 }
951 }
952 }
953 return result;
954 }
955
956
957
958
959
960
961
962
963
964 private Set<String> getIncludedDependencies(final DependencySet depSet) {
965 return projectDependencies.getPathElements().entrySet().stream()
966 .filter(entry -> entry != null
967 && entry.getKey() != null
968 && filterDependency(depSet, entry.getKey(), entry.getValue()))
969 .map(entry -> entry.getKey().toString())
970 .collect(Collectors.toSet());
971 }
972
973
974
975
976
977
978
979
980
981 @SuppressWarnings("unused")
982 private Set<String> getExcludedDependencies(final DependencySet depSet) {
983 return projectDependencies.getPathElements().entrySet().stream()
984 .filter(entry -> entry != null
985 && entry.getKey() != null
986 && !filterDependency(depSet, entry.getKey(), entry.getValue()))
987 .map(entry -> entry.getKey().toString())
988 .collect(Collectors.toSet());
989 }
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004 private boolean filterDependency(final DependencySet depSet, final File file,
1005 final JavaModuleDescriptor descriptor) {
1006
1007 if (descriptor == null) {
1008 if (getLog().isWarnEnabled()) {
1009 getLog().warn("Missing module descriptor: " + file);
1010 }
1011 } else {
1012 if (descriptor.isAutomatic() && getLog().isDebugEnabled()) {
1013 getLog().debug("Found automatic module: " + file);
1014 }
1015 }
1016
1017 boolean isIncluded = false;
1018
1019 if (depSet == null) {
1020
1021 isIncluded = true;
1022
1023 if (descriptor != null && descriptor.isAutomatic()
1024 && getLog().isDebugEnabled()) {
1025 getLog().debug("Included automatic module: " + file);
1026 }
1027
1028 if (file.compareTo(getOutputDir()) == 0) {
1029 isIncluded = false;
1030 if (getLog().isDebugEnabled()) {
1031 getLog().debug("Excluded output module: " + file);
1032 }
1033 }
1034 } else {
1035 if (descriptor != null && descriptor.isAutomatic()
1036 && depSet.isAutomaticExcluded()) {
1037 if (getLog().isDebugEnabled()) {
1038 getLog().debug("Excluded automatic module: " + file);
1039 }
1040 } else {
1041 if (file.compareTo(getOutputDir()) == 0) {
1042 if (depSet.isOutputIncluded()) {
1043 isIncluded = true;
1044 if (getLog().isDebugEnabled()) {
1045 getLog().debug("Included output module: " + file);
1046 }
1047 } else {
1048 if (getLog().isDebugEnabled()) {
1049 getLog().debug("Excluded output module: " + file);
1050 }
1051 }
1052 } else {
1053 isIncluded = matchesIncludes(depSet, file, descriptor)
1054 && !matchesExcludes(depSet, file, descriptor);
1055 }
1056 }
1057 }
1058
1059 if (getLog().isDebugEnabled()) {
1060 getLog().debug(PluginUtils.getDependencyDebugInfo(file, descriptor,
1061 isIncluded));
1062 }
1063
1064 return isIncluded;
1065 }
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078 private boolean matchesIncludes(final DependencySet depSet, final File file,
1079 final JavaModuleDescriptor descriptor) {
1080
1081 final String name = descriptor == null ? "" : descriptor.name();
1082
1083 final List<String> includes = depSet.getIncludes();
1084 final List<String> includenames = depSet.getIncludeNames();
1085
1086 boolean result = true;
1087
1088 if (includenames == null || includenames.isEmpty()) {
1089 if (includes == null || includes.isEmpty()) {
1090 result = true;
1091 } else {
1092 result = pathMatches(includes, file.toPath());
1093 }
1094 } else {
1095 if (includes == null || includes.isEmpty()) {
1096 result = nameMatches(includenames, name);
1097 } else {
1098 result = pathMatches(includes, file.toPath())
1099 || nameMatches(includenames, name);
1100 }
1101 }
1102 return result;
1103 }
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116 private boolean matchesExcludes(final DependencySet depSet, final File file,
1117 final JavaModuleDescriptor descriptor) {
1118
1119 final String name = descriptor == null ? "" : descriptor.name();
1120
1121 final List<String> excludes = depSet.getExcludes();
1122 final List<String> excludenames = depSet.getExcludeNames();
1123
1124 boolean result = false;
1125
1126 if (excludenames == null || excludenames.isEmpty()) {
1127 if (excludes == null || excludes.isEmpty()) {
1128 result = false;
1129 } else {
1130 result = pathMatches(excludes, file.toPath());
1131 }
1132 } else {
1133 if (excludes == null || excludes.isEmpty()) {
1134 result = nameMatches(excludenames, name);
1135 } else {
1136 result = pathMatches(excludes, file.toPath())
1137 || nameMatches(excludenames, name);
1138 }
1139 }
1140 return result;
1141 }
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 private boolean pathMatches(final List<String> patterns, final Path path) {
1155 for (final String pattern : patterns) {
1156 final PathMatcher pathMatcher =
1157 FileSystems.getDefault().getPathMatcher(pattern);
1158 if (pathMatcher.matches(path)) {
1159 return true;
1160 }
1161 }
1162 return false;
1163 }
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 private boolean nameMatches(final List<String> patterns, final String name) {
1176 for (final String pattern : patterns) {
1177 final Pattern regexPattern = Pattern.compile(pattern);
1178 final Matcher nameMatcher = regexPattern.matcher(name);
1179 if (nameMatcher.matches()) {
1180 return true;
1181 }
1182 }
1183 return false;
1184 }
1185
1186
1187
1188
1189
1190
1191
1192
1193 private void processModules(final CommandLineBuilder cmdLine)
1194 throws MojoExecutionException {
1195 CommandLineOption opt = null;
1196
1197 if (modulepath != null) {
1198 final StringBuilder path = new StringBuilder();
1199 final String pathElements = getPathElements();
1200 if (!StringUtils.isBlank(pathElements)) {
1201 path.append(StringUtils.stripToEmpty(pathElements));
1202 }
1203 final String fileSets = getFileSets();
1204 if (!StringUtils.isBlank(fileSets)) {
1205 if (path.length() != 0) {
1206 path.append(File.pathSeparator);
1207 }
1208 path.append(StringUtils.stripToEmpty(fileSets));
1209 }
1210 final String dirSets = getDirSets();
1211 if (!StringUtils.isBlank(dirSets)) {
1212 if (path.length() != 0) {
1213 path.append(File.pathSeparator);
1214 }
1215 path.append(StringUtils.stripToEmpty(dirSets));
1216 }
1217 final String dependencySets = getDependencySets();
1218 if (!StringUtils.isBlank(dependencySets)) {
1219 if (path.length() != 0) {
1220 path.append(File.pathSeparator);
1221 }
1222 path.append(StringUtils.stripToEmpty(dependencySets));
1223 }
1224 if (path.length() != 0) {
1225 opt = cmdLine.createOpt();
1226 opt.createArg().setValue("--module-path");
1227 opt.createArg().setValue(path.toString());
1228 }
1229 }
1230
1231 if (includelocales != null && !includelocales.isEmpty()) {
1232 if (addmodules == null) {
1233 addmodules = new ArrayList<>();
1234 }
1235 addmodules.add("jdk.localedata");
1236 }
1237 if (addmodules != null && !addmodules.isEmpty()) {
1238 opt = cmdLine.createOpt();
1239 opt.createArg().setValue("--add-modules");
1240 opt.createArg().setValue(
1241 addmodules.stream().collect(Collectors.joining(",")));
1242 }
1243 }
1244
1245
1246
1247
1248
1249
1250
1251
1252 private void processOptions(final CommandLineBuilder cmdLine)
1253 throws MojoExecutionException {
1254 CommandLineOption opt = null;
1255
1256 opt = cmdLine.createOpt();
1257 opt.createArg().setValue("--output");
1258 try {
1259 opt.createArg().setValue(output.getCanonicalPath());
1260 } catch (IOException ex) {
1261 throw new MojoExecutionException(MessageFormat.format(
1262 ERROR_RESOLVE,
1263 "--output",
1264 output.toString()), ex);
1265 }
1266
1267 if (saveopts != null) {
1268 opt = cmdLine.createOpt();
1269 opt.createArg().setValue("--save-opts");
1270 try {
1271 opt.createArg().setValue(saveopts.getCanonicalPath());
1272 } catch (IOException ex) {
1273 throw new MojoExecutionException(MessageFormat.format(
1274 ERROR_RESOLVE,
1275 "--save-opts",
1276 saveopts.toString()), ex);
1277 }
1278 }
1279
1280 if (postprocesspath != null) {
1281 opt = cmdLine.createOpt();
1282 opt.createArg().setValue("--post-process-path");
1283 try {
1284 opt.createArg().setValue(postprocesspath.getCanonicalPath());
1285 } catch (IOException ex) {
1286 throw new MojoExecutionException(MessageFormat.format(
1287 ERROR_RESOLVE,
1288 "--post-process-path",
1289 postprocesspath.toString()), ex);
1290 }
1291 }
1292
1293 if (!StringUtils.isBlank(resourceslastsorter)) {
1294 opt = cmdLine.createOpt();
1295 opt.createArg().setValue("--resources-last-sorter");
1296 opt.createArg().setValue(StringUtils.stripToEmpty(resourceslastsorter));
1297 }
1298
1299 if (verbose) {
1300 opt = cmdLine.createOpt();
1301 opt.createArg().setValue("--verbose");
1302 }
1303
1304 if (bindservices) {
1305 opt = cmdLine.createOpt();
1306 opt.createArg().setValue("--bind-services");
1307 }
1308
1309 if (noheaderfiles) {
1310 opt = cmdLine.createOpt();
1311 opt.createArg().setValue("--no-header-files");
1312 }
1313
1314 if (nomanpages) {
1315 opt = cmdLine.createOpt();
1316 opt.createArg().setValue("--no-man-pages");
1317 }
1318
1319 if (ignoresigninginformation) {
1320 opt = cmdLine.createOpt();
1321 opt.createArg().setValue("--ignore-signing-information");
1322 }
1323
1324 if (stripdebug) {
1325 opt = cmdLine.createOpt();
1326 opt.createArg().setValue("--strip-debug");
1327 }
1328
1329 if (stripjavadebugattributes) {
1330 if (toolJavaVersion.atLeast(JavaVersion.JAVA_13)) {
1331 opt = cmdLine.createOpt();
1332 opt.createArg().setValue("--strip-java-debug-attributes");
1333 } else {
1334 stripjavadebugattributes = false;
1335 if (getLog().isWarnEnabled()) {
1336 getLog().warn(MessageFormat.format(
1337 "Parameter [{0}] skiped, at least {1} is required to use it",
1338 "--strip-java-debug-attributes",
1339 JavaVersion.JAVA_13));
1340 }
1341 }
1342 }
1343
1344 if (stripnativecommands) {
1345 opt = cmdLine.createOpt();
1346 opt.createArg().setValue("--strip-native-commands");
1347 }
1348
1349 if (deduplegalnotices) {
1350 opt = cmdLine.createOpt();
1351 opt.createArg().setValue(
1352 "--dedup-legal-notices=error-if-not-same-content");
1353 }
1354
1355 if (limitmodules != null && !limitmodules.isEmpty()) {
1356 opt = cmdLine.createOpt();
1357 opt.createArg().setValue("--limit-modules");
1358 opt.createArg().setValue(
1359 limitmodules.stream().collect(Collectors.joining(",")));
1360 }
1361
1362 if (suggestproviders != null && !suggestproviders.isEmpty()) {
1363 opt = cmdLine.createOpt();
1364 opt.createArg().setValue("--suggest-providers");
1365 opt.createArg().setValue(
1366 suggestproviders.stream().collect(Collectors.joining(",")));
1367 }
1368
1369 if (endian != null && !endian.equals(Endian.NATIVE)) {
1370 opt = cmdLine.createOpt();
1371 opt.createArg().setValue("--endian");
1372 opt.createArg().setValue(endian.toString().toLowerCase(Locale.ROOT));
1373 }
1374
1375 if (disableplugins != null) {
1376 for (final String plugin : disableplugins) {
1377 opt = cmdLine.createOpt();
1378 opt.createArg().setValue("--disable-plugin");
1379 opt.createArg().setValue(plugin);
1380 }
1381 }
1382
1383 if (includelocales != null && !includelocales.isEmpty()) {
1384 opt = cmdLine.createOpt();
1385 opt.createArg().setValue(
1386 includelocales.stream()
1387 .collect(Collectors.joining(",", "--include-locales=", "")));
1388 }
1389
1390 if (excludejmodsection != null) {
1391 opt = cmdLine.createOpt();
1392 opt.createArg().setValue("--exclude-jmod-section="
1393 + excludejmodsection.toString().toLowerCase(Locale.ROOT));
1394 }
1395
1396 if (generatejliclasses != null) {
1397 opt = cmdLine.createOpt();
1398 try {
1399 opt.createArg().setValue("--generate-jli-classes=@"
1400 + generatejliclasses.getCanonicalPath());
1401 } catch (IOException ex) {
1402 throw new MojoExecutionException(MessageFormat.format(
1403 ERROR_RESOLVE,
1404 "----generate-jli-classes",
1405 generatejliclasses.toString()), ex);
1406 }
1407 }
1408
1409 if (vm != null) {
1410 opt = cmdLine.createOpt();
1411 opt.createArg().setValue("--vm="
1412 + vm.toString().toLowerCase(Locale.ROOT));
1413 }
1414
1415 if (launcher != null) {
1416 final String launcherCommand =
1417 StringUtils.stripToEmpty(launcher.getCommand());
1418 if (!StringUtils.isBlank(launcherCommand)) {
1419 final String launcherModule =
1420 StringUtils.stripToEmpty(launcher.getMainModule());
1421 if (!StringUtils.isBlank(launcherModule)) {
1422 opt = cmdLine.createOpt();
1423 opt.createArg().setValue("--launcher");
1424 final String launcherClass =
1425 StringUtils.stripToEmpty(launcher.getMainClass());
1426 if (StringUtils.isBlank(launcherClass)) {
1427 opt.createArg().setValue(launcherCommand + "="
1428 + launcherModule);
1429 } else {
1430 opt.createArg().setValue(launcherCommand + "="
1431 + launcherModule + "/" + launcherClass);
1432 }
1433 }
1434 }
1435 }
1436
1437 if (compress != null) {
1438 final Compression compression = compress.getCompression();
1439 final List<String> filters = compress.getFilters();
1440 if (compression != null) {
1441 final StringBuilder option = new StringBuilder("--compress=");
1442 option.append(compression.getValue());
1443 if (filters != null) {
1444 option.append(filters.stream()
1445 .collect(Collectors.joining(",", ":filter=", "")));
1446 }
1447 opt = cmdLine.createOpt();
1448 opt.createArg().setValue(option.toString());
1449 }
1450 }
1451
1452 if (orderresources != null && !orderresources.isEmpty()) {
1453 opt = cmdLine.createOpt();
1454 opt.createArg().setValue(orderresources.stream()
1455 .collect(Collectors.joining(",", "--order-resources=", "")));
1456 }
1457
1458 if (excluderesources != null && !excluderesources.isEmpty()) {
1459 opt = cmdLine.createOpt();
1460 opt.createArg().setValue(excluderesources.stream()
1461 .collect(Collectors.joining(",", "--exclude-resources=", "")));
1462 }
1463
1464 if (excludefiles != null && !excludefiles.isEmpty()) {
1465 opt = cmdLine.createOpt();
1466 opt.createArg().setValue(excludefiles.stream()
1467 .collect(Collectors.joining(",", "--exclude-files=", "")));
1468 }
1469
1470 if (releaseinfo != null) {
1471 final StringBuilder option = new StringBuilder();
1472 final File releaseinfofile = releaseinfo.getFile();
1473 if (releaseinfofile != null) {
1474 option.append(releaseinfofile.toString());
1475 }
1476 final Map<String, String> adds = releaseinfo.getAdds();
1477 if (adds != null && !adds.entrySet().isEmpty()) {
1478 if (option.length() != 0) {
1479 option.append(':');
1480 }
1481 option.append(adds.entrySet().stream()
1482 .filter(add -> add != null && !StringUtils.isBlank(add.getKey()))
1483 .map(add -> StringUtils.stripToEmpty(add.getKey()) + "="
1484 + StringUtils.stripToEmpty(add.getValue()))
1485 .collect(Collectors.joining(":", "add:", "")));
1486 }
1487 final Map<String, String> dels = releaseinfo.getDels();
1488 if (dels != null && !dels.entrySet().isEmpty()) {
1489 if (option.length() != 0) {
1490 option.append(':');
1491 }
1492 option.append(dels.entrySet().stream()
1493 .filter(del -> del != null && !StringUtils.isBlank(del.getKey()))
1494 .map(del -> StringUtils.stripToEmpty(del.getKey()))
1495 .collect(Collectors.joining(":", "del:", "")));
1496 }
1497 opt = cmdLine.createOpt();
1498 opt.createArg().setValue("--release-info=" + option.toString());
1499 }
1500 }
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510 private void copyFiles(final List<File> files, final File dir)
1511 throws MojoExecutionException {
1512 if (getLog().isDebugEnabled()) {
1513 getLog().debug(MessageFormat.format("Copy files to: [{0}]", dir));
1514 }
1515 for (final File file : files) {
1516 try {
1517 if (file.exists()) {
1518 if (file.isDirectory()) {
1519 if (getLog().isDebugEnabled()) {
1520 getLog().debug(MessageFormat.format("Skiped directory: [{0}]",
1521 file));
1522 }
1523 } else {
1524 FileUtils.copyFileToDirectory(file, dir);
1525 if (getLog().isDebugEnabled()) {
1526 getLog().debug(MessageFormat.format("Copied file: [{0}]", file));
1527 }
1528 }
1529 }
1530 } catch (IOException | IllegalArgumentException ex) {
1531 throw new MojoExecutionException(MessageFormat.format(
1532 "Error: Unable to copy file: [{0}]", file), ex);
1533 }
1534 }
1535 }
1536
1537
1538
1539
1540
1541
1542 private void processLauncherScripts() throws MojoExecutionException {
1543 if (launcher == null) {
1544 return;
1545 }
1546
1547 final String scriptName = StringUtils.stripToEmpty(launcher.getCommand());
1548 if (StringUtils.isBlank(scriptName)) {
1549 return;
1550 }
1551
1552 final Path nixScript = output.toPath().resolve("bin/" + scriptName);
1553 final Path winScript = output.toPath().resolve("bin/" + scriptName
1554 + ".bat");
1555
1556 if (stripnativecommands) {
1557 if (Files.exists(nixScript) && !Files.isDirectory(nixScript)) {
1558 try {
1559 FileUtils.forceDelete(nixScript.toFile());
1560 } catch (IOException ex) {
1561 if (getLog().isWarnEnabled()) {
1562 getLog().warn(MessageFormat.format(
1563 "Unable to delete launcher script: [{0}]", nixScript));
1564 }
1565 }
1566 }
1567 if (Files.exists(winScript) && !Files.isDirectory(winScript)) {
1568 try {
1569 FileUtils.forceDelete(winScript.toFile());
1570 } catch (IOException ex) {
1571 if (getLog().isWarnEnabled()) {
1572 getLog().warn(MessageFormat.format(
1573 "Unable to delete launcher script: [{0}]", winScript));
1574 }
1575 }
1576 }
1577 return;
1578 }
1579
1580 final String moduleName = StringUtils.stripToEmpty(
1581 launcher.getMainModule());
1582 if (StringUtils.isEmpty(moduleName)) {
1583 return;
1584 }
1585
1586 final String mainClassName = StringUtils.stripToEmpty(
1587 launcher.getMainClass());
1588
1589 final StringBuilder mainName = new StringBuilder(moduleName);
1590 if (!StringUtils.isEmpty(mainClassName)) {
1591 mainName
1592 .append('/')
1593 .append(mainClassName);
1594 }
1595
1596 final String args = StringUtils.stripToEmpty(launcher.getArgs());
1597
1598 final String jvmArgs = StringUtils.stripToEmpty(launcher.getJvmArgs());
1599
1600 if (getLog().isDebugEnabled()) {
1601 getLog().debug(System.lineSeparator()
1602 + "Processing launcher scripts with following variables:"
1603 + System.lineSeparator()
1604 + MessageFormat.format(" - moduleName = [{0}]", moduleName)
1605 + System.lineSeparator()
1606 + MessageFormat.format(" - mainClassName = [{0}]", mainClassName)
1607 + System.lineSeparator()
1608 + MessageFormat.format(" - mainName = [{0}]", mainName.toString())
1609 + System.lineSeparator()
1610 + MessageFormat.format(" - args = [{0}]", args)
1611 + System.lineSeparator()
1612 + MessageFormat.format(" - jvmArgs = [{0}]", jvmArgs));
1613 }
1614
1615 final Map<String, String> data = new HashMap<>();
1616 data.put("moduleName", moduleName);
1617 data.put("mainClassName", mainClassName);
1618 data.put("mainName", mainName.toString());
1619 data.put("args", args);
1620 data.put("jvmArgs", jvmArgs);
1621
1622 final File nixTemplate = launcher.getNixTemplate();
1623 if (nixTemplate != null && Files.exists(nixTemplate.toPath())
1624 && !Files.isDirectory(nixTemplate.toPath())) {
1625 createLauncherScript(nixScript, nixTemplate.toPath(), data);
1626 }
1627
1628 final File winTemplate = launcher.getWinTemplate();
1629 if (winTemplate != null && Files.exists(winTemplate.toPath())
1630 && !Files.isDirectory(winTemplate.toPath())) {
1631 createLauncherScript(winScript, winTemplate.toPath(), data);
1632 }
1633
1634 }
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646 private void createLauncherScript(final Path script, final Path template,
1647 final Map<String, String> data) throws MojoExecutionException {
1648 if (getLog().isDebugEnabled()) {
1649 getLog().debug(System.lineSeparator()
1650 + MessageFormat.format("Fixing launcher script: [{0}]", script)
1651 + System.lineSeparator()
1652 + MessageFormat.format("with template: [{0}]", template));
1653 }
1654 final StringSubstitutor engine = new StringSubstitutor(data)
1655 .setEnableUndefinedVariableException(true)
1656 .setPreserveEscapes(true)
1657 .setEscapeChar('\\');
1658 try {
1659 Files.write(script,
1660 Files.lines(template, getCharset())
1661 .map(line -> engine.replace(line).replace("\\$", "$"))
1662 .collect(Collectors.toList()),
1663 getCharset());
1664 } catch (IllegalArgumentException ex) {
1665 throw new MojoExecutionException(MessageFormat.format(
1666 "Error: Variable not found in the launcher template file: [{0}]",
1667 template), ex);
1668 } catch (IOException ex) {
1669 throw new MojoExecutionException(MessageFormat.format(
1670 "Error: Unable to write to the launcher script file: [{0}]",
1671 script), ex);
1672 }
1673 }
1674
1675
1676
1677
1678
1679
1680 @Override
1681 public void execute() throws MojoExecutionException {
1682
1683
1684 init(TOOL_NAME, toolhome, TOOL_HOME_BIN);
1685
1686
1687 toolJavaVersion = getToolJavaVersion();
1688 if (toolJavaVersion == null
1689 || !toolJavaVersion.atLeast(JavaVersion.JAVA_9)) {
1690 throw new MojoExecutionException(MessageFormat.format(
1691 "Error: At least {0} is required to use [{1}]", JavaVersion.JAVA_9,
1692 TOOL_NAME));
1693 }
1694
1695
1696 try {
1697 FileUtils.forceMkdir(modsdir);
1698 } catch (IOException | IllegalArgumentException ex) {
1699 throw new MojoExecutionException(MessageFormat.format(
1700 "Error: Unable to create mods directory: [{0}]", modsdir), ex);
1701 }
1702
1703
1704 try {
1705 FileUtils.forceMkdir(libsdir);
1706 } catch (IOException | IllegalArgumentException ex) {
1707 throw new MojoExecutionException(MessageFormat.format(
1708 "Error: Unable to create libs directory: [{0}]", libsdir), ex);
1709 }
1710
1711
1712 if (getLog().isDebugEnabled()) {
1713 getLog().debug(MessageFormat.format("Output directory: [{0}]", output));
1714 }
1715 if (output.exists() && output.isDirectory()) {
1716 try {
1717 FileUtils.deleteDirectory(output);
1718 } catch (IOException ex) {
1719 throw new MojoExecutionException(MessageFormat.format(
1720 "Error: Unable to delete image output directory: [{0}]", output),
1721 ex);
1722 }
1723 }
1724
1725
1726 projectDependencies = resolveDependencies();
1727 mainModuleDescriptor = fetchMainModuleDescriptor();
1728 List<File> classpathElements = fetchClasspathElements();
1729 List<File> modulepathElements = fetchModulepathElements();
1730 Map<File, String> pathExceptions = fetchPathExceptions();
1731 if (!pathExceptions.isEmpty() && getLog().isWarnEnabled()) {
1732 getLog().warn("Found path exceptions: " + pathExceptions.size()
1733 + System.lineSeparator()
1734 + pathExceptions.entrySet().stream()
1735 .map(entry -> entry.getKey().toString()
1736 + System.lineSeparator()
1737 + entry.getValue())
1738 .collect(Collectors.joining(System.lineSeparator())));
1739 }
1740
1741
1742 copyFiles(modulepathElements, modsdir);
1743 copyFiles(classpathElements, libsdir);
1744
1745
1746 final CommandLineBuilder cmdLineBuilder = new CommandLineBuilder();
1747 cmdLineBuilder.setExecutable(getToolExecutable().toString());
1748 processOptions(cmdLineBuilder);
1749 processModules(cmdLineBuilder);
1750 final List<String> optsLines = new ArrayList<>();
1751 optsLines.add("# " + TOOL_NAME);
1752 optsLines.addAll(cmdLineBuilder.buildOptionList());
1753 if (getLog().isDebugEnabled()) {
1754 getLog().debug(optsLines.stream()
1755 .collect(Collectors.joining(System.lineSeparator(),
1756 System.lineSeparator(), "")));
1757 }
1758
1759
1760
1761 final Path cmdOptsPath = getBuildDir().toPath().resolve(OPTS_FILE);
1762 try {
1763 Files.write(cmdOptsPath, optsLines, getCharset());
1764 } catch (IOException ex) {
1765 throw new MojoExecutionException(MessageFormat.format(
1766 "Error: Unable to write command options to file: [{0}]",
1767 cmdOptsPath), ex);
1768 }
1769
1770
1771
1772 final Commandline cmdLine = new Commandline();
1773 cmdLine.setExecutable(getToolExecutable().toString());
1774 cmdLine.createArg().setValue("@" + cmdOptsPath.toString());
1775
1776
1777 int exitCode = 0;
1778 try {
1779 exitCode = execCmdLine(cmdLine);
1780 } catch (CommandLineException ex) {
1781 throw new MojoExecutionException(MessageFormat.format(
1782 "Error: Unable to execute [{0}] tool", TOOL_NAME), ex);
1783 }
1784 if (exitCode != 0) {
1785 if (getLog().isErrorEnabled()) {
1786 getLog().error(System.lineSeparator()
1787 + "Command options was: "
1788 + System.lineSeparator()
1789 + optsLines.stream()
1790 .collect(Collectors.joining(System.lineSeparator())));
1791 }
1792 throw new MojoExecutionException(MessageFormat.format(
1793 "Error: Tool execution failed [{0}] with exit code: {1}", TOOL_NAME,
1794 exitCode));
1795 }
1796
1797
1798 processLauncherScripts();
1799
1800
1801 try {
1802 FileUtils.forceDelete(cmdOptsPath.toFile());
1803 } catch (IOException ex) {
1804 throw new MojoExecutionException(MessageFormat.format(
1805 "Error: Unable to delete temporary file: [{0}]", cmdOptsPath), ex);
1806 }
1807
1808 }
1809
1810 }