org.apache.maven.dotnet.AbstractDotNetMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of maven-dotnet-plugin Show documentation
Show all versions of maven-dotnet-plugin Show documentation
A plugin that provides general build and test facilities for .Net projects and solutions
/*
* 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