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