package org.codehaus.mojo.aspectj; /** * The MIT License * * Copyright 2005-2006 The Codehaus. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.handler.ArtifactHandler; import org.apache.maven.plugin.MojoExecutionException; import org.aspectj.bridge.IMessage; import org.aspectj.tools.ajc.Main; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; /** * Base class for the two aspectJ compiletime weaving mojos. * * @author <a href="mailto:kaare.nilsen@gmail.com">Kaare Nilsen</a> */ public abstract class AbstractAjcCompiler extends AbstractAjcMojo { /** * The source directory for the aspects * * @parameter default-value="src/main/aspect" */ protected String aspectDirectory = "src/main/aspect"; /** * The source directory for the test aspects * * @parameter default-value="src/test/aspect" */ protected String testAspectDirectory = "src/test/aspect"; /** * List of ant-style patterns used to specify the aspects that should be included when compiling. When none * specified all .java and .aj files in the project source directories, or directories spesified by the ajdtDefFile * property are included. * * @parameter */ protected String[] includes; /** * List of ant-style patterns used to specify the aspects that should be excluded when compiling. When none * specified all .java and .aj files in the project source directories, or directories spesified by the ajdtDefFile * property are included. * * @parameter */ protected String[] excludes; /** * Where to find the ajdt build definition file. <i>If set this will override the use of project sourcedirs</i>. * * @parameter */ protected String ajdtBuildDefFile; /** * Generate aop.xml file for load-time weaving with default name.(/META-INF/aop.xml) * * @parameter */ protected boolean outxml; /** * Generate aop.xml file for load-time weaving with custom name. * * @parameter */ protected String outxmlfile; /** * Generate .ajesym symbol files for emacs support * * @parameter */ protected boolean emacssym; /** * Set default level for messages about potential programming mistakes in crosscutting code. {level} may be ignore, * warning, or error. This overrides entries in org/aspectj/weaver/XlintDefault.properties from aspectjtools.jar. * * @parameter */ protected String Xlint; /** * Enables the compiler to support hasmethod(method_pattern) and hasfield(field_pattern) type patterns, but only * within declare statements. It's experimental and undocumented because it may change, and because it doesn't yet * take into account ITDs. * * @parameter * @since 1.3 */ protected boolean XhasMember; /** * Specify classfile target setting (1.1 to 1.6) default is 1.2 * * @parameter default-value="${project.build.java.target}" */ protected String target; /** * Toggle assertions (1.3, 1.4, or 1.6 - default is 1.4). When using -source 1.3, an assert() statement valid under * Java 1.4 will result in a compiler error. When using -source 1.4, treat assert as a keyword and implement * assertions according to the 1.4 language spec. When using -source 1.5 or higher, Java 5 language features are * permitted. * * @parameter default-value="${mojo.java.target}" */ protected String source; /** * Specify compiler compliance setting (1.3 to 1.6) default is 1.4 * * @parameter */ protected String complianceLevel; /** * Toggle warningmessages on deprecations * * @parameter */ protected boolean deprecation; /** * Emit no errors for unresolved imports; * * @parameter */ protected boolean noImportError; /** * Keep compiling after error, dumping class files with problem methods * * @parameter */ protected boolean proceedOnError; /** * Preserve all local variables during code generation (to facilitate debugging). * * @parameter */ protected boolean preserveAllLocals; /** * Compute reference information. * * @parameter */ protected boolean referenceInfo; /** * Specify default source encoding format. * * @parameter expression="${project.build.sourceEncoding}" */ protected String encoding; /** * Emit messages about accessed/processed compilation units * * @parameter */ protected boolean verbose; /** * Emit messages about weaving * * @parameter */ protected boolean showWeaveInfo; /** * Repeat compilation process N times (typically to do performance analysis). * * @parameter */ protected int repeat; /** * (Experimental) runs weaver in reweavable mode which causes it to create woven classes that can be rewoven, * subject to the restriction that on attempting a reweave all the types that advised the woven type must be * accessible. * * @parameter */ protected boolean Xreweavable; /** * (Experimental) do not inline around advice * * @parameter */ protected boolean XnoInline; /** * (Experimental) Normally it is an error to declare aspects Serializable. This option removes that restriction. * * @parameter */ protected boolean XserializableAspects; /** * Causes the compiler to calculate and add the SerialVersionUID field to any type implementing Serializable that is * affected by an aspect. The field is calculated based on the class before weaving has taken place. * * @parameter */ protected boolean XaddSerialVersionUID; /** * Override location of VM's bootclasspath for purposes of evaluating types when compiling. Path is a single * argument containing a list of paths to zip files or directories, delimited by the platform-specific path * delimiter. * * @parameter */ protected String bootclasspath; /** * Emit warnings for any instances of the comma-delimited list of questionable code (eg 'unusedLocals,deprecation'): * see http://www.eclipse.org/aspectj/doc/released/devguide/ajc-ref.html#ajc for available settings * * @parameter */ protected String warn; /** * The filename to store build configuration in. This file will be placed in the project build output directory, and * will contain all the arguments passed to the compiler in the last run, and also all the filenames included in the * build. Aspects as well as java files. * * @parameter default-value="builddef.lst" */ protected String argumentFileName = "builddef.lst"; /** * Forces re-compilation, regardless of whether the compiler arguments or the sources have changed. * * @parameter */ protected boolean forceAjcCompile; /** * Holder for ajc compiler options */ protected List ajcOptions = new ArrayList(); /** * Holds all files found using the includes, excludes parameters. */ protected Set resolvedIncludes; /** * Abstract method used by child classes to spesify the correct output directory for compiled classes. * * @return where compiled classes should be put. */ protected abstract List getOutputDirectories(); /** * Abstract method used by child classes to spesify the correct source directory for classes. * * @return where sources may be found. */ protected abstract List getSourceDirectories(); /** * Abstract method used by cild classes to specify aditional aspect paths. * * @return the additional aspect paths */ protected abstract String getAdditionalAspectPaths(); /** * Do the AspectJ compiling. * * @throws MojoExecutionException */ public void execute() throws MojoExecutionException { ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler(); if ( !"java".equals( artifactHandler.getLanguage() ) ) { getLog().warn( "Not executing aspectJ compiler as the project is not a Java classpath-capable package" ); return; } Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() ); project.getCompileSourceRoots().add( basedir.getAbsolutePath() + "/" + aspectDirectory ); project.getTestCompileSourceRoots().add( basedir.getAbsolutePath() + "/" + testAspectDirectory ); assembleArguments(); if ( !forceAjcCompile && !hasSourcesToCompile() ) { getLog().warn( "No sources found skipping aspectJ compile" ); return; } if ( !forceAjcCompile && !isBuildNeeded() ) { getLog().info( "No modifications found skipping aspectJ compile" ); return; } if ( getLog().isDebugEnabled() ) { String command = "Running : ajc "; Iterator iter = ajcOptions.iterator(); while ( iter.hasNext() ) { command += ( iter.next() + " " ); } getLog().debug( command ); } try { getLog().debug( "Compiling and weaving " + resolvedIncludes.size() + " sources to " + getOutputDirectories().get( 0 ) ); File outDir = new File( (String) getOutputDirectories().get( 0 ) ); AjcHelper.writeBuildConfigToFile( ajcOptions, argumentFileName, outDir ); getLog().debug( "Argumentsfile written : " + new File( outDir.getAbsolutePath() + argumentFileName ).getAbsolutePath() ); } catch ( IOException e ) { throw new MojoExecutionException( "Could not write arguments file to the target area", e ); } Main main = new Main(); MavenMessageHandler mavenMessageHandler = new MavenMessageHandler( getLog() ); main.setHolder( mavenMessageHandler ); main.runMain( (String[]) ajcOptions.toArray( new String[0] ), false ); IMessage[] errors = mavenMessageHandler.getMessages( IMessage.ERROR, true ); if ( !proceedOnError && errors.length > 0 ) { throw new CompilationFailedException( errors ); } } /** * Assembles a complete ajc compiler arguments list. * * @throws MojoExecutionException error in configuration */ protected void assembleArguments() throws MojoExecutionException { if ( XhasMember ) { ajcOptions.add( "-XhasMember" ); } // Add classpath ajcOptions.add( "-classpath" ); ajcOptions.add( AjcHelper.createClassPath( project, null, getOutputDirectories() ) ); // Add boot classpath if ( null != bootclasspath ) { ajcOptions.add( "-bootclasspath" ); ajcOptions.add( bootclasspath ); } // Add warn option if ( null != warn ) { ajcOptions.add( "-warn:" + warn ); } // Add artifacts to weave addModulesArgument( "-inpath", ajcOptions, weaveDependencies, null, "a dependency to weave" ); // Add library artifacts addModulesArgument( "-aspectpath", ajcOptions, aspectLibraries, getAdditionalAspectPaths(), "an aspect library" ); // add target dir argument ajcOptions.add( "-d" ); ajcOptions.add( getOutputDirectories().get( 0 ) ); // Add all the files to be included in the build, if ( null != ajdtBuildDefFile ) { resolvedIncludes = AjcHelper.getBuildFilesForAjdtFile( ajdtBuildDefFile, basedir ); } else { resolvedIncludes = AjcHelper.getBuildFilesForSourceDirs( getSourceDirectories(), this.includes, this.excludes ); } ajcOptions.addAll( resolvedIncludes ); } /** * Finds all artifacts in the weavemodule property, and adds them to the ajc options. * * @param argument * @param arguments * @param modules * @param aditionalpath * @param role * @throws MojoExecutionException */ private void addModulesArgument( String argument, List arguments, Module[] modules, String aditionalpath, String role ) throws MojoExecutionException { StringBuffer buf = new StringBuffer(); if ( null != aditionalpath ) { arguments.add( argument ); buf.append( aditionalpath ); } if ( modules != null && modules.length > 0 ) { if ( !arguments.contains( argument ) ) { arguments.add( argument ); } for ( int i = 0; i < modules.length; ++i ) { Module module = modules[i]; // String key = ArtifactUtils.versionlessKey( module.getGroupId(), module.getArtifactId() ); // Artifact artifact = (Artifact) project.getArtifactMap().get( key ); Artifact artifact = null; Set allArtifacts = project.getArtifacts(); for ( Iterator iterator = allArtifacts.iterator(); iterator.hasNext(); ) { Artifact art = (Artifact) iterator.next(); if ( art.getGroupId().equals( module.getGroupId() ) && art.getArtifactId().equals( module.getArtifactId() ) && StringUtils.equals( module.getClassifier(), art.getClassifier() ) && StringUtils.equals( module.getType(), module.getType() ) ) { artifact = art; break; } } if ( artifact == null ) { throw new MojoExecutionException( "The artifact " + module.toString() + " referenced in aspectj plugin as " + role + ", is not found the project dependencies" ); } if ( buf.length() != 0 ) { buf.append( File.pathSeparatorChar ); } buf.append( artifact.getFile().getPath() ); } } if ( buf.length() > 0 ) { String pathString = buf.toString(); arguments.add( pathString ); getLog().debug( "Adding " + argument + ": " + pathString ); } } /** * Checks modifications that would make us need a build * * @return <code>true</code> if build is needed, otherwise <code>false</code> * @throws MojoExecutionException */ protected boolean isBuildNeeded() throws MojoExecutionException { File outDir = new File( getOutputDirectories().get( 0 ).toString() ); return hasNoPreviousBuild( outDir ) || hasArgumentsChanged( outDir ) || hasSourcesChanged( outDir ); } private boolean hasNoPreviousBuild( File outDir ) { return ( !FileUtils.fileExists( new File( outDir.getAbsolutePath(), argumentFileName ).getAbsolutePath() ) ); } private boolean hasArgumentsChanged( File outDir ) throws MojoExecutionException { try { return ( !ajcOptions.equals( AjcHelper.readBuildConfigFile( argumentFileName, outDir ) ) ); } catch ( IOException e ) { throw new MojoExecutionException( "Error during reading of previous argumentsfile " ); } } /** * Not entirely safe, assembleArguments() must be run */ private boolean hasSourcesToCompile() { return resolvedIncludes.size() > 0; } private boolean hasSourcesChanged( File outDir ) { Iterator sourceIter = resolvedIncludes.iterator(); long lastBuild = new File( outDir.getAbsolutePath(), argumentFileName ).lastModified(); while ( sourceIter.hasNext() ) { File sourceFile = new File( (String) sourceIter.next() ); long sourceModified = sourceFile.lastModified(); if ( sourceModified >= lastBuild ) { return true; } } return false; } /** * Setters which when called sets compiler arguments * @param complianceLevel the complianceLevel */ public void setComplianceLevel( String complianceLevel ) { if ( complianceLevel.equals( "1.3" ) || complianceLevel.equals( "1.4" ) || complianceLevel.equals( "1.5" ) || complianceLevel.equals( "1.6" ) ) { ajcOptions.add( "-" + complianceLevel ); } } public void setDeprecation( boolean deprecation ) { if ( deprecation ) { ajcOptions.add( "-deprecation" ); } } public void setEmacssym( boolean emacssym ) { if ( emacssym ) { ajcOptions.add( "-emacssym" ); } } public void setEncoding( String encoding ) { ajcOptions.add( "-encoding" ); ajcOptions.add( encoding ); } public void setNoImportError( boolean noImportError ) { if ( noImportError ) { ajcOptions.add( "-noImportError" ); } } public void setOutxml( boolean outxml ) { if ( outxml ) { ajcOptions.add( "-outxml" ); } } public void setOutxmlfile( String outxmlfile ) { ajcOptions.add( "-outxmlfile" ); ajcOptions.add( outxmlfile ); } public void setPreserveAllLocals( boolean preserveAllLocals ) { if ( preserveAllLocals ) { ajcOptions.add( "-preserveAllLocals" ); } } public void setProceedOnError( boolean proceedOnError ) { if ( proceedOnError ) { ajcOptions.add( "-proceedOnError" ); } this.proceedOnError = proceedOnError; } public void setReferenceInfo( boolean referenceInfo ) { if ( referenceInfo ) { ajcOptions.add( "-referenceInfo" ); } } public void setRepeat( int repeat ) { ajcOptions.add( "-repeat" ); ajcOptions.add( "" + repeat ); } public void setShowWeaveInfo( boolean showWeaveInfo ) { if ( showWeaveInfo ) { ajcOptions.add( "-showWeaveInfo" ); } } public void setTarget( String target ) { ajcOptions.add( "-target" ); ajcOptions.add( target ); } public void setSource( String source ) { ajcOptions.add( "-source" ); ajcOptions.add( source ); } public void setVerbose( boolean verbose ) { if ( verbose ) { ajcOptions.add( "-verbose" ); } } public void setXhasMember( boolean xhasMember ) { XhasMember = xhasMember; } public void setXlint( String xlint ) { ajcOptions.add( "-Xlint:" + xlint ); } public void setXnoInline( boolean xnoInline ) { if ( xnoInline ) { ajcOptions.add( "-XnoInline" ); } } public void setXreweavable( boolean xreweavable ) { if ( xreweavable ) { ajcOptions.add( "-Xreweavable" ); } } public void setXserializableAspects( boolean xserializableAspects ) { if ( xserializableAspects ) { ajcOptions.add( "-XserializableAspects" ); } } public void setXaddSerialVersionUID( boolean xaddSerialVersionUID ) { if ( xaddSerialVersionUID ) { ajcOptions.add( "-XaddSerialVersionUID" ); } } public void setBootClassPath( String bootclasspath ) { this.bootclasspath = bootclasspath; } public void setWarn( String warn ) { this.warn = warn; } public void setArgumentFileName( String argumentFileName ) { this.argumentFileName = argumentFileName; } }