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