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.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.HashSet;
27 import java.util.LinkedHashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.compiler.module.ModuleInfoParser;
34 import org.apache.maven.plugins.annotations.Component;
35 import org.apache.maven.plugins.annotations.LifecyclePhase;
36 import org.apache.maven.plugins.annotations.Mojo;
37 import org.apache.maven.plugins.annotations.Parameter;
38 import org.apache.maven.plugins.annotations.ResolutionScope;
39 import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
40 import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
41 import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
42
43
44
45
46
47
48
49
50 @Mojo( name = "testCompile", defaultPhase = LifecyclePhase.TEST_COMPILE, threadSafe = true,
51 requiresDependencyResolution = ResolutionScope.TEST )
52 public class TestCompilerMojo
53 extends AbstractCompilerMojo
54 {
55 protected static final String PS = System.getProperty( "path.separator" );
56
57
58
59
60
61 @Parameter ( property = "maven.test.skip" )
62 private boolean skip;
63
64
65
66
67 @Parameter ( defaultValue = "${project.testCompileSourceRoots}", readonly = true, required = true )
68 private List<String> compileSourceRoots;
69
70
71
72
73 @Parameter ( defaultValue = "${project.build.testOutputDirectory}", required = true, readonly = true )
74 private File outputDirectory;
75
76
77
78
79 @Parameter
80 private Set<String> testIncludes = new HashSet<String>();
81
82
83
84
85 @Parameter
86 private Set<String> testExcludes = new HashSet<String>();
87
88
89
90
91
92
93 @Parameter ( property = "maven.compiler.testSource" )
94 private String testSource;
95
96
97
98
99
100
101 @Parameter ( property = "maven.compiler.testTarget" )
102 private String testTarget;
103
104
105
106
107
108
109 @Parameter ( property = "maven.compiler.testRelease" )
110 private String testRelease;
111
112
113
114
115
116
117
118
119
120
121
122
123 @Parameter
124 private Map<String, String> testCompilerArguments;
125
126
127
128
129
130
131
132
133
134
135
136
137 @Parameter
138 private String testCompilerArgument;
139
140
141
142
143
144
145
146
147
148 @Parameter ( defaultValue = "${project.build.directory}/generated-test-sources/test-annotations" )
149 private File generatedTestSourcesDirectory;
150
151 @Parameter( defaultValue = "${project.compileClasspathElements}", readonly = true )
152 private List<String> compilePath;
153
154 @Parameter( defaultValue = "${project.testClasspathElements}", readonly = true )
155 private List<String> testPath;
156
157 @Component
158 private Map<String, ModuleInfoParser> moduleInfoParsers;
159
160 private List<String> classpathElements;
161
162 private List<String> modulepathElements;
163
164 public void execute()
165 throws MojoExecutionException, CompilationFailureException
166 {
167 if ( skip )
168 {
169 getLog().info( "Not compiling test sources" );
170 return;
171 }
172 super.execute();
173 }
174
175 protected List<String> getCompileSourceRoots()
176 {
177 return compileSourceRoots;
178 }
179
180 protected List<String> getClasspathElements()
181 {
182 return classpathElements;
183 }
184
185 @Override
186 protected List<String> getModulepathElements()
187 {
188 return modulepathElements;
189 }
190
191 protected File getOutputDirectory()
192 {
193 return outputDirectory;
194 }
195
196 @Override
197 protected void preparePaths( Set<File> sourceFiles )
198 {
199 File mainOutputDirectory = new File( getProject().getBuild().getOutputDirectory() );
200
201 boolean hasMainModuleDescriptor = new File( mainOutputDirectory, "module-info.class" ).exists();
202
203 boolean hasTestModuleDescriptor = false;
204
205
206 for ( File sourceFile : sourceFiles )
207 {
208
209 if ( "module-info.java".equals( sourceFile.getName() ) )
210 {
211 hasTestModuleDescriptor = true;
212 break;
213 }
214 }
215
216 List<String> testScopedElements = new ArrayList<String>( testPath );
217 testScopedElements.removeAll( compilePath );
218
219 if ( release != null )
220 {
221 if ( Integer.valueOf( release ) < 9 )
222 {
223 modulepathElements = Collections.emptyList();
224 classpathElements = testPath;
225 return;
226 }
227 }
228 else if ( Double.valueOf( getTarget() ) < Double.valueOf( MODULE_INFO_TARGET ) )
229 {
230 modulepathElements = Collections.emptyList();
231 classpathElements = testPath;
232 return;
233 }
234
235 if ( hasTestModuleDescriptor )
236 {
237 modulepathElements = testPath;
238 classpathElements = Collections.emptyList();
239
240 if ( hasMainModuleDescriptor )
241 {
242
243 }
244 else
245 {
246
247
248
249
250 throw new UnsupportedOperationException( "Can't compile test sources "
251 + "when main sources are missing a module descriptor" );
252 }
253 }
254 else
255 {
256 if ( hasMainModuleDescriptor )
257 {
258 modulepathElements = compilePath;
259 classpathElements = testScopedElements;
260 if ( compilerArgs == null )
261 {
262 compilerArgs = new ArrayList<String>();
263 }
264
265 String moduleName = getModuleName( mainOutputDirectory );
266
267 compilerArgs.add( "--patch-module" );
268
269 StringBuilder addReadsValue = new StringBuilder( moduleName )
270 .append( '=' )
271 .append( mainOutputDirectory )
272 .append( PS );
273 for ( String root : compileSourceRoots )
274 {
275 addReadsValue.append( root ).append( PS );
276 }
277
278 compilerArgs.add( addReadsValue.toString() );
279
280 compilerArgs.add( "--add-reads" );
281 compilerArgs.add( moduleName + "=ALL-UNNAMED" );
282 }
283 else
284 {
285 modulepathElements = Collections.emptyList();
286 classpathElements = testPath;
287 }
288 }
289 }
290
291 private String getModuleName( File mainOutputDirectory )
292 {
293 String moduleName = null;
294
295 Map<String, Exception> exceptionMap = new LinkedHashMap<String, Exception>( moduleInfoParsers.size() );
296
297 List<String> parserKeys = Arrays.asList( "reflect", "asm" );
298
299
300 for ( String parserKey: parserKeys )
301 {
302 ModuleInfoParser parser = moduleInfoParsers.get( parserKey );
303
304 try
305 {
306 moduleName = parser.getModuleDescriptor( mainOutputDirectory ).name();
307
308 if ( moduleName != null )
309 {
310 break;
311 }
312 }
313 catch ( Exception e )
314 {
315 exceptionMap.put( parserKey, e );
316 }
317 }
318
319 if ( moduleName == null )
320 {
321 getLog().error( "Failed to parse module-info:" );
322
323 for ( Map.Entry<String, Exception> exception : exceptionMap.entrySet() )
324 {
325 getLog().error( "With " + exception.getKey() + ": " + exception.getValue().getMessage() );
326 }
327
328 throw new RuntimeException( "Failed to parse module-info" );
329 }
330 return moduleName;
331 }
332
333 protected SourceInclusionScanner getSourceInclusionScanner( int staleMillis )
334 {
335 SourceInclusionScanner scanner;
336
337 if ( testIncludes.isEmpty() && testExcludes.isEmpty() )
338 {
339 scanner = new StaleSourceScanner( staleMillis );
340 }
341 else
342 {
343 if ( testIncludes.isEmpty() )
344 {
345 testIncludes.add( "**/*.java" );
346 }
347 scanner = new StaleSourceScanner( staleMillis, testIncludes, testExcludes );
348 }
349
350 return scanner;
351 }
352
353 protected SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding )
354 {
355 SourceInclusionScanner scanner;
356
357
358 String defaultIncludePattern = "**/*" + ( inputFileEnding.startsWith( "." ) ? "" : "." ) + inputFileEnding;
359
360 if ( testIncludes.isEmpty() && testExcludes.isEmpty() )
361 {
362 testIncludes = Collections.singleton( defaultIncludePattern );
363 scanner = new SimpleSourceInclusionScanner( testIncludes, Collections.<String>emptySet() );
364 }
365 else
366 {
367 if ( testIncludes.isEmpty() )
368 {
369 testIncludes.add( defaultIncludePattern );
370 }
371 scanner = new SimpleSourceInclusionScanner( testIncludes, testExcludes );
372 }
373
374 return scanner;
375 }
376
377 protected String getSource()
378 {
379 return testSource == null ? source : testSource;
380 }
381
382 protected String getTarget()
383 {
384 return testTarget == null ? target : testTarget;
385 }
386
387 @Override
388 protected String getRelease()
389 {
390 return testRelease == null ? release : testRelease;
391 }
392
393 protected String getCompilerArgument()
394 {
395 return testCompilerArgument == null ? compilerArgument : testCompilerArgument;
396 }
397
398 protected Map<String, String> getCompilerArguments()
399 {
400 return testCompilerArguments == null ? compilerArguments : testCompilerArguments;
401 }
402
403 protected File getGeneratedSourcesDirectory()
404 {
405 return generatedTestSourcesDirectory;
406 }
407
408 @Override
409 protected boolean isTestCompile()
410 {
411 return true;
412 }
413
414 }