Build server working end to end
- Added a UseServer switch - Updated tasks to use the build server in enabled
This commit is contained in:
parent
5c6ff4366f
commit
4500de5862
|
|
@ -1,7 +1,7 @@
|
|||
<Project>
|
||||
|
||||
<!-- Using explicit SDK imports here because the default way conflicts with the AfterBuild target -->
|
||||
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk"/>
|
||||
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains MSBuild support for Razor.</Description>
|
||||
|
|
@ -28,21 +28,22 @@
|
|||
<ItemGroup>
|
||||
|
||||
<!-- These are just normal MSBuild files that we want to include in the package -->
|
||||
<Content Include="build\**\*.props" PackagePath="build\"/>
|
||||
<Content Include="build\**\*.targets" PackagePath="build\"/>
|
||||
<Content Include="buildMultiTargeting\*.props" PackagePath="buildMultiTargeting\"/>
|
||||
<Content Include="buildMultiTargeting\*.targets" PackagePath="buildMultiTargeting\"/>
|
||||
<Content Include="build\**\*.props" PackagePath="build\" />
|
||||
<Content Include="build\**\*.targets" PackagePath="build\" />
|
||||
<Content Include="buildMultiTargeting\*.props" PackagePath="buildMultiTargeting\" />
|
||||
<Content Include="buildMultiTargeting\*.targets" PackagePath="buildMultiTargeting\" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- This is the tasks project that needs to be included in the package. -->
|
||||
<ItemGroup>
|
||||
<TaskProject Include="..\Microsoft.AspNetCore.Razor.Tasks\Microsoft.AspNetCore.Razor.Tasks.csproj"/>
|
||||
<TaskProject Include="..\Microsoft.AspNetCore.Razor.Tasks\Microsoft.AspNetCore.Razor.Tasks.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- These are tools that need to be included in the package. -->
|
||||
<ItemGroup>
|
||||
<ToolProject Include="..\Microsoft.AspNetCore.Razor.GenerateTool\Microsoft.AspNetCore.Razor.GenerateTool.csproj"/>
|
||||
<ToolProject Include="..\Microsoft.AspNetCore.Razor.TagHelperTool\Microsoft.AspNetCore.Razor.TagHelperTool.csproj"/>
|
||||
<ToolProject Include="..\Microsoft.AspNetCore.Razor.GenerateTool\Microsoft.AspNetCore.Razor.GenerateTool.csproj" />
|
||||
<ToolProject Include="..\Microsoft.AspNetCore.Razor.TagHelperTool\Microsoft.AspNetCore.Razor.TagHelperTool.csproj" />
|
||||
<ToolProject Include="..\Microsoft.AspNetCore.Razor.Tools\Microsoft.AspNetCore.Razor.Tools.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Using explicit SDK imports here because the default way conflicts with the AfterBuild target -->
|
||||
|
|
@ -63,47 +64,38 @@
|
|||
|
||||
First, build the project, then copy it to the ouput directory, then add it as packable content.
|
||||
-->
|
||||
<MSBuild Projects="@(TaskProject)"/>
|
||||
<MSBuild Projects="@(TaskProject)" />
|
||||
|
||||
<MSBuild
|
||||
Projects="@(TaskProject)"
|
||||
Properties="TargetFramework=net46"
|
||||
Targets="GetTargetPath">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="TaskAssemblyNet46"/>
|
||||
<MSBuild Projects="@(TaskProject)" Properties="TargetFramework=net46" Targets="GetTargetPath">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="TaskAssemblyNet46" />
|
||||
</MSBuild>
|
||||
|
||||
<MSBuild
|
||||
Projects="@(TaskProject)"
|
||||
Properties="TargetFramework=netstandard2.0"
|
||||
Targets="GetTargetPath">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="TaskAssemblyNetStandard"/>
|
||||
<MSBuild Projects="@(TaskProject)" Properties="TargetFramework=netstandard2.0" Targets="GetTargetPath">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="TaskAssemblyNetStandard" />
|
||||
</MSBuild>
|
||||
|
||||
<Copy SourceFiles="@(TaskAssemblyNet46)" DestinationFolder="$(OutputPath)\tasks\net46\">
|
||||
<Output TaskParameter="CopiedFiles" ItemName="FileWrites"/>
|
||||
<Output TaskParameter="CopiedFiles" ItemName="FileWrites" />
|
||||
</Copy>
|
||||
<Copy SourceFiles="@(TaskAssemblyNetStandard)" DestinationFolder="$(OutputPath)\tasks\netstandard2.0\">
|
||||
<Output TaskParameter="CopiedFiles" ItemName="FileWrites"/>
|
||||
<Output TaskParameter="CopiedFiles" ItemName="FileWrites" />
|
||||
</Copy>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="@(TaskAssemblyNet46)" PackagePath="tasks\net46\" Pack="true"/>
|
||||
<None Include="@(TaskAssemblyNetStandard)" PackagePath="tasks\netstandard2.0\" Pack="true"/>
|
||||
<None Include="@(TaskAssemblyNet46)" PackagePath="tasks\net46\" Pack="true" />
|
||||
<None Include="@(TaskAssemblyNetStandard)" PackagePath="tasks\netstandard2.0\" Pack="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<Error Text="TaskAssemblyNet46 is empty. This is a bug" Condition="'@(TaskAssemblyNet46)'==''"/>
|
||||
<Error Text="TaskAssemblyNetStandard is empty. This is a bug" Condition="'@(TaskAssemblyNetStandard)'==''"/>
|
||||
<Error Text="TaskAssemblyNet46 is empty. This is a bug" Condition="'@(TaskAssemblyNet46)'==''" />
|
||||
<Error Text="TaskAssemblyNetStandard is empty. This is a bug" Condition="'@(TaskAssemblyNetStandard)'==''" />
|
||||
|
||||
<!--
|
||||
Next we need to build the netcoreapp2.0 tools. In this case we need to do a publish, because we need
|
||||
all of the output to put in the package.
|
||||
-->
|
||||
<RemoveDir Directories="tools\"/>
|
||||
<MSBuild Projects="@(ToolProject)"/>
|
||||
<MSBuild
|
||||
Projects="@(ToolProject)"
|
||||
Properties="PublishDir=$(MSBuildProjectDirectory)\$(OutputPath)tools\"
|
||||
Targets="Publish"/>
|
||||
<RemoveDir Directories="tools\" />
|
||||
<MSBuild Projects="@(ToolProject)" />
|
||||
<MSBuild Projects="@(ToolProject)" Properties="PublishDir=$(MSBuildProjectDirectory)\$(OutputPath)tools\" Targets="Publish" />
|
||||
|
||||
<ItemGroup>
|
||||
<_RazorTool Include="$(OutputPath)tools\**\*" Exclude="$(OutputPath)tools\**\*.xml;$(OutputPath)tools\**\*.pdb" />
|
||||
|
|
@ -116,6 +108,6 @@
|
|||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<Error Text="_RazorTool is empty. This is a bug" Condition="'@(_RazorTool)'==''"/>
|
||||
<Error Text="_RazorTool is empty. This is a bug" Condition="'@(_RazorTool)'==''" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
<!-- Used to locate our tools -->
|
||||
<_RazorGenerateToolAssembly>$(_RazorMSBuildRoot)tools\Microsoft.AspNetCore.Razor.GenerateTool.dll</_RazorGenerateToolAssembly>
|
||||
<_RazorTagHelperToolAssembly>$(_RazorMSBuildRoot)tools\Microsoft.AspNetCore.Razor.TagHelperTool.dll</_RazorTagHelperToolAssembly>
|
||||
<_RazorBuildServerAssembly>$(_RazorMSBuildRoot)tools\rzc.dll</_RazorBuildServerAssembly>
|
||||
|
||||
<!-- Used to hash file inputs for RazorGenerate -->
|
||||
<_RazorGenerateInputsHash></_RazorGenerateInputsHash>
|
||||
|
|
@ -74,8 +75,10 @@
|
|||
Debug="$(_RazorDebugTagHelperTask)"
|
||||
DebugTool="$(_RazorDebugTagHelperTool)"
|
||||
ToolAssembly="$(_RazorTagHelperToolAssembly)"
|
||||
UseServer="$(UseRazorBuildServer)"
|
||||
ServerAssembly="$(_RazorBuildServerAssembly)"
|
||||
Assemblies="@(RazorReferencePath)"
|
||||
ProjectRoot="$(MSBuildProjectDirectory)"
|
||||
TagHelperManifest="$(_RazorTagHelperOutputCache)">
|
||||
<Output
|
||||
TaskParameter="TagHelperManifest"
|
||||
|
|
@ -107,6 +110,8 @@
|
|||
Debug="$(_RazorDebugGenerateCodeTask)"
|
||||
DebugTool="$(_RazorDebugGenerateCodeTool)"
|
||||
ToolAssembly="$(_RazorGenerateToolAssembly)"
|
||||
UseServer="$(UseRazorBuildServer)"
|
||||
ServerAssembly="$(_RazorBuildServerAssembly)"
|
||||
Sources="@(RazorGenerate)"
|
||||
ProjectRoot="$(MSBuildProjectDirectory)"
|
||||
TagHelperManifest="$(_RazorTagHelperOutputCache)"
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
so that it only shows up in supported versions.
|
||||
-->
|
||||
<IsRazorCompilerReferenced>true</IsRazorCompilerReferenced>
|
||||
|
||||
|
||||
<!-- Override this to hijack the tasks and targets. Used by tests. -->
|
||||
<_RazorMSBuildRoot Condition="'$(_RazorMSBuildRoot)'==''">$(MSBuildThisFileDirectory)..\..\</_RazorMSBuildRoot>
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,10 @@
|
|||
|
||||
<!-- Default to on if MvcRazorCompileOnPublish isn't set for some reason -->
|
||||
<RazorCompileOnPublish Condition="'$(RazorCompileOnPublish)'==''">true</RazorCompileOnPublish>
|
||||
|
||||
<UseRazorBuildServer Condition="'$(UseRazorBuildServer)'==''">false</UseRazorBuildServer>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<!--
|
||||
Properties that configure Razor SDK, but need to be defined in targets due to evaluation order.
|
||||
-->
|
||||
|
|
@ -121,14 +122,14 @@
|
|||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
These are the targets that generate code using Razor, separated for the main file for ease of maintenance.
|
||||
These are the targets that generate code using Razor, separated from the main file for ease of maintenance.
|
||||
|
||||
Most targets related to Razor code generation are defined there.
|
||||
-->
|
||||
<Import Project="Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets" />
|
||||
|
||||
<!--
|
||||
These are the targets that actually do compilation using CSC, separated for the main file for ease of maintenance.
|
||||
These are the targets that actually do compilation using CSC, separated from the main file for ease of maintenance.
|
||||
|
||||
RazorCoreCompile should be defined there.
|
||||
-->
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelperTool
|
|||
|
||||
HelpOption("-?|-h|--help");
|
||||
|
||||
TagHelperManifest = Option("-o", "output file", CommandOptionType.SingleValue);
|
||||
Assemblies = Argument("assemblies", "assemblies to search for tag helpers", multipleValues: true);
|
||||
TagHelperManifest = Option("-o", "output file", CommandOptionType.SingleValue);
|
||||
ProjectRoot = Option("-p", "project root directory", CommandOptionType.SingleValue);
|
||||
|
||||
new RunCommand().Configure(this);
|
||||
}
|
||||
|
|
@ -30,6 +31,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelperTool
|
|||
|
||||
public CommandOption TagHelperManifest { get; }
|
||||
|
||||
public CommandOption ProjectRoot { get; }
|
||||
|
||||
public new int Execute(params string[] args)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -32,12 +32,15 @@ namespace Microsoft.AspNetCore.Razor.TagHelperTool
|
|||
}
|
||||
|
||||
return ExecuteCore(
|
||||
projectDirectory: application.ProjectRoot.Value(),
|
||||
outputFilePath: application.TagHelperManifest.Value(),
|
||||
assemblies: application.Assemblies.Values.ToArray());
|
||||
}
|
||||
|
||||
private int ExecuteCore(string outputFilePath, string[] assemblies)
|
||||
private int ExecuteCore(string projectDirectory, string outputFilePath, string[] assemblies)
|
||||
{
|
||||
outputFilePath = Path.Combine(projectDirectory, outputFilePath);
|
||||
|
||||
var metadataReferences = new MetadataReference[assemblies.Length];
|
||||
for (var i = 0; i < assemblies.Length; i++)
|
||||
{
|
||||
|
|
@ -147,6 +150,11 @@ namespace Microsoft.AspNetCore.Razor.TagHelperTool
|
|||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(application.ProjectRoot.Value()))
|
||||
{
|
||||
application.ProjectRoot.Values.Add(Environment.CurrentDirectory);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,24 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Razor.Tools;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.CodeAnalysis.CommandLine;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Roslyn.Utilities;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tasks
|
||||
{
|
||||
public abstract class DotNetToolTask : ToolTask
|
||||
{
|
||||
private CancellationTokenSource _razorServerCts;
|
||||
|
||||
public bool Debug { get; set; }
|
||||
|
||||
public bool DebugTool { get; set; }
|
||||
|
|
@ -19,6 +27,11 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
[Required]
|
||||
public string ToolAssembly { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ServerAssembly { get; set; }
|
||||
|
||||
public bool UseServer { get; set; }
|
||||
|
||||
protected override string ToolName => "dotnet";
|
||||
|
||||
// If we're debugging then make all of the stdout gets logged in MSBuild
|
||||
|
|
@ -26,6 +39,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
|
||||
protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High;
|
||||
|
||||
internal abstract RequestCommand Command { get; }
|
||||
|
||||
protected override string GenerateFullPathToTool()
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
|
|
@ -62,12 +77,19 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
}
|
||||
}
|
||||
|
||||
if (TryExecuteOnServer(out var result))
|
||||
return base.Execute();
|
||||
}
|
||||
|
||||
protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
|
||||
{
|
||||
if (UseServer &&
|
||||
!string.IsNullOrEmpty(ServerAssembly) &&
|
||||
TryExecuteOnServer(pathToTool, responseFileCommands, commandLineCommands, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return base.Execute();
|
||||
return base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands);
|
||||
}
|
||||
|
||||
protected override void LogToolCommand(string message)
|
||||
|
|
@ -82,10 +104,80 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
}
|
||||
}
|
||||
|
||||
protected virtual bool TryExecuteOnServer(out bool result)
|
||||
public override void Cancel()
|
||||
{
|
||||
result = false;
|
||||
base.Cancel();
|
||||
|
||||
_razorServerCts?.Cancel();
|
||||
}
|
||||
|
||||
protected virtual bool TryExecuteOnServer(string pathToTool, string responseFileCommands, string commandLineCommands, out int result)
|
||||
{
|
||||
CompilerServerLogger.Log("Server execution started.");
|
||||
using (_razorServerCts = new CancellationTokenSource())
|
||||
{
|
||||
CompilerServerLogger.Log($"CommandLine = '{commandLineCommands}'");
|
||||
CompilerServerLogger.Log($"BuildResponseFile = '{responseFileCommands}'");
|
||||
|
||||
// The server contains the tools for discovering tag helpers and generating Razor code.
|
||||
var clientDir = Path.GetDirectoryName(ServerAssembly);
|
||||
var workingDir = CurrentDirectoryToUse();
|
||||
var tempDir = BuildServerConnection.GetTempPath(workingDir);
|
||||
|
||||
var buildPaths = new BuildPathsAlt(
|
||||
clientDir,
|
||||
// MSBuild doesn't need the .NET SDK directory
|
||||
sdkDir: null,
|
||||
workingDir: workingDir,
|
||||
tempDir: tempDir);
|
||||
|
||||
var responseTask = BuildServerConnection.RunServerCompilation(
|
||||
Command,
|
||||
GetArguments(responseFileCommands),
|
||||
buildPaths,
|
||||
keepAlive: null,
|
||||
cancellationToken: _razorServerCts.Token);
|
||||
|
||||
responseTask.Wait(_razorServerCts.Token);
|
||||
|
||||
var response = responseTask.Result;
|
||||
if (response.Type == BuildResponse.ResponseType.Completed &&
|
||||
response is CompletedBuildResponse completedResponse)
|
||||
{
|
||||
result = completedResponse.ReturnCode;
|
||||
|
||||
CompilerServerLogger.Log($"Server execution completed with return code {result}.");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
CompilerServerLogger.Log("Server execution failed.");
|
||||
result = -1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current directory that the compiler should run in.
|
||||
/// </summary>
|
||||
private string CurrentDirectoryToUse()
|
||||
{
|
||||
// ToolTask has a method for this. But it may return null. Use the process directory
|
||||
// if ToolTask didn't override. MSBuild uses the process directory.
|
||||
string workingDirectory = GetWorkingDirectory();
|
||||
if (string.IsNullOrEmpty(workingDirectory))
|
||||
{
|
||||
workingDirectory = Directory.GetCurrentDirectory();
|
||||
}
|
||||
return workingDirectory;
|
||||
}
|
||||
|
||||
private List<string> GetArguments(string responseFileCommands)
|
||||
{
|
||||
var responseFileArguments =
|
||||
CommandLineUtilities.SplitCommandLineIntoArguments(responseFileCommands, removeHashComments: true);
|
||||
return responseFileArguments.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,5 +15,30 @@
|
|||
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
||||
|
||||
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\Roslyn\BuildServerConnection.cs">
|
||||
<Link>Shared\BuildServerConnection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\Roslyn\NativeMethods.cs">
|
||||
<Link>Shared\NativeMethods.cs</Link>
|
||||
</Compile>
|
||||
<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\Roslyn\BuildProtocol.cs">
|
||||
<Link>Shared\BuildProtocol.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\Roslyn\CommandLineUtilities.cs">
|
||||
<Link>Shared\CommandLineUtilities.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\PipeName.cs">
|
||||
<Link>Shared\PipeName.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Microsoft.AspNetCore.Razor.Tools\MutexName.cs">
|
||||
<Link>Shared\MutexName.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.Text;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.CodeAnalysis.CommandLine;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tasks
|
||||
{
|
||||
|
|
@ -20,6 +21,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
[Required]
|
||||
public string TagHelperManifest { get; set; }
|
||||
|
||||
internal override RequestCommand Command => RequestCommand.RazorGenerate;
|
||||
|
||||
protected override string GenerateResponseFileCommands()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.CodeAnalysis.CommandLine;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tasks
|
||||
{
|
||||
|
|
@ -17,7 +18,9 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
[Required]
|
||||
public string TagHelperManifest { get; set; }
|
||||
|
||||
public string ServerAssembly { get; set; }
|
||||
public string ProjectRoot { get; set; }
|
||||
|
||||
internal override RequestCommand Command => RequestCommand.RazorTagHelper;
|
||||
|
||||
protected override bool SkipTaskExecution()
|
||||
{
|
||||
|
|
@ -43,6 +46,9 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
builder.AppendLine("-o");
|
||||
builder.AppendLine(TagHelperManifest);
|
||||
|
||||
builder.AppendLine("-p");
|
||||
builder.AppendLine(ProjectRoot);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
|
||||
Commands.Add(new DiscoverCommand(this));
|
||||
Commands.Add(new ServerCommand(this));
|
||||
Commands.Add(new ShutdownCommand(this));
|
||||
}
|
||||
|
||||
public CancellationToken CancellationToken { get; }
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis.CommandLine;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
|
|
@ -25,7 +27,23 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
return new RejectedBuildResponse();
|
||||
}
|
||||
|
||||
return null;
|
||||
var app = new Application(cancellationToken);
|
||||
var commandArgs = parsed.args.ToArray();
|
||||
|
||||
CommandBase command = null;
|
||||
if (request.Command == RequestCommand.RazorGenerate)
|
||||
{
|
||||
command = new GenerateCommand(app);
|
||||
}
|
||||
else if (request.Command == RequestCommand.RazorTagHelper)
|
||||
{
|
||||
command = new DiscoverCommand(app);
|
||||
}
|
||||
|
||||
var exitCode = command?.Execute(commandArgs) ?? 0;
|
||||
var output = command?.Out.ToString() ?? string.Empty;
|
||||
|
||||
return new CompletedBuildResponse(exitCode, utf8output: false, output: output);
|
||||
}
|
||||
|
||||
private bool TryParseArguments(BuildRequest request, out (string workingDirectory, string tempDirectory, string[] args) parsed)
|
||||
|
|
@ -33,14 +51,10 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
string workingDirectory = null;
|
||||
string tempDirectory = null;
|
||||
|
||||
// The parsed arguments will contain 'string.Empty' in place of the arguments that we don't want to pass
|
||||
// to the compiler.
|
||||
var args = new List<string>(request.Arguments.Count);
|
||||
|
||||
for (var i = 0; i < request.Arguments.Count; i++)
|
||||
{
|
||||
args[i] = string.Empty;
|
||||
|
||||
var argument = request.Arguments[i];
|
||||
if (argument.ArgumentId == BuildProtocolConstants.ArgumentId.CurrentDirectory)
|
||||
{
|
||||
|
|
@ -52,7 +66,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
}
|
||||
else if (argument.ArgumentId == BuildProtocolConstants.ArgumentId.CommandLineArgument)
|
||||
{
|
||||
args[i] = argument.Value;
|
||||
args.Add(argument.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,14 +23,17 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
public DiscoverCommand(Application parent)
|
||||
: base(parent, "discover")
|
||||
{
|
||||
TagHelperManifest = Option("-o", "output file", CommandOptionType.SingleValue);
|
||||
Assemblies = Argument("assemblies", "assemblies to search for tag helpers", multipleValues: true);
|
||||
TagHelperManifest = Option("-o", "output file", CommandOptionType.SingleValue);
|
||||
ProjectDirectory = Option("-p", "project root directory", CommandOptionType.SingleValue);
|
||||
}
|
||||
|
||||
public CommandArgument Assemblies { get; }
|
||||
|
||||
public CommandOption TagHelperManifest { get; }
|
||||
|
||||
public CommandOption ProjectDirectory { get; }
|
||||
|
||||
protected override bool ValidateArguments()
|
||||
{
|
||||
if (string.IsNullOrEmpty(TagHelperManifest.Value()))
|
||||
|
|
@ -45,19 +48,27 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ProjectDirectory.Value()))
|
||||
{
|
||||
ProjectDirectory.Values.Add(Environment.CurrentDirectory);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override Task<int> ExecuteCoreAsync()
|
||||
{
|
||||
var result = ExecuteCore(
|
||||
projectDirectory: ProjectDirectory.Value(),
|
||||
outputFilePath: TagHelperManifest.Value(),
|
||||
assemblies: Assemblies.Values.ToArray());
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private int ExecuteCore(string outputFilePath, string[] assemblies)
|
||||
private int ExecuteCore(string projectDirectory, string outputFilePath, string[] assemblies)
|
||||
{
|
||||
outputFilePath = Path.Combine(projectDirectory, outputFilePath);
|
||||
|
||||
var metadataReferences = new MetadataReference[assemblies.Length];
|
||||
for (var i = 0; i < assemblies.Length; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,204 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
{
|
||||
internal class GenerateCommand : CommandBase
|
||||
{
|
||||
public GenerateCommand(Application parent)
|
||||
: base(parent, "generate")
|
||||
{
|
||||
Sources = Argument("sources", ".cshtml files to compile", multipleValues: true);
|
||||
ProjectDirectory = Option("-p", "project root directory", CommandOptionType.SingleValue);
|
||||
OutputDirectory = Option("-o", "output directory", CommandOptionType.SingleValue);
|
||||
TagHelperManifest = Option("-t", "tag helper manifest file", CommandOptionType.SingleValue);
|
||||
}
|
||||
|
||||
public CommandArgument Sources { get; }
|
||||
|
||||
public CommandOption OutputDirectory { get; }
|
||||
|
||||
public CommandOption ProjectDirectory { get; }
|
||||
|
||||
public CommandOption TagHelperManifest { get; }
|
||||
|
||||
protected override Task<int> ExecuteCoreAsync()
|
||||
{
|
||||
var result = ExecuteCore(
|
||||
projectDirectory: ProjectDirectory.Value() ?? Environment.CurrentDirectory,
|
||||
outputDirectory: OutputDirectory.Value(),
|
||||
tagHelperManifest: TagHelperManifest.Value(),
|
||||
sources: Sources.Values.ToArray());
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
protected override bool ValidateArguments()
|
||||
{
|
||||
if (string.IsNullOrEmpty(OutputDirectory.Value()))
|
||||
{
|
||||
Error.WriteLine($"{OutputDirectory.ValueName} not specified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Sources.Values.Count == 0)
|
||||
{
|
||||
Error.WriteLine($"{Sources.Name} should have at least one value.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int ExecuteCore(string projectDirectory, string outputDirectory, string tagHelperManifest, string[] sources)
|
||||
{
|
||||
tagHelperManifest = Path.Combine(projectDirectory, tagHelperManifest);
|
||||
outputDirectory = Path.Combine(projectDirectory, outputDirectory);
|
||||
|
||||
var tagHelpers = GetTagHelpers(tagHelperManifest);
|
||||
|
||||
var engine = RazorEngine.Create(b =>
|
||||
{
|
||||
RazorExtensions.Register(b);
|
||||
|
||||
b.Features.Add(new StaticTagHelperFeature() { TagHelpers = tagHelpers, });
|
||||
});
|
||||
|
||||
var templateEngine = new MvcRazorTemplateEngine(engine, RazorProject.Create(projectDirectory));
|
||||
|
||||
var sourceItems = GetRazorFiles(projectDirectory, sources);
|
||||
var results = GenerateCode(templateEngine, sourceItems);
|
||||
|
||||
var success = true;
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.CSharpDocument.Diagnostics.Count > 0)
|
||||
{
|
||||
success = false;
|
||||
foreach (var error in result.CSharpDocument.Diagnostics)
|
||||
{
|
||||
Console.Error.WriteLine(error.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
var outputFilePath = Path.Combine(outputDirectory, Path.ChangeExtension(result.ViewFileInfo.ViewEnginePath.Substring(1), ".cs"));
|
||||
File.WriteAllText(outputFilePath, result.CSharpDocument.GeneratedCode);
|
||||
}
|
||||
|
||||
return success ? 0 : -1;
|
||||
}
|
||||
|
||||
private IReadOnlyList<TagHelperDescriptor> GetTagHelpers(string tagHelperManifest)
|
||||
{
|
||||
if (!File.Exists(tagHelperManifest))
|
||||
{
|
||||
return Array.Empty<TagHelperDescriptor>();
|
||||
}
|
||||
|
||||
using (var stream = File.OpenRead(tagHelperManifest))
|
||||
{
|
||||
var reader = new JsonTextReader(new StreamReader(stream));
|
||||
|
||||
var serializer = new JsonSerializer();
|
||||
serializer.Converters.Add(new RazorDiagnosticJsonConverter());
|
||||
serializer.Converters.Add(new TagHelperDescriptorJsonConverter());
|
||||
|
||||
return serializer.Deserialize<IReadOnlyList<TagHelperDescriptor>>(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private List<SourceItem> GetRazorFiles(string projectDirectory, string[] sources)
|
||||
{
|
||||
var trimLength = projectDirectory.EndsWith("/") ? projectDirectory.Length - 1 : projectDirectory.Length;
|
||||
|
||||
var items = new List<SourceItem>(sources.Length);
|
||||
for (var i = 0; i < sources.Length; i++)
|
||||
{
|
||||
var fullPath = Path.Combine(projectDirectory, sources[i]);
|
||||
if (fullPath.StartsWith(projectDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var viewEnginePath = fullPath.Substring(trimLength).Replace('\\', '/');
|
||||
items.Add(new SourceItem(fullPath, viewEnginePath));
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private OutputItem[] GenerateCode(RazorTemplateEngine templateEngine, IReadOnlyList<SourceItem> sources)
|
||||
{
|
||||
var outputs = new OutputItem[sources.Count];
|
||||
Parallel.For(0, outputs.Length, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, i =>
|
||||
{
|
||||
var source = sources[i];
|
||||
|
||||
var csharpDocument = templateEngine.GenerateCode(source.ViewEnginePath);
|
||||
outputs[i] = new OutputItem(source, csharpDocument);
|
||||
});
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
private struct OutputItem
|
||||
{
|
||||
public OutputItem(
|
||||
SourceItem viewFileInfo,
|
||||
RazorCSharpDocument cSharpDocument)
|
||||
{
|
||||
ViewFileInfo = viewFileInfo;
|
||||
CSharpDocument = cSharpDocument;
|
||||
}
|
||||
|
||||
public SourceItem ViewFileInfo { get; }
|
||||
|
||||
public RazorCSharpDocument CSharpDocument { get; }
|
||||
}
|
||||
|
||||
private struct SourceItem
|
||||
{
|
||||
public SourceItem(string fullPath, string viewEnginePath)
|
||||
{
|
||||
FullPath = fullPath;
|
||||
ViewEnginePath = viewEnginePath;
|
||||
}
|
||||
|
||||
public string FullPath { get; }
|
||||
|
||||
public string ViewEnginePath { get; }
|
||||
|
||||
public Stream CreateReadStream()
|
||||
{
|
||||
// We are setting buffer size to 1 to prevent FileStream from allocating it's internal buffer
|
||||
// 0 causes constructor to throw
|
||||
var bufferSize = 1;
|
||||
return new FileStream(
|
||||
FullPath,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.ReadWrite,
|
||||
bufferSize,
|
||||
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
}
|
||||
}
|
||||
|
||||
private class StaticTagHelperFeature : ITagHelperFeature
|
||||
{
|
||||
public RazorEngine Engine { get; set; }
|
||||
|
||||
public IReadOnlyList<TagHelperDescriptor> TagHelpers { get; set; }
|
||||
|
||||
public IReadOnlyList<TagHelperDescriptor> GetDescriptors() => TagHelpers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
return null;
|
||||
}
|
||||
|
||||
return $"{userName}.{isAdmin}.{baseName}";
|
||||
return $"{userName}.{(isAdmin ? 'T' : 'F')}.{baseName}";
|
||||
}
|
||||
|
||||
private static string ComputeBaseName(string baseDirectory)
|
||||
|
|
@ -59,6 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
{
|
||||
var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(baseDirectory));
|
||||
return Convert.ToBase64String(bytes)
|
||||
.Substring(0, 25) // We only have ~50 total characters on Mac, so strip that down
|
||||
.Replace("/", "_")
|
||||
.Replace("=", string.Empty);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -446,10 +446,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
{
|
||||
CompilerServerLogger.Log("Begin processing request");
|
||||
|
||||
|
||||
// TODO: this is where we actually process the request.
|
||||
// Take a look at BuildProtocolUtil
|
||||
var response = (BuildResponse)null;
|
||||
var response = _compilerHost.Execute(buildRequest, cancellationToken);
|
||||
|
||||
CompilerServerLogger.Log("End processing request");
|
||||
return response;
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
internal class BuildRequest
|
||||
{
|
||||
public readonly uint ProtocolVersion;
|
||||
public readonly RequestLanguage Language;
|
||||
public readonly RequestCommand Command;
|
||||
public readonly ReadOnlyCollection<Argument> Arguments;
|
||||
|
||||
public BuildRequest(uint protocolVersion, RequestLanguage language, IEnumerable<Argument> arguments)
|
||||
public BuildRequest(uint protocolVersion, RequestCommand command, IEnumerable<Argument> arguments)
|
||||
{
|
||||
ProtocolVersion = protocolVersion;
|
||||
Language = language;
|
||||
Command = command;
|
||||
Arguments = new ReadOnlyCollection<Argument>(arguments.ToList());
|
||||
|
||||
if (Arguments.Count > ushort.MaxValue)
|
||||
|
|
@ -80,7 +80,7 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
}
|
||||
|
||||
public static BuildRequest Create(
|
||||
RequestLanguage language,
|
||||
RequestCommand command,
|
||||
string workingDirectory,
|
||||
string tempDirectory,
|
||||
IList<string> args,
|
||||
|
|
@ -115,13 +115,13 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
requestArgs.Add(new Argument(ArgumentId.CommandLineArgument, i, arg));
|
||||
}
|
||||
|
||||
return new BuildRequest(BuildProtocolConstants.ProtocolVersion, language, requestArgs);
|
||||
return new BuildRequest(BuildProtocolConstants.ProtocolVersion, command, requestArgs);
|
||||
}
|
||||
|
||||
public static BuildRequest CreateShutdown()
|
||||
{
|
||||
var requestArgs = new[] { new Argument(ArgumentId.Shutdown, argumentIndex: 0, value: "") };
|
||||
return new BuildRequest(BuildProtocolConstants.ProtocolVersion, RequestLanguage.CSharpCompile, requestArgs);
|
||||
return new BuildRequest(BuildProtocolConstants.ProtocolVersion, RequestCommand.None, requestArgs);
|
||||
}
|
||||
|
||||
public bool IsShutdownRequest()
|
||||
|
|
@ -163,7 +163,7 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
using (var reader = new BinaryReader(new MemoryStream(requestBuffer), Encoding.Unicode))
|
||||
{
|
||||
var protocolVersion = reader.ReadUInt32();
|
||||
var language = (RequestLanguage)reader.ReadUInt32();
|
||||
var command = (RequestCommand)reader.ReadUInt32();
|
||||
uint argumentCount = reader.ReadUInt32();
|
||||
|
||||
var argumentsBuilder = new List<Argument>((int)argumentCount);
|
||||
|
|
@ -175,7 +175,7 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
}
|
||||
|
||||
return new BuildRequest(protocolVersion,
|
||||
language,
|
||||
command,
|
||||
argumentsBuilder);
|
||||
}
|
||||
}
|
||||
|
|
@ -191,7 +191,7 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
// Format the request.
|
||||
Log("Formatting request");
|
||||
writer.Write(ProtocolVersion);
|
||||
writer.Write((uint)Language);
|
||||
writer.Write((uint)Command);
|
||||
writer.Write(Arguments.Count);
|
||||
foreach (Argument arg in Arguments)
|
||||
{
|
||||
|
|
@ -506,10 +506,11 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
|
||||
// The id numbers below are just random. It's useful to use id numbers
|
||||
// that won't occur accidentally for debugging.
|
||||
internal enum RequestLanguage
|
||||
internal enum RequestCommand
|
||||
{
|
||||
CSharpCompile = 0x44532521,
|
||||
VisualBasicCompile = 0x44532522,
|
||||
None = 0x44532621,
|
||||
RazorTagHelper = 0x44532622,
|
||||
RazorGenerate = 0x44532623,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,69 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis.CommandLine;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.CompilerServer
|
||||
{
|
||||
internal static class BuildProtocolUtil
|
||||
{
|
||||
internal static RunRequest GetRunRequest(BuildRequest req)
|
||||
{
|
||||
string currentDirectory;
|
||||
string libDirectory;
|
||||
string tempDirectory;
|
||||
string[] arguments = GetCommandLineArguments(req, out currentDirectory, out tempDirectory, out libDirectory);
|
||||
string language = "";
|
||||
switch (req.Language)
|
||||
{
|
||||
case RequestLanguage.CSharpCompile:
|
||||
language = LanguageNames.CSharp;
|
||||
break;
|
||||
case RequestLanguage.VisualBasicCompile:
|
||||
language = LanguageNames.VisualBasic;
|
||||
break;
|
||||
}
|
||||
|
||||
return new RunRequest(language, currentDirectory, tempDirectory, libDirectory, arguments);
|
||||
}
|
||||
|
||||
internal static string[] GetCommandLineArguments(BuildRequest req, out string currentDirectory, out string tempDirectory, out string libDirectory)
|
||||
{
|
||||
currentDirectory = null;
|
||||
libDirectory = null;
|
||||
tempDirectory = null;
|
||||
List<string> commandLineArguments = new List<string>();
|
||||
|
||||
foreach (BuildRequest.Argument arg in req.Arguments)
|
||||
{
|
||||
if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.CurrentDirectory)
|
||||
{
|
||||
currentDirectory = arg.Value;
|
||||
}
|
||||
else if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.TempDirectory)
|
||||
{
|
||||
tempDirectory = arg.Value;
|
||||
}
|
||||
else if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.LibEnvVariable)
|
||||
{
|
||||
libDirectory = arg.Value;
|
||||
}
|
||||
else if (arg.ArgumentId == BuildProtocolConstants.ArgumentId.CommandLineArgument)
|
||||
{
|
||||
int argIndex = arg.ArgumentIndex;
|
||||
while (argIndex >= commandLineArguments.Count)
|
||||
commandLineArguments.Add("");
|
||||
commandLineArguments[argIndex] = arg.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return commandLineArguments.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,12 +8,10 @@ using System.IO;
|
|||
using System.IO.Pipes;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Tools;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using Roslyn.Utilities;
|
||||
using static Microsoft.CodeAnalysis.CommandLine.CompilerServerLogger;
|
||||
|
|
@ -56,8 +54,7 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
|
||||
internal sealed class BuildServerConnection
|
||||
{
|
||||
internal const string ServerNameDesktop = "VBCSCompiler.exe";
|
||||
internal const string ServerNameCoreClr = "VBCSCompiler.dll";
|
||||
internal const string ServerNameCoreClr = "rzc.dll";
|
||||
|
||||
// Spend up to 1s connecting to existing process (existing processes should be always responsive).
|
||||
internal const int TimeOutMsExistingProcess = 1000;
|
||||
|
|
@ -65,8 +62,29 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
// Spend up to 20s connecting to a new process, to allow time for it to start.
|
||||
internal const int TimeOutMsNewProcess = 20000;
|
||||
|
||||
public static Task<BuildResponse> RunServerCompilation(
|
||||
RequestCommand command,
|
||||
List<string> arguments,
|
||||
BuildPathsAlt buildPaths,
|
||||
string keepAlive,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var pipeName = PipeName.ComputeDefault();
|
||||
|
||||
return RunServerCompilationCore(
|
||||
command,
|
||||
arguments,
|
||||
buildPaths,
|
||||
pipeName: pipeName,
|
||||
keepAlive: keepAlive,
|
||||
libEnvVariable: null,
|
||||
timeoutOverride: null,
|
||||
tryCreateServerFunc: TryCreateServerCore,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
internal static async Task<BuildResponse> RunServerCompilationCore(
|
||||
RequestLanguage language,
|
||||
RequestCommand language,
|
||||
List<string> arguments,
|
||||
BuildPathsAlt buildPaths,
|
||||
string pipeName,
|
||||
|
|
@ -89,7 +107,7 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
var clientDir = buildPaths.ClientDirectory;
|
||||
var timeoutNewProcess = timeoutOverride ?? TimeOutMsNewProcess;
|
||||
var timeoutExistingProcess = timeoutOverride ?? TimeOutMsExistingProcess;
|
||||
var clientMutexName = GetClientMutexName(pipeName);
|
||||
var clientMutexName = MutexName.GetClientMutexName(pipeName);
|
||||
Task<NamedPipeClientStream> pipeTask = null;
|
||||
using (var clientMutex = new Mutex(initiallyOwned: true,
|
||||
name: clientMutexName,
|
||||
|
|
@ -115,8 +133,8 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
}
|
||||
|
||||
// Check for an already running server
|
||||
var serverMutexName = GetServerMutexName(pipeName);
|
||||
bool wasServerRunning = true;
|
||||
var serverMutexName = MutexName.GetServerMutexName(pipeName);
|
||||
bool wasServerRunning = WasServerMutexOpen(serverMutexName);
|
||||
var timeout = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess;
|
||||
|
||||
if (wasServerRunning || tryCreateServerFunc(clientDir, pipeName))
|
||||
|
|
@ -152,6 +170,18 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
return new RejectedBuildResponse();
|
||||
}
|
||||
|
||||
internal static bool WasServerMutexOpen(string mutexName)
|
||||
{
|
||||
Mutex mutex;
|
||||
var open = Mutex.TryOpenExisting(mutexName, out mutex);
|
||||
if (open)
|
||||
{
|
||||
mutex.Dispose();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to compile using the server. Returns a null-containing Task if a response
|
||||
/// from the server cannot be retrieved.
|
||||
|
|
@ -312,31 +342,17 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
|
||||
internal static bool TryCreateServerCore(string clientDir, string pipeName)
|
||||
{
|
||||
bool isRunningOnCoreClr = true;
|
||||
string expectedPath;
|
||||
string processArguments;
|
||||
if (isRunningOnCoreClr)
|
||||
{
|
||||
// The server should be in the same directory as the client
|
||||
var expectedCompilerPath = Path.Combine(clientDir, ServerNameCoreClr);
|
||||
expectedPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? "dotnet";
|
||||
processArguments = $@"""{expectedCompilerPath}"" ""-pipename:{pipeName}""";
|
||||
|
||||
if (!File.Exists(expectedCompilerPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The server should be in the same directory as the client
|
||||
expectedPath = Path.Combine(clientDir, ServerNameDesktop);
|
||||
processArguments = $@"""-pipename:{pipeName}""";
|
||||
// The server should be in the same directory as the client
|
||||
var expectedCompilerPath = Path.Combine(clientDir, ServerNameCoreClr);
|
||||
expectedPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? "dotnet";
|
||||
processArguments = $@"""{expectedCompilerPath}"" server -p {pipeName}";
|
||||
|
||||
if (!File.Exists(expectedPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!File.Exists(expectedCompilerPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PlatformInformation.IsWindows)
|
||||
|
|
@ -456,17 +472,6 @@ namespace Microsoft.CodeAnalysis.CommandLine
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
internal static string GetServerMutexName(string pipeName)
|
||||
{
|
||||
return $"{pipeName}.server";
|
||||
}
|
||||
|
||||
internal static string GetClientMutexName(string pipeName)
|
||||
{
|
||||
return $"{pipeName}.client";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the temporary path for the current environment assuming the working directory
|
||||
/// is <paramref name="workingDir"/>. This function must emulate <see cref="Path.GetTempPath"/> as
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
// 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)
|
||||
{
|
||||
char? unused;
|
||||
return SplitCommandLineIntoArguments(commandLine, removeHashComments, out 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue