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