View Javadoc

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 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   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
60   * @version $Id: MavenTwoBuildExecutor.java 1372260 2012-08-13 04:29:09Z brett $
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       * @plexus.requirement
80       */
81      private MavenBuilderHelper builderHelper;
82  
83      /**
84       * @plexus.requirement
85       */
86      private MavenProjectHelper projectHelper;
87  
88      /**
89       * @plexus.requirement
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     // ContinuumBuilder Implementation
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         // append -Dmaven.repo.local if project group has a local repository
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         // Maven could help us out a lot more here by knowing how to get the deployment artifacts from a project.
230         // TODO: this is currently quite lame
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             // Attach pom
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             // sources jar
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             // tests sources jar
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             // javadoc jar
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             // client jar
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             // Tests jar
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      * @return true if changes are in the current project, not only in sub-modules and in non-recursive mode
393      * @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)
394      */
395     @Override
396     public boolean shouldBuild( List<ChangeSet> changes, Project continuumProject, File workingDirectory,
397                                 BuildDefinition buildDefinition )
398         throws ContinuumBuildExecutorException
399     {
400         //Check if it's a recursive build
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         //check if changes are only in sub-modules or not
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 }