This usually consists of taking the built assemblies, and related files, and generating an app bundle.
Wasm app build can run in two scenarios:
- After build, eg. when running the app in VS, or
dotnet build foo.csproj - For Publish, eg, when publishing the app to a folder, or Azure
A dotnet wasm app has some native wasm files (dotnet.wasm, and dotnet.js). How these files are obtained, or generated:
- Build
- a. with no native libraries referenced (AOT setting is ignored here)
- files from the runtime pack are used as-is
- b. with native libraries referenced
- dotnet.wasm is relinked with the native libraries
- a. with no native libraries referenced (AOT setting is ignored here)
- Publish
- dotnet.wasm is relinked with the native libraries, and updated pinvoke/icalls from the trimmed assemblies
- if
RunAOTCompilation=true, then the relinking includes AOT'ed assemblies
Implementation:
-
Target
WasmBuildApp -
runs after
Buildby default- which can be disabled by
$(DisableAutoWasmBuildApp) - or the run-after target can be set via
$(WasmBuildAppAfterThisTarget)
- which can be disabled by
-
To run a custom target
- before any of the wasm build targets, use
$(WasmBuildAppDependsOn), and prepend your target name to that - after any of the wasm build targets, use
AfterTargets="WasmBuildApp"on that target
- before any of the wasm build targets, use
-
Avoid depending on this target, because it is available only when the workload is installed. Use
$(WasmNativeWorkload)to check if it is installed. -
When
Module.disableDotnet6Compatibilityis set it would not pollute global namespace.
Implementation:
-
This part runs as a nested build using a
MSBuildtask, which means that the project gets reevaluated. So, if there were any changes made to items/properties in targets before this, then they won't be visible in the nested build. -
By default
WasmTriggerPublishAppruns after thePublishtarget, and that triggers the nested build- The nested build runs
WasmNestedPublishApp, which causesBuild, andPublishtargets to be run - Because this causes
Buildto be run again, if you have any targets that get triggered by that, then they will be running twice.- But the original build run, and this publish run can be differentiated using
$(WasmBuildingForNestedPublish)
- But the original build run, and this publish run can be differentiated using
- The nested build runs
-
WasmTriggerPublishAppessentially just invokes the nested publish-
This runs after
Publish- which can be disabled by
$(DisableAutoWasmPublishApp) - or the run-after target can be set via
$(WasmTriggerPublishAppAfterThisTarget)
- which can be disabled by
-
To influence the wasm build for publish, use
WasmNestedPublishApp- To run a custom target before it, use
$(WasmNestedPublishAppDependsOn) - to run a custom target after it, use
AfterTargets="WasmNestedPublishApp"
- To run a custom target before it, use
-
If you want to dependsOn on this, then use
DependsOnTargets="WasmTriggerPublishApp"
-
-
Any project that wants to use this, can import the props+targets, and set up the various properties before the target
WasmBuildAppgets executed.- the recommended way to do this is to prepend the target to
$(WasmAppBuildDependsOn)
- the recommended way to do this is to prepend the target to
-
Any wasm projects within the
dotnet/runtimerepo should use theWasmApp.InTree.{props,targets}files- These files have relevant properties/targets to work with a local build
-
Generally, the props file can be imported at the top, and the targets file at the bottom of a project file.
-
WasmBuildApptarget is not run by default. The importing project will have to do that. -
By default, the
WasmLoadAssembliesAndReferencestask is not run, and the specified@(WasmAssembliesToBundle)are directly passed toWasmAppBuilder.- If the project needs assembly dependencies to be resolved, then
set
$(WasmResolveAssembliesBeforeBuild) == true. - Should you need to run the AOT toolset, ensure
$(RunAOTCompilation) == trueand set$(WasmAOTDir)to the directory that you want to AOT. Make sure that both@(WasmAssembliesToBundle)and$(WasmAOTDir)are absolute paths.
- If the project needs assembly dependencies to be resolved, then
set
-
Assemblies to be bundled with the app are set via
@(WasmAssembliesToBundle)(which optionally will have dependencies resolved)
The various task inputs correspond to properties as:
AssemblySearchPaths : @(WasmAssemblySearchPaths)
Assemblies : @(WasmAssembliesToBundle)
AppDir : $(WasmAppDir)
MainAssembly : $(WasmMainAssemblyPath)
InvariantGlobalization : $(WasmInvariantGlobalization)
SatelliteAssemblies : @(WasmSatelliteAssemblies)
FilesToIncludeInFileSystem : @(WasmFilesToIncludeInFileSystem)
DebugLevel : $(WasmDebugLevel)
ExtraFilesToDeploy : @(WasmExtraFilesToDeploy)
MicrosoftNetCoreAppRuntimePackDir : $(MicrosoftNetCoreAppRuntimePackRidDir)
run-v8.shscript is emitted to$(WasmRunV8ScriptPath)which defaults to$(WasmAppDir).- To control it's generation use
$(WasmGenerateRunV8Script)(false by default)
- To control it's generation use
This should be a step towards eventually having this build as a sdk.
Refer to WasmApp.targets for more information about the properties/items used as inputs to the process.
For example, if the wasm targets are using a new task, then references to that
need to be updated in a few places. Essentially, look for existing references
to MonoAOTCompiler, or WasmAppBuilder in the relevant files, and duplicate
them for the new task assembly.
-
The task assembly dir, and its path need to be in two properties:
<MonoTargetsTasksDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoTargetsTasks', 'Debug', '$(NetCoreAppToolCurrent)'))</MonoTargetsTasksDir> <MonoTargetsTasksAssemblyPath>$([MSBuild]::NormalizePath('$(MonoTargetsTasksDir)', 'MonoTargetsTasks.dll'))</MonoTargetsTasksAssemblyPath>
And this needs to be set in:
Directory.Build.propssrc/mono/wasm/build/WasmApp.LocalBuild.propssrc/mono/wasm/build/WasmApp.LocalBuild.targetssrc/tests/Common/wasm-test-runner/WasmTestRunner.proj
-
The new dependency (eg. task assembly) needs to be sent to helix as a payload, see
src/libraries/sendtohelixhelp.proj. UseMonoAOTCompileras an example. -
Make changes similar to the one for existing dependent tasks in
eng/testing/linker/trimmingTests.targets,src/tests/Common/wasm-test-runner/WasmTestRunner.projsrc/tests/Directory.Build.targets
If encountering build performance issues, you can use the rollup --perf option and the typescript compiler --generateCpuProfile option to get build profile data, like so:
../emsdk/node/14.18.2_64bit/bin/npm run rollup --perf -- --perf --environment Configuration:Release,NativeBinDir:./rollup-test-data,ProductVersion:12.3.4
node node_modules/typescript/lib/tsc.js --generateCpuProfile dotnet-tsc.cpuprofile -p tsconfig.json
The .cpuprofile file generated by node can be opened in the Performance tab of Chrome or Edge's devtools.