Cleaned up logging in build server

This commit is contained in:
Ajay Bhargav Baaskaran 2018-02-06 16:28:15 -08:00
parent f991b9ae48
commit 8b78b4f421
15 changed files with 149 additions and 126 deletions

View File

@ -71,6 +71,7 @@
DebugTool="$(_RazorDebugTagHelperTool)" DebugTool="$(_RazorDebugTagHelperTool)"
ToolAssembly="$(_RazorToolAssembly)" ToolAssembly="$(_RazorToolAssembly)"
UseServer="$(UseRazorBuildServer)" UseServer="$(UseRazorBuildServer)"
ForceServer="$(_RazorForceBuildServer)"
PipeName="$(_RazorBuildServerPipeName)" PipeName="$(_RazorBuildServerPipeName)"
Assemblies="@(RazorReferencePath)" Assemblies="@(RazorReferencePath)"
ProjectRoot="$(MSBuildProjectDirectory)" ProjectRoot="$(MSBuildProjectDirectory)"
@ -118,6 +119,7 @@
DebugTool="$(_RazorDebugGenerateCodeTool)" DebugTool="$(_RazorDebugGenerateCodeTool)"
ToolAssembly="$(_RazorToolAssembly)" ToolAssembly="$(_RazorToolAssembly)"
UseServer="$(UseRazorBuildServer)" UseServer="$(UseRazorBuildServer)"
ForceServer="$(_RazorForceBuildServer)"
PipeName="$(_RazorBuildServerPipeName)" PipeName="$(_RazorBuildServerPipeName)"
Sources="@(RazorGenerateWithTargetPath)" Sources="@(RazorGenerateWithTargetPath)"
ProjectRoot="$(MSBuildProjectDirectory)" ProjectRoot="$(MSBuildProjectDirectory)"

View File

@ -5,14 +5,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.Razor.Tools; using Microsoft.AspNetCore.Razor.Tools;
using Microsoft.Build.Framework; using Microsoft.Build.Framework;
using Microsoft.Build.Utilities; using Microsoft.Build.Utilities;
using Microsoft.CodeAnalysis.CommandLine;
using Microsoft.Extensions.CommandLineUtils; using Microsoft.Extensions.CommandLineUtils;
using Roslyn.Utilities;
namespace Microsoft.AspNetCore.Razor.Tasks namespace Microsoft.AspNetCore.Razor.Tasks
{ {
@ -29,6 +26,9 @@ namespace Microsoft.AspNetCore.Razor.Tasks
public bool UseServer { get; set; } public bool UseServer { get; set; }
// Specifies whether we should fallback to in-process execution if server execution fails.
public bool ForceServer { get; set; }
public string PipeName { get; set; } public string PipeName { get; set; }
protected override string ToolName => "dotnet"; protected override string ToolName => "dotnet";
@ -115,11 +115,11 @@ namespace Microsoft.AspNetCore.Razor.Tasks
string commandLineCommands, string commandLineCommands,
out int result) out int result)
{ {
CompilerServerLogger.Log("Server execution started."); Log.LogMessage(StandardOutputLoggingImportance, "Server execution started.");
using (_razorServerCts = new CancellationTokenSource()) using (_razorServerCts = new CancellationTokenSource())
{ {
CompilerServerLogger.Log($"CommandLine = '{commandLineCommands}'"); Log.LogMessage(StandardOutputLoggingImportance, $"CommandLine = '{commandLineCommands}'");
CompilerServerLogger.Log($"ServerResponseFile = '{responseFileCommands}'"); Log.LogMessage(StandardOutputLoggingImportance, $"ServerResponseFile = '{responseFileCommands}'");
// The server contains the tools for discovering tag helpers and generating Razor code. // The server contains the tools for discovering tag helpers and generating Razor code.
var clientDir = Path.GetDirectoryName(ToolAssembly); var clientDir = Path.GetDirectoryName(ToolAssembly);
@ -141,14 +141,35 @@ namespace Microsoft.AspNetCore.Razor.Tasks
{ {
result = completedResponse.ReturnCode; result = completedResponse.ReturnCode;
CompilerServerLogger.Log($"Server execution completed with return code {result}."); if (result == 0)
{
Log.LogMessage(StandardOutputLoggingImportance, $"Server execution completed with return code {result}.");
return true;
}
else
{
Log.LogMessage(
StandardOutputLoggingImportance,
$"Server execution completed with return code {result}. For more info, check the server log file in the location specified by the RAZORBUILDSERVER_LOG environment variable.");
}
}
else
{
Log.LogMessage(
StandardOutputLoggingImportance,
$"Server execution failed with response {response.Type}. For more info, check the server log file in the location specified by the RAZORBUILDSERVER_LOG environment variable.");
}
result = -1;
if (ForceServer)
{
// We don't want to fallback to in-process execution.
return true; return true;
} }
}
CompilerServerLogger.Log("Server execution failed."); Log.LogMessage(StandardOutputLoggingImportance, "Fallback to in-process execution.");
result = -1; }
return false; return false;
} }

View File

@ -16,12 +16,6 @@
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" /> <PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
<PackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" /> <PackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\Roslyn\CompilerServerLogger.cs">
<Link>Shared\CompilerServerLogger.cs</Link>
</Compile>
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\Roslyn\PlatformInformation.cs">
<Link>Shared\PlatformInformation.cs</Link>
</Compile>
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\ServerProtocol\*.cs"> <Compile Include="..\Microsoft.AspNetCore.Razor.Tools\ServerProtocol\*.cs">
<Link>Shared\ServerProtocol\%(FileName)</Link> <Link>Shared\ServerProtocol\%(FileName)</Link>
</Compile> </Compile>

View File

@ -6,7 +6,6 @@ using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CommandLine;
namespace Microsoft.AspNetCore.Razor.Tools namespace Microsoft.AspNetCore.Razor.Tools
{ {
@ -39,12 +38,12 @@ namespace Microsoft.AspNetCore.Razor.Tools
// Machine-local named pipes are named "\\.\pipe\<pipename>". // Machine-local named pipes are named "\\.\pipe\<pipename>".
// We use the SHA1 of the directory the compiler exes live in as the pipe name. // We use the SHA1 of the directory the compiler exes live in as the pipe name.
// The NamedPipeClientStream class handles the "\\.\pipe\" part for us. // The NamedPipeClientStream class handles the "\\.\pipe\" part for us.
CompilerServerLogger.Log("Attempt to open named pipe '{0}'", pipeName); ServerLogger.Log("Attempt to open named pipe '{0}'", pipeName);
var stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); var stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
CompilerServerLogger.Log("Attempt to connect named pipe '{0}'", pipeName); ServerLogger.Log("Attempt to connect named pipe '{0}'", pipeName);
try try
{ {
await stream.ConnectAsync(timeoutMilliseconds, cancellationToken); await stream.ConnectAsync(timeoutMilliseconds, cancellationToken);
@ -55,11 +54,11 @@ namespace Microsoft.AspNetCore.Razor.Tools
// From docs: // From docs:
// - TimeoutException: Could not connect to the server within the specified timeout period. // - TimeoutException: Could not connect to the server within the specified timeout period.
// - IOException: The server is connected to another client and the time-out period has expired. // - IOException: The server is connected to another client and the time-out period has expired.
CompilerServerLogger.Log($"Connecting to server timed out after {timeoutMilliseconds} ms"); ServerLogger.Log($"Connecting to server timed out after {timeoutMilliseconds} ms");
return null; return null;
} }
CompilerServerLogger.Log("Named pipe '{0}' connected", pipeName); ServerLogger.Log("Named pipe '{0}' connected", pipeName);
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
// The original code in Roslyn checks that the server pipe is owned by the same user for security. // The original code in Roslyn checks that the server pipe is owned by the same user for security.
@ -70,7 +69,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
} }
catch (Exception e) when (!(e is TaskCanceledException || e is OperationCanceledException)) catch (Exception e) when (!(e is TaskCanceledException || e is OperationCanceledException))
{ {
CompilerServerLogger.LogException(e, "Exception while connecting to process"); ServerLogger.LogException(e, "Exception while connecting to process");
return null; return null;
} }
} }
@ -108,9 +107,9 @@ namespace Microsoft.AspNetCore.Razor.Tools
try try
{ {
CompilerServerLogger.Log($"Before poking pipe {Identifier}."); ServerLogger.Log($"Before poking pipe {Identifier}.");
await Stream.ReadAsync(Array.Empty<byte>(), 0, 0, cancellationToken); await Stream.ReadAsync(Array.Empty<byte>(), 0, 0, cancellationToken);
CompilerServerLogger.Log($"After poking pipe {Identifier}."); ServerLogger.Log($"After poking pipe {Identifier}.");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -119,7 +118,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
{ {
// It is okay for this call to fail. Errors will be reflected in the // It is okay for this call to fail. Errors will be reflected in the
// IsConnected property which will be read on the next iteration. // IsConnected property which will be read on the next iteration.
CompilerServerLogger.LogException(e, $"Error poking pipe {Identifier}."); ServerLogger.LogException(e, $"Error poking pipe {Identifier}.");
} }
} }
} }

View File

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis.CommandLine;
namespace Microsoft.AspNetCore.Razor.Tools namespace Microsoft.AspNetCore.Razor.Tools
{ {
@ -26,13 +26,33 @@ namespace Microsoft.AspNetCore.Razor.Tools
return new RejectedServerResponse(); return new RejectedServerResponse();
} }
var exitCode = 0;
var output = string.Empty;
var app = new Application(cancellationToken); var app = new Application(cancellationToken);
var commandArgs = parsed.args.ToArray(); var commandArgs = parsed.args.ToArray();
var exitCode = app.Execute(commandArgs); if (ServerLogger.IsLoggingEnabled)
var output = app.Out.ToString() ?? string.Empty; {
using (var writer = new StringWriter())
{
app.Out = writer;
app.Error = writer;
exitCode = app.Execute(commandArgs);
output = writer.ToString();
ServerLogger.Log(output);
}
}
else
{
using (var writer = new StreamWriter(Stream.Null))
{
app.Out = writer;
app.Error = writer;
exitCode = app.Execute(commandArgs);
}
}
return new CompletedServerResponse(exitCode, utf8output: false, output: output); return new CompletedServerResponse(exitCode, utf8output: false, output: string.Empty);
} }
private bool TryParseArguments(ServerRequest request, out (string workingDirectory, string tempDirectory, string[] args) parsed) private bool TryParseArguments(ServerRequest request, out (string workingDirectory, string tempDirectory, string[] args) parsed)
@ -59,16 +79,16 @@ namespace Microsoft.AspNetCore.Razor.Tools
} }
} }
CompilerServerLogger.Log($"WorkingDirectory = '{workingDirectory}'"); ServerLogger.Log($"WorkingDirectory = '{workingDirectory}'");
CompilerServerLogger.Log($"TempDirectory = '{tempDirectory}'"); ServerLogger.Log($"TempDirectory = '{tempDirectory}'");
for (var i = 0; i < args.Count; i++) for (var i = 0; i < args.Count; i++)
{ {
CompilerServerLogger.Log($"Argument[{i}] = '{request.Arguments[i]}'"); ServerLogger.Log($"Argument[{i}] = '{request.Arguments[i]}'");
} }
if (string.IsNullOrEmpty(workingDirectory)) if (string.IsNullOrEmpty(workingDirectory))
{ {
CompilerServerLogger.Log($"Rejecting build due to missing working directory"); ServerLogger.Log($"Rejecting build due to missing working directory");
parsed = default; parsed = default;
return false; return false;
@ -76,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
if (string.IsNullOrEmpty(tempDirectory)) if (string.IsNullOrEmpty(tempDirectory))
{ {
CompilerServerLogger.Log($"Rejecting build due to missing temp directory"); ServerLogger.Log($"Rejecting build due to missing temp directory");
parsed = default; parsed = default;
return false; return false;
@ -84,7 +104,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
if (string.IsNullOrEmpty(tempDirectory)) if (string.IsNullOrEmpty(tempDirectory))
{ {
CompilerServerLogger.Log($"Rejecting build due to missing temp directory"); ServerLogger.Log($"Rejecting build due to missing temp directory");
parsed = default; parsed = default;
return false; return false;

View File

@ -6,7 +6,6 @@ using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CommandLine;
namespace Microsoft.AspNetCore.Razor.Tools namespace Microsoft.AspNetCore.Razor.Tools
{ {
@ -58,13 +57,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
PipeBufferSize, // Default input buffer PipeBufferSize, // Default input buffer
PipeBufferSize);// Default output buffer PipeBufferSize);// Default output buffer
CompilerServerLogger.Log("Waiting for new connection"); ServerLogger.Log("Waiting for new connection");
await pipeStream.WaitForConnectionAsync(cancellationToken); await pipeStream.WaitForConnectionAsync(cancellationToken);
CompilerServerLogger.Log("Pipe connection detected."); ServerLogger.Log("Pipe connection detected.");
if (Environment.Is64BitProcess || Memory.IsMemoryAvailable()) if (Environment.Is64BitProcess || Memory.IsMemoryAvailable())
{ {
CompilerServerLogger.Log("Memory available - accepting connection"); ServerLogger.Log("Memory available - accepting connection");
return new NamedPipeConnection(pipeStream, GetNextIdentifier()); return new NamedPipeConnection(pipeStream, GetNextIdentifier());
} }
@ -96,9 +95,9 @@ namespace Microsoft.AspNetCore.Razor.Tools
try try
{ {
CompilerServerLogger.Log($"Before poking pipe {Identifier}."); ServerLogger.Log($"Before poking pipe {Identifier}.");
await Stream.ReadAsync(Array.Empty<byte>(), 0, 0, cancellationToken); await Stream.ReadAsync(Array.Empty<byte>(), 0, 0, cancellationToken);
CompilerServerLogger.Log($"After poking pipe {Identifier}."); ServerLogger.Log($"After poking pipe {Identifier}.");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -107,14 +106,14 @@ namespace Microsoft.AspNetCore.Razor.Tools
{ {
// It is okay for this call to fail. Errors will be reflected in the // It is okay for this call to fail. Errors will be reflected in the
// IsConnected property which will be read on the next iteration. // IsConnected property which will be read on the next iteration.
CompilerServerLogger.LogException(e, $"Error poking pipe {Identifier}."); ServerLogger.LogException(e, $"Error poking pipe {Identifier}.");
} }
} }
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
CompilerServerLogger.Log($"Pipe {Identifier}: Closing."); ServerLogger.Log($"Pipe {Identifier}: Closing.");
try try
{ {
@ -126,7 +125,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
// for which we can no longer communicate and that's okay because the Close method indicates we are // for which we can no longer communicate and that's okay because the Close method indicates we are
// done with the client already. // done with the client already.
var message = string.Format($"Pipe {Identifier}: Error closing pipe."); var message = string.Format($"Pipe {Identifier}: Error closing pipe.");
CompilerServerLogger.LogException(ex, message); ServerLogger.LogException(ex, message);
} }
} }
} }

View File

@ -8,7 +8,6 @@ using System.Linq;
using System.Runtime; using System.Runtime;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CommandLine;
namespace Microsoft.AspNetCore.Razor.Tools namespace Microsoft.AspNetCore.Razor.Tools
{ {
@ -318,7 +317,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
{ {
// Unable to establish a connection with the client. The client is responsible for // Unable to establish a connection with the client. The client is responsible for
// handling this case. Nothing else for us to do here. // handling this case. Nothing else for us to do here.
CompilerServerLogger.LogException(ex, "Error creating client named pipe"); ServerLogger.LogException(ex, "Error creating client named pipe");
return new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted); return new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted);
} }
@ -329,13 +328,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
ServerRequest request; ServerRequest request;
try try
{ {
CompilerServerLogger.Log("Begin reading request."); ServerLogger.Log("Begin reading request.");
request = await ServerRequest.ReadAsync(connection.Stream, cancellationToken).ConfigureAwait(false); request = await ServerRequest.ReadAsync(connection.Stream, cancellationToken).ConfigureAwait(false);
CompilerServerLogger.Log("End reading request."); ServerLogger.Log("End reading request.");
} }
catch (Exception e) catch (Exception e)
{ {
CompilerServerLogger.LogException(e, "Error reading build request."); ServerLogger.LogException(e, "Error reading build request.");
return new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted); return new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted);
} }
@ -379,9 +378,9 @@ namespace Microsoft.AspNetCore.Razor.Tools
try try
{ {
CompilerServerLogger.Log("Begin writing response."); ServerLogger.Log("Begin writing response.");
await response.WriteAsync(connection.Stream, cancellationToken); await response.WriteAsync(connection.Stream, cancellationToken);
CompilerServerLogger.Log("End writing response."); ServerLogger.Log("End writing response.");
reason = ConnectionResult.Reason.CompilationCompleted; reason = ConnectionResult.Reason.CompilationCompleted;
} }
@ -405,7 +404,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
CompilerServerLogger.LogException(ex, "Error handling connection"); ServerLogger.LogException(ex, "Error handling connection");
return new ConnectionResult(ConnectionResult.Reason.ClientException); return new ConnectionResult(ConnectionResult.Reason.ClientException);
} }
} }
@ -414,11 +413,11 @@ namespace Microsoft.AspNetCore.Razor.Tools
{ {
Func<ServerResponse> func = () => Func<ServerResponse> func = () =>
{ {
CompilerServerLogger.Log("Begin processing request"); ServerLogger.Log("Begin processing request");
var response = _compilerHost.Execute(buildRequest, cancellationToken); var response = _compilerHost.Execute(buildRequest, cancellationToken);
CompilerServerLogger.Log("End processing request"); ServerLogger.Log("End processing request");
return response; return response;
}; };

View File

@ -1,17 +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.IO;
namespace Roslyn.Utilities
{
/// <summary>
/// This class provides simple properties for determining whether the current platform is Windows or Unix-based.
/// We intentionally do not use System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(...) because
/// it incorrectly reports 'true' for 'Windows' in desktop builds running on Unix-based platforms via Mono.
/// </summary>
internal static class PlatformInformation
{
public static bool IsWindows => Path.DirectorySeparatorChar == '\\';
public static bool IsUnix => Path.DirectorySeparatorChar == '/';
}
}

View File

@ -8,8 +8,6 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Roslyn.Utilities;
using static Microsoft.CodeAnalysis.CommandLine.CompilerServerLogger;
namespace Microsoft.AspNetCore.Razor.Tools namespace Microsoft.AspNetCore.Razor.Tools
{ {
@ -204,26 +202,26 @@ namespace Microsoft.AspNetCore.Razor.Tools
// Write the request // Write the request
try try
{ {
Log("Begin writing request"); ServerLogger.Log("Begin writing request");
await request.WriteAsync(client.Stream, cancellationToken).ConfigureAwait(false); await request.WriteAsync(client.Stream, cancellationToken).ConfigureAwait(false);
Log("End writing request"); ServerLogger.Log("End writing request");
} }
catch (Exception e) catch (Exception e)
{ {
LogException(e, "Error writing build request."); ServerLogger.LogException(e, "Error writing build request.");
return new RejectedServerResponse(); return new RejectedServerResponse();
} }
// Wait for the compilation and a monitor to detect if the server disconnects // Wait for the compilation and a monitor to detect if the server disconnects
var serverCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var serverCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
Log("Begin reading response"); ServerLogger.Log("Begin reading response");
var responseTask = ServerResponse.ReadAsync(client.Stream, serverCts.Token); var responseTask = ServerResponse.ReadAsync(client.Stream, serverCts.Token);
var monitorTask = client.WaitForDisconnectAsync(serverCts.Token); var monitorTask = client.WaitForDisconnectAsync(serverCts.Token);
await Task.WhenAny(responseTask, monitorTask).ConfigureAwait(false); await Task.WhenAny(responseTask, monitorTask).ConfigureAwait(false);
Log("End reading response"); ServerLogger.Log("End reading response");
if (responseTask.IsCompleted) if (responseTask.IsCompleted)
{ {
@ -234,13 +232,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
LogException(e, "Error reading response"); ServerLogger.LogException(e, "Error reading response");
response = new RejectedServerResponse(); response = new RejectedServerResponse();
} }
} }
else else
{ {
Log("Server disconnect"); ServerLogger.Log("Server disconnect");
response = new RejectedServerResponse(); response = new RejectedServerResponse();
} }
@ -282,7 +280,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
startInfo.dwFlags = NativeMethods.STARTF_USESTDHANDLES; startInfo.dwFlags = NativeMethods.STARTF_USESTDHANDLES;
var dwCreationFlags = NativeMethods.NORMAL_PRIORITY_CLASS | NativeMethods.CREATE_NO_WINDOW; var dwCreationFlags = NativeMethods.NORMAL_PRIORITY_CLASS | NativeMethods.CREATE_NO_WINDOW;
Log("Attempting to create process '{0}'", expectedPath); ServerLogger.Log("Attempting to create process '{0}'", expectedPath);
var builder = new StringBuilder($@"""{expectedPath}"" {processArguments}"); var builder = new StringBuilder($@"""{expectedPath}"" {processArguments}");
@ -300,13 +298,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
if (success) if (success)
{ {
Log("Successfully created process with process id {0}", processInfo.dwProcessId); ServerLogger.Log("Successfully created process with process id {0}", processInfo.dwProcessId);
NativeMethods.CloseHandle(processInfo.hProcess); NativeMethods.CloseHandle(processInfo.hProcess);
NativeMethods.CloseHandle(processInfo.hThread); NativeMethods.CloseHandle(processInfo.hThread);
} }
else else
{ {
Log("Failed to create process. GetLastError={0}", Marshal.GetLastWin32Error()); ServerLogger.Log("Failed to create process. GetLastError={0}", Marshal.GetLastWin32Error());
} }
return success; return success;
} }
@ -336,4 +334,15 @@ namespace Microsoft.AspNetCore.Razor.Tools
} }
} }
} }
/// <summary>
/// This class provides simple properties for determining whether the current platform is Windows or Unix-based.
/// We intentionally do not use System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(...) because
/// it incorrectly reports 'true' for 'Windows' in desktop builds running on Unix-based platforms via Mono.
/// </summary>
internal static class PlatformInformation
{
public static bool IsWindows => Path.DirectorySeparatorChar == '\\';
public static bool IsUnix => Path.DirectorySeparatorChar == '/';
}
} }

View File

@ -1,25 +1,22 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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 Roslyn.Utilities;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
namespace Microsoft.CodeAnalysis.CommandLine namespace Microsoft.AspNetCore.Razor.Tools
{ {
/// <summary> /// <summary>
/// Class for logging information about what happens in the server and client parts of the /// Class for logging information about what happens in the server and client parts of the
/// Roslyn command line compiler and build tasks. Useful for debugging what is going on. /// Razor command line compiler and build tasks. Useful for debugging what is going on.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// To use the logging, set the environment variable RoslynCommandLineLogFile to the name /// To use the logging, set the environment variable RAZORBUILDSERVER_LOG to the name
/// of a file to log to. This file is logged to by both client and server components. /// of a file to log to. This file is logged to by both client and server components.
/// </remarks> /// </remarks>
internal class CompilerServerLogger internal class ServerLogger
{ {
// Environment variable, if set, to enable logging and set the file to log to. // Environment variable, if set, to enable logging and set the file to log to.
private const string EnvironmentVariable = "RAZORBUILDSERVER_LOG"; private const string EnvironmentVariable = "RAZORBUILDSERVER_LOG";
@ -30,17 +27,19 @@ namespace Microsoft.CodeAnalysis.CommandLine
/// <summary> /// <summary>
/// Static class initializer that initializes logging. /// Static class initializer that initializes logging.
/// </summary> /// </summary>
static CompilerServerLogger() static ServerLogger()
{ {
s_loggingStream = null; s_loggingStream = null;
try try
{ {
// Check if the environment // Check if the environment
string loggingFileName = Environment.GetEnvironmentVariable(EnvironmentVariable); var loggingFileName = Environment.GetEnvironmentVariable(EnvironmentVariable);
if (loggingFileName != null) if (loggingFileName != null)
{ {
IsLoggingEnabled = true;
// If the environment variable contains the path of a currently existing directory, // If the environment variable contains the path of a currently existing directory,
// then use a process-specific name for the log file and put it in that directory. // then use a process-specific name for the log file and put it in that directory.
// Otherwise, assume that the environment variable specifies the name of the log file. // Otherwise, assume that the environment variable specifies the name of the log file.
@ -59,6 +58,8 @@ namespace Microsoft.CodeAnalysis.CommandLine
} }
} }
public static bool IsLoggingEnabled { get; }
/// <summary> /// <summary>
/// Set the logging prefix that describes our role. /// Set the logging prefix that describes our role.
/// Typically a 3-letter abbreviation. If logging happens before this, it's logged with "---". /// Typically a 3-letter abbreviation. If logging happens before this, it's logged with "---".
@ -73,11 +74,11 @@ namespace Microsoft.CodeAnalysis.CommandLine
/// </summary> /// </summary>
public static void LogException(Exception e, string reason) public static void LogException(Exception e, string reason)
{ {
if (s_loggingStream != null) if (IsLoggingEnabled)
{ {
Log("Exception '{0}' occurred during '{1}'. Stack trace:\r\n{2}", e.Message, reason, e.StackTrace); Log("Exception '{0}' occurred during '{1}'. Stack trace:\r\n{2}", e.Message, reason, e.StackTrace);
int innerExceptionLevel = 0; var innerExceptionLevel = 0;
e = e.InnerException; e = e.InnerException;
while (e != null) while (e != null)
@ -94,7 +95,7 @@ namespace Microsoft.CodeAnalysis.CommandLine
/// </summary> /// </summary>
public static void Log(string format, params object[] arguments) public static void Log(string format, params object[] arguments)
{ {
if (s_loggingStream != null) if (IsLoggingEnabled)
{ {
Log(string.Format(format, arguments)); Log(string.Format(format, arguments));
} }
@ -106,12 +107,12 @@ namespace Microsoft.CodeAnalysis.CommandLine
/// <param name="message"></param> /// <param name="message"></param>
public static void Log(string message) public static void Log(string message)
{ {
if (s_loggingStream != null) if (IsLoggingEnabled)
{ {
string prefix = GetLoggingPrefix(); var prefix = GetLoggingPrefix();
string output = prefix + message + "\r\n"; var output = prefix + message + "\r\n";
byte[] bytes = Encoding.UTF8.GetBytes(output); var bytes = Encoding.UTF8.GetBytes(output);
// Because multiple processes might be logging to the same file, we always seek to the end, // Because multiple processes might be logging to the same file, we always seek to the end,
// write, and flush. // write, and flush.

View File

@ -4,7 +4,6 @@
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CommandLine;
namespace Microsoft.AspNetCore.Razor.Tools namespace Microsoft.AspNetCore.Razor.Tools
{ {
@ -49,7 +48,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
var totalBytesRead = 0; var totalBytesRead = 0;
do do
{ {
CompilerServerLogger.Log("Attempting to read {0} bytes from the stream", count - totalBytesRead); ServerLogger.Log("Attempting to read {0} bytes from the stream", count - totalBytesRead);
var bytesRead = await stream.ReadAsync( var bytesRead = await stream.ReadAsync(
buffer, buffer,
totalBytesRead, totalBytesRead,
@ -59,13 +58,13 @@ namespace Microsoft.AspNetCore.Razor.Tools
if (bytesRead == 0) if (bytesRead == 0)
{ {
CompilerServerLogger.Log("Unexpected -- read 0 bytes from the stream."); ServerLogger.Log("Unexpected -- read 0 bytes from the stream.");
throw new EndOfStreamException("Reached end of stream before end of read."); throw new EndOfStreamException("Reached end of stream before end of read.");
} }
CompilerServerLogger.Log("Read {0} bytes", bytesRead); ServerLogger.Log("Read {0} bytes", bytesRead);
totalBytesRead += bytesRead; totalBytesRead += bytesRead;
} while (totalBytesRead < count); } while (totalBytesRead < count);
CompilerServerLogger.Log("Finished read"); ServerLogger.Log("Finished read");
} }
} }
} }

View File

@ -9,7 +9,6 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using static Microsoft.CodeAnalysis.CommandLine.CompilerServerLogger;
// After the server pipe is connected, it forks off a thread to handle the connection, and creates // After the server pipe is connected, it forks off a thread to handle the connection, and creates
// a new instance of the pipe to listen for new clients. When it gets a request, it validates // a new instance of the pipe to listen for new clients. When it gets a request, it validates
@ -84,9 +83,9 @@ namespace Microsoft.AspNetCore.Razor.Tools
IList<string> args, IList<string> args,
string keepAlive = null) string keepAlive = null)
{ {
Log("Creating ServerRequest"); ServerLogger.Log("Creating ServerRequest");
Log($"Working directory: {workingDirectory}"); ServerLogger.Log($"Working directory: {workingDirectory}");
Log($"Temp directory: {tempDirectory}"); ServerLogger.Log($"Temp directory: {tempDirectory}");
var requestLength = args.Count + 1; var requestLength = args.Count + 1;
var requestArgs = new List<RequestArgument>(requestLength) var requestArgs = new List<RequestArgument>(requestLength)
@ -103,7 +102,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
for (var i = 0; i < args.Count; ++i) for (var i = 0; i < args.Count; ++i)
{ {
var arg = args[i]; var arg = args[i];
Log($"argument[{i}] = {arg}"); ServerLogger.Log($"argument[{i}] = {arg}");
requestArgs.Add(new RequestArgument(RequestArgument.ArgumentId.CommandLineArgument, i, arg)); requestArgs.Add(new RequestArgument(RequestArgument.ArgumentId.CommandLineArgument, i, arg));
} }
@ -130,14 +129,14 @@ namespace Microsoft.AspNetCore.Razor.Tools
{ {
// Read the length of the request // Read the length of the request
var lengthBuffer = new byte[4]; var lengthBuffer = new byte[4];
Log("Reading length of request"); ServerLogger.Log("Reading length of request");
await ServerProtocol.ReadAllAsync(inStream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false); await ServerProtocol.ReadAllAsync(inStream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false);
var length = BitConverter.ToInt32(lengthBuffer, 0); var length = BitConverter.ToInt32(lengthBuffer, 0);
// Back out if the request is > 1MB // Back out if the request is > 1MB
if (length > 0x100000) if (length > 0x100000)
{ {
Log("Request is over 1MB in length, cancelling read."); ServerLogger.Log("Request is over 1MB in length, cancelling read.");
return null; return null;
} }
@ -149,7 +148,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
Log("Parsing request"); ServerLogger.Log("Parsing request");
// Parse the request into the Request data structure. // Parse the request into the Request data structure.
using (var reader = new BinaryReader(new MemoryStream(requestBuffer), Encoding.Unicode)) using (var reader = new BinaryReader(new MemoryStream(requestBuffer), Encoding.Unicode))
{ {
@ -177,7 +176,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode)) using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode))
{ {
// Format the request. // Format the request.
Log("Formatting request"); ServerLogger.Log("Formatting request");
writer.Write(ProtocolVersion); writer.Write(ProtocolVersion);
writer.Write(Arguments.Count); writer.Write(Arguments.Count);
foreach (var arg in Arguments) foreach (var arg in Arguments)
@ -195,17 +194,17 @@ namespace Microsoft.AspNetCore.Razor.Tools
// Back out if the request is > 1 MB // Back out if the request is > 1 MB
if (memoryStream.Length > 0x100000) if (memoryStream.Length > 0x100000)
{ {
Log("Request is over 1MB in length, cancelling write"); ServerLogger.Log("Request is over 1MB in length, cancelling write");
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
// Send the request to the server // Send the request to the server
Log("Writing length of request."); ServerLogger.Log("Writing length of request.");
await outStream await outStream
.WriteAsync(BitConverter.GetBytes(length), 0, 4, cancellationToken) .WriteAsync(BitConverter.GetBytes(length), 0, 4, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
Log("Writing request of size {0}", length); ServerLogger.Log("Writing request of size {0}", length);
// Write the request // Write the request
memoryStream.Position = 0; memoryStream.Position = 0;
await memoryStream await memoryStream

View File

@ -6,7 +6,6 @@ using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using static Microsoft.CodeAnalysis.CommandLine.CompilerServerLogger;
// After the server pipe is connected, it forks off a thread to handle the connection, and creates // After the server pipe is connected, it forks off a thread to handle the connection, and creates
// a new instance of the pipe to listen for new clients. When it gets a request, it validates // a new instance of the pipe to listen for new clients. When it gets a request, it validates
@ -55,7 +54,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode)) using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode))
{ {
// Format the response // Format the response
Log("Formatting Response"); ServerLogger.Log("Formatting Response");
writer.Write((int)Type); writer.Write((int)Type);
AddResponseBody(writer); AddResponseBody(writer);
@ -68,7 +67,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
// Write the length of the response // Write the length of the response
var length = checked((int)memoryStream.Length); var length = checked((int)memoryStream.Length);
Log("Writing response length"); ServerLogger.Log("Writing response length");
// There is no way to know the number of bytes written to // There is no way to know the number of bytes written to
// the pipe stream. We just have to assume all of them are written. // the pipe stream. We just have to assume all of them are written.
await outStream await outStream
@ -76,7 +75,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
.ConfigureAwait(false); .ConfigureAwait(false);
// Write the response // Write the response
Log("Writing response of size {0}", length); ServerLogger.Log("Writing response of size {0}", length);
memoryStream.Position = 0; memoryStream.Position = 0;
await memoryStream await memoryStream
.CopyToAsync(outStream, bufferSize: length, cancellationToken: cancellationToken) .CopyToAsync(outStream, bufferSize: length, cancellationToken: cancellationToken)
@ -94,14 +93,14 @@ namespace Microsoft.AspNetCore.Razor.Tools
/// <returns></returns> /// <returns></returns>
public static async Task<ServerResponse> ReadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) public static async Task<ServerResponse> ReadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
{ {
Log("Reading response length"); ServerLogger.Log("Reading response length");
// Read the response length // Read the response length
var lengthBuffer = new byte[4]; var lengthBuffer = new byte[4];
await ServerProtocol.ReadAllAsync(stream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false); await ServerProtocol.ReadAllAsync(stream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false);
var length = BitConverter.ToUInt32(lengthBuffer, 0); var length = BitConverter.ToUInt32(lengthBuffer, 0);
// Read the response // Read the response
Log("Reading response of length {0}", length); ServerLogger.Log("Reading response of length {0}", length);
var responseBuffer = new byte[length]; var responseBuffer = new byte[length];
await ServerProtocol.ReadAllAsync( await ServerProtocol.ReadAllAsync(
stream, stream,

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{ {
var result = await DotnetMSBuild( var result = await DotnetMSBuild(
"Build", "Build",
$"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName}", $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName} /p:_RazorForceBuildServer=true",
msBuildProcessKind: msBuildProcessKind); msBuildProcessKind: msBuildProcessKind);
Assert.BuildPassed(result); Assert.BuildPassed(result);
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{ {
var result = await DotnetMSBuild( var result = await DotnetMSBuild(
"Build", "Build",
$"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName}"); $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName} /p:_RazorForceBuildServer=true");
Assert.BuildPassed(result); Assert.BuildPassed(result);
Assert.FileExists(result, OutputPath, "SimpleMvc.dll"); Assert.FileExists(result, OutputPath, "SimpleMvc.dll");
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{ {
var result = await DotnetMSBuild( var result = await DotnetMSBuild(
"Build", "Build",
$"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName}"); $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName} /p:_RazorForceBuildServer=true");
Assert.BuildPassed(result); Assert.BuildPassed(result);
Assert.FileExists(result, OutputPath, "Whitespace in name.dll"); Assert.FileExists(result, OutputPath, "Whitespace in name.dll");

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CommandLine;
namespace Microsoft.AspNetCore.Razor.Tools namespace Microsoft.AspNetCore.Razor.Tools
{ {