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 }