1 package org.apache.maven.continuum.execution.maven.m2;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.continuum.model.repository.LocalRepository;
23 import org.apache.maven.artifact.Artifact;
24 import org.apache.maven.artifact.metadata.ArtifactMetadata;
25 import org.apache.maven.continuum.configuration.ConfigurationException;
26 import org.apache.maven.continuum.configuration.ConfigurationService;
27 import org.apache.maven.continuum.execution.AbstractBuildExecutor;
28 import org.apache.maven.continuum.execution.ContinuumBuildExecutionResult;
29 import org.apache.maven.continuum.execution.ContinuumBuildExecutor;
30 import org.apache.maven.continuum.execution.ContinuumBuildExecutorConstants;
31 import org.apache.maven.continuum.execution.ContinuumBuildExecutorException;
32 import org.apache.maven.continuum.installation.InstallationService;
33 import org.apache.maven.continuum.model.project.BuildDefinition;
34 import org.apache.maven.continuum.model.project.Project;
35 import org.apache.maven.continuum.model.scm.ChangeFile;
36 import org.apache.maven.continuum.model.scm.ChangeSet;
37 import org.apache.maven.continuum.model.scm.ScmResult;
38 import org.apache.maven.continuum.model.system.Installation;
39 import org.apache.maven.continuum.model.system.Profile;
40 import org.apache.maven.continuum.project.builder.ContinuumProjectBuildingResult;
41 import org.apache.maven.project.MavenProject;
42 import org.apache.maven.project.MavenProjectHelper;
43 import org.apache.maven.project.artifact.ProjectArtifactMetadata;
44 import org.codehaus.plexus.util.DirectoryScanner;
45 import org.codehaus.plexus.util.FileUtils;
46 import org.codehaus.plexus.util.StringUtils;
47
48 import java.io.File;
49 import java.io.IOException;
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.Enumeration;
53 import java.util.HashMap;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.Properties;
57
58
59
60
61
62 public class MavenTwoBuildExecutor
63 extends AbstractBuildExecutor
64 implements ContinuumBuildExecutor
65 {
66
67
68
69
70 public static final String CONFIGURATION_GOALS = "goals";
71
72 public static final String ID = ContinuumBuildExecutorConstants.MAVEN_TWO_BUILD_EXECUTOR;
73
74
75
76
77
78
79
80
81 private MavenBuilderHelper builderHelper;
82
83
84
85
86 private MavenProjectHelper projectHelper;
87
88
89
90
91 private ConfigurationService configurationService;
92
93
94
95
96
97 public MavenTwoBuildExecutor()
98 {
99 super( ID, true );
100 }
101
102 public MavenBuilderHelper getBuilderHelper()
103 {
104 return builderHelper;
105 }
106
107 public void setBuilderHelper( MavenBuilderHelper builderHelper )
108 {
109 this.builderHelper = builderHelper;
110 }
111
112 public MavenProjectHelper getProjectHelper()
113 {
114 return projectHelper;
115 }
116
117 public void setProjectHelper( MavenProjectHelper projectHelper )
118 {
119 this.projectHelper = projectHelper;
120 }
121
122 public ConfigurationService getConfigurationService()
123 {
124 return configurationService;
125 }
126
127 public void setConfigurationService( ConfigurationService configurationService )
128 {
129 this.configurationService = configurationService;
130 }
131
132
133
134
135
136 public ContinuumBuildExecutionResult build( Project project, BuildDefinition buildDefinition, File buildOutput,
137 List<Project> projectsWithCommonScmRoot, String projectScmRootUrl )
138 throws ContinuumBuildExecutorException
139 {
140 String executable = getInstallationService().getExecutorConfigurator(
141 InstallationService.MAVEN2_TYPE ).getExecutable();
142
143 StringBuffer arguments = new StringBuffer();
144
145 String buildFile = getBuildFileForProject( project, buildDefinition );
146
147 if ( !StringUtils.isEmpty( buildFile ) && !"pom.xml".equals( buildFile ) )
148 {
149 arguments.append( "-f " ).append( buildFile ).append( " " );
150 }
151
152 arguments.append( StringUtils.clean( buildDefinition.getArguments() ) ).append( " " );
153
154 Properties props = getContinuumSystemProperties( project );
155 for ( Enumeration itr = props.propertyNames(); itr.hasMoreElements(); )
156 {
157 String name = (String) itr.nextElement();
158 String value = props.getProperty( name );
159 arguments.append( "\"-D" ).append( name ).append( "=" ).append( value ).append( "\" " );
160 }
161
162
163 LocalRepository repository = project.getProjectGroup().getLocalRepository();
164 if ( repository != null )
165 {
166 arguments.append( "\"-Dmaven.repo.local=" ).append( StringUtils.clean( repository.getLocation() ) ).append(
167 "\" " );
168 }
169
170 arguments.append( StringUtils.clean( buildDefinition.getGoals() ) );
171
172 Map<String, String> environments = getEnvironments( buildDefinition );
173 String m2Home = environments.get( getInstallationService().getEnvVar( InstallationService.MAVEN2_TYPE ) );
174 if ( StringUtils.isNotEmpty( m2Home ) )
175 {
176 executable = m2Home + File.separator + "bin" + File.separator + executable;
177 setResolveExecutable( false );
178 }
179
180 return executeShellCommand( project, executable, arguments.toString(), buildOutput, environments, null, null );
181 }
182
183 public void updateProjectFromCheckOut( File workingDirectory, Project project, BuildDefinition buildDefinition,
184 ScmResult scmResult )
185 throws ContinuumBuildExecutorException
186 {
187 File f = getPomFile( getBuildFileForProject( project, buildDefinition ), workingDirectory );
188
189 if ( !f.exists() )
190 {
191 throw new ContinuumBuildExecutorException( "Could not find Maven project descriptor." );
192 }
193 ContinuumProjectBuildingResult result = new ContinuumProjectBuildingResult();
194 boolean update = isDescriptionUpdated( buildDefinition, scmResult, project );
195 builderHelper.mapMetadataToProject( result, f, project, update );
196
197 if ( result.hasErrors() )
198 {
199 throw new ContinuumBuildExecutorException( "Error while mapping metadata:" + result.getErrorsAsString() );
200 }
201 }
202
203 private static File getPomFile( String projectBuildFile, File workingDirectory )
204 {
205 File f = null;
206
207 String buildFile = StringUtils.clean( projectBuildFile );
208
209 if ( !StringUtils.isEmpty( buildFile ) )
210 {
211 f = new File( workingDirectory, buildFile );
212 }
213
214 if ( f == null )
215 {
216 f = new File( workingDirectory, "pom.xml" );
217 }
218
219 return f;
220 }
221
222 @Override
223 public List<Artifact> getDeployableArtifacts( Project continuumProject, File workingDirectory,
224 BuildDefinition buildDefinition )
225 throws ContinuumBuildExecutorException
226 {
227 MavenProject project = getMavenProject( continuumProject, workingDirectory, buildDefinition );
228
229
230
231
232 Artifact artifact = project.getArtifact();
233
234 String projectPackaging = project.getPackaging();
235
236 boolean isPomArtifact = "pom".equals( projectPackaging );
237
238 if ( isPomArtifact )
239 {
240 artifact.setFile( project.getFile() );
241 }
242 else
243 {
244
245 ArtifactMetadata metadata = new ProjectArtifactMetadata( artifact, project.getFile() );
246
247 artifact.addMetadata( metadata );
248
249 String finalName = project.getBuild().getFinalName();
250
251 String filename = finalName + "." + artifact.getArtifactHandler().getExtension();
252
253 String buildDirectory = project.getBuild().getDirectory();
254
255 File artifactFile = new File( buildDirectory, filename );
256
257 artifact.setFile( artifactFile );
258
259
260 File sourcesFile = new File( buildDirectory, finalName + "-sources.jar" );
261
262 if ( sourcesFile.exists() )
263 {
264 projectHelper.attachArtifact( project, "java-source", "sources", sourcesFile );
265 }
266
267
268 File testsSourcesFile = new File( buildDirectory, finalName + "-test-sources.jar" );
269
270 if ( testsSourcesFile.exists() )
271 {
272 projectHelper.attachArtifact( project, "java-source", "test-sources", testsSourcesFile );
273 }
274
275
276 File javadocFile = new File( buildDirectory, finalName + "-javadoc.jar" );
277
278 if ( javadocFile.exists() )
279 {
280 projectHelper.attachArtifact( project, "javadoc", "javadoc", javadocFile );
281 }
282
283
284 File clientFile = new File( buildDirectory, finalName + "-client.jar" );
285
286 if ( clientFile.exists() )
287 {
288 projectHelper.attachArtifact( project, projectPackaging + "-client", "client", clientFile );
289 }
290
291
292 File testsFile = new File( buildDirectory, finalName + "-tests.jar" );
293
294 if ( testsFile.exists() )
295 {
296 projectHelper.attachArtifact( project, "jar", "tests", testsFile );
297 }
298 }
299
300 List<Artifact> attachedArtifacts = project.getAttachedArtifacts();
301
302 List<Artifact> artifacts = new ArrayList<Artifact>( attachedArtifacts.size() + 1 );
303
304 if ( artifact.getFile().exists() )
305 {
306 artifacts.add( artifact );
307 }
308
309 for ( Artifact attachedArtifact : attachedArtifacts )
310 {
311 artifacts.add( attachedArtifact );
312 }
313
314 return artifacts;
315 }
316
317 private MavenProject getMavenProject( Project continuumProject, File workingDirectory,
318 BuildDefinition buildDefinition )
319 throws ContinuumBuildExecutorException
320 {
321 ContinuumProjectBuildingResult result = new ContinuumProjectBuildingResult();
322
323 File f = getPomFile( getBuildFileForProject( continuumProject, buildDefinition ), workingDirectory );
324
325 if ( !f.exists() )
326 {
327 throw new ContinuumBuildExecutorException( "Could not find Maven project descriptor '" + f + "'." );
328 }
329
330 MavenProject project = builderHelper.getMavenProject( result, f );
331
332 if ( result.hasErrors() )
333 {
334 throw new ContinuumBuildExecutorException(
335 "Unable to read the Maven project descriptor '" + f + "': " + result.getErrorsAsString() );
336 }
337 return project;
338 }
339
340 @Override
341 public void backupTestFiles( Project project, int buildId, String projectScmRootUrl,
342 List<Project> projectsWithCommonScmRoot )
343 {
344 File backupDirectory = null;
345 try
346 {
347 backupDirectory = configurationService.getTestReportsDirectory( buildId, project.getId() );
348 if ( !backupDirectory.exists() )
349 {
350 backupDirectory.mkdirs();
351 }
352 }
353 catch ( ConfigurationException e )
354 {
355 log.info( "error on surefire backup directory creation skip backup " + e.getMessage(), e );
356 }
357 backupTestFiles( getWorkingDirectory( project, projectScmRootUrl, projectsWithCommonScmRoot ),
358 backupDirectory );
359 }
360
361 private void backupTestFiles( File workingDir, File backupDirectory )
362 {
363 DirectoryScanner scanner = new DirectoryScanner();
364 scanner.setBasedir( workingDir );
365 scanner.setIncludes(
366 new String[]{"**/target/surefire-reports/TEST-*.xml", "**/target/surefire-it-reports/TEST-*.xml"} );
367 scanner.scan();
368
369 String[] testResultFiles = scanner.getIncludedFiles();
370 if ( testResultFiles.length > 0 )
371 {
372 log.info( "Backup surefire files." );
373 }
374 for ( String testResultFile : testResultFiles )
375 {
376 File xmlFile = new File( workingDir, testResultFile );
377 try
378 {
379 if ( backupDirectory != null )
380 {
381 FileUtils.copyFileToDirectory( xmlFile, backupDirectory );
382 }
383 }
384 catch ( IOException e )
385 {
386 log.info( "failed to backup unit report file " + xmlFile.getPath() );
387 }
388 }
389 }
390
391
392
393
394
395 @Override
396 public boolean shouldBuild( List<ChangeSet> changes, Project continuumProject, File workingDirectory,
397 BuildDefinition buildDefinition )
398 throws ContinuumBuildExecutorException
399 {
400
401 boolean isRecursive = false;
402 if ( StringUtils.isNotEmpty( buildDefinition.getArguments() ) )
403 {
404 isRecursive = buildDefinition.getArguments().indexOf( "-N" ) < 0 && buildDefinition.getArguments().indexOf(
405 "--non-recursive" ) < 0;
406 }
407 if ( isRecursive && changes != null && !changes.isEmpty() )
408 {
409 if ( log.isInfoEnabled() )
410 {
411 log.info( "recursive build and changes found --> building" );
412 }
413 return true;
414 }
415
416 MavenProject project = getMavenProject( continuumProject, workingDirectory, buildDefinition );
417
418 if ( changes == null || changes.isEmpty() )
419 {
420 if ( log.isInfoEnabled() )
421 {
422 log.info( "Found no changes, not building" );
423 }
424 return false;
425 }
426
427
428 List<String> modules = project.getModules();
429
430 List<ChangeFile> files = new ArrayList<ChangeFile>();
431 for ( ChangeSet changeSet : changes )
432 {
433 files.addAll( changeSet.getFiles() );
434 }
435
436 int i = 0;
437 while ( i <= files.size() - 1 )
438 {
439 ChangeFile file = files.get( i );
440 if ( log.isDebugEnabled() )
441 {
442 log.debug( "changeFile.name " + file.getName() );
443 log.debug( "check in modules " + modules );
444 }
445 boolean found = false;
446 for ( String module : modules )
447 {
448 if ( file.getName().indexOf( module ) >= 0 )
449 {
450 if ( log.isDebugEnabled() )
451 {
452 log.debug( "changeFile.name " + file.getName() + " removed because in a module" );
453 }
454 files.remove( file );
455 found = true;
456 break;
457 }
458 if ( log.isDebugEnabled() )
459 {
460 log.debug( "no remving file " + file.getName() + " not in module " + module );
461 }
462 }
463 if ( !found )
464 {
465 i++;
466 }
467 }
468
469 boolean shouldBuild = !files.isEmpty();
470
471 if ( !shouldBuild )
472 {
473 log.info( "Changes are only in sub-modules." );
474 }
475
476 if ( log.isDebugEnabled() )
477 {
478 log.debug( "shoulbuild = " + shouldBuild );
479 }
480 return shouldBuild;
481 }
482
483 @Override
484 protected Map<String, String> getEnvironments( BuildDefinition buildDefinition )
485 {
486 Profile profile = buildDefinition.getProfile();
487 if ( profile == null )
488 {
489 return Collections.EMPTY_MAP;
490 }
491 Map<String, String> envVars = new HashMap<String, String>();
492 String javaHome = getJavaHomeValue( buildDefinition );
493 if ( !StringUtils.isEmpty( javaHome ) )
494 {
495 envVars.put( getInstallationService().getEnvVar( InstallationService.JDK_TYPE ), javaHome );
496 }
497 Installation builder = profile.getBuilder();
498 if ( builder != null )
499 {
500 envVars.put( getInstallationService().getEnvVar( InstallationService.MAVEN2_TYPE ), builder.getVarValue() );
501 }
502 envVars.putAll( getEnvironmentVariables( buildDefinition ) );
503 return envVars;
504
505 }
506 }