Split arguments for the build server properly

This commit is contained in:
Ajay Bhargav Baaskaran 2018-02-05 20:04:50 -08:00
parent 84beb5985f
commit ba7f955afa
7 changed files with 30 additions and 166 deletions

View File

@ -168,11 +168,10 @@ namespace Microsoft.AspNetCore.Razor.Tasks
return workingDirectory;
}
private List<string> GetArguments(string responseFileCommands)
private IList<string> GetArguments(string responseFileCommands)
{
var responseFileArguments =
CommandLineUtilities.SplitCommandLineIntoArguments(responseFileCommands, removeHashComments: true);
return responseFileArguments.ToList();
var list = responseFileCommands.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
return list;
}
protected override bool HandleTaskExecutionErrors()

View File

@ -22,9 +22,6 @@
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\Roslyn\PlatformInformation.cs">
<Link>Shared\PlatformInformation.cs</Link>
</Compile>
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\Roslyn\CommandLineUtilities.cs">
<Link>Shared\CommandLineUtilities.cs</Link>
</Compile>
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\ServerProtocol\*.cs">
<Link>Shared\ServerProtocol\%(FileName)</Link>
</Compile>

View File

@ -1,154 +0,0 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Text;
namespace Roslyn.Utilities
{
/*
* Copied from https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/InternalUtilities/CommandLineUtilities.cs
*/
internal static class CommandLineUtilities
{
/// <summary>
/// Split a command line by the same rules as Main would get the commands except the original
/// state of backslashes and quotes are preserved. For example in normal Windows command line
/// parsing the following command lines would produce equivalent Main arguments:
///
/// - /r:a,b
/// - /r:"a,b"
///
/// This method will differ as the latter will have the quotes preserved. The only case where
/// quotes are removed is when the entire argument is surrounded by quotes without any inner
/// quotes.
/// </summary>
/// <remarks>
/// Rules for command line parsing, according to MSDN:
///
/// Arguments are delimited by white space, which is either a space or a tab.
///
/// A string surrounded by double quotation marks ("string") is interpreted
/// as a single argument, regardless of white space contained within.
/// A quoted string can be embedded in an argument.
///
/// A double quotation mark preceded by a backslash (\") is interpreted as a
/// literal double quotation mark character (").
///
/// Backslashes are interpreted literally, unless they immediately precede a
/// double quotation mark.
///
/// If an even number of backslashes is followed by a double quotation mark,
/// one backslash is placed in the argv array for every pair of backslashes,
/// and the double quotation mark is interpreted as a string delimiter.
///
/// If an odd number of backslashes is followed by a double quotation mark,
/// one backslash is placed in the argv array for every pair of backslashes,
/// and the double quotation mark is "escaped" by the remaining backslash,
/// causing a literal double quotation mark (") to be placed in argv.
/// </remarks>
public static IEnumerable<string> SplitCommandLineIntoArguments(string commandLine, bool removeHashComments)
{
return SplitCommandLineIntoArguments(commandLine, removeHashComments, out var unused);
}
public static IEnumerable<string> SplitCommandLineIntoArguments(string commandLine, bool removeHashComments, out char? illegalChar)
{
var builder = new StringBuilder(commandLine.Length);
var list = new List<string>();
var i = 0;
illegalChar = null;
while (i < commandLine.Length)
{
while (i < commandLine.Length && char.IsWhiteSpace(commandLine[i]))
{
i++;
}
if (i == commandLine.Length)
{
break;
}
if (commandLine[i] == '#' && removeHashComments)
{
break;
}
var quoteCount = 0;
builder.Length = 0;
while (i < commandLine.Length && (!char.IsWhiteSpace(commandLine[i]) || (quoteCount % 2 != 0)))
{
var current = commandLine[i];
switch (current)
{
case '\\':
{
var slashCount = 0;
do
{
builder.Append(commandLine[i]);
i++;
slashCount++;
} while (i < commandLine.Length && commandLine[i] == '\\');
// Slashes not followed by a quote character can be ignored for now
if (i >= commandLine.Length || commandLine[i] != '"')
{
break;
}
// If there is an odd number of slashes then it is escaping the quote
// otherwise it is just a quote.
if (slashCount % 2 == 0)
{
quoteCount++;
}
builder.Append('"');
i++;
break;
}
case '"':
builder.Append(current);
quoteCount++;
i++;
break;
default:
if ((current >= 0x1 && current <= 0x1f) || current == '|')
{
if (illegalChar == null)
{
illegalChar = current;
}
}
else
{
builder.Append(current);
}
i++;
break;
}
}
// If the quote string is surrounded by quotes with no interior quotes then
// remove the quotes here.
if (quoteCount == 2 && builder[0] == '"' && builder[builder.Length - 1] == '"')
{
builder.Remove(0, length: 1);
builder.Remove(builder.Length - 1, length: 1);
}
if (builder.Length > 0)
{
list.Add(builder.ToString());
}
}
return list;
}
}
}

View File

@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
public static Task<ServerResponse> RunOnServer(
string pipeName,
List<string> arguments,
IList<string> arguments,
ServerPaths buildPaths,
CancellationToken cancellationToken,
string keepAlive = null,
@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
}
private static async Task<ServerResponse> RunOnServerCore(
List<string> arguments,
IList<string> arguments,
ServerPaths buildPaths,
string pipeName,
string keepAlive,

View File

@ -33,5 +33,20 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.FileExists(result, OutputPath, "SimpleMvc.PrecompiledViews.dll");
Assert.FileExists(result, OutputPath, "SimpleMvc.PrecompiledViews.pdb");
}
[Fact]
[InitializeTestProject("SimpleMvc", baseDirectory: "Whitespace in path", additionalProjects: new string[] { })]
public async Task Build_AppWithWhitespaceInPath_CanBuildSuccessfully()
{
var result = await DotnetMSBuild(
"Build",
$"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName}");
Assert.BuildPassed(result);
Assert.FileExists(result, OutputPath, "SimpleMvc.dll");
Assert.FileExists(result, OutputPath, "SimpleMvc.pdb");
Assert.FileExists(result, OutputPath, "SimpleMvc.PrecompiledViews.dll");
Assert.FileExists(result, OutputPath, "SimpleMvc.PrecompiledViews.pdb");
}
}
}

View File

@ -10,11 +10,18 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
public class InitializeTestProjectAttribute : BeforeAfterTestAttribute
{
private readonly string _projectName;
private readonly string _baseDirectory;
private readonly string[] _additionalProjects;
public InitializeTestProjectAttribute(string projectName, params string[] additionalProjects)
: this (projectName, string.Empty, additionalProjects)
{
}
public InitializeTestProjectAttribute(string projectName, string baseDirectory, string[] additionalProjects)
{
_projectName = projectName;
_baseDirectory = baseDirectory;
_additionalProjects = additionalProjects;
}
@ -25,7 +32,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
throw new InvalidOperationException($"This should be used on a class derived from {typeof(MSBuildIntegrationTestBase)}");
}
MSBuildIntegrationTestBase.Project = ProjectDirectory.Create(_projectName, _additionalProjects);
MSBuildIntegrationTestBase.Project = ProjectDirectory.Create(_projectName, _baseDirectory, _additionalProjects);
}
public override void After(MethodInfo methodUnderTest)

View File

@ -18,9 +18,9 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
public bool PreserveWorkingDirectory { get; set; }
#endif
public static ProjectDirectory Create(string projectName, string[] additionalProjects)
public static ProjectDirectory Create(string projectName, string baseDirectory, string[] additionalProjects)
{
var destinationPath = Path.Combine(Path.GetTempPath(), "Razor", Path.GetRandomFileName());
var destinationPath = Path.Combine(Path.GetTempPath(), "Razor", baseDirectory, Path.GetRandomFileName());
Directory.CreateDirectory(destinationPath);
try