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

org.apache.maven.dotnet.AbstractDotNetMojo Maven / Gradle / Ivy

Go to download

A plugin that provides general build and test facilities for .Net projects and solutions

There is a newer version: 1.2
Show newest version
/*
 * Maven and Sonar plugin for .Net
 * Copyright (C) 2010 Jose Chillan and Alexandre Victoor
 * mailto: [email protected] or [email protected]
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */

package org.apache.maven.dotnet;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.dotnet.commons.project.DotNetProjectException;
import org.apache.maven.dotnet.commons.project.VisualStudioProject;
import org.apache.maven.dotnet.commons.project.VisualStudioSolution;
import org.apache.maven.dotnet.commons.project.VisualStudioUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import org.codehaus.plexus.util.cli.StreamConsumer;

/**
 * A utilitary class to factor some features for the DotNet relative mojos.
 * 
 * @author Jose CHILLAN Apr 14, 2009
 */
public abstract class AbstractDotNetMojo extends AbstractMojo {
  /**
   * Name of the file that contains the names of the files to export in a
   * resource folder
   */
  private final static String CONTENT_FILE_NAME = "content.txt";

  /**
   * A utility empty array.
   */
  private static final String[] EMPTY_STRING_ARRAY = new String[0];

  /**
   * The name of the solution to use in case there are multiple solutions in the
   * folder AND none has the same name as the artifact with a ".sln" extension.
   * 
   * @parameter expression="${visual.studio.solution}"
   */
  protected String solutionName;

  /**
   * The name of the project to use if it doesn't have the same name as the
   * artifact with a ".csproj" extension.
   * 
   * @parameter expression="${visual.studio.project}"
   */
  protected String projectName;

  /**
   * A pattern that will help to figure out if a project is a test project from
   * its name. The '*' and '?' common jokers are accepted. Several patterns may be 
   * specified using ";" as a delimiter.
   * 
   * @parameter expression="${visual.test.project.pattern}"
   *            default-value="*.Tests"
   */
  protected String testProjectPattern;

  /**
   * The build configurations to use for the project or solution, separated by
   * colons or semi-colons as in "Debug,Release".
   * Default value is "Debug". If value is "ALL", all the build configurations defined
   * in the sln file will be used.
   * 
   * @parameter expression="${msbuild.configurations}"
   *            alias="${buildConfigurations}" default-value="Debug"
   */
  protected String buildConfigurations;
  
  /**
   * Defines if the build should generate debug symbols (typically .pdb files)
   * 
   * @parameter expression="${maven.compiler.debug}" default-value="true"
   */
  protected boolean generatePdb;

  /**
   * Defines if the plugin can use the maven-dotnet-runtime artifact to export
   * the relevant .Net quality applications instead of using their global path.
   * The defined path is always taken in priority.
   * 
   * @parameter expression="${dotnet.use.embedded.runtime}" default-value="true"
   */
  protected boolean useEmbbededRuntime;

  /**
   * Location of the output files
   * 
   * @parameter expression="${project.build.directory}"
   * @required
   */
  protected File outputDirectory;

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

  /**
   * Defines if the launched commands should be appended into a "command.txt"
   * file. For debug purpose.
   * 
   * @parameter expression="${trace.commands}"
   */
  protected boolean traceCommands = false;
  
  /**
   * List of the excluded projects, using ',' as delimiter. C# files
   * of these projects will be ignored. No violation on files
   * of these projects should be reported. 
   * 
   * @parameter expression="${skippedProjects}"
   */
  protected String skippedProjects;

  /**
   * Locations of the assemblies that should be analysed in case default location
   * should not be used. Actually AOP tools such as PostSharp mess up the cil and 
   * may provoke false positives. 
   * Project names should be used as keys. Values should be the path to the wanted DLLs. 
   * If the assembly of the "Cache" project is located in the "compile" directory instead 
   * of usual "bin/Debug", you can add in the configuration section of the plugin something 
   * like <assemblyDirectories\><Cache>compile</Cache></assemblyDirectories> 
   * @parameter
   */
  protected Map assemblyDirectories = new HashMap();
  

  /**
   * Constructs a @link{AbstractDotNetMojo}.
   */
  public AbstractDotNetMojo() {
  }

  
  /**
   * Launches the MOJO action. 
* This method checks whether the pom.xml is for a project or a solution and * launches the adequate method {@link #executeProject(VisualStudioProject)} * or {@link #executeSolution(VisualStudioSolution)}. * * @throws MojoExecutionException * @throws MojoFailureException */ @Override public void execute() throws MojoExecutionException, MojoFailureException { if (!checkExecutionAllowed()) { return; } // Here we should add : if the project is a "sln" packaging, then launch for // the solution if (VisualStudioUtils.SOLUTION_PACKAGINGS.contains(project.getPackaging())) { VisualStudioSolution visualSolution = null; try { visualSolution = VisualStudioUtils.getVisualSolution(project, solutionName); } catch (DotNetProjectException e) { throw new MojoExecutionException( "The solution for project " + project.getArtifactId() + " is not a " + "properly configured Visual Studio solution.\nPlease ensure you have a '.sln' file in " + project.getBasedir(), e); } if (visualSolution == null) { throw new MojoExecutionException( "The solution for project " + project.getArtifactId() + " is not a " + "properly configured Visual Studio solution.\nPlease ensure you have a '.sln' file in " + project.getBasedir()); } if ("ALL".equals(buildConfigurations)) { buildConfigurations = StringUtils.join(visualSolution.getBuildConfigurations(),','); } if (assemblyDirectories != null ) { visualSolution.overrideAssemblyDirectories(assemblyDirectories); } visualSolution.filterProjects(skippedProjects); executeSolution(visualSolution); return; } VisualStudioProject visualProject = getVisualProject(); if (visualProject == null) { getLog().info( "The project " + project.getArtifactId() + " is not a visual studio project"); return; } executeProject(visualProject); } /** * Launches the action in case the project is a VisualStudio project * * @param visualProject */ protected abstract void executeProject(VisualStudioProject visualProject) throws MojoExecutionException, MojoFailureException; /** * Launches the action in case the project is a VisualStudio solution * * @param visualSolution */ protected abstract void executeSolution(VisualStudioSolution visualSolution) throws MojoExecutionException, MojoFailureException; /** * Gets the Visual Studio project associated to the current project. * * @return the project, or null if the project is not a Visual * Studio project * @throws MojoExecutionException * if the current pom.xml correspond to a badly defined project */ protected final VisualStudioProject getVisualProject() throws MojoExecutionException { File projectFile; try { projectFile = VisualStudioUtils.getVisualFile(project, projectName, ".csproj", "project"); } catch (DotNetProjectException except) { throw new MojoExecutionException( "Could not create a VisualStudio project", except); } // No solution defined if (projectFile == null) { getLog().debug( "The project " + project.getName() + " is not a Visual Studio project"); return null; } try { VisualStudioProject visualProject = VisualStudioUtils .getProject(projectFile); assessTestProject(visualProject); return visualProject; } catch (FileNotFoundException e) { throw new MojoExecutionException( "Could not extract the project information for " + projectFile, e); } catch (DotNetProjectException e) { throw new MojoExecutionException( "Could not extract the project information for " + projectFile, e); } } /** * Gets the solution corresponding to the current project if applicable. * * @return the generated solution, or null if the project is not * a solution */ protected final VisualStudioSolution getVisualSolution() throws MojoExecutionException { File basedir = project.getBasedir(); File solutionFile; try { solutionFile = VisualStudioUtils.getVisualFile(project, solutionName, ".sln", "solution"); } catch (DotNetProjectException except) { throw new MojoExecutionException( "Could not create a VisualStudio solution", except); } // No solution defined if (solutionFile == null) { getLog().debug( "The project " + project.getName() + " is not a Visual Studio solution"); return null; } // We define the solution file solutionName = solutionFile.getName(); // We try to build the solution VisualStudioSolution solution; try { solution = VisualStudioUtils.getSolution(basedir, solutionName); } catch (Exception e) { throw new MojoExecutionException( "Could not extract the solution information for " + solutionFile, e); } List projects = solution.getProjects(); // We define for each project if it is a test project for (VisualStudioProject visualStudioProject : projects) { assessTestProject(visualStudioProject); } return solution; } /** * @param visualStudioProject */ private void assessTestProject(VisualStudioProject visualStudioProject) { VisualStudioUtils.assessTestProject(visualStudioProject, testProjectPattern); } /** * Deletes a set of file before generation * * @param files * the files to delete */ protected void deleteFiles(File... files) { for (File file : files) { if (file.exists()) { file.delete(); } } } /** * Ensures that the current running system is a Windows version * * @throws MojoExecutionException * if the system is not windows */ protected void ensureWindowsSystem() throws MojoExecutionException { String osName = System.getProperty("os.name").toLowerCase(); if (!osName.contains("windows")) { throw new MojoExecutionException( "The task must be launched on a Windows system"); } } /** * Ensure that the java version is at least 1.6 * * @throws MojoExecutionException */ protected void ensureJavaVersion() throws MojoExecutionException { String version = System.getProperty("java.version"); int versionValue = Integer.parseInt(new StringBuilder() .append(version.charAt(0)).append(version.charAt(2)).toString()); if (versionValue < 16) { throw new MojoExecutionException( "The C# reporting plugin requires to be run in at least the JDK 6.0"); } } /** * Launches a command line, redirecting the stream to the maven logs. * @param reportType * a display type for the report * @param acceptedMask * a mask for the exit code of the command that is accepted (put 0 if * you don't know) * @param commandline * the command ready to be executed * * @return the status of the command after execution * @throws MojoExecutionException * if the execution command could not be launched for any reason * @throws MojoFailureException * if the command status after execution is not satisfying */ protected int launchCommand(File executable, List arguments, String reportType, int acceptedMask) throws MojoExecutionException, MojoFailureException { Commandline commandline = generateCommandLine(executable, arguments); return launchCommand(commandline, reportType, acceptedMask, true); } /** * Launches a command line, redirecting the stream to the maven logs * @param commandline * the command ready to be executed * @param reportType * a display type for the report * @param acceptedMask * a mask for the exit code of the command that is accepted (put 0 if * you don't know) * @param throwsFailure * true if the method should throw an exception in case * of failure * * @return the status of the command after execution * @throws MojoExecutionException * if the execution command could not be launched for any reason * @throws MojoFailureException * if the command status after execution is not satisfying and the * throwsFailure parameter is not false */ protected int launchCommand(Commandline commandline, String reportType, int acceptedMask, boolean throwsFailure) throws MojoExecutionException, MojoFailureException { int commandLineResult; Log log = getLog(); String[] commandLineElements = commandline.getCommandline(); CommandStreamConsumer systemOut = new CommandStreamConsumer(log); try { // Execute the commandline log.debug("Executing command: " + commandline); log.debug("Command elements :" + Arrays.toString(commandLineElements)); ProcessBuilder builder = new ProcessBuilder( Arrays.asList(commandLineElements)); builder.redirectErrorStream(true); if (traceCommands) { try { File commandFile = getReportFile("command.txt"); OutputStream stream = new FileOutputStream(commandFile, true); PrintWriter writer = new PrintWriter(stream); writer.println("Mojo : " + reportType + " on " + new Timestamp(System.currentTimeMillis())); writer.println(commandline); writer.println(); writer.close(); } catch (FileNotFoundException e) { // Nothing : commands are not logged log.debug("command.txt not found"); } } commandLineResult = CommandLineUtils.executeCommandLine(commandline, systemOut, systemOut); // Check if nunit-console is not in the path if (systemOut.isCommandNotFound()) { throw new MojoExecutionException("Please add the executable for " + reportType + " to your path"); } else if ((commandLineResult & (~acceptedMask)) != 0) { if (log.isWarnEnabled()) { log.warn("FAILURE !!!"); log.warn("Launched command : " + commandline); log.warn(""); log.warn(systemOut.getContent()); } // We throw the exception only if asked for if (throwsFailure) { throw new MojoFailureException("Failure during the " + reportType + " generation that ended with status=" + commandLineResult); } } } catch (CommandLineException e) { throw new MojoExecutionException("Failure during the " + reportType + " generation, executing commandline:\n" + commandline, e); } return commandLineResult; } /** * Generates a command line with the arguments. * * @param executable * @param arguments * @return */ protected Commandline generateCommandLine(File executable, List arguments) { Commandline commandline = new Java5CommandLine(); // We create the work directory if necessary if (!outputDirectory.exists()) { outputDirectory.mkdirs(); } commandline.setWorkingDirectory(outputDirectory.toString()); commandline.setExecutable(executable.toString()); commandline.addArguments(arguments.toArray(EMPTY_STRING_ARRAY)); return commandline; } /** * Exports a resource folder to a subdirectory of the maven build directory.
* The folder is supposed to contain a "context.txt" file that contains the * list of the files to export, one name per line * * @param resourceDir * the resource directory to export * @param destinationSubFolder * the subfolder to use, that will be created under * ${project.build.dir} * @param application * the exported application * @return the generated folder * @throws MojoExecutionException */ protected File extractFolder(String resourceDir, String destinationSubFolder, String application) throws MojoExecutionException { if (!useEmbbededRuntime) { getLog() .warn( "The use of the embedded runtime package is not enabled. Please add the settings 'dotnet.use.embedded.runtime=true'"); throw new MojoExecutionException( "The use of the embedded runtime package is not enabled for " + application); } getLog().debug("Exporting files for " + application); String contentFile = resourceDir + "/" + CONTENT_FILE_NAME; InputStream contentResource = getClassLoader().getResourceAsStream( contentFile); if (contentResource==null) { throw new MojoExecutionException(application + " binaries were not found"); } LineNumberReader reader = new LineNumberReader(new InputStreamReader( contentResource)); String line = null; List contentFiles = new ArrayList(); try { while ((line = reader.readLine()) != null) { contentFiles.add(line.trim()); } } catch (IOException e) { throw new MojoExecutionException("Could not extract the files for " + application, e); } File reportDirectory = getReportDirectory(); File extractFolder = new File(reportDirectory, destinationSubFolder); extractResources(extractFolder, resourceDir, contentFiles, application); return extractFolder; } /** * Extracts the resources to a specified directory. * * @param destinationDirectory * the directory to which the files will be extracted * @param resourceDirectory * the resource directory from which they are extracted * @param resourceNames * the name of the resource files to extract * @param application * the name of the application for debug purpose * @throws MojoExecutionException * if a file is missing or the folder could not be written to */ protected void extractResources(File destinationDirectory, String resourceDirectory, List resourceNames, String application) throws MojoExecutionException { if (!destinationDirectory.exists()) { destinationDirectory.mkdirs(); } for (String resource : resourceNames) { extractResource(destinationDirectory, resourceDirectory + "/" + resource, resource, application); } } /** * Extracts a resource file from the plugin jar to a given destination folder. * * @param exportDirectory * the destination folder * @param resourcePath * the full path to the extracted resource * @param fileName * the name of the file after export (usually the terminal part of * resourcePath) * @param application * the name of the application for debug purpose * @return the exported file * @throws MojoExecutionException * if the resource is not found or the file could no be written */ protected File extractResource(File exportDirectory, String resourcePath, String fileName, String application) throws MojoExecutionException { File exportedFile = new File(exportDirectory, fileName); // First we create the parent folder if necessary // NOTE here that as the resource may be far in the subtree, // the parent is not necessary the export directory File parentDirectory = exportedFile.getParentFile(); if (!parentDirectory.exists()) { getLog().debug("Creating parent export directory : " + parentDirectory); parentDirectory.mkdirs(); } // We delete the file to replace it if (exportedFile.exists()) { exportedFile.delete(); } // Exports the file try { exportedFile.createNewFile(); InputStream fileStream = getClassLoader().getResourceAsStream( resourcePath); OutputStream out = new FileOutputStream(exportedFile); long length = 0; byte buf[] = new byte[1024]; int len; // We write all the bytes by block while ((len = fileStream.read(buf)) > 0) { out.write(buf, 0, len); length += len; } out.close(); fileStream.close(); getLog().debug( "Exported file " + exportedFile + " : " + length + " bytes"); } catch (Exception e) { // Something went wrong... throw new MojoExecutionException("A problem occurred for " + application + " while extracting file " + fileName + " to " + exportedFile, e); } return exportedFile; } /** * Gets a file for a report whose name is given * * @param fileName * the name of the file * @return the file to generate */ protected File getReportFile(String fileName) { File reportDirectory = getReportDirectory(); return new File(reportDirectory, fileName); } /** * Escapes a file if necessary for command generation. * * @param file * the file to escape * @return the escapes file name * */ protected String toCommandPath(File file) { String absolutePath; try { absolutePath = file.getCanonicalPath(); } catch (IOException e) { // We try another way of processing absolutePath = file.getAbsolutePath(); } return absolutePath; } /** * Gets the current class loader * * @return */ protected ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } /** * Gets the report directory to use. * * @return the report directory */ protected File getReportDirectory() { String buildPath = project.getBuild().getDirectory(); File reportDirectory = new File(buildPath); reportDirectory.mkdirs(); return reportDirectory; } /** * Ensures that the execution of the Mojo is allowed.
* The current implementation checks that the execution is launched on a * Windows system and uses at least Java 6.0. This method may be overridden * but should better invoke the super. * * @return true if the execution is allowed * @throws MojoExecutionException */ protected boolean checkExecutionAllowed() throws MojoExecutionException { ensureWindowsSystem(); ensureJavaVersion(); return true; } /** * A consumer for command outputs. * * @author Jose CHILLAN Jul 30, 2009 */ private static final class CommandStreamConsumer implements StreamConsumer { private static final String COMMAND_NOT_FOUND_FRAGMENT = "is not recognized as an internal or external command"; private boolean commandNotFound; private StringBuilder consumedLines = new StringBuilder(); private Log log; private CommandStreamConsumer(Log log) { this.log = log; } /** * Callback each time a line is consumed. * * @param line */ @Override public void consumeLine(String line) { consumedLines.append(line + "\n"); log.info(line); if (line.contains(COMMAND_NOT_FOUND_FRAGMENT)) { commandNotFound = true; } } /** * Checks if the command was not found. * * @return true if the command was not found */ public boolean isCommandNotFound() { return commandNotFound; } /** * Gets the content of the stream. * * @return the stream content */ public CharSequence getContent() { return consumedLines; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy