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.nio.file.Files;
25 import java.nio.file.Paths;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashSet;
30 import java.util.LinkedHashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.Set;
35
36 import org.apache.maven.plugin.MojoExecutionException;
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.toolchain.Toolchain;
42 import org.apache.maven.toolchain.java.DefaultJavaToolChain;
43 import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
44 import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
45 import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
46 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
47 import org.codehaus.plexus.languages.java.jpms.LocationManager;
48 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
49 import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
50
51
52
53
54
55
56
57 @Mojo( name = "testCompile", defaultPhase = LifecyclePhase.TEST_COMPILE, threadSafe = true,
58 requiresDependencyResolution = ResolutionScope.TEST )
59 public class TestCompilerMojo
60 extends AbstractCompilerMojo
61 {
62
63
64
65
66 @Parameter ( property = "maven.test.skip" )
67 private boolean skip;
68
69
70
71
72 @Parameter ( defaultValue = "${project.testCompileSourceRoots}", readonly = true, required = true )
73 private List<String> compileSourceRoots;
74
75
76
77
78 @Parameter ( defaultValue = "${project.build.testOutputDirectory}", required = true, readonly = true )
79 private File outputDirectory;
80
81
82
83
84 @Parameter
85 private Set<String> testIncludes = new HashSet<>();
86
87
88
89
90 @Parameter
91 private Set<String> testExcludes = new HashSet<>();
92
93
94
95
96
97
98 @Parameter ( property = "maven.compiler.testSource" )
99 private String testSource;
100
101
102
103
104
105
106 @Parameter ( property = "maven.compiler.testTarget" )
107 private String testTarget;
108
109
110
111
112
113
114 @Parameter ( property = "maven.compiler.testRelease" )
115 private String testRelease;
116
117
118
119
120
121
122
123
124
125
126
127
128 @Parameter
129 private Map<String, String> testCompilerArguments;
130
131
132
133
134
135
136
137
138
139
140
141
142 @Parameter
143 private String testCompilerArgument;
144
145
146
147
148
149
150
151
152
153 @Parameter ( defaultValue = "${project.build.directory}/generated-test-sources/test-annotations" )
154 private File generatedTestSourcesDirectory;
155
156 @Parameter( defaultValue = "${project.testClasspathElements}", readonly = true )
157 private List<String> testPath;
158
159 final LocationManager locationManager = new LocationManager();
160
161 private Map<String, JavaModuleDescriptor> pathElements;
162
163 private Collection<String> classpathElements;
164
165 private Collection<String> modulepathElements;
166
167 public void execute()
168 throws MojoExecutionException, CompilationFailureException
169 {
170 if ( skip )
171 {
172 getLog().info( "Not compiling test sources" );
173 return;
174 }
175 super.execute();
176 }
177
178 protected List<String> getCompileSourceRoots()
179 {
180 return compileSourceRoots;
181 }
182
183 @Override
184 protected Map<String, JavaModuleDescriptor> getPathElements()
185 {
186 return pathElements;
187 }
188
189 protected List<String> getClasspathElements()
190 {
191 return new ArrayList<>( classpathElements );
192 }
193
194 @Override
195 protected List<String> getModulepathElements()
196 {
197 return new ArrayList<>( modulepathElements );
198 }
199
200 protected File getOutputDirectory()
201 {
202 return outputDirectory;
203 }
204
205 @Override
206 protected void preparePaths( Set<File> sourceFiles )
207 {
208 File mainOutputDirectory = new File( getProject().getBuild().getOutputDirectory() );
209
210 File mainModuleDescriptorClassFile = new File( mainOutputDirectory, "module-info.class" );
211 JavaModuleDescriptor mainModuleDescriptor = null;
212
213 File testModuleDescriptorJavaFile = new File( "module-info.java" );
214 JavaModuleDescriptor testModuleDescriptor = null;
215
216
217 for ( File sourceFile : sourceFiles )
218 {
219
220 if ( "module-info.java".equals( sourceFile.getName() ) )
221 {
222 testModuleDescriptorJavaFile = sourceFile;
223 break;
224 }
225 }
226
227
228 if ( mainModuleDescriptorClassFile.exists() )
229 {
230 ResolvePathsResult<String> result;
231
232 try
233 {
234 ResolvePathsRequest<String> request =
235 ResolvePathsRequest.ofStrings( testPath )
236 .setMainModuleDescriptor( mainModuleDescriptorClassFile.getAbsolutePath() );
237
238 Toolchain toolchain = getToolchain();
239 if ( toolchain instanceof DefaultJavaToolChain )
240 {
241 request.setJdkHome( ( (DefaultJavaToolChain) toolchain ).getJavaHome() );
242 }
243
244 result = locationManager.resolvePaths( request );
245
246 for ( Entry<String, Exception> pathException : result.getPathExceptions().entrySet() )
247 {
248 Throwable cause = pathException.getValue();
249 while ( cause.getCause() != null )
250 {
251 cause = cause.getCause();
252 }
253 String fileName = Paths.get( pathException.getKey() ).getFileName().toString();
254 getLog().warn( "Can't extract module name from " + fileName + ": " + cause.getMessage() );
255 }
256 }
257 catch ( IOException e )
258 {
259 throw new RuntimeException( e );
260 }
261
262 mainModuleDescriptor = result.getMainModuleDescriptor();
263
264 pathElements = new LinkedHashMap<>( result.getPathElements().size() );
265 pathElements.putAll( result.getPathElements() );
266
267 modulepathElements = result.getModulepathElements().keySet();
268 classpathElements = result.getClasspathElements();
269 }
270
271
272 if ( testModuleDescriptorJavaFile.exists() )
273 {
274 ResolvePathsResult<String> result;
275
276 try
277 {
278 ResolvePathsRequest<String> request =
279 ResolvePathsRequest.ofStrings( testPath )
280 .setMainModuleDescriptor( testModuleDescriptorJavaFile.getAbsolutePath() );
281
282 Toolchain toolchain = getToolchain();
283 if ( toolchain instanceof DefaultJavaToolChain )
284 {
285 request.setJdkHome( ( (DefaultJavaToolChain) toolchain ).getJavaHome() );
286 }
287
288 result = locationManager.resolvePaths( request );
289 }
290 catch ( IOException e )
291 {
292 throw new RuntimeException( e );
293 }
294
295 testModuleDescriptor = result.getMainModuleDescriptor();
296 }
297
298 if ( release != null )
299 {
300 if ( Integer.valueOf( release ) < 9 )
301 {
302 pathElements = Collections.emptyMap();
303 modulepathElements = Collections.emptyList();
304 classpathElements = testPath;
305 return;
306 }
307 }
308 else if ( Double.valueOf( getTarget() ) < Double.valueOf( MODULE_INFO_TARGET ) )
309 {
310 pathElements = Collections.emptyMap();
311 modulepathElements = Collections.emptyList();
312 classpathElements = testPath;
313 return;
314 }
315
316 if ( testModuleDescriptor != null )
317 {
318 modulepathElements = testPath;
319 classpathElements = Collections.emptyList();
320
321 if ( mainModuleDescriptor != null )
322 {
323 if ( getLog().isDebugEnabled() )
324 {
325 getLog().debug( "Main and test module descriptors exist:" );
326 getLog().debug( " main module = " + mainModuleDescriptor.name() );
327 getLog().debug( " test module = " + testModuleDescriptor.name() );
328 }
329
330 if ( testModuleDescriptor.name().equals( mainModuleDescriptor.name() ) )
331 {
332 if ( compilerArgs == null )
333 {
334 compilerArgs = new ArrayList<>();
335 }
336 compilerArgs.add( "--patch-module" );
337
338 StringBuilder patchModuleValue = new StringBuilder();
339 patchModuleValue.append( testModuleDescriptor.name() );
340 patchModuleValue.append( '=' );
341
342 for ( String root : getProject().getCompileSourceRoots() )
343 {
344 if ( Files.exists( Paths.get( root ) ) )
345 {
346 patchModuleValue.append( root ).append( PS );
347 }
348 }
349
350 compilerArgs.add( patchModuleValue.toString() );
351 }
352 else
353 {
354 getLog().debug( "Black-box testing - all is ready to compile" );
355 }
356 }
357 else
358 {
359
360 if ( !mainOutputDirectory.exists() )
361 {
362 return;
363 }
364
365
366
367
368 throw new UnsupportedOperationException( "Can't compile test sources "
369 + "when main sources are missing a module descriptor" );
370 }
371 }
372 else
373 {
374 if ( mainModuleDescriptor != null )
375 {
376 if ( compilerArgs == null )
377 {
378 compilerArgs = new ArrayList<>();
379 }
380 compilerArgs.add( "--patch-module" );
381
382 StringBuilder patchModuleValue = new StringBuilder( mainModuleDescriptor.name() )
383 .append( '=' )
384 .append( mainOutputDirectory )
385 .append( PS );
386 for ( String root : compileSourceRoots )
387 {
388 patchModuleValue.append( root ).append( PS );
389 }
390
391 compilerArgs.add( patchModuleValue.toString() );
392
393 compilerArgs.add( "--add-reads" );
394 compilerArgs.add( mainModuleDescriptor.name() + "=ALL-UNNAMED" );
395 }
396 else
397 {
398 modulepathElements = Collections.emptyList();
399 classpathElements = testPath;
400 }
401 }
402 }
403
404 protected SourceInclusionScanner getSourceInclusionScanner( int staleMillis )
405 {
406 SourceInclusionScanner scanner;
407
408 if ( testIncludes.isEmpty() && testExcludes.isEmpty() )
409 {
410 scanner = new StaleSourceScanner( staleMillis );
411 }
412 else
413 {
414 if ( testIncludes.isEmpty() )
415 {
416 testIncludes.add( "**/*.java" );
417 }
418 scanner = new StaleSourceScanner( staleMillis, testIncludes, testExcludes );
419 }
420
421 return scanner;
422 }
423
424 protected SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding )
425 {
426 SourceInclusionScanner scanner;
427
428
429 String defaultIncludePattern = "**/*" + ( inputFileEnding.startsWith( "." ) ? "" : "." ) + inputFileEnding;
430
431 if ( testIncludes.isEmpty() && testExcludes.isEmpty() )
432 {
433 testIncludes = Collections.singleton( defaultIncludePattern );
434 scanner = new SimpleSourceInclusionScanner( testIncludes, Collections.<String>emptySet() );
435 }
436 else
437 {
438 if ( testIncludes.isEmpty() )
439 {
440 testIncludes.add( defaultIncludePattern );
441 }
442 scanner = new SimpleSourceInclusionScanner( testIncludes, testExcludes );
443 }
444
445 return scanner;
446 }
447
448 protected String getSource()
449 {
450 return testSource == null ? source : testSource;
451 }
452
453 protected String getTarget()
454 {
455 return testTarget == null ? target : testTarget;
456 }
457
458 @Override
459 protected String getRelease()
460 {
461 return testRelease == null ? release : testRelease;
462 }
463
464 protected String getCompilerArgument()
465 {
466 return testCompilerArgument == null ? compilerArgument : testCompilerArgument;
467 }
468
469 protected Map<String, String> getCompilerArguments()
470 {
471 return testCompilerArguments == null ? compilerArguments : testCompilerArguments;
472 }
473
474 protected File getGeneratedSourcesDirectory()
475 {
476 return generatedTestSourcesDirectory;
477 }
478
479 @Override
480 protected boolean isTestCompile()
481 {
482 return true;
483 }
484
485 }