1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.compiler;
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.Collection;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.LinkedHashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Map.Entry;
32 import java.util.Objects;
33 import java.util.Optional;
34 import java.util.Set;
35
36 import org.apache.maven.artifact.Artifact;
37 import org.apache.maven.plugin.MojoExecutionException;
38 import org.apache.maven.plugins.annotations.LifecyclePhase;
39 import org.apache.maven.plugins.annotations.Mojo;
40 import org.apache.maven.plugins.annotations.Parameter;
41 import org.apache.maven.plugins.annotations.ResolutionScope;
42 import org.apache.maven.project.MavenProject;
43 import org.apache.maven.shared.utils.StringUtils;
44 import org.apache.maven.shared.utils.logging.MessageUtils;
45 import org.apache.maven.toolchain.Toolchain;
46 import org.apache.maven.toolchain.java.DefaultJavaToolChain;
47 import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
48 import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
49 import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
50 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
51 import org.codehaus.plexus.languages.java.jpms.LocationManager;
52 import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
53 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
54 import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
55
56
57
58
59
60
61
62
63
64
65
66 @Mojo(
67 name = "compile",
68 defaultPhase = LifecyclePhase.COMPILE,
69 threadSafe = true,
70 requiresDependencyResolution = ResolutionScope.COMPILE)
71 public class CompilerMojo extends AbstractCompilerMojo {
72
73
74
75 @Parameter(defaultValue = "${project.compileSourceRoots}", readonly = false, required = true)
76 private List<String> compileSourceRoots;
77
78
79
80
81
82
83
84
85
86
87
88
89
90 @Parameter(
91 property = "maven.compiler.outputDirectory",
92 defaultValue = "${project.build.outputDirectory}",
93 required = true,
94 readonly = false)
95 private File outputDirectory;
96
97
98
99
100
101
102 @Parameter(defaultValue = "${project.artifact}", readonly = true, required = true)
103 private Artifact projectArtifact;
104
105
106
107
108 @Parameter
109 private Set<String> includes = new HashSet<>();
110
111
112
113
114 @Parameter
115 private Set<String> excludes = new HashSet<>();
116
117
118
119
120
121 @Parameter
122 private Set<String> incrementalExcludes = new HashSet<>();
123
124
125
126
127
128
129 @Parameter(defaultValue = "${project.build.directory}/generated-sources/annotations")
130 private File generatedSourcesDirectory;
131
132
133
134
135
136 @Parameter(property = "maven.main.skip")
137 private boolean skipMain;
138
139 @Parameter(defaultValue = "${project.compileClasspathElements}", readonly = true, required = true)
140 private List<String> compilePath;
141
142
143
144
145
146
147
148
149
150
151
152
153
154 @Parameter
155 private boolean multiReleaseOutput;
156
157
158
159
160
161
162 @Parameter(defaultValue = "javac")
163 private String debugFileName;
164
165
166
167
168
169
170
171
172 @Parameter(property = "maven.compiler.moduleVersion", defaultValue = "${project.version}")
173 private String moduleVersion;
174
175
176
177
178
179
180
181
182 @Parameter(property = "maven.compiler.useModuleVersion", defaultValue = "true")
183 private boolean useModuleVersion;
184
185 final LocationManager locationManager = new LocationManager();
186
187 private List<String> classpathElements;
188
189 private List<String> modulepathElements;
190
191 private Map<String, JavaModuleDescriptor> pathElements;
192
193 @Override
194 protected List<String> getCompileSourceRoots() {
195 return compileSourceRoots;
196 }
197
198 @Override
199 protected List<String> getClasspathElements() {
200 return classpathElements;
201 }
202
203 @Override
204 protected List<String> getModulepathElements() {
205 return modulepathElements;
206 }
207
208 @Override
209 protected Map<String, JavaModuleDescriptor> getPathElements() {
210 return pathElements;
211 }
212
213 @Override
214 protected File getOutputDirectory() {
215 File dir;
216 if (!multiReleaseOutput) {
217 dir = outputDirectory;
218 } else {
219 dir = new File(outputDirectory, "META-INF/versions/" + release);
220 }
221 return dir;
222 }
223
224 @Override
225 public void execute() throws MojoExecutionException, CompilationFailureException {
226 if (skipMain) {
227 getLog().info("Not compiling main sources");
228 return;
229 }
230
231 if (multiReleaseOutput && release == null) {
232 throw new MojoExecutionException("When using 'multiReleaseOutput' the release must be set");
233 }
234
235 super.execute();
236
237 if (outputDirectory.isDirectory()) {
238 File artifactFile = projectArtifact.getFile();
239 if (artifactFile != null && !Objects.equals(artifactFile, outputDirectory)) {
240 getLog().warn("Overwriting artifact's file from " + artifactFile + " to " + outputDirectory);
241 }
242 projectArtifact.setFile(outputDirectory);
243 }
244 }
245
246 @Override
247 protected Set<String> getIncludes() {
248 return includes;
249 }
250
251 @Override
252 protected Set<String> getExcludes() {
253 return excludes;
254 }
255
256 @Override
257 protected void preparePaths(Set<File> sourceFiles) {
258
259
260 Optional<Path> moduleDeclaration = getModuleDeclaration(sourceFiles);
261
262 if (moduleDeclaration.isPresent()) {
263
264
265
266
267 modulepathElements = new ArrayList<>(compilePath.size());
268 classpathElements = new ArrayList<>(compilePath.size());
269 pathElements = new LinkedHashMap<>(compilePath.size());
270
271 ResolvePathsResult<File> resolvePathsResult;
272 try {
273 Collection<File> dependencyArtifacts = getCompileClasspathElements(getProject());
274
275 ResolvePathsRequest<File> request = ResolvePathsRequest.ofFiles(dependencyArtifacts)
276 .setIncludeStatic(true)
277 .setMainModuleDescriptor(moduleDeclaration.get().toFile());
278
279 Toolchain toolchain = getToolchain();
280 if (toolchain instanceof DefaultJavaToolChain) {
281 request.setJdkHome(new File(((DefaultJavaToolChain) toolchain).getJavaHome()));
282 }
283
284 resolvePathsResult = locationManager.resolvePaths(request);
285
286 for (Entry<File, Exception> pathException :
287 resolvePathsResult.getPathExceptions().entrySet()) {
288 Throwable cause = pathException.getValue();
289 while (cause.getCause() != null) {
290 cause = cause.getCause();
291 }
292 String fileName = pathException.getKey().getName();
293 getLog().warn("Can't extract module name from " + fileName + ": " + cause.getMessage());
294 }
295
296 JavaModuleDescriptor moduleDescriptor = resolvePathsResult.getMainModuleDescriptor();
297
298 detectFilenameBasedAutomodules(resolvePathsResult, moduleDescriptor);
299
300 for (Map.Entry<File, JavaModuleDescriptor> entry :
301 resolvePathsResult.getPathElements().entrySet()) {
302 pathElements.put(entry.getKey().getPath(), entry.getValue());
303 }
304
305 if (compilerArgs == null) {
306 compilerArgs = new ArrayList<>();
307 }
308
309 for (File file : resolvePathsResult.getClasspathElements()) {
310 classpathElements.add(file.getPath());
311
312 if (multiReleaseOutput) {
313 if (getOutputDirectory().toPath().startsWith(file.getPath())) {
314 compilerArgs.add("--patch-module");
315 compilerArgs.add(String.format("%s=%s", moduleDescriptor.name(), file.getPath()));
316 }
317 }
318 }
319
320 for (File file : resolvePathsResult.getModulepathElements().keySet()) {
321 modulepathElements.add(file.getPath());
322 }
323
324 if (useModuleVersion) {
325 compilerArgs.add("--module-version");
326 compilerArgs.add(moduleVersion);
327 }
328 } catch (IOException e) {
329 getLog().warn(e.getMessage());
330 }
331 } else {
332 classpathElements = new ArrayList<>();
333 for (File element : getCompileClasspathElements(getProject())) {
334 classpathElements.add(element.getPath());
335 }
336 modulepathElements = Collections.emptyList();
337 pathElements = Collections.emptyMap();
338 }
339 }
340
341 private void detectFilenameBasedAutomodules(
342 final ResolvePathsResult<File> resolvePathsResult, final JavaModuleDescriptor moduleDescriptor) {
343 List<String> automodulesDetected = new ArrayList<>();
344 for (Entry<File, ModuleNameSource> entry :
345 resolvePathsResult.getModulepathElements().entrySet()) {
346 if (ModuleNameSource.FILENAME.equals(entry.getValue())) {
347 automodulesDetected.add(entry.getKey().getName());
348 }
349 }
350
351 if (!automodulesDetected.isEmpty()) {
352 final String message = "Required filename-based automodules detected: "
353 + automodulesDetected + ". "
354 + "Please don't publish this project to a public artifact repository!";
355
356 if (moduleDescriptor.exports().isEmpty()) {
357
358 getLog().info(message);
359 } else {
360
361 writeBoxedWarning(message);
362 }
363 }
364 }
365
366 private List<File> getCompileClasspathElements(MavenProject project) {
367
368 List<File> list = new ArrayList<>(project.getArtifacts().size() + 3);
369
370 if (multiReleaseOutput) {
371 File versionsFolder = new File(project.getBuild().getOutputDirectory(), "META-INF/versions");
372
373
374 for (int version = Integer.parseInt(getRelease()) - 1; version >= 9; version--) {
375 File versionSubFolder = new File(versionsFolder, String.valueOf(version));
376 if (versionSubFolder.exists()) {
377 list.add(versionSubFolder);
378 }
379 }
380 }
381
382 list.add(new File(project.getBuild().getOutputDirectory()));
383
384 for (Artifact a : project.getArtifacts()) {
385 if (a.getArtifactHandler().isAddedToClasspath()) {
386 list.add(a.getFile());
387 }
388 }
389 return list;
390 }
391
392 @Override
393 protected SourceInclusionScanner getSourceInclusionScanner(int staleMillis) {
394 if (includes.isEmpty() && excludes.isEmpty() && incrementalExcludes.isEmpty()) {
395 return new StaleSourceScanner(staleMillis);
396 }
397
398 if (includes.isEmpty()) {
399 includes.add("**/*.java");
400 }
401
402 Set<String> excludesIncr = new HashSet<>(excludes);
403 excludesIncr.addAll(this.incrementalExcludes);
404 return new StaleSourceScanner(staleMillis, includes, excludesIncr);
405 }
406
407 @Override
408 protected SourceInclusionScanner getSourceInclusionScanner(String inputFileEnding) {
409
410 String defaultIncludePattern = "**/*" + (inputFileEnding.startsWith(".") ? "" : ".") + inputFileEnding;
411
412 if (includes.isEmpty()) {
413 includes.add(defaultIncludePattern);
414 }
415 Set<String> excludesIncr = new HashSet<>(excludes);
416 excludesIncr.addAll(excludesIncr);
417 return new SimpleSourceInclusionScanner(includes, excludesIncr);
418 }
419
420 @Override
421 protected String getSource() {
422 return source;
423 }
424
425 @Override
426 protected String getTarget() {
427 return target;
428 }
429
430 @Override
431 protected String getRelease() {
432 return release;
433 }
434
435 @Override
436 protected String getCompilerArgument() {
437 return compilerArgument;
438 }
439
440 @Override
441 protected Map<String, String> getCompilerArguments() {
442 return compilerArguments;
443 }
444
445 @Override
446 protected File getGeneratedSourcesDirectory() {
447 return generatedSourcesDirectory;
448 }
449
450 @Override
451 protected String getDebugFileName() {
452 return debugFileName;
453 }
454
455 private void writeBoxedWarning(String message) {
456 String line = StringUtils.repeat("*", message.length() + 4);
457 getLog().warn(line);
458 getLog().warn("* " + MessageUtils.buffer().strong(message) + " *");
459 getLog().warn(line);
460 }
461 }