View Javadoc

1   package org.codehaus.mojo.aspectj;
2   
3   /**
4    * The MIT License
5    *
6    * Copyright 2005-2006 The Codehaus.
7    *
8    * Permission is hereby granted, free of charge, to any person obtaining a copy of
9    * this software and associated documentation files (the "Software"), to deal in
10   * the Software without restriction, including without limitation the rights to
11   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12   * of the Software, and to permit persons to whom the Software is furnished to do
13   * so, subject to the following conditions:
14   *
15   * The above copyright notice and this permission notice shall be included in all
16   * copies or substantial portions of the Software.
17   *
18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   * SOFTWARE.
25   */
26  
27  import java.io.File;
28  import java.io.IOException;
29  import java.util.ArrayList;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Set;
33  
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.artifact.ArtifactUtils;
36  import org.apache.maven.artifact.handler.ArtifactHandler;
37  import org.apache.maven.plugin.MojoExecutionException;
38  import org.aspectj.bridge.IMessage;
39  import org.aspectj.tools.ajc.Main;
40  import org.codehaus.plexus.util.FileUtils;
41  
42  /**
43   * Base class for the two aspectJ compiletime weaving mojos.
44   * 
45   * @author <a href="mailto:kaare.nilsen@gmail.com">Kaare Nilsen</a>
46   */
47  public abstract class AbstractAjcCompiler
48      extends AbstractAjcMojo
49  {
50  
51      /**
52       * The source directory for the aspects
53       * @parameter default-value="src/main/aspect"
54       */
55      protected String aspectDirectory = "src/main/aspect";
56  
57      /**
58       * The source directory for the test aspects
59       * @parameter default-value="src/test/aspect"
60       */
61      protected String testAspectDirectory = "src/test/aspect";
62  
63      /**
64       * List of ant-style patterns used to specify the aspects that should be included when 
65       * compiling. When none specified all .java and .aj files in the project source directories, or
66       * directories spesified by the ajdtDefFile property are included.
67       * 
68       * @parameter
69       */
70      protected String[] includes;
71  
72      /**
73       * List of ant-style patterns used to specify the aspects that should be excluded when 
74       * compiling. When none specified all .java and .aj files in the project source directories, or
75       * directories spesified by the ajdtDefFile property are included.
76       * 
77       * @parameter
78       */
79      protected String[] excludes;
80  
81      /**
82       * Where to find the ajdt build definition file.
83       * <i>If set this will override the use of project sourcedirs</i>.
84       * 
85       * @parameter
86       */
87      protected String ajdtBuildDefFile;
88  
89      /**
90       * Generate aop.xml file for load-time weaving with default name.(/META-INF/aop.xml)
91       * 
92       * @parameter
93       */
94      protected boolean outxml;
95  
96      /**
97       * Generate aop.xml file for load-time weaving with custom name.
98       * 
99       * @parameter
100      */
101     protected String outxmlfile;
102 
103     /**
104      * Generate .ajesym symbol files for emacs support
105      * 
106      *  @parameter
107      */
108     protected boolean emacssym;
109 
110     /**
111      * Set default level for messages about potential 
112      * programming mistakes in crosscutting code. 
113      * {level} may be ignore, warning, or error. 
114      * This overrides entries in org/aspectj/weaver/XlintDefault.properties 
115      * from aspectjtools.jar.
116      * 
117      *  @parameter
118      */
119     protected String Xlint;
120     
121     /**
122      * Enables the compiler to support hasmethod(method_pattern) 
123      * and hasfield(field_pattern) type patterns,
124      * but only within declare statements. 
125      * 
126      * It's experimental and undocumented because it may change, 
127      * and because it doesn't yet take into account ITDs. 
128      *  
129      * @parameter
130      * @since 1.3
131      */
132     protected boolean XhasMember;
133 
134     /**
135      * Specify classfile target setting (1.1 to 1.6) default is 1.2
136      * 
137      *  @parameter default-value="${project.build.java.target}"
138      */
139     protected String target;
140 
141     /**
142      * Toggle assertions (1.3, 1.4, or 1.6 - default is 1.4). 
143      * When using -source 1.3, an assert() statement valid under Java 1.4 
144      * will result in a compiler error. When using -source 1.4, 
145      * treat assert as a keyword and implement assertions 
146      * according to the 1.4 language spec. 
147      * When using -source 1.5 or higher, Java 5 language features are permitted.
148      * 
149      *  @parameter default-value="${project.build.java.target}"
150      */
151     protected String source;
152 
153     /**
154      * Specify compiler compliance setting (1.3 to 1.6)
155      * default is 1.4
156      * 
157      *  @parameter
158      */
159     protected String complianceLevel;
160 
161     /**
162      * Toggle warningmessages on deprecations
163      * 
164      * @parameter
165      */
166     protected boolean deprecation;
167 
168     /**
169      * Emit no errors for unresolved imports;
170      * 
171      * @parameter
172      */
173     protected boolean noImportError;
174 
175     /**
176      * Keep compiling after error, dumping class files with problem methods
177      * 
178      * @parameter
179      */
180     protected boolean proceedOnError;
181 
182     /**
183      * Preserve all local variables during code generation (to facilitate debugging).
184      * 
185      * @parameter
186      */
187     protected boolean preserveAllLocals;
188 
189     /**
190      * Compute reference information.
191      * 
192      * @parameter
193      */
194     protected boolean referenceInfo;
195 
196     /**
197      * Specify default source encoding format. 
198      *      
199      * @parameter expression="${project.build.sourceEncoding}"
200      */
201     protected String encoding;
202 
203     /**
204      * Emit messages about accessed/processed compilation units
205      * 
206      * @parameter
207      */
208     protected boolean verbose;
209 
210     /**
211      * Emit messages about weaving
212      * 
213      * @parameter
214      */
215     protected boolean showWeaveInfo;
216 
217     /**
218      * Repeat compilation process N times (typically to do performance analysis).
219      * 
220      * @parameter
221      */
222     protected int repeat;
223 
224     /**
225      * (Experimental) runs weaver in reweavable mode which causes it to create 
226      * woven classes that can be rewoven, subject to the restriction 
227      * that on attempting a reweave all the types that advised the woven 
228      * type must be accessible.
229      * 
230      * @parameter
231      */
232     protected boolean Xreweavable;
233 
234     /**
235      * (Experimental) do not inline around advice
236      * 
237      * @parameter
238      */
239     protected boolean XnoInline;
240 
241     /**
242      * (Experimental) Normally it is an error to declare aspects Serializable. This option removes that restriction.
243      * 
244      * @parameter
245      */
246     protected boolean XserializableAspects;
247 
248     /**
249      * Causes the compiler to calculate and add the SerialVersionUID field to any type implementing Serializable that is affected by an aspect. 
250      * The field is calculated based on the class before weaving has taken place.
251      * 
252      * @parameter
253      */
254     protected boolean XaddSerialVersionUID;
255 
256     /**
257      * Override location of VM's bootclasspath for purposes of evaluating types when compiling. 
258      * Path is a single argument containing a list of paths to zip files or directories, delimited by the platform-specific path delimiter.
259      *
260      * @parameter
261      */
262     protected String bootclasspath;
263     
264     /**
265      * Emit warnings for any instances of the comma-delimited list of questionable code (eg 'unusedLocals,deprecation'):
266      * see http://www.eclipse.org/aspectj/doc/released/devguide/ajc-ref.html#ajc for available settings
267      * @parameter
268      */
269     protected String warn;
270 
271     /**
272      * The filename to store build configuration in.
273      * This file will be placed in the project build output
274      * directory, and will contain all the arguments
275      * passed to the compiler in the last run, and also
276      * all the filenames included in the build. Aspects as
277      * well as java files.
278      * 
279      * @parameter default-value="builddef.lst"
280      */
281     protected String argumentFileName = "builddef.lst";
282     
283     /**
284      * Forces re-compilation, regardless of whether the compiler arguments or
285      * the sources have changed.
286      * 
287      * @parameter
288      */
289     protected boolean forceAjcCompile;
290 
291     /**
292      * Holder for ajc compiler options
293      */
294     protected List ajcOptions = new ArrayList();
295 
296     /**
297      * Holds all files found using the includes, excludes parameters.
298      */
299     protected Set resolvedIncludes;
300 
301     /**
302      * Abstract method used by child classes to spesify the correct output
303      * directory for compiled classes.
304      * 
305      * @return where compiled classes should be put.
306      */
307     protected abstract List getOutputDirectories();
308 
309     /**
310      * Abstract method used by child classes to spesify the correct source directory for classes.
311      * 
312      * @return where sources may be found.
313      */
314     protected abstract List getSourceDirectories();
315 
316     /**
317      * Abstract method used by cild classes to specify aditional aspect paths.
318      * @return
319      */
320     protected abstract String getAdditionalAspectPaths();
321 
322     /**
323      * Do the AspectJ compiling.
324      * 
325      * @throws MojoExecutionException
326      */
327     public void execute()
328         throws MojoExecutionException
329     {
330         ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler();
331         if ( !"java".equals( artifactHandler.getLanguage() ) )
332         {
333             getLog().warn( "Not executing aspectJ compiler as the project is not a Java classpath-capable package" );
334             return;
335         }
336 
337         Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() );
338         project.getCompileSourceRoots().add( basedir.getAbsolutePath() + "/" + aspectDirectory );
339         project.getTestCompileSourceRoots().add( basedir.getAbsolutePath() + "/" + testAspectDirectory );
340         assembleArguments();
341 
342         if ( !hasSourcesToCompile() )
343         {
344             getLog().warn( "No sources found skipping aspectJ compile" );
345             return;
346         }
347 
348         if ( !forceAjcCompile && !isBuildNeeded() )
349         {
350             getLog().info( "No modifications found skipping aspectJ compile" );
351             return;
352         }
353 
354         if ( getLog().isDebugEnabled() )
355         {
356             String command = "Running : ajc ";
357             Iterator iter = ajcOptions.iterator();
358             while ( iter.hasNext() )
359             {
360                 command += ( iter.next() + " " );
361             }
362             getLog().debug( command );
363         }
364         try
365         {
366             getLog().debug( "Compiling and weaving " + resolvedIncludes.size() + " sources to " + getOutputDirectories().get( 0 ) );
367             File outDir = new File( (String) getOutputDirectories().get( 0 ) );
368             AjcHelper.writeBuildConfigToFile( ajcOptions, argumentFileName, outDir );
369             getLog().debug(
370                             "Argumentsfile written : "
371                                 + new File( outDir.getAbsolutePath() + argumentFileName ).getAbsolutePath() );
372         }
373         catch ( IOException e )
374         {
375             throw new MojoExecutionException( "Could not write arguments file to the target area", e );
376         }
377         Main main = new Main();
378         MavenMessageHandler mavenMessageHandler = new MavenMessageHandler( getLog() );
379         main.setHolder( mavenMessageHandler );
380 
381         main.runMain( (String[]) ajcOptions.toArray( new String[0] ), false );
382         IMessage[] errors = mavenMessageHandler.getMessages( IMessage.ERROR, true );
383         if ( !proceedOnError && errors.length > 0 )
384         {
385             throw new CompilationFailedException( errors );
386         }
387     }
388 
389     /**
390      * Assembles a complete ajc compiler arguments list.
391      *
392      * @throws MojoExecutionException error in configuration
393      */
394     protected void assembleArguments()
395         throws MojoExecutionException
396     {
397         if ( XhasMember ) {
398           ajcOptions.add( "-XhasMember" );
399         }
400         
401         // Add classpath
402         ajcOptions.add( "-classpath" );
403         ajcOptions.add( AjcHelper.createClassPath( project, null, getOutputDirectories() ) );
404 
405         // Add boot classpath
406         if ( null != bootclasspath )
407         {
408             ajcOptions.add( "-bootclasspath" );
409             ajcOptions.add( bootclasspath );
410         }
411         
412         // Add warn option
413         if (null != warn )
414         {
415             ajcOptions.add( "-warn:" + warn );
416         }
417 
418         // Add artifacts to weave
419         addModulesArgument( "-inpath", ajcOptions, weaveDependencies, null, "a dependency to weave" );
420 
421         // Add library artifacts 
422         addModulesArgument( "-aspectpath", ajcOptions, aspectLibraries, getAdditionalAspectPaths(), "an aspect library" );
423 
424         //add target dir argument
425         ajcOptions.add( "-d" );
426         ajcOptions.add( getOutputDirectories().get( 0 ) );
427 
428         // Add all the files to be included in the build,
429         if ( null != ajdtBuildDefFile )
430         {
431             resolvedIncludes = AjcHelper.getBuildFilesForAjdtFile( ajdtBuildDefFile, basedir );
432         }
433         else
434         {
435             resolvedIncludes = AjcHelper.getBuildFilesForSourceDirs( getSourceDirectories(), this.includes,
436                                                                      this.excludes );
437         }
438         ajcOptions.addAll( resolvedIncludes );
439     }
440 
441     /**
442      * Finds all artifacts in the weavemodule property,
443      * and adds them to the ajc options.
444      *  
445      * @param arguments
446      * @throws MojoExecutionException
447      */
448     private void addModulesArgument( String argument, List arguments, Module[] modules, String aditionalpath,
449                                      String role )
450         throws MojoExecutionException
451     {
452         StringBuffer buf = new StringBuffer();
453 
454         if ( null != aditionalpath )
455         {
456             arguments.add( argument );
457             buf.append( aditionalpath );
458         }
459         if ( modules != null && modules.length > 0 )
460         {
461             if ( !arguments.contains( argument ) )
462             {
463                 arguments.add( argument );
464             }
465 
466             for ( int i = 0; i < modules.length; ++i )
467             {
468                 Module module = modules[i];
469                 String key = ArtifactUtils.versionlessKey( module.getGroupId(), module.getArtifactId() );
470                 Artifact artifact = (Artifact) project.getArtifactMap().get( key );
471                 if ( artifact == null )
472                 {
473                     throw new MojoExecutionException( "The artifact " + key + " referenced in aspectj plugin as "
474                         + role + ", is not found the project dependencies" );
475                 }
476                 if ( buf.length() != 0 )
477                 {
478                     buf.append( File.pathSeparatorChar );
479                 }
480                 buf.append( artifact.getFile().getPath() );
481             }
482         }
483         if ( buf.length() > 0 )
484         {
485             String pathString = buf.toString();
486             arguments.add( pathString );
487             getLog().debug( "Adding " + argument + ": " + pathString );
488         }
489     }
490 
491     /**
492      * Checks modifications that would make us need a build
493      * 
494      * @throws MojoExecutionException 
495      *
496      */
497     protected boolean isBuildNeeded()
498         throws MojoExecutionException
499     {
500         File outDir = new File( getOutputDirectories().get( 0 ).toString() );
501         return hasNoPreviousBuild( outDir ) || hasArgumentsChanged( outDir ) || hasSourcesChanged( outDir );
502 
503     }
504 
505     private boolean hasNoPreviousBuild( File outDir )
506     {
507         return ( !FileUtils.fileExists( new File( outDir.getAbsolutePath(), argumentFileName ).getAbsolutePath() ) );
508     }
509 
510     private boolean hasArgumentsChanged( File outDir )
511         throws MojoExecutionException
512     {
513         try
514         {
515             return ( !ajcOptions.equals( AjcHelper.readBuildConfigFile( argumentFileName, outDir ) ) );
516         }
517         catch ( IOException e )
518         {
519             throw new MojoExecutionException( "Error during reading of previous argumentsfile " );
520         }
521     }
522 
523     /**
524      * Not entirely safe, assembleArguments() must be run 
525      */
526     private boolean hasSourcesToCompile()
527     {
528         return resolvedIncludes.size() > 0;
529     }
530 
531     private boolean hasSourcesChanged( File outDir )
532     {
533         Iterator sourceIter = resolvedIncludes.iterator();
534         long lastBuild = new File( outDir.getAbsolutePath(), argumentFileName ).lastModified();
535         while ( sourceIter.hasNext() )
536         {
537             File sourceFile = new File( (String) sourceIter.next() );
538             long sourceModified = sourceFile.lastModified();
539             if ( sourceModified >= lastBuild )
540             {
541                 return true;
542             }
543 
544         }
545         return false;
546     }
547 
548     /** 
549      * Setters which when called sets compiler arguments
550      */
551     public void setComplianceLevel( String complianceLevel )
552     {
553         if ( complianceLevel.equals( "1.3" ) || complianceLevel.equals( "1.4" ) || complianceLevel.equals( "1.5" ) || complianceLevel.equals( "1.6" ) )
554         {
555             ajcOptions.add( "-" + complianceLevel );
556         }
557 
558     }
559 
560     public void setDeprecation( boolean deprecation )
561     {
562         if ( deprecation )
563         {
564             ajcOptions.add( "-deprecation" );
565         }
566     }
567 
568     public void setEmacssym( boolean emacssym )
569     {
570         if ( emacssym )
571         {
572             ajcOptions.add( "-emacssym" );
573         }
574 
575     }
576 
577     public void setEncoding( String encoding )
578     {
579         ajcOptions.add( "-encoding" );
580         ajcOptions.add( encoding );
581     }
582 
583     public void setNoImportError( boolean noImportError )
584     {
585         if ( noImportError )
586         {
587             ajcOptions.add( "-noImportError" );
588         }
589 
590     }
591 
592     public void setOutxml( boolean outxml )
593     {
594         if ( outxml )
595         {
596             ajcOptions.add( "-outxml" );
597         }
598 
599     }
600 
601     public void setOutxmlfile( String outxmlfile )
602     {
603         ajcOptions.add( "-outxmlfile" );
604         ajcOptions.add( outxmlfile );
605     }
606 
607     public void setPreserveAllLocals( boolean preserveAllLocals )
608     {
609         if ( preserveAllLocals )
610         {
611             ajcOptions.add( "-preserveAllLocals" );
612         }
613 
614     }
615 
616     public void setProceedOnError( boolean proceedOnError )
617     {
618         if ( proceedOnError )
619         {
620             ajcOptions.add( "-proceedOnError" );
621         }
622         this.proceedOnError = proceedOnError;
623     }
624 
625     public void setReferenceInfo( boolean referenceInfo )
626     {
627         if ( referenceInfo )
628         {
629             ajcOptions.add( "-referenceInfo" );
630         }
631 
632     }
633 
634     public void setRepeat( int repeat )
635     {
636         ajcOptions.add( "-repeat" );
637         ajcOptions.add( "" + repeat );
638     }
639 
640     public void setShowWeaveInfo( boolean showWeaveInfo )
641     {
642         if ( showWeaveInfo )
643         {
644             ajcOptions.add( "-showWeaveInfo" );
645         }
646 
647     }
648 
649     public void setTarget( String target )
650     {
651         ajcOptions.add( "-target" );
652         ajcOptions.add( target );
653     }
654 
655     public void setSource( String source )
656     {
657         ajcOptions.add( "-source" );
658         ajcOptions.add( source );
659     }
660 
661     public void setVerbose( boolean verbose )
662     {
663         if ( verbose )
664         {
665             ajcOptions.add( "-verbose" );
666         }
667     }
668     
669     public void setXhasMember( boolean xhasMember )
670     {
671         XhasMember = xhasMember;
672     }
673 
674     public void setXlint( String xlint )
675     {
676         ajcOptions.add( "-Xlint:" + xlint );
677     }
678 
679     public void setXnoInline( boolean xnoInline )
680     {
681         if ( xnoInline )
682         {
683             ajcOptions.add( "-XnoInline" );
684         }
685 
686     }
687 
688     public void setXreweavable( boolean xreweavable )
689     {
690         if ( xreweavable )
691         {
692             ajcOptions.add( "-Xreweavable" );
693         }
694 
695     }
696 
697     public void setXserializableAspects( boolean xserializableAspects )
698     {
699         if ( xserializableAspects )
700         {
701             ajcOptions.add( "-XserializableAspects" );
702         }
703     }
704 
705     public void setXaddSerialVersionUID( boolean xaddSerialVersionUID )
706     {
707         if ( xaddSerialVersionUID )
708         {
709             ajcOptions.add( "-XaddSerialVersionUID" );
710         }
711     }
712 
713     public void setBootClassPath( String bootclasspath )
714     {
715         this.bootclasspath = bootclasspath;
716     }
717     
718     public void setWarn( String warn )
719     {
720         this.warn = warn;
721     }
722 
723     public void setArgumentFileName( String argumentFileName )
724     {
725         this.argumentFileName = argumentFileName;
726 
727     }
728 
729 }