1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package ru.akman.maven.plugins;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.nio.charset.Charset;
22 import java.nio.file.Files;
23 import java.nio.file.Path;
24 import java.nio.file.Paths;
25 import java.text.MessageFormat;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Properties;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
33 import org.apache.commons.lang3.JavaVersion;
34 import org.apache.commons.lang3.StringUtils;
35 import org.apache.commons.lang3.SystemUtils;
36 import org.apache.maven.execution.MavenSession;
37 import org.apache.maven.plugin.AbstractMojo;
38 import org.apache.maven.plugin.BuildPluginManager;
39 import org.apache.maven.plugin.MojoExecutionException;
40 import org.apache.maven.plugins.annotations.Component;
41 import org.apache.maven.plugins.annotations.Parameter;
42 import org.apache.maven.project.MavenProject;
43 import org.apache.maven.shared.model.fileset.util.FileSetManager;
44 import org.apache.maven.toolchain.Toolchain;
45 import org.apache.maven.toolchain.ToolchainManager;
46 import org.codehaus.plexus.util.cli.CommandLineException;
47 import org.codehaus.plexus.util.cli.CommandLineUtils;
48 import org.codehaus.plexus.util.cli.Commandline;
49
50
51
52
53 public abstract class BaseToolMojo extends AbstractMojo {
54
55
56
57
58 private static final String JDK = "jdk";
59
60
61
62
63 private static final String JAVA_HOME = "JAVA_HOME";
64
65
66
67
68 private static final int OLD_MAJOR = 1;
69
70
71
72
73 private static final int NEW_MAJOR = 9;
74
75
76
77
78 private static final int NEW_RECENT = 14;
79
80
81
82
83 private static final int ANDROID_MAJOR = 0;
84
85
86
87
88 private static final int ANDROID_MINOR = 9;
89
90
91
92
93 private static final String JAVA_HOME_BIN = "bin";
94
95
96
97
98 private static final String PATH = "PATH";
99
100
101
102
103 private static final String PATHEXT = "PATHEXT";
104
105
106
107
108 private static final String VERSION_PATTERN = "^(\\d+)(\\.(\\d+))?.*";
109
110
111
112
113 private static final String VERSION_OPTION = "--version";
114
115
116
117
118 private File baseDir;
119
120
121
122
123 private File buildDir;
124
125
126
127
128 private File outputDir;
129
130
131
132
133 private Properties properties;
134
135
136
137
138 private Charset sourceEncoding = Charset.defaultCharset();
139
140
141
142
143 private FileSetManager fileSetManager;
144
145
146
147
148
149 private List<Toolchain> toolchains;
150
151
152
153
154
155 private Toolchain toolchain;
156
157
158
159
160 private File toolHomeDirectory;
161
162
163
164
165 private File toolExecutable;
166
167
168
169
170 private String toolVersion;
171
172
173
174
175 private JavaVersion toolJavaVersion;
176
177
178
179
180 @Component
181 private ToolchainManager toolchainManager;
182
183
184
185
186 @Component
187 private BuildPluginManager pluginManager;
188
189
190
191
192 @Parameter(
193 defaultValue = "${project}",
194 readonly = true,
195 required = true
196 )
197 private MavenProject project;
198
199
200
201
202 @Parameter(
203 defaultValue = "${session}",
204 readonly = true,
205 required = true
206 )
207 private MavenSession session;
208
209
210
211
212
213
214
215
216
217
218
219 private Path getExecutableFromToolHome(final String toolName,
220 final File toolHomeDir, final String toolBinDirName) {
221 Path executablePath = toolHomeDir == null
222 ? null
223 : resolveToolPath(toolName, toolHomeDir.toPath(), toolBinDirName);
224 if (executablePath != null) {
225 try {
226 executablePath = executablePath.toRealPath();
227 toolHomeDirectory = toolHomeDir;
228 if (getLog().isDebugEnabled()) {
229 getLog().debug(MessageFormat.format(
230 "Executable (toolhome) for [{0}]: {1}", toolName,
231 executablePath));
232 getLog().debug(MessageFormat.format(
233 "Home directory (toolhome) for [{0}]: {1}", toolName,
234 toolHomeDirectory));
235 }
236 } catch (IOException ex) {
237 if (getLog().isWarnEnabled()) {
238 getLog().warn(MessageFormat.format(
239 "Unable to resolve executable (toolhome) for [{0}]: {1}",
240 toolName, executablePath), ex);
241 }
242 }
243 }
244 return executablePath;
245 }
246
247
248
249
250
251
252
253
254
255 @SuppressWarnings("deprecation")
256 private Path getExecutableFromToolchain(final String toolName) {
257 final String tcJavaHome = toolchain == null
258 ? null
259 : org.apache.maven.toolchain.java.DefaultJavaToolChain.class.cast(
260 toolchain).getJavaHome();
261 final String tcToolExecutable = toolchain == null
262 ? null
263 : toolchain.findTool(toolName);
264 Path executablePath = null;
265 if (!StringUtils.isBlank(tcJavaHome)
266 && !StringUtils.isBlank(tcToolExecutable)) {
267 try {
268 executablePath = Paths.get(tcToolExecutable).toRealPath();
269 toolHomeDirectory = new File(tcJavaHome);
270 if (getLog().isDebugEnabled()) {
271 getLog().debug(MessageFormat.format(
272 "Executable (toolchain) for [{0}]: {1}", toolName,
273 executablePath));
274 getLog().debug(MessageFormat.format(
275 "Home directory (toolchain) for [{0}]: {1}", toolName,
276 toolHomeDirectory));
277 }
278 } catch (IOException ex) {
279 if (getLog().isWarnEnabled()) {
280 getLog().warn(MessageFormat.format(
281 "Unable to resolve executable (toolchain) for [{0}]: {1}",
282 toolName, executablePath), ex);
283 }
284 }
285 }
286 return executablePath;
287 }
288
289
290
291
292
293
294
295
296
297 private Path getExecutableFromJavaHome(final String toolName) {
298 final File javaHomeDir = getJavaHome();
299 Path executablePath = javaHomeDir == null
300 ? null
301 : resolveToolPath(toolName, javaHomeDir.toPath(), JAVA_HOME_BIN);
302 if (executablePath != null) {
303 try {
304 executablePath = executablePath.toRealPath();
305 toolHomeDirectory = javaHomeDir;
306 if (getLog().isDebugEnabled()) {
307 getLog().debug(MessageFormat.format(
308 "Executable (javahome) for [{0}]: {1}", toolName,
309 executablePath));
310 getLog().debug(MessageFormat.format(
311 "Home directory (javahome) for [{0}]: {1}", toolName,
312 toolHomeDirectory));
313 }
314 } catch (IOException ex) {
315 if (getLog().isWarnEnabled()) {
316 getLog().warn(MessageFormat.format(
317 "Unable to resolve executable (javahome) for [{0}]: {1}",
318 toolName, executablePath), ex);
319 }
320 }
321 }
322 return executablePath;
323 }
324
325
326
327
328
329
330
331
332
333 private Path getExecutableFromSystemPath(final String toolName) {
334 final List<Path> systemPath = getSystemPath();
335 Path executablePath = null;
336 for (final Path path : systemPath) {
337 executablePath = resolveToolPath(toolName, path, null);
338 if (executablePath != null) {
339 break;
340 }
341 }
342 if (executablePath != null) {
343 try {
344 final Path toolHomePath = executablePath.getParent();
345 toolHomeDirectory = toolHomePath == null
346 ? null : toolHomePath.toRealPath().toFile();
347 executablePath = executablePath.toRealPath();
348 if (getLog().isDebugEnabled()) {
349 getLog().debug(MessageFormat.format(
350 "Executable (systempath) for [{0}]: {1}", toolName,
351 executablePath));
352 getLog().debug(MessageFormat.format(
353 "Home directory (systempath) for [{0}]: {1}", toolName,
354 toolHomeDirectory));
355 }
356 } catch (IOException ex) {
357 if (getLog().isWarnEnabled()) {
358 getLog().warn(MessageFormat.format(
359 "Unable to resolve executable (systempath) for [{0}]: {1}",
360 toolName, executablePath), ex);
361 }
362 }
363 }
364 return executablePath;
365 }
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386 private Path getToolExecutablePath(final String toolName,
387 final File toolHomeDir, final String toolBinDirName) {
388 Path executablePath =
389 getExecutableFromToolHome(toolName, toolHomeDir, toolBinDirName);
390 if (executablePath != null) {
391 return executablePath;
392 }
393 if (getLog().isDebugEnabled()) {
394 getLog().debug(MessageFormat.format(
395 "Executable (toolhome) for [{0}] not found", toolName));
396 }
397 executablePath = getExecutableFromToolchain(toolName);
398 if (executablePath != null) {
399 return executablePath;
400 }
401 if (getLog().isDebugEnabled()) {
402 getLog().debug(MessageFormat.format(
403 "Executable (toolchain) for [{0}] not found", toolName));
404 }
405 executablePath = getExecutableFromJavaHome(toolName);
406 if (executablePath != null) {
407 return executablePath;
408 }
409 if (getLog().isDebugEnabled()) {
410 getLog().debug(MessageFormat.format(
411 "Executable (javahome) for [{0}] not found", toolName));
412 }
413 executablePath = getExecutableFromSystemPath(toolName);
414 if (executablePath != null) {
415 return executablePath;
416 }
417 if (getLog().isDebugEnabled()) {
418 getLog().debug(MessageFormat.format(
419 "Executable (systempath) for [{0}] not found", toolName));
420 }
421 return executablePath;
422 }
423
424
425
426
427
428
429
430
431
432
433 private Path resolveToolPath(final String toolName, final Path toolHomeDir,
434 final String toolBinDirName) {
435 if (toolHomeDir == null || StringUtils.isBlank(toolName)) {
436 return null;
437 }
438 Path toolBinDir = toolHomeDir;
439 if (!StringUtils.isBlank(toolBinDirName)) {
440 toolBinDir = toolHomeDir.resolve(toolBinDirName);
441 }
442 if (!Files.exists(toolBinDir) || !Files.isDirectory(toolBinDir)) {
443 return null;
444 }
445 return findToolExecutable(toolName, List.of(toolBinDir));
446 }
447
448
449
450
451
452
453
454
455
456 private Path findToolExecutable(final String toolName,
457 final List<Path> paths) {
458 Path executablePath = null;
459 Path toolFile = null;
460 final List<String> exts = getPathExt();
461 for (final Path path : paths) {
462 if (SystemUtils.IS_OS_WINDOWS) {
463 for (final String ext : exts) {
464 toolFile = path.resolve(toolName.concat(ext));
465 if (Files.isExecutable(toolFile)
466 && !Files.isDirectory(toolFile)) {
467 executablePath = toolFile;
468 break;
469 }
470 }
471 } else {
472 toolFile = path.resolve(toolName);
473 if (Files.isExecutable(toolFile)
474 && !Files.isDirectory(toolFile)) {
475 executablePath = toolFile;
476 break;
477 }
478 }
479 }
480 return executablePath;
481 }
482
483
484
485
486
487
488 private File getJavaHome() {
489 final String javaHome = StringUtils.stripToEmpty(System.getenv(JAVA_HOME));
490 return StringUtils.isBlank(javaHome) ? null : new File(javaHome);
491 }
492
493
494
495
496
497
498
499 private List<Path> getSystemPath() {
500 final String systemPath = StringUtils.stripToEmpty(System.getenv(PATH));
501 if (StringUtils.isBlank(systemPath)) {
502 return new ArrayList<Path>();
503 }
504 return Stream.of(systemPath.split(File.pathSeparator))
505 .filter(s -> !StringUtils.isBlank(s))
506 .map(s -> Paths.get(StringUtils.stripToEmpty(s)))
507 .collect(Collectors.toList());
508 }
509
510
511
512
513
514
515
516
517 private List<String> getPathExt() {
518 if (SystemUtils.IS_OS_WINDOWS) {
519 final String systemPathExt =
520 StringUtils.stripToEmpty(System.getenv(PATHEXT));
521 if (!StringUtils.isBlank(systemPathExt)) {
522 return Stream.of(systemPathExt.split(File.pathSeparator))
523 .filter(s -> !StringUtils.isBlank(s))
524 .map(s -> StringUtils.stripToEmpty(s))
525 .collect(Collectors.toList());
526 }
527 }
528 return new ArrayList<String>();
529 }
530
531
532
533
534
535
536
537
538
539 private String obtainToolVersion(final Path executablePath)
540 throws CommandLineException {
541 final Commandline cmdLine = new Commandline();
542 cmdLine.setExecutable(executablePath.toString());
543 cmdLine.createArg().setValue(VERSION_OPTION);
544 final CommandLineUtils.StringStreamConsumer out =
545 new CommandLineUtils.StringStreamConsumer();
546 final CommandLineUtils.StringStreamConsumer err =
547 new CommandLineUtils.StringStreamConsumer();
548 return execCmdLine(cmdLine, out, err) == 0
549 ? StringUtils.stripToEmpty(out.getOutput())
550 : null;
551 }
552
553
554
555
556
557
558
559
560 private JavaVersion getCorrespondingJavaVersion(final String version) {
561 JavaVersion resolvedVersion = null;
562 if (version != null) {
563 final Matcher versionMatcher = Pattern.compile(VERSION_PATTERN)
564 .matcher(version);
565 if (versionMatcher.matches()) {
566
567 final String majorVersionPart = versionMatcher.group(1);
568 final int majorVersion = Integer.parseInt(majorVersionPart);
569
570 final String minorVersionPart = versionMatcher.group(3);
571 final int minorVersion = StringUtils.isBlank(minorVersionPart)
572 ? 0 : Integer.parseInt(minorVersionPart);
573 if (majorVersion >= NEW_MAJOR) {
574 if (majorVersion >= NEW_RECENT) {
575 resolvedVersion = JavaVersion.JAVA_RECENT;
576 } else {
577 resolvedVersion = JavaVersion.valueOf("JAVA_" + majorVersion);
578 }
579 } else {
580
581 if (majorVersion == OLD_MAJOR
582 && minorVersion > 0
583 && minorVersion <= NEW_MAJOR
584 || majorVersion == ANDROID_MAJOR
585 && minorVersion == ANDROID_MINOR) {
586 resolvedVersion = JavaVersion.valueOf("JAVA_" + majorVersion
587 + "_" + minorVersion);
588 }
589 }
590 }
591 }
592 return resolvedVersion;
593 }
594
595
596
597
598
599
600
601 @SuppressWarnings("deprecation")
602 private Toolchain getDefaultJavaToolchain() {
603 final Toolchain ctxToolchain =
604 getToolchainManager().getToolchainFromBuildContext(JDK, getSession());
605 return ctxToolchain == null || !(ctxToolchain
606 instanceof org.apache.maven.toolchain.java.DefaultJavaToolChain)
607 ? null : ctxToolchain;
608 }
609
610
611
612
613
614
615
616
617
618 private void logCommandLineExecution(final Commandline cmdLine,
619 final int exitCode, final String stdout, final String stderr) {
620 if (exitCode == 0) {
621 if (getLog().isDebugEnabled()) {
622 if (!StringUtils.isBlank(stdout)) {
623 getLog().debug(System.lineSeparator() + stdout);
624 }
625 if (!StringUtils.isBlank(stderr)) {
626 getLog().debug(System.lineSeparator() + stderr);
627 }
628 }
629 } else {
630 if (getLog().isErrorEnabled()) {
631 getLog().error(System.lineSeparator() + "Exit code: " + exitCode);
632 if (!StringUtils.isBlank(stdout)) {
633 getLog().error(System.lineSeparator() + stdout);
634 }
635 if (!StringUtils.isBlank(stderr)) {
636 getLog().error(System.lineSeparator() + stderr);
637 }
638 getLog().error(System.lineSeparator()
639 + "Command line was: "
640 + CommandLineUtils.toString(cmdLine.getCommandline()));
641 }
642 }
643 }
644
645
646
647
648
649
650 protected File getBaseDir() {
651 return baseDir;
652 }
653
654
655
656
657
658
659 protected File getBuildDir() {
660 return buildDir;
661 }
662
663
664
665
666
667
668 protected File getOutputDir() {
669 return outputDir;
670 }
671
672
673
674
675
676
677 protected Properties getProperties() {
678 return properties;
679 }
680
681
682
683
684
685
686 protected Charset getCharset() {
687 return sourceEncoding;
688 }
689
690
691
692
693
694
695 protected FileSetManager getFileSetManager() {
696 return fileSetManager;
697 }
698
699
700
701
702
703
704
705 protected List<Toolchain> getToolchains() {
706 return toolchains;
707 }
708
709
710
711
712
713
714
715 protected Toolchain getToolchain() {
716 return toolchain;
717 }
718
719
720
721
722
723
724 protected File getToolHomeDirectory() {
725 return toolHomeDirectory;
726 }
727
728
729
730
731
732
733 protected File getToolExecutable() {
734 return toolExecutable;
735 }
736
737
738
739
740
741
742 protected String getToolVersion() {
743 return toolVersion;
744 }
745
746
747
748
749
750
751 protected JavaVersion getToolJavaVersion() {
752 return toolJavaVersion;
753 }
754
755
756
757
758
759
760 protected ToolchainManager getToolchainManager() {
761 return toolchainManager;
762 }
763
764
765
766
767
768
769 protected BuildPluginManager getPluginManager() {
770 return pluginManager;
771 }
772
773
774
775
776
777
778 protected MavenProject getProject() {
779 return project;
780 }
781
782
783
784
785
786
787 protected MavenSession getSession() {
788 return session;
789 }
790
791
792
793
794
795
796
797
798
799
800
801 protected int execCmdLine(final Commandline cmdLine)
802 throws CommandLineException {
803 return execCmdLine(cmdLine, null, null);
804 }
805
806
807
808
809
810
811
812
813
814
815
816
817
818 protected int execCmdLine(final Commandline cmdLine,
819 final CommandLineUtils.StringStreamConsumer out,
820 final CommandLineUtils.StringStreamConsumer err)
821 throws CommandLineException {
822 if (getLog().isDebugEnabled()) {
823 getLog().debug(CommandLineUtils.toString(cmdLine.getCommandline()));
824 }
825 final CommandLineUtils.StringStreamConsumer stdout = out == null
826 ? new CommandLineUtils.StringStreamConsumer()
827 : out;
828 final CommandLineUtils.StringStreamConsumer stderr = err == null
829 ? new CommandLineUtils.StringStreamConsumer()
830 : err;
831 final int exitCode =
832 CommandLineUtils.executeCommandLine(cmdLine, stdout, err);
833 logCommandLineExecution(cmdLine, exitCode, stdout.getOutput(),
834 stderr.getOutput());
835 return exitCode;
836 }
837
838
839
840
841
842
843
844
845
846
847
848
849 protected void init(final String toolName, final File toolHomeDir,
850 final String toolBinDirName) throws MojoExecutionException {
851 if (getProject() == null) {
852 throw new MojoExecutionException(
853 "Error: The predefined variable ${project} is not defined");
854 }
855
856 if (getSession() == null) {
857 throw new MojoExecutionException(
858 "Error: The predefined variable ${session} is not defined");
859 }
860
861 baseDir = getProject().getBasedir();
862 if (baseDir == null) {
863 throw new MojoExecutionException(
864 "Error: The predefined variable ${project.basedir} is not defined");
865 }
866
867 buildDir = new File(getProject().getBuild().getDirectory());
868 if (buildDir == null) {
869 throw new MojoExecutionException(
870 "Error: The predefined variable ${project.build.directory} is not defined");
871 }
872
873 outputDir = new File(getProject().getBuild().getOutputDirectory());
874 if (outputDir == null) {
875 throw new MojoExecutionException(
876 "Error: The predefined variable ${project.build.outputDirectory} is not defined");
877 }
878
879 properties = getProject().getProperties();
880 if (properties == null) {
881 throw new MojoExecutionException(
882 "Error: Unable to read project properties");
883 }
884
885 fileSetManager = new FileSetManager();
886 if (fileSetManager == null) {
887 throw new MojoExecutionException(
888 "Error: Unable to create file set manager");
889 }
890
891
892 final String encoding =
893 properties.getProperty("project.build.sourceEncoding");
894 try {
895 sourceEncoding = Charset.forName(encoding);
896 } catch (IllegalArgumentException ex) {
897 if (getLog().isWarnEnabled()) {
898 getLog().warn("Unable to read ${project.build.sourceEncoding}");
899 }
900 }
901 if (getLog().isDebugEnabled()) {
902 getLog().debug(MessageFormat.format(
903 "Using source encoding: [{0}] to write files", sourceEncoding));
904 }
905
906
907 toolchains = getToolchainManager().getToolchains(getSession(), JDK, null);
908 if (toolchains == null) {
909 if (getLog().isDebugEnabled()) {
910 getLog().debug("No toolchains found");
911 }
912 } else {
913 toolchains.forEach(tc -> {
914 if (getLog().isDebugEnabled()) {
915 getLog().debug("Found toolchain: " + tc);
916 }
917 });
918 }
919
920
921
922 toolchain = getDefaultJavaToolchain();
923 if (toolchain == null) {
924 if (getLog().isDebugEnabled()) {
925 getLog().debug("Toolchain not specified");
926 }
927 } else {
928 if (getLog().isInfoEnabled()) {
929 getLog().info("Using toolchain: " + toolchain);
930 }
931 }
932
933
934 final Path executablePath =
935 getToolExecutablePath(toolName, toolHomeDir, toolBinDirName);
936 if (executablePath == null) {
937 throw new MojoExecutionException(MessageFormat.format(
938 "Error: Executable for [{0}] not found", toolName));
939 }
940 toolExecutable = executablePath.toFile();
941
942
943 try {
944 toolVersion = obtainToolVersion(executablePath);
945 } catch (CommandLineException ex) {
946 throw new MojoExecutionException(MessageFormat.format(
947 "Error: Unable to obtain version of [{0}]", toolName), ex);
948 }
949 if (toolVersion == null) {
950 if (getLog().isWarnEnabled()) {
951 getLog().warn(MessageFormat.format(
952 "Unable to resolve version of [{0}]", toolName));
953 }
954 } else {
955 if (getLog().isInfoEnabled()) {
956 getLog().info(MessageFormat.format("Version of [{0}]: {1}", toolName,
957 toolVersion));
958 }
959 }
960
961
962 toolJavaVersion = getCorrespondingJavaVersion(toolVersion);
963 if (toolJavaVersion == null) {
964 if (getLog().isWarnEnabled()) {
965 getLog().warn(MessageFormat.format(
966 "Unable to resolve corresponding java version of [{0}]",
967 toolName));
968 }
969 } else {
970 if (getLog().isDebugEnabled()) {
971 getLog().debug(MessageFormat.format(
972 "Version (corresponding java version) of [{0}]: {1}", toolName,
973 toolJavaVersion));
974 }
975 }
976
977 }
978
979 }