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 final LocationManager locationManager = new LocationManager();
133
134 private List<String> classpathElements;
135
136 private List<String> modulepathElements;
137
138 private Map<String, JavaModuleDescriptor> pathElements;
139
140 protected List<String> getCompileSourceRoots()
141 {
142 return compileSourceRoots;
143 }
144
145 @Override
146 protected List<String> getClasspathElements()
147 {
148 return classpathElements;
149 }
150
151 @Override
152 protected List<String> getModulepathElements()
153 {
154 return modulepathElements;
155 }
156
157 @Override
158 protected Map<String, JavaModuleDescriptor> getPathElements()
159 {
160 return pathElements;
161 }
162
163 protected File getOutputDirectory()
164 {
165 File dir;
166 if ( !multiReleaseOutput )
167 {
168 dir = outputDirectory;
169 }
170 else
171 {
172 dir = new File( outputDirectory, "META-INF/versions/" + release );
173 }
174 return dir;
175 }
176
177 public void execute()
178 throws MojoExecutionException, CompilationFailureException
179 {
180 if ( skipMain )
181 {
182 getLog().info( "Not compiling main sources" );
183 return;
184 }
185
186 if ( multiReleaseOutput && release == null )
187 {
188 throw new MojoExecutionException( "When using 'multiReleaseOutput' the release must be set" );
189 }
190
191 super.execute();
192
193 if ( outputDirectory.isDirectory() )
194 {
195 projectArtifact.setFile( outputDirectory );
196 }
197 }
198
199 @Override
200 protected void preparePaths( Set<File> sourceFiles )
201 {
202
203
204 File moduleDescriptorPath = null;
205
206 boolean hasModuleDescriptor = false;
207 for ( File sourceFile : sourceFiles )
208 {
209 if ( "module-info.java".equals( sourceFile.getName() ) )
210 {
211 moduleDescriptorPath = sourceFile;
212 hasModuleDescriptor = true;
213 break;
214 }
215 }
216
217 if ( hasModuleDescriptor )
218 {
219
220
221
222
223 modulepathElements = new ArrayList<>( compilePath.size() );
224 classpathElements = new ArrayList<>( compilePath.size() );
225 pathElements = new LinkedHashMap<>( compilePath.size() );
226
227 ResolvePathsResult<File> resolvePathsResult;
228 try
229 {
230 Collection<File> dependencyArtifacts = getCompileClasspathElements( getProject() );
231
232 ResolvePathsRequest<File> request =
233 ResolvePathsRequest.ofFiles( dependencyArtifacts )
234 .setMainModuleDescriptor( moduleDescriptorPath );
235
236 Toolchain toolchain = getToolchain();
237 if ( toolchain instanceof DefaultJavaToolChain )
238 {
239 request.setJdkHome( new File( ( (DefaultJavaToolChain) toolchain ).getJavaHome() ) );
240 }
241
242 resolvePathsResult = locationManager.resolvePaths( request );
243
244 for ( Entry<File, Exception> pathException : resolvePathsResult.getPathExceptions().entrySet() )
245 {
246 Throwable cause = pathException.getValue();
247 while ( cause.getCause() != null )
248 {
249 cause = cause.getCause();
250 }
251 String fileName = pathException.getKey().getName();
252 getLog().warn( "Can't extract module name from " + fileName + ": " + cause.getMessage() );
253 }
254
255 JavaModuleDescriptor moduleDescriptor = resolvePathsResult.getMainModuleDescriptor();
256
257 detectFilenameBasedAutomodules( resolvePathsResult, moduleDescriptor );
258
259 for ( Map.Entry<File, JavaModuleDescriptor> entry : resolvePathsResult.getPathElements().entrySet() )
260 {
261 pathElements.put( entry.getKey().getPath(), entry.getValue() );
262 }
263
264 if ( compilerArgs == null )
265 {
266 compilerArgs = new ArrayList<>();
267 }
268
269 for ( File file : resolvePathsResult.getClasspathElements() )
270 {
271 classpathElements.add( file.getPath() );
272
273 if ( multiReleaseOutput )
274 {
275 if ( getOutputDirectory().toPath().startsWith( file.getPath() ) )
276 {
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 {
285 modulepathElements.add( file.getPath() );
286 }
287
288 compilerArgs.add( "--module-version" );
289 compilerArgs.add( getProject().getVersion() );
290
291 }
292 catch ( IOException e )
293 {
294 getLog().warn( e.getMessage() );
295 }
296 }
297 else
298 {
299 classpathElements = new ArrayList<>();
300 for ( File element : getCompileClasspathElements( getProject() ) )
301 {
302 classpathElements.add( element.getPath() );
303 }
304 modulepathElements = Collections.emptyList();
305 }
306 }
307
308 private void detectFilenameBasedAutomodules( final ResolvePathsResult<File> resolvePathsResult,
309 final JavaModuleDescriptor moduleDescriptor )
310 {
311 List<String> automodulesDetected = new ArrayList<>();
312 for ( Entry<File, ModuleNameSource> entry : resolvePathsResult.getModulepathElements().entrySet() )
313 {
314 if ( ModuleNameSource.FILENAME.equals( entry.getValue() ) )
315 {
316 automodulesDetected.add( entry.getKey().getName() );
317 }
318 }
319
320 if ( !automodulesDetected.isEmpty() )
321 {
322 final String message = "Required filename-based automodules detected: "
323 + automodulesDetected + ". "
324 + "Please don't publish this project to a public artifact repository!";
325
326 if ( moduleDescriptor.exports().isEmpty() )
327 {
328
329 getLog().info( message );
330 }
331 else
332 {
333
334 writeBoxedWarning( message );
335 }
336 }
337 }
338
339 private List<File> getCompileClasspathElements( MavenProject project )
340 {
341
342 List<File> list = new ArrayList<>( project.getArtifacts().size() + 3 );
343
344 if ( multiReleaseOutput )
345 {
346 File versionsFolder = new File( project.getBuild().getOutputDirectory(), "META-INF/versions" );
347
348
349 for ( int version = Integer.parseInt( getRelease() ) - 1; version >= 9 ; version-- )
350 {
351 File versionSubFolder = new File( versionsFolder, String.valueOf( version ) );
352 if ( versionSubFolder.exists() )
353 {
354 list.add( versionSubFolder );
355 }
356 }
357 }
358
359 list.add( new File( project.getBuild().getOutputDirectory() ) );
360
361 for ( Artifact a : project.getArtifacts() )
362 {
363 list.add( a.getFile() );
364 }
365 return list;
366 }
367
368 protected SourceInclusionScanner getSourceInclusionScanner( int staleMillis )
369 {
370 if ( includes.isEmpty() && excludes.isEmpty() )
371 {
372 return new StaleSourceScanner( staleMillis );
373 }
374
375 if ( includes.isEmpty() )
376 {
377 includes.add( "**/*.java" );
378 }
379
380 return new StaleSourceScanner( staleMillis, includes, excludes );
381 }
382
383 protected SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding )
384 {
385
386 String defaultIncludePattern = "**/*" + ( inputFileEnding.startsWith( "." ) ? "" : "." ) + inputFileEnding;
387
388 if ( includes.isEmpty() )
389 {
390 includes.add( defaultIncludePattern );
391 }
392
393 return new SimpleSourceInclusionScanner( includes, excludes );
394 }
395
396 protected String getSource()
397 {
398 return source;
399 }
400
401 protected String getTarget()
402 {
403 return target;
404 }
405
406 @Override
407 protected String getRelease()
408 {
409 return release;
410 }
411
412 protected String getCompilerArgument()
413 {
414 return compilerArgument;
415 }
416
417 protected Map<String, String> getCompilerArguments()
418 {
419 return compilerArguments;
420 }
421
422 protected File getGeneratedSourcesDirectory()
423 {
424 return generatedSourcesDirectory;
425 }
426
427 private void writeBoxedWarning( String message )
428 {
429 String line = StringUtils.repeat( "*", message.length() + 4 );
430 getLog().warn( line );
431 getLog().warn( "* " + MessageUtils.buffer().strong( message ) + " *" );
432 getLog().warn( line );
433 }
434 }