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