Skip to content

Fix blazor-gateway.dll not found: pack BlazorGateway subfolder into wasm packages#130764

Draft
pavelsavara with Copilot wants to merge 5 commits into
mainfrom
copilot/implement-transition
Draft

Fix blazor-gateway.dll not found: pack BlazorGateway subfolder into wasm packages#130764
pavelsavara with Copilot wants to merge 5 commits into
mainfrom
copilot/implement-transition

Conversation

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The WasmAppHost browser dev server launches the Blazor Gateway from a BlazorGateway/ subfolder next to WasmAppHost.dll, but the wasm packaging projects collected WasmAppHost output with a non-recursive glob ($(WasmAppHostDir)\*). The subfolder — and blazor-gateway.dll — was omitted from the shipped packages, so GatewayServer.LocateGatewayDll() threw FileNotFoundException at runtime (observed in Wasm.Build.Tests).

Changes

  • Pack the BlazorGateway subtree recursively in the three pkgprojs that ship WasmAppHost (Microsoft.NET.Sdk.WebAssembly.Pack, Microsoft.NET.Runtime.WebAssembly.Sdk, Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk), preserving the folder structure so the payload lands at WasmAppHost/BlazorGateway/.
  • Two-step %(RecursiveDir) transform — the subtree is globbed into a raw item first, then re-included with TargetPath, because %(RecursiveDir) is not populated when referenced on the same wildcard Include (would otherwise flatten nested files).
<_WasmAppHostFiles Include="$(WasmAppHostDir)\*" TargetPath="WasmAppHost" />
<_WasmAppHostGatewayFiles Include="$(WasmAppHostDir)\BlazorGateway\**\*" />
<_WasmAppHostFiles Include="@(_WasmAppHostGatewayFiles)" TargetPath="WasmAppHost\BlazorGateway\%(RecursiveDir)" />

The Arcade packer treats the extension-less TargetPath as a directory and appends the filename, producing WasmAppHost/BlazorGateway/blazor-gateway.dll — the exact path the loader searches.

Copilot AI self-assigned this Jul 15, 2026
Copilot AI review requested due to automatic review settings July 15, 2026 10:03
Copilot AI removed the request for review from Copilot July 15, 2026 10:03
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
11 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI requested a review from pavelsavara July 15, 2026 10:19
@pavelsavara pavelsavara added arch-wasm WebAssembly architecture os-browser Browser variant of arch-wasm labels Jul 15, 2026
@maraf

maraf commented Jul 15, 2026

Copy link
Copy Markdown
Member

Does the subprocess approach work with HotReload?

Copilot AI requested review from Copilot and removed request for Copilot July 15, 2026 12:05
Copilot AI changed the title Unify WasmAppHost dev server with Blazor Gateway Fix blazor-gateway.dll not found: pack BlazorGateway subfolder into wasm packages Jul 15, 2026
Copilot AI review requested due to automatic review settings July 15, 2026 14:17
@pavelsavara pavelsavara temporarily deployed to copilot-pat-pool July 15, 2026 14:17 — with GitHub Actions Inactive
@pavelsavara pavelsavara temporarily deployed to copilot-pat-pool July 15, 2026 14:17 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the browser WASM toolchain to ship and launch the Blazor Gateway (blazor-gateway.dll) correctly by (1) copying the Gateway payload next to WasmAppHost under a BlazorGateway/ subfolder and (2) ensuring the WASM SDK NuGet packages pack that subfolder recursively so runtime lookup succeeds.

Changes:

  • Copy Microsoft.AspNetCore.Components.Gateway payload into the WasmAppHost output under BlazorGateway/.
  • Replace the in-proc dev server implementation with a subprocess-based GatewayServer that locates and launches blazor-gateway.dll.
  • Pack the WasmAppHost/BlazorGateway/ subtree recursively into the three WASM SDK packages; also enable SPA fallback default for root apps.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/mono/wasm/host/WasmAppHost.csproj Adds Gateway package reference + copies/packs its tools/** payload next to WasmAppHost.
src/mono/wasm/host/README.md Documents Gateway-based dev server behavior and known feature gaps vs legacy server.
src/mono/wasm/host/DevServer/WebAssemblyNetDebugProxyAppBuilderExtensions.cs Removes legacy debugging middleware implementation.
src/mono/wasm/host/DevServer/GatewayServer.cs Adds subprocess launcher that parses Gateway stdout to determine listening URLs.
src/mono/wasm/host/DevServer/DevServerStartup.cs Removes legacy in-process dev server startup pipeline.
src/mono/wasm/host/DevServer/DevServer.cs Removes legacy dev server host builder entrypoint.
src/mono/wasm/host/DevServer/DebugProxyLauncher.cs Removes legacy debug proxy launcher implementation.
src/mono/wasm/host/DevServer/ContentEncodingNegotiator.cs Removes legacy content-encoding negotiation middleware.
src/mono/wasm/host/BrowserHost.cs Routes static web assets scenario to the new GatewayServer and warns about missing console forwarding.
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/Microsoft.NET.Sdk.WebAssembly.Pack.pkgproj Packs WasmAppHost/BlazorGateway/** recursively into the package.
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.props Sets StaticWebAssetSpaFallbackEnabled=true by default for root apps.
src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk/Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk.pkgproj Packs WasmAppHost/BlazorGateway/** recursively into the package.
src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj Packs WasmAppHost/BlazorGateway/** recursively into the package.
eng/Versions.props Adds a pinned Gateway package version property consumed by WasmAppHost.

Comment on lines +22 to +24
<_blazorGatewayFiles Include="$(PkgMicrosoft_AspNetCore_Components_Gateway)\tools\**\*" />
<None Include="@(_blazorGatewayFiles)" CopyToOutputDirectory="PreserveNewest" Link="BlazorGateway\%(RecursiveDir)%(Filename)%(Extension)" />
<PackageFile Include="@(_blazorGatewayFiles)" TargetPath="tools\$(NetCoreAppCurrent)\BlazorGateway\%(RecursiveDir)" />
Comment on lines +47 to +50
await host.StartAsync(token);

ServerURLs serverUrls = await realUrlsAvailableTcs.Task;
return (serverUrls, host);
Comment on lines +189 to +232
Match listeningMatch = NowListeningRegex.Match(line);
if (listeningMatch.Success)
{
lock (_listeningUrls)
{
_listeningUrls.Add(listeningMatch.Groups["url"].Value.Trim());
}
}
else if (ApplicationStartedRegex.IsMatch(line))
{
ResolveServerUrls();
}

// Suppress the gateway's own status messages so they are not confused with the application's output.
foreach (string prefix in MessageSuppressionPrefixes)
{
if (line.StartsWith(prefix, StringComparison.Ordinal))
return;
}

Console.WriteLine(line);
}

private void OnErrorDataReceived(object sender, DataReceivedEventArgs eventArgs)
{
if (!string.IsNullOrEmpty(eventArgs.Data))
_logger.LogError("[BlazorGateway] {Message}", eventArgs.Data);
}

private void ResolveServerUrls()
{
string? http;
string? https;
lock (_listeningUrls)
{
http = GetServerAddress(_listeningUrls, secure: false);
https = GetServerAddress(_listeningUrls, secure: true);
}

if (http is null)
_realUrlsAvailableTcs.TrySetException(new InvalidOperationException("Failed to determine the Blazor Gateway's HTTP address or port."));
else
_realUrlsAvailableTcs.TrySetResult(new ServerURLs(http, https));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-Build-mono os-browser Browser variant of arch-wasm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants