1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.plugin;
20
21 import java.io.File;
22 import java.net.URI;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.LinkedHashSet;
26 import java.util.List;
27 import java.util.Set;
28
29 import org.apache.maven.artifact.Artifact;
30 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
31 import org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
34 import org.apache.maven.plugin.descriptor.PluginDescriptor;
35 import org.apache.maven.plugins.annotations.Component;
36 import org.apache.maven.plugins.annotations.LifecyclePhase;
37 import org.apache.maven.plugins.annotations.Mojo;
38 import org.apache.maven.plugins.annotations.Parameter;
39 import org.apache.maven.plugins.annotations.ResolutionScope;
40 import org.apache.maven.settings.Settings;
41 import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
42 import org.apache.maven.tools.plugin.ExtendedPluginDescriptor;
43 import org.apache.maven.tools.plugin.PluginToolsRequest;
44 import org.apache.maven.tools.plugin.extractor.ExtractionException;
45 import org.apache.maven.tools.plugin.generator.GeneratorException;
46 import org.apache.maven.tools.plugin.generator.GeneratorUtils;
47 import org.apache.maven.tools.plugin.generator.PluginDescriptorFilesGenerator;
48 import org.apache.maven.tools.plugin.scanner.MojoScanner;
49 import org.codehaus.plexus.component.repository.ComponentDependency;
50 import org.codehaus.plexus.util.ReaderFactory;
51 import org.eclipse.aether.RepositorySystemSession;
52 import org.sonatype.plexus.build.incremental.BuildContext;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 @Mojo(
69 name = "descriptor",
70 defaultPhase = LifecyclePhase.PROCESS_CLASSES,
71 requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
72 threadSafe = true)
73 public class DescriptorGeneratorMojo extends AbstractGeneratorMojo {
74 private static final String VALUE_AUTO = "auto";
75
76
77
78
79 @Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/maven", readonly = true)
80 private File outputDirectory;
81
82
83
84
85
86
87 @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}")
88 private String encoding;
89
90
91
92
93
94
95 @Parameter(defaultValue = "false")
96 private boolean skipDescriptor;
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 @Parameter
130 private Set<String> extractors;
131
132
133
134
135
136
137
138
139 @Parameter(property = "maven.plugin.skipErrorNoDescriptorsFound", defaultValue = "false")
140 private boolean skipErrorNoDescriptorsFound;
141
142
143
144
145
146
147
148 @Parameter(defaultValue = "true", property = "maven.plugin.checkExpectedProvidedScope")
149 private boolean checkExpectedProvidedScope = true;
150
151
152
153
154
155
156
157 @Parameter
158 private List<String> expectedProvidedScopeGroupIds = Collections.singletonList("org.apache.maven");
159
160
161
162
163
164
165
166
167 @Parameter
168 private List<String> expectedProvidedScopeExclusions = Arrays.asList(
169 "org.apache.maven:maven-archiver", "org.apache.maven:maven-jxr", "org.apache.maven:plexus-utils");
170
171
172
173
174
175
176
177
178
179 @Parameter
180 private List<String> mojoDependencies = null;
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 @Parameter(property = "externalJavadocBaseUrls", alias = "links")
200 protected List<URI> externalJavadocBaseUrls;
201
202
203
204
205
206
207
208
209
210
211 @Parameter(property = "internalJavadocBaseUrl")
212 protected URI internalJavadocBaseUrl;
213
214
215
216
217
218
219
220
221 @Parameter(property = "internalJavadocVersion", defaultValue = "${java.version}")
222 protected String internalJavadocVersion;
223
224
225
226
227
228
229 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
230 private Settings settings;
231
232 @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, required = true)
233 private RepositorySystemSession repoSession;
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248 @Parameter(defaultValue = VALUE_AUTO)
249 String requiredJavaVersion;
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 @Parameter(defaultValue = VALUE_AUTO)
268 String requiredMavenVersion;
269
270
271
272
273 @Component
274 private MojoScanner mojoScanner;
275
276 @Component
277 protected BuildContext buildContext;
278
279 public void generate() throws MojoExecutionException {
280
281 if (!"maven-plugin".equalsIgnoreCase(project.getArtifactId())
282 && project.getArtifactId().toLowerCase().startsWith("maven-")
283 && project.getArtifactId().toLowerCase().endsWith("-plugin")
284 && !"org.apache.maven.plugins".equals(project.getGroupId())) {
285 getLog().warn(LS + LS + "Artifact Ids of the format maven-___-plugin are reserved for" + LS
286 + "plugins in the Group Id org.apache.maven.plugins" + LS
287 + "Please change your artifactId to the format ___-maven-plugin" + LS
288 + "In the future this error will break the build." + LS + LS);
289 }
290
291 if (skipDescriptor) {
292 getLog().warn("Execution skipped");
293 return;
294 }
295
296 if (checkExpectedProvidedScope) {
297 Set<Artifact> wrongScopedArtifacts = dependenciesNotInProvidedScope();
298 if (!wrongScopedArtifacts.isEmpty()) {
299 StringBuilder message = new StringBuilder(
300 LS + LS + "Some dependencies of Maven Plugins are expected to be in provided scope." + LS
301 + "Please make sure that dependencies listed below declared in POM" + LS
302 + "have set '<scope>provided</scope>' as well." + LS + LS
303 + "The following dependencies are in wrong scope:" + LS);
304 for (Artifact artifact : wrongScopedArtifacts) {
305 message.append(" * ").append(artifact).append(LS);
306 }
307 message.append(LS).append(LS);
308
309 getLog().warn(message.toString());
310 }
311 }
312
313 mojoScanner.setActiveExtractors(extractors);
314
315
316 PluginDescriptor pluginDescriptor = new PluginDescriptor();
317
318 pluginDescriptor.setGroupId(project.getGroupId());
319
320 pluginDescriptor.setArtifactId(project.getArtifactId());
321
322 pluginDescriptor.setVersion(project.getVersion());
323
324 pluginDescriptor.setGoalPrefix(goalPrefix);
325
326 pluginDescriptor.setName(project.getName());
327
328 pluginDescriptor.setDescription(project.getDescription());
329
330 if (encoding == null || encoding.length() < 1) {
331 getLog().warn("Using platform encoding (" + ReaderFactory.FILE_ENCODING
332 + " actually) to read mojo source files, i.e. build is platform dependent!");
333 } else {
334 getLog().info("Using '" + encoding + "' encoding to read mojo source files.");
335 }
336
337 if (internalJavadocBaseUrl != null && !internalJavadocBaseUrl.getPath().endsWith("/")) {
338 throw new MojoExecutionException("Given parameter 'internalJavadocBaseUrl' must end with a slash but is '"
339 + internalJavadocBaseUrl + "'");
340 }
341 try {
342 List<ComponentDependency> deps = GeneratorUtils.toComponentDependencies(project.getArtifacts());
343 pluginDescriptor.setDependencies(deps);
344
345 PluginToolsRequest request = new DefaultPluginToolsRequest(project, pluginDescriptor);
346 request.setEncoding(encoding);
347 request.setSkipErrorNoDescriptorsFound(skipErrorNoDescriptorsFound);
348 request.setDependencies(filterMojoDependencies());
349 request.setRepoSession(repoSession);
350 request.setInternalJavadocBaseUrl(internalJavadocBaseUrl);
351 request.setInternalJavadocVersion(internalJavadocVersion);
352 request.setExternalJavadocBaseUrls(externalJavadocBaseUrls);
353 request.setSettings(settings);
354
355 mojoScanner.populatePluginDescriptor(request);
356 request.setPluginDescriptor(extendPluginDescriptor(request));
357
358 outputDirectory.mkdirs();
359
360 PluginDescriptorFilesGenerator pluginDescriptorGenerator = new PluginDescriptorFilesGenerator();
361 pluginDescriptorGenerator.execute(outputDirectory, request);
362
363 buildContext.refresh(outputDirectory);
364 } catch (GeneratorException e) {
365 throw new MojoExecutionException("Error writing plugin descriptor", e);
366 } catch (InvalidPluginDescriptorException | ExtractionException e) {
367 throw new MojoExecutionException(
368 "Error extracting plugin descriptor: '" + e.getLocalizedMessage() + "'", e);
369 } catch (LinkageError e) {
370 throw new MojoExecutionException(
371 "The API of the mojo scanner is not compatible with this plugin version."
372 + " Please check the plugin dependencies configured"
373 + " in the POM and ensure the versions match.",
374 e);
375 }
376 }
377
378 private PluginDescriptor extendPluginDescriptor(PluginToolsRequest request) {
379 ExtendedPluginDescriptor extendedPluginDescriptor = new ExtendedPluginDescriptor(request.getPluginDescriptor());
380 extendedPluginDescriptor.setRequiredJavaVersion(getRequiredJavaVersion(request));
381 extendedPluginDescriptor.setRequiredMavenVersion(getRequiredMavenVersion(request));
382 return extendedPluginDescriptor;
383 }
384
385 private String getRequiredMavenVersion(PluginToolsRequest request) {
386 if (!VALUE_AUTO.equals(requiredMavenVersion)) {
387 return requiredMavenVersion;
388 }
389 getLog().debug("Trying to derive Maven version automatically from project prerequisites...");
390 String requiredMavenVersion =
391 project.getPrerequisites() != null ? project.getPrerequisites().getMaven() : null;
392 if (requiredMavenVersion == null) {
393 getLog().debug("Trying to derive Maven version automatically from referenced Maven Plugin API artifact "
394 + "version...");
395 requiredMavenVersion = request.getUsedMavenApiVersion();
396 }
397 if (requiredMavenVersion == null) {
398 getLog().warn("Cannot determine the required Maven version automatically, it is recommended to "
399 + "configure some explicit value manually.");
400 }
401 return requiredMavenVersion;
402 }
403
404 private String getRequiredJavaVersion(PluginToolsRequest request) {
405 if (!VALUE_AUTO.equals(requiredJavaVersion)) {
406 return requiredJavaVersion;
407 }
408 String minRequiredJavaVersion = request.getRequiredJavaVersion();
409 if (minRequiredJavaVersion == null) {
410 getLog().warn("Cannot determine the minimally required Java version automatically, it is recommended to "
411 + "configure some explicit value manually.");
412 return null;
413 }
414
415 return minRequiredJavaVersion;
416 }
417
418
419
420
421 private Set<Artifact> dependenciesNotInProvidedScope() {
422 LinkedHashSet<Artifact> wrongScopedDependencies = new LinkedHashSet<>();
423
424 for (Artifact dependency : project.getArtifacts()) {
425 String ga = dependency.getGroupId() + ":" + dependency.getArtifactId();
426 if (expectedProvidedScopeGroupIds.contains(dependency.getGroupId())
427 && !expectedProvidedScopeExclusions.contains(ga)
428 && !Artifact.SCOPE_PROVIDED.equals(dependency.getScope())) {
429 wrongScopedDependencies.add(dependency);
430 }
431 }
432
433 return wrongScopedDependencies;
434 }
435
436
437
438
439
440
441
442
443 private Set<Artifact> filterMojoDependencies() {
444 Set<Artifact> filteredArtifacts;
445 if (mojoDependencies == null) {
446 filteredArtifacts = new LinkedHashSet<>(project.getArtifacts());
447 } else if (mojoDependencies.isEmpty()) {
448 filteredArtifacts = null;
449 } else {
450 filteredArtifacts = new LinkedHashSet<>();
451
452 ArtifactFilter filter = new IncludesArtifactFilter(mojoDependencies);
453
454 for (Artifact artifact : project.getArtifacts()) {
455 if (filter.include(artifact)) {
456 filteredArtifacts.add(artifact);
457 }
458 }
459 }
460
461 return filteredArtifacts;
462 }
463 }