com.buildml.main.commands.CliCommandScanBuild.java Source code

Java tutorial

Introduction

Here is the source code for com.buildml.main.commands.CliCommandScanBuild.java

Source

/*******************************************************************************
 * Copyright (c) 2012 Arapiki Solutions 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:
 *    "Peter Smith <psmith@arapiki.com>" - initial API and 
 *        implementation and/or initial documentation
 *******************************************************************************/

package com.buildml.main.commands;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;

import com.buildml.main.CliUtils;
import com.buildml.main.ICliCommand;
import com.buildml.model.IBuildStore;
import com.buildml.scanner.FatalBuildScannerError;
import com.buildml.scanner.legacy.LegacyBuildScanner;

/**
 * BuildML CLI Command class that implements the "scan-build" command.
 * 
 * @author "Peter Smith <psmith@arapiki.com>"
 */
public class CliCommandScanBuild implements ICliCommand {

    /** If not-null, the name of the trace file to write/read. */
    private String traceFileName = null;

    /** Set if the user specified --trace-only. */
    private boolean optionTraceOnly = false;

    /** Set if the user specified --read-trace. */
    private boolean optionReadTrace = false;

    /** Set if the user specified -c, to pass the single argument through a shell. */
    private boolean optionUseShell = false;

    /** Set if the user specified --trace-level=. */
    private int optionDebugLevel = 0;

    /** The name of the log file to send debug information to */
    private String logFileName = null;

    /*=====================================================================================*
     * PUBLIC METHODS
     *=====================================================================================*/

    /* (non-Javadoc)
     * @see com.buildml.main.ICliCommand#getLongDescription()
     */
    @Override
    public String getLongDescription() {
        return CliUtils.genLocalizedMessage("#include commands/scan-build.txt");
    }

    /*-------------------------------------------------------------------------------------*/

    /* (non-Javadoc)
     * @see com.buildml.main.ICliCommand#getName()
     */
    @Override
    public String getName() {
        return "scan-build";
    }

    /*-------------------------------------------------------------------------------------*/

    /* (non-Javadoc)
     * @see com.buildml.main.ICliCommand#getOptions()
     */
    @Override
    public Options getOptions() {
        Options opts = new Options();

        /* add the --trace-file option */
        Option traceFileOpt = new Option("f", "trace-file", true,
                "Specify the name of the trace file to write/read.");
        traceFileOpt.setArgName("file-name");
        opts.addOption(traceFileOpt);

        /* add the --trace-only option */
        Option traceOnlyOpt = new Option("t", "trace-only", false,
                "Trace the shell command, but don't create a database.");
        opts.addOption(traceOnlyOpt);

        /* add the --read-trace option */
        Option readTraceOpt = new Option("r", "read-trace", false,
                "Read an existing trace file, creating a new database.");
        opts.addOption(readTraceOpt);

        /* add the --debug-level option */
        Option dumpTraceOpt = new Option("d", "debug-level", true,
                "Debug level (0 = none, 1 = brief, 2 = detailed).");
        opts.addOption(dumpTraceOpt);

        /* add the -c option */
        Option useShellOpt = new Option("c", "command-string", false,
                "Execute the quoted string as the whole command line.");
        opts.addOption(useShellOpt);

        /* add the --log-file option */
        Option logFileOpt = new Option("l", "log-file", true,
                "File for capturing debug information (default: cfs.log).");
        logFileOpt.setArgName("file-name");
        opts.addOption(logFileOpt);

        return opts;
    }

    /*-------------------------------------------------------------------------------------*/

    /* (non-Javadoc)
     * @see com.buildml.main.ICliCommand#getParameterDescription()
     */
    @Override
    public String getParameterDescription() {
        return "<build-command> <args> ...";
    }

    /*-------------------------------------------------------------------------------------*/

    /* (non-Javadoc)
     * @see com.buildml.main.ICliCommand#getShortDescription()
     */
    @Override
    public String getShortDescription() {
        return "Scan a legacy shell-based build command.";
    }

    /*-------------------------------------------------------------------------------------*/

    /* (non-Javadoc)
     * @see com.buildml.main.ICliCommand#processOptions(org.apache.commons.cli.CommandLine)
     */
    @Override
    public void processOptions(IBuildStore buildStore, CommandLine cmdLine) {

        /* fetch the option values */
        optionTraceOnly = cmdLine.hasOption("trace-only");
        optionReadTrace = cmdLine.hasOption("read-trace");
        optionUseShell = cmdLine.hasOption("command-string");
        traceFileName = cmdLine.getOptionValue("trace-file");
        logFileName = cmdLine.getOptionValue("log-file");

        /*
         * We can't specify both --trace-only and --read-trace
         */
        if (optionTraceOnly && optionReadTrace) {
            CliUtils.reportErrorAndExit("Options --trace-only and --read-trace can't be used together.");
        }

        if (cmdLine.hasOption("debug-level")) {
            String level = cmdLine.getOptionValue("debug-level");
            if (level.equals("0")) {
                optionDebugLevel = 0; /* disable debugging - the default */
            } else if (level.equals("1")) {
                optionDebugLevel = 1; /* basic debugging output */
            } else if (level.equals("2")) {
                optionDebugLevel = 2; /* extended debugging output */
            } else {
                CliUtils.reportErrorAndExit("Invalid argument to --debug-level: " + level + ".");
            }
        }

    }

    /*-------------------------------------------------------------------------------------*/

    /* (non-Javadoc)
     * @see com.buildml.main.ICliCommand#invoke(com.buildml.model.BuildStore, java.lang.String[])
     */
    @Override
    public void invoke(IBuildStore buildStore, String buildStorePath, String[] args) {

        CliUtils.validateArgs(getName(), args, 1, CliUtils.ARGS_INFINITE,
                "A shell command and arguments must be specified.");

        /*
         * Create a LegacyBuildScanner object, which can execute the shell command, 
         * generate a trace file, and create a BuildStore.
         */
        LegacyBuildScanner lbs = new LegacyBuildScanner();

        /* set the trace file (if this is null, the default will be used */
        lbs.setTraceFile(traceFileName);

        /* set the log file (if this is null, the default will be used */
        lbs.setLogFile(logFileName);

        /* 
         * Possibly dump the content of the trace file to the stdout. This depends on whether
         * --debug-level was specified.
         */
        if (optionDebugLevel != 0) {
            lbs.setDebugLevel(optionDebugLevel);
            System.err.println("Debug output will be recorded in cfs.log.");
        }

        /*
         * Should we populate a BuildStore, based on the content of the trace file? Do so, unless
         * --trace-only was specified.
         */
        if (!optionTraceOnly) {
            lbs.setBuildStore(buildStore);
        }

        /* if -c is specified, only a single command argument is allowed (the quoted string) */
        if (optionUseShell && args.length != 1) {
            CliUtils.reportErrorAndExit("When using -c, only a single (quoted) argument is permitted.");
        }

        /*
         * Invoke the shell commands via "cfs", and collect the trace output. However, skip this
         * step if the user selected --read-trace (which uses an existing trace file)
         */
        try {
            if (!optionReadTrace) {
                try {
                    /* invoke the shell commands, and construct the trace file */
                    lbs.traceShellCommand(args, null, System.out, optionUseShell);

                } catch (Exception e) {
                    CliUtils.reportErrorAndExit("The shell command returned a non-zero exit code."
                            + " The legacy build process will not be scanned.");
                }
            }

            /* 
             * Now create the BuildStore and/or display debug trace information (depending on 
             * what the user has chosen to do). If they want to dump the trace file, or if they
             * didn't select --trace-only, parse the trace file.
             */
            if ((optionDebugLevel != 0) || !optionTraceOnly) {
                lbs.parseTraceFile();
            }

        } catch (FatalBuildScannerError e) {
            System.err.println(e.getMessage());
        }
    }

    /*-------------------------------------------------------------------------------------*/
}