All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.mojo.apt.AbstractAptMojo Maven / Gradle / Ivy

There is a newer version: 1.0-alpha-5
Show newest version
package org.codehaus.mojo.apt;

/*
 * The MIT License
 *
 * Copyright 2006-2008 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.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.util.StringUtils;

/**
 * Base mojo for executing apt.
 * 
 * @author Juraj Burian
 * @author Mark Hobson
 * @version $Id: AbstractAptMojo.java 12251 2010-06-08 11:11:49Z mark $
 */
public abstract class AbstractAptMojo extends AbstractMojo
{
    // read-only parameters ---------------------------------------------------

    /**
     * The maven project.
     * 
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * The plugin's artifacts.
     * 
     * @parameter expression="${plugin.artifacts}"
     * @required
     * @readonly
     */
    private List pluginArtifacts;

    /**
     * The directory to run apt from when forked.
     * 
     * @parameter expression="${basedir}"
     * @required
     * @readonly
     */
    private File workingDirectory;

    // configurable parameters ------------------------------------------------

    /**
     * Whether to run apt in a separate process.
     * 
     * @parameter default-value="false"
     */
    private boolean fork;

    /**
     * The apt executable to use when forked.
     * 
     * @parameter expression="${maven.apt.executable}" default-value="apt"
     */
    private String executable;

    /**
     * The initial size of the memory allocation pool when forked, for example 64m.
     * 
     * @parameter
     */
    private String meminitial;

    /**
     * The maximum size of the memory allocation pool when forked, for example 128m.
     * 
     * @parameter
     */
    private String maxmem;

    /**
     * Whether to show apt warnings. This is opposite to the -nowarn argument for apt.
     * 
     * @parameter expression="${maven.apt.showWarnings}" default-value="false"
     */
    private boolean showWarnings;

    /**
     * The source file encoding name, such as EUC-JP and UTF-8. If encoding is not
     * specified, the encoding ISO-8859-1 is used rather than the platform default for reproducibility
     * reasons. This is equivalent to the -encoding argument for apt.
     * 
     * @parameter expression="${maven.apt.encoding}" default-value="ISO-8859-1"
     */
    private String encoding;

    /**
     * Whether to output information about each class loaded and each source file processed. This is equivalent to the
     * -verbose argument for apt.
     * 
     * @parameter expression="${maven.apt.verbose}" default-value="false"
     */
    private boolean verbose;

    /**
     * Options to pass to annotation processors. These are equivalent to multiple -A arguments for apt.
     * 
     * @parameter
     */
    private String[] options;

    /**
     * Name of AnnotationProcessorFactory to use; bypasses default discovery process. This is equivalent
     * to the -factory argument for apt.
     * 
     * @parameter expression="${maven.apt.factory}"
     */
    private String factory;

    /**
     * The source directories containing any additional sources to be processed.
     * 
     * @parameter
     */
    private List additionalSourceRoots;

    /**
     * The path for processor-generated resources.
     * 
     * @parameter
     */
    private String resourceTargetPath;

    /**
     * Whether resource filtering is enabled for processor-generated resources.
     * 
     * @parameter default-value="false"
     */
    private boolean resourceFiltering;

    /**
     * Force apt processing without staleness checking. When false, use outputFiles or
     * outputFileEndings to control computing staleness.
     * 
     * @parameter default-value="false"
     */
    private boolean force;
    
    /**
     * The filenames of processor-generated files to examine when computing staleness. For example,
     * generated.xml would specify that the processor creates the aforementioned single file from all
     * .java source files. When this parameter is not specified, outputFileEndings is used
     * instead.
     * 
     * @parameter
     */
    private Set outputFiles;

    /**
     * The filename endings of processor-generated files to examine when computing staleness. For example,
     * .txt would specify that the processor creates a corresponding .txt file for every
     * .java source file. Default value is .java. Note that this parameter has no effect if
     * outputFiles is specified.
     * 
     * @parameter
     */
    private Set outputFileEndings;

    /**
     * Sets the granularity in milliseconds of the last modification date for testing whether a source needs
     * processing.
     * 
     * @parameter expression="${maven.apt.staleMillis}" default-value="0"
     */
    private int staleMillis;

    /**
     * Whether to bypass running apt.
     * 
     * @parameter expression="${maven.apt.skip}" default-value="false"
     */
    private boolean skip;
    
    // fields -----------------------------------------------------------------
    
    private Set includes;
    
    private Set excludes;

    // Mojo methods -----------------------------------------------------------

    /**
     * {@inheritDoc}
     */
    public final void execute() throws MojoExecutionException
    {
        if ( skip )
        {
            getLog().info( "Skipping apt" );
        }
        else
        {
            executeImpl();
        }
    }

    // protected methods ------------------------------------------------------

    protected void executeImpl() throws MojoExecutionException
    {
        // apply defaults
        
        includes = CollectionUtils.defaultSet( getIncludes(), Collections.singleton( "**/*.java" ) );
        excludes = CollectionUtils.defaultSet( getExcludes() );
        
        // invoke apt
        
        List staleSourceFiles = getSourceFiles( getStaleScanner(), "stale sources" );

        if ( staleSourceFiles.isEmpty() )
        {
            getLog().info( "Nothing to process - all processor-generated files are up to date" );
        }
        else
        {
            executeApt();
        }
        
        // add source root

        String sourcePath = getSourceOutputDirectory().getPath();

        if ( !getCompileSourceRoots().contains( sourcePath ) )
        {
            getCompileSourceRoots().add( sourcePath );
        }
        
        // add resource

        String resourcePath = getOutputDirectory().getPath();

        if ( !MavenProjectUtils.containsDirectory( getResources(), resourcePath ) )
        {
            Resource resource = new Resource();

            resource.setDirectory( resourcePath );
            resource.addExclude( "**/*.java" );
            resource.setFiltering( resourceFiltering );

            if ( resourceTargetPath != null )
            {
                resource.setTargetPath( resourceTargetPath );
            }

            getResources().add( resource );
        }
    }

    /**
     * Gets the Maven project.
     * 
     * @return the project
     */
    protected MavenProject getProject()
    {
        return project;
    }

    /**
     * Gets the options to pass to annotation processors.
     * 
     * @return an array of options to pass to annotation processors
     */
    protected String[] getOptions()
    {
        return options;
    }

    /**
     * The source directories containing the sources to be processed.
     * 
     * @return list of compilation source roots
     */
    protected abstract List getCompileSourceRoots();

    /**
     * Gets the project's resources.
     * 
     * @return the project's resources
     */
    protected abstract List getResources();

    /**
     * Gets the project's classpath.
     * 
     * @return a list of classpath elements
     */
    protected abstract List getClasspathElements();

    /**
     * Gets a set of inclusion filters for apt.
     * 
     * @return a set of inclusion filters
     */
    protected abstract Set getIncludes();

    /**
     * Gets a set of exclusion filters for apt.
     * 
     * @return a set of exclusion filters
     */
    protected abstract Set getExcludes();

    /**
     * The directory to place processor and generated class files.
     * 
     * @return the directory to place processor and generated class files
     */
    protected abstract File getOutputDirectory();
    
    /**
     * The directory root under which processor-generated source files will be placed; files are placed in
     * subdirectories based on package namespace.
     * 
     * @return the directory root under which processor-generated source files will be placed
     */
    protected abstract File getSourceOutputDirectory();
    
    // private methods --------------------------------------------------------
    
    private void executeApt() throws MojoExecutionException
    {
        List sourceFiles = getSourceFiles( getSourceScanner(), "sources" );
        
        if ( getLog().isInfoEnabled() )
        {
            int count = sourceFiles.size();
            
            getLog().info( "Processing " + count + " source file" + ( count == 1 ? "" : "s" ) );
        }

        List args = createArgs( sourceFiles );

        boolean success;

        if ( fork )
        {
            success = AptUtils.invokeForked( getLog(), workingDirectory, executable, meminitial, maxmem, args );
        }
        else
        {
            success = AptUtils.invoke( getLog(), args );
        }

        if ( !success )
        {
            throw new MojoExecutionException( "Apt failed" );
        }
    }

    private List createArgs( List sourceFiles ) throws MojoExecutionException
    {
        List args = new ArrayList();

        // javac arguments

        Set classpathElements = new LinkedHashSet();
        classpathElements.addAll( getPluginClasspathElements() );
        classpathElements.addAll( getClasspathElements() );

        if ( !classpathElements.isEmpty() )
        {
            args.add( "-classpath" );
            args.add( toPath( classpathElements ) );
        }

        List sourcePaths = getSourcePaths();

        if ( !sourcePaths.isEmpty() )
        {
            args.add( "-sourcepath" );
            args.add( toPath( sourcePaths ) );
        }

        args.add( "-d" );
        args.add( getOutputDirectory().getAbsolutePath() );

        if ( !showWarnings )
        {
            args.add( "-nowarn" );
        }

        if ( encoding != null )
        {
            args.add( "-encoding" );
            args.add( encoding );
        }

        if ( verbose )
        {
            args.add( "-verbose" );
        }

        // apt arguments

        args.add( "-s" );
        args.add( getSourceOutputDirectory().getAbsolutePath() );

        // never compile
        args.add( "-nocompile" );

        if ( options != null )
        {
            for ( String option : options )
            {
                args.add( "-A" + option.trim() );
            }
        }

        if ( StringUtils.isNotEmpty( factory ) )
        {
            args.add( "-factory" );
            args.add( factory );
        }

        // source files

        for ( File file : sourceFiles )
        {
            args.add( file.getAbsolutePath() );
        }

        return args;
    }

    private static String toPath( Collection paths )
    {
        StringBuffer buffer = new StringBuffer();

        for ( Iterator iterator = paths.iterator(); iterator.hasNext(); )
        {
            buffer.append( iterator.next() );

            if ( iterator.hasNext() )
            {
                buffer.append( File.pathSeparator );
            }
        }

        return buffer.toString();
    }

    private List getPluginClasspathElements() throws MojoExecutionException
    {
        try
        {
            return MavenProjectUtils.getClasspathElements( project, pluginArtifacts );
        }
        catch ( DependencyResolutionRequiredException exception )
        {
            throw new MojoExecutionException( "Cannot get plugin classpath elements", exception );
        }
    }

    private List getSourcePaths()
    {
        List sourcePaths = new ArrayList();

        sourcePaths.addAll( getCompileSourceRoots() );

        if ( additionalSourceRoots != null )
        {
            sourcePaths.addAll( additionalSourceRoots );
        }

        return sourcePaths;
    }

    private List getSourceFiles( SourceInclusionScanner scanner, String name ) throws MojoExecutionException
    {
        List sourceFiles = new ArrayList();

        for ( String path : getSourcePaths() )
        {
            File sourceDir = new File( path );

            sourceFiles.addAll( getSourceFiles( scanner, name, sourceDir ) );
        }

        return sourceFiles;
    }

    private Set getSourceFiles( SourceInclusionScanner scanner, String name, File sourceDir )
        throws MojoExecutionException
    {
        Set sources;

        if ( sourceDir.isDirectory() )
        {
            try
            {
                Set rawSources = scanner.getIncludedSources( sourceDir, getOutputDirectory() );

                sources = CollectionUtils.genericSet( rawSources, File.class );                    
            }
            catch ( InclusionScanException exception )
            {
                throw new MojoExecutionException( "Error scanning source directory: " + sourceDir, exception );
            }
        }
        else
        {
            sources = Collections.emptySet();
        }

        if ( getLog().isDebugEnabled() )
        {
            if ( sources.isEmpty() )
            {
                getLog().debug( "No " + name + " found in " + sourceDir );
            }
            else
            {
                getLog().debug( StringUtils.capitalizeFirstLetter( name ) + " found in " + sourceDir + ":" );

                LogUtils.log( getLog(), LogUtils.LEVEL_DEBUG, sources, "  " );
            }
        }

        return sources;
    }

    private SourceInclusionScanner getStaleScanner()
    {
        // create scanner
        
        SourceInclusionScanner scanner;
        
        if ( force )
        {
            if ( !CollectionUtils.isEmpty( outputFiles ) || !CollectionUtils.isEmpty( outputFileEndings ) )
            {
                getLog().warn( "Not using staleness checking - ignoring outputFiles and outputFileEndings" );
            }
            
            getLog().debug( "Processing all source files" );
            
            scanner = createSimpleScanner();
        }
        else
        {
            scanner = new StaleSourceScanner( staleMillis, includes, excludes );
            
            if ( !CollectionUtils.isEmpty( outputFiles ) )
            {
                if ( !CollectionUtils.isEmpty( outputFileEndings ) )
                {
                    getLog().warn( "Both outputFiles and outputFileEndings specified - using outputFiles" );
                }
                
                getLog().debug( "Computing stale sources against target files " + outputFiles );
                
                for ( String file : outputFiles )
                {                    
                    scanner.addSourceMapping( new SingleTargetSourceMapping( ".java", file ) );
                }
            }
            else
            {
                Set suffixes = CollectionUtils.defaultSet( outputFileEndings, Collections.singleton( ".java" ) );
                
                getLog().debug( "Computing stale sources against target file endings " + suffixes );
                
                scanner.addSourceMapping( new SuffixMapping( ".java", suffixes ) );
            }
        }
        
        return scanner;
    }
    
    private SourceInclusionScanner getSourceScanner()
    {
        SourceInclusionScanner scanner;
        
        if ( force || CollectionUtils.isEmpty( outputFiles ) )
        {
            scanner = getStaleScanner();
        }
        else
        {
            scanner = createSimpleScanner();
        }
        
        return scanner;
    }
    
    private SourceInclusionScanner createSimpleScanner()
    {
        SourceInclusionScanner scanner = new SimpleSourceInclusionScanner( includes, excludes );
        
        // dummy mapping required to function
        scanner.addSourceMapping( new SuffixMapping( "", "" ) );
        
        return scanner;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy