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