/*
* .NET tools :: StyleCop Runner
* Copyright (C) 2010 Jose Chillan, Alexandre Victoor and SonarSource
* dev@sonar.codehaus.org
*
* 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.sonar.dotnet.tools.stylecop;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.utils.command.CommandExecutor;
import org.sonar.dotnet.tools.commons.utils.ZipUtils;
import org.sonar.dotnet.tools.commons.visualstudio.VisualStudioProject;
import org.sonar.dotnet.tools.commons.visualstudio.VisualStudioSolution;
/**
* Class that runs the StyleCop program.
*/
public class StyleCopRunner { // NOSONAR : can't mock it otherwise
private static final Logger LOG = LoggerFactory.getLogger(StyleCopRunner.class);
private static final long MINUTES_TO_MILLISECONDS = 60000;
private static final String EMBEDDED_VERSION = "4.4.0.14";
private File styleCopFolder;
private File dotnetSdkFolder;
/**
* Constructs a {@link StyleCopRunner}.
*
* @param configuration
* StyleCop configuration elements
* @param fileSystem
* the file system of the project
*/
private StyleCopRunner() {
}
/**
* Creates a new {@link StyleCopRunner} object for the given executable file. If the executable file does not exist, then the embedded one
* will be used.
*
* @param styleCopPath
* the full path of the StyleCop install directory. For instance: "C:/Program Files/Microsoft StyleCop 4.4.0.14". May be null: in
* this case, the embedded StyleCop executable will be used.
* @param tempFolder
* the temporary folder where the embedded StyleCop executable will be copied if the styleCopPath does not point to a valid
* executable
*/
public static StyleCopRunner create(String styleCopPath, String dotnetSdkPath, String tempFolder) throws StyleCopException {
StyleCopRunner runner = new StyleCopRunner();
File dotnetSdkDir = new File(dotnetSdkPath);
runner.dotnetSdkFolder = dotnetSdkDir;
File styleCopDir = new File(styleCopPath);
if ( !styleCopDir.exists() || !styleCopDir.isDirectory()) {
LOG.info("StyleCop install folder not found: '{}'. The embedded version ({}) will be used instead.", styleCopDir.getAbsolutePath(),
EMBEDDED_VERSION);
styleCopDir = new File(tempFolder, "StyleCop-" + EMBEDDED_VERSION);
if ( !styleCopDir.isDirectory()) {
LOG.info("Extracting StyleCop binaries in {}", tempFolder);
extractStyleCopBinaries(tempFolder);
}
}
runner.styleCopFolder = styleCopDir;
return runner;
}
protected static void extractStyleCopBinaries(String tempFolder) throws StyleCopException {
try {
URL executableURL = StyleCopRunner.class.getResource("/StyleCop-" + EMBEDDED_VERSION);
ZipUtils.extractArchiveFolderIntoDirectory(StringUtils.substringBefore(executableURL.getFile(), "!").substring(5), "StyleCop-"
+ EMBEDDED_VERSION, tempFolder);
} catch (IOException e) {
throw new StyleCopException("Could not extract the embedded StyleCop executable: " + e.getMessage(), e);
}
}
/**
* Creates a pre-configured {@link StyleCopCommandBuilder} that needs to be completed before running the
* {@link #execute(StyleCopCommandBuilder, int)} method.
*
* @param solution
* the solution to analyse
* @return the command to complete.
*/
public StyleCopCommandBuilder createCommandBuilder(VisualStudioSolution solution) {
StyleCopCommandBuilder builder = StyleCopCommandBuilder.createBuilder(solution);
builder.setDotnetSdkDirectory(dotnetSdkFolder);
builder.setStyleCopFolder(styleCopFolder);
return builder;
}
/**
* Creates a pre-configured {@link StyleCopCommandBuilder} that needs to be completed before running the
* {@link #execute(StyleCopCommandBuilder, int)} method.
*
* @param solution
* the solution that contains the VS project
* @param project
* the VS project to analyse
* @return the command to complete.
*/
public StyleCopCommandBuilder createCommandBuilder(VisualStudioSolution solution, VisualStudioProject project) {
StyleCopCommandBuilder builder = StyleCopCommandBuilder.createBuilder(solution, project);
builder.setDotnetSdkDirectory(dotnetSdkFolder);
builder.setStyleCopFolder(styleCopFolder);
return builder;
}
/**
* Executes the given StyleCop command.
*
* @param styleCopCommandBuilder
* the styleCopCommandBuilder
* @param timeoutMinutes
* the timeout for the command
* @throws StyleCopException
* if StyleCop fails to execute
*/
public void execute(StyleCopCommandBuilder styleCopCommandBuilder, int timeoutMinutes) throws StyleCopException {
LOG.debug("Executing StyleCop program...");
int exitCode = CommandExecutor.create().execute(styleCopCommandBuilder.toCommand(), timeoutMinutes * MINUTES_TO_MILLISECONDS);
if (exitCode != 0) {
throw new StyleCopException("StyleCop execution failed with return code '" + exitCode
+ "'. Check StyleCop documentation for more information.");
}
}
}