Skip to content
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
17 changes: 12 additions & 5 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,21 @@
<TestExclusionListTasksAssemblyPath>$([MSBuild]::NormalizePath('$(TestExclusionListTasksDir)', 'TestExclusionListTasks.dll'))</TestExclusionListTasksAssemblyPath>
<CoreCLRToolPath>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(RuntimeConfiguration)'))</CoreCLRToolPath>
<ILAsmToolPath Condition="'$(DotNetBuildSourceOnly)' == 'true' or '$(BuildArchitecture)' == 's390x' or '$(BuildArchitecture)' == 'ppc64le'">$(CoreCLRToolPath)</ILAsmToolPath>
<!-- force download temmporarily https://github.com/dotnet/runtime/issues/101528
<WasmtimeDir Condition="'$(WasmtimeDir)' == '' and '$(WASMTIME_PATH)' != '' and Exists($(WASMTIME_PATH))">$(WASMTIME_PATH)</WasmtimeDir>
-->
<WasmtimeDir Condition="'$(WasmtimeDir)' == ''">$([MSBuild]::NormalizeDirectory($(ArtifactsObjDir), 'wasmtime'))</WasmtimeDir>
<InstallWasmtimeForTests Condition="'$(InstallWasmtimeForTests)' == '' and !Exists($(WasmtimeDir))">true</InstallWasmtimeForTests>
<WasmCommonTargetsPath>$([MSBuild]::NormalizeDirectory($(WasmProjectRoot), 'build'))</WasmCommonTargetsPath>
</PropertyGroup>

<!-- Shared cache locations for externally acquired wasm tools. Needs $(BrowserProjectRoot),
$(WasiProjectRoot) and $(ArtifactsDir) from above. -->
<Import Project="$(RepositoryEngineeringDir)wasm\WasmToolCache.props" />

<PropertyGroup>
<!-- Overriding with a local wasmtime is disabled for now: https://github.com/dotnet/runtime/issues/101528
<WasmtimeDir Condition="'$(WasmtimeDir)' == '' and '$(WASMTIME_PATH)' != '' and Exists('$(WASMTIME_PATH)')">$([MSBuild]::EnsureTrailingSlash('$(WASMTIME_PATH)'))</WasmtimeDir>
-->
<WasmtimeDir Condition="'$(WasmtimeDir)' == ''">$(WasmtimeCacheDir)</WasmtimeDir>
<InstallWasmtimeForTests Condition="'$(InstallWasmtimeForTests)' == '' and !Exists('$(WasmtimeStampFile)')">true</InstallWasmtimeForTests>
</PropertyGroup>

<PropertyGroup Condition="'$(DotNetBuildMonoCrossAOT)' == 'true'">
<BuildMonoAOTCrossCompilerOnly>true</BuildMonoAOTCrossCompilerOnly>
<MonoCrossAOTTargetOS>android+browser+wasi</MonoCrossAOTTargetOS>
Expand Down
2 changes: 1 addition & 1 deletion docs/workflow/building/coreclr/wasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ To build the CoreCLR runtime for WebAssembly, use the following command from the
```

This command will:
- Install the Emscripten SDK (emsdk) automatically
- Install the Emscripten SDK (emsdk) automatically, into a shared cache under the main checkout
- Build the CoreCLR runtime for WebAssembly
- Build the required libraries

Expand Down
42 changes: 42 additions & 0 deletions docs/workflow/building/libraries/webassembly-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,48 @@

If you haven't already done so, please read [this document](../../README.md#Build_Requirements) to understand the build requirements for your operating system.

## Wasm tool provisioning

The external tools that the WebAssembly build and test workflows need — the Emscripten SDK, the
WASI SDK, wasmtime, Chrome, chromedriver, Firefox, geckodriver, and V8 — are downloaded on demand
into a shared cache under the repository's main checkout:

```
<main checkout>/.dotnet/wasm-tools/<tool>/<version>-<host rid>/ # the tool
<main checkout>/.dotnet/wasm-tools/<tool>/<version>-<host rid>.complete # written once usable
```

Because the version is part of the path:

- Bumping a version provisions a new entry instead of silently reusing a stale one.
- Switching between branches that pin different versions reuses both, with no re-download.

The cache is anchored at the **main checkout** (not the individual working directory), so all git
worktrees of the same clone share a single copy — no per-worktree re-download — while the cache is
still deleted when you delete the repo. It also lives outside `artifacts/`, so it survives `clean`.
Set `DOTNET_WASM_TOOL_CACHE_DIR` to relocate it (for example to a user-global `~/.dotnet/wasm-tools`
shared across unrelated clones, or a CI agent cache). Setting `EMSDK_PATH` or `WASI_SDK_PATH`
continues to take precedence over the cache for the corresponding SDK. (`WASMTIME_PATH` is *not*
honored: pointing the build at a local wasmtime is currently disabled, see
[#101528](https://github.com/dotnet/runtime/issues/101528).)

Versions are pinned in `src/mono/browser/emscripten-version.txt`, `eng/wasm/wasi-sdk-version.txt`,
`src/mono/wasi/wasmtime-version.txt` and `eng/testing/BrowserVersions.props`.

To provision emscripten without building anything else:

```bash
./build.sh -s provision.emsdk -os browser
```

To remove cache entries that the current checkout no longer references:

```bash
./build.sh -s clean.wasmtools
```

Add `/p:PruneAllWasmTools=true` to remove the cache entirely.

## Building

At this time no other build dependencies are necessary to start building for WebAssembly. Emscripten will be downloaded and installed automatically in the build process. To read how to build on specific platforms, see [Building](../../../../src/mono/browser/README.md#building).
Expand Down
4 changes: 3 additions & 1 deletion docs/workflow/debugging/mono/wasm-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,9 @@ There is another random number generator in `upstream/emscripten/src/determinist
which needs the same treatment.

Running `make patch-deterministic` in `src/mono/wasm` will patch the
emscripten installation in `src/mono/browser/emsdk` with these changes.
provisioned emscripten installation with these changes. Run
`./build.sh -s provision.emsdk -os browser` first and use the reported path,
or set `EMSDK_PATH` to point at your own installation.

### Debugging Signature Mismatch Errors

Expand Down
71 changes: 48 additions & 23 deletions eng/AcquireEmscriptenSdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@
- By default, this target is conditioned on $(TargetsBrowser).
- You can depend on "AcquireEmscriptenSdkUnconditional" instead if this doesn't work for your project.
- Use $(EMSDK_PATH) (set by "AcquireEmscriptenSdk") to refer to the SDK root in your target.

The SDK is composed from NuGet packages into the shared cache described in
eng/wasm/WasmToolCache.props, keyed by emscripten version and host RID, so that a version
bump provisions a fresh entry and every clone and git worktree on the machine shares one copy.
-->
<Project>
<!-- Directory to provision and use emscripten if EMSDK_PATH env variable is not set -->
<PropertyGroup Condition="'$(TargetsBrowser)' == 'true' or $(MonoGenerateOffsetsOSGroups.Contains('browser'))">
<ProvisionEmscriptenDir>$([MSBuild]::NormalizeDirectory('$(MonoProjectRoot)', 'browser', 'emsdk'))</ProvisionEmscriptenDir>
<ShouldProvisionEmscripten Condition="'$(EMSDK_PATH)' == '' and !Exists('$(ProvisionEmscriptenDir)')">true</ShouldProvisionEmscripten>
<EMSDK_PATH Condition="Exists('$(ProvisionEmscriptenDir)') and '$(EMSDK_PATH)' == ''">$(ProvisionEmscriptenDir.Replace('\', '/'))</EMSDK_PATH>
</PropertyGroup>

<PropertyGroup>
<_AcquireEmscriptenSdkInputsFile>$(MSBuildThisFileFullPath)</_AcquireEmscriptenSdkInputsFile>
<_AcquireEmscriptenSdkOutputsFile>$(ProvisionEmscriptenDir)emscripten/.emscripten</_AcquireEmscriptenSdkOutputsFile>
<ShouldProvisionEmscripten Condition="'$(EMSDK_PATH)' == '' and !Exists('$(EmscriptenSdkStampFile)')">true</ShouldProvisionEmscripten>
<EMSDK_PATH Condition="Exists('$(EmscriptenSdkStampFile)') and '$(EMSDK_PATH)' == ''">$(EmscriptenSdkCacheDir.Replace('\', '/'))</EMSDK_PATH>
</PropertyGroup>

<ItemGroup Condition="'$(ShouldProvisionEmscripten)' == 'true'">
Expand All @@ -39,30 +36,30 @@

<!-- Validate that EMSDK_PATH is set -->
<Target Name="_ValidateEmscriptenSdk">
<!-- $(EmsdkVersion) drives the package ids while emscripten-version.txt drives the native
build and the cache key, so they must agree. -->
<Error Condition="'$(ShouldProvisionEmscripten)' == 'true' and '$(EmsdkVersion)' != '$(EmscriptenVersion)'"
Text="%24(EmsdkVersion) is '$(EmsdkVersion)' in eng/Versions.props but src/mono/browser/emscripten-version.txt says '$(EmscriptenVersion)'. Both must be updated together." />
<PropertyGroup>
<_UseRuntimeLocalEmscriptenSdk Condition="'$(ShouldProvisionEmscripten)' == 'true'">true</_UseRuntimeLocalEmscriptenSdk>
</PropertyGroup>
</Target>

<!-- Provision Emscripten SDK from NuGet packages. -->
<Target Name="_AcquireLocalEmscriptenSdk"
Condition="'$(_UseRuntimeLocalEmscriptenSdk)' == 'true'"
Inputs="$(_AcquireEmscriptenSdkInputsFile)"
Outputs="$(_AcquireEmscriptenSdkOutputsFile)">

<Message Text="Provisioning Emscripten SDK to '$(ProvisionEmscriptenDir)'" Importance="High" />
Condition="'$(_UseRuntimeLocalEmscriptenSdk)' == 'true'">

<ReadLinesFromFile File="$(MonoProjectRoot)browser/emscripten-version.txt">
<Output TaskParameter="Lines" ItemName="_VersionLines" />
</ReadLinesFromFile>
<Message Text="Provisioning Emscripten SDK $(EmscriptenVersion) to '$(EmscriptenSdkCacheDir)'" Importance="High" />

<PropertyGroup>
<ScriptExt Condition="'$(HostOS)' == 'windows'">.cmd</ScriptExt>
<ScriptExt Condition="'$(HostOS)' != 'windows'">.sh</ScriptExt>
<EMSDK_PATH>$(ProvisionEmscriptenDir)</EMSDK_PATH>
<BrowserLocalPath>$([MSBuild]::NormalizeDirectory('$(MonoProjectRoot)', 'browser'))</BrowserLocalPath>
<EmsdkLocalPath>emsdk</EmsdkLocalPath>
<EmscriptenVersion>%(_VersionLines.Identity)</EmscriptenVersion>
<!-- Compose into a unique staging directory and only publish it under the final name once
complete, so a concurrent or interrupted build can't leave a partial SDK behind. -->
<_EmsdkStagingDir>$([MSBuild]::EnsureTrailingSlash('$(EmscriptenSdkCacheDir.TrimEnd('\/')).tmp-$([System.Guid]::NewGuid().ToString('N'))'))</_EmsdkStagingDir>
<_EmsdkFinalDir>$(EmscriptenSdkCacheDir.TrimEnd('\/'))</_EmsdkFinalDir>
<_EmsdkParentDir>$([System.IO.Path]::GetDirectoryName($(_EmsdkFinalDir)))</_EmsdkParentDir>
<EMSDK_PATH>$(_EmsdkStagingDir)</EMSDK_PATH>
<_EmsdkPaths Condition="'$(HostOS)' != 'windows'">
CURRENT_SCRIPT=
DIR="."
Expand Down Expand Up @@ -146,7 +143,8 @@ JS_ENGINES = [NODE_JS]
</_EmscriptenPaths>
</PropertyGroup>

<RemoveDir Directories="$(EMSDK_PATH)" />
<RemoveDir Directories="$(_EmsdkStagingDir)" />
<MakeDir Directories="$(_EmsdkParentDir);$(_EmsdkStagingDir)" />

<ItemGroup>
<EmsdkFiles Condition="'%(PackageReference.Identity)' != 'runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.NETCore.Runtime.Wasm.Node.Transport' and '%(PackageReference.Identity)' != 'Microsoft.NET.Runtime.Emscripten.$(EmsdkVersion).Python.win-$(BuildArchitecture)'"
Expand Down Expand Up @@ -185,15 +183,42 @@ JS_ENGINES = [NODE_JS]
File="%(NodeModulesBinFiles.RootDir)%(NodeModulesBinFiles.Directory)%(NodeModulesBinFiles.Filename)"
Overwrite="true" />

<!-- Publish the staged SDK under its final, version-keyed name and only then record it as
complete. The staging-dir -> final-dir move is a rename within the cache root (same
filesystem) and therefore atomic, so a final directory that already exists is a complete
SDK from an earlier or concurrent build: never delete it (another build may be using it),
just move ours in when the slot is free and always ensure the sentinel exists (which also
self-heals a complete-but-unstamped directory). -->
<PropertyGroup>
<_MoveEmsdkIntoPlace Condition="!Exists('$(_EmsdkFinalDir)')">true</_MoveEmsdkIntoPlace>
</PropertyGroup>
<Message Condition="'$(_MoveEmsdkIntoPlace)' != 'true'" Importance="High"
Text="Emscripten SDK $(EmscriptenVersion) is already present at '$(_EmsdkFinalDir)'; using it." />

<!-- The existence test lives inside the command rather than in the task Condition because both
'mv' and 'move' move the source *into* an existing destination directory instead of
failing, which would pollute a complete cache entry. Re-testing immediately before the
rename keeps the window down to a single shell statement; a build that loses the race
discards its staging copy (removed below) and just re-stamps. -->
<Exec Condition="'$(HostOS)' != 'windows'"
Command="if [ ! -e &quot;$(_EmsdkFinalDir)&quot; ]; then mv &quot;$(_EmsdkStagingDir.TrimEnd('\/'))&quot; &quot;$(_EmsdkFinalDir)&quot;; fi"
IgnoreStandardErrorWarningFormat="true" />
<Exec Condition="'$(HostOS)' == 'windows'"
Command="if not exist &quot;$(_EmsdkFinalDir)&quot; move /Y &quot;$(_EmsdkStagingDir.TrimEnd('\/'))&quot; &quot;$(_EmsdkFinalDir)&quot;"
IgnoreStandardErrorWarningFormat="true" />
<RemoveDir Directories="$(_EmsdkStagingDir)" />
<Touch Files="$(EmscriptenSdkStampFile)" AlwaysCreate="true" />

<PropertyGroup>
<EMSDK_PATH>$(EmscriptenSdkCacheDir)</EMSDK_PATH>
<ShouldProvisionEmscripten>false</ShouldProvisionEmscripten>
</PropertyGroup>
</Target>

<Target Name="AcquireEmscriptenSdkUnconditional"
DependsOnTargets="_ValidateEmscriptenSdk;_AcquireLocalEmscriptenSdk">
<PropertyGroup>
<EMSDK_PATH Condition="'$(_UseRuntimeLocalEmscriptenSdk)' == 'true'">$(ProvisionEmscriptenDir.Replace('\', '/'))</EMSDK_PATH>
<EMSDK_PATH Condition="'$(_UseRuntimeLocalEmscriptenSdk)' == 'true'">$(EmscriptenSdkCacheDir.Replace('\', '/'))</EMSDK_PATH>
</PropertyGroup>
<Error Condition="'$(EMSDK_PATH)' == ''" Text="The EMSDK_PATH environment variable should be set pointing to the emscripten SDK root dir."/>
</Target>
Expand Down
60 changes: 28 additions & 32 deletions eng/AcquireWasiSdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,51 @@
- By default, this target is conditioned on $(TargetsWasi).
- You can depend on "AcquireWasiSdkUnconditional" instead if this doesn't work for your project.
- Use $(RuntimeBuildWasiSdkPath) (set by "AcquireWasiSdk") to refer to the SDK root in your target.

The SDK is downloaded into the shared cache described in eng/wasm/WasmToolCache.props, keyed
by version and host RID. $(WasiSdkVersion) is defined there.
-->
<Project>
<PropertyGroup>
<_WasiSdkVersion>33.0</_WasiSdkVersion>
<_RuntimeLocalWasiSdkPath>$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'wasi-sdk'))</_RuntimeLocalWasiSdkPath>
<_WasiSdkMajorVersion>$(WasiSdkVersion.Split('.')[0])</_WasiSdkMajorVersion>
<_WasiSdkOS>linux</_WasiSdkOS>
<_WasiSdkOS Condition="'$(HostOS)' == 'osx'">macos</_WasiSdkOS>
<_WasiSdkOS Condition="'$(HostOS)' == 'windows'">windows</_WasiSdkOS>
<_WasiSdkArch Condition="'$(BuildArchitecture)' == 'arm64'">arm64</_WasiSdkArch>
<_WasiSdkArch Condition="'$(_WasiSdkArch)' == ''">x86_64</_WasiSdkArch>
<!-- Windows releases are only published for x86_64. -->
<_WasiSdkArch Condition="'$(_WasiSdkOS)' == 'windows'">x86_64</_WasiSdkArch>
<_WasiSdkUrl>https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(_WasiSdkMajorVersion)/wasi-sdk-$(WasiSdkVersion)-$(_WasiSdkArch)-$(_WasiSdkOS).tar.gz</_WasiSdkUrl>
</PropertyGroup>

<!-- If the user wants us to use an explicit SDK, validate it matches our expectations. -->
<Target Name="_ValidateWasiSdk">
<PropertyGroup>
<_ActualWasiSdkVersion Condition="'$(WASI_SDK_PATH)' != '' and Exists('$(WASI_SDK_PATH)/VERSION')">$([System.IO.File]::ReadAllText('$(WASI_SDK_PATH)/VERSION').Split()[0])</_ActualWasiSdkVersion>
<_UseRuntimeLocalWasiSdk Condition="'$(_WasiSdkVersion)' != '$(_ActualWasiSdkVersion)'">true</_UseRuntimeLocalWasiSdk>
<_UseRuntimeLocalWasiSdk Condition="'$(WasiSdkVersion)' != '$(_ActualWasiSdkVersion)'">true</_UseRuntimeLocalWasiSdk>
</PropertyGroup>
</Target>

<!-- Otherwise, download our own SDK. Use this file as the input since that's what defines the version. -->
<Target Name="_AcquireLocalWasiSdk"
Condition="'$(_UseRuntimeLocalWasiSdk)' == 'true'"
Inputs="$(MSBuildThisFileFullPath)"
Outputs="$(_RuntimeLocalWasiSdkPath)VERSION">

<Message Text="Downloading a runtime-local WASI SDK $(_WasiSdkVersion) to '$(_RuntimeLocalWasiSdkPath)'" Importance="High" />

<PropertyGroup>
<_WasiSdkMajorVersion>$(_WasiSdkVersion.Split('.')[0])</_WasiSdkMajorVersion>
<_WasiSdkUrl>https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(_WasiSdkMajorVersion)/wasi-sdk-$(_WasiSdkVersion)-x86_64-linux.tar.gz</_WasiSdkUrl>
<_WasiSdkUrl Condition="'$(HostOS)' == 'osx'" >https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(_WasiSdkMajorVersion)/wasi-sdk-$(_WasiSdkVersion)-x86_64-macos.tar.gz</_WasiSdkUrl>
<_WasiSdkUrl Condition="'$(HostOS)' == 'windows'" >https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(_WasiSdkMajorVersion)/wasi-sdk-$(_WasiSdkVersion)-x86_64-windows.tar.gz</_WasiSdkUrl>
</PropertyGroup>

<RemoveDir Directories="$(_RuntimeLocalWasiSdkPath)" />
<MakeDir Directories="$(_RuntimeLocalWasiSdkPath)" />

<Exec Command="curl --silent --show-error --fail --retry 3 --retry-delay 5 -L -o wasi-sdk-$(_WasiSdkVersion).tar.gz $(_WasiSdkUrl) &amp;&amp; tar --strip-components=1 -xzmf wasi-sdk-$(_WasiSdkVersion).tar.gz -C $(_RuntimeLocalWasiSdkPath)"
Condition="'$(HostOS)' != 'windows'"
WorkingDirectory="$(ArtifactsObjDir)"
IgnoreStandardErrorWarningFormat="true" />

<Exec Command="powershell -NonInteractive -command &quot;&amp; $(MSBuildThisFileDirectory)\download-wasi-sdk.ps1 -WasiSdkUrl $(_WasiSdkUrl) -WasiSdkVersion $(_WasiSdkVersion) -WasiSdkPath $(_RuntimeLocalWasiSdkPath); Exit $LastExitCode &quot;"
Condition="'$(HostOS)' == 'windows'"
WorkingDirectory="$(ArtifactsObjDir)"
IgnoreStandardErrorWarningFormat="true" />
<Target Name="_AcquireWasiSdk"
Condition="'$(_UseRuntimeLocalWasiSdk)' == 'true' and !Exists('$(WasiSdkStampFile)')">
<MSBuild Projects="$(RepositoryEngineeringDir)wasm\AcquireWasmTool.proj"
Targets="AcquireWasmTool"
Properties="WasmToolName=wasi-sdk $(WasiSdkVersion);
WasmToolUrl=$(_WasiSdkUrl);
WasmToolKind=targz;
WasmToolStripComponents=1;
WasmToolCacheEntryDir=$(WasiSdkCacheDir);
WasmToolStampFile=$(WasiSdkStampFile);
WasmToolVerify=$(WasiSdkCacheDir)VERSION;
WasmToolDownloadDir=$(WasmToolDownloadDir);
HostOS=$(HostOS)" />
</Target>

<Target Name="AcquireWasiSdkUnconditional"
DependsOnTargets="_ValidateWasiSdk;_AcquireLocalWasiSdk">
DependsOnTargets="_ValidateWasiSdk;_AcquireWasiSdk">
<PropertyGroup>
<RuntimeBuildWasiSdkPath Condition="'$(_UseRuntimeLocalWasiSdk)' != 'true'">$([MSBuild]::NormalizeDirectory('$(WASI_SDK_PATH)'))</RuntimeBuildWasiSdkPath>
<RuntimeBuildWasiSdkPath Condition="'$(_UseRuntimeLocalWasiSdk)' == 'true'">$(_RuntimeLocalWasiSdkPath)</RuntimeBuildWasiSdkPath>
<RuntimeBuildWasiSdkPath Condition="'$(_UseRuntimeLocalWasiSdk)' == 'true'">$(WasiSdkCacheDir)</RuntimeBuildWasiSdkPath>
</PropertyGroup>
</Target>

Expand Down
5 changes: 5 additions & 0 deletions eng/Subsets.props
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@
<SubsetName Include="Mono" Description="The Mono runtime and CoreLib. Equivalent to: $(DefaultMonoSubsets)" />
<SubsetName Include="Mono.Runtime" Description="The Mono .NET runtime." />
<SubsetName Include="Provision.EmSDK" Description="The emsdk provisioning." />
<SubsetName Include="Clean.WasmTools" Description="Prunes unreferenced entries from the shared wasm tool cache." />
<SubsetName Include="Mono.AotCross" Description="The cross-compiler runtime for Mono AOT." />
<SubsetName Include="Mono.CoreLib" Description="The managed System.Private.CoreLib library for Mono." />
<SubsetName Include="Mono.Manifests" Description="The NuGet packages with manifests defining the mobile and Blazor workloads." />
Expand Down Expand Up @@ -309,6 +310,10 @@
<ProjectToBuild Include="$(RepositoryEngineeringDir)AcquireEmscriptenSdk.proj" Category="provision" />
</ItemGroup>

<ItemGroup Condition="$(_subset.Contains('+clean.wasmtools+'))">
<ProjectToBuild Include="$(RepositoryEngineeringDir)wasm\PruneWasmToolCache.proj" Category="provision" />
</ItemGroup>

<!-- CoreClr sets -->
<ItemGroup Condition="$(_subset.Contains('+clr.corelib+'))">
<ProjectToBuild Include="$(CoreClrProjectRoot)System.Private.CoreLib\System.Private.CoreLib.csproj" Category="clr" />
Expand Down
Loading
Loading