2021-08-11 11:06:46 +03:00
|
|
|
using System;
|
2021-09-20 08:40:57 +03:00
|
|
|
using System.IO;
|
2016-04-21 16:40:25 +03:00
|
|
|
using System.Text;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
2021-09-20 08:40:57 +03:00
|
|
|
using Microsoft.Build.Utilities;
|
2019-10-14 17:18:46 +03:00
|
|
|
using Xamarin.Utils;
|
|
|
|
|
2022-09-28 17:25:35 +03:00
|
|
|
namespace Xamarin.MacDev {
|
2016-04-21 16:40:25 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Builds a process argument string.
|
|
|
|
/// </summary>
|
2022-09-28 17:25:35 +03:00
|
|
|
public class CommandLineArgumentBuilder {
|
|
|
|
static readonly char [] QuoteSpecials = new char [] { ' ', '\\', '\'', '"', ',', ';' };
|
2016-04-21 16:40:25 +03:00
|
|
|
|
|
|
|
readonly HashSet<string> hash = new HashSet<string> ();
|
|
|
|
readonly StringBuilder builder = new StringBuilder ();
|
|
|
|
|
|
|
|
public string ProcessPath {
|
|
|
|
get; private set;
|
|
|
|
}
|
|
|
|
|
2018-01-08 22:29:52 +03:00
|
|
|
public CommandLineArgumentBuilder ()
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-01-08 22:29:52 +03:00
|
|
|
public CommandLineArgumentBuilder (string processPath)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
ProcessPath = processPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int Length {
|
|
|
|
get { return builder.Length; }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Adds an argument without escaping or quoting.
|
|
|
|
/// </summary>
|
2018-03-05 19:59:53 +03:00
|
|
|
public void Add (string argument, bool appendLine = false)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
2019-12-27 17:15:52 +03:00
|
|
|
if (builder.Length > 0 && !appendLine)
|
2016-04-21 16:40:25 +03:00
|
|
|
builder.Append (' ');
|
|
|
|
|
|
|
|
builder.Append (argument);
|
2018-03-05 19:59:53 +03:00
|
|
|
|
|
|
|
if (appendLine)
|
|
|
|
builder.AppendLine ();
|
|
|
|
|
2016-04-21 16:40:25 +03:00
|
|
|
hash.Add (argument);
|
|
|
|
}
|
|
|
|
|
2021-09-20 08:40:57 +03:00
|
|
|
public void AddQuotedSwitchIfNotNull (string name, string value)
|
|
|
|
{
|
|
|
|
if (value == null)
|
|
|
|
return;
|
|
|
|
AddQuoted (name + value);
|
|
|
|
}
|
|
|
|
|
2018-03-05 19:59:53 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Adds an argument without escaping or quoting and goes to the next line
|
|
|
|
/// </summary>
|
|
|
|
public void AddLine (string argument)
|
|
|
|
{
|
|
|
|
Add (argument, true);
|
|
|
|
}
|
|
|
|
|
2016-04-21 16:40:25 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Adds multiple arguments without escaping or quoting.
|
|
|
|
/// </summary>
|
2022-09-28 17:25:35 +03:00
|
|
|
public void Add (params string [] args)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
foreach (var a in args)
|
|
|
|
Add (a);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Adds a formatted argument, quoting and escaping as necessary.
|
|
|
|
/// </summary>
|
2022-09-28 17:25:35 +03:00
|
|
|
public void AddQuotedFormat (string argumentFormat, params object [] values)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
AddQuoted (string.Format (argumentFormat, values));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void AddQuotedFormat (string argumentFormat, object val0)
|
|
|
|
{
|
|
|
|
AddQuoted (string.Format (argumentFormat, val0));
|
|
|
|
}
|
|
|
|
|
2018-03-05 19:59:53 +03:00
|
|
|
static void AppendQuoted (StringBuilder quoted, string text, bool appendLine = false)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
if (text.IndexOfAny (QuoteSpecials) != -1) {
|
|
|
|
quoted.Append ("\"");
|
|
|
|
|
|
|
|
for (int i = 0; i < text.Length; i++) {
|
2022-09-28 17:25:35 +03:00
|
|
|
if (text [i] == '\\' || text [i] == '"')
|
2016-04-21 16:40:25 +03:00
|
|
|
quoted.Append ('\\');
|
2022-09-28 17:25:35 +03:00
|
|
|
quoted.Append (text [i]);
|
2016-04-21 16:40:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
quoted.Append ("\"");
|
|
|
|
} else {
|
|
|
|
quoted.Append (text);
|
|
|
|
}
|
2018-03-05 19:59:53 +03:00
|
|
|
|
|
|
|
if (appendLine)
|
|
|
|
quoted.AppendLine ();
|
2016-04-21 16:40:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>Adds an argument, quoting and escaping as necessary.</summary>
|
|
|
|
/// <remarks>The .NET process class does not support escaped
|
|
|
|
/// arguments, only quoted arguments with escaped quotes.</remarks>
|
2018-03-05 19:59:53 +03:00
|
|
|
public void AddQuoted (string argument, bool appendLine = false)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
if (argument == null)
|
|
|
|
return;
|
|
|
|
|
2018-03-05 19:59:53 +03:00
|
|
|
if (builder.Length > 0 && !appendLine)
|
2016-04-21 16:40:25 +03:00
|
|
|
builder.Append (' ');
|
|
|
|
|
2018-03-05 19:59:53 +03:00
|
|
|
AppendQuoted (builder, argument, appendLine);
|
2016-04-21 16:40:25 +03:00
|
|
|
hash.Add (argument);
|
|
|
|
}
|
|
|
|
|
2018-03-05 19:59:53 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Adds an argument, quoting, escaping as necessary, and goes to the next line
|
|
|
|
/// </summary>
|
|
|
|
public void AddQuotedLine (string argument)
|
|
|
|
{
|
|
|
|
AddQuoted (argument, true);
|
|
|
|
}
|
|
|
|
|
2016-04-21 16:40:25 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Adds multiple arguments, quoting and escaping each as necessary.
|
|
|
|
/// </summary>
|
2022-09-28 17:25:35 +03:00
|
|
|
public void AddQuoted (params string [] args)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
foreach (var a in args)
|
|
|
|
AddQuoted (a);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Contains the specified argument.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="argument">Argument.</param>
|
|
|
|
public bool Contains (string argument)
|
|
|
|
{
|
|
|
|
return hash.Contains (argument);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>Quotes a string, escaping if necessary.</summary>
|
|
|
|
/// <remarks>The .NET process class does not support escaped
|
|
|
|
/// arguments, only quoted arguments with escaped quotes.</remarks>
|
|
|
|
public static string Quote (string text)
|
|
|
|
{
|
|
|
|
var quoted = new StringBuilder ();
|
|
|
|
|
|
|
|
AppendQuoted (quoted, text);
|
|
|
|
|
|
|
|
return quoted.ToString ();
|
|
|
|
}
|
|
|
|
|
|
|
|
public override string ToString ()
|
|
|
|
{
|
|
|
|
return builder.ToString ();
|
|
|
|
}
|
|
|
|
|
2022-09-28 17:25:35 +03:00
|
|
|
static bool TryParse (string commandline, out string [] argv, out Exception ex)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
2019-10-14 17:18:46 +03:00
|
|
|
return StringUtils.TryParseArguments (commandline, out argv, out ex);
|
2016-04-21 16:40:25 +03:00
|
|
|
}
|
|
|
|
|
2022-09-28 17:25:35 +03:00
|
|
|
public static bool TryParse (string commandline, out string [] argv)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
|
|
|
Exception ex;
|
|
|
|
|
|
|
|
return TryParse (commandline, out argv, out ex);
|
|
|
|
}
|
|
|
|
|
2020-10-14 20:15:53 +03:00
|
|
|
public IList<string> ToList ()
|
|
|
|
{
|
|
|
|
return Parse (ToString ());
|
|
|
|
}
|
|
|
|
|
2022-09-28 17:25:35 +03:00
|
|
|
public static string [] Parse (string commandline)
|
2016-04-21 16:40:25 +03:00
|
|
|
{
|
2022-09-28 17:25:35 +03:00
|
|
|
string [] argv;
|
2016-04-21 16:40:25 +03:00
|
|
|
Exception ex;
|
|
|
|
|
|
|
|
if (!TryParse (commandline, out argv, out ex))
|
|
|
|
throw ex;
|
|
|
|
|
|
|
|
return argv;
|
|
|
|
}
|
2021-09-20 08:40:57 +03:00
|
|
|
|
|
|
|
// Creates a response file for the given arguments, and returns the command line
|
|
|
|
// with the response file and any arguments that can't go into the response file.
|
|
|
|
public string CreateResponseFile (Task task, string responseFilePath, IList<string> nonResponseArguments)
|
|
|
|
{
|
|
|
|
// Generate a response file
|
|
|
|
var responseFile = Path.GetFullPath (responseFilePath);
|
|
|
|
|
|
|
|
if (File.Exists (responseFile))
|
|
|
|
File.Delete (responseFile);
|
|
|
|
|
|
|
|
try {
|
|
|
|
using (var fs = File.Create (responseFile)) {
|
|
|
|
using (var writer = new StreamWriter (fs))
|
|
|
|
writer.Write (this);
|
|
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
|
|
task.Log.LogWarning ("Failed to create response file '{0}': {1}", responseFile, ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some arguments can not safely go in the response file and are
|
|
|
|
// added separately. They must go _after_ the response file
|
|
|
|
// as they may override options passed in the response file
|
|
|
|
var actualArgs = new CommandLineArgumentBuilder ();
|
|
|
|
|
|
|
|
actualArgs.AddQuoted ($"@{responseFile}");
|
|
|
|
|
|
|
|
if (nonResponseArguments != null) {
|
|
|
|
foreach (var arg in nonResponseArguments)
|
|
|
|
actualArgs.AddQuoted (arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the command line
|
|
|
|
return actualArgs.ToString ();
|
|
|
|
}
|
2016-04-21 16:40:25 +03:00
|
|
|
}
|
|
|
|
}
|