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