Skip to content

Commit

Permalink
Fix running WebAssembly Browser App from VS (#75384)
Browse files Browse the repository at this point in the history
* 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

* Disable failing test

.. which is failing because the WBT is still using net7.0 on `main`,
thus using the 7.0 targets, which don't have the fix yet.

We can enable this once the fix is merged for 7.0 .

Co-authored-by: Ankit Jain <radical@gmail.com>
  • Loading branch information
thaystg and radical authored Sep 14, 2022
1 parent 59d0065 commit b55b256
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 11 deletions.
113 changes: 110 additions & 3 deletions src/mono/wasm/Wasm.Build.Tests/WasmTemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public WasmTemplateTests(ITestOutputHelper output, SharedBuildPerTestClassFixtur
{
}

private void updateProgramCS()
private void UpdateProgramCS()
{
string programText = """
Console.WriteLine("Hello, Console!");
Expand Down Expand Up @@ -191,7 +191,7 @@ public void ConsoleBuildAndRun(string config, bool relinking)
string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
string projectName = Path.GetFileNameWithoutExtension(projectFile);

updateProgramCS();
UpdateProgramCS();
UpdateConsoleMainJs();
if (relinking)
AddItemsPropertiesToProject(projectFile, "<WasmBuildNative>true</WasmBuildNative>");
Expand Down Expand Up @@ -219,6 +219,113 @@ public void ConsoleBuildAndRun(string config, bool relinking)
Assert.Contains("args[2] = z", output);
}

public static TheoryData<bool, bool, string> TestDataForAppBundleDir()
{
var data = new TheoryData<bool, bool, string>();
AddTestData(forConsole: true, runOutsideProjectDirectory: false);
AddTestData(forConsole: true, runOutsideProjectDirectory: true);

AddTestData(forConsole: false, runOutsideProjectDirectory: false);
AddTestData(forConsole: false, runOutsideProjectDirectory: true);

void AddTestData(bool forConsole, bool runOutsideProjectDirectory)
{
// FIXME: Disabled for `main` right now, till 7.0 gets the fix
//data.Add(runOutsideProjectDirectory, forConsole, string.Empty);

data.Add(runOutsideProjectDirectory, forConsole,
$"<OutputPath>{Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())}</OutputPath>");
data.Add(runOutsideProjectDirectory, forConsole,
$"<WasmAppDir>{Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())}</WasmAppDir>");
}

return data;
}

[ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
[MemberData(nameof(TestDataForAppBundleDir))]
public async Task RunWithDifferentAppBundleLocations(bool forConsole, bool runOutsideProjectDirectory, string extraProperties)
=> await (forConsole
? ConsoleRunWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory)
: BrowserRunTwiceWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory));

private async Task BrowserRunTwiceWithAndThenWithoutBuildAsync(string config, string extraProperties = "", bool runOutsideProjectDirectory = false)
{
string id = $"browser_{config}_{Path.GetRandomFileName()}";
string projectFile = CreateWasmTemplateProject(id, "wasmbrowser");

UpdateBrowserMainJs(DefaultTargetFramework);

if (!string.IsNullOrEmpty(extraProperties))
AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);

string workingDir = runOutsideProjectDirectory ? Path.GetTempPath() : _projectDir!;

{
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
.WithWorkingDirectory(workingDir);

await using var runner = new BrowserRunner();
var page = await runner.RunAsync(runCommand, $"run -c {config} --project {projectFile} --forward-console");
await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
}

{
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
.WithWorkingDirectory(workingDir);

await using var runner = new BrowserRunner();
var page = await runner.RunAsync(runCommand, $"run -c {config} --no-build --project {projectFile} --forward-console");
await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
}
}

private Task ConsoleRunWithAndThenWithoutBuildAsync(string config, string extraProperties = "", bool runOutsideProjectDirectory = false)
{
string id = $"console_{config}_{Path.GetRandomFileName()}";
string projectFile = CreateWasmTemplateProject(id, "wasmconsole");

UpdateProgramCS();
UpdateConsoleMainJs();

if (!string.IsNullOrEmpty(extraProperties))
AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);

string workingDir = runOutsideProjectDirectory ? Path.GetTempPath() : _projectDir!;

{
string runArgs = $"run -c {config} --project {projectFile}";
runArgs += " x y z";
using var cmd = new RunCommand(s_buildEnv, _testOutput, label: id)
.WithWorkingDirectory(workingDir)
.WithEnvironmentVariables(s_buildEnv.EnvVars);
var res = cmd.ExecuteWithCapturedOutput(runArgs).EnsureExitCode(42);

Assert.Contains("args[0] = x", res.Output);
Assert.Contains("args[1] = y", res.Output);
Assert.Contains("args[2] = z", res.Output);
}

_testOutput.WriteLine($"{Environment.NewLine}[{id}] Running again with --no-build{Environment.NewLine}");

{
// Run with --no-build
string runArgs = $"run -c {config} --project {projectFile} --no-build";
runArgs += " x y z";
using var cmd = new RunCommand(s_buildEnv, _testOutput, label: id)
.WithWorkingDirectory(workingDir);
var res = cmd.ExecuteWithCapturedOutput(runArgs).EnsureExitCode(42);

Assert.Contains("args[0] = x", res.Output);
Assert.Contains("args[1] = y", res.Output);
Assert.Contains("args[2] = z", res.Output);
}

return Task.CompletedTask;
}

public static TheoryData<string, bool, bool> TestDataForConsolePublishAndRun()
{
var data = new TheoryData<string, bool, bool>();
Expand All @@ -245,7 +352,7 @@ public void ConsolePublishAndRun(string config, bool aot, bool relinking)
string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
string projectName = Path.GetFileNameWithoutExtension(projectFile);

updateProgramCS();
UpdateProgramCS();
UpdateConsoleMainJs();

if (aot)
Expand Down
30 changes: 22 additions & 8 deletions src/mono/wasm/build/WasmApp.targets
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,34 @@

<!-- if DebuggerSupport==true, then ensure that WasmDebugLevel isn't disabling debugging -->
<WasmDebugLevel Condition="('$(WasmDebugLevel)' == '' or '$(WasmDebugLevel)' == '0') and ('$(DebuggerSupport)' == 'true' or '$(Configuration)' == 'Debug')">-1</WasmDebugLevel>
</PropertyGroup>

<PropertyGroup Label="Identify app bundle directory to run from">
<!-- Allow running from custom WasmAppDir -->
<_AppBundleDirForRunCommand Condition="'$(WasmAppDir)' != ''">$(WasmAppDir)</_AppBundleDirForRunCommand>

<!--
This is the default path. We have to build it explicitly because
RuntimeIdentifierInference.targets is imported after this file, and
updates OutputPath to include the RID. So, we don't have the correct
final OutputPath here. But we need it for `dotnet run` to work, as it
just reads the RunCommand after evaluation.
<!-- workaround: RuntimeIdentifierInference.targets is imported after this file, and updates OutputPath to include
the RID. So, we don't have the correct final OutputPath here. But we need it for `dotnet run` to work,
as it just reads the RunCommand after evaluation. -->
<_AppBundleDirForRunCommand Condition="Exists('$(WasmAppDir)/$(AssemblyName).runtimeconfig.json')">$(WasmAppDir)</_AppBundleDirForRunCommand>
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == '' and Exists('$(OutputPath)/AppBundle')">$([MSBuild]::NormalizeDirectory($(OutputPath), 'AppBundle'))</_AppBundleDirForRunCommand>
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == '' and Exists('$(OutputPath)/browser-wasm/AppBundle')">$([MSBuild]::NormalizeDirectory($(OutputPath), 'browser-wasm', 'AppBundle'))</_AppBundleDirForRunCommand>
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == ''">OutputPath=$(OutputPath), OutDir=$(OutDir)</_AppBundleDirForRunCommand>
The path might not have been created yet, for example when creating a new project in VS, so don't use an Exists() check
-->
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == ''">$([System.IO.Path]::Combine($(OutputPath), 'browser-wasm', 'AppBundle'))</_AppBundleDirForRunCommand>

<!-- Ensure the path is absolute. In case of VS, the cwd might not be the correct one, so explicitly
use $(MSBuildProjectDirectory). -->
<_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' != '' and !$([System.IO.Path]::IsPathRooted($(_AppBundleDirForRunCommand)))">$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(_AppBundleDirForRunCommand)))</_AppBundleDirForRunCommand>
</PropertyGroup>

<PropertyGroup Condition="'$(WasmGenerateAppBundle)' == 'true'">
<RunCommand Condition="'$(DOTNET_HOST_PATH)' != '' and Exists($(DOTNET_HOST_PATH))">$(DOTNET_HOST_PATH)</RunCommand>
<RunCommand Condition="'$(RunCommand)' == ''">dotnet</RunCommand>
<RunArguments Condition="'$(RunArguments)' == ''">exec &quot;$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))&quot; --runtime-config &quot;$(_AppBundleDirForRunCommand)/$(AssemblyName).runtimeconfig.json&quot; $(WasmHostArguments)</RunArguments>

<_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(_AppBundleDirForRunCommand), '$(AssemblyName).runtimeconfig.json'))</_RuntimeConfigJsonPath>
<RunArguments Condition="'$(RunArguments)' == ''">exec &quot;$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))&quot; --runtime-config &quot;$(_RuntimeConfigJsonPath)&quot; $(WasmHostArguments)</RunArguments>
<RunWorkingDirectory Condition="'$(RunWorkingDirectory)' == ''">$(_AppBundleDirForRunCommand)</RunWorkingDirectory>
</PropertyGroup>

Expand Down

0 comments on commit b55b256

Please sign in to comment.