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

uk.org.raje.maven.plugin.msbuild.AbstractMSBuildPluginMojo Maven / Gradle / Ivy

/*
 * Copyright 2013 Andrew Everitt, Andrew Heckford, Daniele Masato
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package uk.org.raje.maven.plugin.msbuild;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;

import uk.org.raje.maven.plugin.msbuild.configuration.BuildConfiguration;
import uk.org.raje.maven.plugin.msbuild.configuration.BuildPlatform;
import uk.org.raje.maven.plugin.msbuild.configuration.CppCheckConfiguration;
import uk.org.raje.maven.plugin.msbuild.configuration.CxxTestConfiguration;
import uk.org.raje.maven.plugin.msbuild.configuration.SonarConfiguration;
import uk.org.raje.maven.plugin.msbuild.configuration.VersionInfoConfiguration;
import uk.org.raje.maven.plugin.msbuild.parser.VCProject;

/**
 * Abstract base class for the msbuild-maven-plugin which defines all configuration properties exposed.
 */
public abstract class AbstractMSBuildPluginMojo extends AbstractMojo
{
    @Override
    public final void execute() throws MojoExecutionException, MojoFailureException
    {
        // Fix up configuration
        // This is done with the following hard coded fixes for parameters that
        // we want to be able to pull from -D's or settings.xml but are stored
        // in configuration sub-classes.
        try
        {
            if ( cppCheckPath != null )
            {
                Field cppCheckPathField = CppCheckConfiguration.class.getDeclaredField( "cppCheckPath" );
                cppCheckPathField.setAccessible( true );
                cppCheckPathField.set( cppCheck, cppCheckPath );
                getLog().debug( "Found property for cppcheck.path, using this value" );
            }
            if ( cxxTestHome != null )
            {
                Field cxxTestPathField = CxxTestConfiguration.class.getDeclaredField( "cxxTestHome" );
                cxxTestPathField.setAccessible( true );
                cxxTestPathField.set( cxxTest, cxxTestHome );
                getLog().debug( "Found property for cxxtest.home, using this value" );
           }
        }
        catch ( NoSuchFieldException nsfe )
        {
            throw new MojoFailureException( "Internal error, please contact the Mojo developer", nsfe );
        }
        catch ( IllegalAccessException iae )
        {
            throw new MojoFailureException( "Internal error, please contact the Mojo developer", iae );
        }

        // Configuration fixed, call child to do real work
        doExecute();
    }

    
    /**
     * Actually perform the work of this Mojo now the configuration has been fixed.
     * @see AbstractMojo#execute
     */
    protected abstract void doExecute() throws MojoExecutionException, MojoFailureException;

    /**
     * Check that we have a valid project or solution file.
     * @throws MojoExecutionException if the specified projectFile is invalid.
     */
    protected void validateProjectFile() 
            throws MojoExecutionException
    {
        if ( projectFile != null
                && projectFile.exists()
                && projectFile.isFile() )
        {
            getLog().debug( "Project file validated at " + projectFile );

            boolean solutionFile = projectFile.getName().toLowerCase().endsWith( "." + SOLUTION_EXTENSION ); 
            if ( ( MSBuildPackaging.isSolution( mavenProject.getPackaging() ) && ! solutionFile )
                    || ( ! MSBuildPackaging.isSolution( mavenProject.getPackaging() ) && solutionFile ) )
            {
                // Solution packaging defined but the projectFile is not a .sln
                String msg = "Packaging doesn't match project file type. "
                        + "If you specify a solution file then packaging must be " + MSBuildPackaging.MSBUILD_SOLUTION;
                getLog().error( msg );
                throw new MojoExecutionException( msg );
            }
            return;
        }
        String prefix = "Missing projectFile";
        if ( projectFile != null )
        {
            prefix = ". The specified projectFile '" + projectFile
                    + "' is not valid";
        }
        throw new MojoExecutionException( prefix
                + ", please check your configuration" );
    }

    /**
     * Compute the relative path portion from a path and a base directory.
     * If basedir and target are the same "." is returned.
     * For example: Given C:\foo\bar and C:\foo\bar\baz this method will return baz
     * @param basedir the base directory
     * @param target the path to express as relative to basedir
     * @return the relative portion of the path between basedir and target
     * @throws MojoExecutionException if the target is not basedir or a subpath of basedir
     */
    protected File getRelativeFile( File basedir, File target ) throws MojoExecutionException
    {
        String basedirStr = basedir.getPath();
        String targetDirStr = target.getPath();
        
        if ( targetDirStr.equals( basedirStr ) )
        {
            return new File( "." );
        }
        else if ( targetDirStr.startsWith( basedirStr + File.separator ) ) // add slash to ensure directory
        {
            return new File( targetDirStr.substring( basedirStr.length() + 1 ) ); // + slash char
        }
        throw new MojoExecutionException( "Unable to relativize " + targetDirStr + " to " + basedir );
    }

    /**
     * Return project configurations for the specified platform and configuration.
     * @param platform the platform to parse for
     * @param configuration the configuration to parse for
     * @return a list of VCProject objects containing configuration for the specified platform and configuration
     * @throws MojoExecutionException if parsing fails
     */
    protected List getParsedProjects( BuildPlatform platform, BuildConfiguration configuration ) 
            throws MojoExecutionException
    {
        String key = platform + "|" + configuration;
        List vcProjects;

        vcProjects = parsedProjects.get( key );
        
        if ( vcProjects == null )
        {
            VCParserHelper vcParserHelper = new VCParserHelper( getLog() );
            
            try
            {
                if ( MSBuildPackaging.isSolution( mavenProject.getPackaging() ) ) 
                {
                    vcParserHelper.loadSolutionFile( projectFile, platform, configuration );
                }
                else 
                {
                    vcParserHelper.loadProjectFile( projectFile, platform, configuration );
                }
                
                vcProjects = vcParserHelper.getVCProjects();
                parsedProjects.put( key, vcProjects );
            }
            catch ( MojoExecutionException mee )
            {
                getLog().error( mee.getMessage() );
                throw mee;
            }
        }

        return vcProjects;
    }

    /**
     * Return project configurations for the specified platform and configuration filtered by name using the specified 
     * Pattern.
     * @param platform the platform to parse for
     * @param configuration the configuration to parse for
     * @param filterRegex a Pattern to use to filter the projects
     * @return a list of VCProject objects containing configuration for the specified platform and configuration
     * @throws MojoExecutionException if parsing fails
     */
    protected List getParsedProjects( BuildPlatform platform, BuildConfiguration configuration, 
            Pattern filterRegex ) throws MojoExecutionException
    {
        List unfiltered = getParsedProjects( platform, configuration );
        List filteredList = new ArrayList( unfiltered.size() );
        for ( VCProject p : unfiltered )
        {
            if ( filterRegex == null )
            {
                filteredList.add( p );
            }
            else
            {
                Matcher prjExcludeMatcher = filterRegex.matcher( p.getName() );
                if ( ! prjExcludeMatcher.matches() )
                {
                    filteredList.add( p );
                }
            }
        }
        return filteredList;
    }    

    /**
     * Return the project configuration for the specified target, platform and configuration
     * Note: This is only valid for solutions as target names don't apply for a standalone project file
     * @param targetName the target to look for
     * @param platform the platform to parse for
     * @param configuration the configuration to parse for
     * @return the VCProject for the specified target
     * @throws MojoExecutionException if the requested project cannot be identified
     */
    protected VCProject getParsedProject( String targetName, BuildPlatform platform, BuildConfiguration configuration )
            throws MojoExecutionException
    {
        List projects = getParsedProjects( platform, configuration );
        for ( VCProject project : projects )
        {
            if ( targetName.equals( project.getTargetName() ) )
            {
                return project;
            }
        }
        throw new MojoExecutionException( "Target '" + targetName + "' not found in project files" );
    }

    /**
     * Determine whether CxxTest is enabled by the configuration
     * @param stepName the string to use in log messages to describe the process being attempted
     * @return true if CxxTest is configured, false otherwise
     * @throws MojoExecutionException if CxxTest is configured for a project not a solution
     */
    protected boolean isCxxTestEnabled( String stepName ) throws MojoExecutionException
    {
        if ( ! MSBuildPackaging.isSolution( mavenProject.getPackaging() )
            && ( cxxTest.getTestTargets() != null ) )
        {
            String msg = "CxxTest is only supported for solution (.sln) files!";
            getLog().error( msg );
            throw new MojoExecutionException( msg );
        }

        if ( cxxTest.skip() )
        {
            getLog().info( CXXTEST_SKIP_MESSAGE + " " + stepName + ", 'skip' set to true in the " 
                    + CxxTestConfiguration.CXXTEST_NAME + " configuration." );
            
            return false;
        }
        
        if ( cxxTest.getCxxTestHome() == null ) 
        {
            getLog().info( CXXTEST_SKIP_MESSAGE + ", path to " + CxxTestConfiguration.CXXTEST_NAME + " not set." );
            return false;
        }
        
        return true;
    }

    protected void validateCxxTestConfiguration() throws MojoExecutionException, MojoFailureException 
    {
        if ( !getCxxTestPython2Home().isDirectory() )
        {
            throw new MojoExecutionException( "Could not find the Python 2 scripts for " 
                    + CxxTestConfiguration.CXXTEST_NAME + " at " + getCxxTestPython2Home(), 
                    new FileNotFoundException( getCxxTestPython2Home().getAbsolutePath() ) );
        }
        
        try 
        {
            MojoHelper.validateToolPath( new File( getCxxTestPython2Home(), "cxxtest/cxxtestgen.py" ), 
                    CxxTestConfiguration.CXXTEST_HOME, CxxTestConfiguration.CXXTEST_NAME, getLog() );
        }
        catch ( FileNotFoundException fnfe )
        {
            throw new MojoExecutionException( CxxTestConfiguration.CXXTEST_NAME 
                    + " could not be found at " + fnfe.getMessage() + ". "
                    + "You need to configure it in the plugin configuration section in the "
                    + "POM file using ... "
                    + "or ...; "
                    + "alternatively, you can use the command-line parameter -Dcxxtest.home=... "
                    + "or set the environment variable " + CxxTestConfiguration.CXXTEST_HOME, fnfe );
        }
        
        if ( cxxTest.getTestTargets() == null || cxxTest.getTestTargets().size() == 0 )
        {
            throw new MojoExecutionException( "You must specify at least one test target. If you want to skip "
                    + "running the tests, please set 'skip' to true in the " + CxxTestConfiguration.CXXTEST_NAME 
                    + " configuration.", new InvalidParameterException( "testTargets" ) );
        }
        
        validateProjectFile();
        platforms = MojoHelper.validatePlatforms( platforms );
    }

    protected File getCxxTestPython2Home() 
    {
        return new File( cxxTest.getCxxTestHome(), "python" );
    }
    
    protected boolean isCppCheckEnabled() 
    {
        if ( cppCheck.skip() )
        {
            getLog().info( CPPCHECK_SKIP_MESSAGE + ", 'skip' set to true in the " + CppCheckConfiguration.CPPCHECK_NAME
                    + " configuration." );
            
            return false;
        }
        
        if ( cppCheck.getCppCheckPath() == null ) 
        {
            getLog().info( CPPCHECK_SKIP_MESSAGE + ", path to " + CppCheckConfiguration.CPPCHECK_NAME + " not set." );
            return false;
        }        
        
        return true;
    }

    protected void validateCppCheckConfiguration() throws MojoExecutionException, MojoFailureException 
    {
        try 
        {
            MojoHelper.validateToolPath( cppCheck.getCppCheckPath(), CppCheckConfiguration.CPPCHECK_PATH_ENVVAR, 
                    CppCheckConfiguration.CPPCHECK_NAME, getLog() );
        }
        catch ( FileNotFoundException fnfe )
        {
            throw new MojoExecutionException( CppCheckConfiguration.CPPCHECK_NAME 
                    + "could not be found at " + fnfe.getMessage() + ". "
                    + "You need to configure it in the plugin configuration section in the "
                    + "POM file using ... "
                    + "or ...; "
                    + "alternatively, you can use the command-line parameter -Dcppcheck.path=... "
                    + "or set the environment variable " + CppCheckConfiguration.CPPCHECK_PATH_ENVVAR, fnfe );
        }
        
        validateProjectFile();
        platforms = MojoHelper.validatePlatforms( platforms );
    }
    
    protected boolean isSonarEnabled() throws MojoExecutionException
    {
        if ( sonar.skip() )
        {
            getLog().info( SONAR_SKIP_MESSAGE + ", 'skip' set to true in the " + SonarConfiguration.SONAR_NAME
                    + " configuration." );
            
            return false;
        }
        
        validateProjectFile();
        platforms = MojoHelper.validatePlatforms( platforms );
        
        return true;
    }    

    /**
     * The MavenProject for the current build.
     */
    @Parameter( defaultValue = "${project}" )
    protected MavenProject mavenProject;

    /**
     * The project (.vcxproj) or solution (.sln) file to build.
     */
    @Parameter(
            readonly = false,
            required = true )
    protected File projectFile;

    /**
     * The set of platforms and configurations to build.
     */
    @Parameter(
            readonly = false,
            required = false )
    protected List platforms;

    /**
     * The set of targets to build.
     */
    @Parameter(
            readonly = false,
            required = false )
    protected List targets;

    /**
     * The path to MSBuild.
     */
    @Parameter(
            property = "msbuild.path",
            readonly = false,
            required = true )
    protected File msbuildPath;

    /**
     * Configure the version-info Mojo.
     */
    @Parameter
    protected VersionInfoConfiguration versionInfo = new VersionInfoConfiguration();

    /**
     * Configure the CppCheck Mojo.
     */
    @Parameter
    protected CppCheckConfiguration cppCheck = new CppCheckConfiguration();

    /**
     * Configure the CxxTest Mojo.
     */
    @Parameter
    protected CxxTestConfiguration cxxTest = new CxxTestConfiguration();

    /**
     * Configure the Sonar Mojo.
     */
    @Parameter
    protected SonarConfiguration sonar = new SonarConfiguration();
    
    /**
     * This parameter only exists to pickup a -D property or property in settings.xml
     * @see CppCheckConfiguration#cppCheckPath
     */
    @Parameter(
            property = "cppcheck.path",
            readonly = true,
            required = false )
    private File cppCheckPath;

    /**
     * This parameter only exists to pickup a -D property or property in settings.xml
     * @see CxxTestConfiguration#cxxTestHome
     */
    @Parameter( 
            property = "cxxtest.home", 
            readonly = true, 
            required = false )
    private File cxxTestHome;

    /**
     * A Map containing data parsed from the project files for each platform-configuration pair.
     * The Map is populated as needed (lazy load) by the method 
     * {@link #parsedProjects(BuildPlatform, BuildConfiguration)}.
     */
    private Map > parsedProjects = new HashMap>();

    /**
     * The file extension for solution files.
     */
    private static final String SOLUTION_EXTENSION = "sln";

    private static final String CXXTEST_SKIP_MESSAGE = "Skipping test";
    private static final String CPPCHECK_SKIP_MESSAGE = "Skipping static code analysis";
    private static final String SONAR_SKIP_MESSAGE = "Skipping Sonar analysis";
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy