/*******************************************************************************
* Copyright (c) 2009 Thales Corporate Services SAS *
* Author : Gregory Boissinot *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy *
* of this software and associated documentation files (the "Software"), to deal*
* in the Software without restriction, including without limitation the rights *
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *
* copies of the Software, and to permit persons to whom the Software is *
* furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,*
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN *
* THE SOFTWARE. *
*******************************************************************************/
package com.thalesgroup.hudson.plugins.cppcheck;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.FreeStyleProject;
import hudson.model.Result;
import hudson.remoting.VirtualChannel;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Publisher;
import hudson.tasks.Recorder;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
import com.thalesgroup.hudson.plugins.cppcheck.config.CppcheckConfig;
import com.thalesgroup.hudson.plugins.cppcheck.model.CppcheckSourceContainer;
import com.thalesgroup.hudson.plugins.cppcheck.model.CppcheckWorkspaceFile;
import com.thalesgroup.hudson.plugins.cppcheck.util.CppcheckBuildResultEvaluator;
import com.thalesgroup.hudson.plugins.cppcheck.util.CppcheckLogger;
public class CppcheckPublisher extends Recorder {
private CppcheckConfig cppcheckConfig;
@Override
public CppcheckDescriptor getDescriptor() {
return DESCRIPTOR;
}
@Override
public Action getProjectAction(AbstractProject<?, ?> project) {
return new CppcheckProjectAction(project);
}
protected boolean canContinue(final Result result) {
return result != Result.ABORTED && result != Result.FAILURE;
}
public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.BUILD;
}
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
if (this.canContinue(build.getResult())) {
CppcheckLogger.log(listener, "Starting the cppcheck analysis.");
// final FilePath[] moduleRoots = build.getModuleRoots();
// final boolean multipleModuleRoots = moduleRoots != null && moduleRoots.length > 1;
// final FilePath moduleRoot = multipleModuleRoots ? build.getWorkspace() : build.getModuleRoot();
CppcheckParserResult parser = new CppcheckParserResult(listener, cppcheckConfig.getCppcheckReportPattern());
CppcheckReport cppcheckReport;
try {
cppcheckReport = build.getWorkspace().act(parser);
}
catch (Exception e) {
CppcheckLogger.log(listener, "Error on cppcheck analysis: " + e);
build.setResult(Result.FAILURE);
return false;
}
if (cppcheckReport == null) {
build.setResult(Result.FAILURE);
return false;
}
CppcheckSourceContainer cppcheckSourceContainer = new CppcheckSourceContainer(listener, build.getWorkspace(), cppcheckReport.getEverySeverities());
CppcheckResult result = new CppcheckResult(cppcheckReport, cppcheckSourceContainer, build);
Result buildResult = new CppcheckBuildResultEvaluator().evaluateBuildResult(
listener, result.getNumberErrorsAccordingConfiguration(cppcheckConfig, false),
result.getNumberErrorsAccordingConfiguration(cppcheckConfig, true),
cppcheckConfig);
if (buildResult != Result.SUCCESS) {
build.setResult(buildResult);
}
CppcheckBuildAction buildAction = new CppcheckBuildAction(build, result, cppcheckConfig);
build.addAction(buildAction);
if (build.getWorkspace().isRemote()) {
copyFilesFromSlaveToMaster(build.getRootDir(), launcher.getChannel(), cppcheckSourceContainer.getInternalMap().values());
}
CppcheckLogger.log(listener, "End of the cppcheck analysis.");
}
return true;
}
/**
* Copies all the source files from stave to master for a remote build.
*
* @param rootDir directory to store the copied files in
* @param channel channel to get the files from
* @param sourcesFiles the sources files to be copied
* @throws IOException if the files could not be written
* @throws FileNotFoundException if the files could not be written
* @throws InterruptedException if the user cancels the processing
*/
private void copyFilesFromSlaveToMaster(final File rootDir,
final VirtualChannel channel, final Collection<CppcheckWorkspaceFile> sourcesFiles)
throws IOException, InterruptedException {
File directory = new File(rootDir, CppcheckWorkspaceFile.WORKSPACE_FILES);
if (!directory.exists()) {
if (!directory.delete()) {
//do nothing
}
if (!directory.mkdir()) {
throw new IOException("Can't create directory for remote source files: " + directory.getAbsolutePath());
}
}
for (CppcheckWorkspaceFile file : sourcesFiles) {
if (!file.isSourceIgnored()) {
File masterFile = new File(directory, file.getTempName());
if (!masterFile.exists()) {
FileOutputStream outputStream = new FileOutputStream(masterFile);
new FilePath(channel, file.getFileName()).copyTo(outputStream);
}
}
}
}
@Extension
public static final CppcheckDescriptor DESCRIPTOR = new CppcheckDescriptor();
/**
* The Cppcheck Descriptor
*/
public static final class CppcheckDescriptor extends BuildStepDescriptor<Publisher> {
public CppcheckDescriptor() {
super(CppcheckPublisher.class);
load();
}
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return FreeStyleProject.class.isAssignableFrom(jobType) || MatrixProject.class.isAssignableFrom(jobType);
}
@Override
public String getDisplayName() {
return "Publish Cppcheck results";
}
@Override
public final String getHelpFile() {
return getPluginRoot() + "help.html";
}
public String getPluginRoot() {
return "/plugin/cppcheck/";
}
@SuppressWarnings("unused")
public CppcheckConfig getConfig() {
return new CppcheckConfig();
}
@Override
public Publisher newInstance(StaplerRequest req, JSONObject formData)
throws hudson.model.Descriptor.FormException {
CppcheckPublisher pub = new CppcheckPublisher();
CppcheckConfig cppcheckConfig = req.bindJSON(CppcheckConfig.class, formData);
pub.setCppcheckConfig(cppcheckConfig);
return pub;
}
}
@SuppressWarnings("unused")
public CppcheckConfig getCppcheckConfig() {
return cppcheckConfig;
}
public void setCppcheckConfig(CppcheckConfig cppcheckConfig) {
this.cppcheckConfig = cppcheckConfig;
}
}