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.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import org.apache.maven.artifact.Artifact;
33  import org.apache.maven.plugin.MojoExecutionException;
34  import org.apache.maven.plugin.compiler.module.JavaModuleDescriptor;
35  import org.apache.maven.plugin.compiler.module.ModuleInfoParser;
36  import org.apache.maven.plugin.compiler.module.ProjectAnalyzer;
37  import org.apache.maven.plugin.compiler.module.ProjectAnalyzerRequest;
38  import org.apache.maven.plugin.compiler.module.ProjectAnalyzerResult;
39  import org.apache.maven.plugins.annotations.Component;
40  import org.apache.maven.plugins.annotations.LifecyclePhase;
41  import org.apache.maven.plugins.annotations.Mojo;
42  import org.apache.maven.plugins.annotations.Parameter;
43  import org.apache.maven.plugins.annotations.ResolutionScope;
44  import org.apache.maven.project.MavenProject;
45  import org.apache.maven.shared.utils.StringUtils;
46  import org.apache.maven.shared.utils.logging.MessageUtils;
47  import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
48  import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
49  import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
50  
51  /**
52   * Compiles application sources
53   *
54   * @author <a href="mailto:jason@maven.org">Jason van Zyl </a>
55   * @version $Id: CompilerMojo.html 1015972 2017-07-25 23:24:13Z olamy $
56   * @since 2.0
57   */
58  @Mojo( name = "compile", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true, 
59      requiresDependencyResolution = ResolutionScope.COMPILE )
60  public class CompilerMojo
61      extends AbstractCompilerMojo
62  {
63      /**
64       * The source directories containing the sources to be compiled.
65       */
66      @Parameter( defaultValue = "${project.compileSourceRoots}", readonly = true, required = true )
67      private List<String> compileSourceRoots;
68  
69      /**
70       * The directory for compiled classes.
71       */
72      @Parameter( defaultValue = "${project.build.outputDirectory}", required = true, readonly = true )
73      private File outputDirectory;
74  
75      /**
76       * Projects main artifact.
77       *
78       * @todo this is an export variable, really
79       */
80      @Parameter( defaultValue = "${project.artifact}", readonly = true, required = true )
81      private Artifact projectArtifact;
82  
83      /**
84       * A list of inclusion filters for the compiler.
85       */
86      @Parameter
87      private Set<String> includes = new HashSet<String>();
88  
89      /**
90       * A list of exclusion filters for the compiler.
91       */
92      @Parameter
93      private Set<String> excludes = new HashSet<String>();
94  
95      /**
96       * <p>
97       * Specify where to place generated source files created by annotation processing. Only applies to JDK 1.6+
98       * </p>
99       *
100      * @since 2.2
101      */
102     @Parameter( defaultValue = "${project.build.directory}/generated-sources/annotations" )
103     private File generatedSourcesDirectory;
104 
105     /**
106      * Set this to 'true' to bypass compilation of main sources. Its use is NOT RECOMMENDED, but quite convenient on
107      * occasion.
108      */
109     @Parameter( property = "maven.main.skip" )
110     private boolean skipMain;
111 
112     @Parameter( defaultValue = "${project.compileClasspathElements}", readonly = true, required = true )
113     private List<String> compilePath;
114     
115     @Parameter
116     private boolean allowPartialRequirements;
117 
118     @Component( hint = "qdox" )
119     private ModuleInfoParser moduleInfoParser;
120 
121     @Component
122     private ProjectAnalyzer projectAnalyzer;
123 
124     private List<String> classpathElements;
125 
126     private List<String> modulepathElements;
127 
128     protected List<String> getCompileSourceRoots()
129     {
130         return compileSourceRoots;
131     }
132 
133     protected List<String> getClasspathElements()
134     {
135         return classpathElements;
136     }
137 
138     @Override
139     protected List<String> getModulepathElements()
140     {
141         return modulepathElements;
142     }
143 
144     protected File getOutputDirectory()
145     {
146         return outputDirectory;
147     }
148 
149     public void execute()
150         throws MojoExecutionException, CompilationFailureException
151     {
152         if ( skipMain )
153         {
154             getLog().info( "Not compiling main sources" );
155             return;
156         }
157 
158         super.execute();
159 
160         if ( outputDirectory.isDirectory() )
161         {
162             projectArtifact.setFile( outputDirectory );
163         }
164     }
165 
166     @Override
167     protected void preparePaths( Set<File> sourceFiles )
168     {
169         assert compilePath != null;
170 
171         JavaModuleDescriptor moduleDescriptor = null;
172 
173         boolean hasModuleDescriptor = false;
174         for ( File sourceFile : sourceFiles )
175         {
176             if ( "module-info.java".equals( sourceFile.getName() ) )
177             {
178                 try
179                 {
180                     moduleDescriptor = moduleInfoParser.getModuleDescriptor( sourceFile.getParentFile() );
181                 }
182                 catch ( IOException e )
183                 {
184                     getLog().warn( "Failed to parse module-info.java: " + e.getMessage() );
185                 }
186                 hasModuleDescriptor = true;
187                 break;
188             }
189         }
190 
191         if ( hasModuleDescriptor )
192         {
193             // For now only allow named modules. Once we can create a graph with ASM we can specify exactly the modules
194             // and we can detect if auto modules are used. In that case, MavenProject.setFile() should not be used, so
195             // you cannot depend on this project and so it won't be distributed.
196 
197             modulepathElements = new ArrayList<String>( compilePath.size() );
198             classpathElements = new ArrayList<String>( compilePath.size() );
199 
200             ProjectAnalyzerResult analyzerResult;
201             try
202             {
203                 Collection<File> dependencyArtifacts = getCompileClasspathElements( getProject() );
204 
205                 ProjectAnalyzerRequest analyzerRequest = new ProjectAnalyzerRequest()
206                                 .setBaseModuleDescriptor( moduleDescriptor )
207                                 .setDependencyArtifacts( dependencyArtifacts );
208 
209                 analyzerResult = projectAnalyzer.analyze( analyzerRequest );
210 
211                 if ( !analyzerResult.getRequiredAutomaticModules().isEmpty() )
212                 {
213                     boolean filenameBased = false;
214                     
215                     for ( String automodule : analyzerResult.getRequiredAutomaticModules() )
216                     {
217                         filenameBased =
218                             ProjectAnalyzerResult.ModuleNameSource.FILENAME.equals( 
219                                                             analyzerResult.getModuleNameSource( automodule ) );
220                         
221                         if ( filenameBased )
222                         {
223                             final String message = "Required automodules detected. "
224                                 + "Please don't publish this project to a public artifact repository!";
225                             
226                             if ( moduleDescriptor.exports().isEmpty() )
227                             {
228                                 // application
229                                 getLog().info( message );
230                             }
231                             else
232                             {
233                                 // library
234                                 writeBoxedWarning( message );
235                             }
236                             
237                             break;
238                         }
239                     }
240                 }
241                 
242                 for ( Map.Entry<File, JavaModuleDescriptor> entry : analyzerResult.getPathElements().entrySet() )
243                 {
244                     if ( !allowPartialRequirements )
245                     {
246                         modulepathElements.add( entry.getKey().getPath() );
247                     }
248                     else if ( entry.getValue() == null )
249                     {
250                         classpathElements.add( entry.getKey().getPath() );
251                     }
252                     else if ( analyzerResult.getRequiredNormalModules().contains( entry.getValue().name() ) )
253                     {
254                         modulepathElements.add( entry.getKey().getPath() );
255                     }
256                     else if ( analyzerResult.getRequiredAutomaticModules().contains( entry.getValue().name() ) )
257                     {
258                         modulepathElements.add( entry.getKey().getPath() );
259                     }
260                     else
261                     {
262                         classpathElements.add( entry.getKey().getPath() );
263                     }
264                 }
265             }
266             catch ( IOException e )
267             {
268                 getLog().warn( e.getMessage() );
269             }
270 
271 //            if ( !classpathElements.isEmpty() )
272 //            {
273 //                if ( compilerArgs == null )
274 //                {
275 //                    compilerArgs = new ArrayList<String>();
276 //                }
277 //                compilerArgs.add( "--add-reads" );
278 //                compilerArgs.add( moduleDescriptor.name() + "=ALL-UNNAMED" );
279 //
280 //                if ( !modulepathElements.isEmpty() )
281 //                {
282 //                    compilerArgs.add( "--add-reads" );
283 //                    compilerArgs.add( "ALL-MODULE-PATH=ALL-UNNAMED" );
284 //                }
285 //            }
286         }
287         else
288         {
289             classpathElements = compilePath;
290             modulepathElements = Collections.emptyList();
291         }
292     }
293     
294     private List<File> getCompileClasspathElements( MavenProject project )
295     {
296         List<File> list = new ArrayList<File>( project.getArtifacts().size() + 1 );
297 
298         list.add( new File( project.getBuild().getOutputDirectory() ) );
299 
300         for ( Artifact a : project.getArtifacts() )
301         {
302             list.add( a.getFile() );
303         }
304         return list;
305     }
306     
307     protected SourceInclusionScanner getSourceInclusionScanner( int staleMillis )
308     {
309         SourceInclusionScanner scanner;
310 
311         if ( includes.isEmpty() && excludes.isEmpty() )
312         {
313             scanner = new StaleSourceScanner( staleMillis );
314         }
315         else
316         {
317             if ( includes.isEmpty() )
318             {
319                 includes.add( "**/*.java" );
320             }
321             scanner = new StaleSourceScanner( staleMillis, includes, excludes );
322         }
323 
324         return scanner;
325     }
326 
327     protected SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding )
328     {
329         SourceInclusionScanner scanner;
330 
331         // it's not defined if we get the ending with or without the dot '.'
332         String defaultIncludePattern = "**/*" + ( inputFileEnding.startsWith( "." ) ? "" : "." ) + inputFileEnding;
333 
334         if ( includes.isEmpty() && excludes.isEmpty() )
335         {
336             includes = Collections.singleton( defaultIncludePattern );
337             scanner = new SimpleSourceInclusionScanner( includes, Collections.<String>emptySet() );
338         }
339         else
340         {
341             if ( includes.isEmpty() )
342             {
343                 includes.add( defaultIncludePattern );
344             }
345             scanner = new SimpleSourceInclusionScanner( includes, excludes );
346         }
347 
348         return scanner;
349     }
350 
351     protected String getSource()
352     {
353         return source;
354     }
355 
356     protected String getTarget()
357     {
358         return target;
359     }
360 
361     @Override
362     protected String getRelease()
363     {
364         return release;
365     }
366 
367     protected String getCompilerArgument()
368     {
369         return compilerArgument;
370     }
371 
372     protected Map<String, String> getCompilerArguments()
373     {
374         return compilerArguments;
375     }
376 
377     protected File getGeneratedSourcesDirectory()
378     {
379         return generatedSourcesDirectory;
380     }
381 
382     private void writeBoxedWarning( String message )
383     {
384         String line = StringUtils.repeat( "*", message.length() + 4 );
385         getLog().warn( line );
386         getLog().warn( "* " + MessageUtils.buffer().strong( message )  + " *" );
387         getLog().warn( line );
388     }
389 }