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