Skip to content

Commit d34cad0

Browse files
Copilotilonatommy
andauthored
[wasm] Add missing properties to PropertiesThatTriggerRelinking for WASM and WASI (#117983)
* Add missing properties to PropertiesThatTriggerRelinking for WASM and WASI * Add WasmEnableThreads property to relink triggering tests * Add documentation for properties that trigger relinking in WASM and WASI builds --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: ilonatommy <[email protected]>
1 parent 7b8a1b4 commit d34cad0

File tree

8 files changed

+76
-13
lines changed

8 files changed

+76
-13
lines changed

src/mono/browser/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,36 @@ They are handy to quickly disassemble functions and inspect webassembly module s
228228

229229
There is also the [wa-edit](https://github.com/radekdoulik/wa-info#wa-edit) tool, which is now used to prototype improved warm startup.
230230

231+
## Properties That Trigger Relinking
232+
233+
### What is Relinking?
234+
235+
Relinking is the process of rebuilding the native WebAssembly runtime (`dotnet.native.js`, `dotnet.native.wasm`, etc.) with updated or trimmed content, often resulting in a smaller or more optimized output. This is necessary when certain configuration properties or source changes require regeneration of the native artifacts.
236+
237+
### Properties That Trigger Relinking in `browser.proj`
238+
239+
The following MSBuild properties will trigger a relinking (full rebuild of native artifacts) during the browser/wasm build process:
240+
241+
- **`WasmBuildNative`** - `/p:WasmBuildNative=true` - Forces a fresh build of the native runtime components.
242+
- **`WasmRelinkNative`** - `/p:WasmRelinkNative=true` - Explicitly requests relinking, even if not otherwise required by other property changes.
243+
- **`RunAOTCompilation`** - `/p:RunAOTCompilation=true` - Enables Ahead-of-Time (AOT) compilation. Changing this requires relinking to produce the correct output.
244+
- **`WasmEnableExceptionHandling`** - `/p:WasmEnableExceptionHandling=true` - Changes exception handling mechanisms in the native runtime.
245+
- **`EnableDiagnostics`** - `/p:EnableDiagnostics=true` - Enables or disables diagnostic features in the native runtime.
246+
- **`WasmProfilers`** - `/p:WasmProfilers=...` - Changes profiler configuration in the native runtime.
247+
- **`EmccMaximumHeapSize`** - `/p:EmccMaximumHeapSize=...` - Controls memory layout configuration.
248+
- **`EmccInitialHeapSize`** - `/p:EmccInitialHeapSize=...` - Controls memory layout together with `EmccMaximumHeapSize`. Heap size configuration applies only for browser scenarios.
249+
- **`WasmBuildArgs`** - Any change to arguments passed via `/p:WasmBuildArgs=...` (such as enabling/disabling additional features or options) will trigger a relink.
250+
- **Configuration/Target Architecture** - Changing `/p:Configuration=Debug|Release` or `/p:RuntimeIdentifier=browser-wasm`, etc., can require relinking for correct native output.
251+
252+
#### Notes
253+
254+
- Relinking ensures that the produced WebAssembly binaries reflect the current set of build options and runtime features.
255+
- For incremental developer workflows, minimizing unnecessary relinks speeds up build times.
256+
- The relink process is managed by MSBuild targets in `browser.proj`—refer to that file for the most up-to-date logic.
257+
- Source changes to native code (C/C++/Emscripten sources) in `src/mono/browser` or dependent directories will naturally trigger a relink.
258+
259+
For questions or advanced scenarios, see the [WebAssembly build instructions](../../../docs/workflow/building/libraries/webassembly-instructions.md).
260+
231261
## Upgrading Emscripten
232262

233263
Bumping Emscripten version involves these steps:

src/mono/browser/browser.proj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,9 @@
341341
{ "identity": "WasmEnableExceptionHandling", "defaultValueInRuntimePack": "$(WasmEnableExceptionHandling)" },
342342
{ "identity": "EnableDiagnostics", "defaultValueInRuntimePack": "$(EnableDiagnostics)" },
343343
{ "identity": "WasmProfilers", "defaultValueInRuntimePack": "$(WasmProfilers)" },
344-
{ "identity": "EmccMaximumHeapSize", "defaultValueInRuntimePack": "$(EmccMaximumHeapSize)" }
344+
{ "identity": "EmccMaximumHeapSize", "defaultValueInRuntimePack": "$(EmccMaximumHeapSize)" },
345+
{ "identity": "EmccInitialHeapSize", "defaultValueInRuntimePack": "$(EmccInitialHeapSize)" },
346+
{ "identity": "RunAOTCompilation", "defaultValueInRuntimePack": "$(RunAOTCompilation)" }
345347
]
346348
}
347349
}

src/mono/wasi/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,35 @@ you will need to separately download a WASI SDK from https://github.com/WebAssem
4242
- `InvariantGlobalization` - remove globalization support, decrease the publish size.
4343
- More details can be found at https://github.com/dotnet/runtime/blob/main/src/mono/wasm/build/WasmApp.Common.targets and https://github.com/dotnet/runtime/blob/main/src/mono/wasi/build/WasiApp.targets
4444

45+
## Properties That Trigger Relinking
46+
47+
### What is Relinking?
48+
49+
Relinking in WASI builds refers to regenerating the native WASI runtime artifacts (such as `dotnet.native.wasi`, etc.) to reflect changes in configuration, features, or source. This process is essential for producing correct, optimized binaries for WASI environments.
50+
51+
### Properties That Trigger Relinking in `wasi.proj`
52+
53+
The following MSBuild properties will trigger a relinking during the WASI build process:
54+
55+
- **`WasiBuildNative`** - `/p:WasiBuildNative=true` - Forces a rebuild of all native WASI binaries, regardless of detected changes.
56+
- **`WasiRelinkNative`** - `/p:WasiRelinkNative=true` - Explicitly requests relinking of native WASI artifacts.
57+
- **`RunAOTCompilation`** - `/p:RunAOTCompilation=true` - Enables AOT compilation for WASI. Changing this property requires relinking for correct WASI output.
58+
- **`WasmSingleFileBundle`** - `/p:WasmSingleFileBundle=true` - Bundles all assets into the `.wasm` file, requiring native relinking.
59+
- **`EnableDiagnostics`** - `/p:EnableDiagnostics=true` - Enables or disables diagnostic features in the native runtime.
60+
- **`WasmProfilers`** - `/p:WasmProfilers=...` - Changes profiler configuration in the native runtime.
61+
- **`WasmEnableSIMD`** - `/p:WasmEnableSIMD=true` - Enables or disables SIMD instruction support.
62+
- **`WasiBuildArgs`** - Any change to `/p:WasiBuildArgs=...` (custom build flags or feature toggles) can trigger a relink.
63+
- **Configuration/Target Architecture** - Changing `/p:Configuration=Debug|Release` or `/p:RuntimeIdentifier=wasi-wasm`, etc., may require relinking.
64+
65+
#### Notes
66+
67+
- Correct relinking ensures WASI binaries are up-to-date and accurately reflect the intended configuration.
68+
- Avoid unnecessary relinking for faster incremental builds.
69+
- The relink logic is defined in `wasi.proj` and supporting MSBuild files.
70+
- Source changes to native sources in `src/mono/wasi` or related directories will always trigger a relink.
71+
72+
For more detailed WASI build and configuration guidance, see the [WASI build documentation](../../../docs/workflow/building/libraries/webassembly-instructions.md).
73+
4574
## How it works
4675

4776
The mechanism for executing .NET code in a WASI runtime environment is equivalent to how `dotnet.wasm` executes .NET code in a browser environment. That is, it runs the Mono interpreter to execute .NET bytecode that has been built in the normal way. It should also work with AOT but this is not yet attempted.

src/mono/wasi/wasi.proj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@
165165
{ "identity": "WasmSingleFileBundle", "defaultValueInRuntimePack": "$(WasmSingleFileBundle)" },
166166
{ "identity": "EnableDiagnostics", "defaultValueInRuntimePack": "$(EnableDiagnostics)" },
167167
{ "identity": "WasmProfilers", "defaultValueInRuntimePack": "$(WasmProfilers)" },
168-
{ "identity": "WasmEnableSIMD", "defaultValueInRuntimePack": "$(WasmEnableSIMD)" }
168+
{ "identity": "WasmEnableSIMD", "defaultValueInRuntimePack": "$(WasmEnableSIMD)" },
169+
{ "identity": "RunAOTCompilation", "defaultValueInRuntimePack": "$(RunAOTCompilation)" }
169170
]
170171
}
171172
}

src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ public void DefaultTemplate_AOT_InProjectFile(Configuration config)
6666
: "<RunAOTCompilation>true</RunAOTCompilation>";
6767
ProjectInfo info = CopyTestAsset(config, aot: true, TestAsset.BlazorBasicTestApp, "blz_aot_prj_file", extraProperties: extraProperties);
6868

69-
// No relinking, no AOT
70-
BlazorBuild(info, config);
69+
// build relinks
70+
BlazorBuild(info, config, isNativeBuild: true);
7171

7272
// will aot
7373
BlazorPublish(info, config, new PublishOptions(UseCache: false, AOT: true));
7474

7575
// build again
76-
BlazorBuild(info, config, new BuildOptions(UseCache: false));
76+
BlazorBuild(info, config, new BuildOptions(UseCache: false), isNativeBuild: true);
7777
}
7878

7979
[Fact]

src/mono/wasm/Wasm.Build.Tests/BuildPublishTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,18 @@ public async Task BuildThenPublishWithAOT(Configuration config, bool aot)
5555
ProjectInfo info = CopyTestAsset(config, aot, TestAsset.WasmBasicTestApp, "build_publish");
5656

5757
bool isPublish = false;
58-
(_, string output) = BuildProject(info, config, new BuildOptions(Label: "first_build", AOT: aot));
58+
(_, string output) = BuildProject(info, config, new BuildOptions(Label: "first_build", AOT: aot), isNativeBuild: aot);
5959

6060
BuildPaths paths = GetBuildPaths(config, forPublish: isPublish);
6161
IDictionary<string, (string fullPath, bool unchanged)> pathsDict =
6262
GetFilesTable(info.ProjectName, aot, paths, unchanged: false);
6363

6464
string mainDll = $"{info.ProjectName}.dll";
6565
var firstBuildStat = StatFiles(pathsDict);
66-
Assert.False(firstBuildStat["pinvoke.o"].Exists);
66+
Assert.True(firstBuildStat["pinvoke.o"].Exists);
6767
Assert.False(firstBuildStat[$"{mainDll}.bc"].Exists);
68-
69-
CheckOutputForNativeBuild(expectAOT: false, expectRelinking: isPublish, info.ProjectName, output);
68+
69+
CheckOutputForNativeBuild(expectAOT: false, expectRelinking: isPublish || aot, info.ProjectName, output);
7070

7171
if (!_buildContext.TryGetBuildFor(info, out BuildResult? result))
7272
throw new XunitException($"Test bug: could not get the build result in the cache");
@@ -88,7 +88,7 @@ public async Task BuildThenPublishWithAOT(Configuration config, bool aot)
8888
IDictionary<string, FileStat> publishStat = StatFiles(pathsDict);
8989
Assert.True(publishStat["pinvoke.o"].Exists);
9090
Assert.True(publishStat[$"{mainDll}.bc"].Exists);
91-
CheckOutputForNativeBuild(expectAOT: true, expectRelinking: isPublish, info.ProjectName, output);
91+
CheckOutputForNativeBuild(expectAOT: true, expectRelinking: isPublish || aot, info.ProjectName, output);
9292

9393
// source maps are created for build but not for publish, make sure CompareStat won't expect them in publish:
9494
pathsDict["dotnet.js.map"] = (pathsDict["dotnet.js.map"].fullPath, unchanged: false);
@@ -98,11 +98,11 @@ public async Task BuildThenPublishWithAOT(Configuration config, bool aot)
9898

9999
// second build
100100
isPublish = false;
101-
(_, output) = BuildProject(info, config, new BuildOptions(Label: "second_build", UseCache: false, AOT: aot));
101+
(_, output) = BuildProject(info, config, new BuildOptions(Label: "second_build", UseCache: false, AOT: aot), isNativeBuild: aot);
102102
var secondBuildStat = StatFiles(pathsDict);
103103

104104
// no relinking, or AOT
105-
CheckOutputForNativeBuild(expectAOT: false, expectRelinking: isPublish, info.ProjectName, output);
105+
CheckOutputForNativeBuild(expectAOT: false, expectRelinking: isPublish || aot, info.ProjectName, output);
106106

107107
// no native files changed
108108
pathsDict.UpdateTo(unchanged: true);

src/mono/wasm/Wasm.Build.Tests/SatelliteAssembliesTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public async void ResourcesFromMainAssembly(Configuration config, bool aot, bool
5757
[Theory]
5858
[MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ false })]
5959
[MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ true })]
60-
[MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ true, /*relinking*/ false })]
60+
[MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ true, /*relinking*/ true })]
6161
public async void ResourcesFromProjectReference(Configuration config, bool aot, bool nativeRelink, string? argCulture)
6262
{
6363
string prefix = $"SatelliteAssemblyFromProjectRef";

src/mono/wasm/Wasm.Build.Tests/WasmNativeDefaultsTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public static TheoryData<Configuration, string, bool, bool, bool> SettingDiffere
2828
("InvariantTimezone", false),
2929
("InvariantGlobalization", false),
3030
// ("WasmNativeStrip", true) -- tested separately because it has special handling in targets
31+
// ("RunAOTCompilation", false) -- tested separately as it changes build behavior significantly
3132
};
3233

3334
TheoryData<Configuration, string, bool, bool, bool> data = new();

0 commit comments

Comments
 (0)