Skip to content

Commit 6b3ea40

Browse files
authored
[wasm] First attempt at a wasm app host (#68696)
First attempt at implementing a wasm app host Regular dotnet apps can be run with `dotnet run`. But this is not the case for wasm apps. We use `xharness` to run the test apps that we generate for library tests. Templates for browser, and console wasm projects were added recently, but they have to be run manually by either invoking a v8 script, or running a `http-server` in the bundle directory. Though to use a debugger with this requires starting the debug proxy separately, and connecting few bits. # WasmAppHost This PR adds a new `WasmAppHost`, which can be used through `dotnet run`: - it can run "wasm app" from it's `AppBundle` directory, using various hosts like `v8`, `node`, or a browser. An example use: ``` $ dotnet new wasmconsole $ dotnet run WasmAppHost --runtime-config /tmp/c0/bin/Debug/net7.0/browser-wasm/AppBundle//c0.runtimeconfig.json [02:26:40] info: host[0] Running: node main.cjs [02:26:40] dbug: host[0] Using working directory: /tmp/c0/bin/Debug/net7.0/browser-wasm/AppBundle Incoming arguments: Application arguments: Debugger.Debug: DEBUGGING ENABLED mono_wasm_runtime_ready fe00e07a-5519-4dfe-b35a-f867dbaf2e28 Could not find symbols file dotnet.js.symbols. Ignoring. Initializing..... Hello, Console! ``` The console template project is run with `node` by default. - it supports a `--debug` parameter, which (for the browser case) would startup a webserver, and the debug proxy for Chrome, and firefox. ``` $ dotnet new wasmbrowser $ dotnet run --debug WasmAppHost --runtime-config /workspaces/test/br0/bin/Debug/net7.0/browser-wasm/AppBundle//br0.runtimeconfig.json --debug --forward-console Debug proxy for chrome now listening on http://127.0.0.1:9300/. And expecting chrome at http://localhost:9222/ Hosting environment: Production Content root path: /workspaces/test/br0/bin/Debug/net7.0/browser-wasm/AppBundle Now listening on: http://127.0.0.1:9300 Debug proxy for firefox now listening on tcp://127.0.0.1:6300. And expecting firefox at port 6000 . App url: http://127.0.0.1:9000/index.html App url: https://127.0.0.1:38331/index.html ``` - This enables using `dotnet run` with wasm samples, and library tests too. ## How do I try this out? You'll need a dotnet with the updated packs. For now, you can use: ``` $ ./dotnet.sh build -p:TargetOS=Browser -p:TargetArchitecture=wasm -p:Configuration=Release src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj /t:InstallWorkloadUsingArtifacts ``` .. which would install a sdk in `artifacts/bin/dotnet-workload` with all the packs installed. - Use that dotnet to create console, or browser wasm apps from templates, for example `dotnet new wasmconsole`, or `dotnet new wasmbrowser`. ### Debugging library tests ``` runtime$ ./artifacts/bin/dotnet-workload/dotnet run -f net7.0 -r browser-wasm -c $YourRuntimeConfig --project src/libraries/System.Buffers/tests/System.Buffers.Tests.csproj -p:DebuggerSupport=true --debug --host browser ``` - If the runtime was built in `Release` config, then you need to add `-p:DebuggerSupport=true`, so the pdbs would get deployed - And use `--debug` to start the debugger - Opening the url will show a `Run tests` button, which you can click after connecting with the IDE, to start running the tests ## Notes, and TODO - `WasmAppHost`, along with the debug proxy are bundled in the `WebAssembly.Sdk` pack right now. - This might be changed in the future to use the dotnet app host pack pattern, and with additional support adding in the Sdk. - This would also allow using `launchSettings.json`, similar to blazor - `WasmAppHost` depends on host config in `runtimeconfig.json`, which can be controlled by a `runtimeconfig.template.json` in the project directory. - Needs more tests, cleanup, `--help` for command line arguments - `app-support.*js` in templates, and `test-main.js` need more work to share the code Fixes #67236
1 parent 637f8f2 commit 6b3ea40

File tree

63 files changed

+4718
-354
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+4718
-354
lines changed

Directory.Build.props

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<_hostOS Condition="$([MSBuild]::IsOSPlatform('SOLARIS'))">Solaris</_hostOS>
1919
<_hostOS Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">windows</_hostOS>
2020
<HostOS>$(_hostOS)</HostOS>
21-
<_hostOS Condition="'$(TargetOS)' == 'Browser'">Browser</_hostOS>
21+
<TargetOS Condition="'$(TargetOS)' == '' and '$(RuntimeIdentifier)' == 'browser-wasm'">browser</TargetOS>
2222
<TargetOS Condition="'$(TargetOS)' == ''">$(_hostOS)</TargetOS>
2323
<TargetsMobile Condition="'$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'iOSSimulator' or '$(TargetOS)' == 'MacCatalyst' or '$(TargetOS)' == 'tvOS' or '$(TargetOS)' == 'tvOSSimulator' or '$(TargetOS)' == 'Android' or '$(TargetOS)' == 'Browser'">true</TargetsMobile>
2424
<TargetsAppleMobile Condition="'$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'iOSSimulator' or '$(TargetOS)' == 'MacCatalyst' or '$(TargetOS)' == 'tvOS' or '$(TargetOS)' == 'tvOSSimulator'">true</TargetsAppleMobile>
@@ -35,7 +35,7 @@
3535
<TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'arm64'">arm64</TargetArchitecture>
3636
<TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'loongarch64'">loongarch64</TargetArchitecture>
3737
<TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 's390x'">s390x</TargetArchitecture>
38-
<TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(TargetOS)' == 'Browser'">wasm</TargetArchitecture>
38+
<TargetArchitecture Condition="'$(TargetArchitecture)' == '' and ('$(TargetOS)' == 'Browser' or '$(RuntimeIdentifier)' == 'browser-wasm')">wasm</TargetArchitecture>
3939
<TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(TargetsMobile)' == 'true'">x64</TargetArchitecture>
4040
<TargetArchitecture Condition="'$(TargetArchitecture)' == ''">x64</TargetArchitecture>
4141
<Platform Condition="'$(Platform)' == '' and '$(InferPlatformFromTargetArchitecture)' == 'true'">$(TargetArchitecture)</Platform>
@@ -109,6 +109,7 @@
109109
<AndroidAppBuilderTasksAssemblyPath>$([MSBuild]::NormalizePath('$(AndroidAppBuilderDir)', 'AndroidAppBuilder.dll'))</AndroidAppBuilderTasksAssemblyPath>
110110
<WasmAppBuilderTasksAssemblyPath>$([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll'))</WasmAppBuilderTasksAssemblyPath>
111111
<WasmBuildTasksAssemblyPath>$([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll'))</WasmBuildTasksAssemblyPath>
112+
<WasmAppHostDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppHost', 'wasm', '$(Configuration)'))</WasmAppHostDir>
112113
<WorkloadBuildTasksAssemblyPath>$([MSBuild]::NormalizePath('$(WorkloadBuildTasksDir)', 'WorkloadBuildTasks.dll'))</WorkloadBuildTasksAssemblyPath>
113114
<MonoAOTCompilerTasksAssemblyPath>$([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll'))</MonoAOTCompilerTasksAssemblyPath>
114115
<MonoTargetsTasksAssemblyPath>$([MSBuild]::NormalizePath('$(MonoTargetsTasksDir)', 'MonoTargetsTasks.dll'))</MonoTargetsTasksAssemblyPath>

eng/testing/tests.wasm.targets

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,25 @@
5555
<SetScriptCommands Condition="'$(JSEngineArgs)' != ''" Include="set &quot;JS_ENGINE_ARGS=$(JSEngineArgs)&quot;" />
5656
<SetScriptCommands Condition="'$(_WasmMainJSFileName)' != ''" Include="set &quot;MAIN_JS=--js-file^=$(_WasmMainJSFileName)&quot;" />
5757
</ItemGroup>
58-
<PropertyGroup Condition="'$(RunScriptCommand)' == ''">
59-
<_XHarnessArgs Condition="'$(OS)' != 'Windows_NT'">wasm $XHARNESS_COMMAND --app=. --output-directory=$XHARNESS_OUT</_XHarnessArgs>
60-
<_XHarnessArgs Condition="'$(OS)' == 'Windows_NT'">wasm %XHARNESS_COMMAND% --app=. --output-directory=%XHARNESS_OUT%</_XHarnessArgs>
61-
62-
<_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode)</_XHarnessArgs>
63-
<_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs)</_XHarnessArgs>
64-
<_XHarnessArgs >$(_XHarnessArgs) -s dotnet.js.symbols</_XHarnessArgs>
65-
<_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli)</_XHarnessArgs>
6658

59+
<PropertyGroup>
6760
<_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(Scenario)' != 'BuildWasmApps' and '$(WasmMainAssemblyFileName)' == ''">--run WasmTestRunner.dll $(AssemblyName).dll</_AppArgs>
6861
<_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(WasmMainAssemblyFileName)' != ''">--run $(WasmMainAssemblyFileName)</_AppArgs>
6962
<_AppArgs Condition="'$(IsFunctionalTest)' == 'true'">--run $(AssemblyName).dll</_AppArgs>
7063

7164
<_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) $(WasmTestAppArgs)</_AppArgs>
7265

7366
<WasmXHarnessMonoArgs Condition="'$(XunitShowProgress)' == 'true'">$(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=1</WasmXHarnessMonoArgs>
67+
</PropertyGroup>
68+
69+
<PropertyGroup Condition="'$(RunScriptCommand)' == ''">
70+
<_XHarnessArgs Condition="'$(OS)' != 'Windows_NT'">wasm $XHARNESS_COMMAND --app=. --output-directory=$XHARNESS_OUT</_XHarnessArgs>
71+
<_XHarnessArgs Condition="'$(OS)' == 'Windows_NT'">wasm %XHARNESS_COMMAND% --app=. --output-directory=%XHARNESS_OUT%</_XHarnessArgs>
72+
73+
<_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode)</_XHarnessArgs>
74+
<_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs)</_XHarnessArgs>
75+
<_XHarnessArgs >$(_XHarnessArgs) -s dotnet.js.symbols</_XHarnessArgs>
76+
<_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli)</_XHarnessArgs>
7477

7578
<!-- There two flavors of WasmXHarnessArgs and WasmXHarnessMonoArgs, one is MSBuild property and the other is environment variable -->
7679
<RunScriptCommand Condition="'$(OS)' != 'Windows_NT'">$HARNESS_RUNNER $(_XHarnessArgs) %24XHARNESS_ARGS %24WasmXHarnessArgs -- $(WasmXHarnessMonoArgs) %24WasmXHarnessMonoArgs $(_AppArgs) %24WasmTestAppArgs</RunScriptCommand>
@@ -102,6 +105,11 @@
102105
<PropertyGroup>
103106
<BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOn)' == 'local'">WasmTriggerPublishApp</BundleTestWasmAppDependsOn>
104107
<BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOnHelix)' == 'true'">$(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix</BundleTestWasmAppDependsOn>
108+
109+
<RunCommand>$(WasmAppHostDir)/WasmAppHost</RunCommand>
110+
<!-- Use BundleDir here, since WasmAppDir is set in a target, and `dotnet run` reads
111+
$(Run*) without running any targets -->
112+
<RunArguments>--runtime-config $(BundleDir)/WasmTestRunner.runtimeconfig.json $(WasmHostArguments) $(StartArguments) $(WasmXHarnessMonoArgs) $(_AppArgs)</RunArguments>
105113
</PropertyGroup>
106114

107115
<PropertyGroup Condition="'$(BuildAOTTestsOnHelix)' == 'true'">

eng/testing/xunit/xunit.targets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
<!-- Run target (F5) support. -->
1010
<PropertyGroup>
11-
<RunWorkingDirectory>$(OutDir)</RunWorkingDirectory>
11+
<RunWorkingDirectory Condition="'$(RunWorkingDirectory)' == ''">$(OutDir)</RunWorkingDirectory>
1212
</PropertyGroup>
1313

14-
<PropertyGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
14+
<PropertyGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' and '$(RunCommand)' == ''">
1515
<RunCommand>$(DotNetTool)</RunCommand>
1616
<RunArguments>test $(TargetPath) --settings $(OutDir).runsettings</RunArguments>
1717
</PropertyGroup>

src/libraries/Directory.Build.targets

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
<Import Project="..\..\Directory.Build.targets" />
1313

1414
<PropertyGroup>
15+
<UseDefaultTestHost Condition="'$(UseDefaultTestHost)' == ''">false</UseDefaultTestHost>
1516
<NetCoreAppCurrentBuildSettings>$(NetCoreAppCurrent)-$(TargetOS)-$(Configuration)-$(TargetArchitecture)</NetCoreAppCurrentBuildSettings>
1617
<NativeBinDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'native', '$(NetCoreAppCurrentBuildSettings)'))</NativeBinDir>
17-
<NetCoreAppCurrentTestHostPath>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'testhost', '$(NetCoreAppCurrentBuildSettings)'))</NetCoreAppCurrentTestHostPath>
18-
<NetCoreAppCurrentTestHostSharedFrameworkPath>$([MSBuild]::NormalizeDirectory('$(NetCoreAppCurrentTestHostPath)', 'shared', '$(MicrosoftNetCoreAppFrameworkName)', '$(ProductVersion)'))</NetCoreAppCurrentTestHostSharedFrameworkPath>
18+
<NetCoreAppCurrentTestHostPath Condition="'$(UseDefaultTestHost)' != 'true'">$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'testhost', '$(NetCoreAppCurrentBuildSettings)'))</NetCoreAppCurrentTestHostPath>
19+
<NetCoreAppCurrentTestHostSharedFrameworkPath Condition="'$(UseDefaultTestHost)' != 'true'">$([MSBuild]::NormalizeDirectory('$(NetCoreAppCurrentTestHostPath)', 'shared', '$(MicrosoftNetCoreAppFrameworkName)', '$(ProductVersion)'))</NetCoreAppCurrentTestHostSharedFrameworkPath>
1920
<NETStandard21RefPath>$([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'netstandard.library.ref', '$(NETStandardLibraryRefVersion)', 'ref', 'netstandard2.1'))</NETStandard21RefPath>
2021

2122
<NoWarn Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">$(NoWarn);nullable</NoWarn>

src/libraries/System.Buffers/tests/System.Buffers.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111
<Compile Include="ArrayPool\UnitTests.cs" />
1212
<Compile Include="$(CommonTestPath)System\Diagnostics\Tracing\TestEventListener.cs" />
1313
</ItemGroup>
14-
</Project>
14+
</Project>

src/mono/mono/component/mini-wasm-debugger.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ mono_wasm_enable_debugging_internal (int debug_level)
137137
{
138138
log_level = debug_level;
139139
if (debug_level != 0) {
140-
PRINT_DEBUG_MSG (1, "DEBUGGING ENABLED\n");
140+
wasm_debugger_log(1, "DEBUGGING ENABLED\n");
141141
debugger_enabled = TRUE;
142142
}
143143
}

src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<ItemGroup>
99
<ProjectReference Include="$(RepoTasksDir)WasmAppBuilder\WasmAppBuilder.csproj" />
1010
<ProjectReference Include="$(RepoTasksDir)WasmBuildTasks\WasmBuildTasks.csproj" />
11+
<ProjectReference Include="$(RepoRoot)src\mono\wasm\host\WasmAppHost.csproj" />
1112

1213
<PackageFile Include="Sdk\AutoImport.props" TargetPath="Sdk" />
1314
<PackageFile Include="$(RepoRoot)\src\mono\wasm\build\WasmApp.props" TargetPath="Sdk" />
@@ -33,7 +34,12 @@
3334

3435
<ItemGroup>
3536
<PackageFile Include="$(SdkTargetsPath)" TargetPath="Sdk" />
37+
38+
<_WasmAppHostFiles Include="$(WasmAppHostDir)\*" TargetPath="WasmAppHost" />
39+
<PackageFile Include="@(_WasmAppHostFiles)" />
3640
</ItemGroup>
41+
42+
<Error Text="Could not find WasmAppHost files in $(WasmAppHostDir)" Condition="@(_WasmAppHostFiles->Count()) == 0" />
3743
</Target>
3844

3945
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />

src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/Sdk.targets.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
<WasmAppBuilderTasksAssemblyPath>$(_TasksDir)WasmAppBuilder.dll</WasmAppBuilderTasksAssemblyPath>
88
<WasmBuildTasksAssemblyPath>$(_TasksDir)WasmBuildTasks.dll</WasmBuildTasksAssemblyPath>
9+
<WasmAppHostDir>$([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory), '..', 'WasmAppHost'))</WasmAppHostDir>
910
</PropertyGroup>
1011

1112
<Import Project="$(MSBuildThisFileDirectory)\WasmApp.props" />

src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm' AND '$(UsingBrowserRuntimeWorkload)' == ''">
1111
<!-- $(WasmBuildNative)==true is needed to enable workloads, when using native references, without AOT -->
12-
<UsingBrowserRuntimeWorkload Condition="'$(RunAOTCompilation)' == 'true' or '$(WasmBuildNative)' == 'true' or '$(UsingMicrosoftNETSdkBlazorWebAssembly)' != 'true'" >true</UsingBrowserRuntimeWorkload>
12+
<UsingBrowserRuntimeWorkload Condition="'$(RunAOTCompilation)' == 'true' or '$(WasmBuildNative)' == 'true' or '$(WasmGenerateAppBundle)' == 'true' or '$(UsingMicrosoftNETSdkBlazorWebAssembly)' != 'true'" >true</UsingBrowserRuntimeWorkload>
1313
<UsingBrowserRuntimeWorkload Condition="'$(UsingBrowserRuntimeWorkload)' == ''" >$(WasmNativeWorkload)</UsingBrowserRuntimeWorkload>
1414
</PropertyGroup>
1515

src/mono/sample/wasm/Directory.Build.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
<!-- These need to be set here because the root Directory.Build.props sets up the intermediate path early -->
44
<Configuration>Release</Configuration>
55
<OutputType>Exe</OutputType>
6+
<TargetOS>browser</TargetOS>
7+
<TargetArchitecture>wasm</TargetArchitecture>
8+
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
69
</PropertyGroup>
710

811
<Import Project="..\Directory.Build.props"/>

0 commit comments

Comments
 (0)