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