jenkins.plugins.coverity.CoverityUtils.java Source code

Java tutorial

Introduction

Here is the source code for jenkins.plugins.coverity.CoverityUtils.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.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.*;
import hudson.EnvVars;
import hudson.model.Queue;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
import hudson.util.ArgumentListBuilder;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.tools.ant.types.Commandline;

import java.io.*;
import java.lang.reflect.Array;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CoverityUtils {

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

    /**
      * Evaluates an environment variable using the specified parser. The result is an interpolated string.
      */
    public static String evaluateEnvVars(String input, EnvVars environment, boolean useAdvancedParser)
            throws RuntimeException {
        try {
            if (useAdvancedParser) {
                String interpolated = EnvParser.interpolateRecursively(input, 1, environment);
                return interpolated;
            } else {
                return environment.expand(input);
            }
        } catch (Exception e) {
            throw new RuntimeException("Error trying to evaluate environment variable: " + input);
        }
    }

    public static void checkDir(VirtualChannel channel, String home) throws Exception {
        Validate.notNull(channel, VirtualChannel.class.getName() + " object can't be null");
        Validate.notNull(home, String.class.getName() + " object can't be null");
        FilePath homePath = new FilePath(channel, home);
        if (!homePath.exists()) {
            throw new Exception("Directory: " + home + " doesn't exist.");
        }
    }

    /**
     * getCovBuild
     *
     * Retrieves the location of cov-build executable/sh from the system and returns the string of the
     * path
     * @return  string of cov-build's path
     */
    public static String getCovBuild(TaskListener listener, Node node) {
        AbstractBuild build = getBuild();
        AbstractProject project = build.getProject();
        CoverityPublisher publisher = (CoverityPublisher) project.getPublishersList().get(CoverityPublisher.class);
        InvocationAssistance invocationAssistance = publisher.getInvocationAssistance();

        if (listener == null) {
            try {
                throw new Exception("Listener used by getCovBuild() is null.");
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        String covBuild = "cov-build";
        String home = null;
        try {
            home = publisher.getDescriptor().getHome(node, build.getEnvironment(listener));
        } catch (Exception e) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            listener.getLogger().println("[Error] " + pw.toString());
            e.printStackTrace();
        }
        if (invocationAssistance != null) {
            if (invocationAssistance.getSaOverride() != null) {
                try {
                    home = new CoverityInstallation(invocationAssistance.getSaOverride())
                            .forEnvironment(build.getEnvironment(listener)).getHome();
                    CoverityUtils.checkDir(node.getChannel(), home);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        if (home != null) {
            covBuild = new FilePath(node.getChannel(), home).child("bin").child(covBuild).getRemote();
        }

        return covBuild;
    }

    /**
     * Calls intepolate() in order to evaluate environment variables. In the case that a substitution took place, it
     * tokenize the result and it calls itself on each token in case further evaluations are needed.
     *
     * In the case of a recursive definition (ex: VAR1=$VAR2, VAR2=$VAR1) an exception is thrown.
     */
    public static List<String> expand(String input, EnvVars environment) throws ParseException {
        /**
         * Interpolates environment
         */
        List<String> output = new ArrayList<>();
        String interpolated = EnvParser.interpolateRecursively(input, 1, environment);
        output.addAll(EnvParser.tokenize(interpolated));
        return output;
    }

    /**
     * Evaluates environment variables on a command represented by a list of tokens.
     */
    public static List<String> evaluateEnvVars(List<String> input, EnvVars environment) {
        List<String> output = new ArrayList<String>();
        try {
            for (String arg : input) {
                output.addAll(expand(arg, environment));
            }
        } catch (ParseException e) {
            throw new RuntimeException(e.getMessage());
        }
        return output;
    }

    /**
     * Gets the stacktrace from an exception, so that this exception can be handled.
     */
    public static String getStackTrace(Exception e) {
        StringWriter writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        e.printStackTrace(printWriter);
        printWriter.flush();
        String stackTrace = writer.toString();
        try {
            writer.close();
            printWriter.close();
        } catch (IOException e1) {
        }
        return stackTrace;
    }

    public static void handleException(String message, AbstractBuild<?, ?> build, BuildListener listener,
            Exception exception) {
        listener.getLogger().println(message);
        listener.getLogger().println("Stacktrace: \n" + CoverityUtils.getStackTrace(exception));
        build.setResult(Result.FAILURE);
    }

    public static void handleException(String message, AbstractBuild<?, ?> build, TaskListener listener,
            Exception exception) {
        listener.getLogger().println(message);
        listener.getLogger().println("Stacktrace: \n" + CoverityUtils.getStackTrace(exception));
        build.setResult(Result.FAILURE);
    }

    /**
     * Prepares command according with the specified parsing mechanism. If "useAdvancedParser" is set to true, the plugin
     * will evaluate environment variables with its custom mechanism. If not, environment variable substitution will be
     * handled by Jenkins in the standard way.
     */
    public static List<String> prepareCmds(List<String> input, String[] envVarsArray, boolean useAdvancedParser) {
        if (useAdvancedParser) {
            EnvVars envVars = new EnvVars(arrayToMap(envVarsArray));
            return CoverityUtils.evaluateEnvVars(input, envVars);
        } else {
            return input;
        }
    }

    public static List<String> prepareCmds(List<String> input, EnvVars envVars, boolean useAdvancedParser) {
        if (useAdvancedParser) {
            return CoverityUtils.evaluateEnvVars(input, envVars);
        } else {
            return input;
        }
    }

    /**
     * Jenkins API ProcStarter.envs() returns an array of environment variables where each element is a string "key=value".
     * However the constructor for EnvVars accepts only arrays of the format [key1, value1, key2, value2]. Because of
     * this, we need to transform that array into a map and use a constructor that accepts that map.
     */
    public static Map<String, String> arrayToMap(String[] input) {
        Map<String, String> result = new HashMap<String, String>();
        for (int i = 0; i < input.length; i++) {
            List<String> keyValuePair = splitKeyValue(input[i]);
            if (keyValuePair != null && !keyValuePair.isEmpty()) {
                String key = keyValuePair.get(0);
                String value = keyValuePair.get(1);
                result.put(key, value);
            }
        }

        return result;
    }

    /**
     * Split string of the form key=value into an array [key, value]
     */
    public static List<String> splitKeyValue(String input) {
        if (input == null) {
            return null;
        }

        List<String> result = new ArrayList<>();

        int index = input.indexOf('=');
        if (index == 0) {
            logger.warning("Could not parse environment variable \"" + input + "\" because its key is empty.");
        } else if (index < 0) {
            logger.warning("Could not parse environment variable \"" + input
                    + "\" because no value for it has been defined.");
        } else if (index == input.length() - 1) {
            logger.warning(
                    "Could not parse environment variable \"" + input + "\" because the value for it is empty.");
        } else {
            String key = input.substring(0, index);
            String value = input.substring(index + 1);
            result.add(key);
            result.add(value);
        }

        return result;
    }

    /**
     * Returns the InvocationAssistance for a given build.
     */
    public static InvocationAssistance getInvocationAssistance(AbstractBuild<?, ?> build) {
        AbstractProject project = build.getProject();
        CoverityPublisher publisher = (CoverityPublisher) project.getPublishersList().get(CoverityPublisher.class);
        return publisher.getInvocationAssistance();
    }

    /**
     * Returns the InvocationAssistance on the current thread. This can be used when an "AbstractBuild" object is not
     * available, for example while decorating the launcher.
     */
    public static InvocationAssistance getInvocationAssistance() {
        AbstractBuild build = getBuild();
        return getInvocationAssistance(build);
    }

    /**
     * Collects environment variables from an array and an EnvVars object and returns an updated EnvVars object.
     * This is useful for updating the environment variables on a ProcStarter with the variables from the listener.
     */
    public static String[] addEnvVars(String[] envVarsArray, EnvVars envVars) {
        // All variables are stored on a map, the ones from ProcStarter will take precedence.
        EnvVars resultMap = new EnvVars(envVars);
        resultMap.putAll(arrayToMap(envVarsArray));

        String[] r = new String[resultMap.size()];
        int idx = 0;

        for (Map.Entry<String, String> e : resultMap.entrySet()) {
            r[idx++] = e.getKey() + '=' + e.getValue();
        }
        return r;
    }

    public static int runCmd(List<String> cmd, AbstractBuild<?, ?> build, Launcher launcher, TaskListener listener,
            EnvVars envVars, boolean useAdvancedParser) throws IOException, InterruptedException {
        /**
         * Get environment variables from a launcher, add custom environment environment variables if needed,
         * then call join() to starts the launcher process.
         */
        String[] launcherEnvVars = launcher.launch().envs();
        launcherEnvVars = CoverityUtils.addEnvVars(launcherEnvVars, envVars);
        cmd = prepareCmds(cmd, launcherEnvVars, useAdvancedParser);
        int result = launcher.launch().cmds(new ArgumentListBuilder(cmd.toArray(new String[cmd.size()])))
                .pwd(build.getWorkspace()).stdout(listener).stderr(listener.getLogger()).envs(launcherEnvVars)
                .join();
        return result;
    }

    public static AbstractBuild getBuild() {
        Executor executor = Executor.currentExecutor();
        Queue.Executable exec = executor.getCurrentExecutable();
        AbstractBuild build = (AbstractBuild) exec;
        return build;
    }

    public static String doubleQuote(String input) {
        return "\"" + input + "\"";
    }

    /**
     * Coverity's parser remove double/single quotes but Jenkins parser does not. When dealing (for instance) with
     * streams with spaces, we would expect [--stream, My Stream]. In order to do this the token "My Stream" must be
     * quoted if using out parser, but not if using Jenkins.
     */
    public static String doubleQuote(String input, boolean useAdvancedParser) {
        if (useAdvancedParser) {
            return "\"" + input + "\"";
        } else {
            return input;
        }
    }

    /**
     * Gets environment variables from the build.
     */
    public static EnvVars getBuildEnvVars(TaskListener listener) {
        AbstractBuild build = CoverityUtils.getBuild();
        EnvVars envVars = null;
        try {
            envVars = build.getEnvironment(listener);
        } catch (Exception e) {
            CoverityUtils.handleException(e.getMessage(), build, listener, e);
        }
        return envVars;
    }

    /**
     * Gets environment variables from the given build
     */
    public static EnvVars getBuildEnvVars(AbstractBuild build, TaskListener listener) {
        EnvVars envVars = null;
        try {
            envVars = build.getEnvironment(listener);
        } catch (Exception e) {
            CoverityUtils.handleException(e.getMessage(), build, listener, e);
        }
        return envVars;
    }

    public static Collection<File> listFiles(File directory, FilenameFilter filter, boolean recurse) {
        Vector<File> files = new Vector<File>();
        File[] entries = directory.listFiles();
        if (entries == null) {
            return files;
        }

        for (File entry : entries) {
            if (filter == null || filter.accept(directory, entry.getName())) {
                files.add(entry);
            }

            if (recurse && entry.isDirectory()) {
                files.addAll(listFiles(entry, filter, recurse));
            }
        }

        return files;
    }

    public static File[] listFilesAsArray(File directory, FilenameFilter filter, boolean recurse) {
        Collection<File> files = listFiles(directory, filter, recurse);

        File[] arr = new File[files.size()];
        return files.toArray(arr);
    }
}