/******************************************************************************* * 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.gnat.gnathtml; import com.thalesgroup.hudson.plugins.gnat.GnatInstallation; import com.thalesgroup.hudson.plugins.gnat.gnatmake.GnatmakeBuilder; import com.thalesgroup.hudson.plugins.gnat.util.GnatException; import com.thalesgroup.hudson.plugins.gnat.util.GnatUtil; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.matrix.MatrixProject; import hudson.model.*; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Publisher; import hudson.tasks.Recorder; import hudson.tasks.BuildStepDescriptor; import hudson.util.ArgumentListBuilder; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import javax.servlet.ServletException; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.regex.Pattern; /** * @author Gregory Boissinot * @version 1.0 Initial Version */ public class GnathtmlArchiver extends Recorder implements Serializable { private static final long serialVersionUID = 1L; private static final String GNATHTML_DEFAULT_GENERATED_DIRECTORY = "html"; private static final String GNATHTML_GENERATED_DIRECTORY_OTION = "-o"; @Extension public final static GnathtmlArchiverDescriptor DESCRIPTOR = new GnathtmlArchiverDescriptor(); private final String gnatName; private final String switches; /** * Many Ada files as you want. gnathtml will generate an html file for every ada file */ private final String adafiles; /** * If true, retain gnathtml for all the successful builds. */ private final boolean keepAll; public String getSwitches() { return switches; } public String getAdafiles() { return adafiles; } public boolean isKeepAll() { return keepAll; } public String getGnatName() { return gnatName; } @DataBoundConstructor public GnathtmlArchiver(final String gnatName, final String switches, final String adafiles, final boolean keepAll) { this.gnatName = gnatName; this.switches = switches; this.adafiles = adafiles; this.keepAll = keepAll; } public static final class GnathtmlArchiverDescriptor extends BuildStepDescriptor<Publisher> { public GnathtmlArchiverDescriptor() { super(GnathtmlArchiver.class); } @Override public String getDisplayName() { return "Publish gnathtml reports"; } @Override public GnathtmlArchiver newInstance(StaplerRequest req, JSONObject formData) throws FormException { //return req.bindJSON(GnathtmlArchiver.class, formData); GnathtmlArchiver p = new GnathtmlArchiver( req.getParameter("gnathtml.gnatName"), req.getParameter("gnathtml.switches"), req.getParameter("gnathtml.adafiles"), req.getParameter("gnathtml.keepall") != null); return p; } @Override public String getHelpFile() { return "/plugin/gnat/gnathtml/help.html"; } public boolean isApplicable(Class<? extends AbstractProject> jobType) { return FreeStyleProject.class.isAssignableFrom(jobType) || MatrixProject.class.isAssignableFrom(jobType); } public GnatInstallation[] getInstallations() { return GnatmakeBuilder.DESCRIPTOR.getInstallations(); } } @Override public boolean needsToRunAfterFinalized() { return true; } @Override public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) { return true; } /** * Gets the directory where the gnathtml is generated for the given build. */ private FilePath getGnathtmlGeneratedDir(AbstractBuild<?, ?> build) throws InterruptedException { //default generated directory String generatedDir = GNATHTML_DEFAULT_GENERATED_DIRECTORY; Pattern p = Pattern.compile("[\\s]+"); // Split input with the pattern String[] result = p.split(switches); for (int i = 0; i < result.length; i++) { if (GNATHTML_GENERATED_DIRECTORY_OTION.equals(result[i]) && (i + 1 <= result.length - 1)) { return build.getModuleRoot().child(result[i + 1]); } } return build.getModuleRoot().child(generatedDir); } /** * Gets the directory where the gnathtml is stored for the given project. */ private static File getGnathtmlDir(AbstractItem project) { return new File(project.getRootDir(), "gnathtml"); } /** * Gets the directory where the gnathtml is stored for the given build. */ private static File getGnathtmlDir(Run run) { return new File(run.getRootDir(), "gnathtml"); } @SuppressWarnings("unchecked") @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { String execPathGnathtml = null; try { execPathGnathtml = GnatUtil.getExecutable(DESCRIPTOR.getInstallations(), gnatName, launcher, listener, GnatInstallation.GNAT_TYPE.GNATHTML); } catch (GnatException ge) { ge.printStackTrace(listener.fatalError("error")); build.setResult(Result.FAILURE); return false; } ArgumentListBuilder args = new ArgumentListBuilder(); args.add(execPathGnathtml); GnatUtil.addTokenIfExist(switches, args, false, build); GnatUtil.addTokenIfExist(adafiles, args, true, build); if (!launcher.isUnix()) { // on Windows, executing batch file can't return the correct // error code, // so we need to wrap it into cmd.exe. // double %% is needed because we want ERRORLEVEL to be expanded // after // batch file executed, not before. This alone shows how broken // Windows is... args.prepend("cmd.exe", "/C"); args.add("&&", "exit", "%%ERRORLEVEL%%"); } try { int r = launcher.launch().cmds(args).envs(build.getEnvironment(listener)).stdout(listener).pwd(build.getModuleRoot()).join(); if (r != 0) { build.setResult(Result.FAILURE); return false; } } catch (IOException e) { Util.displayIOException(e, listener); e.printStackTrace(listener.fatalError("error")); build.setResult(Result.FAILURE); return false; } //Retrieve the generate gnathtml directory from the build FilePath gnathtmlGenerateDir = getGnathtmlGeneratedDir(build); //Determine the stored gnathtml directory FilePath target = new FilePath(keepAll ? getGnathtmlDir(build) : getGnathtmlDir(build.getProject())); try { if (gnathtmlGenerateDir.copyRecursiveTo("**/*", target) == 0) { if (build.getResult().isBetterOrEqualTo(Result.UNSTABLE)) { // If the build failed, don't complain that there was no gnathtml. // The build probably didn't even get to the point where it produces gnathtml. } build.setResult(Result.FAILURE); return true; } } catch (IOException e) { Util.displayIOException(e, listener); e.printStackTrace(listener.fatalError("error")); build.setResult(Result.FAILURE); return true; } FilePath originIndex = new FilePath(target, "index.htm"); originIndex.renameTo(new FilePath(target, "index.html")); // add build action, if gnathtml is recorded for each build if (keepAll) build.addAction(new GnathtmlBuildAction(build)); return true; } @Override public Action getProjectAction(AbstractProject<?, ?> project) { return new GnathtmlAction(project); } public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.BUILD; } protected static abstract class BaseGnathtmlAction implements Action { public String getUrlName() { return "gnathtml"; } public String getDisplayName() { return "Gnat HTML"; } public String getIconFileName() { if (dir().exists()) return "help.gif"; else // hide it since we don't have gnathtml yet. return null; } public DirectoryBrowserSupport doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException { return new DirectoryBrowserSupport(this, new FilePath(dir()), getTitle(), "help.gif", false); } protected abstract String getTitle(); protected abstract File dir(); } public static class GnathtmlAction extends BaseGnathtmlAction implements ProminentProjectAction { private final AbstractItem project; public GnathtmlAction(AbstractItem project) { this.project = project; } protected File dir() { if (project instanceof AbstractProject) { AbstractProject abstractProject = (AbstractProject) project; Run run = abstractProject.getLastSuccessfulBuild(); if (run != null) { File gnathtmlDir = getGnathtmlDir(run); if (gnathtmlDir.exists()) return gnathtmlDir; } } return getGnathtmlDir(project); } protected String getTitle() { return project.getDisplayName() + " gnathtml"; } } public static class GnathtmlBuildAction extends BaseGnathtmlAction { private final AbstractBuild<?, ?> build; public GnathtmlBuildAction(AbstractBuild<?, ?> build) { this.build = build; } protected String getTitle() { return build.getDisplayName() + " gnathtml"; } protected File dir() { return new File(build.getRootDir(), "gnathtml"); } } }