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

[browser] Fix debugger support in WasmAppBuilder #100675

Merged
merged 18 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion eng/testing/scenarios/BuildWasmAppsJobsList.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ Wasm.Build.Tests.WasmSIMDTests
Wasm.Build.Tests.WasmTemplateTests
Wasm.Build.Tests.WorkloadTests
Wasm.Build.Tests.MT.Blazor.SimpleMultiThreadedTests
Wasm.Build.Tests.TestAppScenarios.DebugLevelTests
Wasm.Build.Tests.TestAppScenarios.WasmSdkDebugLevelTests
Wasm.Build.Tests.TestAppScenarios.WasmAppBuilderDebugLevelTests
2 changes: 2 additions & 0 deletions src/mono/browser/build/BrowserWasmApp.targets
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
<!-- TODO: set this from some user-facing property? -1 means use the default baked into dotnet.native.js -->
<_WasmPThreadPoolInitialSize Condition="'$(_WasmPThreadPoolInitialSize)' == ''">-1</_WasmPThreadPoolInitialSize>
<_WasmPThreadPoolUnusedSize Condition="'$(_WasmPThreadPoolUnusedSize)' == ''">-1</_WasmPThreadPoolUnusedSize>
<_WasmIsPublishing Condition="'$(_WasmIsPublishing)' == '' and '$(_IsPublishing)' != ''">$(_IsPublishing)</_WasmIsPublishing>
maraf marked this conversation as resolved.
Show resolved Hide resolved
</PropertyGroup>

<ItemGroup>
Expand All @@ -152,6 +153,7 @@
ExtraConfig="@(WasmExtraConfig)"
NativeAssets="@(WasmNativeAsset)"
DebugLevel="$(WasmDebugLevel)"
IsPublish="$(_WasmIsPublishing)"
IncludeThreadsWorker="$(WasmEnableThreads)"
PThreadPoolInitialSize="$(_WasmPThreadPoolInitialSize)"
PThreadPoolUnusedSize="$(_WasmPThreadPoolUnusedSize)"
Expand Down
109 changes: 0 additions & 109 deletions src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTests.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<RunResult> RunForBuild(string configuration);
protected abstract Task<RunResult> 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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");
string projectDir = Path.GetDirectoryName(projectfile)!;
string mainJs = Path.Combine(projectDir, "main.mjs");
string mainJsContent = File.ReadAllText(mainJs);
mainJsContent = mainJsContent
.Replace("import { dotnet }", "import { dotnet, exit }")
.Replace("await runMainAndExit()", "console.log('TestOutput -> WasmDebugLevel: ' + config.debugLevel); exit(0)");
File.WriteAllText(mainJs, mainJsContent);
}

protected override Task<RunResult> 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<RunResult> 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));
}
}
Original file line number Diff line number Diff line change
@@ -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, "App");

protected override Task<RunResult> RunForBuild(string configuration) => RunSdkStyleAppForBuild(new(
Configuration: configuration,
TestScenario: "DebugLevelTest"
));

protected override Task<RunResult> 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, assertAppBundle: false, extraArgs: $"-p:CopyOutputSymbolsToPublishDirectory=true");

var result = await RunForPublish(configuration);
AssertDebugLevel(result, -1);
}
}
2 changes: 1 addition & 1 deletion src/mono/wasm/build/WasmApp.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@
<!-- Use a unique property, so the already run wasm targets can also run -->
<MSBuild Projects="$(MSBuildProjectFile)"
Targets="WasmNestedPublishApp"
Properties="_WasmInNestedPublish_UniqueProperty_XYZ=true;;WasmBuildingForNestedPublish=true;DeployOnBuild=;_IsPublishing=">
Properties="_WasmInNestedPublish_UniqueProperty_XYZ=true;;WasmBuildingForNestedPublish=true;DeployOnBuild=;_IsPublishing=;_WasmIsPublishing=$(_IsPublishing)">
<Output TaskParameter="TargetOutputs" ItemName="WasmNestedPublishAppResultItems" />
</MSBuild>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Microsoft.NET.Sdk.WebAssembly
{
public class BootJsonBuilderHelper(TaskLoggingHelper Log, bool IsMultiThreaded)
public class BootJsonBuilderHelper(TaskLoggingHelper Log, string DebugLevel, bool IsMultiThreaded, bool IsPublish)
{
private static readonly string[] coreAssemblyNames = [
"System.Private.CoreLib",
Expand Down Expand Up @@ -106,5 +106,35 @@ static void AddDictionary(StringBuilder sb, Dictionary<string, string>? 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;
}
}
}
Loading
Loading