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