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