jenkins.plugins.coverity.CoverityLauncher.java Source code

Java tutorial

Introduction

Here is the source code for jenkins.plugins.coverity.CoverityLauncher.java

Source

/*******************************************************************************
 * Copyright (c) 2017 Synopsys, Inc
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Synopsys, Inc - initial implementation and documentation
 *******************************************************************************/
package jenkins.plugins.coverity;

import hudson.EnvVars;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Proc;
import hudson.model.*;
import hudson.remoting.Channel;
import hudson.tasks.Builder;
import jenkins.plugins.coverity.CoverityTool.CovBuildCompileCommand;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 * A decorated {@link Launcher} that puts the given set of arguments as a prefix to any commands that it invokes.
 */
public class CoverityLauncher extends Launcher {

    private static final Logger logger = Logger.getLogger(CoverityLauncherDecorator.class.getName());

    private final Launcher decorated;
    private String[] prefix;
    private final String toolsDir;
    private final Node node;
    private EnvVars envVars;

    public CoverityLauncher(Launcher decorated, Node node) {
        super(decorated);
        this.decorated = decorated;
        this.node = node;
        this.toolsDir = node.getRootPath().child("tools").getRemote();
    }

    private String[] getPrefix() {
        String[] tp = prefix.clone();
        if (tp.length > 0) {
            tp[0] = CoverityUtils.getCovBuild(decorated.getListener(), node);
        }
        return tp;
    }

    @Override
    public Proc launch(ProcStarter starter) throws IOException {
        EnvVars buildEnvVars = CoverityUtils.getBuildEnvVars(listener);
        if (envVars == null || envVars.isEmpty()) {
            envVars = buildEnvVars;
        } else if (buildEnvVars != null) {
            envVars.overrideAll(buildEnvVars);
        }

        AbstractBuild build = CoverityUtils.getBuild();
        AbstractProject project = build.getProject();

        CoverityPublisher publisher = (CoverityPublisher) project.getPublishersList().get(CoverityPublisher.class);

        // Setup(or resolve) intermediate directory
        setupIntermediateDirectory(build, this.getListener(), node);

        // Any Coverity Post-build action such as cov-analyze, cov-import-scm, etc will not be wrapped
        // with the cov-build.
        if (CoverityLauncherDecorator.CoverityPostBuildAction.get()) {
            String[] starterEnvVars = starter.envs();
            starterEnvVars = CoverityUtils.addEnvVars(starterEnvVars, envVars);
            starter = starter.envs(starterEnvVars);
            return decorated.launch(starter);
        }

        boolean isCoverityBuildStepEnabled = false;

        // Check if there are any Coverity Build Step configured.
        // This is required to support backward compatibility.
        if (project instanceof Project) {
            List<Builder> builders = ((Project) project).getBuilders();
            for (Builder buildStep : builders) {
                if (buildStep.getDescriptor() instanceof CoverityBuildStep.CoverityBuildStepDescriptor) {
                    isCoverityBuildStepEnabled = true;
                    break;
                }
            }
        }

        // The first condition is for the case where there are no coverity build steps.
        // Then we want to wrap the build steps with cov-build. This is to support backward compatibility
        // The second condition is for the case where there are coverity build step and
        // the current build step is the coverity build step.
        if (!isCoverityBuildStepEnabled
                || (isCoverityBuildStepEnabled && CoverityLauncherDecorator.CoverityBuildStep.get())) {

            InvocationAssistance invocationAssistance = CoverityUtils.getInvocationAssistance();
            String home = publisher.getDescriptor().getHome(node, envVars);
            if (invocationAssistance != null && invocationAssistance.getSaOverride() != null) {
                home = new CoverityInstallation(CoverityUtils.evaluateEnvVars(invocationAssistance.getSaOverride(),
                        envVars, invocationAssistance.getUseAdvancedParser())).forEnvironment(envVars).getHome();
            }

            List<String> cmds = starter.cmds();
            if (invocationAssistance != null) {
                List<String> args = new CovBuildCompileCommand(build, decorated, decorated.getListener(), publisher,
                        home, envVars).constructArguments();
                prefix = args.toArray(new String[args.size()]);
                cmds.addAll(0, args);
            } else {
                prefix = new String[0];
            }

            boolean useAdvancedParser = false;

            if (invocationAssistance != null && invocationAssistance.getUseAdvancedParser()) {
                useAdvancedParser = true;
            }

            //skip jdk installations
            String lastArg = cmds.get(cmds.size() - 1);
            if (lastArg.startsWith(toolsDir) && lastArg.endsWith(".sh")) {
                logger.info(lastArg + " is a tools script, skipping cov-build");
                return decorated.launch(starter);
            }

            /**
             * Overrides environment variables on the ProcStarter.
             *
             * First, get the environment variables from the ProcStarter. Then, add more environment variables to it.
             * Finally, creates a new ProcStarter object identically to the first one but with overridden variables.
             */
            String[] starterEnvVars = starter.envs();
            starterEnvVars = CoverityUtils.addEnvVars(starterEnvVars, envVars);
            starter = starter.envs(starterEnvVars);

            cmds = CoverityUtils.prepareCmds(cmds, envVars, useAdvancedParser);

            starter = starter.cmds(cmds);
            boolean[] masks = starter.masks();
            if (masks == null) {
                masks = new boolean[cmds.size()];
                starter = starter.masks(masks);
            } else {
                starter = starter.masks(prefix(masks));
            }

            CoverityLauncherDecorator.CoverityBuildStep.set(false);
        }

        return decorated.launch(starter);
    }

    @Override
    public Channel launchChannel(String[] cmd, OutputStream out, FilePath workDir, Map<String, String> envVars)
            throws IOException, InterruptedException {
        String lastArg = cmd[cmd.length - 1];
        if (lastArg.startsWith(toolsDir) && lastArg.endsWith(".sh")) {
            logger.info(lastArg + " is a tools script, skipping cov-build");
            decorated.launchChannel(cmd, out, workDir, envVars);
        }
        return decorated.launchChannel(prefix(cmd), out, workDir, envVars);
    }

    /*
     * When running remotely, overriding this method makes sure that the platform is detected correctly.
     */
    @Override
    public boolean isUnix() {
        return decorated.isUnix();
    }

    @Override
    public void kill(Map<String, String> modelEnvVars) throws IOException, InterruptedException {
        decorated.kill(modelEnvVars);
    }

    private String[] prefix(String[] args) {
        String[] newArgs = new String[args.length + prefix.length];
        System.arraycopy(getPrefix(), 0, newArgs, 0, prefix.length);
        System.arraycopy(args, 0, newArgs, prefix.length, args.length);
        return newArgs;
    }

    private boolean[] prefix(boolean[] args) {
        boolean[] newArgs = new boolean[args.length + prefix.length];
        System.arraycopy(args, 0, newArgs, prefix.length, args.length);
        return newArgs;
    }

    /**
     * Resolves environment variables on the specified intermediate directory. If the result is a null object or the
     * path is empty an exception is thrown.
     */
    public FilePath resolveIntermediateDirectory(AbstractBuild<?, ?> build, TaskListener listener, Node node,
            String idirInput) {
        FilePath idirFilePath = null;
        try {
            String idir = EnvParser.interpolateRecursively(idirInput, 1, envVars);
            if (idir == null || idir.isEmpty()) {
                throw new Exception("The specified Intermediate Directory is not valid: " + idirInput);
            }
            idirFilePath = new FilePath(node.getChannel(), idir);
        } catch (ParseException e) {
            CoverityUtils.handleException(e.getMessage(), build, listener, e);
        } catch (Exception e) {
            CoverityUtils.handleException("An error occured while setting intermediate directory: " + idirInput,
                    build, listener, e);
        }
        return idirFilePath;
    }

    /**
     * Sets the value of environment variable "COV_IDIR" and creates necessary directories. This variable is used as argument of "--dir" for
     * cov-build.
     *
     * If the environment variable has already being set then that value is used. Otherwise, the value of
     * this variable is set to either a temporary directory or the idir specified on the UI under the
     * Intermediate Directory option.
     *
     * Notice this variable must be resolved before running cov-build. Also this method creates necessary directories.
     */
    public void setupIntermediateDirectory(@Nonnull AbstractBuild<?, ?> build, @Nonnull TaskListener listener,
            @Nonnull Node node) {
        Validate.notNull(build, AbstractBuild.class.getName() + " object can't be null");
        Validate.notNull(listener, TaskListener.class.getName() + " object can't be null");
        Validate.notNull(node, Node.class.getName() + " object can't be null");
        Validate.notNull(envVars, EnvVars.class.getName() + " object can't be null");
        if (!envVars.containsKey("COV_IDIR")) {
            FilePath temp;
            InvocationAssistance invocationAssistance = CoverityUtils.getInvocationAssistance(build);
            try {
                if (invocationAssistance == null || invocationAssistance.getIntermediateDir() == null
                        || invocationAssistance.getIntermediateDir().isEmpty()) {
                    FilePath coverityDir = node.getRootPath().child("coverity");
                    coverityDir.mkdirs();
                    temp = coverityDir.createTempDir("temp-", null);
                } else {
                    // Gets a not null nor empty intermediate directory.
                    temp = resolveIntermediateDirectory(build, listener, node,
                            invocationAssistance.getIntermediateDir());
                    if (temp != null) {
                        File idir = new File(temp.getRemote());
                        String workspace = envVars.get("WORKSPACE");
                        if (idir != null && !idir.isAbsolute() && !StringUtils.isEmpty(workspace)) {
                            String path = FilenameUtils.concat(workspace, temp.getRemote());
                            if (!StringUtils.isEmpty(path)) {
                                temp = new FilePath(temp.getChannel(), path);
                            }
                        }
                        temp.mkdirs();
                    }
                }

                if (invocationAssistance != null) {
                    build.addAction(new CoverityTempDir(temp, invocationAssistance.getIntermediateDir() == null));
                } else {
                    build.addAction(new CoverityTempDir(temp, true));
                }
            } catch (IOException e) {
                throw new RuntimeException("Error while creating temporary directory for Coverity", e);
            } catch (InterruptedException e) {
                throw new RuntimeException("Interrupted while creating temporary directory for Coverity");
            }
            if (temp != null) {
                envVars.put("COV_IDIR", temp.getRemote());
            }
        }
    }
}