View Javadoc

1   package org.apache.maven.plugin.assembly.archive.phase;
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.HashMap;
27  import java.util.Iterator;
28  import java.util.LinkedHashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.artifact.ArtifactUtils;
35  import org.apache.maven.plugin.assembly.AssemblerConfigurationSource;
36  import org.apache.maven.plugin.assembly.AssemblyContext;
37  import org.apache.maven.plugin.assembly.InvalidAssemblerConfigurationException;
38  import org.apache.maven.plugin.assembly.archive.ArchiveCreationException;
39  import org.apache.maven.plugin.assembly.archive.task.AddArtifactTask;
40  import org.apache.maven.plugin.assembly.archive.task.AddDependencySetsTask;
41  import org.apache.maven.plugin.assembly.archive.task.AddFileSetsTask;
42  import org.apache.maven.plugin.assembly.format.AssemblyFormattingException;
43  import org.apache.maven.plugin.assembly.model.Assembly;
44  import org.apache.maven.plugin.assembly.model.DependencySet;
45  import org.apache.maven.plugin.assembly.model.FileSet;
46  import org.apache.maven.plugin.assembly.model.ModuleBinaries;
47  import org.apache.maven.plugin.assembly.model.ModuleSet;
48  import org.apache.maven.plugin.assembly.model.ModuleSources;
49  import org.apache.maven.plugin.assembly.utils.AssemblyFormatUtils;
50  import org.apache.maven.plugin.assembly.utils.FilterUtils;
51  import org.apache.maven.plugin.assembly.utils.ProjectUtils;
52  import org.apache.maven.plugin.assembly.utils.TypeConversionUtils;
53  import org.apache.maven.project.MavenProject;
54  import org.apache.maven.project.MavenProjectBuilder;
55  import org.codehaus.plexus.archiver.Archiver;
56  import org.codehaus.plexus.archiver.manager.ArchiverManager;
57  import org.codehaus.plexus.component.annotations.Component;
58  import org.codehaus.plexus.component.annotations.Requirement;
59  import org.codehaus.plexus.logging.AbstractLogEnabled;
60  import org.codehaus.plexus.logging.Logger;
61  
62  /**
63   * Handles the <moduleSets/> top-level section of the assembly descriptor.
64   * 
65   * @version $Id: ModuleSetAssemblyPhase.java 1163853 2011-08-31 22:42:32Z jdcasey $
66   */
67  @Component( role = AssemblyArchiverPhase.class, hint = "module-sets" )
68  public class ModuleSetAssemblyPhase
69      extends AbstractLogEnabled
70      implements AssemblyArchiverPhase
71  {
72  
73      @Requirement
74      private MavenProjectBuilder projectBuilder;
75  
76      @Requirement
77      private ArchiverManager archiverManager;
78  
79      public ModuleSetAssemblyPhase()
80      {
81          // needed for plexus
82      }
83  
84      public ModuleSetAssemblyPhase( final MavenProjectBuilder projectBuilder, final Logger logger )
85      {
86          this.projectBuilder = projectBuilder;
87          enableLogging( logger );
88      }
89  
90      /**
91       * {@inheritDoc}
92       */
93      public void execute( final Assembly assembly, final Archiver archiver,
94                           final AssemblerConfigurationSource configSource, final AssemblyContext context )
95          throws ArchiveCreationException, AssemblyFormattingException, InvalidAssemblerConfigurationException
96      {
97          final List<ModuleSet> moduleSets = assembly.getModuleSets();
98  
99          for ( final Iterator<ModuleSet> i = moduleSets.iterator(); i.hasNext(); )
100         {
101             final ModuleSet moduleSet = i.next();
102 
103             validate( moduleSet, configSource );
104 
105             final Set<MavenProject> moduleProjects = getModuleProjects( moduleSet, configSource, getLogger() );
106 
107             final ModuleSources sources = moduleSet.getSources();
108             addModuleSourceFileSets( sources, moduleProjects, archiver, configSource );
109 
110             final ModuleBinaries binaries = moduleSet.getBinaries();
111             addModuleBinaries( binaries, moduleProjects, archiver, configSource, context );
112         }
113     }
114 
115     private void validate( final ModuleSet moduleSet, final AssemblerConfigurationSource configSource )
116     {
117         if ( ( moduleSet.getSources() == null ) && ( moduleSet.getBinaries() == null ) )
118         {
119             getLogger().warn( "Encountered ModuleSet with no sources or binaries specified. Skipping." );
120         }
121 
122         if ( moduleSet.isUseAllReactorProjects() && !moduleSet.isIncludeSubModules() )
123         {
124             getLogger().warn( "includeSubModules == false is incompatible with useAllReactorProjects. Ignoring."
125                                   + "\n\nTo refactor, remove the <includeSubModules/> flag, and use the <includes/> "
126                                   + "and <excludes/> sections to fine-tune the modules included." );
127         }
128 
129         final List<MavenProject> projects = configSource.getReactorProjects();
130         if ( projects != null && projects.size() > 1 && projects.indexOf( configSource.getProject() ) == 0
131             && moduleSet.getBinaries() != null )
132         {
133             getLogger().warn( "[DEPRECATION] moduleSet/binaries section detected in root-project assembly."
134                                   + "\n\nMODULE BINARIES MAY NOT BE AVAILABLE FOR THIS ASSEMBLY!"
135                                   + "\n\n To refactor, move this assembly into a child project and use the flag "
136                                   + "<useAllReactorProjects>true</useAllReactorProjects> in each moduleSet." );
137         }
138 
139         if ( moduleSet.getSources() != null )
140         {
141             final ModuleSources sources = moduleSet.getSources();
142             if ( isDeprecatedModuleSourcesConfigPresent( sources ) )
143             {
144                 getLogger().warn( "[DEPRECATION] Use of <moduleSources/> as a file-set is deprecated. "
145                                       + "Please use the <fileSets/> sub-element of <moduleSources/> instead." );
146             }
147             else if ( !sources.isUseDefaultExcludes() )
148             {
149                 getLogger().warn( "[DEPRECATION] Use of directoryMode, fileMode, or useDefaultExcludes "
150                                       + "elements directly within <moduleSources/> are all deprecated. "
151                                       + "Please use the <fileSets/> sub-element of <moduleSources/> instead." );
152             }
153         }
154     }
155 
156     protected void addModuleBinaries( final ModuleBinaries binaries, final Set<MavenProject> projects,
157                                       final Archiver archiver, final AssemblerConfigurationSource configSource,
158                                       final AssemblyContext context )
159         throws ArchiveCreationException, AssemblyFormattingException, InvalidAssemblerConfigurationException
160     {
161         if ( binaries == null )
162         {
163             return;
164         }
165 
166         final Set<MavenProject> moduleProjects = new LinkedHashSet<MavenProject>( projects );
167 
168         for ( final Iterator<MavenProject> it = moduleProjects.iterator(); it.hasNext(); )
169         {
170             final MavenProject project = it.next();
171 
172             if ( "pom".equals( project.getPackaging() ) )
173             {
174                 final String projectId = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
175 
176                 getLogger().debug( "Excluding POM-packaging module: " + projectId );
177 
178                 it.remove();
179             }
180         }
181 
182         final String classifier = binaries.getAttachmentClassifier();
183 
184         final Map<MavenProject, Artifact> chosenModuleArtifacts = new HashMap<MavenProject, Artifact>();
185 
186         for ( final Iterator<MavenProject> j = moduleProjects.iterator(); j.hasNext(); )
187         {
188             final MavenProject project = j.next();
189 
190             Artifact artifact = null;
191 
192             if ( classifier == null )
193             {
194                 getLogger().debug( "Processing binary artifact for module project: " + project.getId() );
195 
196                 artifact = project.getArtifact();
197             }
198             else
199             {
200                 getLogger().debug( "Processing binary attachment: " + classifier + " for module project: "
201                                        + project.getId() );
202 
203                 @SuppressWarnings( "unchecked" )
204                 final List<Artifact> attachments = project.getAttachedArtifacts();
205                 if ( ( attachments != null ) && !attachments.isEmpty() )
206                 {
207                     for ( final Iterator<Artifact> attachmentIterator = attachments.iterator(); attachmentIterator.hasNext(); )
208                     {
209                         final Artifact attachment = attachmentIterator.next();
210 
211                         if ( classifier.equals( attachment.getClassifier() ) )
212                         {
213                             artifact = attachment;
214                             break;
215                         }
216                     }
217                 }
218 
219                 if ( artifact == null )
220                 {
221                     throw new InvalidAssemblerConfigurationException( "Cannot find attachment with classifier: "
222                         + classifier + " in module project: " + project.getId()
223                         + ". Please exclude this module from the module-set." );
224                 }
225             }
226 
227             chosenModuleArtifacts.put( project, artifact );
228             addModuleArtifact( artifact, project, archiver, configSource, binaries );
229         }
230 
231         final List<DependencySet> depSets = getDependencySets( binaries );
232 
233         if ( depSets != null )
234         {
235             for ( final Iterator<DependencySet> it = depSets.iterator(); it.hasNext(); )
236             {
237                 final DependencySet ds = it.next();
238 
239                 // NOTE: Disabling useProjectArtifact flag, since module artifact has already been handled!
240                 ds.setUseProjectArtifact( false );
241             }
242 
243             // FIXME: This will produce unpredictable results when module dependencies have a version conflict.
244             getLogger().warn( "NOTE: Currently, inclusion of module dependencies may produce unpredictable "
245                                   + "results if a version conflict occurs." );
246 
247             for ( final Iterator<MavenProject> it = moduleProjects.iterator(); it.hasNext(); )
248             {
249                 final MavenProject moduleProject = it.next();
250 
251                 getLogger().debug( "Processing binary dependencies for module project: " + moduleProject.getId() );
252 
253                 final AddDependencySetsTask task =
254                     new AddDependencySetsTask( depSets, context.getResolvedArtifacts(), moduleProject, projectBuilder,
255                                                archiverManager, getLogger() );
256 
257                 task.setModuleProject( moduleProject );
258                 task.setModuleArtifact( chosenModuleArtifacts.get( moduleProject ) );
259                 task.setDefaultOutputDirectory( binaries.getOutputDirectory() );
260                 task.setDefaultOutputFileNameMapping( binaries.getOutputFileNameMapping() );
261 
262                 task.execute( archiver, configSource );
263             }
264         }
265     }
266 
267     public static List<DependencySet> getDependencySets( final ModuleBinaries binaries )
268     {
269         List<DependencySet> depSets = binaries.getDependencySets();
270 
271         if ( ( ( depSets == null ) || depSets.isEmpty() ) && binaries.isIncludeDependencies() )
272         {
273             final DependencySet impliedDependencySet = new DependencySet();
274 
275             impliedDependencySet.setOutputDirectory( binaries.getOutputDirectory() );
276             impliedDependencySet.setOutputFileNameMapping( binaries.getOutputFileNameMapping() );
277             impliedDependencySet.setFileMode( binaries.getFileMode() );
278             impliedDependencySet.setDirectoryMode( binaries.getDirectoryMode() );
279             impliedDependencySet.setExcludes( binaries.getExcludes() );
280             impliedDependencySet.setIncludes( binaries.getIncludes() );
281             impliedDependencySet.setUnpack( binaries.isUnpack() );
282             // unpackOptions is handled in the first stage of dependency-set handling, below.
283 
284             depSets = Collections.singletonList( impliedDependencySet );
285         }
286 
287         return depSets;
288     }
289 
290     // protected List<String> collectExcludesFromQueuedArtifacts( final Set visitedArtifacts, final List binaryExcludes
291     // )
292     // {
293     // List excludes = binaryExcludes;
294     //
295     // if ( excludes == null )
296     // {
297     // excludes = new ArrayList();
298     // }
299     // else
300     // {
301     // excludes = new ArrayList( excludes );
302     // }
303     //
304     // for ( final Iterator it = visitedArtifacts.iterator(); it.hasNext(); )
305     // {
306     // excludes.add( it.next() );
307     // }
308     //
309     // return excludes;
310     // }
311 
312     protected void addModuleArtifact( final Artifact artifact, final MavenProject project, final Archiver archiver,
313                                       final AssemblerConfigurationSource configSource, final ModuleBinaries binaries )
314         throws ArchiveCreationException, AssemblyFormattingException
315     {
316         if ( artifact.getFile() == null )
317         {
318             throw new ArchiveCreationException( "Artifact: " + artifact.getId()
319                 + " (included by module) does not have an artifact with a file. "
320                 + "Please ensure the package phase is run before the assembly is generated." );
321         }
322 
323         final AddArtifactTask task = new AddArtifactTask( artifact, getLogger() );
324 
325         task.setFileNameMapping( binaries.getOutputFileNameMapping() );
326         task.setOutputDirectory( binaries.getOutputDirectory() );
327         task.setProject( project );
328         task.setModuleProject( project );
329         task.setModuleArtifact( artifact );
330 
331         final int dirMode = TypeConversionUtils.modeToInt( binaries.getDirectoryMode(), getLogger() );
332         if ( dirMode != -1 )
333         {
334             task.setDirectoryMode( dirMode );
335         }
336 
337         final int fileMode = TypeConversionUtils.modeToInt( binaries.getFileMode(), getLogger() );
338         if ( fileMode != -1 )
339         {
340             task.setFileMode( fileMode );
341         }
342 
343         task.setUnpack( binaries.isUnpack() );
344 
345         if ( binaries.isUnpack() && binaries.getUnpackOptions() != null )
346         {
347             task.setIncludes( binaries.getUnpackOptions().getIncludes() );
348             task.setExcludes( binaries.getUnpackOptions().getExcludes() );
349         }
350 
351         task.execute( archiver, configSource );
352     }
353 
354     protected void addModuleSourceFileSets( final ModuleSources sources, final Set<MavenProject> moduleProjects,
355                                             final Archiver archiver, final AssemblerConfigurationSource configSource )
356         throws ArchiveCreationException, AssemblyFormattingException
357     {
358         if ( sources == null )
359         {
360             return;
361         }
362 
363         final List<FileSet> fileSets = new ArrayList<FileSet>();
364 
365         if ( isDeprecatedModuleSourcesConfigPresent( sources ) )
366         {
367             final FileSet fs = new FileSet();
368             fs.setOutputDirectory( sources.getOutputDirectory() );
369             fs.setIncludes( sources.getIncludes() );
370             fs.setExcludes( sources.getExcludes() );
371             fs.setUseDefaultExcludes( sources.isUseDefaultExcludes() );
372 
373             fileSets.add( fs );
374         }
375 
376         List<FileSet> subFileSets = sources.getFileSets();
377 
378         if ( ( subFileSets == null ) || subFileSets.isEmpty() )
379         {
380             final FileSet fs = new FileSet();
381             fs.setDirectory( "src" );
382 
383             subFileSets = Collections.singletonList( fs );
384         }
385 
386         fileSets.addAll( subFileSets );
387 
388         for ( final Iterator<MavenProject> j = moduleProjects.iterator(); j.hasNext(); )
389         {
390             final MavenProject moduleProject = j.next();
391 
392             getLogger().info( "Processing sources for module project: " + moduleProject.getId() );
393 
394             final List<FileSet> moduleFileSets = new ArrayList<FileSet>();
395 
396             for ( final Iterator<FileSet> fsIterator = fileSets.iterator(); fsIterator.hasNext(); )
397             {
398                 final FileSet fileSet = fsIterator.next();
399 
400                 moduleFileSets.add( createFileSet( fileSet, sources, moduleProject, configSource ) );
401             }
402 
403             final AddFileSetsTask task = new AddFileSetsTask( moduleFileSets );
404 
405             task.setProject( moduleProject );
406             task.setModuleProject( moduleProject );
407             task.setLogger( getLogger() );
408 
409             task.execute( archiver, configSource );
410         }
411     }
412 
413     /**
414      * Determine whether the deprecated file-set configuration directly within the ModuleSources object is present.
415      */
416     protected boolean isDeprecatedModuleSourcesConfigPresent( final ModuleSources sources )
417     {
418         boolean result = false;
419 
420         if ( sources.getOutputDirectory() != null )
421         {
422             result = true;
423         }
424         else if ( ( sources.getIncludes() != null ) && !sources.getIncludes().isEmpty() )
425         {
426             result = true;
427         }
428         else if ( ( sources.getExcludes() != null ) && !sources.getExcludes().isEmpty() )
429         {
430             result = true;
431         }
432 
433         return result;
434     }
435 
436     protected FileSet createFileSet( final FileSet fileSet, final ModuleSources sources,
437                                      final MavenProject moduleProject, final AssemblerConfigurationSource configSource )
438         throws AssemblyFormattingException
439     {
440         final FileSet fs = new FileSet();
441 
442         String sourcePath = fileSet.getDirectory();
443 
444         final File moduleBasedir = moduleProject.getBasedir();
445 
446         if ( sourcePath != null )
447         {
448             final File sourceDir = new File( sourcePath );
449 
450             if ( !sourceDir.isAbsolute() )
451             {
452                 sourcePath = new File( moduleBasedir, sourcePath ).getAbsolutePath();
453             }
454         }
455         else
456         {
457             sourcePath = moduleBasedir.getAbsolutePath();
458         }
459 
460         fs.setDirectory( sourcePath );
461         fs.setDirectoryMode( fileSet.getDirectoryMode() );
462 
463         final List<String> excludes = new ArrayList<String>();
464 
465         final List<String> originalExcludes = fileSet.getExcludes();
466         if ( ( originalExcludes != null ) && !originalExcludes.isEmpty() )
467         {
468             excludes.addAll( originalExcludes );
469         }
470 
471         if ( sources.isExcludeSubModuleDirectories() )
472         {
473             @SuppressWarnings( "unchecked" )
474             final List<String> modules = moduleProject.getModules();
475             for ( final Iterator<String> moduleIterator = modules.iterator(); moduleIterator.hasNext(); )
476             {
477                 final String moduleSubPath = moduleIterator.next();
478 
479                 excludes.add( moduleSubPath + "/**" );
480             }
481         }
482 
483         fs.setExcludes( excludes );
484         fs.setFiltered( fileSet.isFiltered() );
485         fs.setFileMode( fileSet.getFileMode() );
486         fs.setIncludes( fileSet.getIncludes() );
487         fs.setLineEnding( fileSet.getLineEnding() );
488 
489         String destPathPrefix = "";
490         if ( sources.isIncludeModuleDirectory() )
491         {
492             destPathPrefix =
493                 AssemblyFormatUtils.evaluateFileNameMapping( sources.getOutputDirectoryMapping(),
494                                                              moduleProject.getArtifact(), configSource.getProject(),
495                                                              moduleProject, moduleProject.getArtifact(), moduleProject,
496                                                              configSource );
497 
498             if ( !destPathPrefix.endsWith( "/" ) )
499             {
500                 destPathPrefix += "/";
501             }
502         }
503 
504         String destPath = fileSet.getOutputDirectory();
505 
506         if ( destPath == null )
507         {
508             destPath = destPathPrefix;
509         }
510         else
511         {
512             destPath = destPathPrefix + destPath;
513         }
514 
515         destPath =
516             AssemblyFormatUtils.getOutputDirectory( destPath, configSource.getProject(), moduleProject, moduleProject,
517                                                     configSource.getFinalName(), configSource );
518 
519         fs.setOutputDirectory( destPath );
520 
521         getLogger().debug( "module source directory is: " + sourcePath );
522         getLogger().debug( "module dest directory is: " + destPath + " (assembly basedir may be prepended)" );
523 
524         return fs;
525     }
526 
527     public static Set<MavenProject> getModuleProjects( final ModuleSet moduleSet,
528                                                        final AssemblerConfigurationSource configSource,
529                                                        final Logger logger )
530         throws ArchiveCreationException
531     {
532         MavenProject project = configSource.getProject();
533         Set<MavenProject> moduleProjects = null;
534 
535         if ( moduleSet.isUseAllReactorProjects() )
536         {
537             if ( !moduleSet.isIncludeSubModules() )
538             {
539                 moduleProjects = new LinkedHashSet<MavenProject>( configSource.getReactorProjects() );
540             }
541 
542             project = configSource.getReactorProjects().get( 0 );
543         }
544 
545         if ( moduleProjects == null )
546         {
547             try
548             {
549                 moduleProjects =
550                     ProjectUtils.getProjectModules( project, configSource.getReactorProjects(),
551                                                     moduleSet.isIncludeSubModules(), logger );
552             }
553             catch ( final IOException e )
554             {
555                 throw new ArchiveCreationException( "Error retrieving module-set for project: " + project.getId()
556                     + ": " + e.getMessage(), e );
557             }
558         }
559 
560         FilterUtils.filterProjects( moduleProjects, moduleSet.getIncludes(), moduleSet.getExcludes(), true, logger );
561         return moduleProjects;
562     }
563 
564 }