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