View Javadoc
1   package org.codehaus.plexus.compiler.ajc;
2   
3   import org.aspectj.ajdt.ajc.BuildArgParser;
4   import org.aspectj.ajdt.internal.core.builder.AjBuildConfig;
5   import org.aspectj.ajdt.internal.core.builder.AjBuildManager;
6   import org.aspectj.bridge.AbortException;
7   import org.aspectj.bridge.IMessage;
8   import org.aspectj.bridge.ISourceLocation;
9   import org.aspectj.bridge.MessageHandler;
10  import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
11  import org.aspectj.tools.ajc.Main;
12  import org.codehaus.plexus.compiler.AbstractCompiler;
13  import org.codehaus.plexus.compiler.CompilerConfiguration;
14  import org.codehaus.plexus.compiler.CompilerException;
15  import org.codehaus.plexus.compiler.CompilerMessage;
16  import org.codehaus.plexus.compiler.CompilerOutputStyle;
17  import org.codehaus.plexus.compiler.CompilerResult;
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.net.MalformedURLException;
22  import java.net.URL;
23  import java.net.URLClassLoader;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Map;
29  
30  /**
31   * <p/>
32   * Options
33   * <p/>
34   * -injars JarList
35   * <p/>
36   * Accept as source bytecode any .class files inside the specified .jar files. The output will include these
37   * classes, possibly as woven with any applicable aspects. JarList, like classpath, is a single argument
38   * containing a list of paths to jar files, delimited by the platform- specific classpath delimiter.
39   * <p/>
40   * -aspectpath JarList
41   * <p/>
42   * Weave binary aspects from JarList zip files into all sources. The aspects should have been output by
43   * the same version of the compiler. To run the output classes requires putting all the aspectpath entries on
44   * the run classpath. JarList, like classpath, is a single argument containing a list of paths to jar files,
45   * delimited by the platform- specific classpath delimiter.
46   * <p/>
47   * -argfile File
48   * <p/>
49   * The file is a line-delimited list of arguments. These arguments are inserted into the argument list.
50   * <p/>
51   * -outjar output.jar
52   * <p/>
53   * Put output classes in zip file output.jar.
54   * <p/>
55   * -incremental
56   * <p/>
57   * Run the compiler continuously. After the initial compilation, the compiler will wait to recompile until it
58   * reads a newline from the standard input, and will quit when it reads a 'q'. It will only recompile necessary
59   * components, so a recompile should be much faster than doing a second compile. This requires -sourceroots.
60   * <p/>
61   * -sourceroots DirPaths
62   * <p/>
63   * Find and build all .java or .aj source files under any directory listed in DirPaths. DirPaths, like
64   * classpath, is a single argument containing a list of paths to directories, delimited by the platform-
65   * specific classpath delimiter. Required by -incremental.
66   * <p/>
67   * -emacssym
68   * <p/>
69   * Generate .ajesym symbol files for emacs support
70   * <p/>
71   * -Xlint
72   * <p/>
73   * Same as -Xlint:warning (enabled by default)
74   * <p/>
75   * -Xlint:{level}
76   * <p/>
77   * Set default level for messages about potential programming mistakes in crosscutting code. {level} may be
78   * ignore, warning, or error. This overrides entries in org/aspectj/weaver/XlintDefault.properties from
79   * aspectjtools.jar, but does not override levels set using the -Xlintfile option.
80   * <p/>
81   * -Xlintfile PropertyFile
82   * <p/>
83   * Specify properties file to set levels for specific crosscutting messages. PropertyFile is a path to a
84   * Java .properties file that takes the same property names and values as
85   * org/aspectj/weaver/XlintDefault.properties from aspectjtools.jar, which it also overrides.
86   * -help
87   * <p/>
88   * Emit information on compiler options and usage
89   * <p/>
90   * -version
91   * <p/>
92   * Emit the version of the AspectJ compiler
93   * <p/>
94   * -classpath Path
95   * <p/>
96   * Specify where to find user class files. Path is a single argument containing a list of paths to zip files
97   * or directories, delimited by the platform-specific path delimiter.
98   * <p/>
99   * -bootclasspath Path
100  * <p/>
101  * Override location of VM's bootclasspath for purposes of evaluating types when compiling. Path is a single
102  * argument containing a list of paths to zip files or directories, delimited by the platform-specific path
103  * delimiter.
104  * <p/>
105  * -extdirs Path
106  * <p/>
107  * Override location of VM's extension directories for purposes of evaluating types when compiling. Path is
108  * a single argument containing a list of paths to directories, delimited by the platform-specific path
109  * delimiter.
110  * <p/>
111  * -d Directory
112  * <p/>
113  * Specify where to place generated .class files. If not specified, Directory defaults to the current
114  * working dir.
115  * <p/>
116  * -target [1.1|1.2]
117  * <p/>
118  * Specify classfile target setting (1.1 or 1.2, default is 1.1)
119  * <p/>
120  * -1.3
121  * <p/>
122  * Set compliance level to 1.3 (default)
123  * -1.4
124  * <p/>
125  * Set compliance level to 1.4
126  * -source [1.3|1.4]
127  * <p/>
128  * Toggle assertions (1.3 or 1.4, default is 1.3 in -1.3 mode and 1.4 in -1.4 mode). When using -source 1.3,
129  * an assert() statement valid under Java 1.4 will result in a compiler error. When using -source 1.4, treat
130  * assert as a keyword and implement assertions according to the 1.4 language spec.
131  * <p/>
132  * -nowarn
133  * <p/>
134  * Emit no warnings (equivalent to '-warn:none') This does not suppress messages generated by declare warning
135  * or Xlint.
136  * <p/>
137  * -warn: items
138  * <p/>
139  * Emit warnings for any instances of the comma-delimited list of questionable code
140  * (eg '-warn:unusedLocals,deprecation'):
141  * <p/>
142  * constructorName        method with constructor name
143  * packageDefaultMethod   attempt to override package-default method
144  * deprecation            usage of deprecated type or member
145  * maskedCatchBlocks      hidden catch block
146  * unusedLocals           local variable never read
147  * unusedArguments        method argument never read
148  * unusedImports          import statement not used by code in file
149  * none                   suppress all compiler warnings
150  * <p/>
151  * <p/>
152  * -warn:none does not suppress messages generated by declare warning or Xlint.
153  * <p/>
154  * -deprecation
155  * <p/>
156  * Same as -warn:deprecation
157  * <p/>
158  * -noImportError
159  * <p/>
160  * Emit no errors for unresolved imports
161  * <p/>
162  * -proceedOnError
163  * <p/>
164  * Keep compiling after error, dumping class files with problem methods
165  * <p/>
166  * -g:[lines,vars,source]
167  * <p/>
168  * debug attributes level, that may take three forms:
169  * <p/>
170  * -g         all debug info ('-g:lines,vars,source')
171  * -g:none    no debug info
172  * -g:{items} debug info for any/all of [lines, vars, source], e.g.,
173  * -g:lines,source
174  * <p/>
175  * <p/>
176  * -preserveAllLocals
177  * <p/>
178  * Preserve all local variables during code generation (to facilitate debugging).
179  * <p/>
180  * -referenceInfo
181  * <p/>
182  * Compute reference information.
183  * <p/>
184  * -encoding format
185  * <p/>
186  * Specify default source encoding format. Specify custom encoding on a per file basis by suffixing each
187  * input source file/folder name with '[encoding]'.
188  * <p/>
189  * -verbose
190  * <p/>
191  * Emit messages about accessed/processed compilation units
192  * <p/>
193  * -log file
194  * <p/>
195  * Specify a log file for compiler messages.
196  * -progress
197  * <p/>
198  * Show progress (requires -log mode).
199  * -time
200  * <p/>
201  * Display speed information.
202  * -noExit
203  * <p/>
204  * Do not call System.exit(n) at end of compilation (n=0 if no error)
205  * -repeat N
206  * <p/>
207  * Repeat compilation process N times (typically to do performance analysis).
208  * -Xnoweave
209  * <p/>
210  * (Experimental) produce unwoven class files for input using -injars.
211  * -Xnoinline
212  * <p/>
213  * (Experimental) do not inline around advice
214  * -XincrementalFile file
215  * <p/>
216  * (Experimental) This works like incremental mode, but using a file rather than standard input to control
217  * the compiler. It will recompile each time file is changed and and halt when file is deleted.
218  * <p/>
219  * -XserializableAspects
220  * <p/>
221  * (Experimental) Normally it is an error to declare aspects Serializable. This option removes that restriction.
222  *
223  * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
224  * @plexus.component role="org.codehaus.plexus.compiler.Compiler" role-hint="aspectj"
225  */
226 public class AspectJCompiler
227     extends AbstractCompiler
228 {
229 
230     // ----------------------------------------------------------------------
231     //
232     // ----------------------------------------------------------------------
233 
234     public AspectJCompiler()
235     {
236         super( CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE, ".java", ".class", null );
237     }
238 
239     public CompilerResult performCompile( CompilerConfiguration config )
240         throws CompilerException
241     {
242         File destinationDir = new File( config.getOutputLocation() );
243 
244         if ( !destinationDir.exists() )
245         {
246             destinationDir.mkdirs();
247         }
248 
249         String[] sourceFiles = getSourceFiles( config );
250 
251         if ( sourceFiles.length == 0 )
252         {
253             return new CompilerResult();
254         }
255 
256         System.out.println(
257             "Compiling " + sourceFiles.length + " " + "source file" + ( sourceFiles.length == 1 ? "" : "s" ) + " to "
258                 + destinationDir.getAbsolutePath() );
259 
260         //        String[] args = buildCompilerArguments( config, sourceFiles );
261         AjBuildConfig buildConfig = buildCompilerConfig( config );
262         return new CompilerResult().compilerMessages( compileInProcess( buildConfig ) );
263     }
264 
265     private static class AspectJMessagePrinter extends Main.MessagePrinter
266     {
267         public AspectJMessagePrinter( boolean verbose )
268         {
269             super( verbose );
270         }
271     }
272 
273     private AjBuildConfig buildCompilerConfig( CompilerConfiguration config )
274         throws CompilerException
275     {
276         AjBuildConfig buildConfig = new AjBuildConfig(new BuildArgParser(new AspectJMessagePrinter(config.isVerbose())));
277         buildConfig.setIncrementalMode( false );
278 
279         String[] files = getSourceFiles( config );
280         if ( files != null )
281         {
282             buildConfig.setFiles( buildFileList( Arrays.asList( files ) ) );
283         }
284 
285         setSourceVersion( buildConfig, config.getSourceVersion() );
286 
287         if ( config.isDebug() )
288         {
289             buildConfig.getOptions().produceDebugAttributes =
290                 ClassFileConstants.ATTR_SOURCE + ClassFileConstants.ATTR_LINES + ClassFileConstants.ATTR_VARS;
291         }
292 
293         Map<String, String> javaOpts = config.getCustomCompilerArguments();
294         if ( javaOpts != null && !javaOpts.isEmpty() )
295         {
296             // TODO support customCompilerArguments
297             // buildConfig.setJavaOptions( javaOpts );
298         }
299 
300         List<String> cp = new LinkedList<String>( config.getClasspathEntries() );
301 
302         File javaHomeDir = new File( System.getProperty( "java.home" ) );
303         File[] jars = new File( javaHomeDir, "lib" ).listFiles();
304         if ( jars != null )
305         {
306             for ( File jar : jars )
307             {
308                 if ( jar.getName().endsWith( ".jar" ) || jar.getName().endsWith( ".zip" ) )
309                 {
310                     cp.add( 0, jar.getAbsolutePath() );
311                 }
312             }
313         }
314         jars = new File( javaHomeDir, "../Classes" ).listFiles();
315         if ( jars != null )
316         {
317             for ( File jar : jars )
318             {
319                 if ( jar.getName().endsWith( ".jar" ) || jar.getName().endsWith( ".zip" ) )
320                 {
321                     cp.add( 0, jar.getAbsolutePath() );
322                 }
323             }
324         }
325 
326         checkForAspectJRT( cp );
327         if ( cp != null && !cp.isEmpty() )
328         {
329             List<String> elements = new ArrayList<String>( cp.size() );
330             for ( String path : cp )
331             {
332                 elements.add( ( new File( path ) ).getAbsolutePath() );
333             }
334 
335             buildConfig.setClasspath( elements );
336         }
337 
338         String outputLocation = config.getOutputLocation();
339         if ( outputLocation != null )
340         {
341             File outDir = new File( outputLocation );
342             if ( !outDir.exists() )
343             {
344                 outDir.mkdirs();
345             }
346 
347             buildConfig.setOutputDir( outDir );
348         }
349 
350         if ( config instanceof AspectJCompilerConfiguration )
351         {
352             AspectJCompilerConfiguration ajCfg = (AspectJCompilerConfiguration) config;
353 
354             Map<String, File> sourcePathResources = ajCfg.getSourcePathResources();
355             if ( sourcePathResources != null && !sourcePathResources.isEmpty() )
356             {
357                 buildConfig.setSourcePathResources( sourcePathResources );
358             }
359 
360             Map<String, String> ajOptions = ajCfg.getAJOptions();
361             if ( ajOptions != null && !ajOptions.isEmpty() )
362             {
363                 // TODO not supported
364                 //buildConfig.setAjOptions( ajCfg.getAJOptions() );
365             }
366 
367             List<File> aspectPath = buildFileList( ajCfg.getAspectPath() );
368             if ( aspectPath != null && !aspectPath.isEmpty() )
369             {
370                 buildConfig.setAspectpath( buildFileList( ajCfg.getAspectPath() ) );
371             }
372 
373             List<File> inJars = buildFileList( ajCfg.getInJars() );
374             if ( inJars != null && !inJars.isEmpty() )
375             {
376                 buildConfig.setInJars( buildFileList( ajCfg.getInJars() ) );
377             }
378 
379             List<File> inPaths = buildFileList( ajCfg.getInPath() );
380             if ( inPaths != null && !inPaths.isEmpty() )
381             {
382                 buildConfig.setInPath( buildFileList( ajCfg.getInPath() ) );
383             }
384 
385             String outJar = ajCfg.getOutputJar();
386             if ( outJar != null )
387             {
388                 buildConfig.setOutputJar( new File( ajCfg.getOutputJar() ) );
389             }
390         }
391 
392         return buildConfig;
393     }
394 
395     private List<CompilerMessage> compileInProcess( AjBuildConfig buildConfig )
396         throws CompilerException
397     {
398 
399         MessageHandler messageHandler = new MessageHandler();
400 
401         AjBuildManager manager = new AjBuildManager( messageHandler );
402 
403         try
404         {
405             manager.batchBuild( buildConfig, messageHandler );
406         }
407         catch ( AbortException e )
408         {
409             throw new CompilerException( "Unknown error while compiling", e );
410         }
411         catch ( IOException e )
412         {
413             throw new CompilerException( "Unknown error while compiling", e );
414         }
415 
416         // We need the location of the maven so we have a couple of options
417         // here.
418         //
419         // The aspectjrt jar is something this component needs to function so we
420         // can either
421         // bake it into the plugin and retrieve it somehow or use a system
422         // property or we
423         // could pass in a set of parameters in a Map.
424 
425         boolean errors = messageHandler.hasAnyMessage( IMessage.ERROR, true );
426 
427         List<CompilerMessage> messages = new ArrayList<CompilerMessage>();
428         if ( errors )
429         {
430             IMessage[] errorMessages = messageHandler.getMessages( IMessage.ERROR, true );
431 
432             for ( IMessage m : errorMessages )
433             {
434                 ISourceLocation sourceLocation = m.getSourceLocation();
435                 CompilerMessage error;
436 
437                 if ( sourceLocation == null )
438                 {
439                     error = new CompilerMessage( m.getMessage(), true );
440                 }
441                 else
442                 {
443                     error =
444                         new CompilerMessage( sourceLocation.getSourceFile().getPath(), true, sourceLocation.getLine(),
445                                              sourceLocation.getColumn(), sourceLocation.getEndLine(),
446                                              sourceLocation.getColumn(), m.getMessage() );
447                 }
448                 messages.add( error );
449             }
450         }
451 
452         return messages;
453     }
454 
455     private void checkForAspectJRT( List<String> cp )
456     {
457         if ( cp == null || cp.isEmpty() )
458         {
459             throw new IllegalStateException( "AspectJ Runtime not found in supplied classpath" );
460         }
461         else
462         {
463             try
464             {
465                 URL[] urls = new URL[cp.size()];
466                 for ( int i = 0; i < urls.length; i++ )
467                 {
468                     urls[i] = ( new File( cp.get( i ) ) ).toURL();
469                 }
470 
471                 URLClassLoader cloader = new URLClassLoader( urls );
472 
473                 cloader.loadClass( "org.aspectj.lang.JoinPoint" );
474             }
475             catch ( MalformedURLException e )
476             {
477                 throw new IllegalArgumentException( "Invalid classpath entry" );
478             }
479             catch ( ClassNotFoundException e )
480             {
481                 throw new IllegalStateException( "AspectJ Runtime not found in supplied classpath" );
482             }
483         }
484     }
485 
486     private List<File> buildFileList( List<String> locations )
487     {
488         List<File> fileList = new LinkedList<File>();
489         for ( String location : locations )
490         {
491             fileList.add( new File( location ) );
492         }
493 
494         return fileList;
495     }
496 
497     /**
498      * Set the source version in ajc compiler
499      *
500      * @param buildConfig
501      * @param sourceVersion
502      */
503     private void setSourceVersion( AjBuildConfig buildConfig, String sourceVersion )
504         throws CompilerException
505     {
506         if ( "1.9".equals( sourceVersion ) )
507         {
508             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_9;
509         }
510         else if ( "1.8".equals( sourceVersion ) )
511         {
512             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_8;
513         }
514         else if ( "1.7".equals( sourceVersion ) )
515         {
516             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_7;
517         }
518         else if ( "1.6".equals( sourceVersion ) )
519         {
520             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_6;
521         }
522         else if ( "1.5".equals( sourceVersion ) )
523         {
524             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_5;
525         }
526         else if ( "5.0".equals( sourceVersion ) )
527         {
528             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_5;
529         }
530         else if ( "1.4".equals( sourceVersion ) )
531         {
532             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_4;
533         }
534         else if ( "1.3".equals( sourceVersion ) )
535         {
536             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_3;
537         }
538         else if ( "1.2".equals( sourceVersion ) )
539         {
540             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_2;
541         }
542         else if ( "1.1".equals( sourceVersion ) )
543         {
544             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_1;
545         }
546         else if ( sourceVersion == null || sourceVersion.length() <= 0 )
547         {
548             buildConfig.getOptions().sourceLevel = ClassFileConstants.JDK1_3;
549         }
550         else
551         {
552             throw new CompilerException( "The source version was not recognized: " + sourceVersion );
553         }
554     }
555 
556     /**
557      * @return null
558      */
559     public String[] createCommandLine( CompilerConfiguration config )
560         throws CompilerException
561     {
562         return null;
563     }
564 
565 }