Remove Auto-Rebuild dead-code
Fixes: #17248 Removes the (*unused*) auto-rebuild code left over from early Blazor previews.
This commit is contained in:
parent
3b91f2d787
commit
82cbdc604e
|
|
@ -1,184 +0,0 @@
|
|||
// 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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Blazor.Server;
|
||||
using Microsoft.AspNetCore.Blazor.Server.AutoRebuild;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
internal static class AutoRebuildExtensions
|
||||
{
|
||||
// Note that we don't need to watch typical static-file extensions (.css, .js, etc.)
|
||||
// because anything in wwwroot is just served directly from disk on each reload.
|
||||
// TODO: Make the set of extensions and exclusions configurable in csproj
|
||||
private static string[] _includedSuffixes = new[] { ".cs", ".cshtml" };
|
||||
private static string[] _excludedDirectories = new[] { "obj", "bin" };
|
||||
|
||||
// To ensure the FileSystemWatchers aren't collected, reference them
|
||||
// in this static list. They never need to be removed because there's no
|
||||
// way to remove middleware once it's registered.
|
||||
private static List<object> _uncollectableWatchers = new List<object>();
|
||||
|
||||
public static void UseHostedAutoRebuild(this IApplicationBuilder app, BlazorConfig config, string hostAppContentRootPath)
|
||||
{
|
||||
var isFirstFileWrite = true;
|
||||
WatchFileSystem(config, () =>
|
||||
{
|
||||
if (isFirstFileWrite)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Touch any .cs file to force the host project to rebuild
|
||||
// (which in turn rebuilds the client, since it's referenced)
|
||||
var fileToTouch = Directory.EnumerateFiles(
|
||||
hostAppContentRootPath,
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories).FirstOrDefault();
|
||||
|
||||
if (!string.IsNullOrEmpty(fileToTouch))
|
||||
{
|
||||
File.SetLastWriteTime(fileToTouch, DateTime.Now);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// If we don't have permission to write these files, autorebuild will not be enabled
|
||||
var loggerFactory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger(typeof (AutoRebuildExtensions));
|
||||
logger?.LogWarning(ex,
|
||||
"Cannot autorebuild because there was an error when writing to a file in '{0}'.",
|
||||
hostAppContentRootPath);
|
||||
}
|
||||
|
||||
isFirstFileWrite = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void UseDevServerAutoRebuild(this IApplicationBuilder app, BlazorConfig config)
|
||||
{
|
||||
// Currently this only supports VS for Windows. Later on we can add
|
||||
// an IRebuildService implementation for VS for Mac, etc.
|
||||
if (!VSForWindowsRebuildService.TryCreate(out var rebuildService))
|
||||
{
|
||||
return; // You're not on Windows, or you didn't launch this process from VS
|
||||
}
|
||||
|
||||
// Assume we're up to date when the app starts.
|
||||
var buildToken = new RebuildToken(new DateTime(1970, 1, 1)) { BuildTask = Task.CompletedTask, };
|
||||
|
||||
WatchFileSystem(config, () =>
|
||||
{
|
||||
// Don't start the recompilation immediately. We only start it when the next
|
||||
// HTTP request arrives, because it's annoying if the IDE is constantly rebuilding
|
||||
// when you're making changes to multiple files and aren't ready to reload
|
||||
// in the browser yet.
|
||||
//
|
||||
// Replacing the token means that new requests that come in will trigger a rebuild,
|
||||
// and will all 'join' that build until a new file change occurs.
|
||||
buildToken = new RebuildToken(DateTime.Now);
|
||||
});
|
||||
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var token = buildToken;
|
||||
if (token.BuildTask == null)
|
||||
{
|
||||
// The build is out of date, but a new build is not yet started.
|
||||
//
|
||||
// We can count on VS to only allow one build at a time, this is a safe race
|
||||
// because if we request a second concurrent build, it will 'join' the current one.
|
||||
var task = rebuildService.PerformRebuildAsync(
|
||||
config.SourceMSBuildPath,
|
||||
token.LastChange);
|
||||
token.BuildTask = task;
|
||||
}
|
||||
|
||||
// In the general case it's safe to await this task, it will be a completed task
|
||||
// if everything is up to date.
|
||||
await token.BuildTask;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If there's no listener on the other end of the pipe, or if anything
|
||||
// else goes wrong, we just let the incoming request continue.
|
||||
// There's nowhere useful to log this information so if people report
|
||||
// problems we'll just have to get a repro and debug it.
|
||||
// If it was an error on the VS side, it logs to the output window.
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
}
|
||||
|
||||
private static void WatchFileSystem(BlazorConfig config, Action onWrite)
|
||||
{
|
||||
var clientAppRootDir = Path.GetDirectoryName(config.SourceMSBuildPath);
|
||||
var excludePathPrefixes = _excludedDirectories.Select(subdir
|
||||
=> Path.Combine(clientAppRootDir, subdir) + Path.DirectorySeparatorChar);
|
||||
|
||||
var fsw = new FileSystemWatcher(clientAppRootDir);
|
||||
fsw.Created += OnEvent;
|
||||
fsw.Changed += OnEvent;
|
||||
fsw.Deleted += OnEvent;
|
||||
fsw.Renamed += OnEvent;
|
||||
fsw.IncludeSubdirectories = true;
|
||||
fsw.EnableRaisingEvents = true;
|
||||
|
||||
// Ensure the watcher is not GCed for as long as the app lives
|
||||
lock (_uncollectableWatchers)
|
||||
{
|
||||
_uncollectableWatchers.Add(fsw);
|
||||
}
|
||||
|
||||
void OnEvent(object sender, FileSystemEventArgs eventArgs)
|
||||
{
|
||||
if (!File.Exists(eventArgs.FullPath))
|
||||
{
|
||||
// It's probably a directory rather than a file
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_includedSuffixes.Any(ext => eventArgs.Name.EndsWith(ext, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// Not a candidate file type
|
||||
return;
|
||||
}
|
||||
|
||||
if (excludePathPrefixes.Any(prefix => eventArgs.FullPath.StartsWith(prefix, StringComparison.Ordinal)))
|
||||
{
|
||||
// In an excluded subdirectory
|
||||
return;
|
||||
}
|
||||
|
||||
onWrite();
|
||||
}
|
||||
}
|
||||
|
||||
// Represents a three-state value for the state of the build
|
||||
//
|
||||
// BuildTask == null means the build is out of date, but no build has started
|
||||
// BuildTask.IsCompleted == false means the build has been started, but has not completed
|
||||
// BuildTask.IsCompleted == true means the build has completed
|
||||
private class RebuildToken
|
||||
{
|
||||
public RebuildToken(DateTime lastChange)
|
||||
{
|
||||
LastChange = lastChange;
|
||||
}
|
||||
|
||||
public DateTime LastChange { get; }
|
||||
|
||||
public Task BuildTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.AutoRebuild
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mechanism for rebuilding a .NET project. For example, it
|
||||
/// could be a way of signalling to a VS process to perform a build.
|
||||
/// </summary>
|
||||
internal interface IRebuildService
|
||||
{
|
||||
Task<bool> PerformRebuildAsync(string projectFullPath, DateTime ifNotBuiltSince);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// 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.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.AutoRebuild
|
||||
{
|
||||
internal static class ProcessUtils
|
||||
{
|
||||
// Based on https://stackoverflow.com/a/3346055
|
||||
|
||||
public static Process GetParent(Process process)
|
||||
{
|
||||
var result = new ProcessBasicInformation();
|
||||
var handle = process.Handle;
|
||||
var status = NtQueryInformationProcess(handle, 0, ref result, Marshal.SizeOf(result), out var returnLength);
|
||||
if (status != 0)
|
||||
{
|
||||
throw new Win32Exception(status);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var parentProcessId = result.InheritedFromUniqueProcessId.ToInt32();
|
||||
return parentProcessId > 0 ? Process.GetProcessById(parentProcessId) : null;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return null; // Process not found
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("ntdll.dll")]
|
||||
private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ProcessBasicInformation processInformation, int processInformationLength, out int returnLength);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct ProcessBasicInformation
|
||||
{
|
||||
// These members must match PROCESS_BASIC_INFORMATION
|
||||
public IntPtr Reserved1;
|
||||
public IntPtr PebBaseAddress;
|
||||
public IntPtr Reserved2_0;
|
||||
public IntPtr Reserved2_1;
|
||||
public IntPtr UniqueProcessId;
|
||||
public IntPtr InheritedFromUniqueProcessId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// 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.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.AutoRebuild
|
||||
{
|
||||
internal static class StreamProtocolExtensions
|
||||
{
|
||||
public static async Task WriteStringAsync(this Stream stream, string str)
|
||||
{
|
||||
var utf8Bytes = Encoding.UTF8.GetBytes(str);
|
||||
await stream.WriteAsync(BitConverter.GetBytes(utf8Bytes.Length), 0, 4);
|
||||
await stream.WriteAsync(utf8Bytes, 0, utf8Bytes.Length);
|
||||
}
|
||||
|
||||
public static async Task WriteDateTimeAsync(this Stream stream, DateTime value)
|
||||
{
|
||||
var ticksBytes = BitConverter.GetBytes(value.Ticks);
|
||||
await stream.WriteAsync(ticksBytes, 0, 8);
|
||||
}
|
||||
|
||||
public static async Task<bool> ReadBoolAsync(this Stream stream)
|
||||
{
|
||||
var responseBuf = new byte[1];
|
||||
await stream.ReadAsync(responseBuf, 0, 1);
|
||||
return responseBuf[0] == 1;
|
||||
}
|
||||
|
||||
public static async Task<int> ReadIntAsync(this Stream stream)
|
||||
{
|
||||
var responseBuf = new byte[4];
|
||||
await stream.ReadAsync(responseBuf, 0, 4);
|
||||
return BitConverter.ToInt32(responseBuf, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
// 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.Diagnostics;
|
||||
using System.IO.Pipes;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.AutoRebuild
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds the VS process that launched this app process (if any), and uses
|
||||
/// named pipes to communicate with its AutoRebuild listener (if any).
|
||||
/// </summary>
|
||||
internal class VSForWindowsRebuildService : IRebuildService
|
||||
{
|
||||
private const int _connectionTimeoutMilliseconds = 3000;
|
||||
private readonly Process _vsProcess;
|
||||
|
||||
public static bool TryCreate(out VSForWindowsRebuildService result)
|
||||
{
|
||||
var vsProcess = FindAncestorVSProcess();
|
||||
if (vsProcess != null)
|
||||
{
|
||||
result = new VSForWindowsRebuildService(vsProcess);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> PerformRebuildAsync(string projectFullPath, DateTime ifNotBuiltSince)
|
||||
{
|
||||
var pipeName = $"BlazorAutoRebuild\\{_vsProcess.Id}";
|
||||
using (var pipeClient = new NamedPipeClientStream(pipeName))
|
||||
{
|
||||
await pipeClient.ConnectAsync(_connectionTimeoutMilliseconds);
|
||||
|
||||
// Protocol:
|
||||
// 1. Receive protocol version number from the VS listener
|
||||
// If we're incompatible with it, send back special string "abort" and end
|
||||
// 2. Send the project path to the VS listener
|
||||
// 3. Send the 'if not rebuilt since' timestamp to the VS listener
|
||||
// 4. Wait for it to send back a bool representing the result
|
||||
// Keep in sync with AutoRebuildService.cs in the BlazorExtension project
|
||||
// In the future we may extend this to getting back build error details
|
||||
var remoteProtocolVersion = await pipeClient.ReadIntAsync();
|
||||
if (remoteProtocolVersion == 1)
|
||||
{
|
||||
await pipeClient.WriteStringAsync(projectFullPath);
|
||||
await pipeClient.WriteDateTimeAsync(ifNotBuiltSince);
|
||||
return await pipeClient.ReadBoolAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await pipeClient.WriteStringAsync("abort");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VSForWindowsRebuildService(Process vsProcess)
|
||||
{
|
||||
_vsProcess = vsProcess ?? throw new ArgumentNullException(nameof(vsProcess));
|
||||
}
|
||||
|
||||
private static Process FindAncestorVSProcess()
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var candidateProcess = Process.GetCurrentProcess();
|
||||
try
|
||||
{
|
||||
while (candidateProcess != null && !candidateProcess.HasExited)
|
||||
{
|
||||
// It's unlikely that anyone's going to have a non-VS process in the process
|
||||
// hierarchy called 'devenv', but if that turns out to be a scenario, we could
|
||||
// (for example) write the VS PID to the obj directory during build, and then
|
||||
// only consider processes with that ID. We still want to be sure there really
|
||||
// is such a process in our ancestor chain, otherwise if you did "dotnet run"
|
||||
// in a command prompt, we'd be confused and think it was launched from VS.
|
||||
if (candidateProcess.ProcessName.Equals("devenv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return candidateProcess;
|
||||
}
|
||||
|
||||
candidateProcess = ProcessUtils.GetParent(candidateProcess);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// There's probably some permissions issue that prevents us from seeing
|
||||
// further up the ancestor list, so we have to stop looking here.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ namespace Microsoft.AspNetCore.Blazor.Server
|
|||
public string WebRootPath { get; }
|
||||
public string DistPath
|
||||
=> Path.Combine(Path.GetDirectoryName(SourceOutputAssemblyPath), "dist");
|
||||
public bool EnableAutoRebuilding { get; }
|
||||
public bool EnableDebugging { get; }
|
||||
|
||||
public static BlazorConfig Read(string assemblyPath)
|
||||
|
|
@ -44,7 +43,6 @@ namespace Microsoft.AspNetCore.Blazor.Server
|
|||
WebRootPath = webRootPath;
|
||||
}
|
||||
|
||||
EnableAutoRebuilding = configLines.Contains("autorebuild:true", StringComparer.Ordinal);
|
||||
EnableDebugging = configLines.Contains("debug:true", StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue