1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.source;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.file.Path;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Objects;
29
30 import org.apache.maven.archiver.MavenArchiveConfiguration;
31 import org.apache.maven.archiver.MavenArchiver;
32 import org.apache.maven.artifact.Artifact;
33 import org.apache.maven.artifact.DependencyResolutionRequiredException;
34 import org.apache.maven.execution.MavenSession;
35 import org.apache.maven.model.Resource;
36 import org.apache.maven.plugin.AbstractMojo;
37 import org.apache.maven.plugin.MojoExecutionException;
38 import org.apache.maven.plugins.annotations.Component;
39 import org.apache.maven.plugins.annotations.Parameter;
40 import org.apache.maven.project.MavenProject;
41 import org.apache.maven.project.MavenProjectHelper;
42 import org.codehaus.plexus.archiver.Archiver;
43 import org.codehaus.plexus.archiver.ArchiverException;
44 import org.codehaus.plexus.archiver.jar.JarArchiver;
45 import org.codehaus.plexus.archiver.jar.ManifestException;
46 import org.codehaus.plexus.archiver.util.DefaultFileSet;
47 import org.codehaus.plexus.util.FileUtils;
48
49
50
51
52
53
54 public abstract class AbstractSourceJarMojo extends AbstractMojo {
55 private static final String[] DEFAULT_INCLUDES = new String[] {"**/**"};
56
57 private static final String[] DEFAULT_EXCLUDES = new String[] {};
58
59
60
61
62
63
64
65 @Parameter
66 private String[] includes;
67
68
69
70
71
72
73
74 @Parameter
75 private String[] excludes;
76
77
78
79
80
81
82
83 @Parameter(property = "maven.source.useDefaultExcludes", defaultValue = "true")
84 private boolean useDefaultExcludes;
85
86
87
88
89 @Parameter(defaultValue = "${project}", readonly = true, required = true)
90 private MavenProject project;
91
92
93
94
95 @Component(role = Archiver.class, hint = "jar")
96 private JarArchiver jarArchiver;
97
98
99
100
101
102
103
104
105
106
107 @Parameter
108 private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
109
110
111
112
113
114
115
116
117 @Parameter(
118 defaultValue = "${project.build.outputDirectory}/META-INF/MANIFEST.MF",
119 readonly = false,
120 required = true)
121
122 private File defaultManifestFile;
123
124
125
126
127
128
129 @Parameter(property = "maven.source.useDefaultManifestFile", defaultValue = "false")
130 private boolean useDefaultManifestFile;
131
132
133
134
135 @Parameter(property = "maven.source.attach", defaultValue = "true")
136 private boolean attach;
137
138
139
140
141
142
143
144 @Parameter(property = "maven.source.excludeResources", defaultValue = "false")
145 protected boolean excludeResources;
146
147
148
149
150
151
152 @Parameter(property = "maven.source.includePom", defaultValue = "false")
153 protected boolean includePom;
154
155
156
157
158 @Component
159 private MavenProjectHelper projectHelper;
160
161
162
163
164 @Parameter(defaultValue = "${project.build.directory}")
165 protected File outputDirectory;
166
167
168
169
170
171 @Parameter(defaultValue = "${project.build.finalName}")
172 protected String finalName;
173
174
175
176
177 @Parameter(defaultValue = "${reactorProjects}", readonly = true)
178 protected List<MavenProject> reactorProjects;
179
180
181
182
183
184
185
186 @Parameter(property = "maven.source.forceCreation", defaultValue = "false")
187 private boolean forceCreation;
188
189
190
191
192
193
194
195 @Parameter(property = "maven.source.skip", defaultValue = "false")
196 private boolean skipSource;
197
198
199
200
201 @Parameter(defaultValue = "${session}", readonly = true, required = true)
202 private MavenSession session;
203
204
205
206
207
208
209
210
211 @Parameter(defaultValue = "${project.build.outputTimestamp}")
212 private String outputTimestamp;
213
214
215
216
217
218
219
220
221 public void execute() throws MojoExecutionException {
222 if (skipSource) {
223 getLog().info("Skipping source per configuration.");
224 return;
225 }
226
227 packageSources(project);
228 }
229
230
231
232
233
234
235
236
237 protected abstract String getClassifier();
238
239
240
241
242
243
244 protected abstract List<String> getSources(MavenProject p) throws MojoExecutionException;
245
246
247
248
249
250
251 protected abstract List<Resource> getResources(MavenProject p) throws MojoExecutionException;
252
253
254
255
256
257 protected void packageSources(MavenProject p) throws MojoExecutionException {
258 if (!"pom".equals(p.getPackaging())) {
259 packageSources(Collections.singletonList(p));
260 }
261 }
262
263
264
265
266
267 protected void packageSources(List<MavenProject> theProjects) throws MojoExecutionException {
268 if (project.getArtifact().getClassifier() != null) {
269 getLog().warn("NOT adding sources to artifacts with classifier as Maven only supports one classifier "
270 + "per artifact. Current artifact [" + project.getArtifact().getId() + "] has a ["
271 + project.getArtifact().getClassifier() + "] classifier.");
272
273 return;
274 }
275
276 MavenArchiver archiver = createArchiver();
277
278 for (MavenProject pItem : theProjects) {
279 MavenProject subProject = getProject(pItem);
280
281 if ("pom".equals(subProject.getPackaging())) {
282 continue;
283 }
284
285 archiveProjectContent(subProject, archiver.getArchiver());
286 }
287
288 if (archiver.getArchiver().getResources().hasNext() || forceCreation) {
289
290 if (useDefaultManifestFile && defaultManifestFile.exists() && archive.getManifestFile() == null) {
291 getLog().info("Adding existing MANIFEST to archive. Found under: " + defaultManifestFile.getPath());
292 archive.setManifestFile(defaultManifestFile);
293 }
294
295 File outputFile = new File(outputDirectory, finalName + "-" + getClassifier() + getExtension());
296
297 try {
298 archiver.setOutputFile(outputFile);
299 archive.setForced(forceCreation);
300
301 getLog().debug("create archive " + outputFile);
302 archiver.createArchive(session, project, archive);
303 } catch (IOException | ArchiverException | DependencyResolutionRequiredException | ManifestException e) {
304 throw new MojoExecutionException("Error creating source archive: " + e.getMessage(), e);
305 }
306
307 if (attach) {
308 boolean requiresAttach = true;
309 for (Artifact attachedArtifact : project.getAttachedArtifacts()) {
310 Artifact previouslyAttachedArtifact =
311 getPreviouslyAttached(attachedArtifact, project, getClassifier());
312 if (previouslyAttachedArtifact != null) {
313 File previouslyAttachedFile = previouslyAttachedArtifact.getFile();
314
315 if (!outputFile.equals(previouslyAttachedFile)) {
316 getLog().error("Artifact " + previouslyAttachedArtifact.getId()
317 + " already attached to a file " + relative(previouslyAttachedFile) + ": attach to "
318 + relative(outputFile) + " should be done with another classifier");
319 throw new MojoExecutionException("Presumably you have configured maven-source-plugin "
320 + "to execute twice in your build to different output files. "
321 + "You have to configure a classifier for at least one of them.");
322 }
323 requiresAttach = false;
324 getLog().info("Artifact " + previouslyAttachedArtifact.getId() + " already attached to "
325 + relative(outputFile) + ": ignoring same re-attach (same artifact, same file)");
326 }
327 }
328 if (requiresAttach) {
329 projectHelper.attachArtifact(project, getType(), getClassifier(), outputFile);
330 }
331 } else {
332 getLog().info("NOT adding java-sources to attached artifacts list.");
333 }
334 } else {
335 getLog().info("No sources in project. Archive not created.");
336 }
337 }
338
339 private String relative(File to) {
340 Path basedir = project.getBasedir().getAbsoluteFile().toPath();
341 return basedir.relativize(to.getAbsoluteFile().toPath()).toString();
342 }
343
344 private Artifact getPreviouslyAttached(Artifact artifact, MavenProject checkProject, String classifier) {
345 return artifact.getType().equals(getType())
346 && artifact.getGroupId().equals(checkProject.getGroupId())
347 && artifact.getArtifactId().equals(checkProject.getArtifactId())
348 && artifact.getVersion().equals(checkProject.getVersion())
349 && Objects.equals(artifact.getClassifier(), classifier)
350 ? artifact
351 : null;
352 }
353
354
355
356
357
358
359 protected void archiveProjectContent(MavenProject p, Archiver archiver) throws MojoExecutionException {
360 if (includePom) {
361 try {
362 archiver.addFile(p.getFile(), p.getFile().getName());
363 } catch (ArchiverException e) {
364 throw new MojoExecutionException("Error adding POM file to target jar file.", e);
365 }
366 }
367
368 for (String s : getSources(p)) {
369
370 File sourceDirectory = new File(s);
371
372 if (sourceDirectory.exists()) {
373 addDirectory(archiver, sourceDirectory, getCombinedIncludes(null), getCombinedExcludes(null));
374 }
375 }
376
377
378 for (Resource resource : getResources(p)) {
379
380 File sourceDirectory = new File(resource.getDirectory());
381
382 if (!sourceDirectory.exists()) {
383 continue;
384 }
385
386 List<String> resourceIncludes = resource.getIncludes();
387
388 String[] combinedIncludes = getCombinedIncludes(resourceIncludes);
389
390 List<String> resourceExcludes = resource.getExcludes();
391
392 String[] combinedExcludes = getCombinedExcludes(resourceExcludes);
393
394 String targetPath = resource.getTargetPath();
395 if (targetPath != null) {
396 if (!targetPath.trim().endsWith("/")) {
397 targetPath += "/";
398 }
399 addDirectory(archiver, sourceDirectory, targetPath, combinedIncludes, combinedExcludes);
400 } else {
401 addDirectory(archiver, sourceDirectory, combinedIncludes, combinedExcludes);
402 }
403 }
404 }
405
406
407
408
409
410 protected MavenArchiver createArchiver() throws MojoExecutionException {
411 MavenArchiver archiver = new MavenArchiver();
412 archiver.setArchiver(jarArchiver);
413 archiver.setCreatedBy("Maven Source Plugin", "org.apache.maven.plugins", "maven-source-plugin");
414 archiver.setBuildJdkSpecDefaultEntry(false);
415
416
417 archiver.configureReproducibleBuild(outputTimestamp);
418
419 if (project.getBuild() != null) {
420 List<Resource> resources = project.getBuild().getResources();
421
422 for (Resource r : resources) {
423 if (r.getDirectory().endsWith("maven-shared-archive-resources")) {
424 addDirectory(
425 archiver.getArchiver(),
426 new File(r.getDirectory()),
427 getCombinedIncludes(null),
428 getCombinedExcludes(null));
429 }
430 }
431 }
432
433 return archiver;
434 }
435
436
437
438
439
440
441
442
443 protected void addDirectory(Archiver archiver, File sourceDirectory, String[] pIncludes, String[] pExcludes)
444 throws MojoExecutionException {
445 try {
446 getLog().debug("add directory " + sourceDirectory + " to archiver");
447 archiver.addFileSet(DefaultFileSet.fileSet(sourceDirectory).includeExclude(pIncludes, pExcludes));
448 } catch (ArchiverException e) {
449 throw new MojoExecutionException("Error adding directory to source archive.", e);
450 }
451 }
452
453
454
455
456
457
458
459
460
461 protected void addDirectory(
462 Archiver archiver, File sourceDirectory, String prefix, String[] pIncludes, String[] pExcludes)
463 throws MojoExecutionException {
464 try {
465 getLog().debug("add directory " + sourceDirectory + " to archiver with prefix " + prefix);
466 archiver.addFileSet(
467 DefaultFileSet.fileSet(sourceDirectory).prefixed(prefix).includeExclude(pIncludes, pExcludes));
468 } catch (ArchiverException e) {
469 throw new MojoExecutionException("Error adding directory to source archive.", e);
470 }
471 }
472
473
474
475
476 protected String getExtension() {
477 return ".jar";
478 }
479
480
481
482
483
484 protected MavenProject getProject(MavenProject p) {
485 if (p.getExecutionProject() != null) {
486 return p.getExecutionProject();
487 }
488
489 return p;
490 }
491
492
493
494
495 protected String getType() {
496 return "java-source";
497 }
498
499
500
501
502
503
504
505
506 private String[] getCombinedIncludes(List<String> additionalIncludes) {
507 List<String> combinedIncludes = new ArrayList<>();
508
509 if (includes != null && includes.length > 0) {
510 combinedIncludes.addAll(Arrays.asList(includes));
511 }
512
513 if (additionalIncludes != null && !additionalIncludes.isEmpty()) {
514 combinedIncludes.addAll(additionalIncludes);
515 }
516
517
518 if (combinedIncludes.isEmpty()) {
519 combinedIncludes.addAll(Arrays.asList(DEFAULT_INCLUDES));
520 }
521
522 return combinedIncludes.toArray(new String[0]);
523 }
524
525
526
527
528
529
530
531
532 private String[] getCombinedExcludes(List<String> additionalExcludes) {
533 List<String> combinedExcludes = new ArrayList<>();
534
535 if (useDefaultExcludes) {
536 combinedExcludes.addAll(FileUtils.getDefaultExcludesAsList());
537 }
538
539 if (excludes != null && excludes.length > 0) {
540 combinedExcludes.addAll(Arrays.asList(excludes));
541 }
542
543 if (additionalExcludes != null && !additionalExcludes.isEmpty()) {
544 combinedExcludes.addAll(additionalExcludes);
545 }
546
547 if (combinedExcludes.isEmpty()) {
548 combinedExcludes.addAll(Arrays.asList(DEFAULT_EXCLUDES));
549 }
550
551 return combinedExcludes.toArray(new String[0]);
552 }
553
554
555
556
557 protected MavenProject getProject() {
558 return project;
559 }
560
561
562
563
564 protected void setProject(MavenProject project) {
565 this.project = project;
566 }
567 }