/******************************************************************************* * 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()); } } /*-------------------------------------------------------------------------------------*/ }