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