From 2027b1c6e6f8658a460f22677be44891cfd2f200 Mon Sep 17 00:00:00 2001
From: Adeel <3840695+am11@users.noreply.github.com>
Date: Thu, 7 May 2026 15:04:23 +0300
Subject: [PATCH 1/8] Enable test builds with --bootstrap
---
eng/targetingpacks.targets | 2 +-
src/libraries/Directory.Build.props | 4 ++++
.../tests/TestAssets/NativeExports/NativeExports.csproj | 9 +--------
src/libraries/tests.proj | 2 +-
src/tests/Common/Directory.Build.targets | 2 ++
src/tests/Directory.Build.props | 4 ++++
src/tests/Directory.Build.targets | 1 +
src/tests/build.proj | 6 +++---
src/tests/build.sh | 5 +++++
9 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/eng/targetingpacks.targets b/eng/targetingpacks.targets
index 978a6c5c89ac28..aeca52efb4c054 100644
--- a/eng/targetingpacks.targets
+++ b/eng/targetingpacks.targets
@@ -86,7 +86,7 @@
ExcludedRuntimeIdentifiers="android"
AppHostPackNamePattern="$(LocalFrameworkOverrideName).Host.**RID**"
AppHostPackVersion="$(ProductVersion)"
- AppHostRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86;linux-ppc64le;linux-riscv64;linux-musl-riscv64;linux-loongarch64;linux-musl-loongarch64"
+ AppHostRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86;freebsd-x64;freebsd-arm64;openbsd-x64;openbsd-arm64;illumos-x64;solaris-x64;haiku-x64;linux-ppc64le;linux-riscv64;linux-musl-riscv64;linux-loongarch64;linux-musl-loongarch64"
TargetFramework="$(NetCoreAppCurrent)"
Condition="'$(UseLocalAppHostPack)' == 'true' and '@(KnownAppHostPack->AnyHaveMetadataValue('TargetFramework', '$(NetCoreAppCurrent)'))' != 'true'" />
diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props
index 95fcf08b1bafa1..55c4d486fbd14a 100644
--- a/src/libraries/Directory.Build.props
+++ b/src/libraries/Directory.Build.props
@@ -11,6 +11,10 @@
+
+ true
+
+
$(RepositoryEngineeringDir)BeforeTargetFrameworkInference.targets
$(RepositoryEngineeringDir)LicenseHeader.txt
diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
index 1bf4d2d611c556..4bb45f4bf89917 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
+++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
@@ -124,14 +124,7 @@
GetUnixCrossBuildArgumentsForDNNE"
BeforeTargets="DnneBuildNativeExports">
- $([System.IO.Path]::GetDirectoryName('$(AppHostSourcePath)'))
-
-
-
-
-
- $([MSBuild]::NormalizePath('$(BootstrapRidGraphDir)', 'runtime.json'))
+ $(DotNetHostBinDir)
diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj
index c3a6d07d77d383..3a3a9f626d3ee3 100644
--- a/src/libraries/tests.proj
+++ b/src/libraries/tests.proj
@@ -821,7 +821,7 @@
+ AdditionalProperties="%(AdditionalProperties);_IsPublishing=true;Configuration=Release;CoreCLRConfiguration=$(CoreCLRConfiguration);LibrariesConfiguration=$(LibrariesConfiguration);TasksConfiguration=$(TasksConfiguration);ToolsConfiguration=$(ToolsConfiguration);PublishDir=$(XUnitLogCheckerLibrariesOutDir);ROOTFS_DIR=$(ROOTFS_DIR);RuntimeIdentifier=$(TargetRid);UseBootstrap=$(UseBootstrap)" />
+
+
false
diff --git a/src/tests/Directory.Build.props b/src/tests/Directory.Build.props
index b6dce9ef455492..3c9c5f152e8183 100644
--- a/src/tests/Directory.Build.props
+++ b/src/tests/Directory.Build.props
@@ -8,6 +8,10 @@
+
+ true
+
+
true
diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets
index c9a5138af52d8a..a8788cfe4afcdf 100644
--- a/src/tests/Directory.Build.targets
+++ b/src/tests/Directory.Build.targets
@@ -517,6 +517,7 @@
+
diff --git a/src/tests/build.proj b/src/tests/build.proj
index e8455e5df74964..6a361422800552 100644
--- a/src/tests/build.proj
+++ b/src/tests/build.proj
@@ -173,8 +173,7 @@
<_RestoreProperties>MSBuildRestoreSessionId=$([System.Guid]::NewGuid());TargetOS=$(TargetOS);TargetArchitecture=$(TargetArchitecture);Configuration=$(Configuration);CrossBuild=$(CrossBuild);SetTFMForRestore=true;RuntimeIdentifier=$(RuntimeIdentifier)
- <_RestoreProperties Condition="'$(UseLocalAppHostPack)' == 'true' or '$(TargetOS)' == 'browser'">$(_RestoreProperties);EnableAppHostPackDownload=false;EnableTargetingPackDownload=false;EnableRuntimePackDownload=false
- <_RestoreProperties Condition="'$(TargetsAppleMobile)' == 'true'">$(_RestoreProperties);RestoreUseStaticGraphEvaluation=false
+ <_RestoreProperties Condition="'$(UseBootstrap)' == 'true'">$(_RestoreProperties);UseBootstrap=true
@@ -231,6 +230,7 @@
$(GroupBuildCmd) "/p:PackageOS=$(PackageOS)"
$(GroupBuildCmd) "/p:RuntimeFlavor=$(RuntimeFlavor)"
$(GroupBuildCmd) "/p:RuntimeVariant=$(RuntimeVariant)"
+ $(GroupBuildCmd) "/p:UseBootstrap=true"
$(GroupBuildCmd) "/p:ServerGarbageCollection=$(ServerGarbageCollection)"
$(GroupBuildCmd) "/p:CLRTestBuildAllTargets=$(CLRTestBuildAllTargets)"
$(GroupBuildCmd) "/p:UseCodeFlowEnforcement=$(UseCodeFlowEnforcement)"
@@ -272,7 +272,7 @@
diff --git a/src/tests/build.sh b/src/tests/build.sh
index a0709d7386517c..bc13c9fbc09e1a 100755
--- a/src/tests/build.sh
+++ b/src/tests/build.sh
@@ -151,6 +151,7 @@ usage_list+=("-nativeaot - Builds the tests for Native AOT compilation.")
usage_list+=("-priority1 - Include priority=1 tests in the build.")
usage_list+=("-perfmap - Emit perfmap symbol files when compiling the framework assemblies using Crossgen2.")
usage_list+=("-allTargets - Build managed tests for all target platforms (including test projects in which CLRTestTargetUnsupported resolves to true).")
+usage_list+=("-use-bootstrap - Use artifacts produced by the bootstrap subset for local targeting, runtime, and apphost packs.")
usage_list+=("")
usage_list+=("-runtests - Run tests after building them.")
usage_list+=("-mono, -excludemonofailures - Build the tests for the Mono runtime honoring mono-specific issues.")
@@ -218,6 +219,10 @@ handle_arguments_local() {
__UnprocessedBuildArgs+=("/p:CLRTestBuildAllTargets=allTargets")
;;
+ use-bootstrap|-use-bootstrap)
+ __UnprocessedBuildArgs+=("/p:UseBootstrap=true")
+ ;;
+
rebuild|-rebuild)
__RebuildTests=1
;;
From 6802573882d0f52e9fecb69efb933f89eabeb1f0 Mon Sep 17 00:00:00 2001
From: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Date: Thu, 7 May 2026 17:02:29 +0300
Subject: [PATCH 2/8] Trim trailing slash for windows
---
.../tests/TestAssets/NativeExports/NativeExports.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
index 4bb45f4bf89917..ef5f42023f6f66 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
+++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
@@ -124,7 +124,7 @@
GetUnixCrossBuildArgumentsForDNNE"
BeforeTargets="DnneBuildNativeExports">
- $(DotNetHostBinDir)
+ $(DotNetHostBinDir.TrimEnd('\/'))
From 93488b22a599e227ad6ef04aad79fbc29760c8ef Mon Sep 17 00:00:00 2001
From: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Date: Thu, 7 May 2026 18:59:20 +0300
Subject: [PATCH 3/8] Add missing condition
---
src/tests/Common/Directory.Build.targets | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/tests/Common/Directory.Build.targets b/src/tests/Common/Directory.Build.targets
index b55acf0eac33fd..c4ae15d47fb260 100644
--- a/src/tests/Common/Directory.Build.targets
+++ b/src/tests/Common/Directory.Build.targets
@@ -10,8 +10,8 @@
-
-
+
+
false
From ca29f1620ed15fbfaee3146a7b42b1dc848429a1 Mon Sep 17 00:00:00 2001
From: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Date: Thu, 7 May 2026 20:54:20 +0300
Subject: [PATCH 4/8] Fix win
---
.../tests/TestAssets/NativeExports/NativeExports.csproj | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
index ef5f42023f6f66..aeb3de9ecc6860 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
+++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/NativeExports.csproj
@@ -124,7 +124,8 @@
GetUnixCrossBuildArgumentsForDNNE"
BeforeTargets="DnneBuildNativeExports">
- $(DotNetHostBinDir.TrimEnd('\/'))
+ $(DotNetHostBinDir.TrimEnd('/\'))
+ $([System.IO.Path]::GetDirectoryName('$(AppHostSourcePath)'))
From 8369bdcbfd809d861fa756c67b2f9c71cc32d42a Mon Sep 17 00:00:00 2001
From: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Date: Thu, 7 May 2026 21:56:05 +0300
Subject: [PATCH 5/8] Add another condition
---
src/tests/Directory.Build.targets | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets
index a8788cfe4afcdf..3be09c549dda98 100644
--- a/src/tests/Directory.Build.targets
+++ b/src/tests/Directory.Build.targets
@@ -517,7 +517,7 @@
-
+
From cb12c3421bab8ac5231fe9eb2249266079948888 Mon Sep 17 00:00:00 2001
From: Adeel <3840695+am11@users.noreply.github.com>
Date: Fri, 8 May 2026 02:05:05 +0300
Subject: [PATCH 6/8] Fix wasm
---
src/tests/build.proj | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/tests/build.proj b/src/tests/build.proj
index 6a361422800552..767c50ae78cc1b 100644
--- a/src/tests/build.proj
+++ b/src/tests/build.proj
@@ -173,6 +173,8 @@
<_RestoreProperties>MSBuildRestoreSessionId=$([System.Guid]::NewGuid());TargetOS=$(TargetOS);TargetArchitecture=$(TargetArchitecture);Configuration=$(Configuration);CrossBuild=$(CrossBuild);SetTFMForRestore=true;RuntimeIdentifier=$(RuntimeIdentifier)
+ <_RestoreProperties Condition="'$(UseLocalAppHostPack)' == 'true' or '$(TargetOS)' == 'browser'">$(_RestoreProperties);EnableAppHostPackDownload=false;EnableTargetingPackDownload=false;EnableRuntimePackDownload=false
+ <_RestoreProperties Condition="'$(TargetsAppleMobile)' == 'true'">$(_RestoreProperties);RestoreUseStaticGraphEvaluation=false
<_RestoreProperties Condition="'$(UseBootstrap)' == 'true'">$(_RestoreProperties);UseBootstrap=true
From 5749142df77eb55ea5a040ae213a787d3c56dfa5 Mon Sep 17 00:00:00 2001
From: Adeel <3840695+am11@users.noreply.github.com>
Date: Fri, 8 May 2026 02:08:07 +0300
Subject: [PATCH 7/8] .
---
src/tests/build.proj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/tests/build.proj b/src/tests/build.proj
index 767c50ae78cc1b..884ccfcddb0f79 100644
--- a/src/tests/build.proj
+++ b/src/tests/build.proj
@@ -173,7 +173,7 @@
<_RestoreProperties>MSBuildRestoreSessionId=$([System.Guid]::NewGuid());TargetOS=$(TargetOS);TargetArchitecture=$(TargetArchitecture);Configuration=$(Configuration);CrossBuild=$(CrossBuild);SetTFMForRestore=true;RuntimeIdentifier=$(RuntimeIdentifier)
- <_RestoreProperties Condition="'$(UseLocalAppHostPack)' == 'true' or '$(TargetOS)' == 'browser'">$(_RestoreProperties);EnableAppHostPackDownload=false;EnableTargetingPackDownload=false;EnableRuntimePackDownload=false
+ <_RestoreProperties Condition="'$(TargetOS)' == 'browser'">$(_RestoreProperties);EnableAppHostPackDownload=false;EnableTargetingPackDownload=false;EnableRuntimePackDownload=false
<_RestoreProperties Condition="'$(TargetsAppleMobile)' == 'true'">$(_RestoreProperties);RestoreUseStaticGraphEvaluation=false
<_RestoreProperties Condition="'$(UseBootstrap)' == 'true'">$(_RestoreProperties);UseBootstrap=true
From 8b2760b67b4ce17ed54143fd1d1d058fc69982ae Mon Sep 17 00:00:00 2001
From: Adeel <3840695+am11@users.noreply.github.com>
Date: Fri, 8 May 2026 23:51:37 +0300
Subject: [PATCH 8/8] Update docs
---
.../building/coreclr/cross-building.md | 99 +++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/docs/workflow/building/coreclr/cross-building.md b/docs/workflow/building/coreclr/cross-building.md
index 04b5640f7e32e6..6188026f1387a8 100644
--- a/docs/workflow/building/coreclr/cross-building.md
+++ b/docs/workflow/building/coreclr/cross-building.md
@@ -14,6 +14,8 @@
* [Cross-Building using Docker](#cross-building-using-docker)
* [Cross-Compiling for ARM32 and ARM64 with Docker](#cross-compiling-for-arm32-and-arm64-with-docker)
* [Cross-Compiling for FreeBSD with Docker](#cross-compiling-for-freebsd-with-docker)
+ * [Building CoreCLR with Bootstrapping](#building-coreclr-with-bootstrapping)
+ * [Building Tests with Bootstrapping](#building-tests-with-bootstrapping)
This guide will go more in-depth on how to do cross-building across multiple operating systems and architectures. It's worth mentioning this is not an any-to-any scenario. Only the combinations explained here are possible/supported. If/When any other combinations get supported/discovered, this document will get updated accordingly.
@@ -183,3 +185,100 @@ To build the bootstrap subset of the runtime repo, you can build the `bootstrap`
For simplicity, a `--bootstrap` option is also provided. This option will build the `bootstrap` subset, clean up the artifacts directory, and then build the runtime repo with the `--use-bootstrap` option. This is useful for building the runtime repo with the live NativeAOT version without having to run two separate commands.
The `--bootstrap` option is automatically specified when building the runtime repo for .NET Source Build, as the vast majority of Source Build scenarios use non-portable RIDs.
+
+### Building Tests with Bootstrapping
+
+For community-supported platforms where Microsoft does not publish targeting/runtime/apphost packs (e.g. FreeBSD, illumos, Haiku, linux-riscv64, linux-loongarch64, linux-ppc64le), the test builds need to consume the locally-built bootstrap artifacts instead of trying to download packs from NuGet. Both CoreCLR runtime tests and libraries tests support this via the `--use-bootstrap` flag.
+
+The steps below use FreeBSD ARM64 as an example. The same flow applies to any other community-supported target by swapping `--os`/`--arch` and the docker image.
+
+#### Building in the Container
+
+Start the cross-build container:
+
+```bash
+docker run --rm -it \
+ -v $(pwd):/runtime \
+ -w /runtime \
+ -e ROOTFS_DIR=/crossrootfs/arm64 \
+ mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net11.0-cross-freebsd-14-arm64 \
+ bash
+```
+
+Inside the container, set the target and build the product, then the tests:
+
+```bash
+os=freebsd
+arch=arm64
+
+# Initial product build (also produces the bootstrap subset).
+./build.sh clr+libs --cross --arch $arch --os $os --bootstrap
+
+# Subsequent rebuilds (e.g. after editing code) reuse the existing bootstrap.
+./build.sh clr+libs --cross --arch $arch --os $os --use-bootstrap
+
+# CoreCLR runtime tests.
+src/tests/build.sh -cross -$arch -$os -p:LibrariesConfiguration=Debug --use-bootstrap
+
+# Libraries tests (produces zipped per-library test archives under artifacts/helix/tests/).
+./build.sh libs.tests --cross --arch $arch --os $os --use-bootstrap -p:ArchiveTests=true
+```
+
+Without `--use-bootstrap`, restore would fail with `NU1102` errors because the apphost/runtime/targeting packs for `freebsd-arm64` are not published to NuGet feeds.
+
+#### Running CoreCLR Runtime Tests on the Target
+
+From the host machine, pack and upload `artifacts/tests/coreclr/..Debug` to the target:
+
+```bash
+tar -czf coreclr-tests-freebsd-arm64-Debug.tar.gz artifacts/tests/coreclr/freebsd.arm64.Debug
+scp coreclr-tests-freebsd-arm64-Debug.tar.gz $TargetMachine:/tmp
+```
+
+On the target machine, extract and run a test. For example, the interpreter tests:
+
+```bash
+ssh $TargetMachine
+
+mkdir coreclr-tests && cd $_
+tar -xzf /tmp/coreclr-tests-freebsd-arm64-Debug.tar.gz
+
+CORE_ROOT=$(pwd)/artifacts/tests/coreclr/freebsd.arm64.Debug/Tests/Core_Root \
+ DOTNET_TieredCompilation=0 \
+ artifacts/tests/coreclr/freebsd.arm64.Debug/JIT/Interpreter/InterpreterTester/InterpreterTester.sh
+```
+
+Exit code `100` means pass; any other value means fail.
+
+#### Running Libraries Tests on the Target
+
+Libraries tests need the test host (`artifacts/bin/testhost/...`) plus the per-library test archive. From the host machine:
+
+```bash
+# Pack and upload the test host.
+tar -czf testhost_net11.0-freebsd-Debug-arm64.tar.gz artifacts/bin/testhost/net11.0-freebsd-Debug-arm64
+scp testhost_net11.0-freebsd-Debug-arm64.tar.gz $TargetMachine:/tmp
+
+# Copy a specific test's archive (paths and names vary; this example uses System.Text.RegularExpressions).
+scp artifacts/helix/tests/freebsd.AnyCPU.Debug/System.Text.RegularExpressions.Unit.Tests.zip $TargetMachine:/tmp
+```
+
+On the target machine:
+
+```bash
+ssh $TargetMachine
+
+mkdir testhost
+tar -xzf /tmp/testhost_net11.0-freebsd-Debug-arm64.tar.gz -C testhost
+
+mkdir regex-tests && cd $_
+unzip /tmp/System.Text.RegularExpressions.Unit.Tests.zip
+
+./RunTests.sh --runtime-path ~/testhost/artifacts/bin/testhost/net11.0-freebsd-Debug-arm64
+```
+
+#### Notes
+
+* `--use-bootstrap` works on any platform; on community-supported platforms it is the only way to build the tests because the targeting/runtime/apphost packs are not published to NuGet feeds.
+* If restore still fails after passing the flag, double-check that the `bootstrap` subset built successfully and produced `artifacts/bin/bootstrap/`.
+* Building/packing an individual library test project (e.g. `./dotnet.sh build src/libraries/System.Formats.Nrbf/tests -p:UseBootstrap=true -p:ArchiveTests=true ...`) currently produces an unusable archive (missing `xunit.console.runtimeconfig.json` and similar). Until that is fixed, build the whole `libs.tests` subset as shown above.