diff --git a/eng/testing/scenarios/BuildWasmAppsJobsList.txt b/eng/testing/scenarios/BuildWasmAppsJobsList.txt index e16c74fe4f0750..9bb06b872bb6c9 100644 --- a/eng/testing/scenarios/BuildWasmAppsJobsList.txt +++ b/eng/testing/scenarios/BuildWasmAppsJobsList.txt @@ -43,3 +43,5 @@ Wasm.Build.Tests.WasmTemplateTests Wasm.Build.Tests.WorkloadTests Wasm.Build.Tests.TestAppScenarios.ModuleConfigTests Wasm.Build.Tests.MT.Blazor.SimpleMultiThreadedTests +Wasm.Build.Tests.TestAppScenarios.WasmSdkDebugLevelTests +Wasm.Build.Tests.TestAppScenarios.WasmAppBuilderDebugLevelTests diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index 4dc6b71005449b..9e0fdaf3289e6f 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -15,10 +15,7 @@ But we do want to set it for Configuration=Debug . --> -1 - - reset-to-zero + 0 full true diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index 324f36cad7957f..b13b13a02bcffc 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -212,9 +212,6 @@ Copyright (c) .NET Foundation. All rights reserved. <_BlazorWebAssemblyStartupMemoryCache>$(BlazorWebAssemblyStartupMemoryCache) <_BlazorWebAssemblyJiterpreter>$(BlazorWebAssemblyJiterpreter) <_BlazorWebAssemblyRuntimeOptions>$(BlazorWebAssemblyRuntimeOptions) - <_WasmDebugLevel>$(WasmDebugLevel) - <_WasmDebugLevel Condition="'$(_WasmDebugLevel)' == ''">0 - <_WasmDebugLevel Condition="'$(_WasmDebugLevel)' == '0' and ('$(DebuggerSupport)' == 'true' or '$(Configuration)' == 'Debug')">-1 $(OutputPath)$(PublishDirName)\ @@ -373,7 +370,7 @@ Copyright (c) .NET Foundation. All rights reserved. AssemblyPath="@(IntermediateAssembly)" Resources="@(_WasmOutputWithHash)" DebugBuild="true" - DebugLevel="$(_WasmDebugLevel)" + DebugLevel="$(WasmDebugLevel)" LinkerEnabled="false" CacheBootResources="$(BlazorCacheBootResources)" OutputPath="$(_WasmBuildBootJsonPath)" @@ -389,7 +386,8 @@ Copyright (c) .NET Foundation. All rights reserved. Extensions="@(WasmBootConfigExtension)" TargetFrameworkVersion="$(TargetFrameworkVersion)" ModuleAfterConfigLoaded="@(WasmModuleAfterConfigLoaded)" - ModuleAfterRuntimeReady="@(WasmModuleAfterRuntimeReady)" /> + ModuleAfterRuntimeReady="@(WasmModuleAfterRuntimeReady)" + IsPublish="false" /> @@ -566,7 +564,7 @@ Copyright (c) .NET Foundation. All rights reserved. AssemblyPath="@(IntermediateAssembly)" Resources="@(_WasmPublishBootResourceWithHash)" DebugBuild="false" - DebugLevel="$(_WasmDebugLevel)" + DebugLevel="$(WasmDebugLevel)" LinkerEnabled="$(PublishTrimmed)" CacheBootResources="$(BlazorCacheBootResources)" OutputPath="$(IntermediateOutputPath)blazor.publish.boot.json" @@ -582,7 +580,8 @@ Copyright (c) .NET Foundation. All rights reserved. Extensions="@(WasmBootConfigExtension)" TargetFrameworkVersion="$(TargetFrameworkVersion)" ModuleAfterConfigLoaded="@(WasmModuleAfterConfigLoaded)" - ModuleAfterRuntimeReady="@(WasmModuleAfterRuntimeReady)" /> + ModuleAfterRuntimeReady="@(WasmModuleAfterRuntimeReady)" + IsPublish="true" /> diff --git a/src/mono/wasi/build/WasiApp.targets b/src/mono/wasi/build/WasiApp.targets index 099ba8c3ebea27..c403cd718f55c6 100644 --- a/src/mono/wasi/build/WasiApp.targets +++ b/src/mono/wasi/build/WasiApp.targets @@ -368,6 +368,11 @@ + + <_WasmOutputSymbolsToAppBundle Condition="'$(CopyOutputSymbolsToPublishDirectory)' == 'true' and '$(_IsPublishing)' == 'true'">true + <_WasmOutputSymbolsToAppBundle Condition="'$(_WasmOutputSymbolsToAppBundle)' == ''">false + + diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmTestBase.cs index 1d13051dd7344e..dbf0e848f75135 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmTestBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmTestBase.cs @@ -75,6 +75,10 @@ public string CreateBlazorWasmTemplateProject(string id) if (options.ExpectSuccess && options.AssertAppBundle) { + // Because we do relink in Release publish by default + if (options.Config == "Release") + options = options with { ExpectedFileType = NativeFilesType.Relinked }; + AssertBundle(res.Output, options with { IsPublish = true }); } diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs index 96f2c4ebd6a1bf..5d028cc238909a 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs @@ -28,7 +28,7 @@ public async Task LoadAppSettingsBasedOnApplicationEnvironment(string applicatio CopyTestAsset("WasmBasicTestApp", "AppSettingsTests"); PublishProject("Debug"); - var result = await RunSdkStyleApp(new( + var result = await RunSdkStyleAppForPublish(new( Configuration: "Debug", TestScenario: "AppSettingsTest", BrowserQueryString: new Dictionary { ["applicationEnvironment"] = applicationEnvironment } diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppTestBase.cs index dc6fb9b490e7f3..3b3baf581c185a 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppTestBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppTestBase.cs @@ -36,15 +36,15 @@ protected void CopyTestAsset(string assetName, string generatedProjectNamePrefix _projectDir = Path.Combine(_projectDir!, "App"); } - protected void BuildProject(string configuration) + protected void BuildProject(string configuration, bool assertAppBundle = true, params string[] extraArgs) { - (CommandResult result, _) = BlazorBuild(new BlazorBuildOptions(Id, configuration)); + (CommandResult result, _) = BlazorBuild(new BlazorBuildOptions(Id, configuration, AssertAppBundle: assertAppBundle), extraArgs); result.EnsureSuccessful(); } - protected void PublishProject(string configuration) + protected void PublishProject(string configuration, bool assertAppBundle = true, params string[] extraArgs) { - (CommandResult result, _) = BlazorPublish(new BlazorBuildOptions(Id, configuration)); + (CommandResult result, _) = BlazorPublish(new BlazorBuildOptions(Id, configuration, AssertAppBundle: assertAppBundle), extraArgs); result.EnsureSuccessful(); } @@ -52,7 +52,13 @@ protected void PublishProject(string configuration) .WithWorkingDirectory(_projectDir!) .WithEnvironmentVariable("NUGET_PACKAGES", _nugetPackagesDir); - protected async Task RunSdkStyleApp(RunOptions options) + protected Task RunSdkStyleAppForBuild(RunOptions options) + => RunSdkStyleApp(options, BlazorRunHost.DotnetRun); + + protected Task RunSdkStyleAppForPublish(RunOptions options) + => RunSdkStyleApp(options, BlazorRunHost.WebServer); + + private async Task RunSdkStyleApp(RunOptions options, BlazorRunHost host = BlazorRunHost.DotnetRun) { string queryString = "?test=" + options.TestScenario; if (options.BrowserQueryString != null) @@ -67,9 +73,10 @@ protected async Task RunSdkStyleApp(RunOptions options) CheckCounter: false, Config: options.Configuration, OnConsoleMessage: OnConsoleMessage, - QueryString: queryString); + QueryString: queryString, + Host: host); - await BlazorRunForBuildWithDotnetRun(blazorRunOptions); + await BlazorRunTest(blazorRunOptions); void OnConsoleMessage(IConsoleMessage msg) { diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTestsBase.cs new file mode 100644 index 00000000000000..219b8b2ed6714d --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTestsBase.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests.TestAppScenarios; + +public abstract class DebugLevelTestsBase : AppTestBase +{ + public DebugLevelTestsBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + protected void AssertDebugLevel(RunResult result, int value) + { + Assert.Collection( + result.TestOutput, + m => Assert.Equal($"WasmDebugLevel: {value}", m) + ); + } + + protected abstract void SetupProject(string projectId); + protected abstract Task RunForBuild(string configuration); + protected abstract Task RunForPublish(string configuration); + + [Theory] + [InlineData("Debug")] + [InlineData("Release")] + public async Task BuildWithDefaultLevel(string configuration) + { + SetupProject($"DebugLevelTests_BuildWithDefaultLevel_{configuration}"); + BuildProject(configuration, assertAppBundle: false); + + var result = await RunForBuild(configuration); + AssertDebugLevel(result, -1); + } + + [Theory] + [InlineData("Debug", 1)] + [InlineData("Release", 1)] + [InlineData("Debug", 0)] + [InlineData("Release", 0)] + public async Task BuildWithExplicitValue(string configuration, int debugLevel) + { + SetupProject($"DebugLevelTests_BuildWithExplicitValue_{configuration}"); + BuildProject(configuration, assertAppBundle: false, extraArgs: $"-p:WasmDebugLevel={debugLevel}"); + + var result = await RunForBuild(configuration); + AssertDebugLevel(result, debugLevel); + } + + [Theory] + [InlineData("Debug")] + [InlineData("Release")] + public async Task PublishWithDefaultLevel(string configuration) + { + SetupProject($"DebugLevelTests_PublishWithDefaultLevel_{configuration}"); + PublishProject(configuration, assertAppBundle: false); + + var result = await RunForPublish(configuration); + AssertDebugLevel(result, 0); + } + + [Theory] + [InlineData("Debug", 1)] + [InlineData("Release", 1)] + [InlineData("Debug", -1)] + [InlineData("Release", -1)] + public async Task PublishWithExplicitValue(string configuration, int debugLevel) + { + SetupProject($"DebugLevelTests_PublishWithExplicitValue_{configuration}"); + PublishProject(configuration, assertAppBundle: false, extraArgs: $"-p:WasmDebugLevel={debugLevel}"); + + var result = await RunForPublish(configuration); + AssertDebugLevel(result, debugLevel); + } +} \ No newline at end of file diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs index 8f37a47e18860a..cf16a0536a38da 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs @@ -26,7 +26,7 @@ public async Task LoadLazyAssemblyBeforeItIsNeeded() CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests"); PublishProject("Debug"); - var result = await RunSdkStyleApp(new(Configuration: "Debug", TestScenario: "LazyLoadingTest")); + var result = await RunSdkStyleAppForPublish(new(Configuration: "Debug", TestScenario: "LazyLoadingTest")); Assert.True(result.TestOutput.Any(m => m.Contains("FirstName")), "The lazy loading test didn't emit expected message with JSON"); } @@ -36,7 +36,7 @@ public async Task FailOnMissingLazyAssembly() CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests"); PublishProject("Debug"); - var result = await RunSdkStyleApp(new( + var result = await RunSdkStyleAppForPublish(new( Configuration: "Debug", TestScenario: "LazyLoadingTest", BrowserQueryString: new Dictionary { ["loadRequiredAssembly"] = "false" }, diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LibraryInitializerTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LibraryInitializerTests.cs index 6f68a96ad1d61a..e985ad23d89a0b 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LibraryInitializerTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LibraryInitializerTests.cs @@ -29,7 +29,7 @@ public async Task LoadLibraryInitializer() CopyTestAsset("WasmBasicTestApp", "LibraryInitializerTests_LoadLibraryInitializer"); PublishProject("Debug"); - var result = await RunSdkStyleApp(new(Configuration: "Debug", TestScenario: "LibraryInitializerTest")); + var result = await RunSdkStyleAppForPublish(new(Configuration: "Debug", TestScenario: "LibraryInitializerTest")); Assert.Collection( result.TestOutput, m => Assert.Equal("LIBRARY_INITIALIZER_TEST = 1", m) @@ -42,7 +42,7 @@ public async Task AbortStartupOnError() CopyTestAsset("WasmBasicTestApp", "LibraryInitializerTests_AbortStartupOnError"); PublishProject("Debug"); - var result = await RunSdkStyleApp(new( + var result = await RunSdkStyleAppForPublish(new( Configuration: "Debug", TestScenario: "LibraryInitializerTest", BrowserQueryString: new Dictionary { ["throwError"] = "true" }, diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/ModuleConfigTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/ModuleConfigTests.cs index 1b8c43ecea77b0..bc2b5631d2373f 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/ModuleConfigTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/ModuleConfigTests.cs @@ -28,7 +28,7 @@ public async Task DownloadProgressFinishes(bool failAssemblyDownload) CopyTestAsset("WasmBasicTestApp", $"ModuleConfigTests_DownloadProgressFinishes_{failAssemblyDownload}"); PublishProject("Debug"); - var result = await RunSdkStyleApp(new( + var result = await RunSdkStyleAppForPublish(new( Configuration: "Debug", TestScenario: "DownloadResourceProgressTest", BrowserQueryString: new Dictionary { ["failAssemblyDownload"] = failAssemblyDownload.ToString().ToLowerInvariant() } @@ -54,24 +54,4 @@ public async Task DownloadProgressFinishes(bool failAssemblyDownload) : "The download progress test did emit unexpected message about failing download" ); } - - [Fact] - public async Task OutErrOverrideWorks() - { - CopyTestAsset("WasmBasicTestApp", $"ModuleConfigTests_OutErrOverrideWorks"); - PublishProject("Debug"); - - var result = await RunSdkStyleApp(new( - Configuration: "Debug", - TestScenario: "OutErrOverrideWorks" - )); - Assert.True( - result.ConsoleOutput.Any(m => m.Contains("Emscripten out override works!")), - "Emscripten out override doesn't work" - ); - Assert.True( - result.ConsoleOutput.Any(m => m.Contains("Emscripten err override works!")), - "Emscripten err override doesn't work" - ); - } } diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs index 31dcb65582869e..2088e1522ad73b 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs @@ -29,7 +29,7 @@ public async Task LoadSatelliteAssembly() CopyTestAsset("WasmBasicTestApp", "SatelliteLoadingTests"); BuildProject("Debug"); - var result = await RunSdkStyleApp(new(Configuration: "Debug", TestScenario: "SatelliteAssembliesTest")); + var result = await RunSdkStyleAppForBuild(new(Configuration: "Debug", TestScenario: "SatelliteAssembliesTest")); Assert.Collection( result.TestOutput, m => Assert.Equal("default: hello", m), diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmAppBuilderDebugLevelTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmAppBuilderDebugLevelTests.cs new file mode 100644 index 00000000000000..60fb3d86f7d0ea --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmAppBuilderDebugLevelTests.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests.TestAppScenarios; + +public class WasmAppBuilderDebugLevelTests : DebugLevelTestsBase +{ + public WasmAppBuilderDebugLevelTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + protected override void SetupProject(string projectId) + { + Id = $"{projectId}_{GetRandomId()}"; + string projectfile = CreateWasmTemplateProject(Id, "wasmconsole", extraArgs: "-f net8.0"); + string projectDir = Path.GetDirectoryName(projectfile)!; + string mainJs = Path.Combine(projectDir, "main.mjs"); + string mainJsContent = File.ReadAllText(mainJs); + mainJsContent = mainJsContent + .Replace("await dotnet.run()", "console.log('TestOutput -> WasmDebugLevel: ' + config.debugLevel); exit(0)"); + File.WriteAllText(mainJs, mainJsContent); + } + + protected override Task RunForBuild(string configuration) + { + CommandResult res = new RunCommand(s_buildEnv, _testOutput) + .WithWorkingDirectory(_projectDir!) + .ExecuteWithCapturedOutput($"run --no-silent --no-build -c {configuration}"); + + return Task.FromResult(ProcessRunOutput(res)); + } + + private RunResult ProcessRunOutput(CommandResult res) + { + var output = res.Output.Split(Environment.NewLine); + _testOutput.WriteLine($"DEBUG: parsed lines '{String.Join(", ", output)}'"); + + var prefix = "[] TestOutput -> "; + var testOutput = output + .Where(l => l.StartsWith(prefix)) + .Select(l => l.Substring(prefix.Length)) + .ToArray(); + + _testOutput.WriteLine($"DEBUG: testOutput '{String.Join(", ", testOutput)}'"); + return new RunResult(res.ExitCode, testOutput, output); + } + + protected override Task RunForPublish(string configuration) + { + // WasmAppBuilder does publish to the same folder as build (it overrides the output), + // and thus using dotnet run work correctly for publish as well. + CommandResult res = new RunCommand(s_buildEnv, _testOutput) + .WithWorkingDirectory(_projectDir!) + .ExecuteWithCapturedOutput($"run --no-silent --no-build -c {configuration}"); + + return Task.FromResult(ProcessRunOutput(res)); + } +} diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmSdkDebugLevelTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmSdkDebugLevelTests.cs new file mode 100644 index 00000000000000..f88bb3c7239c99 --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/WasmSdkDebugLevelTests.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests.TestAppScenarios; + +public class WasmSdkDebugLevelTests : DebugLevelTestsBase +{ + public WasmSdkDebugLevelTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + protected override void SetupProject(string projectId) => CopyTestAsset("WasmBasicTestApp", projectId); + + protected override Task RunForBuild(string configuration) => RunSdkStyleAppForBuild(new( + Configuration: configuration, + TestScenario: "DebugLevelTest" + )); + + protected override Task RunForPublish(string configuration) => RunSdkStyleAppForPublish(new( + Configuration: configuration, + TestScenario: "DebugLevelTest" + )); + + [Theory] + [InlineData("Debug")] + [InlineData("Release")] + public async Task PublishWithDefaultLevelAndPdbs(string configuration) + { + SetupProject($"DebugLevelTests_PublishWithDefaultLevelAndPdbs_{configuration}"); + PublishProject(configuration, extraArgs: $"-p:CopyOutputSymbolsToPublishDirectory=true"); + + var result = await RunForPublish(configuration); + AssertDebugLevel(result, -1); + } +} diff --git a/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs index a7ae3fba70ed30..1cc70d823ef14e 100644 --- a/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTestBase.cs @@ -21,7 +21,7 @@ protected WasmTemplateTestBase(ITestOutputHelper output, SharedBuildPerTestClass _provider.BundleDirName = "AppBundle"; } - public string CreateWasmTemplateProject(string id, string template = "wasmbrowser", string extraArgs = "", bool runAnalyzers = true) + public string CreateWasmTemplateProject(string id, string template = "wasmbrowser", string extraArgs = "", bool runAnalyzers = true, string? extraProperties = null) { InitPaths(id); InitProjectDir(_projectDir, addNuGetSourceForLocalPackages: true); @@ -42,7 +42,9 @@ public string CreateWasmTemplateProject(string id, string template = "wasmbrowse .EnsureSuccessful(); string projectfile = Path.Combine(_projectDir!, $"{id}.csproj"); - string extraProperties = string.Empty; + if (extraProperties == null) + extraProperties = string.Empty; + extraProperties += "true"; if (runAnalyzers) extraProperties += "true"; diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index a640a128b25044..bc92659fa3479b 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -142,9 +142,6 @@ false - - -1 - true .wasm @@ -210,7 +207,7 @@ + Properties="_WasmInNestedPublish_UniqueProperty_XYZ=true;;WasmBuildingForNestedPublish=true;DeployOnBuild=;_IsPublishing=;_WasmIsPublishing=$(_IsPublishing)"> @@ -420,6 +417,7 @@ <_WasmAppIncludeThreadsWorker Condition="'$(WasmEnableThreads)' == 'true' or '$(MonoWasmBuildVariant)' == 'multithread'">true <_WasmPThreadPoolSize Condition="'$(_WasmPThreadPoolSize)' == ''">-1 + <_WasmIsPublishing Condition="'$(_WasmIsPublishing)' == '' and '$(_IsPublishing)' != ''">$(_IsPublishing) @@ -445,6 +443,7 @@ ExtraConfig="@(WasmExtraConfig)" NativeAssets="@(WasmNativeAsset)" DebugLevel="$(WasmDebugLevel)" + IsPublish="$(_WasmIsPublishing)" IncludeThreadsWorker="$(_WasmAppIncludeThreadsWorker)" PThreadPoolSize="$(_WasmPThreadPoolSize)" UseWebcil="$(WasmEnableWebcil)" diff --git a/src/mono/wasm/runtime/lazyLoading.ts b/src/mono/wasm/runtime/lazyLoading.ts index 55bcfd67101e8c..85058f89819b7c 100644 --- a/src/mono/wasm/runtime/lazyLoading.ts +++ b/src/mono/wasm/runtime/lazyLoading.ts @@ -26,7 +26,7 @@ export async function loadLazyAssembly(assemblyNameToLoad: string): Promise debugBuild=true & debugLevel=-1 => -1 - // - Build (release) => debugBuild=true & debugLevel=0 => 0 - // - Publish (debug) => debugBuild=false & debugLevel=-1 => 0 - // - Publish (release) => debugBuild=false & debugLevel=0 => 0 - config.debugLevel = hasDebuggingEnabled(config) ? config.debugLevel : 0; - if (config.diagnosticTracing === undefined && BuildConfiguration === "Debug") { config.diagnosticTracing = true; } @@ -264,14 +257,13 @@ export async function mono_wasm_load_config(module: DotnetModuleInternal): Promi } } -export function hasDebuggingEnabled(config: MonoConfigInternal): boolean { +export function isDebuggingSupported(): boolean { // Copied from blazor MonoDebugger.ts/attachDebuggerHotkey if (!globalThis.navigator) { return false; } - const hasReferencedPdbs = !!config.resources!.pdb; - return (hasReferencedPdbs || config.debugLevel != 0) && (loaderHelpers.isChromium || loaderHelpers.isFirefox); + return loaderHelpers.isChromium || loaderHelpers.isFirefox; } async function loadBootConfig(module: DotnetModuleInternal): Promise { diff --git a/src/mono/wasm/runtime/loader/globals.ts b/src/mono/wasm/runtime/loader/globals.ts index 88b0d472de3ca8..0bd081ed8a82cc 100644 --- a/src/mono/wasm/runtime/loader/globals.ts +++ b/src/mono/wasm/runtime/loader/globals.ts @@ -12,7 +12,7 @@ import { assertIsControllablePromise, createPromiseController, getPromiseControl import { mono_download_assets, resolve_single_asset_path, retrieve_asset_download } from "./assets"; import { setup_proxy_console } from "./logging"; import { invokeLibraryInitializers } from "./libraryInitializers"; -import { hasDebuggingEnabled } from "./config"; +import { isDebuggingSupported } from "./config"; import { logDownloadStatsToConsole, purgeUnusedCacheEntriesAsync } from "./assetsCache"; export const ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string"; @@ -102,9 +102,9 @@ export function setLoaderGlobals( logDownloadStatsToConsole, purgeUnusedCacheEntriesAsync, - hasDebuggingEnabled, retrieve_asset_download, invokeLibraryInitializers, + isDebuggingSupported, // from wasm-feature-detect npm package exceptions, diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 324bdc78b0435d..6efa969a953a82 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -548,7 +548,7 @@ async function mono_wasm_before_memory_snapshot() { if (runtimeHelpers.config.browserProfilerOptions) mono_wasm_init_browser_profiler(runtimeHelpers.config.browserProfilerOptions); - mono_wasm_load_runtime("unused", runtimeHelpers.config.debugLevel); + mono_wasm_load_runtime(); // we didn't have snapshot yet and the feature is enabled. Take snapshot now. if (runtimeHelpers.config.startupMemoryCache) { @@ -561,17 +561,21 @@ async function mono_wasm_before_memory_snapshot() { endMeasure(mark, MeasuredBlock.memorySnapshot); } -export function mono_wasm_load_runtime(unused?: string, debugLevel?: number): void { +export function mono_wasm_load_runtime(): void { mono_log_debug("mono_wasm_load_runtime"); try { const mark = startMeasure(); + let debugLevel = runtimeHelpers.config.debugLevel; if (debugLevel == undefined) { debugLevel = 0; if (runtimeHelpers.config.debugLevel) { debugLevel = 0 + debugLevel; } } - cwraps.mono_wasm_load_runtime(unused || "unused", debugLevel); + if (!loaderHelpers.isDebuggingSupported() || !runtimeHelpers.config.resources!.pdb) { + debugLevel = 0; + } + cwraps.mono_wasm_load_runtime("unused", debugLevel); endMeasure(mark, MeasuredBlock.loadRuntime); } catch (err: any) { diff --git a/src/mono/wasm/runtime/types/internal.ts b/src/mono/wasm/runtime/types/internal.ts index c43443a48da6bc..c0b14b72abe5da 100644 --- a/src/mono/wasm/runtime/types/internal.ts +++ b/src/mono/wasm/runtime/types/internal.ts @@ -145,7 +145,6 @@ export type LoaderHelpers = { out(message: string): void; err(message: string): void; - hasDebuggingEnabled(config: MonoConfig): boolean, retrieve_asset_download(asset: AssetEntry): Promise; onDownloadResourceProgress?: (resourcesLoaded: number, totalResources: number) => void; logDownloadStatsToConsole: () => void; @@ -155,6 +154,7 @@ export type LoaderHelpers = { invokeLibraryInitializers: (functionName: string, args: any[]) => Promise, libraryInitializers?: { scriptName: string, exports: any }[]; + isDebuggingSupported(): boolean, isChromium: boolean, isFirefox: boolean diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/LazyLoadingTest.cs b/src/mono/wasm/testassets/WasmBasicTestApp/App/LazyLoadingTest.cs index 5aba6b1ee48a0f..797956d23f8e4d 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/LazyLoadingTest.cs +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/LazyLoadingTest.cs @@ -4,6 +4,7 @@ using Library; using System; using System.Text.Json; +using System.Text.Json.Serialization; using System.Runtime.InteropServices.JavaScript; public partial class LazyLoadingTest @@ -13,7 +14,12 @@ public static void Run() { // System.Text.Json is marked as lazy loaded in the csproj ("BlazorWebAssemblyLazyLoad"), this method can be called only after the assembly is lazy loaded // In the test case it is done in the JS before call to this method - var text = JsonSerializer.Serialize(new Person("John", "Doe")); + var text = JsonSerializer.Serialize(new Person("John", "Doe"), PersonJsonSerializerContext.Default.Person); TestOutput.WriteLine(text); } } + +[JsonSerializable(typeof(Person))] +public partial class PersonJsonSerializerContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index 8e3485bc2e67cd..5092e4cb945b75 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -33,6 +33,8 @@ switch (testCase) { Math.floor(Math.random() * 5) + 5, Math.floor(Math.random() * 5) + 10 ]; + console.log(`Failing test at assembly indexes [${failAtAssemblyNumbers.join(", ")}]`); + let alreadyFailed = []; dotnet.withDiagnosticTracing(true).withResourceLoader((type, name, defaultUri, integrity, behavior) => { if (type === "dotnetjs") { // loadBootResource could return string with unqualified name of resource. @@ -45,9 +47,10 @@ switch (testCase) { } assemblyCounter++; - if (!failAtAssemblyNumbers.includes(assemblyCounter)) + if (!failAtAssemblyNumbers.includes(assemblyCounter) || alreadyFailed.includes(defaultUri)) return defaultUri; + alreadyFailed.push(defaultUri); testOutput("Throw error instead of downloading resource"); const error = new Error("Simulating a failed fetch"); error.silent = true; @@ -106,6 +109,10 @@ try { case "DownloadResourceProgressTest": exit(0); break; + case "DebugLevelTest": + testOutput("WasmDebugLevel: " + config.debugLevel); + exit(0); + break; case "OutErrOverrideWorks": dotnet.run(); break; diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs index 07c05113ac843f..748283e74e673b 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs @@ -10,7 +10,7 @@ namespace Microsoft.NET.Sdk.WebAssembly { - public class BootJsonBuilderHelper(TaskLoggingHelper Log) + public class BootJsonBuilderHelper(TaskLoggingHelper Log, string DebugLevel, bool IsPublish) { public void ComputeResourcesHash(BootJsonData bootConfig) { @@ -73,5 +73,35 @@ static void AddDictionary(StringBuilder sb, Dictionary? res) return null; } + + public int GetDebugLevel(bool hasPdb) + { + int? debugLevel = ParseOptionalInt(DebugLevel); + + // If user didn't give us a value, check if we have any PDB. + if (debugLevel == null && hasPdb) + debugLevel = -1; + + // Fallback to -1 for build, or 0 for publish + debugLevel ??= IsPublish ? 0 : -1; + + return debugLevel.Value; + } + + public bool? ParseOptionalBool(string value) + { + if (string.IsNullOrEmpty(value) || !bool.TryParse(value, out var boolValue)) + return null; + + return boolValue; + } + + public int? ParseOptionalInt(string value) + { + if (string.IsNullOrEmpty(value) || !int.TryParse(value, out var intValue)) + return null; + + return intValue; + } } } diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs index ef42b6fa952f10..3f12c54e07f9c5 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs @@ -71,6 +71,8 @@ public class GenerateWasmBootJson : Task public ITaskItem[] LazyLoadedAssemblies { get; set; } + public bool IsPublish { get; set; } + public override bool Execute() { using var fileStream = File.Create(OutputPath); @@ -91,17 +93,16 @@ public override bool Execute() // Internal for tests public void WriteBootJson(Stream output, string entryAssemblyName) { - var helper = new BootJsonBuilderHelper(Log); + var helper = new BootJsonBuilderHelper(Log, DebugLevel, IsPublish); var result = new BootJsonData { resources = new ResourcesData(), - startupMemoryCache = ParseOptionalBool(StartupMemoryCache), + startupMemoryCache = helper.ParseOptionalBool(StartupMemoryCache), }; if (IsTargeting80OrLater()) { - result.debugLevel = ParseOptionalInt(DebugLevel) ?? (DebugBuild ? 1 : 0); result.mainAssemblyName = entryAssemblyName; result.globalizationMode = GetGlobalizationMode().ToString().ToLowerInvariant(); @@ -127,7 +128,7 @@ public void WriteBootJson(Stream output, string entryAssemblyName) result.runtimeOptions = runtimeOptions; } - bool? jiterpreter = ParseOptionalBool(Jiterpreter); + bool? jiterpreter = helper.ParseOptionalBool(Jiterpreter); if (jiterpreter != null) { var runtimeOptions = result.runtimeOptions?.ToHashSet() ?? new HashSet(3); @@ -329,6 +330,20 @@ public void WriteBootJson(Stream output, string entryAssemblyName) } } + if (IsTargeting80OrLater()) + { + int? debugLevel = helper.ParseOptionalInt(DebugLevel); + + // If user didn't give us a value, check if we have any PDB. + if (debugLevel == null && result.resources?.pdb?.Count > 0) + debugLevel = -1; + + // Fallback to -1 for build, or 0 for publish + debugLevel ??= IsPublish ? 0 : -1; + + result.debugLevel = debugLevel.Value; + } + if (ConfigurationFiles != null) { foreach (var configFile in ConfigurationFiles) @@ -394,22 +409,6 @@ private GlobalizationMode GetGlobalizationMode() return GlobalizationMode.Sharded; } - private static bool? ParseOptionalBool(string value) - { - if (string.IsNullOrEmpty(value) || !bool.TryParse(value, out var boolValue)) - return null; - - return boolValue; - } - - private static int? ParseOptionalInt(string value) - { - if (string.IsNullOrEmpty(value) || !int.TryParse(value, out var intValue)) - return null; - - return intValue; - } - private void AddToAdditionalResources(ITaskItem resource, Dictionary additionalResources, string resourceName, string behavior) { if (!additionalResources.ContainsKey(resourceName)) diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 9c7d8a6799134e..d5e2aa53e14862 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -27,6 +27,8 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask public bool WasmIncludeFullIcuData { get; set; } public string? WasmIcuDataFileName { get; set; } public string? RuntimeAssetsLocation { get; set; } + public string? DebugLevel { get; set; } + public bool IsPublish { get; set; } // // Extra json elements to add to _framework/blazor.boot.json @@ -83,7 +85,7 @@ private GlobalizationMode GetGlobalizationMode() protected override bool ExecuteInternal() { - var helper = new BootJsonBuilderHelper(Log); + var helper = new BootJsonBuilderHelper(Log, DebugLevel!, IsPublish); if (!ValidateArguments()) return false; @@ -115,6 +117,8 @@ protected override bool ExecuteInternal() if (UseWebcil) Log.LogMessage(MessageImportance.Normal, "Converting assemblies to Webcil"); + int baseDebugLevel = helper.GetDebugLevel(false); + foreach (var assembly in _assemblies) { if (UseWebcil) @@ -133,7 +137,7 @@ protected override bool ExecuteInternal() { FileCopyChecked(assembly, Path.Combine(runtimeAssetsPath, Path.GetFileName(assembly)), "Assemblies"); } - if (DebugLevel != 0) + if (baseDebugLevel != 0) { var pdb = assembly; pdb = Path.ChangeExtension(pdb, ".pdb"); @@ -191,7 +195,7 @@ protected override bool ExecuteInternal() } bootConfig.resources.assembly[Path.GetFileName(assemblyPath)] = Utils.ComputeIntegrity(bytes); - if (DebugLevel != 0) + if (baseDebugLevel != 0) { var pdb = Path.ChangeExtension(assembly, ".pdb"); if (File.Exists(pdb)) @@ -205,7 +209,7 @@ protected override bool ExecuteInternal() } } - bootConfig.debugLevel = DebugLevel; + bootConfig.debugLevel = helper.GetDebugLevel(bootConfig.resources.pdb?.Count > 0); ProcessSatelliteAssemblies(args => { diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs b/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs index c533e404c41739..feee565ad4f6cc 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs @@ -36,7 +36,6 @@ public abstract class WasmAppBuilderBaseTask : Task // https://github.com/dotnet/icu/tree/maint/maint-67/icu-filters public string[] IcuDataFileNames { get; set; } = Array.Empty(); - public int DebugLevel { get; set; } public ITaskItem[] SatelliteAssemblies { get; set; } = Array.Empty(); public bool HybridGlobalization { get; set; } public bool InvariantGlobalization { get; set; } diff --git a/src/tasks/WasmAppBuilder/wasi/WasiAppBuilder.cs b/src/tasks/WasmAppBuilder/wasi/WasiAppBuilder.cs index 0974904e6627bd..521284dd56c779 100644 --- a/src/tasks/WasmAppBuilder/wasi/WasiAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/wasi/WasiAppBuilder.cs @@ -11,6 +11,7 @@ namespace Microsoft.WebAssembly.Build.Tasks; public class WasiAppBuilder : WasmAppBuilderBaseTask { public bool IsSingleFileBundle { get; set; } + public bool OutputSymbolsToAppBundle { get; set; } protected override bool ValidateArguments() { @@ -53,7 +54,7 @@ protected override bool ExecuteInternal() { FileCopyChecked(assembly, Path.Combine(asmRootPath, Path.GetFileName(assembly)), "Assemblies"); - if (DebugLevel != 0) + if (OutputSymbolsToAppBundle) { string pdb = Path.ChangeExtension(assembly, ".pdb"); if (File.Exists(pdb))