mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Since we are now able to trace shared compilation builds on Linux and macOS (starting from .NET Core 3), and always were able to on Windows, there is no need to set `UseSharedCompilation=false` in those cases. This may have a positive performance impact, as shared compilation is generally faster then non-shared compilation.
199 lines
6.6 KiB
C#
199 lines
6.6 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace Semmle.Autobuild
|
|
{
|
|
/// <summary>
|
|
/// Utility to construct a build command.
|
|
/// </summary>
|
|
class CommandBuilder
|
|
{
|
|
enum EscapeMode { Process, Cmd };
|
|
|
|
readonly StringBuilder arguments;
|
|
bool firstCommand;
|
|
string executable;
|
|
readonly EscapeMode escapingMode;
|
|
readonly string workingDirectory;
|
|
readonly IDictionary<string, string> environment;
|
|
readonly bool silent;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="T:Semmle.Autobuild.CommandBuilder"/> class.
|
|
/// </summary>
|
|
/// <param name="workingDirectory">The working directory (<code>null</code> for current directory).</param>
|
|
/// <param name="environment">Additional environment variables.</param>
|
|
/// <param name="silent">Whether this command should be run silently.</param>
|
|
public CommandBuilder(IBuildActions actions, string workingDirectory = null, IDictionary<string, string> environment = null, bool silent = false)
|
|
{
|
|
arguments = new StringBuilder();
|
|
if (actions.IsWindows())
|
|
{
|
|
executable = "cmd.exe";
|
|
arguments.Append("/C");
|
|
escapingMode = EscapeMode.Cmd;
|
|
}
|
|
else
|
|
{
|
|
escapingMode = EscapeMode.Process;
|
|
}
|
|
|
|
firstCommand = true;
|
|
this.workingDirectory = workingDirectory;
|
|
this.environment = environment;
|
|
this.silent = silent;
|
|
}
|
|
|
|
void OdasaIndex(string odasa)
|
|
{
|
|
RunCommand(odasa, "index --auto");
|
|
}
|
|
|
|
public CommandBuilder CallBatFile(string batFile, string argumentsOpt = null)
|
|
{
|
|
NextCommand();
|
|
arguments.Append(" CALL");
|
|
QuoteArgument(batFile);
|
|
Argument(argumentsOpt);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Perform odasa index on a given command or BAT file.
|
|
/// </summary>
|
|
/// <param name="odasa">The odasa executable.</param>
|
|
/// <param name="command">The command to run.</param>
|
|
/// <param name="argumentsOpt">Additional arguments.</param>
|
|
/// <returns>this for chaining calls.</returns>
|
|
public CommandBuilder IndexCommand(string odasa, string command, string argumentsOpt = null)
|
|
{
|
|
OdasaIndex(odasa);
|
|
QuoteArgument(command);
|
|
Argument(argumentsOpt);
|
|
return this;
|
|
}
|
|
|
|
static readonly char[] specialChars = { ' ', '\t', '\n', '\v', '\"' };
|
|
static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|' };
|
|
|
|
/// <summary>
|
|
/// Appends the given argument to the command line.
|
|
/// </summary>
|
|
/// <param name="argument">The argument to append.</param>
|
|
/// <param name="force">Whether to always quote the argument.</param>
|
|
/// <param name="cmd">Whether to escape for cmd.exe</param>
|
|
///
|
|
/// <remarks>
|
|
/// This implementation is copied from
|
|
/// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
|
|
/// </remarks>
|
|
void ArgvQuote(string argument, bool force)
|
|
{
|
|
bool cmd = escapingMode == EscapeMode.Cmd;
|
|
if (!force &&
|
|
!string.IsNullOrEmpty(argument) &&
|
|
argument.IndexOfAny(specialChars) == -1)
|
|
{
|
|
arguments.Append(argument);
|
|
}
|
|
else
|
|
{
|
|
if (cmd) arguments.Append('^');
|
|
arguments.Append('\"');
|
|
for (int it = 0; ; ++it)
|
|
{
|
|
var numBackslashes = 0;
|
|
while (it != argument.Length && argument[it] == '\\')
|
|
{
|
|
++it;
|
|
++numBackslashes;
|
|
}
|
|
|
|
if (it == argument.Length)
|
|
{
|
|
arguments.Append('\\', numBackslashes * 2);
|
|
break;
|
|
}
|
|
else if (argument[it] == '\"')
|
|
{
|
|
arguments.Append('\\', numBackslashes * 2 + 1);
|
|
if (cmd) arguments.Append('^');
|
|
arguments.Append(arguments[it]);
|
|
}
|
|
else
|
|
{
|
|
arguments.Append('\\', numBackslashes);
|
|
if (cmd && cmdMetacharacter.Any(c => c == argument[it]))
|
|
arguments.Append('^');
|
|
|
|
arguments.Append(argument[it]);
|
|
}
|
|
}
|
|
if (cmd) arguments.Append('^');
|
|
arguments.Append('\"');
|
|
}
|
|
}
|
|
|
|
public CommandBuilder QuoteArgument(string argumentsOpt)
|
|
{
|
|
if (argumentsOpt != null)
|
|
{
|
|
NextArgument();
|
|
ArgvQuote(argumentsOpt, false);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void NextArgument()
|
|
{
|
|
if (arguments.Length > 0)
|
|
arguments.Append(' ');
|
|
}
|
|
|
|
public CommandBuilder Argument(string argumentsOpt)
|
|
{
|
|
if (argumentsOpt != null)
|
|
{
|
|
NextArgument();
|
|
arguments.Append(argumentsOpt);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void NextCommand()
|
|
{
|
|
if (firstCommand)
|
|
firstCommand = false;
|
|
else
|
|
arguments.Append(" &&");
|
|
}
|
|
|
|
public CommandBuilder RunCommand(string exe, string argumentsOpt = null)
|
|
{
|
|
var (exe0, arg0) =
|
|
escapingMode == EscapeMode.Process && exe.EndsWith(".exe", System.StringComparison.Ordinal)
|
|
? ("mono", exe) // Linux
|
|
: (exe, null);
|
|
|
|
NextCommand();
|
|
if (executable == null)
|
|
{
|
|
executable = exe0;
|
|
}
|
|
else
|
|
{
|
|
QuoteArgument(exe0);
|
|
}
|
|
Argument(arg0);
|
|
Argument(argumentsOpt);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a build script that contains just this command.
|
|
/// </summary>
|
|
public BuildScript Script => BuildScript.Create(executable, arguments.ToString(), silent, workingDirectory, environment);
|
|
}
|
|
}
|