1 package org.apache.maven.plugin.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.MojoExecutionException;
27 import org.apache.maven.plugin.MojoFailureException;
28 import org.apache.maven.plugin.invoker.model.BuildJob;
29 import org.apache.maven.plugin.invoker.model.io.xpp3.BuildJobXpp3Writer;
30 import org.apache.maven.plugin.registry.TrackableBase;
31 import org.apache.maven.project.MavenProject;
32 import org.apache.maven.settings.Settings;
33 import org.apache.maven.settings.SettingsUtils;
34 import org.apache.maven.settings.io.xpp3.SettingsXpp3Reader;
35 import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
36 import org.apache.maven.shared.invoker.CommandLineConfigurationException;
37 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
38 import org.apache.maven.shared.invoker.InvocationRequest;
39 import org.apache.maven.shared.invoker.InvocationResult;
40 import org.apache.maven.shared.invoker.Invoker;
41 import org.apache.maven.shared.invoker.MavenCommandLineBuilder;
42 import org.apache.maven.shared.invoker.MavenInvocationException;
43 import org.apache.maven.shared.scriptinterpreter.RunErrorException;
44 import org.apache.maven.shared.scriptinterpreter.RunFailureException;
45 import org.apache.maven.shared.scriptinterpreter.ScriptRunner;
46 import org.codehaus.plexus.interpolation.InterpolationException;
47 import org.codehaus.plexus.interpolation.Interpolator;
48 import org.codehaus.plexus.interpolation.MapBasedValueSource;
49 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
50 import org.codehaus.plexus.util.DirectoryScanner;
51 import org.codehaus.plexus.util.FileUtils;
52 import org.codehaus.plexus.util.IOUtil;
53 import org.codehaus.plexus.util.InterpolationFilterReader;
54 import org.codehaus.plexus.util.ReaderFactory;
55 import org.codehaus.plexus.util.StringUtils;
56 import org.codehaus.plexus.util.WriterFactory;
57 import org.codehaus.plexus.util.xml.XmlStreamReader;
58 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
59
60 import java.io.BufferedReader;
61 import java.io.File;
62 import java.io.FileInputStream;
63 import java.io.FileOutputStream;
64 import java.io.FileWriter;
65 import java.io.IOException;
66 import java.io.InputStream;
67 import java.io.OutputStreamWriter;
68 import java.io.Reader;
69 import java.io.Writer;
70 import java.text.DecimalFormat;
71 import java.text.DecimalFormatSymbols;
72 import java.util.ArrayList;
73 import java.util.Arrays;
74 import java.util.Collection;
75 import java.util.Collections;
76 import java.util.HashMap;
77 import java.util.LinkedHashMap;
78 import java.util.LinkedHashSet;
79 import java.util.List;
80 import java.util.Locale;
81 import java.util.Map;
82 import java.util.Properties;
83 import java.util.Set;
84 import java.util.StringTokenizer;
85 import java.util.TreeSet;
86 import java.util.concurrent.ExecutorService;
87 import java.util.concurrent.Executors;
88 import java.util.concurrent.TimeUnit;
89
90
91
92
93
94
95
96 public abstract class AbstractInvokerMojo
97 extends AbstractMojo
98 {
99
100
101
102
103
104
105
106 private boolean skipInvocation;
107
108
109
110
111
112
113
114
115
116 protected boolean suppressSummaries;
117
118
119
120
121
122
123 private boolean streamLogs;
124
125
126
127
128
129
130
131
132 private File localRepositoryPath;
133
134
135
136
137
138
139 private File projectsDirectory;
140
141
142
143
144
145
146
147
148
149
150 private File reportsDirectory;
151
152
153
154
155
156
157
158 private boolean disableReports;
159
160
161
162
163
164
165
166
167
168 private File cloneProjectsTo;
169
170
171
172
173
174
175
176
177
178
179 private boolean cloneAllFiles;
180
181
182
183
184
185
186
187 private boolean cloneClean;
188
189
190
191
192
193
194 private File pom;
195
196
197
198
199
200
201
202
203
204
205
206
207
208 private List<String> pomIncludes = Collections.singletonList( "*/pom.xml" );
209
210
211
212
213
214
215
216
217 private List<String> pomExcludes = Collections.emptyList();
218
219
220
221
222
223
224
225
226
227
228
229 private List<String> setupIncludes = Collections.singletonList( "setup*/pom.xml" );
230
231
232
233
234
235
236 private List<String> goals = Collections.singletonList( "package" );
237
238
239
240
241
242
243
244
245 private String goalsFile;
246
247
248
249
250 private Invoker invoker;
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 private String selectorScript;
266
267
268
269
270
271
272
273
274
275
276
277 private String preBuildHookScript;
278
279
280
281
282
283
284
285
286
287
288 private String postBuildHookScript;
289
290
291
292
293
294
295 private String testPropertiesFile;
296
297
298
299
300
301
302
303 private Properties testProperties;
304
305
306
307
308
309
310
311 private Map<String, String> properties;
312
313
314
315
316
317
318 private boolean showErrors;
319
320
321
322
323
324
325 private boolean debug;
326
327
328
329
330
331
332 private boolean noLog;
333
334
335
336
337
338
339
340 private List<String> profiles;
341
342
343
344
345
346
347
348
349 private Properties interpolationsProperties;
350
351
352
353
354
355
356
357 private Map<String, String> filterProperties;
358
359
360
361
362
363
364
365
366
367 private MavenProject project;
368
369
370
371
372
373
374
375
376
377
378
379
380 private String invokerTest;
381
382
383
384
385
386
387
388
389
390
391 private String profilesFile;
392
393
394
395
396
397
398
399
400
401 private File settingsFile;
402
403
404
405
406
407
408
409
410 private String mavenOpts;
411
412
413
414
415
416
417
418
419 private File mavenHome;
420
421
422
423
424
425
426
427
428 private File javaHome;
429
430
431
432
433
434
435
436 private String encoding;
437
438
439
440
441
442
443
444
445
446 private Settings settings;
447
448
449
450
451
452
453
454
455
456
457
458 private boolean addTestClassPath;
459
460
461
462
463
464
465
466 private List<String> testClassPath;
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 private String invokerPropertiesFile;
537
538
539
540
541
542
543
544 private boolean showVersion;
545
546
547
548
549
550
551
552
553 private int parallelThreads;
554
555
556
557
558
559
560
561 private List<Artifact> pluginArtifacts;
562
563
564
565
566
567
568
569
570 private boolean mergeUserSettings;
571
572
573
574
575 private ScriptRunner scriptRunner;
576
577
578
579
580
581
582 private String filteredPomPrefix = "interpolated-";
583
584
585
586
587 private final DecimalFormat secFormat = new DecimalFormat( "(0.0 s)", new DecimalFormatSymbols( Locale.ENGLISH ) );
588
589
590
591
592
593
594
595
596
597 public void execute()
598 throws MojoExecutionException, MojoFailureException
599 {
600 if ( skipInvocation )
601 {
602 getLog().info( "Skipping invocation per configuration."
603 + " If this is incorrect, ensure the skipInvocation parameter is not set to true." );
604 return;
605 }
606
607
608 if ( !disableReports && !reportsDirectory.exists() )
609 {
610 reportsDirectory.mkdirs();
611 }
612
613 BuildJob[] buildJobs;
614 if ( pom != null )
615 {
616 try
617 {
618 projectsDirectory = pom.getCanonicalFile().getParentFile();
619 }
620 catch ( IOException e )
621 {
622 throw new MojoExecutionException(
623 "Failed to discover projectsDirectory from pom File parameter." + " Reason: " + e.getMessage(), e );
624 }
625
626 buildJobs = new BuildJob[]{ new BuildJob( pom.getName(), BuildJob.Type.NORMAL ) };
627 }
628 else
629 {
630 try
631 {
632 buildJobs = getBuildJobs();
633 }
634 catch ( final IOException e )
635 {
636 throw new MojoExecutionException(
637 "Error retrieving POM list from includes, excludes, " + "and projects directory. Reason: "
638 + e.getMessage(), e );
639 }
640 }
641
642 if ( ( buildJobs == null ) || ( buildJobs.length < 1 ) )
643 {
644 getLog().info( "No projects were selected for execution." );
645 return;
646 }
647
648 if ( StringUtils.isEmpty( encoding ) )
649 {
650 getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
651 + ", i.e. build is platform dependent!" );
652 }
653
654 final List<String> scriptClassPath;
655 if ( addTestClassPath )
656 {
657 scriptClassPath = new ArrayList<String>( testClassPath );
658 for ( Artifact pluginArtifact : pluginArtifacts )
659 {
660 scriptClassPath.remove( pluginArtifact.getFile().getAbsolutePath() );
661 }
662 }
663 else
664 {
665 scriptClassPath = null;
666 }
667 scriptRunner = new ScriptRunner( getLog() );
668 scriptRunner.setScriptEncoding( encoding );
669 scriptRunner.setGlobalVariable( "localRepositoryPath", localRepositoryPath );
670 scriptRunner.setClassPath( scriptClassPath );
671
672 Collection<String> collectedProjects = new LinkedHashSet<String>();
673 for ( int i = 0; i < buildJobs.length; i++ )
674 {
675 collectProjects( projectsDirectory, buildJobs[i].getProject(), collectedProjects, true );
676 }
677
678 File projectsDir = projectsDirectory;
679
680 if ( cloneProjectsTo != null )
681 {
682 cloneProjects( collectedProjects );
683 projectsDir = cloneProjectsTo;
684 }
685 else
686 {
687 getLog().warn( "Filtering of parent/child POMs is not supported without cloning the projects" );
688 }
689
690 runBuilds( projectsDir, buildJobs );
691
692 processResults( new InvokerSession( buildJobs ) );
693 }
694
695
696
697
698
699
700
701
702 abstract void processResults( InvokerSession invokerSession )
703 throws MojoFailureException;
704
705
706
707
708
709
710
711
712 private Reader newReader( File file )
713 throws IOException
714 {
715 if ( StringUtils.isNotEmpty( encoding ) )
716 {
717 return ReaderFactory.newReader( file, encoding );
718 }
719 else
720 {
721 return ReaderFactory.newPlatformReader( file );
722 }
723 }
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740 private void collectProjects( File projectsDir, String projectPath, Collection<String> projectPaths,
741 boolean included )
742 throws MojoExecutionException
743 {
744 projectPath = projectPath.replace( '\\', '/' );
745 File pomFile = new File( projectsDir, projectPath );
746 if ( pomFile.isDirectory() )
747 {
748 pomFile = new File( pomFile, "pom.xml" );
749 if ( !pomFile.exists() )
750 {
751 if ( included )
752 {
753 projectPaths.add( projectPath );
754 }
755 return;
756 }
757 if ( !projectPath.endsWith( "/" ) )
758 {
759 projectPath += '/';
760 }
761 projectPath += "pom.xml";
762 }
763 else if ( !pomFile.isFile() )
764 {
765 return;
766 }
767 if ( !projectPaths.add( projectPath ) )
768 {
769 return;
770 }
771 getLog().debug( "Collecting parent/child projects of " + projectPath );
772
773 Model model = PomUtils.loadPom( pomFile );
774
775 try
776 {
777 String projectsRoot = projectsDir.getCanonicalPath();
778 String projectDir = pomFile.getParent();
779
780 String parentPath = "../pom.xml";
781 if ( model.getParent() != null && StringUtils.isNotEmpty( model.getParent().getRelativePath() ) )
782 {
783 parentPath = model.getParent().getRelativePath();
784 }
785 String parent = relativizePath( new File( projectDir, parentPath ), projectsRoot );
786 if ( parent != null )
787 {
788 collectProjects( projectsDir, parent, projectPaths, false );
789 }
790
791 Collection<String> modulePaths = new LinkedHashSet<String>();
792
793 modulePaths.addAll( (List<String>) model.getModules() );
794
795 for ( Profile profile : (List<Profile>) model.getProfiles() )
796 {
797 modulePaths.addAll( (List<String>) profile.getModules() );
798 }
799
800 for ( String modulePath : modulePaths )
801 {
802 String module = relativizePath( new File( projectDir, modulePath ), projectsRoot );
803 if ( module != null )
804 {
805 collectProjects( projectsDir, module, projectPaths, false );
806 }
807 }
808 }
809 catch ( IOException e )
810 {
811 throw new MojoExecutionException( "Failed to analyze POM: " + pomFile, e );
812 }
813 }
814
815
816
817
818
819
820
821
822
823
824 private void cloneProjects( Collection<String> projectPaths )
825 throws MojoExecutionException
826 {
827 if ( !cloneProjectsTo.mkdirs() && cloneClean )
828 {
829 try
830 {
831 FileUtils.cleanDirectory( cloneProjectsTo );
832 }
833 catch ( IOException e )
834 {
835 throw new MojoExecutionException(
836 "Could not clean the cloneProjectsTo directory. Reason: " + e.getMessage(), e );
837 }
838 }
839
840
841 Collection<String> dirs = new LinkedHashSet<String>();
842 for ( String projectPath : projectPaths )
843 {
844 if ( !new File( projectsDirectory, projectPath ).isDirectory() )
845 {
846 projectPath = getParentPath( projectPath );
847 }
848 dirs.add( projectPath );
849 }
850
851 boolean filter = false;
852
853
854 try
855 {
856 filter = !cloneProjectsTo.getCanonicalFile().equals( projectsDirectory.getCanonicalFile() );
857
858 List<String> clonedSubpaths = new ArrayList<String>();
859
860 for ( String subpath : dirs )
861 {
862
863 if ( !".".equals( subpath ) && dirs.contains( getParentPath( subpath ) ) )
864 {
865 continue;
866 }
867
868
869 if ( !alreadyCloned( subpath, clonedSubpaths ) )
870 {
871
872 if ( ".".equals( subpath ) )
873 {
874 String cloneSubdir = relativizePath( cloneProjectsTo, projectsDirectory.getCanonicalPath() );
875
876
877 if ( cloneSubdir != null )
878 {
879 File temp = File.createTempFile( "pre-invocation-clone.", "" );
880 temp.delete();
881 temp.mkdirs();
882
883 copyDirectoryStructure( projectsDirectory, temp );
884
885 FileUtils.deleteDirectory( new File( temp, cloneSubdir ) );
886
887 copyDirectoryStructure( temp, cloneProjectsTo );
888 }
889 else
890 {
891 copyDirectoryStructure( projectsDirectory, cloneProjectsTo );
892 }
893 }
894 else
895 {
896 File srcDir = new File( projectsDirectory, subpath );
897 File dstDir = new File( cloneProjectsTo, subpath );
898 copyDirectoryStructure( srcDir, dstDir );
899 }
900
901 clonedSubpaths.add( subpath );
902 }
903 }
904 }
905 catch ( IOException e )
906 {
907 throw new MojoExecutionException(
908 "Failed to clone projects from: " + projectsDirectory + " to: " + cloneProjectsTo + ". Reason: "
909 + e.getMessage(), e );
910 }
911
912
913 if ( filter )
914 {
915 for ( String projectPath : projectPaths )
916 {
917 File pomFile = new File( cloneProjectsTo, projectPath );
918 if ( pomFile.isFile() )
919 {
920 buildInterpolatedFile( pomFile, pomFile );
921 }
922 }
923 filteredPomPrefix = null;
924 }
925 }
926
927
928
929
930
931
932
933 private String getParentPath( String path )
934 {
935 int lastSep = Math.max( path.lastIndexOf( '/' ), path.lastIndexOf( '\\' ) );
936 return ( lastSep < 0 ) ? "." : path.substring( 0, lastSep );
937 }
938
939
940
941
942
943
944
945
946 private void copyDirectoryStructure( File sourceDir, File destDir )
947 throws IOException
948 {
949 DirectoryScanner scanner = new DirectoryScanner();
950 scanner.setBasedir( sourceDir );
951 if ( !cloneAllFiles )
952 {
953 scanner.addDefaultExcludes();
954 }
955 scanner.scan();
956
957
958
959
960 destDir.mkdirs();
961 String[] includedDirs = scanner.getIncludedDirectories();
962 for ( int i = 0; i < includedDirs.length; ++i )
963 {
964 File clonedDir = new File( destDir, includedDirs[i] );
965 clonedDir.mkdirs();
966 }
967
968 String[] includedFiles = scanner.getIncludedFiles();
969 for ( int i = 0; i < includedFiles.length; ++i )
970 {
971 File sourceFile = new File( sourceDir, includedFiles[i] );
972 File destFile = new File( destDir, includedFiles[i] );
973 FileUtils.copyFile( sourceFile, destFile );
974 }
975 }
976
977
978
979
980
981
982
983
984
985
986 static boolean alreadyCloned( String subpath, List<String> clonedSubpaths )
987 {
988 for ( String path : clonedSubpaths )
989 {
990 if ( ".".equals( path ) || subpath.equals( path ) || subpath.startsWith( path + File.separator ) )
991 {
992 return true;
993 }
994 }
995
996 return false;
997 }
998
999
1000
1001
1002
1003
1004
1005
1006
1007 private void runBuilds( final File projectsDir, BuildJob[] buildJobs )
1008 throws MojoExecutionException
1009 {
1010 if ( !localRepositoryPath.exists() )
1011 {
1012 localRepositoryPath.mkdirs();
1013 }
1014
1015
1016
1017
1018
1019 File interpolatedSettingsFile = null;
1020 if ( settingsFile != null )
1021 {
1022 if ( cloneProjectsTo != null )
1023 {
1024 interpolatedSettingsFile = new File( cloneProjectsTo, "interpolated-" + settingsFile.getName() );
1025 }
1026 else
1027 {
1028 interpolatedSettingsFile =
1029 new File( settingsFile.getParentFile(), "interpolated-" + settingsFile.getName() );
1030 }
1031 buildInterpolatedFile( settingsFile, interpolatedSettingsFile );
1032 }
1033
1034
1035
1036
1037
1038 SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer();
1039
1040 File mergedSettingsFile;
1041 Settings mergedSettings = this.settings;
1042 if ( mergeUserSettings )
1043 {
1044 if ( interpolatedSettingsFile != null )
1045 {
1046
1047 Reader reader = null;
1048 try
1049 {
1050 reader = new XmlStreamReader( interpolatedSettingsFile );
1051 SettingsXpp3Reader settingsReader = new SettingsXpp3Reader();
1052 Settings dominantSettings = settingsReader.read( reader );
1053 Settings recessiveSettings = this.settings;
1054
1055 SettingsUtils.merge( dominantSettings, recessiveSettings, TrackableBase.USER_LEVEL );
1056
1057 mergedSettings = dominantSettings;
1058 getLog().debug( "Merged specified settings file with settings of invoking process" );
1059 }
1060 catch ( XmlPullParserException e )
1061 {
1062 throw new MojoExecutionException( "Could not read specified settings file", e );
1063 }
1064 catch ( IOException e )
1065 {
1066 throw new MojoExecutionException( "Could not read specified settings file", e );
1067 }
1068 finally
1069 {
1070 IOUtil.close( reader );
1071 }
1072 }
1073 }
1074 if ( this.settingsFile != null && !mergeUserSettings )
1075 {
1076 mergedSettingsFile = interpolatedSettingsFile;
1077 }
1078 else
1079 {
1080 try
1081 {
1082 mergedSettingsFile = File.createTempFile( "invoker-settings", ".xml" );
1083
1084 FileWriter fileWriter = null;
1085 try
1086 {
1087 fileWriter = new FileWriter( mergedSettingsFile );
1088 settingsWriter.write( fileWriter, mergedSettings );
1089 }
1090 finally
1091 {
1092 IOUtil.close( fileWriter );
1093 }
1094
1095 if ( getLog().isDebugEnabled() )
1096 {
1097 getLog().debug(
1098 "Created temporary file for invoker settings.xml: " + mergedSettingsFile.getAbsolutePath() );
1099 }
1100
1101 }
1102 catch ( IOException e )
1103 {
1104 throw new MojoExecutionException( "Could not create temporary file for invoker settings.xml", e );
1105 }
1106 }
1107 final File finalSettingsFile = mergedSettingsFile;
1108
1109 try
1110 {
1111 if ( isParallelRun() )
1112 {
1113 getLog().info( "use parallelThreads " + parallelThreads );
1114
1115 ExecutorService executorService = Executors.newFixedThreadPool( parallelThreads );
1116 for ( int i = 0; i < buildJobs.length; i++ )
1117 {
1118 final BuildJob project = buildJobs[i];
1119 executorService.execute( new Runnable()
1120 {
1121
1122 public void run()
1123 {
1124 try
1125 {
1126 runBuild( projectsDir, project, finalSettingsFile );
1127 }
1128 catch ( MojoExecutionException e )
1129 {
1130 throw new RuntimeException( e.getMessage(), e );
1131 }
1132 }
1133 } );
1134 }
1135
1136 try
1137 {
1138 executorService.shutdown();
1139
1140 executorService.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS );
1141 }
1142 catch ( InterruptedException e )
1143 {
1144 throw new MojoExecutionException( e.getMessage(), e );
1145 }
1146
1147 }
1148 else
1149 {
1150 for ( int i = 0; i < buildJobs.length; i++ )
1151 {
1152 BuildJob project = buildJobs[i];
1153 runBuild( projectsDir, project, finalSettingsFile );
1154 }
1155 }
1156 }
1157 finally
1158 {
1159 if ( interpolatedSettingsFile != null && cloneProjectsTo == null )
1160 {
1161 interpolatedSettingsFile.delete();
1162 }
1163 if ( mergedSettingsFile != null && mergedSettingsFile.exists() )
1164 {
1165 mergedSettingsFile.delete();
1166 }
1167
1168 }
1169 }
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181 private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile )
1182 throws MojoExecutionException
1183 {
1184 File pomFile = new File( projectsDir, buildJob.getProject() );
1185 File basedir;
1186 if ( pomFile.isDirectory() )
1187 {
1188 basedir = pomFile;
1189 pomFile = new File( basedir, "pom.xml" );
1190 if ( !pomFile.exists() )
1191 {
1192 pomFile = null;
1193 }
1194 else
1195 {
1196 buildJob.setProject( buildJob.getProject() + File.separator + "pom.xml" );
1197 }
1198 }
1199 else
1200 {
1201 basedir = pomFile.getParentFile();
1202 }
1203
1204 getLog().info( "Building: " + buildJob.getProject() );
1205
1206 File interpolatedPomFile = null;
1207 if ( pomFile != null )
1208 {
1209 if ( filteredPomPrefix != null )
1210 {
1211 interpolatedPomFile = new File( basedir, filteredPomPrefix + pomFile.getName() );
1212 buildInterpolatedFile( pomFile, interpolatedPomFile );
1213 }
1214 else
1215 {
1216 interpolatedPomFile = pomFile;
1217 }
1218 }
1219
1220 InvokerProperties invokerProperties = getInvokerProperties( basedir );
1221
1222
1223 buildJob.setName( invokerProperties.getJobName() );
1224 buildJob.setDescription( invokerProperties.getJobDescription() );
1225
1226 try
1227 {
1228 if ( isSelected( invokerProperties ) )
1229 {
1230 long milliseconds = System.currentTimeMillis();
1231 boolean executed;
1232 try
1233 {
1234 executed = runBuild( basedir, interpolatedPomFile, settingsFile, invokerProperties );
1235 }
1236 finally
1237 {
1238 milliseconds = System.currentTimeMillis() - milliseconds;
1239 buildJob.setTime( milliseconds / 1000.0 );
1240 }
1241
1242 if ( executed )
1243 {
1244
1245 buildJob.setResult( BuildJob.Result.SUCCESS );
1246
1247 if ( !suppressSummaries )
1248 {
1249 getLog().info( "..SUCCESS " + formatTime( buildJob.getTime() ) );
1250 }
1251 }
1252 else
1253 {
1254 buildJob.setResult( BuildJob.Result.SKIPPED );
1255
1256 if ( !suppressSummaries )
1257 {
1258 getLog().info( "..SKIPPED " + formatTime( buildJob.getTime() ) );
1259 }
1260 }
1261 }
1262 else
1263 {
1264 buildJob.setResult( BuildJob.Result.SKIPPED );
1265
1266 if ( !suppressSummaries )
1267 {
1268 getLog().info( "..SKIPPED " );
1269 }
1270 }
1271 }
1272 catch ( RunErrorException e )
1273 {
1274 buildJob.setResult( BuildJob.Result.ERROR );
1275 buildJob.setFailureMessage( e.getMessage() );
1276
1277 if ( !suppressSummaries )
1278 {
1279 getLog().info( "..ERROR " + formatTime( buildJob.getTime() ) );
1280 getLog().info( " " + e.getMessage() );
1281 }
1282 }
1283 catch ( RunFailureException e )
1284 {
1285 buildJob.setResult( e.getType() );
1286 buildJob.setFailureMessage( e.getMessage() );
1287
1288 if ( !suppressSummaries )
1289 {
1290 getLog().info( "..FAILED " + formatTime( buildJob.getTime() ) );
1291 getLog().info( " " + e.getMessage() );
1292 }
1293 }
1294 finally
1295 {
1296 if ( interpolatedPomFile != null && StringUtils.isNotEmpty( filteredPomPrefix ) )
1297 {
1298 interpolatedPomFile.delete();
1299 }
1300 writeBuildReport( buildJob );
1301 }
1302 }
1303
1304
1305
1306
1307
1308
1309
1310 private boolean isSelected( InvokerProperties invokerProperties )
1311 {
1312 if ( !SelectorUtils.isMavenVersion( invokerProperties.getMavenVersion() ) )
1313 {
1314 return false;
1315 }
1316
1317 if ( !SelectorUtils.isJreVersion( invokerProperties.getJreVersion() ) )
1318 {
1319 return false;
1320 }
1321
1322 if ( !SelectorUtils.isOsFamily( invokerProperties.getOsFamily() ) )
1323 {
1324 return false;
1325 }
1326
1327 return true;
1328 }
1329
1330
1331
1332
1333
1334
1335
1336
1337 private void writeBuildReport( BuildJob buildJob )
1338 throws MojoExecutionException
1339 {
1340 if ( disableReports )
1341 {
1342 return;
1343 }
1344
1345 String safeFileName = buildJob.getProject().replace( '/', '_' ).replace( '\\', '_' ).replace( ' ', '_' );
1346 if ( safeFileName.endsWith( "_pom.xml" ) )
1347 {
1348 safeFileName = safeFileName.substring( 0, safeFileName.length() - "_pom.xml".length() );
1349 }
1350
1351 File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" );
1352 try
1353 {
1354 FileOutputStream fos = new FileOutputStream( reportFile );
1355 try
1356 {
1357 Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() );
1358 BuildJobXpp3Writer writer = new BuildJobXpp3Writer();
1359 writer.write( osw, buildJob );
1360 osw.close();
1361 }
1362 finally
1363 {
1364 fos.close();
1365 }
1366 }
1367 catch ( IOException e )
1368 {
1369 throw new MojoExecutionException( "Failed to write build report " + reportFile, e );
1370 }
1371 }
1372
1373
1374
1375
1376
1377
1378
1379 private String formatTime( double seconds )
1380 {
1381 return secFormat.format( seconds );
1382 }
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399 private boolean runBuild( File basedir, File pomFile, File settingsFile, InvokerProperties invokerProperties )
1400 throws MojoExecutionException, RunFailureException
1401 {
1402 if ( getLog().isDebugEnabled() && !invokerProperties.getProperties().isEmpty() )
1403 {
1404 Properties props = invokerProperties.getProperties();
1405 getLog().debug( "Using invoker properties:" );
1406 for ( String key : new TreeSet<String>( (Set) props.keySet() ) )
1407 {
1408 String value = props.getProperty( key );
1409 getLog().debug( " " + key + " = " + value );
1410 }
1411 }
1412
1413 List<String> goals = getGoals( basedir );
1414
1415 List<String> profiles = getProfiles( basedir );
1416
1417 Map<String, Object> context = new LinkedHashMap<String, Object>();
1418
1419 FileLogger logger = setupLogger( basedir );
1420 try
1421 {
1422 try
1423 {
1424 scriptRunner.run( "selector script", basedir, selectorScript, context, logger, BuildJob.Result.SKIPPED,
1425 false );
1426 }
1427 catch ( RunErrorException e )
1428 {
1429 throw e;
1430 }
1431 catch ( RunFailureException e )
1432 {
1433 return false;
1434 }
1435
1436 scriptRunner.run( "pre-build script", basedir, preBuildHookScript, context, logger,
1437 BuildJob.Result.FAILURE_PRE_HOOK, false );
1438
1439 final InvocationRequest request = new DefaultInvocationRequest();
1440
1441 request.setLocalRepositoryDirectory( localRepositoryPath );
1442
1443 request.setInteractive( false );
1444
1445 request.setShowErrors( showErrors );
1446
1447 request.setDebug( debug );
1448
1449 request.setShowVersion( showVersion );
1450
1451 if ( logger != null )
1452 {
1453 request.setErrorHandler( logger );
1454
1455 request.setOutputHandler( logger );
1456 }
1457
1458 if ( mavenHome != null )
1459 {
1460 invoker.setMavenHome( mavenHome );
1461 request.addShellEnvironment( "M2_HOME", mavenHome.getAbsolutePath() );
1462 }
1463
1464 if ( javaHome != null )
1465 {
1466 request.setJavaHome( javaHome );
1467 }
1468
1469 for ( int invocationIndex = 1; ; invocationIndex++ )
1470 {
1471 if ( invocationIndex > 1 && !invokerProperties.isInvocationDefined( invocationIndex ) )
1472 {
1473 break;
1474 }
1475
1476 request.setBaseDirectory( basedir );
1477
1478 request.setPomFile( pomFile );
1479
1480 request.setGoals( goals );
1481
1482 request.setProfiles( profiles );
1483
1484 request.setMavenOpts( mavenOpts );
1485
1486 request.setOffline( false );
1487
1488 request.setUserSettingsFile( settingsFile );
1489
1490 Properties systemProperties =
1491 getSystemProperties( basedir, invokerProperties.getSystemPropertiesFile( invocationIndex ) );
1492 request.setProperties( systemProperties );
1493
1494 invokerProperties.configureInvocation( request, invocationIndex );
1495
1496 if ( getLog().isDebugEnabled() )
1497 {
1498 try
1499 {
1500 getLog().debug( "Using MAVEN_OPTS: " + request.getMavenOpts() );
1501 getLog().debug( "Executing: " + new MavenCommandLineBuilder().build( request ) );
1502 }
1503 catch ( CommandLineConfigurationException e )
1504 {
1505 getLog().debug( "Failed to display command line: " + e.getMessage() );
1506 }
1507 }
1508
1509 InvocationResult result;
1510
1511 try
1512 {
1513 result = invoker.execute( request );
1514 }
1515 catch ( final MavenInvocationException e )
1516 {
1517 getLog().debug( "Error invoking Maven: " + e.getMessage(), e );
1518 throw new RunFailureException( "Maven invocation failed. " + e.getMessage(),
1519 BuildJob.Result.FAILURE_BUILD );
1520 }
1521
1522 verify( result, invocationIndex, invokerProperties, logger );
1523 }
1524
1525 scriptRunner.run( "post-build script", basedir, postBuildHookScript, context, logger,
1526 BuildJob.Result.FAILURE_POST_HOOK, true );
1527 }
1528 catch ( IOException e )
1529 {
1530 throw new MojoExecutionException( e.getMessage(), e );
1531 }
1532 finally
1533 {
1534 if ( logger != null )
1535 {
1536 logger.close();
1537 }
1538 }
1539 return true;
1540 }
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550 private FileLogger setupLogger( File basedir )
1551 throws MojoExecutionException
1552 {
1553 FileLogger logger = null;
1554
1555 if ( !noLog )
1556 {
1557 File outputLog = new File( basedir, "build.log" );
1558 try
1559 {
1560 if ( streamLogs )
1561 {
1562 logger = new FileLogger( outputLog, getLog() );
1563 }
1564 else
1565 {
1566 logger = new FileLogger( outputLog );
1567 }
1568
1569 getLog().debug( "build log initialized in: " + outputLog );
1570 }
1571 catch ( IOException e )
1572 {
1573 throw new MojoExecutionException( "Error initializing build logfile in: " + outputLog, e );
1574 }
1575 }
1576
1577 return logger;
1578 }
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590 private Properties getSystemProperties( final File basedir, final String filename )
1591 throws MojoExecutionException
1592 {
1593 Properties collectedTestProperties = new Properties();
1594
1595 if ( testProperties != null )
1596 {
1597 collectedTestProperties.putAll( testProperties );
1598 }
1599
1600 if ( properties != null )
1601 {
1602 collectedTestProperties.putAll( properties );
1603 }
1604
1605 File propertiesFile = null;
1606 if ( filename != null )
1607 {
1608 propertiesFile = new File( basedir, filename );
1609 }
1610 else if ( testPropertiesFile != null )
1611 {
1612 propertiesFile = new File( basedir, testPropertiesFile );
1613 }
1614
1615 if ( propertiesFile != null && propertiesFile.isFile() )
1616 {
1617 InputStream fin = null;
1618 try
1619 {
1620 fin = new FileInputStream( propertiesFile );
1621
1622 Properties loadedProperties = new Properties();
1623 loadedProperties.load( fin );
1624 collectedTestProperties.putAll( loadedProperties );
1625 }
1626 catch ( IOException e )
1627 {
1628 throw new MojoExecutionException( "Error reading system properties from " + propertiesFile );
1629 }
1630 finally
1631 {
1632 IOUtil.close( fin );
1633 }
1634 }
1635
1636 return collectedTestProperties;
1637 }
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649 private void verify( InvocationResult result, int invocationIndex, InvokerProperties invokerProperties,
1650 FileLogger logger )
1651 throws RunFailureException
1652 {
1653 if ( result.getExecutionException() != null )
1654 {
1655 throw new RunFailureException(
1656 "The Maven invocation failed. " + result.getExecutionException().getMessage(), BuildJob.Result.ERROR );
1657 }
1658 else if ( !invokerProperties.isExpectedResult( result.getExitCode(), invocationIndex ) )
1659 {
1660 StringBuffer buffer = new StringBuffer( 256 );
1661 buffer.append( "The build exited with code " ).append( result.getExitCode() ).append( ". " );
1662 if ( logger != null )
1663 {
1664 buffer.append( "See " );
1665 buffer.append( logger.getOutputFile().getAbsolutePath() );
1666 buffer.append( " for details." );
1667 }
1668 else
1669 {
1670 buffer.append( "See console output for details." );
1671 }
1672 throw new RunFailureException( buffer.toString(), BuildJob.Result.FAILURE_BUILD );
1673 }
1674 }
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684 List<String> getGoals( final File basedir )
1685 throws MojoExecutionException
1686 {
1687 try
1688 {
1689 return getTokens( basedir, goalsFile, goals );
1690 }
1691 catch ( IOException e )
1692 {
1693 throw new MojoExecutionException( "error reading goals", e );
1694 }
1695 }
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705 List<String> getProfiles( File basedir )
1706 throws MojoExecutionException
1707 {
1708 try
1709 {
1710 return getTokens( basedir, profilesFile, profiles );
1711 }
1712 catch ( IOException e )
1713 {
1714 throw new MojoExecutionException( "error reading profiles", e );
1715 }
1716 }
1717
1718
1719
1720
1721
1722
1723
1724 BuildJob[] getBuildJobs()
1725 throws IOException
1726 {
1727 BuildJob[] buildJobs;
1728
1729 if ( ( pom != null ) && pom.exists() )
1730 {
1731 buildJobs = new BuildJob[]{ new BuildJob( pom.getAbsolutePath(), BuildJob.Type.NORMAL ) };
1732 }
1733 else if ( invokerTest != null )
1734 {
1735 String[] testRegexes = StringUtils.split( invokerTest, "," );
1736 List<String> includes = new ArrayList<String>( testRegexes.length );
1737
1738 for ( int i = 0, size = testRegexes.length; i < size; i++ )
1739 {
1740
1741 includes.add( testRegexes[i] );
1742 }
1743
1744
1745
1746 buildJobs = scanProjectsDirectory( includes, null, BuildJob.Type.DIRECT );
1747 }
1748 else
1749 {
1750 List<String> excludes =
1751 ( pomExcludes != null ) ? new ArrayList<String>( pomExcludes ) : new ArrayList<String>();
1752 if ( this.settingsFile != null )
1753 {
1754 String exclude = relativizePath( this.settingsFile, projectsDirectory.getCanonicalPath() );
1755 if ( exclude != null )
1756 {
1757 excludes.add( exclude.replace( '\\', '/' ) );
1758 getLog().debug( "Automatically excluded " + exclude + " from project scanning" );
1759 }
1760 }
1761
1762 BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP );
1763 getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) );
1764
1765 BuildJob[] normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL );
1766
1767 Map<String, BuildJob> uniquePoms = new LinkedHashMap<String, BuildJob>();
1768 for ( int i = 0; i < setupPoms.length; i++ )
1769 {
1770 uniquePoms.put( setupPoms[i].getProject(), setupPoms[i] );
1771 }
1772 for ( int i = 0; i < normalPoms.length; i++ )
1773 {
1774 if ( !uniquePoms.containsKey( normalPoms[i].getProject() ) )
1775 {
1776 uniquePoms.put( normalPoms[i].getProject(), normalPoms[i] );
1777 }
1778 }
1779
1780 buildJobs = uniquePoms.values().toArray( new BuildJob[uniquePoms.size()] );
1781 }
1782
1783 relativizeProjectPaths( buildJobs );
1784
1785 return buildJobs;
1786 }
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800 private BuildJob[] scanProjectsDirectory( List<String> includes, List<String> excludes, String type )
1801 throws IOException
1802 {
1803 if ( !projectsDirectory.isDirectory() )
1804 {
1805 return new BuildJob[0];
1806 }
1807
1808 DirectoryScanner scanner = new DirectoryScanner();
1809 scanner.setBasedir( projectsDirectory.getCanonicalFile() );
1810 scanner.setFollowSymlinks( false );
1811 if ( includes != null )
1812 {
1813 scanner.setIncludes( includes.toArray( new String[includes.size()] ) );
1814 }
1815 if ( excludes != null )
1816 {
1817 scanner.setExcludes( excludes.toArray( new String[excludes.size()] ) );
1818 }
1819 scanner.addDefaultExcludes();
1820 scanner.scan();
1821
1822 Map<String, BuildJob> matches = new LinkedHashMap<String, BuildJob>();
1823
1824 String[] includedFiles = scanner.getIncludedFiles();
1825 for ( int i = 0; i < includedFiles.length; i++ )
1826 {
1827 matches.put( includedFiles[i], new BuildJob( includedFiles[i], type ) );
1828 }
1829
1830 String[] includedDirs = scanner.getIncludedDirectories();
1831 for ( int i = 0; i < includedDirs.length; i++ )
1832 {
1833 String includedFile = includedDirs[i] + File.separatorChar + "pom.xml";
1834 if ( new File( scanner.getBasedir(), includedFile ).isFile() )
1835 {
1836 matches.put( includedFile, new BuildJob( includedFile, type ) );
1837 }
1838 else
1839 {
1840 matches.put( includedDirs[i], new BuildJob( includedDirs[i], type ) );
1841 }
1842 }
1843
1844 return matches.values().toArray( new BuildJob[matches.size()] );
1845 }
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856 private void relativizeProjectPaths( BuildJob[] buildJobs )
1857 throws IOException
1858 {
1859 String projectsDirPath = projectsDirectory.getCanonicalPath();
1860
1861 for ( int i = 0; i < buildJobs.length; i++ )
1862 {
1863 String projectPath = buildJobs[i].getProject();
1864
1865 File file = new File( projectPath );
1866
1867 if ( !file.isAbsolute() )
1868 {
1869 file = new File( projectsDirectory, projectPath );
1870 }
1871
1872 String relativizedPath = relativizePath( file, projectsDirPath );
1873
1874 if ( relativizedPath == null )
1875 {
1876 relativizedPath = projectPath;
1877 }
1878
1879 buildJobs[i].setProject( relativizedPath );
1880 }
1881 }
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893 private String relativizePath( File path, String basedir )
1894 throws IOException
1895 {
1896 String relativizedPath = path.getCanonicalPath();
1897
1898 if ( relativizedPath.startsWith( basedir ) )
1899 {
1900 relativizedPath = relativizedPath.substring( basedir.length() );
1901 if ( relativizedPath.startsWith( File.separator ) )
1902 {
1903 relativizedPath = relativizedPath.substring( File.separator.length() );
1904 }
1905
1906 return relativizedPath;
1907 }
1908 else
1909 {
1910 return null;
1911 }
1912 }
1913
1914
1915
1916
1917
1918
1919 private Map<String, Object> getInterpolationValueSource()
1920 {
1921 Map<String, Object> props = new HashMap<String, Object>();
1922 if ( interpolationsProperties != null )
1923 {
1924 props.putAll( (Map) interpolationsProperties );
1925 }
1926 if ( filterProperties != null )
1927 {
1928 props.putAll( filterProperties );
1929 }
1930 props.put( "basedir", this.project.getBasedir().getAbsolutePath() );
1931 props.put( "baseurl", toUrl( this.project.getBasedir().getAbsolutePath() ) );
1932 if ( settings.getLocalRepository() != null )
1933 {
1934 props.put( "localRepository", settings.getLocalRepository() );
1935 props.put( "localRepositoryUrl", toUrl( settings.getLocalRepository() ) );
1936 }
1937 return new CompositeMap( this.project, props );
1938 }
1939
1940
1941
1942
1943
1944
1945
1946
1947 private static String toUrl( String filename )
1948 {
1949
1950
1951
1952
1953 String url = "file://" + new File( filename ).toURI().getPath();
1954 if ( url.endsWith( "/" ) )
1955 {
1956 url = url.substring( 0, url.length() - 1 );
1957 }
1958 return url;
1959 }
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973 private List<String> getTokens( File basedir, String filename, List<String> defaultTokens )
1974 throws IOException
1975 {
1976 List<String> tokens = ( defaultTokens != null ) ? defaultTokens : new ArrayList<String>();
1977
1978 if ( StringUtils.isNotEmpty( filename ) )
1979 {
1980 File tokenFile = new File( basedir, filename );
1981
1982 if ( tokenFile.exists() )
1983 {
1984 tokens = readTokens( tokenFile );
1985 }
1986 }
1987
1988 return tokens;
1989 }
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999 private List<String> readTokens( final File tokenFile )
2000 throws IOException
2001 {
2002 List<String> result = new ArrayList<String>();
2003
2004 BufferedReader reader = null;
2005 try
2006 {
2007 Map<String, Object> composite = getInterpolationValueSource();
2008 reader = new BufferedReader( new InterpolationFilterReader( newReader( tokenFile ), composite ) );
2009
2010 String line = null;
2011 while ( ( line = reader.readLine() ) != null )
2012 {
2013 result.addAll( collectListFromCSV( line ) );
2014 }
2015 }
2016 finally
2017 {
2018 IOUtil.close( reader );
2019 }
2020
2021 return result;
2022 }
2023
2024
2025
2026
2027
2028
2029
2030 private List<String> collectListFromCSV( final String csv )
2031 {
2032 final List<String> result = new ArrayList<String>();
2033
2034 if ( ( csv != null ) && ( csv.trim().length() > 0 ) )
2035 {
2036 final StringTokenizer st = new StringTokenizer( csv, "," );
2037
2038 while ( st.hasMoreTokens() )
2039 {
2040 result.add( st.nextToken().trim() );
2041 }
2042 }
2043
2044 return result;
2045 }
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057 void buildInterpolatedFile( File originalFile, File interpolatedFile )
2058 throws MojoExecutionException
2059 {
2060 getLog().debug( "Interpolate " + originalFile.getPath() + " to " + interpolatedFile.getPath() );
2061
2062 try
2063 {
2064 String xml;
2065
2066 Reader reader = null;
2067 try
2068 {
2069
2070 Map<String, Object> composite = getInterpolationValueSource();
2071 reader = ReaderFactory.newXmlReader( originalFile );
2072 reader = new InterpolationFilterReader( reader, composite, "@", "@" );
2073 xml = IOUtil.toString( reader );
2074 }
2075 finally
2076 {
2077 IOUtil.close( reader );
2078 }
2079
2080 Writer writer = null;
2081 try
2082 {
2083 interpolatedFile.getParentFile().mkdirs();
2084 writer = WriterFactory.newXmlWriter( interpolatedFile );
2085 writer.write( xml );
2086 writer.flush();
2087 }
2088 finally
2089 {
2090 IOUtil.close( writer );
2091 }
2092 }
2093 catch ( IOException e )
2094 {
2095 throw new MojoExecutionException( "Failed to interpolate file " + originalFile.getPath(), e );
2096 }
2097 }
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107 private InvokerProperties getInvokerProperties( final File projectDirectory )
2108 throws MojoExecutionException
2109 {
2110 Properties props = new Properties();
2111 if ( invokerPropertiesFile != null )
2112 {
2113 File propertiesFile = new File( projectDirectory, invokerPropertiesFile );
2114 if ( propertiesFile.isFile() )
2115 {
2116 InputStream in = null;
2117 try
2118 {
2119 in = new FileInputStream( propertiesFile );
2120 props.load( in );
2121 }
2122 catch ( IOException e )
2123 {
2124 throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );
2125 }
2126 finally
2127 {
2128 IOUtil.close( in );
2129 }
2130 }
2131
2132 Interpolator interpolator = new RegexBasedInterpolator();
2133 interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource() ) );
2134 for ( String key : (Set<String>) ( (Map) props ).keySet() )
2135 {
2136 String value = props.getProperty( key );
2137 try
2138 {
2139 value = interpolator.interpolate( value, "" );
2140 }
2141 catch ( InterpolationException e )
2142 {
2143 throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile,
2144 e );
2145 }
2146 props.setProperty( key, value );
2147 }
2148 }
2149 return new InvokerProperties( props );
2150 }
2151
2152 protected boolean isParallelRun()
2153 {
2154 return parallelThreads > 1;
2155 }
2156
2157 }