Skip to content

Commit 4d8bebc

Browse files
authored
[wasm] Backport build improvements, and fixes from #61581 (#62757)
* [wasm] Disable native strip for tests * [wasm] improve fixup of symbol names * [wasm] Extract BuildProject params to BuildProjectOptions record * [wasm] Add WasmBuild.sln * [wasm] Add `@(NativeFileReference)` to up-to-date check items for VS VS does its own tracking of inputs/outputs too, and needs to be told about `@(NativeFileReference)` items, so a build can get triggered with F5 when a native file changes. Based on https://github.com/dotnet/project-system/blob/main/docs/up-to-date-check.md?rgh-link-date=2021-10-26T13%3A23%3A47Z#up-to-date-check Fixes #60862 * MonoAOTCompiler: log failed output as error * [wasm] Improve error message when runtime pack cannot be found * [wasm] PInvokeTableGenerator: don't fail on errors in reading custom attributes Inspired by https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1364890?src=WorkItemMention&src-action=artifact_link Works around a NRE in CustomAttributeData.AttributeType because it's ConstructorInfo is null. The mono side issue: mono/mono#15340 * replace '.', '-', and '+' with '_' instead of their code
1 parent a9723f3 commit 4d8bebc

20 files changed

Lines changed: 320 additions & 150 deletions

eng/testing/tests.wasm.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
<!-- Some tests expect to load satellite assemblies by path, eg. System.Runtime.Loader.Tests,
99
so, just setting it true by default -->
1010
<IncludeSatelliteAssembliesInVFS Condition="'$(IncludeSatelliteAssembliesInVFS)' == ''">true</IncludeSatelliteAssembliesInVFS>
11+
12+
<WasmNativeStrip>false</WasmNativeStrip>
1113
</PropertyGroup>
1214

1315
<PropertyGroup>

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
<WasmUseEMSDK_PATH Condition="'$(WasmUseEMSDK_PATH)' == '' and '$(EMSDK_PATH)' != '' and Exists('$(MSBuildThisFileDirectory)WasmApp.InTree.targets')">true</WasmUseEMSDK_PATH>
2828
</PropertyGroup>
2929

30+
<ItemGroup>
31+
<UpToDateCheckInput Include="@(NativeFileReference)" />
32+
</ItemGroup>
33+
3034
<ItemGroup Condition="'$(Configuration)' == 'Debug' and '@(_MonoComponent->Count())' == 0">
3135
<_MonoComponent Include="hot_reload;debugger" />
3236
</ItemGroup>

src/mono/wasm/build/WasmApp.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159

160160
<Target Name="_InitializeCommonProperties">
161161
<Error Condition="'$(MicrosoftNetCoreAppRuntimePackDir)' == '' and ('%(ResolvedRuntimePack.PackageDirectory)' == '' or !Exists(%(ResolvedRuntimePack.PackageDirectory)))"
162-
Text="Could not find %25(ResolvedRuntimePack.PackageDirectory)=%(ResolvedRuntimePack.PackageDirectory)" />
162+
Text="%24(MicrosoftNetCoreAppRuntimePackDir)='', and cannot find %25(ResolvedRuntimePack.PackageDirectory)=%(ResolvedRuntimePack.PackageDirectory). One of these need to be set to a valid path" />
163163
<Error Condition="'$(IntermediateOutputPath)' == ''" Text="%24(IntermediateOutputPath) property needs to be set" />
164164

165165
<PropertyGroup>

src/mono/wasm/sln/WasmBuild.sln

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31722.452
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WasmBuildTasks", "..\..\..\tasks\WasmBuildTasks\WasmBuildTasks.csproj", "{D5BD9C0C-8A05-493E-BE45-13AF8286CD92}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WasmAppBuilder", "..\..\..\tasks\WasmAppBuilder\WasmAppBuilder.csproj", "{8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}"
9+
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoAOTCompiler", "..\..\..\tasks\AotCompilerTask\MonoAOTCompiler.csproj", "{A9C02284-0387-42E7-BF78-47DF13656D5E}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Build.Tests", "..\..\..\tests\BuildWasmApps\Wasm.Build.Tests\Wasm.Build.Tests.csproj", "{94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}"
13+
EndProject
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DebuggerTestSuite", "..\debugger\DebuggerTestSuite\DebuggerTestSuite.csproj", "{4C0EE027-FC30-4167-B2CF-A6D18F00E08F}"
15+
EndProject
16+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserDebugHost", "..\debugger\BrowserDebugHost\BrowserDebugHost.csproj", "{292A88FD-795F-467A-8801-B5B791CEF96E}"
17+
EndProject
18+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserDebugProxy", "..\debugger\BrowserDebugProxy\BrowserDebugProxy.csproj", "{F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}"
19+
EndProject
20+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WasmAppHost", "..\host\WasmAppHost.csproj", "{C7099764-EC2E-4FAF-9057-0321893DE4F8}"
21+
EndProject
22+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplyUpdateReferencedAssembly", "..\debugger\tests\ApplyUpdateReferencedAssembly\ApplyUpdateReferencedAssembly.csproj", "{75477B6F-DC8E-4002-88B8-017C992C568E}"
23+
EndProject
24+
Global
25+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
26+
Debug|Any CPU = Debug|Any CPU
27+
Release|Any CPU = Release|Any CPU
28+
EndGlobalSection
29+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
30+
{D5BD9C0C-8A05-493E-BE45-13AF8286CD92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31+
{D5BD9C0C-8A05-493E-BE45-13AF8286CD92}.Debug|Any CPU.Build.0 = Debug|Any CPU
32+
{D5BD9C0C-8A05-493E-BE45-13AF8286CD92}.Release|Any CPU.ActiveCfg = Release|Any CPU
33+
{D5BD9C0C-8A05-493E-BE45-13AF8286CD92}.Release|Any CPU.Build.0 = Release|Any CPU
34+
{8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35+
{8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}.Debug|Any CPU.Build.0 = Debug|Any CPU
36+
{8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}.Release|Any CPU.ActiveCfg = Release|Any CPU
37+
{8DEBFDE2-B127-46D7-92CF-EEA6D1DA2554}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{A9C02284-0387-42E7-BF78-47DF13656D5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{A9C02284-0387-42E7-BF78-47DF13656D5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{A9C02284-0387-42E7-BF78-47DF13656D5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{A9C02284-0387-42E7-BF78-47DF13656D5E}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43+
{94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
44+
{94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
45+
{94E18644-B0E5-4DBB-9CE4-EA1515ACE4C2}.Release|Any CPU.Build.0 = Release|Any CPU
46+
{4C0EE027-FC30-4167-B2CF-A6D18F00E08F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{4C0EE027-FC30-4167-B2CF-A6D18F00E08F}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{4C0EE027-FC30-4167-B2CF-A6D18F00E08F}.Release|Any CPU.ActiveCfg = Release|Any CPU
49+
{4C0EE027-FC30-4167-B2CF-A6D18F00E08F}.Release|Any CPU.Build.0 = Release|Any CPU
50+
{292A88FD-795F-467A-8801-B5B791CEF96E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51+
{292A88FD-795F-467A-8801-B5B791CEF96E}.Debug|Any CPU.Build.0 = Debug|Any CPU
52+
{292A88FD-795F-467A-8801-B5B791CEF96E}.Release|Any CPU.ActiveCfg = Release|Any CPU
53+
{292A88FD-795F-467A-8801-B5B791CEF96E}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55+
{F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
56+
{F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
57+
{F5AE2AF5-3C30-45E3-A0C6-D962C51FE5E7}.Release|Any CPU.Build.0 = Release|Any CPU
58+
{75477B6F-DC8E-4002-88B8-017C992C568E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
59+
{75477B6F-DC8E-4002-88B8-017C992C568E}.Debug|Any CPU.Build.0 = Debug|Any CPU
60+
{75477B6F-DC8E-4002-88B8-017C992C568E}.Release|Any CPU.ActiveCfg = Release|Any CPU
61+
{75477B6F-DC8E-4002-88B8-017C992C568E}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{C7099764-EC2E-4FAF-9057-0321893DE4F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63+
{C7099764-EC2E-4FAF-9057-0321893DE4F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
64+
{C7099764-EC2E-4FAF-9057-0321893DE4F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
65+
{C7099764-EC2E-4FAF-9057-0321893DE4F8}.Release|Any CPU.Build.0 = Release|Any CPU
66+
EndGlobalSection
67+
GlobalSection(SolutionProperties) = preSolution
68+
HideSolutionNode = FALSE
69+
EndGlobalSection
70+
GlobalSection(ExtensibilityGlobals) = postSolution
71+
SolutionGuid = {2BDE8FDE-4261-4B4D-8B54-ACC88B06C8D1}
72+
EndGlobalSection
73+
EndGlobal

src/mono/wasm/wasm.proj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<EmccCmd>emcc</EmccCmd>
1313
<WasmObjDir>$(ArtifactsObjDir)wasm</WasmObjDir>
1414
<_EmccDefaultsRspPath>$(NativeBinDir)src\emcc-default.rsp</_EmccDefaultsRspPath>
15+
<WasmNativeStrip Condition="'$(ContinuousIntegrationBuild)' == 'true'">false</WasmNativeStrip>
1516
</PropertyGroup>
1617

1718
<Target Name="CheckEnv">

src/tasks/AotCompilerTask/MonoAOTCompiler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -744,13 +744,13 @@ private bool PrecompileLibrary(PrecompileArguments args)
744744
Log.LogMessage(importance, $"{msgPrefix}Exec (with response file contents expanded) in {args.WorkingDir}: {envStr}{CompilerBinaryPath} {File.ReadAllText(args.ResponseFilePath)}");
745745
}
746746

747-
Log.LogMessage(importance, output);
748-
749747
if (exitCode != 0)
750748
{
751-
Log.LogError($"Precompiling failed for {assembly}");
749+
Log.LogError($"Precompiling failed for {assembly}.{Environment.NewLine}{output}");
752750
return false;
753751
}
752+
753+
Log.LogMessage(importance, output);
754754
}
755755
catch (Exception ex)
756756
{

src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class PInvokeTableGenerator : Task
2424
[Output]
2525
public string FileWrites { get; private set; } = string.Empty;
2626

27-
private static char[] s_charsToReplace = new[] { '.', '-', };
27+
private static char[] s_charsToReplace = new[] { '.', '-', '+' };
2828

2929
public override bool Execute()
3030
{
@@ -88,7 +88,21 @@ public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies)
8888

8989
private void CollectPInvokes(List<PInvoke> pinvokes, List<PInvokeCallback> callbacks, Type type)
9090
{
91-
foreach (var method in type.GetMethods(BindingFlags.DeclaredOnly|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance)) {
91+
foreach (var method in type.GetMethods(BindingFlags.DeclaredOnly|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance))
92+
{
93+
try
94+
{
95+
CollectPInvokesForMethod(method);
96+
}
97+
catch (Exception ex)
98+
{
99+
Log.LogMessage(MessageImportance.Low, $"Could not get pinvoke, or callbacks for method {method.Name}: {ex}");
100+
continue;
101+
}
102+
}
103+
104+
void CollectPInvokesForMethod(MethodInfo method)
105+
{
92106
if ((method.Attributes & MethodAttributes.PinvokeImpl) != 0)
93107
{
94108
var dllimport = method.CustomAttributes.First(attr => attr.AttributeType.Name == "DllImportAttribute");
@@ -164,7 +178,8 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary<string, string> modules
164178
Where(l => l.Module == module && !l.Skip).
165179
OrderBy(l => l.EntryPoint).
166180
GroupBy(d => d.EntryPoint).
167-
Select (l => "{\"" + l.Key + "\", " + l.Key + "}, // " + string.Join (", ", l.Select(c => c.Method.DeclaringType!.Module!.Assembly!.GetName ()!.Name!).Distinct().OrderBy(n => n)));
181+
Select (l => "{\"" + FixupSymbolName(l.Key) + "\", " + FixupSymbolName(l.Key) + "}, " +
182+
"// " + string.Join (", ", l.Select(c => c.Method.DeclaringType!.Module!.Assembly!.GetName ()!.Name!).Distinct().OrderBy(n => n)));
168183

169184
foreach (var pinvoke in assemblies_pinvokes) {
170185
w.WriteLine (pinvoke);
@@ -216,6 +231,45 @@ static bool ShouldTreatAsVariadic(PInvoke[] candidates)
216231
}
217232
}
218233

234+
private static string FixupSymbolName(string name)
235+
{
236+
UTF8Encoding utf8 = new();
237+
byte[] bytes = utf8.GetBytes(name);
238+
StringBuilder sb = new();
239+
240+
foreach (byte b in bytes)
241+
{
242+
if ((b >= (byte)'0' && b <= (byte)'9') ||
243+
(b >= (byte)'a' && b <= (byte)'z') ||
244+
(b >= (byte)'A' && b <= (byte)'Z') ||
245+
(b == (byte)'_'))
246+
{
247+
sb.Append((char) b);
248+
}
249+
else if (s_charsToReplace.Contains((char) b))
250+
{
251+
sb.Append('_');
252+
}
253+
else
254+
{
255+
sb.Append($"_{b:X}_");
256+
}
257+
}
258+
259+
return sb.ToString();
260+
}
261+
262+
private static string SymbolNameForMethod(MethodInfo method)
263+
{
264+
StringBuilder sb = new();
265+
Type? type = method.DeclaringType;
266+
sb.Append($"{type!.Module!.Assembly!.GetName()!.Name!}_");
267+
sb.Append($"{(type!.IsNested ? type!.FullName : type!.Name)}_");
268+
sb.Append(method.Name);
269+
270+
return FixupSymbolName(sb.ToString());
271+
}
272+
219273
private string MapType (Type t)
220274
{
221275
string name = t.Name;
@@ -262,7 +316,7 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN
262316
if (method.Name == "EnumCalendarInfo") {
263317
// FIXME: System.Reflection.MetadataLoadContext can't decode function pointer types
264318
// https://github.com/dotnet/runtime/issues/43791
265-
sb.Append($"int {pinvoke.EntryPoint} (int, int, int, int, int);");
319+
sb.Append($"int {FixupSymbolName(pinvoke.EntryPoint)} (int, int, int, int, int);");
266320
return sb.ToString();
267321
}
268322

@@ -274,7 +328,7 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN
274328
}
275329

276330
sb.Append(MapType(method.ReturnType));
277-
sb.Append($" {pinvoke.EntryPoint} (");
331+
sb.Append($" {FixupSymbolName(pinvoke.EntryPoint)} (");
278332
int pindex = 0;
279333
var pars = method.GetParameters();
280334
foreach (var p in pars) {

src/tests/BuildWasmApps/Wasm.Build.Tests/BuildPublishTests.cs

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ public void BuildThenPublishNoAOT(BuildArgs buildArgs, RunHost host, string id)
3333
// no relinking for build
3434
bool relinked = false;
3535
BuildProject(buildArgs,
36-
initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
37-
dotnetWasmFromRuntimePack: !relinked,
3836
id: id,
39-
createProject: true,
40-
publish: false);
37+
new BuildProjectOptions(
38+
InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
39+
DotnetWasmFromRuntimePack: !relinked,
40+
CreateProject: true,
41+
Publish: false
42+
));
43+
4144

4245
Run();
4346

@@ -53,10 +56,11 @@ public void BuildThenPublishNoAOT(BuildArgs buildArgs, RunHost host, string id)
5356
relinked = buildArgs.Config == "Release";
5457
BuildProject(buildArgs,
5558
id: id,
56-
dotnetWasmFromRuntimePack: !relinked,
57-
createProject: false,
58-
publish: true,
59-
useCache: false);
59+
new BuildProjectOptions(
60+
DotnetWasmFromRuntimePack: !relinked,
61+
CreateProject: false,
62+
Publish: true,
63+
UseCache: false));
6064

6165
Run();
6266

@@ -79,12 +83,13 @@ public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id
7983
// no relinking for build
8084
bool relinked = false;
8185
(_, string output) = BuildProject(buildArgs,
82-
initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
83-
dotnetWasmFromRuntimePack: !relinked,
84-
id: id,
85-
createProject: true,
86-
publish: false,
87-
label: "first_build");
86+
id,
87+
new BuildProjectOptions(
88+
InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
89+
DotnetWasmFromRuntimePack: !relinked,
90+
CreateProject: true,
91+
Publish: false,
92+
Label: "first_build"));
8893

8994
BuildPaths paths = GetBuildPaths(buildArgs);
9095
var pathsDict = GetFilesTable(buildArgs, paths, unchanged: false);
@@ -109,11 +114,12 @@ public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id
109114
// relink by default for Release+publish
110115
(_, output) = BuildProject(buildArgs,
111116
id: id,
112-
dotnetWasmFromRuntimePack: false,
113-
createProject: false,
114-
publish: true,
115-
useCache: false,
116-
label: "first_publish");
117+
new BuildProjectOptions(
118+
DotnetWasmFromRuntimePack: false,
119+
CreateProject: false,
120+
Publish: true,
121+
UseCache: false,
122+
Label: "first_publish"));
117123

118124
var publishStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
119125
Assert.True(publishStat["pinvoke.o"].Exists);
@@ -125,12 +131,13 @@ public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id
125131

126132
// second build
127133
(_, output) = BuildProject(buildArgs,
128-
initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
129-
dotnetWasmFromRuntimePack: !relinked,
130-
id: id,
131-
createProject: true,
132-
publish: false,
133-
label: "second_build");
134+
id: id,
135+
new BuildProjectOptions(
136+
InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
137+
DotnetWasmFromRuntimePack: !relinked,
138+
CreateProject: true,
139+
Publish: false,
140+
Label: "second_build"));
134141
var secondBuildStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
135142

136143
// no relinking, or AOT

0 commit comments

Comments
 (0)