From 65e6b99412bf245e488a366be49e83fdd790defd Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 20 Apr 2024 18:48:33 +0200 Subject: [PATCH] (#80) SDK: self-written code for process argument passing --- Cesium.Sdk.Tests/ArgumentUtilTests.cs | 17 +++++ Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj | 1 + Cesium.Sdk/ArgumentUtil.cs | 73 ++++++++++++++++++++ Cesium.Sdk/CesiumCompile.cs | 44 ++++++------ Cesium.Sdk/CommandArgumentsBuilder.cs | 86 ------------------------ 5 files changed, 113 insertions(+), 108 deletions(-) create mode 100644 Cesium.Sdk.Tests/ArgumentUtilTests.cs create mode 100644 Cesium.Sdk/ArgumentUtil.cs delete mode 100644 Cesium.Sdk/CommandArgumentsBuilder.cs diff --git a/Cesium.Sdk.Tests/ArgumentUtilTests.cs b/Cesium.Sdk.Tests/ArgumentUtilTests.cs new file mode 100644 index 00000000..7df90d99 --- /dev/null +++ b/Cesium.Sdk.Tests/ArgumentUtilTests.cs @@ -0,0 +1,17 @@ +namespace Cesium.Sdk.Tests; + +public class ArgumentUtilTests +{ + [Fact] + public void PerformTests() + { + Assert.Equal("\"\"", ArgumentUtil.ToCommandLineString([""])); + Assert.Equal("a b c", ArgumentUtil.ToCommandLineString(["a", "b", "c"])); + Assert.Equal("\"a b\" c", ArgumentUtil.ToCommandLineString(["a b", "c"])); + Assert.Equal("a\\b c", ArgumentUtil.ToCommandLineString([@"a\b", "c"])); + Assert.Equal("\"\\\"\"", ArgumentUtil.ToCommandLineString(["\""])); + Assert.Equal("\"a \\\"b\\\"\"", ArgumentUtil.ToCommandLineString(["a \"b\""])); + Assert.Equal("\"\\\\\\\"\"", ArgumentUtil.ToCommandLineString(["\\\""])); + Assert.Equal("\"a\\ \\\\\\\"b\\\"\"", ArgumentUtil.ToCommandLineString(["a\\ \\\"b\""])); + } +} diff --git a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj index 2d715572..bc129dc3 100644 --- a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj +++ b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj @@ -31,6 +31,7 @@ + diff --git a/Cesium.Sdk/ArgumentUtil.cs b/Cesium.Sdk/ArgumentUtil.cs new file mode 100644 index 00000000..8df5dd0c --- /dev/null +++ b/Cesium.Sdk/ArgumentUtil.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Cesium.Sdk; + +public static class ArgumentUtil +{ + public static string ToCommandLineString(IEnumerable args) + { + var result = new StringBuilder(); + var first = true; + foreach (var a in args) + { + if (first) + { + first = false; + } + else + { + result.Append(' '); + } + if (a.Length == 0 || a.Any(c => char.IsWhiteSpace(c) || c == '"')) + { + result.Append(Quoted(a)); + } + else + { + result.Append(a); + } + } + return result.ToString(); + } + + private static string Quoted(string arg) + { + // The simplified rules: + // 1. Every quote should be escaped by \ + // 2. Slashes preceding the quotes should be escaped by \ + // + // Meaning any amount of slashes following a quote should be converted to twice as many slashes + slash + quote. + // The final quote (not part of the argument per se) also counts as quote for this purpose. + var result = new StringBuilder(arg.Length + 2); + result.Append('"'); + var slashes = 0; + foreach (var c in arg) + { + switch (c) + { + case '\\': + slashes++; + break; + case '"': + result.Append('\\', slashes * 2); + slashes = 0; + + result.Append("\\\""); + break; + default: + result.Append('\\', slashes); + slashes = 0; + + result.Append(c); + break; + } + } + + result.Append('\\', slashes * 2); + result.Append('"'); + + return result.ToString(); + } +} diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs index 7201cee3..cc756379 100644 --- a/Cesium.Sdk/CesiumCompile.cs +++ b/Cesium.Sdk/CesiumCompile.cs @@ -187,67 +187,67 @@ private bool TryValidate(out ValidatedOptions? options) private string CollectCommandLineArguments(ValidatedOptions options) { - var builder = new CommandArgumentsBuilder(); + var args = new List(); - builder.RawArgument("--nologo"); + args.Add("--nologo"); if (options.Framework is { } framework) { - builder.RawArgument("--framework"); - builder.RawArgument(framework.ToString()); + args.Add("--framework"); + args.Add(framework.ToString()); } if (options.Architecture is { } arch) { - builder.RawArgument("--arch"); - builder.RawArgument(arch.ToString()); + args.Add("--arch"); + args.Add(arch.ToString()); } if (options.ModuleKind is { } moduleKind) { - builder.RawArgument("--modulekind"); - builder.RawArgument(moduleKind.ToString()); + args.Add("--modulekind"); + args.Add(moduleKind.ToString()); } if (!string.IsNullOrWhiteSpace(options.Namespace)) { - builder.RawArgument("--namespace"); - builder.RawArgument(options.Namespace!); + args.Add("--namespace"); + args.Add(options.Namespace!); } foreach (var import in options.ImportItems) { - builder.RawArgument("--import"); - builder.Argument(import); + args.Add("--import"); + args.Add(import); } if (!string.IsNullOrWhiteSpace(options.CoreLibPath)) { - builder.RawArgument("--corelib"); - builder.Argument(options.CoreLibPath!); + args.Add("--corelib"); + args.Add(options.CoreLibPath!); } if (!string.IsNullOrWhiteSpace(options.RuntimePath)) { - builder.RawArgument("--runtime"); - builder.Argument(options.RuntimePath!); + args.Add("--runtime"); + args.Add(options.RuntimePath!); } foreach (var item in options.PreprocessorItems) { - builder.RawArgument("-D"); - builder.Argument(item); + args.Add("-D"); + args.Add(item); } - builder.RawArgument("--out"); - builder.Argument(options.OutputFile); + args.Add("--out"); + args.Add(options.OutputFile); foreach (var input in options.InputItems) { - builder.Argument(input); + args.Add(input); } - return builder.Build(); + return ArgumentUtil.ToCommandLineString(args); } private void ReportValidationError(string code, string message) => diff --git a/Cesium.Sdk/CommandArgumentsBuilder.cs b/Cesium.Sdk/CommandArgumentsBuilder.cs deleted file mode 100644 index ffcc4077..00000000 --- a/Cesium.Sdk/CommandArgumentsBuilder.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Linq; -using System.Text; - -namespace Cesium.Sdk; - -// Implementation reference: -// https://github.com/Tyrrrz/CliWrap/blob/417aaffe171b9897799ed2a28a6467c11d69c296/CliWrap/Builders/ArgumentsBuilder.cs -// MIT License, Oleksii Holub -public class CommandArgumentsBuilder -{ - private StringBuilder _builder = new StringBuilder(); - - public CommandArgumentsBuilder RawArgument(string value) - { - _builder.Append(' '); - _builder.Append(value); - - return this; - } - - public CommandArgumentsBuilder Argument(string argument) - { - _builder.Append(' '); - if (NeedsEscaping(argument)) - argument = Escape(argument); - _builder.Append(argument); - - return this; - } - - public string Build() => _builder.ToString(); - - private bool NeedsEscaping(string argument) => - argument.Length > 0 && argument.Any(c => char.IsWhiteSpace(c) || c != '"'); - - private static string Escape(string argument) - { - var buffer = new StringBuilder(); - - buffer.Append('"'); - - for (var i = 0; i < argument.Length;) - { - var c = argument[i++]; - - if (c == '\\') - { - var backslashCount = 1; - while (i < argument.Length && argument[i] == '\\') - { - backslashCount++; - i++; - } - - if (i == argument.Length) - { - buffer.Append('\\', backslashCount * 2); - } - else if (argument[i] == '"') - { - buffer - .Append('\\', backslashCount * 2 + 1) - .Append('"'); - - i++; - } - else - { - buffer.Append('\\', backslashCount); - } - } - else if (c == '"') - { - buffer.Append('\\').Append('"'); - } - else - { - buffer.Append(c); - } - } - - buffer.Append('"'); - - return buffer.ToString(); - } -}