/*
* .NET tools :: Gendarme 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.gendarme;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
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 Gendarme program.
*/
public class GendarmeRunner { // NOSONAR : can't mock it otherwise
private static final Logger LOG = LoggerFactory.getLogger(GendarmeRunner.class);
private static final String GENDARME_EXECUTABLE = "gendarme.exe";
private static final long MINUTES_TO_MILLISECONDS = 60000;
private static final String EMBEDDED_VERSION = "2.10";
private File gendarmeExecutable;
private VisualStudioProject vsProject;
private GendarmeRunner() {
}
/**
* Creates a new {@link GendarmeRunner} object for the given executable file. If the executable file does not exist, then the embedded one
* will be used.
*
* @param gendarmePath
* the full path of the gendarme install directory. For instance: "C:/Program Files/gendarme-2.10-bin". May be null: in this
* case, the embedded Gendarme executable will be used.
* @param tempFolder
* the temporary folder where the embedded Gendarme executable will be copied if the gendarmePath does not point to a valid
* executable
*/
public static GendarmeRunner create(String gendarmePath, String tempFolder) throws GendarmeException {
GendarmeRunner runner = new GendarmeRunner();
File executable = new File(gendarmePath, GENDARME_EXECUTABLE);
if ( !executable.exists() || !executable.isFile()) {
LOG.info("Gendarme executable not found: '{}'. The embedded version ({}) will be used instead.", executable.getAbsolutePath(),
EMBEDDED_VERSION);
executable = new File(tempFolder, "gendarme-" + EMBEDDED_VERSION + "-bin/" + GENDARME_EXECUTABLE);
if ( !executable.isFile()) {
LOG.info("Extracting Gendarme binaries in {}", tempFolder);
extractGendarmeBinaries(tempFolder);
}
}
runner.gendarmeExecutable = executable;
return runner;
}
protected static void extractGendarmeBinaries(String tempFolder) throws GendarmeException {
try {
URL executableURL = GendarmeRunner.class.getResource("/gendarme-" + EMBEDDED_VERSION + "-bin");
ZipUtils.extractArchiveFolderIntoDirectory(StringUtils.substringBefore(executableURL.getFile(), "!").substring(5), "gendarme-"
+ EMBEDDED_VERSION + "-bin", tempFolder);
} catch (IOException e) {
throw new GendarmeException("Could not extract the embedded Gendarme executable: " + e.getMessage(), e);
}
}
/**
* Creates a pre-configured {@link GendarmeCommandBuilder} that needs to be completed before running the
* {@link #execute(GendarmeCommandBuilder, int)} method.
*
* @param solution
* the solution to analyse
* @param project
* the VS project to analyse
* @return the command to complete.
*/
public GendarmeCommandBuilder createCommandBuilder(VisualStudioSolution solution, VisualStudioProject project) {
this.vsProject = project;
GendarmeCommandBuilder builder = GendarmeCommandBuilder.createBuilder(solution, project);
builder.setExecutable(gendarmeExecutable);
return builder;
}
/**
* Executes the given Gendarme command.
*
* @param gendarmeCommandBuilder
* the gendarmeCommandBuilder
* @param timeoutMinutes
* the timeout for the command
* @throws GendarmeException
* if Gendarme fails to execute
*/
public void execute(GendarmeCommandBuilder gendarmeCommandBuilder, int timeoutMinutes) throws GendarmeException {
LOG.debug("Executing Gendarme program...");
try {
int exitCode = CommandExecutor.create().execute(gendarmeCommandBuilder.toCommand(), timeoutMinutes * MINUTES_TO_MILLISECONDS);
// Gendarme returns 1 when the analysis is successful but contains violations, so 1 is valid
if (exitCode != 0 && exitCode != 1) {
throw new GendarmeException(exitCode);
}
} finally {
cleanupFiles(gendarmeCommandBuilder.getBuildConfigurations());
}
}
protected void cleanupFiles(String buildConfigurations) {
if (vsProject != null && vsProject.isSilverlightProject()) {
// need to remove the Silverlight mscorlib.dll
LOG.debug("Delete Silverlight Mscorlib.dll file");
FileUtils.deleteQuietly(new File(vsProject.getArtifactDirectory(buildConfigurations), "mscorlib.dll"));
}
}
}