Skip to content

Commit 819e288

Browse files
marafradical
andauthored
[browser] Update browser template to use Wasm SDK (#90871)
* Use WasmSdk in browser template * Remove default html-path * Use wwwroot when looking for main.js in template for replacement * Temporarily remove WasmRuntimeAssetsLocation from WBT because WasmSDK doesn't support it yet * Integrate console log forward into DevServer * Replace UseRouter * Use next middleware only if it's not console * CancellationTokenSource on CancelKeyPress * Address feedback from initial DevServer integration * Update src/mono/wasm/host/BrowserHost.cs Co-authored-by: Ankit Jain <[email protected]> * Remove debugging code * Fix WBT * Fix starting outside of project directory * Support WasmRuntimeAssetsLocation in Wasm SDK * Revert "Support WasmRuntimeAssetsLocation in Wasm SDK" * Active issue for WasmRuntimeAssetsLocation * Feedback --------- Co-authored-by: Ankit Jain <[email protected]>
1 parent 0b6fe08 commit 819e288

File tree

13 files changed

+88
-56
lines changed

13 files changed

+88
-56
lines changed

src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ Copyright (c) .NET Foundation. All rights reserved.
1919
<RunCommand Condition="'$(RunCommand)' == ''">dotnet</RunCommand>
2020

2121
<WasmAppHostDir>$([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory), '..', 'WasmAppHost'))</WasmAppHostDir>
22-
<_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(OutputPath), '$(AssemblyName).runtimeconfig.json'))</_RuntimeConfigJsonPath>
22+
23+
<_RunWorkingDirectory>$(OutputPath)</_RunWorkingDirectory>
24+
<_RunWorkingDirectory Condition="'$(_RunWorkingDirectory)' != '' and !$([System.IO.Path]::IsPathRooted($(_RunWorkingDirectory)))">$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(_RunWorkingDirectory)))</_RunWorkingDirectory>
25+
<_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(_RunWorkingDirectory), '$(AssemblyName).runtimeconfig.json'))</_RuntimeConfigJsonPath>
26+
2327
<RunArguments>exec &quot;$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))&quot; --use-staticwebassets --runtime-config &quot;$(_RuntimeConfigJsonPath)&quot; $(WasmHostArguments)</RunArguments>
24-
<RunWorkingDirectory>$(OutputPath)</RunWorkingDirectory>
28+
<RunWorkingDirectory>$(_RunWorkingDirectory)</RunWorkingDirectory>
2529
</PropertyGroup>
2630

2731
<PropertyGroup>

src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private void UpdateProgramCS()
3939

4040
private void UpdateBrowserMainJs(string targetFramework, string runtimeAssetsRelativePath = DefaultRuntimeAssetsRelativePath)
4141
{
42-
string mainJsPath = Path.Combine(_projectDir!, "main.js");
42+
string mainJsPath = Path.Combine(_projectDir!, "wwwroot", "main.js");
4343
string mainJsContent = File.ReadAllText(mainJsPath);
4444

4545
// .withExitOnUnhandledError() is available only only >net7.0
@@ -408,11 +408,12 @@ public void ConsolePublishAndRun(string config, bool aot, bool relinking)
408408

409409
[Theory]
410410
[InlineData("", BuildTestBase.DefaultTargetFramework, DefaultRuntimeAssetsRelativePath)]
411-
[InlineData("", BuildTestBase.DefaultTargetFramework, "./")]
411+
// [ActiveIssue("https://github.com/dotnet/runtime/issues/90979")]
412+
// [InlineData("", BuildTestBase.DefaultTargetFramework, "./")]
413+
// [InlineData("-f net8.0", "net8.0", "./")]
412414
// [ActiveIssue("https://github.com/dotnet/runtime/issues/79313")]
413415
// [InlineData("-f net7.0", "net7.0")]
414416
[InlineData("-f net8.0", "net8.0", DefaultRuntimeAssetsRelativePath)]
415-
[InlineData("-f net8.0", "net8.0", "./")]
416417
public async Task BrowserBuildAndRun(string extraNewArgs, string targetFramework, string runtimeAssetsRelativePath)
417418
{
418419
string config = "Debug";

src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@ protected override IReadOnlySet<string> GetDotNetFilesExpectedSet(AssertBundleOp
5757
return res;
5858
}
5959

60+
61+
public void AssertBundle(BuildArgs buildArgs, BuildProjectOptions buildProjectOptions)
62+
{
63+
AssertBundle(new(
64+
Config: buildArgs.Config,
65+
IsPublish: buildProjectOptions.Publish,
66+
TargetFramework: buildProjectOptions.TargetFramework,
67+
BinFrameworkDir: buildProjectOptions.BinFrameworkDir ?? FindBinFrameworkDir(buildArgs.Config, buildProjectOptions.Publish, buildProjectOptions.TargetFramework),
68+
PredefinedIcudt: buildProjectOptions.PredefinedIcudt,
69+
GlobalizationMode: buildProjectOptions.GlobalizationMode,
70+
AssertSymbolsFile: false,
71+
ExpectedFileType: buildProjectOptions.Publish && buildArgs.Config == "Release" ? NativeFilesType.Relinked : NativeFilesType.FromRuntimePack
72+
));
73+
}
74+
6075
public void AssertBundle(AssertWasmSdkBundleOptions assertOptions)
6176
{
6277
IReadOnlyDictionary<string, DotNetFileName> actualDotnetFiles = AssertBasicBundle(assertOptions);

src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,31 +59,49 @@ public string CreateWasmTemplateProject(string id, string template = "wasmbrowse
5959

6060
public (string projectDir, string buildOutput) BuildTemplateProject(BuildArgs buildArgs,
6161
string id,
62-
BuildProjectOptions buildProjectOptions,
63-
AssertTestMainJsAppBundleOptions? assertAppBundleOptions = null)
62+
BuildProjectOptions buildProjectOptions)
6463
{
6564
(CommandResult res, string logFilePath) = BuildProjectWithoutAssert(id, buildArgs.Config, buildProjectOptions);
6665
if (buildProjectOptions.UseCache)
6766
_buildContext.CacheBuild(buildArgs, new BuildProduct(_projectDir!, logFilePath, true, res.Output));
6867

6968
if (buildProjectOptions.AssertAppBundle)
70-
AssertBundle(buildArgs, buildProjectOptions, res.Output, assertAppBundleOptions);
69+
{
70+
if (buildProjectOptions.IsBrowserProject)
71+
AssertWasmSdkBundle(buildArgs, buildProjectOptions, res.Output);
72+
else
73+
AssertTestMainJsBundle(buildArgs, buildProjectOptions, res.Output);
74+
}
7175
return (_projectDir!, res.Output);
7276
}
7377

74-
public void AssertBundle(BuildArgs buildArgs,
78+
public void AssertTestMainJsBundle(BuildArgs buildArgs,
7579
BuildProjectOptions buildProjectOptions,
7680
string? buildOutput = null,
7781
AssertTestMainJsAppBundleOptions? assertAppBundleOptions = null)
7882
{
7983
if (buildOutput is not null)
8084
ProjectProviderBase.AssertRuntimePackPath(buildOutput, buildProjectOptions.TargetFramework ?? DefaultTargetFramework);
8185

82-
// TODO: templates don't use wasm sdk yet
8386
var testMainJsProvider = new TestMainJsProjectProvider(_testOutput, _projectDir!);
8487
if (assertAppBundleOptions is not null)
8588
testMainJsProvider.AssertBundle(assertAppBundleOptions);
8689
else
8790
testMainJsProvider.AssertBundle(buildArgs, buildProjectOptions);
8891
}
92+
93+
public void AssertWasmSdkBundle(BuildArgs buildArgs,
94+
BuildProjectOptions buildProjectOptions,
95+
string? buildOutput = null,
96+
AssertWasmSdkBundleOptions? assertAppBundleOptions = null)
97+
{
98+
if (buildOutput is not null)
99+
ProjectProviderBase.AssertRuntimePackPath(buildOutput, buildProjectOptions.TargetFramework ?? DefaultTargetFramework);
100+
101+
var projectProvider = new WasmSdkBasedProjectProvider(_testOutput, _projectDir!);
102+
if (assertAppBundleOptions is not null)
103+
projectProvider.AssertBundle(assertAppBundleOptions);
104+
else
105+
projectProvider.AssertBundle(buildArgs, buildProjectOptions);
106+
}
89107
}

src/mono/wasm/host/BrowserHost.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ private static DevServerOptions CreateDevServerOptions(BrowserArguments args, st
167167
devServerOptions = CreateDevServerOptions(urls, staticWebAssetsPath, onConsoleConnected);
168168

169169
if (devServerOptions == null)
170-
throw new CommandLineException("Please, provide mainAssembly in hostProperties of runtimeconfig");
170+
throw new CommandLineException($"Please, provide mainAssembly in hostProperties of runtimeconfig. Alternatively leave the static web assets manifest ('*{staticWebAssetsV2Extension}') in the build output directory '{appPath}' .");
171171
}
172172

173173
return devServerOptions;
@@ -183,7 +183,7 @@ private static DevServerOptions CreateDevServerOptions(BrowserArguments args, st
183183
);
184184

185185
private static string? FindFirstFileWithExtension(string directory, string extension)
186-
=> Directory.EnumerateFiles(directory, "*" + extension).First();
186+
=> Directory.EnumerateFiles(directory, "*" + extension).FirstOrDefault();
187187

188188
private async Task RunConsoleMessagesPump(WebSocket socket, WasmTestMessagesProcessor messagesProcessor, CancellationToken token)
189189
{

src/mono/wasm/host/DevServer/DevServer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ private static IConfiguration ConfigureHostConfiguration(DevServerOptions option
7070
[WebHostDefaults.EnvironmentKey] = "Development",
7171
["Logging:LogLevel:Microsoft"] = "Warning",
7272
["Logging:LogLevel:Microsoft.Hosting.Lifetime"] = "Information",
73-
[WebHostDefaults.StaticWebAssetsKey] = options.StaticWebAssetsPath,
74-
["ApplyCopHeaders"] = options.WebServerUseCrossOriginPolicy.ToString()
73+
[WebHostDefaults.StaticWebAssetsKey] = options.StaticWebAssetsPath
7574
};
7675

7776
config.AddInMemoryCollection(inMemoryConfiguration);

src/mono/wasm/host/DevServer/DevServerStartup.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.IO;
5+
using System.Net.WebSockets;
56
using System.Threading.Tasks;
67
using Microsoft.AspNetCore.Builder;
78
using Microsoft.AspNetCore.Http;
9+
using Microsoft.AspNetCore.Routing;
810
using Microsoft.Extensions.Configuration;
911
using Microsoft.Extensions.DependencyInjection;
1012
using Microsoft.Extensions.Hosting;
1113
using Microsoft.Extensions.Logging;
14+
using Microsoft.Extensions.Options;
1215
using Microsoft.WebAssembly.AppHost;
1316

1417
namespace Microsoft.WebAssembly.AppHost.DevServer;
@@ -27,16 +30,16 @@ public static void ConfigureServices(IServiceCollection services)
2730
services.AddRouting();
2831
}
2932

30-
public static void Configure(IApplicationBuilder app, TaskCompletionSource<ServerURLs> realUrlsAvailableTcs, ILogger logger, IHostApplicationLifetime applicationLifetime, IConfiguration configuration)
33+
public static void Configure(IApplicationBuilder app, IOptions<DevServerOptions> optionsContainer, TaskCompletionSource<ServerURLs> realUrlsAvailableTcs, ILogger logger, IHostApplicationLifetime applicationLifetime, IConfiguration configuration)
3134
{
3235
app.UseDeveloperExceptionPage();
3336
EnableConfiguredPathbase(app, configuration);
3437

3538
app.UseWebAssemblyDebugging();
3639

37-
bool applyCopHeaders = configuration.GetValue<bool>("ApplyCopHeaders");
40+
DevServerOptions options = optionsContainer.Value;
3841

39-
if (applyCopHeaders)
42+
if (options.WebServerUseCrossOriginPolicy)
4043
{
4144
app.Use(async (ctx, next) =>
4245
{
@@ -63,14 +66,37 @@ public static void Configure(IApplicationBuilder app, TaskCompletionSource<Serve
6366
});
6467

6568
app.UseRouting();
69+
app.UseWebSockets();
70+
71+
if (options.OnConsoleConnected is not null)
72+
{
73+
app.Use(async (ctx, next) =>
74+
{
75+
if (ctx.Request.Path.StartsWithSegments("/console"))
76+
{
77+
if (!ctx.WebSockets.IsWebSocketRequest)
78+
{
79+
ctx.Response.StatusCode = 400;
80+
return;
81+
}
82+
83+
using WebSocket socket = await ctx.WebSockets.AcceptWebSocketAsync();
84+
await options.OnConsoleConnected(socket);
85+
}
86+
else
87+
{
88+
await next(ctx);
89+
}
90+
});
91+
}
6692

6793
app.UseEndpoints(endpoints =>
6894
{
6995
endpoints.MapFallbackToFile("index.html", new StaticFileOptions
7096
{
7197
OnPrepareResponse = fileContext =>
7298
{
73-
if (applyCopHeaders)
99+
if (options.WebServerUseCrossOriginPolicy)
74100
{
75101
// Browser multi-threaded runtime requires cross-origin policy headers to enable SharedArrayBuffer.
76102
ApplyCrossOriginPolicyHeaders(fileContext.Context);

src/mono/wasm/host/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public static async Task<int> Main(string[] args)
3030
RegisterHostHandler(WasmHost.Wasmtime, WasiEngineHost.InvokeAsync);
3131

3232
using CancellationTokenSource cts = new();
33+
34+
Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel();
35+
3336
ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
3437
builder
3538
.AddPassThroughConsole()

src/mono/wasm/templates/templates/browser/README.md

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk.WebAssembly">
22
<PropertyGroup>
3-
<TargetFramework>net7.0</TargetFramework>
4-
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
5-
<OutputType>Exe</OutputType>
3+
<TargetFramework>net8.0</TargetFramework>
64
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
75
</PropertyGroup>
8-
9-
<ItemGroup>
10-
<WasmExtraFilesToDeploy Include="index.html" />
11-
<WasmExtraFilesToDeploy Include="main.js" />
12-
</ItemGroup>
136
</Project>

0 commit comments

Comments
 (0)