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 java.io.File;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.util.ArrayList;
26  import java.util.Date;
27  import java.util.HashSet;
28  import java.util.LinkedHashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.artifact.DefaultArtifact;
35  import org.apache.maven.artifact.handler.ArtifactHandler;
36  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
37  import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
38  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
39  import org.apache.maven.artifact.resolver.ResolutionErrorHandler;
40  import org.apache.maven.artifact.versioning.VersionRange;
41  import org.apache.maven.execution.MavenSession;
42  import org.apache.maven.plugin.AbstractMojo;
43  import org.apache.maven.plugin.MojoExecution;
44  import org.apache.maven.plugin.MojoExecutionException;
45  import org.apache.maven.plugins.annotations.Component;
46  import org.apache.maven.plugins.annotations.Parameter;
47  import org.apache.maven.project.MavenProject;
48  import org.apache.maven.repository.RepositorySystem;
49  import org.apache.maven.shared.incremental.IncrementalBuildHelper;
50  import org.apache.maven.shared.incremental.IncrementalBuildHelperRequest;
51  import org.apache.maven.shared.utils.ReaderFactory;
52  import org.apache.maven.shared.utils.StringUtils;
53  import org.apache.maven.shared.utils.io.FileUtils;
54  import org.apache.maven.toolchain.Toolchain;
55  import org.apache.maven.toolchain.ToolchainManager;
56  import org.codehaus.plexus.compiler.Compiler;
57  import org.codehaus.plexus.compiler.CompilerConfiguration;
58  import org.codehaus.plexus.compiler.CompilerError;
59  import org.codehaus.plexus.compiler.CompilerException;
60  import org.codehaus.plexus.compiler.CompilerMessage;
61  import org.codehaus.plexus.compiler.CompilerNotImplementedException;
62  import org.codehaus.plexus.compiler.CompilerOutputStyle;
63  import org.codehaus.plexus.compiler.CompilerResult;
64  import org.codehaus.plexus.compiler.manager.CompilerManager;
65  import org.codehaus.plexus.compiler.manager.NoSuchCompilerException;
66  import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
67  import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
68  import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
69  import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
70  import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
71  
72  /**
73   * TODO: At least one step could be optimized, currently the plugin will do two
74   * scans of all the source code if the compiler has to have the entire set of
75   * sources. This is currently the case for at least the C# compiler and most
76   * likely all the other .NET compilers too.
77   *
78   * @author others
79   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
80   * @version $Id: AbstractCompilerMojo.html 999963 2016-10-26 20:17:16Z rfscholte $
81   * @since 2.0
82   */
83  public abstract class AbstractCompilerMojo
84      extends AbstractMojo
85  {
86  
87      static final String DEFAULT_SOURCE = "1.5";
88      
89      static final String DEFAULT_TARGET = "1.5";
90      
91      // Used to compare with older targets
92      static final String MODULE_INFO_TARGET = "1.9";
93      
94      // ----------------------------------------------------------------------
95      // Configurables
96      // ----------------------------------------------------------------------
97  
98      /**
99       * Indicates whether the build will continue even if there are compilation errors.
100      *
101      * @since 2.0.2
102      */
103     @Parameter( property = "maven.compiler.failOnError", defaultValue = "true" )
104     private boolean failOnError = true;
105     
106     /**
107      * Indicates whether the build will continue even if there are compilation warnings.
108      *
109      * @since 3.6
110      */
111     @Parameter( property = "maven.compiler.failOnWarning", defaultValue = "false" )
112     private boolean failOnWarning;  
113 
114     /**
115      * Set to <code>true</code> to include debugging information in the compiled class files.
116      */
117     @Parameter( property = "maven.compiler.debug", defaultValue = "true" )
118     private boolean debug = true;
119 
120     /**
121      * Set to <code>true</code> to show messages about what the compiler is doing.
122      */
123     @Parameter( property = "maven.compiler.verbose", defaultValue = "false" )
124     private boolean verbose;
125 
126     /**
127      * Sets whether to show source locations where deprecated APIs are used.
128      */
129     @Parameter( property = "maven.compiler.showDeprecation", defaultValue = "false" )
130     private boolean showDeprecation;
131 
132     /**
133      * Set to <code>true</code> to optimize the compiled code using the compiler's optimization methods.
134      */
135     @Parameter( property = "maven.compiler.optimize", defaultValue = "false" )
136     private boolean optimize;
137 
138     /**
139      * Set to <code>true</code> to show compilation warnings.
140      */
141     @Parameter( property = "maven.compiler.showWarnings", defaultValue = "false" )
142     private boolean showWarnings;
143 
144     /**
145      * The -source argument for the Java compiler.
146      */
147     @Parameter( property = "maven.compiler.source", defaultValue = DEFAULT_SOURCE )
148     protected String source;
149 
150     /**
151      * The -target argument for the Java compiler.
152      */
153     @Parameter( property = "maven.compiler.target", defaultValue = DEFAULT_TARGET )
154     protected String target;
155 
156     /**
157      * The -release argument for the Java compiler, supported since Java9
158      * 
159      * @since 3.6
160      */
161     @Parameter( property = "maven.compiler.release" )
162     protected String release;
163     
164     /**
165      * The -encoding argument for the Java compiler.
166      *
167      * @since 2.1
168      */
169     @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
170     private String encoding;
171 
172     /**
173      * Sets the granularity in milliseconds of the last modification
174      * date for testing whether a source needs recompilation.
175      */
176     @Parameter( property = "lastModGranularityMs", defaultValue = "0" )
177     private int staleMillis;
178 
179     /**
180      * The compiler id of the compiler to use. See this
181      * <a href="non-javac-compilers.html">guide</a> for more information.
182      */
183     @Parameter( property = "maven.compiler.compilerId", defaultValue = "javac" )
184     private String compilerId;
185 
186     /**
187      * Version of the compiler to use, ex. "1.3", "1.5", if {@link #fork} is set to <code>true</code>.
188      */
189     @Parameter( property = "maven.compiler.compilerVersion" )
190     private String compilerVersion;
191 
192     /**
193      * Allows running the compiler in a separate process.
194      * If <code>false</code> it uses the built in compiler, while if <code>true</code> it will use an executable.
195      */
196     @Parameter( property = "maven.compiler.fork", defaultValue = "false" )
197     private boolean fork;
198 
199     /**
200      * Initial size, in megabytes, of the memory allocation pool, ex. "64", "64m"
201      * if {@link #fork} is set to <code>true</code>.
202      *
203      * @since 2.0.1
204      */
205     @Parameter( property = "maven.compiler.meminitial" )
206     private String meminitial;
207 
208     /**
209      * Sets the maximum size, in megabytes, of the memory allocation pool, ex. "128", "128m"
210      * if {@link #fork} is set to <code>true</code>.
211      *
212      * @since 2.0.1
213      */
214     @Parameter( property = "maven.compiler.maxmem" )
215     private String maxmem;
216 
217     /**
218      * Sets the executable of the compiler to use when {@link #fork} is <code>true</code>.
219      */
220     @Parameter( property = "maven.compiler.executable" )
221     private String executable;
222 
223     /**
224      * <p>
225      * Sets whether annotation processing is performed or not. Only applies to JDK 1.6+
226      * If not set, both compilation and annotation processing are performed at the same time.
227      * </p>
228      * <p>Allowed values are:</p>
229      * <ul>
230      * <li><code>none</code> - no annotation processing is performed.</li>
231      * <li><code>only</code> - only annotation processing is done, no compilation.</li>
232      * </ul>
233      *
234      * @since 2.2
235      */
236     @Parameter
237     private String proc;
238 
239     /**
240      * <p>
241      * Names of annotation processors to run. Only applies to JDK 1.6+
242      * If not set, the default annotation processors discovery process applies.
243      * </p>
244      *
245      * @since 2.2
246      */
247     @Parameter
248     private String[] annotationProcessors;
249 
250     /**
251      * <p>
252      * Classpath elements to supply as annotation processor path. If specified, the compiler will detect annotation
253      * processors only in those classpath elements. If omitted, the default classpath is used to detect annotation
254      * processors. The detection itself depends on the configuration of {@code annotationProcessors}.
255      * </p>
256      * <p>
257      * Each classpath element is specified using their Maven coordinates (groupId, artifactId, version, classifier,
258      * type). Transitive dependencies are added automatically. Example:
259      * </p>
260      *
261      * <pre>
262      * &lt;configuration&gt;
263      *   &lt;annotationProcessorPaths&gt;
264      *     &lt;path&gt;
265      *       &lt;groupId&gt;org.sample&lt;/groupId&gt;
266      *       &lt;artifactId&gt;sample-annotation-processor&lt;/artifactId&gt;
267      *       &lt;version&gt;1.2.3&lt;/version&gt;
268      *     &lt;/path&gt;
269      *     &lt;!-- ... more ... --&gt;
270      *   &lt;/annotationProcessorPaths&gt;
271      * &lt;/configuration&gt;
272      * </pre>
273      *
274      * @since 3.5
275      */
276     @Parameter
277     private List<DependencyCoordinate> annotationProcessorPaths;
278 
279     /**
280      * <p>
281      * Sets the arguments to be passed to the compiler (prepending a dash) if {@link #fork} is set to <code>true</code>.
282      * </p>
283      * <p>
284      * This is because the list of valid arguments passed to a Java compiler varies based on the compiler version.
285      * </p>
286      * <p>
287      * To pass <code>-Xmaxerrs 1000 -Xlint -Xlint:-path -Averbose=true</code> you should include the following:
288      * </p>
289      *
290      * <pre>
291      * &lt;compilerArguments&gt;
292      *   &lt;Xmaxerrs&gt;1000&lt;/Xmaxerrs&gt;
293      *   &lt;Xlint/&gt;
294      *   &lt;Xlint:-path/&gt;
295      *   &lt;Averbose&gt;true&lt;/Averbose&gt;
296      * &lt;/compilerArguments&gt;
297      * </pre>
298      *
299      * @since 2.0.1
300      * @deprecated use {@link #compilerArgs} instead.
301      */
302     @Parameter
303     @Deprecated
304     protected Map<String, String> compilerArguments;
305 
306     /**
307      * <p>
308      * Sets the arguments to be passed to the compiler if {@link #fork} is set to <code>true</code>.
309      * Example:
310      * <pre>
311      * &lt;compilerArgs&gt;
312      *   &lt;arg&gt;-Xmaxerrs=1000&lt;/arg&gt;
313      *   &lt;arg&gt;-Xlint&lt;/arg&gt;
314      *   &lt;arg&gt;-J-Duser.language=en_us&lt;/arg&gt;
315      * &lt;/compilerArgs&gt;
316      * </pre>
317      *
318      * @since 3.1
319      */
320     @Parameter
321     protected List<String> compilerArgs;
322 
323     /**
324      * <p>
325      * Sets the unformatted single argument string to be passed to the compiler if {@link #fork} is set to
326      * <code>true</code>. To pass multiple arguments such as <code>-Xmaxerrs 1000</code> (which are actually two
327      * arguments) you have to use {@link #compilerArguments}.
328      * </p>
329      * <p>
330      * This is because the list of valid arguments passed to a Java compiler varies based on the compiler version.
331      * </p>
332      */
333     @Parameter
334     protected String compilerArgument;
335 
336     /**
337      * Sets the name of the output file when compiling a set of
338      * sources to a single file.
339      * <p/>
340      * expression="${project.build.finalName}"
341      */
342     @Parameter
343     private String outputFileName;
344 
345     /**
346      * Keyword list to be appended to the <code>-g</code> command-line switch. Legal values are none or a
347      * comma-separated list of the following keywords: <code>lines</code>, <code>vars</code>, and <code>source</code>.
348      * If debug level is not specified, by default, nothing will be appended to <code>-g</code>.
349      * If debug is not turned on, this attribute will be ignored.
350      *
351      * @since 2.1
352      */
353     @Parameter( property = "maven.compiler.debuglevel" )
354     private String debuglevel;
355 
356     /**
357      *
358      */
359     @Component
360     private ToolchainManager toolchainManager;
361 
362     /**
363      * <p>
364      * Specify the requirements for this jdk toolchain.
365      * This overrules the toolchain selected by the maven-toolchain-plugin.
366      * </p>
367      * <strong>note:</strong> requires at least Maven 3.3.1
368      * 
369      * @since 3.6
370      */
371     @Parameter
372     private Map<String, String> jdkToolchain;
373 
374     // ----------------------------------------------------------------------
375     // Read-only parameters
376     // ----------------------------------------------------------------------
377 
378     /**
379      * The directory to run the compiler from if fork is true.
380      */
381     @Parameter( defaultValue = "${basedir}", required = true, readonly = true )
382     private File basedir;
383 
384     /**
385      * The target directory of the compiler if fork is true.
386      */
387     @Parameter( defaultValue = "${project.build.directory}", required = true, readonly = true )
388     private File buildDirectory;
389 
390     /**
391      * Plexus compiler manager.
392      */
393     @Component
394     private CompilerManager compilerManager;
395 
396     /**
397      * The current build session instance. This is used for toolchain manager API calls.
398      */
399     @Parameter( defaultValue = "${session}", readonly = true, required = true )
400     private MavenSession session;
401 
402     /**
403      * The current project instance. This is used for propagating generated-sources paths as compile/testCompile source
404      * roots.
405      */
406     @Parameter( defaultValue = "${project}", readonly = true, required = true )
407     private MavenProject project;
408 
409     /**
410      * Strategy to re use javacc class created:
411      * <ul>
412      * <li><code>reuseCreated</code> (default): will reuse already created but in case of multi-threaded builds, each
413      * thread will have its own instance</li>
414      * <li><code>reuseSame</code>: the same Javacc class will be used for each compilation even for multi-threaded build
415      * </li>
416      * <li><code>alwaysNew</code>: a new Javacc class will be created for each compilation</li>
417      * </ul>
418      * Note this parameter value depends on the os/jdk you are using, but the default value should work on most of env.
419      *
420      * @since 2.5
421      */
422     @Parameter( defaultValue = "${reuseCreated}", property = "maven.compiler.compilerReuseStrategy" )
423     private String compilerReuseStrategy = "reuseCreated";
424 
425     /**
426      * @since 2.5
427      */
428     @Parameter( defaultValue = "false", property = "maven.compiler.skipMultiThreadWarning" )
429     private boolean skipMultiThreadWarning;
430 
431     /**
432      * compiler can now use javax.tools if available in your current jdk, you can disable this feature
433      * using -Dmaven.compiler.forceJavacCompilerUse=true or in the plugin configuration
434      *
435      * @since 3.0
436      */
437     @Parameter( defaultValue = "false", property = "maven.compiler.forceJavacCompilerUse" )
438     private boolean forceJavacCompilerUse;
439 
440     /**
441      * @since 3.0 needed for storing the status for the incremental build support.
442      */
443     @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true )
444     private MojoExecution mojoExecution;
445 
446     /**
447      * file extensions to check timestamp for incremental build
448      * <b>default contains only <code>.class</code></b>
449      *
450      * @since 3.1
451      */
452     @Parameter
453     private List<String> fileExtensions;
454 
455     /**
456      * to enable/disable incrementation compilation feature
457      * @since 3.1
458      */
459     @Parameter( defaultValue = "true", property = "maven.compiler.useIncrementalCompilation" )
460     private boolean useIncrementalCompilation = true;
461 
462     /**
463      * Resolves the artifacts needed.
464      */
465     @Component
466     private RepositorySystem repositorySystem;
467 
468     /**
469      * Artifact handler manager.
470      */
471     @Component
472     private ArtifactHandlerManager artifactHandlerManager;
473 
474     /**
475      * Throws an exception on artifact resolution errors.
476      */
477     @Component
478     private ResolutionErrorHandler resolutionErrorHandler;
479 
480     protected abstract SourceInclusionScanner getSourceInclusionScanner( int staleMillis );
481 
482     protected abstract SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding );
483 
484     protected abstract List<String> getClasspathElements();
485 
486     protected abstract List<String> getModulepathElements();
487 
488     protected abstract List<String> getCompileSourceRoots();
489     
490     protected abstract void preparePaths( Set<File> sourceFiles );
491 
492     protected abstract File getOutputDirectory();
493 
494     protected abstract String getSource();
495 
496     protected abstract String getTarget();
497 
498     protected abstract String getRelease();
499 
500     protected abstract String getCompilerArgument();
501 
502     protected abstract Map<String, String> getCompilerArguments();
503 
504     protected abstract File getGeneratedSourcesDirectory();
505 
506     protected final MavenProject getProject()
507     {
508         return project;
509     }
510 
511     @Override
512     public void execute()
513         throws MojoExecutionException, CompilationFailureException
514     {
515         // ----------------------------------------------------------------------
516         // Look up the compiler. This is done before other code than can
517         // cause the mojo to return before the lookup is done possibly resulting
518         // in misconfigured POMs still building.
519         // ----------------------------------------------------------------------
520 
521         Compiler compiler;
522 
523         getLog().debug( "Using compiler '" + compilerId + "'." );
524 
525         try
526         {
527             compiler = compilerManager.getCompiler( compilerId );
528         }
529         catch ( NoSuchCompilerException e )
530         {
531             throw new MojoExecutionException( "No such compiler '" + e.getCompilerId() + "'." );
532         }
533 
534         //-----------toolchains start here ----------------------------------
535         //use the compilerId as identifier for toolchains as well.
536         Toolchain tc = getToolchain();
537         if ( tc != null )
538         {
539             getLog().info( "Toolchain in maven-compiler-plugin: " + tc );
540             if ( executable != null )
541             {
542                 getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + executable );
543             }
544             else
545             {
546                 fork = true;
547                 //TODO somehow shaky dependency between compilerId and tool executable.
548                 executable = tc.findTool( compilerId );
549             }
550         }
551         // ----------------------------------------------------------------------
552         //
553         // ----------------------------------------------------------------------
554 
555         List<String> compileSourceRoots = removeEmptyCompileSourceRoots( getCompileSourceRoots() );
556 
557         if ( compileSourceRoots.isEmpty() )
558         {
559             getLog().info( "No sources to compile" );
560 
561             return;
562         }
563 
564         // ----------------------------------------------------------------------
565         // Create the compiler configuration
566         // ----------------------------------------------------------------------
567 
568         CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
569 
570         compilerConfiguration.setOutputLocation( getOutputDirectory().getAbsolutePath() );
571 
572         compilerConfiguration.setOptimize( optimize );
573 
574         compilerConfiguration.setDebug( debug );
575 
576         if ( debug && StringUtils.isNotEmpty( debuglevel ) )
577         {
578             String[] split = StringUtils.split( debuglevel, "," );
579             for ( String aSplit : split )
580             {
581                 if ( !( aSplit.equalsIgnoreCase( "none" ) || aSplit.equalsIgnoreCase( "lines" )
582                     || aSplit.equalsIgnoreCase( "vars" ) || aSplit.equalsIgnoreCase( "source" ) ) )
583                 {
584                     throw new IllegalArgumentException( "The specified debug level: '" + aSplit + "' is unsupported. "
585                         + "Legal values are 'none', 'lines', 'vars', and 'source'." );
586                 }
587             }
588             compilerConfiguration.setDebugLevel( debuglevel );
589         }
590 
591         compilerConfiguration.setVerbose( verbose );
592 
593         compilerConfiguration.setShowWarnings( showWarnings );
594 
595         compilerConfiguration.setFailOnWarning( failOnWarning );
596 
597         compilerConfiguration.setShowDeprecation( showDeprecation );
598 
599         compilerConfiguration.setSourceVersion( getSource() );
600 
601         compilerConfiguration.setTargetVersion( getTarget() );
602         
603         compilerConfiguration.setReleaseVersion( getRelease() );
604 
605         compilerConfiguration.setProc( proc );
606 
607         File generatedSourcesDirectory = getGeneratedSourcesDirectory();
608         compilerConfiguration.setGeneratedSourcesDirectory( generatedSourcesDirectory != null
609                         ? generatedSourcesDirectory.getAbsoluteFile() : null );
610 
611         if ( generatedSourcesDirectory != null )
612         {
613             String generatedSourcesPath = generatedSourcesDirectory.getAbsolutePath();
614 
615             compileSourceRoots.add( generatedSourcesPath );
616 
617             if ( isTestCompile() )
618             {
619                 getLog().debug( "Adding " + generatedSourcesPath + " to test-compile source roots:\n  "
620                                     + StringUtils.join( project.getTestCompileSourceRoots()
621                                                                .iterator(), "\n  " ) );
622 
623                 project.addTestCompileSourceRoot( generatedSourcesPath );
624 
625                 getLog().debug( "New test-compile source roots:\n  "
626                                     + StringUtils.join( project.getTestCompileSourceRoots()
627                                                                .iterator(), "\n  " ) );
628             }
629             else
630             {
631                 getLog().debug( "Adding " + generatedSourcesPath + " to compile source roots:\n  "
632                                     + StringUtils.join( project.getCompileSourceRoots()
633                                                                .iterator(), "\n  " ) );
634 
635                 project.addCompileSourceRoot( generatedSourcesPath );
636 
637                 getLog().debug( "New compile source roots:\n  " + StringUtils.join( project.getCompileSourceRoots()
638                                                                                            .iterator(), "\n  " ) );
639             }
640         }
641 
642         compilerConfiguration.setSourceLocations( compileSourceRoots );
643 
644         compilerConfiguration.setAnnotationProcessors( annotationProcessors );
645 
646         compilerConfiguration.setProcessorPathEntries( resolveProcessorPathEntries() );
647 
648         compilerConfiguration.setSourceEncoding( encoding );
649 
650         compilerConfiguration.setFork( fork );
651 
652         if ( fork )
653         {
654             if ( !StringUtils.isEmpty( meminitial ) )
655             {
656                 String value = getMemoryValue( meminitial );
657 
658                 if ( value != null )
659                 {
660                     compilerConfiguration.setMeminitial( value );
661                 }
662                 else
663                 {
664                     getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." );
665                 }
666             }
667 
668             if ( !StringUtils.isEmpty( maxmem ) )
669             {
670                 String value = getMemoryValue( maxmem );
671 
672                 if ( value != null )
673                 {
674                     compilerConfiguration.setMaxmem( value );
675                 }
676                 else
677                 {
678                     getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." );
679                 }
680             }
681         }
682 
683         compilerConfiguration.setExecutable( executable );
684 
685         compilerConfiguration.setWorkingDirectory( basedir );
686 
687         compilerConfiguration.setCompilerVersion( compilerVersion );
688 
689         compilerConfiguration.setBuildDirectory( buildDirectory );
690 
691         compilerConfiguration.setOutputFileName( outputFileName );
692 
693         if ( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals( this.compilerReuseStrategy ) )
694         {
695             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew );
696         }
697         else if ( CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy().equals(
698             this.compilerReuseStrategy ) )
699         {
700             if ( getRequestThreadCount() > 1 )
701             {
702                 if ( !skipMultiThreadWarning )
703                 {
704                     getLog().warn( "You are in a multi-thread build and compilerReuseStrategy is set to reuseSame."
705                                        + " This can cause issues in some environments (os/jdk)!"
706                                        + " Consider using reuseCreated strategy."
707                                        + System.getProperty( "line.separator" )
708                                        + "If your env is fine with reuseSame, you can skip this warning with the "
709                                        + "configuration field skipMultiThreadWarning "
710                                        + "or -Dmaven.compiler.skipMultiThreadWarning=true" );
711                 }
712             }
713             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseSame );
714         }
715         else
716         {
717 
718             compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseCreated );
719         }
720 
721         getLog().debug( "CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy() );
722 
723         compilerConfiguration.setForceJavacCompilerUse( forceJavacCompilerUse );
724 
725         boolean canUpdateTarget;
726 
727         IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper( mojoExecution, session );
728 
729         Set<File> sources;
730 
731         IncrementalBuildHelperRequest incrementalBuildHelperRequest = null;
732 
733         if ( useIncrementalCompilation )
734         {
735             getLog().debug( "useIncrementalCompilation enabled" );
736             try
737             {
738                 canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
739 
740                 sources = getCompileSources( compiler, compilerConfiguration );
741                 
742                 preparePaths( sources );
743 
744                 incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles( sources );
745 
746                 // CHECKSTYLE_OFF: LineLength
747                 if ( ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES ) && !canUpdateTarget )
748                     || isDependencyChanged()
749                     || isSourceChanged( compilerConfiguration, compiler )
750                     || incrementalBuildHelper.inputFileTreeChanged( incrementalBuildHelperRequest ) )
751                     // CHECKSTYLE_ON: LineLength
752                 {
753                     getLog().info( "Changes detected - recompiling the module!" );
754 
755                     compilerConfiguration.setSourceFiles( sources );
756                 }
757                 else
758                 {
759                     getLog().info( "Nothing to compile - all classes are up to date" );
760 
761                     return;
762                 }
763             }
764             catch ( CompilerException e )
765             {
766                 throw new MojoExecutionException( "Error while computing stale sources.", e );
767             }
768         }
769         else
770         {
771             getLog().debug( "useIncrementalCompilation disabled" );
772             Set<File> staleSources;
773             try
774             {
775                 staleSources =
776                     computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
777 
778                 canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );
779 
780                 if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
781                     && !canUpdateTarget )
782                 {
783                     getLog().info( "RESCANNING!" );
784                     // TODO: This second scan for source files is sub-optimal
785                     String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
786 
787                     sources = computeStaleSources( compilerConfiguration, compiler,
788                                                              getSourceInclusionScanner( inputFileEnding ) );
789 
790                     compilerConfiguration.setSourceFiles( sources );
791                 }
792                 else
793                 {
794                     compilerConfiguration.setSourceFiles( staleSources );
795                 }
796                 
797                 preparePaths( compilerConfiguration.getSourceFiles() );
798             }
799             catch ( CompilerException e )
800             {
801                 throw new MojoExecutionException( "Error while computing stale sources.", e );
802             }
803 
804             if ( staleSources.isEmpty() )
805             {
806                 getLog().info( "Nothing to compile - all classes are up to date" );
807 
808                 return;
809             }
810         }
811         
812         // Dividing pathElements of classPath and modulePath is based on sourceFiles
813         compilerConfiguration.setClasspathEntries( getClasspathElements() );
814 
815         compilerConfiguration.setModulepathEntries( getModulepathElements() );
816         
817         Map<String, String> effectiveCompilerArguments = getCompilerArguments();
818 
819         String effectiveCompilerArgument = getCompilerArgument();
820 
821         if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null )
822                         || ( compilerArgs != null ) )
823         {
824             if ( effectiveCompilerArguments != null )
825             {
826                 for ( Map.Entry<String, String> me : effectiveCompilerArguments.entrySet() )
827                 {
828                     String key = me.getKey();
829                     String value = me.getValue();
830                     if ( !key.startsWith( "-" ) )
831                     {
832                         key = "-" + key;
833                     }
834 
835                     if ( key.startsWith( "-A" ) && StringUtils.isNotEmpty( value ) )
836                     {
837                         compilerConfiguration.addCompilerCustomArgument( key + "=" + value, null );
838                     }
839                     else
840                     {
841                         compilerConfiguration.addCompilerCustomArgument( key, value );
842                     }
843                 }
844             }
845             if ( !StringUtils.isEmpty( effectiveCompilerArgument ) )
846             {
847                 compilerConfiguration.addCompilerCustomArgument( effectiveCompilerArgument, null );
848             }
849             if ( compilerArgs != null )
850             {
851                 for ( String arg : compilerArgs )
852                 {
853                     compilerConfiguration.addCompilerCustomArgument( arg, null );
854                 }
855             }
856         }
857 
858         // ----------------------------------------------------------------------
859         // Dump configuration
860         // ----------------------------------------------------------------------
861         if ( getLog().isDebugEnabled() )
862         {
863             getLog().debug( "Classpath:" );
864 
865             for ( String s : getClasspathElements() )
866             {
867                 getLog().debug( " " + s );
868             }
869 
870             if ( !getModulepathElements().isEmpty() )
871             {
872                 getLog().debug( "Modulepath:" );
873                 for ( String s : getModulepathElements() )
874                 {
875                     getLog().debug( " " + s );
876                 }
877             }
878 
879             getLog().debug( "Source roots:" );
880 
881             for ( String root : getCompileSourceRoots() )
882             {
883                 getLog().debug( " " + root );
884             }
885 
886             try
887             {
888                 if ( fork )
889                 {
890                     if ( compilerConfiguration.getExecutable() != null )
891                     {
892                         getLog().debug( "Excutable: " );
893                         getLog().debug( " " + compilerConfiguration.getExecutable() );
894                     }
895                 }
896 
897                 String[] cl = compiler.createCommandLine( compilerConfiguration );
898                 if ( getLog().isDebugEnabled() && cl != null && cl.length > 0 )
899                 {
900                     StringBuilder sb = new StringBuilder();
901                     sb.append( cl[0] );
902                     for ( int i = 1; i < cl.length; i++ )
903                     {
904                         sb.append( " " );
905                         sb.append( cl[i] );
906                     }
907                     getLog().debug( "Command line options:" );
908                     getLog().debug( sb );
909                 }
910             }
911             catch ( CompilerException ce )
912             {
913                 getLog().debug( ce );
914             }
915         }
916 
917         // ----------------------------------------------------------------------
918         // Compile!
919         // ----------------------------------------------------------------------
920 
921         if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) )
922         {
923             getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
924                                + ", i.e. build is platform dependent!" );
925         }
926 
927         CompilerResult compilerResult;
928 
929 
930         if ( useIncrementalCompilation )
931         {
932             incrementalBuildHelperRequest.outputDirectory( getOutputDirectory() );
933 
934             incrementalBuildHelper.beforeRebuildExecution( incrementalBuildHelperRequest );
935 
936             getLog().debug( "incrementalBuildHelper#beforeRebuildExecution" );
937         }
938 
939         try
940         {
941             try
942             {
943                 compilerResult = compiler.performCompile( compilerConfiguration );
944             }
945             catch ( CompilerNotImplementedException cnie )
946             {
947                 List<CompilerError> messages = compiler.compile( compilerConfiguration );
948                 compilerResult = convertToCompilerResult( messages );
949             }
950         }
951         catch ( Exception e )
952         {
953             // TODO: don't catch Exception
954             throw new MojoExecutionException( "Fatal error compiling", e );
955         }
956 
957         if ( useIncrementalCompilation )
958         {
959             if ( incrementalBuildHelperRequest.getOutputDirectory().exists() )
960             {
961                 getLog().debug( "incrementalBuildHelper#afterRebuildExecution" );
962                 // now scan the same directory again and create a diff
963                 incrementalBuildHelper.afterRebuildExecution( incrementalBuildHelperRequest );
964             }
965             else
966             {
967                 getLog().debug(
968                     "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist" );
969             }
970         }
971 
972         List<CompilerMessage> warnings = new ArrayList<CompilerMessage>();
973         List<CompilerMessage> errors = new ArrayList<CompilerMessage>();
974         List<CompilerMessage> others = new ArrayList<CompilerMessage>();
975         for ( CompilerMessage message : compilerResult.getCompilerMessages() )
976         {
977             if ( message.getKind() == CompilerMessage.Kind.ERROR )
978             {
979                 errors.add( message );
980             }
981             else if ( message.getKind() == CompilerMessage.Kind.WARNING
982                 || message.getKind() == CompilerMessage.Kind.MANDATORY_WARNING )
983             {
984                 warnings.add( message );
985             }
986             else
987             {
988                 others.add( message );
989             }
990         }
991 
992         if ( failOnError && !compilerResult.isSuccess() )
993         {
994             for ( CompilerMessage message : others )
995             {
996                 assert message.getKind() != CompilerMessage.Kind.ERROR
997                     && message.getKind() != CompilerMessage.Kind.WARNING
998                     && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
999                 getLog().info( message.toString() );
1000             }
1001             if ( !warnings.isEmpty() )
1002             {
1003                 getLog().info( "-------------------------------------------------------------" );
1004                 getLog().warn( "COMPILATION WARNING : " );
1005                 getLog().info( "-------------------------------------------------------------" );
1006                 for ( CompilerMessage warning : warnings )
1007                 {
1008                     getLog().warn( warning.toString() );
1009                 }
1010                 getLog().info( warnings.size() + ( ( warnings.size() > 1 ) ? " warnings " : " warning" ) );
1011                 getLog().info( "-------------------------------------------------------------" );
1012             }
1013 
1014             if ( !errors.isEmpty() )
1015             {
1016                 getLog().info( "-------------------------------------------------------------" );
1017                 getLog().error( "COMPILATION ERROR : " );
1018                 getLog().info( "-------------------------------------------------------------" );
1019                 for ( CompilerMessage error : errors )
1020                 {
1021                     getLog().error( error.toString() );
1022                 }
1023                 getLog().info( errors.size() + ( ( errors.size() > 1 ) ? " errors " : " error" ) );
1024                 getLog().info( "-------------------------------------------------------------" );
1025             }
1026 
1027             if ( !errors.isEmpty() )
1028             {
1029                 throw new CompilationFailureException( errors );
1030             }
1031             else
1032             {
1033                 throw new CompilationFailureException( warnings );
1034             }
1035         }
1036         else
1037         {
1038             for ( CompilerMessage message : compilerResult.getCompilerMessages() )
1039             {
1040                 switch ( message.getKind() )
1041                 {
1042                     case NOTE:
1043                     case OTHER:
1044                         getLog().info( message.toString() );
1045                         break;
1046 
1047                     case ERROR:
1048                         getLog().error( message.toString() );
1049                         break;
1050 
1051                     case MANDATORY_WARNING:
1052                     case WARNING:
1053                     default:
1054                         getLog().warn( message.toString() );
1055                         break;
1056                 }
1057             }
1058         }
1059     }
1060 
1061     protected boolean isTestCompile()
1062     {
1063         return false;
1064     }
1065 
1066     protected CompilerResult convertToCompilerResult( List<CompilerError> compilerErrors )
1067     {
1068         if ( compilerErrors == null )
1069         {
1070             return new CompilerResult();
1071         }
1072         List<CompilerMessage> messages = new ArrayList<CompilerMessage>( compilerErrors.size() );
1073         boolean success = true;
1074         for ( CompilerError compilerError : compilerErrors )
1075         {
1076             messages.add(
1077                 new CompilerMessage( compilerError.getFile(), compilerError.getKind(), compilerError.getStartLine(),
1078                                      compilerError.getStartColumn(), compilerError.getEndLine(),
1079                                      compilerError.getEndColumn(), compilerError.getMessage() ) );
1080             if ( compilerError.isError() )
1081             {
1082                 success = false;
1083             }
1084         }
1085 
1086         return new CompilerResult( success, messages );
1087     }
1088 
1089     /**
1090      * @return all source files for the compiler
1091      */
1092     private Set<File> getCompileSources( Compiler compiler, CompilerConfiguration compilerConfiguration )
1093         throws MojoExecutionException, CompilerException
1094     {
1095         String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );
1096         if ( StringUtils.isEmpty( inputFileEnding ) )
1097         {
1098             // see MCOMPILER-199 GroovyEclipseCompiler doesn't set inputFileEnding
1099             // so we can presume it's all files from the source directory
1100             inputFileEnding = ".*";
1101         }
1102         SourceInclusionScanner scanner = getSourceInclusionScanner( inputFileEnding );
1103 
1104         SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler );
1105 
1106         scanner.addSourceMapping( mapping );
1107 
1108         Set<File> compileSources = new HashSet<File>();
1109 
1110         for ( String sourceRoot : getCompileSourceRoots() )
1111         {
1112             File rootFile = new File( sourceRoot );
1113 
1114             if ( !rootFile.isDirectory()
1115                 || rootFile.getAbsoluteFile().equals( compilerConfiguration.getGeneratedSourcesDirectory() ) )
1116             {
1117                 continue;
1118             }
1119 
1120             try
1121             {
1122                 compileSources.addAll( scanner.getIncludedSources( rootFile, null ) );
1123             }
1124             catch ( InclusionScanException e )
1125             {
1126                 throw new MojoExecutionException(
1127                     "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
1128             }
1129         }
1130 
1131         return compileSources;
1132     }
1133 
1134     /**
1135      * @param compilerConfiguration
1136      * @param compiler
1137      * @return <code>true</code> if at least a single source file is newer than it's class file
1138      */
1139     private boolean isSourceChanged( CompilerConfiguration compilerConfiguration, Compiler compiler )
1140         throws CompilerException, MojoExecutionException
1141     {
1142         Set<File> staleSources =
1143             computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );
1144 
1145         if ( getLog().isDebugEnabled() )
1146         {
1147             for ( File f : staleSources )
1148             {
1149                 getLog().debug( "Stale source detected: " + f.getAbsolutePath() );
1150             }
1151         }
1152         return staleSources != null && staleSources.size() > 0;
1153     }
1154 
1155 
1156     /**
1157      * try to get thread count if a Maven 3 build, using reflection as the plugin must not be maven3 api dependent
1158      *
1159      * @return number of thread for this build or 1 if not multi-thread build
1160      */
1161     protected int getRequestThreadCount()
1162     {
1163         try
1164         {
1165             Method getRequestMethod = session.getClass().getMethod( "getRequest" );
1166             Object mavenExecutionRequest = getRequestMethod.invoke( this.session );
1167             Method getThreadCountMethod = mavenExecutionRequest.getClass().getMethod( "getThreadCount" );
1168             String threadCount = (String) getThreadCountMethod.invoke( mavenExecutionRequest );
1169             return Integer.valueOf( threadCount );
1170         }
1171         catch ( Exception e )
1172         {
1173             getLog().debug( "unable to get threadCount for the current build: " + e.getMessage() );
1174         }
1175         return 1;
1176     }
1177 
1178     protected Date getBuildStartTime()
1179     {
1180         Date buildStartTime = null;
1181         try
1182         {
1183             Method getRequestMethod = session.getClass().getMethod( "getRequest" );
1184             Object mavenExecutionRequest = getRequestMethod.invoke( session );
1185             Method getStartTimeMethod = mavenExecutionRequest.getClass().getMethod( "getStartTime" );
1186             buildStartTime = (Date) getStartTimeMethod.invoke( mavenExecutionRequest );
1187         }
1188         catch ( Exception e )
1189         {
1190             getLog().debug( "unable to get start time for the current build: " + e.getMessage() );
1191         }
1192 
1193         if ( buildStartTime == null )
1194         {
1195             return new Date();
1196         }
1197 
1198         return buildStartTime;
1199     }
1200 
1201 
1202     private String getMemoryValue( String setting )
1203     {
1204         String value = null;
1205 
1206         // Allow '128' or '128m'
1207         if ( isDigits( setting ) )
1208         {
1209             value = setting + "m";
1210         }
1211         else if ( ( isDigits( setting.substring( 0, setting.length() - 1 ) ) )
1212             && ( setting.toLowerCase().endsWith( "m" ) ) )
1213         {
1214             value = setting;
1215         }
1216         return value;
1217     }
1218 
1219     //TODO remove the part with ToolchainManager lookup once we depend on
1220     //3.0.9 (have it as prerequisite). Define as regular component field then.
1221     private Toolchain getToolchain()
1222     {
1223         Toolchain tc = null;
1224         
1225         if ( jdkToolchain != null )
1226         {
1227             // Maven 3.3.1 has plugin execution scoped Toolchain Support
1228             try
1229             {
1230                 Method getToolchainsMethod =
1231                     toolchainManager.getClass().getMethod( "getToolchains", MavenSession.class, String.class,
1232                                                            Map.class );
1233 
1234                 @SuppressWarnings( "unchecked" )
1235                 List<Toolchain> tcs =
1236                     (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, session, "jdk",
1237                                                                   jdkToolchain );
1238 
1239                 if ( tcs != null && tcs.size() > 0 )
1240                 {
1241                     tc = tcs.get( 0 );
1242                 }
1243             }
1244             catch ( NoSuchMethodException e )
1245             {
1246                 // ignore
1247             }
1248             catch ( SecurityException e )
1249             {
1250                 // ignore
1251             }
1252             catch ( IllegalAccessException e )
1253             {
1254                 // ignore
1255             }
1256             catch ( IllegalArgumentException e )
1257             {
1258                 // ignore
1259             }
1260             catch ( InvocationTargetException e )
1261             {
1262                 // ignore
1263             }
1264         }
1265         
1266         if ( tc == null )
1267         {
1268             tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
1269         }
1270         
1271         return tc;
1272     }
1273 
1274     private boolean isDigits( String string )
1275     {
1276         for ( int i = 0; i < string.length(); i++ )
1277         {
1278             if ( !Character.isDigit( string.charAt( i ) ) )
1279             {
1280                 return false;
1281             }
1282         }
1283         return true;
1284     }
1285 
1286     private Set<File> computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler,
1287                                            SourceInclusionScanner scanner )
1288         throws MojoExecutionException, CompilerException
1289     {
1290         SourceMapping mapping = getSourceMapping( compilerConfiguration, compiler );
1291 
1292         File outputDirectory;
1293         CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1294         if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
1295         {
1296             outputDirectory = buildDirectory;
1297         }
1298         else
1299         {
1300             outputDirectory = getOutputDirectory();
1301         }
1302 
1303         scanner.addSourceMapping( mapping );
1304 
1305         Set<File> staleSources = new HashSet<File>();
1306 
1307         for ( String sourceRoot : getCompileSourceRoots() )
1308         {
1309             File rootFile = new File( sourceRoot );
1310 
1311             if ( !rootFile.isDirectory() )
1312             {
1313                 continue;
1314             }
1315 
1316             try
1317             {
1318                 staleSources.addAll( scanner.getIncludedSources( rootFile, outputDirectory ) );
1319             }
1320             catch ( InclusionScanException e )
1321             {
1322                 throw new MojoExecutionException(
1323                     "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e );
1324             }
1325         }
1326 
1327         return staleSources;
1328     }
1329 
1330     private SourceMapping getSourceMapping( CompilerConfiguration compilerConfiguration, Compiler compiler )
1331         throws CompilerException, MojoExecutionException
1332     {
1333         CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();
1334 
1335         SourceMapping mapping;
1336         if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
1337         {
1338             mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ),
1339                                          compiler.getOutputFileEnding( compilerConfiguration ) );
1340         }
1341         else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
1342         {
1343             mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ),
1344                                                      compiler.getOutputFile( compilerConfiguration ) );
1345 
1346         }
1347         else
1348         {
1349             throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
1350         }
1351         return mapping;
1352     }
1353 
1354     /**
1355      * @todo also in ant plugin. This should be resolved at some point so that it does not need to
1356      * be calculated continuously - or should the plugins accept empty source roots as is?
1357      */
1358     private static List<String> removeEmptyCompileSourceRoots( List<String> compileSourceRootsList )
1359     {
1360         List<String> newCompileSourceRootsList = new ArrayList<String>();
1361         if ( compileSourceRootsList != null )
1362         {
1363             // copy as I may be modifying it
1364             for ( String srcDir : compileSourceRootsList )
1365             {
1366                 if ( !newCompileSourceRootsList.contains( srcDir ) && new File( srcDir ).exists() )
1367                 {
1368                     newCompileSourceRootsList.add( srcDir );
1369                 }
1370             }
1371         }
1372         return newCompileSourceRootsList;
1373     }
1374 
1375     /**
1376      * We just compare the timestamps of all local dependency files (inter-module dependency classpath) and the own
1377      * generated classes and if we got a file which is &gt;= the buid-started timestamp, then we catched a file which
1378      * got changed during this build.
1379      *
1380      * @return <code>true</code> if at least one single dependency has changed.
1381      */
1382     protected boolean isDependencyChanged()
1383     {
1384         if ( session == null )
1385         {
1386             // we just cannot determine it, so don't do anything beside logging
1387             getLog().info( "Cannot determine build start date, skipping incremental build detection." );
1388             return false;
1389         }
1390 
1391         if ( fileExtensions == null || fileExtensions.isEmpty() )
1392         {
1393             fileExtensions = new ArrayList<String>();
1394             fileExtensions.add( ".class" );
1395         }
1396 
1397         Date buildStartTime = getBuildStartTime();
1398 
1399         List<String> pathElements = new ArrayList<String>();
1400         pathElements.addAll( getClasspathElements() );
1401         pathElements.addAll( getModulepathElements() );
1402         
1403         for ( String pathElement : pathElements )
1404         {
1405             // ProjectArtifacts are artifacts which are available in the local project
1406             // that's the only ones we are interested in now.
1407             File artifactPath = new File( pathElement );
1408             if ( artifactPath.isDirectory() )
1409             {
1410                 if ( hasNewFile( artifactPath, buildStartTime ) )
1411                 {
1412                     getLog().debug( "New dependency detected: " + artifactPath.getAbsolutePath() );
1413                     return true;
1414                 }
1415             }
1416         }
1417 
1418         // obviously there was no new file detected.
1419         return false;
1420     }
1421 
1422     /**
1423      * @param classPathEntry entry to check
1424      * @param buildStartTime time build start
1425      * @return if any changes occurred
1426      */
1427     private boolean hasNewFile( File classPathEntry, Date buildStartTime )
1428     {
1429         if ( !classPathEntry.exists() )
1430         {
1431             return false;
1432         }
1433 
1434         if ( classPathEntry.isFile() )
1435         {
1436             return classPathEntry.lastModified() >= buildStartTime.getTime()
1437                 && fileExtensions.contains( FileUtils.getExtension( classPathEntry.getName() ) );
1438         }
1439 
1440         File[] children = classPathEntry.listFiles();
1441 
1442         for ( File child : children )
1443         {
1444             if ( hasNewFile( child, buildStartTime ) )
1445             {
1446                 return true;
1447             }
1448         }
1449 
1450         return false;
1451     }
1452 
1453     private List<String> resolveProcessorPathEntries()
1454         throws MojoExecutionException
1455     {
1456         if ( annotationProcessorPaths == null || annotationProcessorPaths.isEmpty() )
1457         {
1458             return null;
1459         }
1460 
1461         try
1462         {
1463             Set<Artifact> requiredArtifacts = new LinkedHashSet<Artifact>();
1464 
1465             for ( DependencyCoordinate coord : annotationProcessorPaths )
1466             {
1467                 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler( coord.getType() );
1468 
1469                 Artifact artifact = new DefaultArtifact(
1470                      coord.getGroupId(),
1471                      coord.getArtifactId(),
1472                      VersionRange.createFromVersionSpec( coord.getVersion() ),
1473                      Artifact.SCOPE_RUNTIME,
1474                      coord.getType(),
1475                      coord.getClassifier(),
1476                      handler,
1477                      false );
1478 
1479                 requiredArtifacts.add( artifact );
1480             }
1481 
1482             ArtifactResolutionRequest request = new ArtifactResolutionRequest()
1483                             .setArtifact( requiredArtifacts.iterator().next() )
1484                             .setResolveRoot( true )
1485                             .setResolveTransitively( true )
1486                             .setArtifactDependencies( requiredArtifacts )
1487                             .setLocalRepository( session.getLocalRepository() )
1488                             .setRemoteRepositories( project.getRemoteArtifactRepositories() );
1489 
1490             ArtifactResolutionResult resolutionResult = repositorySystem.resolve( request );
1491 
1492             resolutionErrorHandler.throwErrors( request, resolutionResult );
1493 
1494             List<String> elements = new ArrayList<String>( resolutionResult.getArtifacts().size() );
1495 
1496             for ( Object resolved : resolutionResult.getArtifacts() )
1497             {
1498                 elements.add( ( (Artifact) resolved ).getFile().getAbsolutePath() );
1499             }
1500 
1501             return elements;
1502         }
1503         catch ( Exception e )
1504         {
1505             throw new MojoExecutionException( "Resolution of annotationProcessorPath dependencies failed: "
1506                 + e.getLocalizedMessage(), e );
1507         }
1508     }
1509 }