Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm] Avoid unnecessary relinking when publishing a blazor project for AOT #82748

Merged
merged 11 commits into from
Mar 2, 2023
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
]
},
"microsoft.dotnet.xharness.cli": {
"version": "1.0.0-prerelease.23117.1",
"version": "1.0.0-prerelease.23151.1",
"commands": [
"xharness"
]
Expand Down
12 changes: 6 additions & 6 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -284,17 +284,17 @@
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>252018c3d3fffdb592413cf61d5b80cf751e0a59</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.XHarness.TestRunners.Common" Version="1.0.0-prerelease.23117.1">
<Dependency Name="Microsoft.DotNet.XHarness.TestRunners.Common" Version="1.0.0-prerelease.23151.1">
<Uri>https://github.com/dotnet/xharness</Uri>
<Sha>8d789cbeecb6c89bf470fdc7727a8f501724fc8a</Sha>
<Sha>05d4d5fc8634fa68ec53a9a3b31271797f4a0c7c</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.23117.1">
<Dependency Name="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.23151.1">
<Uri>https://github.com/dotnet/xharness</Uri>
<Sha>8d789cbeecb6c89bf470fdc7727a8f501724fc8a</Sha>
<Sha>05d4d5fc8634fa68ec53a9a3b31271797f4a0c7c</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.XHarness.CLI" Version="1.0.0-prerelease.23117.1">
<Dependency Name="Microsoft.DotNet.XHarness.CLI" Version="1.0.0-prerelease.23151.1">
<Uri>https://github.com/dotnet/xharness</Uri>
<Sha>8d789cbeecb6c89bf470fdc7727a8f501724fc8a</Sha>
<Sha>05d4d5fc8634fa68ec53a9a3b31271797f4a0c7c</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.PackageTesting" Version="8.0.0-beta.23123.2">
<Uri>https://github.com/dotnet/arcade</Uri>
Expand Down
6 changes: 3 additions & 3 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@
<!-- Testing -->
<MicrosoftNETCoreCoreDisToolsVersion>1.1.0</MicrosoftNETCoreCoreDisToolsVersion>
<MicrosoftNETTestSdkVersion>17.4.0-preview-20220707-01</MicrosoftNETTestSdkVersion>
<MicrosoftDotNetXHarnessTestRunnersCommonVersion>1.0.0-prerelease.23117.1</MicrosoftDotNetXHarnessTestRunnersCommonVersion>
<MicrosoftDotNetXHarnessTestRunnersXunitVersion>1.0.0-prerelease.23117.1</MicrosoftDotNetXHarnessTestRunnersXunitVersion>
<MicrosoftDotNetXHarnessCLIVersion>1.0.0-prerelease.23117.1</MicrosoftDotNetXHarnessCLIVersion>
<MicrosoftDotNetXHarnessTestRunnersCommonVersion>1.0.0-prerelease.23151.1</MicrosoftDotNetXHarnessTestRunnersCommonVersion>
<MicrosoftDotNetXHarnessTestRunnersXunitVersion>1.0.0-prerelease.23151.1</MicrosoftDotNetXHarnessTestRunnersXunitVersion>
<MicrosoftDotNetXHarnessCLIVersion>1.0.0-prerelease.23151.1</MicrosoftDotNetXHarnessCLIVersion>
<MicrosoftDotNetHotReloadUtilsGeneratorBuildToolVersion>1.1.0-alpha.0.23113.1</MicrosoftDotNetHotReloadUtilsGeneratorBuildToolVersion>
<XUnitVersion>2.4.2</XUnitVersion>
<XUnitAnalyzersVersion>1.0.0</XUnitAnalyzersVersion>
Expand Down
82 changes: 67 additions & 15 deletions src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ public void DefaultTemplate_WithoutWorkload(string config)
CreateBlazorWasmTemplateProject(id);

// Build
BuildInternal(id, config, publish: false);
BlazorBuildInternal(id, config, publish: false);
AssertBlazorBootJson(config, isPublish: false);

// Publish
BuildInternal(id, config, publish: true);
BlazorBuildInternal(id, config, publish: true);
AssertBlazorBootJson(config, isPublish: true);
}

Expand All @@ -52,11 +52,11 @@ public void DefaultTemplate_NoAOT_WithWorkload(string config)
if (config == "Release")
{
// relinking in publish for Release config
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked, ExpectRelinkDirWhenPublishing: true));
}
else
{
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack, ExpectRelinkDirWhenPublishing: true));
}
}

Expand All @@ -82,12 +82,23 @@ public void DefaultTemplate_NoAOT_WithWorkload(string config)
//}

[Theory]
[InlineData("Debug")]
[InlineData("Release")]
public async Task WithDllImportInMainAssembly(string config)
[InlineData("Debug", /*build*/true, /*publish*/false)]
[InlineData("Debug", /*build*/false, /*publish*/true)]
[InlineData("Debug", /*build*/true, /*publish*/true)]
[InlineData("Release", /*build*/true, /*publish*/false)]
[InlineData("Release", /*build*/false, /*publish*/true)]
[InlineData("Release", /*build*/true, /*publish*/true)]
public async Task WithDllImportInMainAssembly(string config, bool build, bool publish)
{
// Based on https://github.com/dotnet/runtime/issues/59255
string id = $"blz_dllimp_{config}";
string id = $"blz_dllimp_{config}_";
if (build && publish)
id += "build_then_publish";
else if (build)
id += "build";
else
id += "publish";

string projectFile = CreateProjectWithNativeReference(id);
string nativeSource = @"
#include <stdio.h>
Expand Down Expand Up @@ -118,18 +129,29 @@ public static class MyDllImports
outputText = $"{result}";
""");

BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
CheckNativeFileLinked(forPublish: false);
if (build)
{
BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
CheckNativeFileLinked(forPublish: false);
}

if (publish)
{
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked, ExpectRelinkDirWhenPublishing: build));
CheckNativeFileLinked(forPublish: true);
}

BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
CheckNativeFileLinked(forPublish: true);
if (publish)
await BlazorRunForPublishWithWebServer(config, TestDllImport);
else
await BlazorRunForBuildWithDotnetRun(config, TestDllImport);

await BlazorRun(config, async (page) =>
async Task TestDllImport(IPage page)
{
await page.Locator("text=\"cpp_add\"").ClickAsync();
var txt = await page.Locator("p[role='test']").InnerHTMLAsync();
Assert.Equal("Output: 22", txt);
});
}

void CheckNativeFileLinked(bool forPublish)
{
Expand Down Expand Up @@ -181,7 +203,7 @@ public void BugRegression_60479_WithRazorClassLib()
BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));

// will relink
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked, ExpectRelinkDirWhenPublishing: true));

// publish/wwwroot/_framework/blazor.boot.json
string frameworkDir = FindBlazorBinFrameworkDir(config, forPublish: true);
Expand All @@ -197,4 +219,34 @@ public void BugRegression_60479_WithRazorClassLib()

Assert.Contains("RazorClassLibrary.dll", lazyVal.EnumerateObject().Select(jp => jp.Name));
}

[ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
[InlineData("Debug")]
[InlineData("Release")]
public async Task BlazorBuildRunTest(string config)
{
string id = $"blazor_{config}_{Path.GetRandomFileName()}";
string projectFile = CreateWasmTemplateProject(id, "blazorwasm");

BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));
await BlazorRunForBuildWithDotnetRun(config);
}

[ActiveIssue("https://github.com/dotnet/runtime/issues/82481")]
[ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
[InlineData("Debug", false)]
[InlineData("Debug", true)]
[InlineData("Release", false)]
[InlineData("Release", true)]
public async Task BlazorPublishRunTest(string config, bool aot)
{
string id = $"blazor_{config}_{Path.GetRandomFileName()}";
string projectFile = CreateWasmTemplateProject(id, "blazorwasm");
if (aot)
AddItemsPropertiesToProject(projectFile, "<RunAOTCompilation>true</RunAOTCompilation>");

BlazorPublish(new BlazorBuildOptions(id, config, aot ? NativeFilesType.AOT : NativeFilesType.Relinked));
await BlazorRunForPublishWithWebServer(config);
}

}
4 changes: 2 additions & 2 deletions src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void NativeBuild_WithDeployOnBuild_UsedByVS(string config, bool nativeRel
AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);

// build with -p:DeployOnBuild=true, and that will trigger a publish
(CommandResult res, _) = BuildInternal(id, config, publish: false, setWasmDevel: false, "-p:DeployOnBuild=true");
(CommandResult res, _) = BlazorBuildInternal(id, config, publish: false, setWasmDevel: false, "-p:DeployOnBuild=true");

var expectedFileType = nativeRelink ? NativeFilesType.Relinked : NativeFilesType.AOT;

Expand Down Expand Up @@ -83,7 +83,7 @@ public void DefaultTemplate_AOT_InProjectFile(string config)
BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));

// will aot
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.AOT));
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.AOT, ExpectRelinkDirWhenPublishing: true));

// build again
BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));
Expand Down
6 changes: 3 additions & 3 deletions src/mono/wasm/Wasm.Build.Tests/Blazor/NativeRefTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void WithNativeReference_AOTInProjectFile(string config)

BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));

BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.AOT));
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.AOT, ExpectRelinkDirWhenPublishing: true));

// will relink
BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
Expand All @@ -53,10 +53,10 @@ public void WithNativeReference_AOTOnCommandLine(string config)

BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));

BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.AOT), "-p:RunAOTCompilation=true");
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.AOT, ExpectRelinkDirWhenPublishing: true), "-p:RunAOTCompilation=true");

// no aot!
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked, ExpectRelinkDirWhenPublishing: true));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public async Task<IPage> RunAsync(ToolCommand cmd, string args, bool headless =
throw new Exception($"Process ended before the url was found");
}
if (!urlAvailable.Task.IsCompleted)
throw new Exception("Timed out waiting for the app host url");
throw new Exception("Timed out waiting for the web server url");

var url = new Uri(urlAvailable.Task.Result);
Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/Wasm.Build.Tests/BuildPublishTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ public void BuildThenPublishNoAOT(BuildArgs buildArgs, RunHost host, string id)
Publish: false
));


Run();

if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product))
Expand Down
43 changes: 36 additions & 7 deletions src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ public string CreateBlazorWasmTemplateProject(string id)
if (options.WarnAsError)
extraArgs = extraArgs.Append("/warnaserror").ToArray();

var res = BuildInternal(options.Id, options.Config, publish: false, setWasmDevel: false, extraArgs);
var res = BlazorBuildInternal(options.Id, options.Config, publish: false, setWasmDevel: false, extraArgs);
_testOutput.WriteLine($"BlazorBuild, options.tfm: {options.TargetFramework}");
AssertDotNetNativeFiles(options.ExpectedFileType, options.Config, forPublish: false, targetFramework: options.TargetFramework);
AssertBlazorBundle(options.Config,
Expand All @@ -557,7 +557,7 @@ public string CreateBlazorWasmTemplateProject(string id)

protected (CommandResult, string) BlazorPublish(BlazorBuildOptions options, params string[] extraArgs)
{
var res = BuildInternal(options.Id, options.Config, publish: true, setWasmDevel: false, extraArgs);
var res = BlazorBuildInternal(options.Id, options.Config, publish: true, setWasmDevel: false, extraArgs);
AssertDotNetNativeFiles(options.ExpectedFileType, options.Config, forPublish: true, targetFramework: options.TargetFramework);
AssertBlazorBundle(options.Config,
isPublish: true,
Expand All @@ -572,11 +572,20 @@ public string CreateBlazorWasmTemplateProject(string id)

// make sure this assembly gets skipped
Assert.DoesNotContain("Microsoft.JSInterop.WebAssembly.dll -> Microsoft.JSInterop.WebAssembly.dll.bc", res.Item1.Output);

}

string objBuildDir = Path.Combine(_projectDir!, "obj", options.Config, options.TargetFramework, "wasm", "for-build");
// Check that we linked only for publish
if (options.ExpectRelinkDirWhenPublishing)
Assert.True(Directory.Exists(objBuildDir), $"Could not find expected {objBuildDir}, which gets created when relinking during Build. This is liokely a test authoring error");
else
Assert.False(Directory.Exists(objBuildDir), $"Found unexpected {objBuildDir}, which gets created when relinking during Build");

return res;
}

protected (CommandResult, string) BuildInternal(string id, string config, bool publish=false, bool setWasmDevel=true, params string[] extraArgs)
protected (CommandResult, string) BlazorBuildInternal(string id, string config, bool publish=false, bool setWasmDevel=true, params string[] extraArgs)
{
string label = publish ? "publish" : "build";
_testOutput.WriteLine($"{Environment.NewLine}** {label} **{Environment.NewLine}");
Expand Down Expand Up @@ -923,13 +932,26 @@ public void BlazorAddRazorButton(string buttonText, string customCode, string me
File.WriteAllText(counterRazorPath, oldContent + additionalCode);
}

public async Task BlazorRun(string config, Func<IPage, Task>? test=null, string extraArgs="--no-build")
// Keeping these methods with explicit Build/Publish in the name
// so in the test code it is evident which is being run!
public Task BlazorRunForBuildWithDotnetRun(string config, Func<IPage, Task>? test=null, string extraArgs="--no-build")
=> BlazorRunTest($"run -c {config} {extraArgs}", _projectDir!, test);

public Task BlazorRunForPublishWithWebServer(string config, Func<IPage, Task>? test=null, string extraArgs="")
=> BlazorRunTest($"{s_xharnessRunnerCommand} wasm webserver --app=. --web-server-use-default-files {extraArgs}",
Path.GetFullPath(Path.Combine(FindBlazorBinFrameworkDir(config, forPublish: true), "..")),
test);

public async Task BlazorRunTest(string runArgs,
string workingDirectory,
Func<IPage, Task>? test = null,
bool detectRuntimeFailures = true)
{
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
.WithWorkingDirectory(_projectDir!);
.WithWorkingDirectory(workingDirectory);

await using var runner = new BrowserRunner(_testOutput);
var page = await runner.RunAsync(runCommand, $"run -c {config} {extraArgs}", onConsoleMessage: OnConsoleMessage);
var page = await runner.RunAsync(runCommand, runArgs, onConsoleMessage: OnConsoleMessage);

await page.Locator("text=Counter").ClickAsync();
var txt = await page.Locator("p[role='status']").InnerHTMLAsync();
Expand All @@ -947,6 +969,12 @@ void OnConsoleMessage(IConsoleMessage msg)
if (EnvironmentVariables.ShowBuildOutput)
Console.WriteLine($"[{msg.Type}] {msg.Text}");
_testOutput.WriteLine($"[{msg.Type}] {msg.Text}");

if (detectRuntimeFailures)
{
if (msg.Text.Contains("[MONO] * Assertion") || msg.Text.Contains("Error: [MONO] "))
throw new XunitException($"Detected a runtime failure at line: {msg.Text}");
}
}
}

Expand Down Expand Up @@ -1211,7 +1239,8 @@ public record BlazorBuildOptions
string Config,
NativeFilesType ExpectedFileType,
string TargetFramework = BuildTestBase.DefaultTargetFrameworkForBlazor,
bool WarnAsError = true
bool WarnAsError = true,
bool ExpectRelinkDirWhenPublishing=false
);

public enum GlobalizationMode
Expand Down
4 changes: 2 additions & 2 deletions src/mono/wasm/Wasm.Build.Tests/CleanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ private void Blazor_BuildNativeNonNative_ThenCleanTest(string config, bool first
AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);

bool relink = firstBuildNative;
BuildInternal(id, config, publish: false,
BlazorBuildInternal(id, config, publish: false,
extraArgs: relink ? "-p:WasmBuildNative=true" : string.Empty);

string relinkDir = Path.Combine(_projectDir!, "obj", config, DefaultTargetFrameworkForBlazor, "wasm", "for-build");
if (relink)
Assert.True(Directory.Exists(relinkDir), $"Could not find expected relink dir: {relinkDir}");

relink = !firstBuildNative;
BuildInternal(id, config, publish: false,
BlazorBuildInternal(id, config, publish: false,
extraArgs: relink ? "-p:WasmBuildNative=true" : string.Empty);

if (relink)
Expand Down
8 changes: 6 additions & 2 deletions src/mono/wasm/Wasm.Build.Tests/WasmNativeDefaultsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ public void Defaults(string config, string extraProperties, bool aot, bool build
{
string output = CheckWasmNativeDefaultValue("native_defaults_publish", config, extraProperties, aot, dotnetWasmFromRuntimePack: !publishValue);

Assert.Contains($"** WasmBuildNative: '{buildValue.ToString().ToLower()}', WasmBuildingForNestedPublish: ''", output);
// for build
Assert.DoesNotContain($"** WasmBuildNative: '{buildValue.ToString().ToLower()}', WasmBuildingForNestedPublish: ''", output);
// for publish
Assert.Contains($"** WasmBuildNative: '{publishValue.ToString().ToLower()}', WasmBuildingForNestedPublish: 'true'", output);
Assert.Contains("Stopping the build", output);
}
Expand All @@ -61,7 +63,9 @@ public void WithNativeReference(string config, string extraProperties, bool buil
dotnetWasmFromRuntimePack: !publishValue,
extraItems: nativeRefItem);

Assert.Contains($"** WasmBuildNative: '{buildValue.ToString().ToLower()}', WasmBuildingForNestedPublish: ''", output);
// for build
Assert.DoesNotContain($"** WasmBuildNative: '{buildValue.ToString().ToLower()}', WasmBuildingForNestedPublish: ''", output);
// for publish
Assert.Contains($"** WasmBuildNative: '{publishValue.ToString().ToLower()}', WasmBuildingForNestedPublish: 'true'", output);
Assert.Contains("Stopping the build", output);
}
Expand Down
Loading