1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.invoker;
20
21 import java.io.BufferedWriter;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileOutputStream;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStreamWriter;
29 import java.io.Reader;
30 import java.io.Writer;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.text.DecimalFormat;
35 import java.text.DecimalFormatSymbols;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.LinkedHashMap;
43 import java.util.LinkedHashSet;
44 import java.util.List;
45 import java.util.Locale;
46 import java.util.Map;
47 import java.util.Properties;
48 import java.util.Set;
49 import java.util.TreeSet;
50 import java.util.stream.Collectors;
51
52 import org.apache.maven.artifact.Artifact;
53 import org.apache.maven.execution.MavenSession;
54 import org.apache.maven.model.Model;
55 import org.apache.maven.plugin.AbstractMojo;
56 import org.apache.maven.plugin.MojoExecution;
57 import org.apache.maven.plugin.MojoExecutionException;
58 import org.apache.maven.plugin.MojoFailureException;
59 import org.apache.maven.plugins.annotations.Component;
60 import org.apache.maven.plugins.annotations.Parameter;
61 import org.apache.maven.plugins.invoker.model.BuildJob;
62 import org.apache.maven.plugins.invoker.model.io.xpp3.BuildJobXpp3Writer;
63 import org.apache.maven.project.MavenProject;
64 import org.apache.maven.settings.Settings;
65 import org.apache.maven.settings.SettingsUtils;
66 import org.apache.maven.settings.TrackableBase;
67 import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
68 import org.apache.maven.settings.building.SettingsBuilder;
69 import org.apache.maven.settings.building.SettingsBuildingException;
70 import org.apache.maven.settings.building.SettingsBuildingRequest;
71 import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
72 import org.apache.maven.shared.invoker.CommandLineConfigurationException;
73 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
74 import org.apache.maven.shared.invoker.InvocationRequest;
75 import org.apache.maven.shared.invoker.InvocationResult;
76 import org.apache.maven.shared.invoker.Invoker;
77 import org.apache.maven.shared.invoker.MavenCommandLineBuilder;
78 import org.apache.maven.shared.invoker.MavenInvocationException;
79 import org.apache.maven.shared.scriptinterpreter.ScriptException;
80 import org.apache.maven.shared.scriptinterpreter.ScriptReturnException;
81 import org.apache.maven.shared.scriptinterpreter.ScriptRunner;
82 import org.apache.maven.shared.utils.logging.MessageBuilder;
83 import org.apache.maven.toolchain.MisconfiguredToolchainException;
84 import org.apache.maven.toolchain.ToolchainManagerPrivate;
85 import org.apache.maven.toolchain.ToolchainPrivate;
86 import org.codehaus.plexus.interpolation.InterpolationException;
87 import org.codehaus.plexus.interpolation.Interpolator;
88 import org.codehaus.plexus.interpolation.MapBasedValueSource;
89 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
90 import org.codehaus.plexus.util.DirectoryScanner;
91 import org.codehaus.plexus.util.FileUtils;
92 import org.codehaus.plexus.util.IOUtil;
93 import org.codehaus.plexus.util.InterpolationFilterReader;
94 import org.codehaus.plexus.util.ReaderFactory;
95 import org.codehaus.plexus.util.ReflectionUtils;
96 import org.codehaus.plexus.util.StringUtils;
97 import org.codehaus.plexus.util.WriterFactory;
98 import org.codehaus.plexus.util.cli.CommandLineException;
99 import org.codehaus.plexus.util.cli.CommandLineUtils;
100 import org.codehaus.plexus.util.cli.Commandline;
101 import org.codehaus.plexus.util.cli.StreamConsumer;
102 import org.codehaus.plexus.util.xml.Xpp3Dom;
103 import org.codehaus.plexus.util.xml.Xpp3DomWriter;
104
105 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
106
107
108
109
110
111
112
113 public abstract class AbstractInvokerMojo extends AbstractMojo {
114
115
116
117 private static final int RESULT_COLUMN = 60;
118
119
120
121
122
123
124 @Parameter(property = "invoker.skip", defaultValue = "false")
125 private boolean skipInvocation;
126
127
128
129
130
131
132
133
134
135 @Parameter(defaultValue = "false")
136 protected boolean suppressSummaries;
137
138
139
140
141
142
143 @Parameter(property = "invoker.streamLogs", defaultValue = "false")
144 private boolean streamLogs;
145
146
147
148
149
150
151
152
153 @Parameter(property = "invoker.localRepositoryPath", defaultValue = "${settings.localRepository}")
154 private File localRepositoryPath;
155
156
157
158
159
160
161 @Parameter(property = "invoker.projectsDirectory", defaultValue = "${basedir}/src/it/")
162 private File projectsDirectory;
163
164
165
166
167
168
169
170
171 @Parameter(property = "invoker.reportsDirectory", defaultValue = "${project.build.directory}/invoker-reports")
172 private File reportsDirectory;
173
174
175
176
177
178
179 @Parameter(property = "invoker.disableReports", defaultValue = "false")
180 private boolean disableReports;
181
182
183
184
185
186
187
188
189
190
191 @Parameter(property = "invoker.cloneProjectsTo")
192 private File cloneProjectsTo;
193
194
195
196
197
198
199
200
201
202
203
204
205 @Parameter(defaultValue = "false")
206 private boolean cloneAllFiles;
207
208
209
210
211
212
213
214 @Parameter(defaultValue = "true")
215 private boolean cloneClean;
216
217
218
219
220
221
222 @Parameter(property = "invoker.pom")
223 private File pom;
224
225
226
227
228
229
230
231
232
233
234
235
236
237 @Parameter
238 private List<String> pomIncludes = Collections.singletonList("*/pom.xml");
239
240
241
242
243
244
245
246
247 @Parameter
248 private List<String> pomExcludes = Collections.emptyList();
249
250
251
252
253
254
255
256
257
258
259 @Parameter
260 private List<String> setupIncludes = Collections.singletonList("setup*/pom.xml");
261
262
263
264
265
266
267 @Parameter
268 private List<String> goals = Collections.singletonList("package");
269
270
271
272
273
274
275
276
277
278
279
280
281
282 @Parameter(property = "invoker.selectorScript", defaultValue = "selector")
283 private String selectorScript;
284
285
286
287
288
289
290
291
292
293
294
295 @Parameter(property = "invoker.preBuildHookScript", defaultValue = "prebuild")
296 private String preBuildHookScript;
297
298
299
300
301
302
303
304
305
306
307 @Parameter(property = "invoker.postBuildHookScript", defaultValue = "postbuild")
308 private String postBuildHookScript;
309
310
311
312
313
314
315 @Parameter(property = "invoker.testPropertiesFile", defaultValue = "test.properties")
316 private String testPropertiesFile;
317
318
319
320
321
322
323 @Parameter
324 private Map<String, String> properties;
325
326
327
328
329
330
331 @Parameter(property = "invoker.showErrors", defaultValue = "false")
332 private boolean showErrors;
333
334
335
336
337
338
339 @Parameter(property = "invoker.debug", defaultValue = "false")
340 private boolean debug;
341
342
343
344
345
346
347 @Parameter(property = "invoker.quiet", defaultValue = "false")
348 private boolean quiet;
349
350
351
352
353
354
355 @Parameter(property = "invoker.noLog", defaultValue = "false")
356 private boolean noLog;
357
358
359
360
361
362
363
364 @Parameter
365 private File logDirectory;
366
367
368
369
370
371
372 @Parameter
373 private List<String> profiles;
374
375
376
377
378
379
380 @Parameter
381 private Map<String, String> filterProperties;
382
383
384
385
386
387
388
389
390
391
392
393
394 @Parameter(property = "invoker.test")
395 private String invokerTest;
396
397
398
399
400
401
402
403
404 @Parameter(property = "invoker.settingsFile")
405 private File settingsFile;
406
407
408
409
410
411
412
413 @Parameter(property = "invoker.mavenOpts")
414 private String mavenOpts;
415
416
417
418
419
420
421
422 @Parameter(property = "invoker.mavenHome")
423 private File mavenHome;
424
425
426
427
428
429
430
431 @Parameter(property = "invoker.mavenExecutable")
432 private File mavenExecutable;
433
434
435
436
437
438
439
440 @Parameter(property = "invoker.javaHome")
441 private File javaHome;
442
443
444
445
446
447
448 @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}")
449 private String encoding;
450
451
452
453
454
455
456
457
458
459
460 @Parameter(property = "invoker.addTestClassPath", defaultValue = "false")
461 private boolean addTestClassPath;
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604 @Parameter(property = "invoker.invokerPropertiesFile", defaultValue = "invoker.properties")
605 private String invokerPropertiesFile;
606
607
608
609
610
611
612 @Parameter(property = "invoker.showVersion", defaultValue = "false")
613 private boolean showVersion;
614
615
616
617
618
619
620
621
622
623
624 @Parameter(property = "invoker.parallelThreads", defaultValue = "1")
625 private String parallelThreads;
626
627
628
629
630
631
632
633 @Parameter(property = "invoker.mergeUserSettings", defaultValue = "false")
634 private boolean mergeUserSettings;
635
636
637
638
639
640
641 @Parameter
642 private Map<String, String> environmentVariables;
643
644
645
646
647
648
649 @Parameter
650 private Map<String, String> scriptVariables;
651
652
653
654
655
656 @Parameter(defaultValue = "0", property = "invoker.timeoutInSeconds")
657 private int timeoutInSeconds;
658
659
660
661
662
663 @Parameter(defaultValue = "false", property = "invoker.writeJunitReport")
664 private boolean writeJunitReport;
665
666
667
668
669
670 @Parameter(defaultValue = "maven.invoker.it", property = "invoker.junitPackageName")
671 private String junitPackageName = "maven.invoker.it";
672
673
674
675
676
677
678
679 @Parameter(defaultValue = "false", property = "invoker.updateOnly")
680 private boolean updateOnly = false;
681
682
683
684
685
686
687
688 @Parameter(defaultValue = "false", property = "invoker.updateSnapshots")
689 private boolean updateSnapshots;
690
691
692
693
694
695
696 private ScriptRunner scriptRunner;
697
698
699
700
701
702
703 private String filteredPomPrefix = "interpolated-";
704
705
706
707
708 private final DecimalFormat secFormat = new DecimalFormat("(0.0 s)", new DecimalFormatSymbols(Locale.ENGLISH));
709
710
711
712
713 private String actualMavenVersion;
714
715
716
717 @Parameter(property = "plugin.artifacts", required = true, readonly = true)
718 private List<Artifact> pluginArtifacts;
719
720 @Parameter(defaultValue = "${project.testClasspathElements}", readonly = true)
721 private List<String> testClassPath;
722
723 @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
724 private MojoExecution mojoExecution;
725
726 @Parameter(defaultValue = "${project}", readonly = true, required = true)
727 private MavenProject project;
728
729 @Parameter(defaultValue = "${session}", readonly = true, required = true)
730 private MavenSession session;
731
732 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
733 private Settings settings;
734
735 @Component
736 private Invoker invoker;
737
738 @Component
739 private SettingsBuilder settingsBuilder;
740
741 @Component
742 private ToolchainManagerPrivate toolchainManagerPrivate;
743
744
745
746
747
748
749
750 public void execute() throws MojoExecutionException, MojoFailureException {
751 if (skipInvocation) {
752 getLog().info("Skipping invocation per configuration."
753 + " If this is incorrect, ensure the skipInvocation parameter is not set to true.");
754 return;
755 }
756
757 if (StringUtils.isEmpty(encoding)) {
758 getLog().warn("File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
759 + ", i.e. build is platform dependent!");
760 }
761
762
763 if (!disableReports) {
764 setupReportsFolder();
765 }
766
767 List<BuildJob> buildJobs;
768 if (pom == null) {
769 try {
770 buildJobs = getBuildJobs();
771 } catch (final IOException e) {
772 throw new MojoExecutionException(
773 "Error retrieving POM list from includes, " + "excludes, and projects directory. Reason: "
774 + e.getMessage(),
775 e);
776 }
777 } else {
778 try {
779 projectsDirectory = pom.getCanonicalFile().getParentFile();
780 } catch (IOException e) {
781 throw new MojoExecutionException(
782 "Failed to discover projectsDirectory from " + "pom File parameter. Reason: " + e.getMessage(),
783 e);
784 }
785
786 buildJobs = Collections.singletonList(new BuildJob(pom.getName()));
787 }
788
789 if (buildJobs.isEmpty()) {
790 doFailIfNoProjects();
791
792 getLog().info("No projects were selected for execution.");
793 return;
794 }
795
796 setupActualMavenVersion();
797
798 handleScriptRunnerWithScriptClassPath();
799
800 Collection<String> collectedProjects = new LinkedHashSet<>();
801 for (BuildJob buildJob : buildJobs) {
802 collectProjects(projectsDirectory, buildJob.getProject(), collectedProjects, true);
803 }
804
805 File projectsDir = projectsDirectory;
806
807 if (cloneProjectsTo == null && "maven-plugin".equals(project.getPackaging())) {
808 cloneProjectsTo = new File(project.getBuild().getDirectory(), "its");
809 }
810
811 if (updateOnly) {
812 if (cloneProjectsTo == null) {
813 getLog().warn("updateOnly functionality is not supported without cloning the projects");
814 } else if (lastModifiedRecursive(projectsDirectory) <= lastModifiedRecursive(cloneProjectsTo)) {
815 getLog().debug("Skipping invocation as cloned projects are up-to-date "
816 + "and updateOnly parameter is set to true.");
817 return;
818 } else {
819 getLog().debug("Cloned projects are out of date");
820 }
821 }
822
823 if (cloneProjectsTo != null) {
824 cloneProjects(collectedProjects);
825 projectsDir = cloneProjectsTo;
826 } else {
827 getLog().warn("Filtering of parent/child POMs is not supported without cloning the projects");
828 }
829
830
831 List<BuildJob> setupBuildJobs = getSetupJobs(buildJobs);
832
833 if (!setupBuildJobs.isEmpty()) {
834
835
836
837 getLog().info("Running " + setupBuildJobs.size() + " setup job" + ((setupBuildJobs.size() < 2) ? "" : "s")
838 + ":");
839 runBuilds(projectsDir, setupBuildJobs, 1);
840 getLog().info("Setup done.");
841 }
842
843 List<BuildJob> nonSetupBuildJobs = getNonSetupJobs(buildJobs);
844
845 if (setupBuildJobs.isEmpty() || setupBuildJobs.stream().allMatch(BuildJob::isNotError)) {
846
847
848 runBuilds(projectsDir, nonSetupBuildJobs, getParallelThreadsCount());
849 } else {
850 for (BuildJob buildJob : nonSetupBuildJobs) {
851 buildJob.setResult(BuildJob.Result.SKIPPED);
852 buildJob.setFailureMessage("Skipped due to setup job(s) failure");
853 writeBuildReport(buildJob);
854 }
855 }
856
857 writeSummaryFile(buildJobs);
858 processResults(new InvokerSession(buildJobs));
859 }
860
861 private void setupActualMavenVersion() throws MojoExecutionException {
862 if (mavenHome != null) {
863 try {
864 actualMavenVersion = SelectorUtils.getMavenVersion(mavenHome);
865 } catch (IOException e) {
866 throw new MojoExecutionException(e.getMessage(), e);
867 }
868 } else {
869 actualMavenVersion = SelectorUtils.getMavenVersion();
870 }
871 }
872
873
874
875
876
877
878
879 private long lastModifiedRecursive(File file) {
880 long lastModified = file.lastModified();
881
882 final File[] entries = file.listFiles();
883
884 if (entries != null) {
885 for (File entry : entries) {
886 lastModified = Math.max(lastModified, lastModifiedRecursive(entry));
887 }
888 }
889
890 return lastModified;
891 }
892
893
894
895
896
897
898 private void setupReportsFolder() throws MojoExecutionException {
899
900 if (reportsDirectory.exists()) {
901 try {
902 FileUtils.deleteDirectory(reportsDirectory);
903 } catch (IOException e) {
904 throw new MojoExecutionException(
905 "Failure while trying to delete " + reportsDirectory.getAbsolutePath(), e);
906 }
907 }
908 if (!reportsDirectory.mkdirs()) {
909 throw new MojoExecutionException("Failure while creating the " + reportsDirectory.getAbsolutePath());
910 }
911 }
912
913 private List<BuildJob> getSetupJobs(List<BuildJob> buildJobs) {
914 return buildJobs.stream()
915 .filter(buildJob -> buildJob.getType().equals(BuildJob.Type.SETUP))
916 .collect(Collectors.toList());
917 }
918
919 private List<BuildJob> getNonSetupJobs(List<BuildJob> buildJobs) {
920 return buildJobs.stream()
921 .filter(buildJob -> !buildJob.getType().equals(BuildJob.Type.SETUP))
922 .collect(Collectors.toList());
923 }
924
925 private void handleScriptRunnerWithScriptClassPath() {
926 final List<String> scriptClassPath;
927 if (addTestClassPath) {
928 scriptClassPath = new ArrayList<>(testClassPath);
929 for (Artifact pluginArtifact : pluginArtifacts) {
930 scriptClassPath.remove(pluginArtifact.getFile().getAbsolutePath());
931 }
932 } else {
933 scriptClassPath = null;
934 }
935 scriptRunner = new ScriptRunner();
936 scriptRunner.setScriptEncoding(encoding);
937 scriptRunner.setGlobalVariable("localRepositoryPath", localRepositoryPath);
938 scriptRunner.setGlobalVariable("mavenVersion", actualMavenVersion);
939 if (scriptVariables != null) {
940 scriptVariables.forEach((key, value) -> scriptRunner.setGlobalVariable(key, value));
941 }
942 scriptRunner.setClassPath(scriptClassPath);
943 }
944
945 private void writeSummaryFile(List<BuildJob> buildJobs) throws MojoExecutionException {
946
947 File summaryReportFile = new File(reportsDirectory, "invoker-summary.txt");
948
949 try (Writer writer = new BufferedWriter(new FileWriter(summaryReportFile))) {
950 for (BuildJob buildJob : buildJobs) {
951 if (!BuildJob.Result.SUCCESS.equals(buildJob.getResult())) {
952 writer.append(buildJob.getResult());
953 writer.append(" [");
954 writer.append(buildJob.getProject());
955 writer.append("] ");
956 if (buildJob.getFailureMessage() != null) {
957 writer.append(" ");
958 writer.append(buildJob.getFailureMessage());
959 }
960 writer.append("\n");
961 }
962 }
963 } catch (IOException e) {
964 throw new MojoExecutionException("Failed to write summary report " + summaryReportFile, e);
965 }
966 }
967
968 protected void doFailIfNoProjects() throws MojoFailureException {
969
970 }
971
972
973
974
975
976
977
978
979 abstract void processResults(InvokerSession invokerSession) throws MojoFailureException;
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995 private void collectProjects(
996 File projectsDir, String projectPath, Collection<String> projectPaths, boolean included)
997 throws MojoExecutionException {
998 projectPath = projectPath.replace('\\', '/');
999 File pomFile = new File(projectsDir, projectPath);
1000 if (pomFile.isDirectory()) {
1001 pomFile = new File(pomFile, "pom.xml");
1002 if (!pomFile.exists()) {
1003 if (included) {
1004 projectPaths.add(projectPath);
1005 }
1006 return;
1007 }
1008 if (!projectPath.endsWith("/")) {
1009 projectPath += '/';
1010 }
1011 projectPath += "pom.xml";
1012 } else if (!pomFile.isFile()) {
1013 return;
1014 }
1015 if (!projectPaths.add(projectPath)) {
1016 return;
1017 }
1018 getLog().debug("Collecting parent/child projects of " + projectPath);
1019
1020 Model model = PomUtils.loadPom(pomFile);
1021
1022 try {
1023 String projectsRoot = projectsDir.getCanonicalPath();
1024 String projectDir = pomFile.getParent();
1025
1026 String parentPath = "../pom.xml";
1027 if (model.getParent() != null
1028 && StringUtils.isNotEmpty(model.getParent().getRelativePath())) {
1029 parentPath = model.getParent().getRelativePath();
1030 }
1031 String parent = relativizePath(new File(projectDir, parentPath), projectsRoot);
1032 if (parent != null) {
1033 collectProjects(projectsDir, parent, projectPaths, false);
1034 }
1035
1036 Collection<String> modulePaths = new LinkedHashSet<>(model.getModules());
1037
1038 model.getProfiles().forEach(profile -> modulePaths.addAll(profile.getModules()));
1039
1040 for (String modulePath : modulePaths) {
1041 String module = relativizePath(new File(projectDir, modulePath), projectsRoot);
1042 if (module != null) {
1043 collectProjects(projectsDir, module, projectPaths, false);
1044 }
1045 }
1046 } catch (IOException e) {
1047 throw new MojoExecutionException("Failed to analyze POM: " + pomFile, e);
1048 }
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059 private void cloneProjects(Collection<String> projectPaths) throws MojoExecutionException {
1060 if (!cloneProjectsTo.mkdirs() && cloneClean) {
1061 try {
1062 FileUtils.cleanDirectory(cloneProjectsTo);
1063 } catch (IOException e) {
1064 throw new MojoExecutionException(
1065 "Could not clean the cloneProjectsTo directory. Reason: " + e.getMessage(), e);
1066 }
1067 }
1068
1069
1070 Collection<String> dirs = new LinkedHashSet<>();
1071 for (String projectPath : projectPaths) {
1072 if (!new File(projectsDirectory, projectPath).isDirectory()) {
1073 projectPath = getParentPath(projectPath);
1074 }
1075 dirs.add(projectPath);
1076 }
1077
1078 boolean filter;
1079
1080
1081 try {
1082 filter = !cloneProjectsTo.getCanonicalFile().equals(projectsDirectory.getCanonicalFile());
1083
1084 List<String> clonedSubpaths = new ArrayList<>();
1085
1086 for (String subpath : dirs) {
1087
1088 if (!".".equals(subpath) && dirs.contains(getParentPath(subpath))) {
1089 continue;
1090 }
1091
1092
1093 if (!alreadyCloned(subpath, clonedSubpaths)) {
1094
1095 if (".".equals(subpath)) {
1096 String cloneSubdir = relativizePath(cloneProjectsTo, projectsDirectory.getCanonicalPath());
1097
1098
1099 if (cloneSubdir != null) {
1100 File temp = Files.createTempDirectory("pre-invocation-clone.")
1101 .toFile();
1102
1103 copyDirectoryStructure(projectsDirectory, temp);
1104
1105 FileUtils.deleteDirectory(new File(temp, cloneSubdir));
1106
1107 copyDirectoryStructure(temp, cloneProjectsTo);
1108 } else {
1109 copyDirectoryStructure(projectsDirectory, cloneProjectsTo);
1110 }
1111 } else {
1112 File srcDir = new File(projectsDirectory, subpath);
1113 File dstDir = new File(cloneProjectsTo, subpath);
1114 copyDirectoryStructure(srcDir, dstDir);
1115 }
1116
1117 clonedSubpaths.add(subpath);
1118 }
1119 }
1120 } catch (IOException e) {
1121 throw new MojoExecutionException(
1122 "Failed to clone projects from: " + projectsDirectory + " to: " + cloneProjectsTo + ". Reason: "
1123 + e.getMessage(),
1124 e);
1125 }
1126
1127
1128 if (filter) {
1129 for (String projectPath : projectPaths) {
1130 File pomFile = new File(cloneProjectsTo, projectPath);
1131 if (pomFile.isFile()) {
1132 buildInterpolatedFile(pomFile, pomFile);
1133 }
1134
1135
1136
1137
1138 File baseDir = pomFile.getParentFile();
1139 File mvnDir = new File(baseDir, ".mvn");
1140 if (mvnDir.isDirectory()) {
1141 File extensionsFile = new File(mvnDir, "extensions.xml");
1142 if (extensionsFile.isFile()) {
1143 buildInterpolatedFile(extensionsFile, extensionsFile);
1144 }
1145 }
1146
1147 }
1148 filteredPomPrefix = null;
1149 }
1150 }
1151
1152
1153
1154
1155
1156
1157
1158 private String getParentPath(String path) {
1159 int lastSep = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
1160 return (lastSep < 0) ? "." : path.substring(0, lastSep);
1161 }
1162
1163
1164
1165
1166
1167
1168
1169
1170 private void copyDirectoryStructure(File sourceDir, File destDir) throws IOException {
1171 DirectoryScanner scanner = new DirectoryScanner();
1172 scanner.setBasedir(sourceDir);
1173 if (!cloneAllFiles) {
1174 scanner.addDefaultExcludes();
1175 }
1176 scanner.scan();
1177
1178
1179
1180
1181 destDir.mkdirs();
1182
1183 FileUtils.mkDirs(sourceDir, scanner.getIncludedDirectories(), destDir);
1184
1185 for (String includedFile : scanner.getIncludedFiles()) {
1186 File sourceFile = new File(sourceDir, includedFile);
1187 File destFile = new File(destDir, includedFile);
1188 FileUtils.copyFile(sourceFile, destFile);
1189
1190
1191 destFile.setWritable(true);
1192 }
1193 }
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204 static boolean alreadyCloned(String subpath, List<String> clonedSubpaths) {
1205 for (String path : clonedSubpaths) {
1206 if (".".equals(path) || subpath.equals(path) || subpath.startsWith(path + File.separator)) {
1207 return true;
1208 }
1209 }
1210
1211 return false;
1212 }
1213
1214
1215
1216
1217
1218
1219
1220
1221 private void runBuilds(final File projectsDir, List<BuildJob> buildJobs, int runWithParallelThreads)
1222 throws MojoExecutionException {
1223 if (!localRepositoryPath.exists()) {
1224 localRepositoryPath.mkdirs();
1225 }
1226
1227
1228
1229
1230
1231 File interpolatedSettingsFile = interpolateSettings(settingsFile);
1232
1233 final File mergedSettingsFile = mergeSettings(interpolatedSettingsFile);
1234
1235 final CharSequence actualJreVersion;
1236
1237 if (javaHome != null) {
1238 actualJreVersion = resolveExternalJreVersion();
1239 } else {
1240 actualJreVersion = SelectorUtils.getJreVersion();
1241 }
1242
1243 final Path projectsPath = this.projectsDirectory.toPath();
1244
1245 Set<Path> folderGroupSet = new HashSet<>();
1246 folderGroupSet.add(Paths.get("."));
1247 for (BuildJob buildJob : buildJobs) {
1248 Path p = Paths.get(buildJob.getProject());
1249
1250 if (Files.isRegularFile(projectsPath.resolve(p))) {
1251 p = p.getParent();
1252 }
1253
1254 if (p != null) {
1255 p = p.getParent();
1256 }
1257
1258 while (p != null && folderGroupSet.add(p)) {
1259 p = p.getParent();
1260 }
1261 }
1262
1263 List<Path> folderGroup = new ArrayList<>(folderGroupSet);
1264 Collections.sort(folderGroup);
1265
1266 final Map<Path, Properties> globalInvokerProperties = new HashMap<>();
1267
1268 for (Path path : folderGroup) {
1269 Properties ancestorProperties =
1270 globalInvokerProperties.get(projectsPath.resolve(path).getParent());
1271
1272 Path currentInvokerProperties = projectsPath.resolve(path).resolve(invokerPropertiesFile);
1273
1274 Properties currentProperties;
1275 if (Files.isRegularFile(currentInvokerProperties)) {
1276 if (ancestorProperties != null) {
1277 currentProperties = new Properties(ancestorProperties);
1278
1279 } else {
1280 currentProperties = new Properties();
1281 }
1282 } else {
1283 currentProperties = ancestorProperties;
1284 }
1285
1286 if (Files.isRegularFile(currentInvokerProperties)) {
1287 try (InputStream in = new FileInputStream(currentInvokerProperties.toFile())) {
1288 currentProperties.load(in);
1289 } catch (IOException e) {
1290 throw new MojoExecutionException("Failed to read invoker properties: " + currentInvokerProperties);
1291 }
1292 }
1293
1294 if (currentProperties != null) {
1295 globalInvokerProperties.put(projectsPath.resolve(path).normalize(), currentProperties);
1296 }
1297 }
1298
1299 try {
1300 if (runWithParallelThreads > 1) {
1301 getLog().info("use parallelThreads " + runWithParallelThreads);
1302 }
1303
1304 JobExecutor jobExecutor = new JobExecutor(buildJobs, runWithParallelThreads);
1305 jobExecutor.forEach(job -> {
1306 Path ancestorFolder = getAncestorFolder(projectsPath.resolve(job.getProject()));
1307
1308 runBuild(
1309 projectsDir,
1310 job,
1311 mergedSettingsFile,
1312 javaHome,
1313 actualJreVersion,
1314 globalInvokerProperties.get(ancestorFolder));
1315 });
1316 } finally {
1317 if (interpolatedSettingsFile != null && cloneProjectsTo == null) {
1318 interpolatedSettingsFile.delete();
1319 }
1320 if (mergedSettingsFile != null && mergedSettingsFile.exists()) {
1321 mergedSettingsFile.delete();
1322 }
1323 }
1324 }
1325
1326 private Path getAncestorFolder(Path p) {
1327 Path ancestor = p;
1328 if (Files.isRegularFile(ancestor)) {
1329 ancestor = ancestor.getParent();
1330 }
1331 if (ancestor != null) {
1332 ancestor = ancestor.getParent();
1333 }
1334 return ancestor;
1335 }
1336
1337
1338
1339
1340
1341
1342
1343
1344 private File interpolateSettings(File settingsFile) throws MojoExecutionException {
1345 File interpolatedSettingsFile = null;
1346 if (settingsFile != null) {
1347 if (cloneProjectsTo != null) {
1348 interpolatedSettingsFile = new File(cloneProjectsTo, "interpolated-" + settingsFile.getName());
1349 } else {
1350 interpolatedSettingsFile =
1351 new File(settingsFile.getParentFile(), "interpolated-" + settingsFile.getName());
1352 }
1353 buildInterpolatedFile(settingsFile, interpolatedSettingsFile);
1354 }
1355 return interpolatedSettingsFile;
1356 }
1357
1358
1359
1360
1361
1362
1363
1364
1365 private File mergeSettings(File interpolatedSettingsFile) throws MojoExecutionException {
1366 File mergedSettingsFile;
1367 Settings mergedSettings = this.settings;
1368 if (mergeUserSettings) {
1369 if (interpolatedSettingsFile != null) {
1370
1371 try {
1372 SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
1373 request.setGlobalSettingsFile(interpolatedSettingsFile);
1374
1375 Settings dominantSettings = settingsBuilder.build(request).getEffectiveSettings();
1376 Settings recessiveSettings = cloneSettings();
1377 SettingsUtils.merge(dominantSettings, recessiveSettings, TrackableBase.USER_LEVEL);
1378
1379 mergedSettings = dominantSettings;
1380 getLog().debug("Merged specified settings file with settings of invoking process");
1381 } catch (SettingsBuildingException e) {
1382 throw new MojoExecutionException("Could not read specified settings file", e);
1383 }
1384 }
1385 }
1386
1387 if (this.settingsFile != null && !mergeUserSettings) {
1388 mergedSettingsFile = interpolatedSettingsFile;
1389 } else {
1390 try {
1391 mergedSettingsFile = writeMergedSettingsFile(mergedSettings);
1392 } catch (IOException e) {
1393 throw new MojoExecutionException("Could not create temporary file for invoker settings.xml", e);
1394 }
1395 }
1396 return mergedSettingsFile;
1397 }
1398
1399 private File writeMergedSettingsFile(Settings mergedSettings) throws IOException {
1400 File mergedSettingsFile;
1401 mergedSettingsFile = Files.createTempFile("invoker-settings", ".xml").toFile();
1402
1403 SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer();
1404
1405 try (FileWriter fileWriter = new FileWriter(mergedSettingsFile)) {
1406 settingsWriter.write(fileWriter, mergedSettings);
1407 }
1408
1409 if (getLog().isDebugEnabled()) {
1410 getLog().debug("Created temporary file for invoker settings.xml: " + mergedSettingsFile.getAbsolutePath());
1411 }
1412 return mergedSettingsFile;
1413 }
1414
1415 private Settings cloneSettings() {
1416 Settings recessiveSettings = SettingsUtils.copySettings(this.settings);
1417
1418
1419 resetSourceLevelSet(recessiveSettings);
1420 for (org.apache.maven.settings.Mirror mirror : recessiveSettings.getMirrors()) {
1421 resetSourceLevelSet(mirror);
1422 }
1423 for (org.apache.maven.settings.Server server : recessiveSettings.getServers()) {
1424 resetSourceLevelSet(server);
1425 }
1426 for (org.apache.maven.settings.Proxy proxy : recessiveSettings.getProxies()) {
1427 resetSourceLevelSet(proxy);
1428 }
1429 for (org.apache.maven.settings.Profile profile : recessiveSettings.getProfiles()) {
1430 resetSourceLevelSet(profile);
1431 }
1432
1433 return recessiveSettings;
1434 }
1435
1436 private void resetSourceLevelSet(org.apache.maven.settings.TrackableBase trackable) {
1437 try {
1438 ReflectionUtils.setVariableValueInObject(trackable, "sourceLevelSet", Boolean.FALSE);
1439 getLog().debug("sourceLevelSet: "
1440 + ReflectionUtils.getValueIncludingSuperclasses("sourceLevelSet", trackable));
1441 } catch (IllegalAccessException e) {
1442
1443 }
1444 }
1445
1446 private CharSequence resolveExternalJreVersion() {
1447 Artifact pluginArtifact =
1448 mojoExecution.getMojoDescriptor().getPluginDescriptor().getPluginArtifact();
1449 pluginArtifact.getFile();
1450
1451 Commandline commandLine = new Commandline();
1452 commandLine.setExecutable(new File(javaHome, "bin/java").getAbsolutePath());
1453 commandLine.createArg().setValue("-cp");
1454 commandLine.createArg().setFile(pluginArtifact.getFile());
1455 commandLine.createArg().setValue(SystemPropertyPrinter.class.getName());
1456 commandLine.createArg().setValue("java.version");
1457
1458 final StringBuilder actualJreVersion = new StringBuilder();
1459 StreamConsumer consumer = actualJreVersion::append;
1460 try {
1461 CommandLineUtils.executeCommandLine(commandLine, consumer, null);
1462 } catch (CommandLineException e) {
1463 getLog().warn(e.getMessage());
1464 }
1465 return actualJreVersion;
1466 }
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477 private File interpolatePomFile(File pomFile, File basedir) throws MojoExecutionException {
1478 File interpolatedPomFile = null;
1479 if (pomFile != null) {
1480 if (StringUtils.isNotEmpty(filteredPomPrefix)) {
1481 interpolatedPomFile = new File(basedir, filteredPomPrefix + pomFile.getName());
1482 buildInterpolatedFile(pomFile, interpolatedPomFile);
1483 } else {
1484 interpolatedPomFile = pomFile;
1485 }
1486 }
1487 return interpolatedPomFile;
1488 }
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500 private void runBuild(
1501 File projectsDir,
1502 BuildJob buildJob,
1503 File settingsFile,
1504 File actualJavaHome,
1505 CharSequence actualJreVersion,
1506 Properties globalInvokerProperties)
1507 throws MojoExecutionException {
1508
1509 File pomFile = new File(projectsDir, buildJob.getProject());
1510 File basedir;
1511 if (pomFile.isDirectory()) {
1512 basedir = pomFile;
1513 pomFile = new File(basedir, "pom.xml");
1514 if (!pomFile.exists()) {
1515 pomFile = null;
1516 } else {
1517 buildJob.setProject(buildJob.getProject() + File.separator + "pom.xml");
1518 }
1519 } else {
1520 basedir = pomFile.getParentFile();
1521 }
1522
1523 File interpolatedPomFile = interpolatePomFile(pomFile, basedir);
1524
1525
1526 getLog().info(buffer().a("Building: ").strong(buildJob.getProject()).toString());
1527
1528 InvokerProperties invokerProperties = getInvokerProperties(basedir, globalInvokerProperties);
1529
1530
1531 buildJob.setName(invokerProperties.getJobName());
1532 buildJob.setDescription(invokerProperties.getJobDescription());
1533
1534 try {
1535 int selection = getSelection(invokerProperties, actualJreVersion);
1536 if (selection == 0) {
1537 long milliseconds = System.currentTimeMillis();
1538 boolean executed;
1539
1540 FileLogger buildLogger = setupBuildLogFile(basedir);
1541 if (buildLogger != null) {
1542 buildJob.setBuildlog(buildLogger.getOutputFile().getAbsolutePath());
1543 }
1544
1545 try {
1546 executed = runBuild(
1547 basedir, interpolatedPomFile, settingsFile, actualJavaHome, invokerProperties, buildLogger);
1548 } finally {
1549 milliseconds = System.currentTimeMillis() - milliseconds;
1550 buildJob.setTime(milliseconds / 1000.0);
1551
1552 if (buildLogger != null) {
1553 buildLogger.close();
1554 }
1555 }
1556
1557 if (executed) {
1558 buildJob.setResult(BuildJob.Result.SUCCESS);
1559
1560 if (!suppressSummaries) {
1561 getLog().info(pad(buildJob).success("SUCCESS").a(' ') + formatTime(buildJob.getTime()));
1562 }
1563 } else {
1564 buildJob.setResult(BuildJob.Result.SKIPPED);
1565
1566 if (!suppressSummaries) {
1567 getLog().info(pad(buildJob).warning("SKIPPED").a(' ') + formatTime(buildJob.getTime()));
1568 }
1569 }
1570 } else {
1571 buildJob.setResult(BuildJob.Result.SKIPPED);
1572
1573 List<String> messages = new ArrayList<>();
1574
1575 if (selection == Selector.SELECTOR_MULTI) {
1576 messages.add("non-matching selectors");
1577 } else {
1578 if ((selection & Selector.SELECTOR_MAVENVERSION) != 0) {
1579 messages.add("Maven version");
1580 }
1581 if ((selection & Selector.SELECTOR_JREVERSION) != 0) {
1582 messages.add("JRE version");
1583 }
1584 if ((selection & Selector.SELECTOR_OSFAMILY) != 0) {
1585 messages.add("OS");
1586 }
1587 if ((selection & Selector.SELECTOR_TOOLCHAIN) != 0) {
1588 messages.add("Toolchain");
1589 }
1590 }
1591
1592 String message = String.join(", ", messages);
1593 if (!suppressSummaries) {
1594 getLog().info(pad(buildJob).warning("SKIPPED") + " due to " + message);
1595 }
1596
1597
1598
1599 buildJob.setFailureMessage("Skipped due to " + message);
1600 }
1601 } catch (RunFailureException e) {
1602 buildJob.setResult(e.getType());
1603 buildJob.setFailureMessage(e.getMessage());
1604
1605 if (!suppressSummaries) {
1606 getLog().info(" " + e.getMessage());
1607 getLog().info(pad(buildJob).failure("FAILED").a(' ') + formatTime(buildJob.getTime()));
1608 }
1609 } finally {
1610 deleteInterpolatedPomFile(interpolatedPomFile);
1611 writeBuildReport(buildJob);
1612 }
1613 }
1614
1615 private MessageBuilder pad(BuildJob buildJob) {
1616 MessageBuilder buffer = buffer(128);
1617
1618 buffer.a(" ");
1619 buffer.a(buildJob.getProject());
1620
1621 int l = 10 + buildJob.getProject().length();
1622
1623 if (l < RESULT_COLUMN) {
1624 buffer.a(' ');
1625 l++;
1626
1627 if (l < RESULT_COLUMN) {
1628 for (int i = RESULT_COLUMN - l; i > 0; i--) {
1629 buffer.a('.');
1630 }
1631 }
1632 }
1633
1634 return buffer.a(' ');
1635 }
1636
1637
1638
1639
1640
1641
1642 private void deleteInterpolatedPomFile(File interpolatedPomFile) {
1643 if (interpolatedPomFile != null && StringUtils.isNotEmpty(filteredPomPrefix)) {
1644 interpolatedPomFile.delete();
1645 }
1646 }
1647
1648
1649
1650
1651
1652
1653
1654
1655 private int getSelection(InvokerProperties invokerProperties, CharSequence actualJreVersion) {
1656 return new Selector(actualMavenVersion, actualJreVersion.toString(), getToolchainPrivateManager())
1657 .getSelection(invokerProperties);
1658 }
1659
1660 private ToolchainPrivateManager getToolchainPrivateManager() {
1661 return new ToolchainPrivateManager(toolchainManagerPrivate, session);
1662 }
1663
1664
1665
1666
1667
1668
1669
1670 private void writeBuildReport(BuildJob buildJob) throws MojoExecutionException {
1671 if (disableReports) {
1672 return;
1673 }
1674
1675 String safeFileName =
1676 buildJob.getProject().replace('/', '_').replace('\\', '_').replace(' ', '_');
1677 if (safeFileName.endsWith("_pom.xml")) {
1678 safeFileName = safeFileName.substring(0, safeFileName.length() - "_pom.xml".length());
1679 }
1680
1681 File reportFile = new File(reportsDirectory, "BUILD-" + safeFileName + ".xml");
1682 try (FileOutputStream fos = new FileOutputStream(reportFile);
1683 Writer osw = new OutputStreamWriter(fos, buildJob.getModelEncoding())) {
1684 BuildJobXpp3Writer writer = new BuildJobXpp3Writer();
1685
1686 writer.write(osw, buildJob);
1687 } catch (IOException e) {
1688 throw new MojoExecutionException("Failed to write build report " + reportFile, e);
1689 }
1690
1691 if (writeJunitReport) {
1692 writeJunitReport(buildJob, safeFileName);
1693 }
1694 }
1695
1696 private void writeJunitReport(BuildJob buildJob, String safeFileName) throws MojoExecutionException {
1697 File reportFile = new File(reportsDirectory, "TEST-" + safeFileName + ".xml");
1698 Xpp3Dom testsuite = new Xpp3Dom("testsuite");
1699 testsuite.setAttribute("name", junitPackageName + "." + safeFileName);
1700 testsuite.setAttribute("time", Double.toString(buildJob.getTime()));
1701
1702
1703 testsuite.setAttribute("tests", "1");
1704 testsuite.setAttribute("errors", "0");
1705 testsuite.setAttribute("skipped", "0");
1706 testsuite.setAttribute("failures", "0");
1707
1708 Xpp3Dom testcase = new Xpp3Dom("testcase");
1709 testsuite.addChild(testcase);
1710 switch (buildJob.getResult()) {
1711 case BuildJob.Result.SUCCESS:
1712 break;
1713 case BuildJob.Result.SKIPPED:
1714 testsuite.setAttribute("skipped", "1");
1715
1716 Xpp3Dom skipped = new Xpp3Dom("skipped");
1717 testcase.addChild(skipped);
1718 skipped.setValue(buildJob.getFailureMessage());
1719 break;
1720 case BuildJob.Result.ERROR:
1721 testsuite.setAttribute("errors", "1");
1722 break;
1723 default:
1724 testsuite.setAttribute("failures", "1");
1725
1726 Xpp3Dom failure = new Xpp3Dom("failure");
1727 testcase.addChild(failure);
1728 failure.setAttribute("message", buildJob.getFailureMessage());
1729 }
1730 testcase.setAttribute("classname", junitPackageName + "." + safeFileName);
1731 testcase.setAttribute("name", safeFileName);
1732 testcase.setAttribute("time", Double.toString(buildJob.getTime()));
1733 Xpp3Dom systemOut = new Xpp3Dom("system-out");
1734 testcase.addChild(systemOut);
1735
1736 File buildLogFile = buildJob.getBuildlog() != null ? new File(buildJob.getBuildlog()) : null;
1737
1738 if (buildLogFile != null && buildLogFile.exists()) {
1739 getLog().debug("fileLogger:" + buildLogFile);
1740 try {
1741 systemOut.setValue(FileUtils.fileRead(buildLogFile));
1742 } catch (IOException e) {
1743 throw new MojoExecutionException("Failed to read logfile " + buildLogFile, e);
1744 }
1745 } else {
1746 getLog().debug(safeFileName + "not exists buildLogFile = " + buildLogFile);
1747 }
1748
1749 try (FileOutputStream fos = new FileOutputStream(reportFile);
1750 Writer osw = new OutputStreamWriter(fos, buildJob.getModelEncoding())) {
1751 Xpp3DomWriter.write(osw, testsuite);
1752 } catch (IOException e) {
1753 throw new MojoExecutionException("Failed to write JUnit build report " + reportFile, e);
1754 }
1755 }
1756
1757
1758
1759
1760
1761
1762
1763 private String formatTime(double seconds) {
1764 return secFormat.format(seconds);
1765 }
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782 private boolean runBuild(
1783 File basedir,
1784 File pomFile,
1785 File settingsFile,
1786 File actualJavaHome,
1787 InvokerProperties invokerProperties,
1788 FileLogger logger)
1789 throws MojoExecutionException, RunFailureException {
1790 if (getLog().isDebugEnabled() && !invokerProperties.getProperties().isEmpty()) {
1791 Properties props = invokerProperties.getProperties();
1792 getLog().debug("Using invoker properties:");
1793 for (String key : new TreeSet<>(props.stringPropertyNames())) {
1794 String value = props.getProperty(key);
1795 getLog().debug(" " + key + " = " + value);
1796 }
1797 }
1798
1799 Map<String, Object> context = new LinkedHashMap<>();
1800
1801 boolean selectorResult = true;
1802
1803 try {
1804 try {
1805 scriptRunner.run("selector script", basedir, selectorScript, context, logger);
1806 } catch (ScriptReturnException e) {
1807 selectorResult = false;
1808 return false;
1809 } catch (ScriptException e) {
1810 throw new RunFailureException(BuildJob.Result.ERROR, e);
1811 }
1812
1813 try {
1814 scriptRunner.run("pre-build script", basedir, preBuildHookScript, context, logger);
1815 } catch (ScriptException e) {
1816 throw new RunFailureException(BuildJob.Result.FAILURE_PRE_HOOK, e);
1817 }
1818
1819 for (int invocationIndex = 1; ; invocationIndex++) {
1820 if (invocationIndex > 1 && !invokerProperties.isInvocationDefined(invocationIndex)) {
1821 break;
1822 }
1823
1824 final InvocationRequest request = new DefaultInvocationRequest();
1825
1826 request.setBatchMode(true);
1827
1828
1829 request.setLocalRepositoryDirectory(localRepositoryPath);
1830 request.setShowErrors(showErrors);
1831 request.setShowVersion(showVersion);
1832 request.setJavaHome(actualJavaHome);
1833 request.setMavenHome(mavenHome);
1834 setupLoggerForBuildJob(logger, request);
1835
1836 request.setBaseDirectory(basedir);
1837 request.setPomFile(pomFile);
1838
1839 String customSettingsFile = invokerProperties.getSettingsFile(invocationIndex);
1840 if (customSettingsFile != null) {
1841 File interpolateSettingsFile = interpolateSettings(new File(customSettingsFile));
1842 File mergeSettingsFile = mergeSettings(interpolateSettingsFile);
1843
1844 request.setUserSettingsFile(mergeSettingsFile);
1845 } else {
1846 request.setUserSettingsFile(settingsFile);
1847 }
1848
1849 Properties systemProperties =
1850 getSystemProperties(basedir, invokerProperties.getSystemPropertiesFile(invocationIndex));
1851 request.setProperties(systemProperties);
1852
1853 invokerProperties.configureInvocation(request, invocationIndex);
1854
1855 if (getLog().isDebugEnabled()) {
1856 try {
1857 getLog().debug("Using MAVEN_OPTS: " + request.getMavenOpts());
1858 getLog().debug("Executing: " + new MavenCommandLineBuilder().build(request));
1859 } catch (CommandLineConfigurationException e) {
1860 getLog().debug("Failed to display command line: " + e.getMessage());
1861 }
1862 }
1863
1864 try {
1865 InvocationResult result = invoker.execute(request);
1866 verify(result, invocationIndex, invokerProperties, logger);
1867 } catch (final MavenInvocationException e) {
1868 getLog().debug("Error invoking Maven: " + e.getMessage(), e);
1869 throw new RunFailureException(
1870 "Maven invocation failed. " + e.getMessage(), BuildJob.Result.FAILURE_BUILD);
1871 }
1872 }
1873 } catch (IOException e) {
1874 throw new MojoExecutionException(e.getMessage(), e);
1875 } finally {
1876 if (selectorResult) {
1877 runPostBuildHook(basedir, context, logger);
1878 }
1879 }
1880 return true;
1881 }
1882
1883 int getParallelThreadsCount() {
1884 if (parallelThreads.endsWith("C")) {
1885 double parallelThreadsMultiple =
1886 Double.parseDouble(parallelThreads.substring(0, parallelThreads.length() - 1));
1887 return (int) (parallelThreadsMultiple * Runtime.getRuntime().availableProcessors());
1888 } else {
1889 return Integer.parseInt(parallelThreads);
1890 }
1891 }
1892
1893 private void runPostBuildHook(File basedir, Map<String, Object> context, FileLogger logger)
1894 throws MojoExecutionException, RunFailureException {
1895 try {
1896 scriptRunner.run("post-build script", basedir, postBuildHookScript, context, logger);
1897 } catch (IOException e) {
1898 throw new MojoExecutionException(e.getMessage(), e);
1899 } catch (ScriptException e) {
1900 throw new RunFailureException(e.getMessage(), BuildJob.Result.FAILURE_POST_HOOK, e);
1901 }
1902 }
1903
1904 private void setupLoggerForBuildJob(final FileLogger logger, final InvocationRequest request) {
1905 if (logger != null) {
1906 request.setErrorHandler(logger);
1907 request.setOutputHandler(logger);
1908 }
1909 }
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919 private FileLogger setupBuildLogFile(File basedir) throws MojoExecutionException {
1920 FileLogger logger = null;
1921
1922 if (!noLog) {
1923 Path projectLogDirectory;
1924 if (logDirectory == null) {
1925 projectLogDirectory = basedir.toPath();
1926 } else if (cloneProjectsTo != null) {
1927 projectLogDirectory =
1928 logDirectory.toPath().resolve(cloneProjectsTo.toPath().relativize(basedir.toPath()));
1929 } else {
1930 projectLogDirectory =
1931 logDirectory.toPath().resolve(projectsDirectory.toPath().relativize(basedir.toPath()));
1932 }
1933
1934 try {
1935 if (streamLogs) {
1936 logger = new FileLogger(
1937 projectLogDirectory.resolve("build.log").toFile(), getLog());
1938 } else {
1939 logger = new FileLogger(
1940 projectLogDirectory.resolve("build.log").toFile());
1941 }
1942
1943 getLog().debug("Build log initialized in: " + projectLogDirectory);
1944 } catch (IOException e) {
1945 throw new MojoExecutionException("Error initializing build logfile in: " + projectLogDirectory, e);
1946 }
1947 }
1948
1949 return logger;
1950 }
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961 private Properties getSystemProperties(final File basedir, final String filename) throws MojoExecutionException {
1962 Properties collectedTestProperties = new Properties();
1963
1964 if (properties != null) {
1965
1966 for (Map.Entry<String, String> entry : properties.entrySet()) {
1967 if (entry.getValue() != null) {
1968 collectedTestProperties.put(entry.getKey(), entry.getValue());
1969 }
1970 }
1971 }
1972
1973 File propertiesFile = null;
1974 if (filename != null) {
1975 propertiesFile = new File(basedir, filename);
1976 } else if (testPropertiesFile != null) {
1977 propertiesFile = new File(basedir, testPropertiesFile);
1978 }
1979
1980 if (propertiesFile != null && propertiesFile.isFile()) {
1981
1982 try (InputStream fin = new FileInputStream(propertiesFile)) {
1983 Properties loadedProperties = new Properties();
1984 loadedProperties.load(fin);
1985 collectedTestProperties.putAll(loadedProperties);
1986 } catch (IOException e) {
1987 throw new MojoExecutionException("Error reading system properties from " + propertiesFile);
1988 }
1989 }
1990
1991 return collectedTestProperties;
1992 }
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002 private void verify(
2003 InvocationResult result, int invocationIndex, InvokerProperties invokerProperties, FileLogger logger)
2004 throws RunFailureException {
2005 if (result.getExecutionException() != null) {
2006 throw new RunFailureException(
2007 "The Maven invocation failed. "
2008 + result.getExecutionException().getMessage(),
2009 BuildJob.Result.ERROR);
2010 } else if (!invokerProperties.isExpectedResult(result.getExitCode(), invocationIndex)) {
2011 StringBuilder buffer = new StringBuilder(256);
2012 buffer.append("The build exited with code ")
2013 .append(result.getExitCode())
2014 .append(". ");
2015 if (logger != null) {
2016 buffer.append("See ");
2017 buffer.append(logger.getOutputFile().getAbsolutePath());
2018 buffer.append(" for details.");
2019 } else {
2020 buffer.append("See console output for details.");
2021 }
2022 throw new RunFailureException(buffer.toString(), BuildJob.Result.FAILURE_BUILD);
2023 }
2024 }
2025
2026 private List<String> calculateIncludes() {
2027 if (invokerTest != null) {
2028 String[] testRegexes = StringUtils.split(invokerTest, ",");
2029 return Arrays.stream(testRegexes)
2030 .map(String::trim)
2031 .filter(s -> !s.isEmpty())
2032 .filter(s -> !s.startsWith("!"))
2033 .collect(Collectors.toList());
2034 } else {
2035 Set<String> uniqueIncludes = new HashSet<>();
2036 uniqueIncludes.addAll(pomIncludes);
2037 uniqueIncludes.addAll(setupIncludes);
2038 return new ArrayList<>(uniqueIncludes);
2039 }
2040 }
2041
2042 private List<String> calculateExcludes() throws IOException {
2043 List<String> excludes;
2044
2045 if (invokerTest != null) {
2046 String[] testRegexes = StringUtils.split(invokerTest, ",");
2047 excludes = Arrays.stream(testRegexes)
2048 .map(String::trim)
2049 .filter(s -> !s.isEmpty())
2050 .filter(s -> s.startsWith("!"))
2051 .map(s -> s.substring(1))
2052 .collect(Collectors.toList());
2053 } else {
2054 excludes = pomExcludes != null ? new ArrayList<>(pomExcludes) : new ArrayList<>();
2055 }
2056
2057 if (this.settingsFile != null) {
2058 String exclude = relativizePath(this.settingsFile, projectsDirectory.getCanonicalPath());
2059 if (exclude != null) {
2060 excludes.add(exclude.replace('\\', '/'));
2061 getLog().debug("Automatically excluded " + exclude + " from project scanning");
2062 }
2063 }
2064 return excludes;
2065 }
2066
2067
2068
2069
2070
2071
2072
2073 List<BuildJob> getBuildJobs() throws IOException, MojoExecutionException {
2074
2075 List<String> includes = calculateIncludes();
2076 List<String> excludes = calculateExcludes();
2077 List<BuildJob> buildJobsAll = scanProjectsDirectory(includes, excludes);
2078 List<BuildJob> buildJobsSetup = scanProjectsDirectory(setupIncludes, excludes);
2079
2080 List<String> setupProjects =
2081 buildJobsSetup.stream().map(BuildJob::getProject).collect(Collectors.toList());
2082
2083 for (BuildJob job : buildJobsAll) {
2084 if (setupProjects.contains(job.getProject())) {
2085 job.setType(BuildJob.Type.SETUP);
2086 }
2087 InvokerProperties invokerProperties =
2088 getInvokerProperties(new File(projectsDirectory, job.getProject()).getParentFile(), null);
2089 job.setOrdinal(invokerProperties.getOrdinal());
2090 }
2091
2092 relativizeProjectPaths(buildJobsAll);
2093
2094 return buildJobsAll;
2095 }
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108 private List<BuildJob> scanProjectsDirectory(List<String> includes, List<String> excludes) throws IOException {
2109 if (!projectsDirectory.isDirectory()) {
2110 return Collections.emptyList();
2111 }
2112
2113 DirectoryScanner scanner = new DirectoryScanner();
2114 scanner.setBasedir(projectsDirectory.getCanonicalFile());
2115 scanner.setFollowSymlinks(false);
2116 if (includes != null) {
2117 scanner.setIncludes(includes.toArray(new String[0]));
2118 }
2119 if (excludes != null) {
2120 if ((includes == null || includes.isEmpty()) && !excludes.isEmpty()) {
2121 scanner.setIncludes(new String[] {"*"});
2122 }
2123 scanner.setExcludes(excludes.toArray(new String[0]));
2124 }
2125 scanner.addDefaultExcludes();
2126 scanner.scan();
2127
2128 Map<String, BuildJob> matches = new LinkedHashMap<>();
2129
2130 for (String includedFile : scanner.getIncludedFiles()) {
2131 matches.put(includedFile, new BuildJob(includedFile));
2132 }
2133
2134 for (String includedDir : scanner.getIncludedDirectories()) {
2135 String includedFile = includedDir + File.separatorChar + "pom.xml";
2136 if (new File(scanner.getBasedir(), includedFile).isFile()) {
2137 matches.put(includedFile, new BuildJob(includedFile));
2138 } else {
2139 matches.put(includedDir, new BuildJob(includedDir));
2140 }
2141 }
2142
2143 return new ArrayList<>(matches.values());
2144 }
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155 private void relativizeProjectPaths(List<BuildJob> buildJobs) throws IOException {
2156 String projectsDirPath = projectsDirectory.getCanonicalPath();
2157
2158 for (BuildJob buildJob : buildJobs) {
2159 String projectPath = buildJob.getProject();
2160
2161 File file = new File(projectPath);
2162
2163 if (!file.isAbsolute()) {
2164 file = new File(projectsDirectory, projectPath);
2165 }
2166
2167 String relativizedPath = relativizePath(file, projectsDirPath);
2168
2169 if (relativizedPath == null) {
2170 relativizedPath = projectPath;
2171 }
2172
2173 buildJob.setProject(relativizedPath);
2174 }
2175 }
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187 private String relativizePath(File path, String basedir) throws IOException {
2188 String relativizedPath = path.getCanonicalPath();
2189
2190 if (relativizedPath.startsWith(basedir)) {
2191 relativizedPath = relativizedPath.substring(basedir.length());
2192 if (relativizedPath.startsWith(File.separator)) {
2193 relativizedPath = relativizedPath.substring(File.separator.length());
2194 }
2195
2196 return relativizedPath;
2197 } else {
2198 return null;
2199 }
2200 }
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210 private Map<String, Object> getInterpolationValueSource(final boolean escapeXml) {
2211 Map<String, Object> props = new HashMap<>();
2212
2213 if (filterProperties != null) {
2214 props.putAll(filterProperties);
2215 }
2216 props.put("basedir", this.project.getBasedir().getAbsolutePath());
2217 props.put("baseurl", toUrl(this.project.getBasedir().getAbsolutePath()));
2218 if (settings.getLocalRepository() != null) {
2219 props.put("localRepository", settings.getLocalRepository());
2220 props.put("localRepositoryUrl", toUrl(settings.getLocalRepository()));
2221 }
2222
2223 return new CompositeMap(this.project, props, escapeXml);
2224 }
2225
2226
2227
2228
2229
2230
2231
2232
2233 private static String toUrl(String filename) {
2234
2235
2236
2237
2238 String url = "file://" + new File(filename).toURI().getPath();
2239 if (url.endsWith("/")) {
2240 url = url.substring(0, url.length() - 1);
2241 }
2242 return url;
2243 }
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258 void buildInterpolatedFile(File originalFile, File interpolatedFile) throws MojoExecutionException {
2259 getLog().debug("Interpolate " + originalFile.getPath() + " to " + interpolatedFile.getPath());
2260
2261 try {
2262 String xml;
2263
2264 Map<String, Object> composite = getInterpolationValueSource(true);
2265
2266
2267 try (Reader reader =
2268 new InterpolationFilterReader(ReaderFactory.newXmlReader(originalFile), composite, "@", "@")) {
2269 xml = IOUtil.toString(reader);
2270 }
2271
2272 try (Writer writer = WriterFactory.newXmlWriter(interpolatedFile)) {
2273 interpolatedFile.getParentFile().mkdirs();
2274
2275 writer.write(xml);
2276 }
2277 } catch (IOException e) {
2278 throw new MojoExecutionException("Failed to interpolate file " + originalFile.getPath(), e);
2279 }
2280 }
2281
2282
2283
2284
2285
2286
2287
2288
2289 private InvokerProperties getInvokerProperties(final File projectDirectory, Properties globalInvokerProperties)
2290 throws MojoExecutionException {
2291 Properties props;
2292 if (globalInvokerProperties != null) {
2293 props = new Properties(globalInvokerProperties);
2294 } else {
2295 props = new Properties();
2296 }
2297
2298 File propertiesFile = new File(projectDirectory, invokerPropertiesFile);
2299 if (propertiesFile.isFile()) {
2300 try (InputStream in = new FileInputStream(propertiesFile)) {
2301 props.load(in);
2302 } catch (IOException e) {
2303 throw new MojoExecutionException("Failed to read invoker properties: " + propertiesFile, e);
2304 }
2305 }
2306
2307 Interpolator interpolator = new RegexBasedInterpolator();
2308 interpolator.addValueSource(new MapBasedValueSource(getInterpolationValueSource(false)));
2309
2310 for (String key : props.stringPropertyNames()) {
2311 String value = props.getProperty(key);
2312 try {
2313 value = interpolator.interpolate(value, "");
2314 } catch (InterpolationException e) {
2315 throw new MojoExecutionException("Failed to interpolate invoker properties: " + propertiesFile, e);
2316 }
2317 props.setProperty(key, value);
2318 }
2319
2320 InvokerProperties invokerProperties = new InvokerProperties(props);
2321
2322
2323 invokerProperties.setDefaultDebug(debug);
2324 invokerProperties.setDefaultQuiet(quiet);
2325 invokerProperties.setDefaultGoals(goals);
2326 invokerProperties.setDefaultProfiles(profiles);
2327 invokerProperties.setDefaultMavenExecutable(mavenExecutable);
2328 invokerProperties.setDefaultMavenOpts(mavenOpts);
2329 invokerProperties.setDefaultTimeoutInSeconds(timeoutInSeconds);
2330 invokerProperties.setDefaultEnvironmentVariables(environmentVariables);
2331 invokerProperties.setDefaultUpdateSnapshots(updateSnapshots);
2332
2333 return invokerProperties;
2334 }
2335
2336 static class ToolchainPrivateManager {
2337 private ToolchainManagerPrivate manager;
2338
2339 private MavenSession session;
2340
2341 ToolchainPrivateManager(ToolchainManagerPrivate manager, MavenSession session) {
2342 this.manager = manager;
2343 this.session = session;
2344 }
2345
2346 ToolchainPrivate[] getToolchainPrivates(String type) throws MisconfiguredToolchainException {
2347 return manager.getToolchainsForType(type, session);
2348 }
2349 }
2350 }