Commit c0a55060 authored by Jesse McGrew's avatar Jesse McGrew

Use background build worker

parent 6f324040930f
......@@ -254,12 +254,14 @@ namespace Zilf.Common
public string GetText(string path) => Encoding.UTF8.GetString(GetBytes(path));
public void SetText(string path, string content)
public void SetBytes(string path, byte[] content)
{
readableFiles[path] = Encoding.UTF8.GetBytes(content);
readableFiles[path] = content;
writtenFiles.Remove(path);
}
public void SetText(string path, string content) => SetBytes(path, Encoding.UTF8.GetBytes(content));
public bool Exists(string path) => readableFiles.ContainsKey(path) || writtenFiles.ContainsKey(path);
public Stream OpenForReading(string path) => new MemoryStream(GetBytes(path), false);
......
......@@ -69,8 +69,8 @@
private void ReplExchangesChanged()
{
StateHasChanged();
shouldScrollToBottom = true;
StateHasChanged();
}
protected override void OnInitialized()
......
......@@ -12,6 +12,7 @@ using Zilf.Playground.Services.Builds;
using Zilf.Playground.Services.Workspaces;
using Zilf.Playground.Services.Repl;
using Zilf.Playground.Services.Templates;
using BlazorWorker.Core;
namespace Zilf.Playground
{
......@@ -32,6 +33,8 @@ namespace Zilf.Playground
builder.Services.AddScoped<ReplService>();
builder.Services.AddScoped<BuildService>();
builder.Services.AddWorkerFactory();
await builder.Build().RunAsync();
}
}
......
#nullable enable
using BlazorWorker.BackgroundServiceFactory;
using BlazorWorker.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Zapf;
using Zilf.Common;
using Zilf.Compiler;
using Zilf.Diagnostics;
using Zilf.Playground.Services.Workspaces;
namespace Zilf.Playground.Services.Builds
{
enum BuildStatus
public enum BuildStatus
{
NotBuilt,
Compiling,
......@@ -21,9 +26,88 @@ namespace Zilf.Playground.Services.Builds
Built,
}
public class BackgroundBuildWorker
{
public event EventHandler<int>? StatusUpdate;
private static string GetZapPath(string zilPath)
{
const string OutputPrefix = "_build/";
var zilFileName = zilPath[(zilPath.LastIndexOf('/') + 1)..];
return OutputPrefix + System.IO.Path.ChangeExtension(zilFileName, ".zap");
}
public (string[] newPaths, string[] newContentsBase64)? Build(string[] filePaths, string[] fileContents, string[] includePaths, string mainFilePath)
{
// set up filesystem
var fileSystem = new InMemoryFileSystem();
for (int i = 0; i < filePaths.Length; i++)
fileSystem.SetText(filePaths[i], fileContents[i]);
var zapPath = GetZapPath(mainFilePath);
// invoke ZILF
var frontEnd = new FrontEnd { FileSystem = fileSystem, Logger = NullDiagnosticLogger.Instance };
foreach (var path in includePaths)
frontEnd.IncludePaths.Add(path);
StatusUpdate?.Invoke(this, (int)BuildStatus.Compiling);
var compResult = frontEnd.Compile(mainFilePath, zapPath, false);
if (!compResult.Success)
{
StatusUpdate?.Invoke(this, (int)BuildStatus.CompilerError);
var logger = new DefaultDiagnosticLogger();
foreach (var d in compResult.Diagnostics)
logger.Log(d);
return null;
}
//foreach (var p in fileSystem.Paths)
// Console.WriteLine(">>> " + p);
// invoke ZAPF
StatusUpdate?.Invoke(this, (int)BuildStatus.Assembling);
var assembler = new Zapf.ZapfAssembler { FileSystem = fileSystem };
var asmResult = assembler.Assemble(zapPath, System.IO.Path.ChangeExtension(zapPath, ".z#"));
if (!asmResult.Success)
{
StatusUpdate?.Invoke(this, (int)BuildStatus.AssemblerError);
return null;
}
StatusUpdate?.Invoke(this, (int)BuildStatus.Built);
// return changed files
var newFilePaths = new List<string>();
var newFileContents = new List<string>();
foreach (var p in fileSystem.Paths)
{
if (!filePaths.Contains(p))
{
newFilePaths.Add(p);
newFileContents.Add(Convert.ToBase64String(fileSystem.GetBytes(p)));
}
}
return (newPaths: newFilePaths.ToArray(), newContentsBase64: newFileContents.ToArray());
}
}
sealed class BuildService
{
private readonly WorkspaceService workspace;
private readonly IWorkerFactory workerFactory;
private readonly JSInterop jsInterop;
private BuildStatus _status = BuildStatus.NotBuilt;
public BuildStatus Status
......@@ -45,19 +129,16 @@ namespace Zilf.Playground.Services.Builds
public FrontEndResult? CompilerResult { get; private set; }
public AssemblyResult? AssemblerResult { get; private set; }
public BuildService(WorkspaceService workspace)
public BuildService(WorkspaceService workspace, IWorkerFactory workerFactory, JSInterop jsInterop)
{
this.workspace = workspace;
this.workerFactory = workerFactory;
this.jsInterop = jsInterop;
}
private static string GetZapPath(string zilPath)
{
const string OutputPrefix = "_build/";
var zilFileName = zilPath[(zilPath.LastIndexOf('/') + 1)..];
return OutputPrefix + System.IO.Path.ChangeExtension(zilFileName, ".zap");
}
private static readonly Regex StoryFileRegExp = new Regex(@"\.z\d$", RegexOptions.IgnoreCase);
public void CompileWorkspace()
public async Task CompileWorkspaceAsync()
{
// make sure there's something to compile
var project = workspace.Project;
......@@ -68,42 +149,53 @@ namespace Zilf.Playground.Services.Builds
return;
}
// invoke ZILF
var fileSystem = project.CopyToFileSystem();
var frontEnd = new FrontEnd { FileSystem = fileSystem, Logger = NullDiagnosticLogger.Instance };
foreach (var path in project.GetIncludePaths())
frontEnd.IncludePaths.Add(path);
var zapPath = GetZapPath(project.MainFile.Path);
// hand off to background build worker
var worker = await workerFactory.CreateAsync();
var service = await worker.CreateBackgroundServiceAsync<BackgroundBuildWorker>(
options => options
.AddAssemblies(
"ReadLine.dll",
"Zapf.dll",
"Zapf.Parsing.dll",
"Zilf.dll",
"Zilf.Common.dll",
"Zilf.Emit.dll",
"Zilf.Playground.dll"
));
var paths = new List<string>();
var contents = new List<string>();
foreach (var f in project.Files)
{
paths.Add(f.Path);
contents.Add(f.Content);
}
Status = BuildStatus.Compiling;
var includePaths = project.GetIncludePaths().ToArray();
var mainFilePath = project.MainFile.Path;
var compResult = frontEnd.Compile(project.MainFile.Path, zapPath, false);
await service.RegisterEventListenerAsync(nameof(BackgroundBuildWorker.StatusUpdate), (object? _, int s) => Status = (BuildStatus)s);
var result = await service.RunAsync(w => w.Build(paths.ToArray(), contents.ToArray(), includePaths, mainFilePath));
if (!compResult.Success)
if (result != null)
{
Status = BuildStatus.CompilerError;
return;
}
var ifs = (Common.InMemoryFileSystem)fileSystem;
foreach (var p in ifs.Paths)
Console.WriteLine(">>> " + p);
var (newPaths, newContentsBase64) = result.Value;
// invoke ZAPF
Status = BuildStatus.Assembling;
for (int i = 0; i < newPaths.Length; i++)
{
var p = newPaths[i];
var assembler = new Zapf.ZapfAssembler { FileSystem = fileSystem };
var asmResult = assembler.Assemble(zapPath, System.IO.Path.ChangeExtension(zapPath, ".z#"));
if (StoryFileRegExp.IsMatch(p))
{
var filename = p[(p.LastIndexOf('/') + 1)..];
const string contentType = "application/x-zmachine";
if (!asmResult.Success)
{
Status = BuildStatus.AssemblerError;
return;
jsInterop.DownloadBytesAsFile(Convert.FromBase64String(newContentsBase64[i]), filename, contentType);
break;
}
}
}
Status = BuildStatus.Built;
}
}
}
......@@ -67,16 +67,6 @@ namespace Zilf.Playground.Services.Workspaces
return true;
}
public IFileSystem CopyToFileSystem()
{
var result = new InMemoryFileSystem();
foreach (var f in files)
result.SetText(f.Path, f.Content);
return result;
}
public IEnumerable<string> GetIncludePaths()
{
var allFilePaths = from f in files
......
......@@ -103,9 +103,9 @@
//}
}
private void Compile()
private async Task Compile()
{
Build.CompileWorkspace();
await Build.CompileWorkspaceAsync();
}
private string GetUnusedFileName()
......
......@@ -10,6 +10,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
<PackageReference Include="Tewr.BlazorWorker.BackgroundService" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment