Skip to content

Commit f190f34

Browse files
authored
[browser] Fix debugger support in WasmAppBuilder (#100675)
1 parent 59f2833 commit f190f34

File tree

10 files changed

+250
-146
lines changed

10 files changed

+250
-146
lines changed

eng/testing/scenarios/BuildWasmAppsJobsList.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,5 @@ Wasm.Build.Tests.WasmSIMDTests
4646
Wasm.Build.Tests.WasmTemplateTests
4747
Wasm.Build.Tests.WorkloadTests
4848
Wasm.Build.Tests.MT.Blazor.SimpleMultiThreadedTests
49-
Wasm.Build.Tests.TestAppScenarios.DebugLevelTests
49+
Wasm.Build.Tests.TestAppScenarios.WasmSdkDebugLevelTests
50+
Wasm.Build.Tests.TestAppScenarios.WasmAppBuilderDebugLevelTests

src/mono/browser/build/BrowserWasmApp.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
<!-- TODO: set this from some user-facing property? -1 means use the default baked into dotnet.native.js -->
128128
<_WasmPThreadPoolInitialSize Condition="'$(_WasmPThreadPoolInitialSize)' == ''">-1</_WasmPThreadPoolInitialSize>
129129
<_WasmPThreadPoolUnusedSize Condition="'$(_WasmPThreadPoolUnusedSize)' == ''">-1</_WasmPThreadPoolUnusedSize>
130+
<_WasmIsPublishing Condition="'$(_WasmIsPublishing)' == '' and '$(_IsPublishing)' != ''">$(_IsPublishing)</_WasmIsPublishing>
130131
</PropertyGroup>
131132

132133
<ItemGroup>
@@ -152,6 +153,7 @@
152153
ExtraConfig="@(WasmExtraConfig)"
153154
NativeAssets="@(WasmNativeAsset)"
154155
DebugLevel="$(WasmDebugLevel)"
156+
IsPublish="$(_WasmIsPublishing)"
155157
IncludeThreadsWorker="$(WasmEnableThreads)"
156158
PThreadPoolInitialSize="$(_WasmPThreadPoolInitialSize)"
157159
PThreadPoolUnusedSize="$(_WasmPThreadPoolUnusedSize)"

src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DebugLevelTests.cs

Lines changed: 0 additions & 109 deletions
This file was deleted.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Threading.Tasks;
9+
using Xunit;
10+
using Xunit.Abstractions;
11+
12+
#nullable enable
13+
14+
namespace Wasm.Build.Tests.TestAppScenarios;
15+
16+
public abstract class DebugLevelTestsBase : AppTestBase
17+
{
18+
public DebugLevelTestsBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
19+
: base(output, buildContext)
20+
{
21+
}
22+
23+
protected void AssertDebugLevel(RunResult result, int value)
24+
{
25+
Assert.Collection(
26+
result.TestOutput,
27+
m => Assert.Equal($"WasmDebugLevel: {value}", m)
28+
);
29+
}
30+
31+
protected abstract void SetupProject(string projectId);
32+
protected abstract Task<RunResult> RunForBuild(string configuration);
33+
protected abstract Task<RunResult> RunForPublish(string configuration);
34+
35+
[Theory]
36+
[InlineData("Debug")]
37+
[InlineData("Release")]
38+
public async Task BuildWithDefaultLevel(string configuration)
39+
{
40+
SetupProject($"DebugLevelTests_BuildWithDefaultLevel_{configuration}");
41+
BuildProject(configuration, assertAppBundle: false);
42+
43+
var result = await RunForBuild(configuration);
44+
AssertDebugLevel(result, -1);
45+
}
46+
47+
[Theory]
48+
[InlineData("Debug", 1)]
49+
[InlineData("Release", 1)]
50+
[InlineData("Debug", 0)]
51+
[InlineData("Release", 0)]
52+
public async Task BuildWithExplicitValue(string configuration, int debugLevel)
53+
{
54+
SetupProject($"DebugLevelTests_BuildWithExplicitValue_{configuration}");
55+
BuildProject(configuration, assertAppBundle: false, extraArgs: $"-p:WasmDebugLevel={debugLevel}");
56+
57+
var result = await RunForBuild(configuration);
58+
AssertDebugLevel(result, debugLevel);
59+
}
60+
61+
[Theory]
62+
[InlineData("Debug")]
63+
[InlineData("Release")]
64+
public async Task PublishWithDefaultLevel(string configuration)
65+
{
66+
SetupProject($"DebugLevelTests_PublishWithDefaultLevel_{configuration}");
67+
PublishProject(configuration, assertAppBundle: false);
68+
69+
var result = await RunForPublish(configuration);
70+
AssertDebugLevel(result, 0);
71+
}
72+
73+
[Theory]
74+
[InlineData("Debug", 1)]
75+
[InlineData("Release", 1)]
76+
[InlineData("Debug", -1)]
77+
[InlineData("Release", -1)]
78+
public async Task PublishWithExplicitValue(string configuration, int debugLevel)
79+
{
80+
SetupProject($"DebugLevelTests_PublishWithExplicitValue_{configuration}");
81+
PublishProject(configuration, assertAppBundle: false, extraArgs: $"-p:WasmDebugLevel={debugLevel}");
82+
83+
var result = await RunForPublish(configuration);
84+
AssertDebugLevel(result, debugLevel);
85+
}
86+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Threading.Tasks;
9+
using Xunit;
10+
using Xunit.Abstractions;
11+
12+
#nullable enable
13+
14+
namespace Wasm.Build.Tests.TestAppScenarios;
15+
16+
public class WasmAppBuilderDebugLevelTests : DebugLevelTestsBase
17+
{
18+
public WasmAppBuilderDebugLevelTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
19+
: base(output, buildContext)
20+
{
21+
}
22+
23+
protected override void SetupProject(string projectId)
24+
{
25+
Id = $"{projectId}_{GetRandomId()}";
26+
string projectfile = CreateWasmTemplateProject(Id, "wasmconsole");
27+
string projectDir = Path.GetDirectoryName(projectfile)!;
28+
string mainJs = Path.Combine(projectDir, "main.mjs");
29+
string mainJsContent = File.ReadAllText(mainJs);
30+
mainJsContent = mainJsContent
31+
.Replace("import { dotnet }", "import { dotnet, exit }")
32+
.Replace("await runMainAndExit()", "console.log('TestOutput -> WasmDebugLevel: ' + config.debugLevel); exit(0)");
33+
File.WriteAllText(mainJs, mainJsContent);
34+
}
35+
36+
protected override Task<RunResult> RunForBuild(string configuration)
37+
{
38+
CommandResult res = new RunCommand(s_buildEnv, _testOutput)
39+
.WithWorkingDirectory(_projectDir!)
40+
.ExecuteWithCapturedOutput($"run --no-silent --no-build -c {configuration}");
41+
42+
return Task.FromResult(ProcessRunOutput(res));
43+
}
44+
45+
private RunResult ProcessRunOutput(CommandResult res)
46+
{
47+
var output = res.Output.Split(Environment.NewLine);
48+
_testOutput.WriteLine($"DEBUG: parsed lines '{String.Join(", ", output)}'");
49+
50+
var prefix = "[] TestOutput -> ";
51+
var testOutput = output
52+
.Where(l => l.StartsWith(prefix))
53+
.Select(l => l.Substring(prefix.Length))
54+
.ToArray();
55+
56+
_testOutput.WriteLine($"DEBUG: testOutput '{String.Join(", ", testOutput)}'");
57+
return new RunResult(res.ExitCode, testOutput, output, []);
58+
}
59+
60+
protected override Task<RunResult> RunForPublish(string configuration)
61+
{
62+
// WasmAppBuilder does publish to the same folder as build (it overrides the output),
63+
// and thus using dotnet run work correctly for publish as well.
64+
CommandResult res = new RunCommand(s_buildEnv, _testOutput)
65+
.WithWorkingDirectory(_projectDir!)
66+
.ExecuteWithCapturedOutput($"run --no-silent --no-build -c {configuration}");
67+
68+
return Task.FromResult(ProcessRunOutput(res));
69+
}
70+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Threading.Tasks;
9+
using Xunit;
10+
using Xunit.Abstractions;
11+
12+
#nullable enable
13+
14+
namespace Wasm.Build.Tests.TestAppScenarios;
15+
16+
public class WasmSdkDebugLevelTests : DebugLevelTestsBase
17+
{
18+
public WasmSdkDebugLevelTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
19+
: base(output, buildContext)
20+
{
21+
}
22+
23+
protected override void SetupProject(string projectId) => CopyTestAsset("WasmBasicTestApp", projectId, "App");
24+
25+
protected override Task<RunResult> RunForBuild(string configuration) => RunSdkStyleAppForBuild(new(
26+
Configuration: configuration,
27+
TestScenario: "DebugLevelTest"
28+
));
29+
30+
protected override Task<RunResult> RunForPublish(string configuration) => RunSdkStyleAppForPublish(new(
31+
Configuration: configuration,
32+
TestScenario: "DebugLevelTest"
33+
));
34+
35+
[Theory]
36+
[InlineData("Debug")]
37+
[InlineData("Release")]
38+
public async Task PublishWithDefaultLevelAndPdbs(string configuration)
39+
{
40+
SetupProject($"DebugLevelTests_PublishWithDefaultLevelAndPdbs_{configuration}");
41+
PublishProject(configuration, assertAppBundle: false, extraArgs: $"-p:CopyOutputSymbolsToPublishDirectory=true");
42+
43+
var result = await RunForPublish(configuration);
44+
AssertDebugLevel(result, -1);
45+
}
46+
}

src/mono/wasm/build/WasmApp.Common.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@
446446
<!-- Use a unique property, so the already run wasm targets can also run -->
447447
<MSBuild Projects="$(MSBuildProjectFile)"
448448
Targets="WasmNestedPublishApp"
449-
Properties="_WasmInNestedPublish_UniqueProperty_XYZ=true;;WasmBuildingForNestedPublish=true;DeployOnBuild=;_IsPublishing=">
449+
Properties="_WasmInNestedPublish_UniqueProperty_XYZ=true;;WasmBuildingForNestedPublish=true;DeployOnBuild=;_IsPublishing=;_WasmIsPublishing=$(_IsPublishing)">
450450
<Output TaskParameter="TargetOutputs" ItemName="WasmNestedPublishAppResultItems" />
451451
</MSBuild>
452452

src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonBuilderHelper.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace Microsoft.NET.Sdk.WebAssembly
1212
{
13-
public class BootJsonBuilderHelper(TaskLoggingHelper Log, bool IsMultiThreaded)
13+
public class BootJsonBuilderHelper(TaskLoggingHelper Log, string DebugLevel, bool IsMultiThreaded, bool IsPublish)
1414
{
1515
private static readonly string[] coreAssemblyNames = [
1616
"System.Private.CoreLib",
@@ -106,5 +106,35 @@ static void AddDictionary(StringBuilder sb, Dictionary<string, string>? res)
106106

107107
return null;
108108
}
109+
110+
public int GetDebugLevel(bool hasPdb)
111+
{
112+
int? debugLevel = ParseOptionalInt(DebugLevel);
113+
114+
// If user didn't give us a value, check if we have any PDB.
115+
if (debugLevel == null && hasPdb)
116+
debugLevel = -1;
117+
118+
// Fallback to -1 for build, or 0 for publish
119+
debugLevel ??= IsPublish ? 0 : -1;
120+
121+
return debugLevel.Value;
122+
}
123+
124+
public bool? ParseOptionalBool(string value)
125+
{
126+
if (string.IsNullOrEmpty(value) || !bool.TryParse(value, out var boolValue))
127+
return null;
128+
129+
return boolValue;
130+
}
131+
132+
public int? ParseOptionalInt(string value)
133+
{
134+
if (string.IsNullOrEmpty(value) || !int.TryParse(value, out var intValue))
135+
return null;
136+
137+
return intValue;
138+
}
109139
}
110140
}

0 commit comments

Comments
 (0)