View Javadoc
1   package org.apache.maven.plugin.compiler;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.plugin.MojoExecutionException;
23  import org.apache.maven.plugins.annotations.LifecyclePhase;
24  import org.apache.maven.plugins.annotations.Mojo;
25  import org.apache.maven.plugins.annotations.Parameter;
26  import org.apache.maven.plugins.annotations.ResolutionScope;
27  import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
28  import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
29  import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
30  
31  
32  import java.io.File;
33  import java.io.IOException;
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.HashSet;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Set;
40  
41  /**
42   * Compiles application test sources.
43   *
44   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
45   * @version $Id: TestCompilerMojo.html 998881 2016-10-04 20:35:50Z hboutemy $
46   * @since 2.0
47   */
48  @Mojo( name = "testCompile", defaultPhase = LifecyclePhase.TEST_COMPILE, threadSafe = true,
49                  requiresDependencyResolution = ResolutionScope.TEST )
50  public class TestCompilerMojo
51      extends AbstractCompilerMojo
52  {
53      /**
54       * Set this to 'true' to bypass compilation of test sources.
55       * Its use is NOT RECOMMENDED, but quite convenient on occasion.
56       */
57      @Parameter ( property = "maven.test.skip" )
58      private boolean skip;
59  
60      /**
61       * The source directories containing the test-source to be compiled.
62       */
63      @Parameter ( defaultValue = "${project.testCompileSourceRoots}", readonly = true, required = true )
64      private List<String> compileSourceRoots;
65  
66      /**
67       * The directory where compiled test classes go.
68       */
69      @Parameter ( defaultValue = "${project.build.testOutputDirectory}", required = true, readonly = true )
70      private File outputDirectory;
71  
72      /**
73       * A list of inclusion filters for the compiler.
74       */
75      @Parameter
76      private Set<String> testIncludes = new HashSet<String>();
77  
78      /**
79       * A list of exclusion filters for the compiler.
80       */
81      @Parameter
82      private Set<String> testExcludes = new HashSet<String>();
83  
84      /**
85       * The -source argument for the test Java compiler.
86       *
87       * @since 2.1
88       */
89      @Parameter ( property = "maven.compiler.testSource" )
90      private String testSource;
91  
92      /**
93       * The -target argument for the test Java compiler.
94       *
95       * @since 2.1
96       */
97      @Parameter ( property = "maven.compiler.testTarget" )
98      private String testTarget;
99  
100     /**
101      * the -release argument for the test Java compiler
102      * 
103      * @since 3.6
104      */
105     @Parameter ( property = "maven.compiler.testRelease" )
106     private String testRelease;
107 
108     /**
109      * <p>
110      * Sets the arguments to be passed to test compiler (prepending a dash) if fork is set to true.
111      * </p>
112      * <p>
113      * This is because the list of valid arguments passed to a Java compiler
114      * varies based on the compiler version.
115      * </p>
116      *
117      * @since 2.1
118      */
119     @Parameter
120     private Map<String, String> testCompilerArguments;
121 
122     /**
123      * <p>
124      * Sets the unformatted argument string to be passed to test compiler if fork is set to true.
125      * </p>
126      * <p>
127      * This is because the list of valid arguments passed to a Java compiler
128      * varies based on the compiler version.
129      * </p>
130      *
131      * @since 2.1
132      */
133     @Parameter
134     private String testCompilerArgument;
135 
136     /**
137      * <p>
138      * Specify where to place generated source files created by annotation processing.
139      * Only applies to JDK 1.6+
140      * </p>
141      *
142      * @since 2.2
143      */
144     @Parameter ( defaultValue = "${project.build.directory}/generated-test-sources/test-annotations" )
145     private File generatedTestSourcesDirectory;
146 
147     @Parameter( defaultValue = "${project.compileClasspathElements}", readonly = true )
148     private List<String> compilePath;
149 
150     @Parameter( defaultValue = "${project.testClasspathElements}", readonly = true )
151     private List<String> testPath;
152 
153     private List<String> classpathElements;
154 
155     private List<String> modulepathElements;
156 
157     public void execute()
158         throws MojoExecutionException, CompilationFailureException
159     {
160         if ( skip )
161         {
162             getLog().info( "Not compiling test sources" );
163         }
164         super.execute();
165     }
166 
167     protected List<String> getCompileSourceRoots()
168     {
169         return compileSourceRoots;
170     }
171 
172     protected List<String> getClasspathElements()
173     {
174         return classpathElements;
175     }
176     
177     @Override
178     protected List<String> getModulepathElements()
179     {
180         return modulepathElements;
181     }
182 
183     protected File getOutputDirectory()
184     {
185         return outputDirectory;
186     }
187 
188     @Override
189     protected void preparePaths( Set<File> sourceFiles )
190     {
191         File mainOutputDirectory = new File( getProject().getBuild().getOutputDirectory() );
192 
193         File mainModuleInfo = new File( mainOutputDirectory, "module-info.class" );
194         
195         boolean hasMainModuleDescriptor = mainModuleInfo.exists();
196         
197         boolean hasTestModuleDescriptor = false;
198         for ( File sourceFile : sourceFiles )
199         {
200             // @todo verify if it is the root of a sourcedirectory?
201             if ( "module-info.java".equals( sourceFile.getName() ) ) 
202             {
203                 hasTestModuleDescriptor = true;
204                 break;
205             }
206         }
207         
208         List<String> testScopedElements = new ArrayList<String>( testPath );
209         testScopedElements.removeAll( compilePath );
210         
211         if ( release != null )
212         {
213             if ( Integer.valueOf( release ) < 9 )
214             {
215                 modulepathElements = Collections.emptyList();
216                 classpathElements = testPath;
217                 return;
218             }
219         }
220         else if ( Double.valueOf( getTarget() ) < Double.valueOf( MODULE_INFO_TARGET ) )
221         {
222             modulepathElements = Collections.emptyList();
223             classpathElements = testPath;
224             return;
225         }
226             
227         if ( hasTestModuleDescriptor )
228         {
229             modulepathElements = testPath;
230             classpathElements = Collections.emptyList();
231 
232             if ( hasMainModuleDescriptor )
233             {
234                 // maybe some extra analysis required
235             }
236             else
237             {
238                 // very odd
239                 // Means that main sources must be compiled with -modulesource and -Xmodule:<moduleName>
240                 // However, this has a huge impact since you can't simply use it as a classpathEntry 
241                 // due to extra folder in between
242                 throw new UnsupportedOperationException( "Can't compile test sources "
243                     + "when main sources are missing a module descriptor" );
244             }
245         }
246         else
247         {
248             if ( hasMainModuleDescriptor )
249             {
250                 modulepathElements = compilePath;
251                 classpathElements = testScopedElements;
252                 if ( compilerArgs == null )
253                 {
254                     compilerArgs = new ArrayList<String>();
255                 }
256                 
257                 try
258                 {
259                     String moduleName = new AsmModuleInfoParser().getModuleName( mainOutputDirectory  );
260                     compilerArgs.add( "-Xmodule:" + moduleName );
261                     compilerArgs.add( "--add-reads" );
262                     compilerArgs.add( moduleName + "=ALL-UNNAMED" );
263                 }
264                 catch ( IOException e )
265                 {
266                     throw new RuntimeException( "Failed to parse module-info: " + e.getMessage() );
267                 }
268             }
269             else
270             {
271                 modulepathElements = Collections.emptyList();
272                 classpathElements = testPath;
273             }
274         }
275     }
276 
277     protected SourceInclusionScanner getSourceInclusionScanner( int staleMillis )
278     {
279         SourceInclusionScanner scanner;
280 
281         if ( testIncludes.isEmpty() && testExcludes.isEmpty() )
282         {
283             scanner = new StaleSourceScanner( staleMillis );
284         }
285         else
286         {
287             if ( testIncludes.isEmpty() )
288             {
289                 testIncludes.add( "**/*.java" );
290             }
291             scanner = new StaleSourceScanner( staleMillis, testIncludes, testExcludes );
292         }
293 
294         return scanner;
295     }
296 
297     protected SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding )
298     {
299         SourceInclusionScanner scanner;
300 
301         // it's not defined if we get the ending with or without the dot '.'
302         String defaultIncludePattern = "**/*" + ( inputFileEnding.startsWith( "." ) ? "" : "." ) + inputFileEnding;
303 
304         if ( testIncludes.isEmpty() && testExcludes.isEmpty() )
305         {
306             testIncludes = Collections.singleton( defaultIncludePattern );
307             scanner = new SimpleSourceInclusionScanner( testIncludes, Collections.<String>emptySet() );
308         }
309         else
310         {
311             if ( testIncludes.isEmpty() )
312             {
313                 testIncludes.add( defaultIncludePattern );
314             }
315             scanner = new SimpleSourceInclusionScanner( testIncludes, testExcludes );
316         }
317 
318         return scanner;
319     }
320 
321     protected String getSource()
322     {
323         return testSource == null ? source : testSource;
324     }
325 
326     protected String getTarget()
327     {
328         return testTarget == null ? target : testTarget;
329     }
330     
331     @Override
332     protected String getRelease()
333     {
334         return testRelease == null ? release : testRelease;
335     }
336 
337     protected String getCompilerArgument()
338     {
339         return testCompilerArgument == null ? compilerArgument : testCompilerArgument;
340     }
341 
342     protected Map<String, String> getCompilerArguments()
343     {
344         return testCompilerArguments == null ? compilerArguments : testCompilerArguments;
345     }
346 
347     protected File getGeneratedSourcesDirectory()
348     {
349         return generatedTestSourcesDirectory;
350     }
351 
352     @Override
353     protected boolean isTestCompile()
354     {
355         return true;
356     }
357 
358 }