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