Skip to content

Commit 79e022f

Browse files
github-actions[bot]thaystgradical
authored
[release/7.0] Fix running WebAssembly Browser App from VS (#75508)
* trying to fix 75356 * Trying to fix for running from VS and from command line * Remove unused spaces * Fix again * Addressing @radical comments * OutputPath can be missing the rid during the evaluation * more cleanup * Replacing NormalizeDirectory with [System.IO.Path]::Combine * [wasm] Add tests for running templates outside project directory * Add tests for browser template * fix Co-authored-by: Thays Grazia <[email protected]> Co-authored-by: Ankit Jain <[email protected]>
1 parent 77295ad commit 79e022f

File tree

2 files changed

+131
-11
lines changed

2 files changed

+131
-11
lines changed

src/mono/wasm/build/WasmApp.targets

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,20 +113,34 @@
113113

114114
<!-- if DebuggerSupport==true, then ensure that WasmDebugLevel isn't disabling debugging -->
115115
<WasmDebugLevel Condition="('$(WasmDebugLevel)' == '' or '$(WasmDebugLevel)' == '0') and ('$(DebuggerSupport)' == 'true' or '$(Configuration)' == 'Debug')">-1</WasmDebugLevel>
116+
</PropertyGroup>
117+
118+
<PropertyGroup Label="Identify app bundle directory to run from">
119+
<!-- Allow running from custom WasmAppDir -->
120+
<_AppBundleDirForRunCommand Condition="'$(WasmAppDir)' != ''">$(WasmAppDir)</_AppBundleDirForRunCommand>
121+
122+
<!--
123+
This is the default path. We have to build it explicitly because
124+
RuntimeIdentifierInference.targets is imported after this file, and
125+
updates OutputPath to include the RID. So, we don't have the correct
126+
final OutputPath here. But we need it for `dotnet run` to work, as it
127+
just reads the RunCommand after evaluation.
116128
117-
<!-- workaround: RuntimeIdentifierInference.targets is imported after this file, and updates OutputPath to include
118-
the RID. So, we don't have the correct final OutputPath here. But we need it for `dotnet run` to work,
119-
as it just reads the RunCommand after evaluation. -->
120-
<_AppBundleDirForRunCommand Condition="Exists('$(WasmAppDir)/$(AssemblyName).runtimeconfig.json')">$(WasmAppDir)</_AppBundleDirForRunCommand>
121-
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == '' and Exists('$(OutputPath)/AppBundle')">$([MSBuild]::NormalizeDirectory($(OutputPath), 'AppBundle'))</_AppBundleDirForRunCommand>
122-
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == '' and Exists('$(OutputPath)/browser-wasm/AppBundle')">$([MSBuild]::NormalizeDirectory($(OutputPath), 'browser-wasm', 'AppBundle'))</_AppBundleDirForRunCommand>
123-
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == ''">OutputPath=$(OutputPath), OutDir=$(OutDir)</_AppBundleDirForRunCommand>
129+
The path might not have been created yet, for example when creating a new project in VS, so don't use an Exists() check
130+
-->
131+
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == ''">$([System.IO.Path]::Combine($(OutputPath), 'browser-wasm', 'AppBundle'))</_AppBundleDirForRunCommand>
132+
133+
<!-- Ensure the path is absolute. In case of VS, the cwd might not be the correct one, so explicitly
134+
use $(MSBuildProjectDirectory). -->
135+
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' != '' and !$([System.IO.Path]::IsPathRooted($(_AppBundleDirForRunCommand)))">$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(_AppBundleDirForRunCommand)))</_AppBundleDirForRunCommand>
124136
</PropertyGroup>
125137

126138
<PropertyGroup Condition="'$(WasmGenerateAppBundle)' == 'true'">
127139
<RunCommand Condition="'$(DOTNET_HOST_PATH)' != '' and Exists($(DOTNET_HOST_PATH))">$(DOTNET_HOST_PATH)</RunCommand>
128140
<RunCommand Condition="'$(RunCommand)' == ''">dotnet</RunCommand>
129-
<RunArguments Condition="'$(RunArguments)' == ''">exec &quot;$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))&quot; --runtime-config &quot;$(_AppBundleDirForRunCommand)/$(AssemblyName).runtimeconfig.json&quot; $(WasmHostArguments)</RunArguments>
141+
142+
<_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(_AppBundleDirForRunCommand), '$(AssemblyName).runtimeconfig.json'))</_RuntimeConfigJsonPath>
143+
<RunArguments Condition="'$(RunArguments)' == ''">exec &quot;$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))&quot; --runtime-config &quot;$(_RuntimeConfigJsonPath)&quot; $(WasmHostArguments)</RunArguments>
130144
<RunWorkingDirectory Condition="'$(RunWorkingDirectory)' == ''">$(_AppBundleDirForRunCommand)</RunWorkingDirectory>
131145
</PropertyGroup>
132146

src/tests/BuildWasmApps/Wasm.Build.Tests/WasmTemplateTests.cs

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public WasmTemplateTests(ITestOutputHelper output, SharedBuildPerTestClassFixtur
2121
{
2222
}
2323

24-
private void updateProgramCS()
24+
private void UpdateProgramCS()
2525
{
2626
string programText = """
2727
Console.WriteLine("Hello, Console!");
@@ -188,7 +188,7 @@ public void ConsoleBuildAndRun(string config, bool relinking)
188188
string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
189189
string projectName = Path.GetFileNameWithoutExtension(projectFile);
190190

191-
updateProgramCS();
191+
UpdateProgramCS();
192192
UpdateConsoleMainJs();
193193
if (relinking)
194194
AddItemsPropertiesToProject(projectFile, "<WasmBuildNative>true</WasmBuildNative>");
@@ -216,6 +216,112 @@ public void ConsoleBuildAndRun(string config, bool relinking)
216216
Assert.Contains("args[2] = z", output);
217217
}
218218

219+
public static TheoryData<bool, bool, string> TestDataForAppBundleDir()
220+
{
221+
var data = new TheoryData<bool, bool, string>();
222+
AddTestData(forConsole: true, runOutsideProjectDirectory: false);
223+
AddTestData(forConsole: true, runOutsideProjectDirectory: true);
224+
225+
AddTestData(forConsole: false, runOutsideProjectDirectory: false);
226+
AddTestData(forConsole: false, runOutsideProjectDirectory: true);
227+
228+
void AddTestData(bool forConsole, bool runOutsideProjectDirectory)
229+
{
230+
data.Add(runOutsideProjectDirectory, forConsole, string.Empty);
231+
232+
data.Add(runOutsideProjectDirectory, forConsole,
233+
$"<OutputPath>{Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())}</OutputPath>");
234+
data.Add(runOutsideProjectDirectory, forConsole,
235+
$"<WasmAppDir>{Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())}</WasmAppDir>");
236+
}
237+
238+
return data;
239+
}
240+
241+
[ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
242+
[MemberData(nameof(TestDataForAppBundleDir))]
243+
public async Task RunWithDifferentAppBundleLocations(bool forConsole, bool runOutsideProjectDirectory, string extraProperties)
244+
=> await (forConsole
245+
? ConsoleRunWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory)
246+
: BrowserRunTwiceWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory));
247+
248+
private async Task BrowserRunTwiceWithAndThenWithoutBuildAsync(string config, string extraProperties = "", bool runOutsideProjectDirectory = false)
249+
{
250+
string id = $"browser_{config}_{Path.GetRandomFileName()}";
251+
string projectFile = CreateWasmTemplateProject(id, "wasmbrowser");
252+
253+
UpdateBrowserMainJs();
254+
255+
if (!string.IsNullOrEmpty(extraProperties))
256+
AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
257+
258+
string workingDir = runOutsideProjectDirectory ? Path.GetTempPath() : _projectDir!;
259+
260+
{
261+
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
262+
.WithWorkingDirectory(workingDir);
263+
264+
await using var runner = new BrowserRunner();
265+
var page = await runner.RunAsync(runCommand, $"run -c {config} --project {projectFile} --forward-console");
266+
await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
267+
Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
268+
}
269+
270+
{
271+
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
272+
.WithWorkingDirectory(workingDir);
273+
274+
await using var runner = new BrowserRunner();
275+
var page = await runner.RunAsync(runCommand, $"run -c {config} --no-build --project {projectFile} --forward-console");
276+
await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
277+
Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
278+
}
279+
}
280+
281+
private Task ConsoleRunWithAndThenWithoutBuildAsync(string config, string extraProperties = "", bool runOutsideProjectDirectory = false)
282+
{
283+
string id = $"console_{config}_{Path.GetRandomFileName()}";
284+
string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
285+
286+
UpdateProgramCS();
287+
UpdateConsoleMainJs();
288+
289+
if (!string.IsNullOrEmpty(extraProperties))
290+
AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
291+
292+
string workingDir = runOutsideProjectDirectory ? Path.GetTempPath() : _projectDir!;
293+
294+
{
295+
string runArgs = $"run -c {config} --project {projectFile}";
296+
runArgs += " x y z";
297+
using var cmd = new RunCommand(s_buildEnv, _testOutput, label: id)
298+
.WithWorkingDirectory(workingDir)
299+
.WithEnvironmentVariables(s_buildEnv.EnvVars);
300+
var res = cmd.ExecuteWithCapturedOutput(runArgs).EnsureExitCode(42);
301+
302+
Assert.Contains("args[0] = x", res.Output);
303+
Assert.Contains("args[1] = y", res.Output);
304+
Assert.Contains("args[2] = z", res.Output);
305+
}
306+
307+
_testOutput.WriteLine($"{Environment.NewLine}[{id}] Running again with --no-build{Environment.NewLine}");
308+
309+
{
310+
// Run with --no-build
311+
string runArgs = $"run -c {config} --project {projectFile} --no-build";
312+
runArgs += " x y z";
313+
using var cmd = new RunCommand(s_buildEnv, _testOutput, label: id)
314+
.WithWorkingDirectory(workingDir);
315+
var res = cmd.ExecuteWithCapturedOutput(runArgs).EnsureExitCode(42);
316+
317+
Assert.Contains("args[0] = x", res.Output);
318+
Assert.Contains("args[1] = y", res.Output);
319+
Assert.Contains("args[2] = z", res.Output);
320+
}
321+
322+
return Task.CompletedTask;
323+
}
324+
219325
public static TheoryData<string, bool, bool> TestDataForConsolePublishAndRun()
220326
{
221327
var data = new TheoryData<string, bool, bool>();
@@ -242,7 +348,7 @@ public void ConsolePublishAndRun(string config, bool aot, bool relinking)
242348
string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
243349
string projectName = Path.GetFileNameWithoutExtension(projectFile);
244350

245-
updateProgramCS();
351+
UpdateProgramCS();
246352
UpdateConsoleMainJs();
247353

248354
if (aot)

0 commit comments

Comments
 (0)