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