Building & Testing in dotnet/runtime
Shell Conventions
Commands below are written for bash. On Windows, use the .cmd entrypoints and PowerShell equivalents:
| bash | Windows (PowerShell) |
|---|---|
./build.sh <args> |
.\build.cmd <args> |
src/tests/build.sh <args> |
src\tests\build.cmd <args> |
src/tests/run.sh <args> |
src\tests\run.cmd <args> |
export FOO=<value> |
$env:FOO = '<value>' |
$CORE_ROOT/corerun <Test>.dll |
& "$env:CORE_ROOT\corerun.exe" <Test>.dll |
find <dir> -name '<pattern>' |
Get-ChildItem <dir> -Recurse -Filter '<pattern>' |
tail -20 <log> |
Get-Content <log> -Tail 20 |
grep '<pattern>' <log> |
Select-String -Path <log> -Pattern '<pattern>' |
Some arguments also differ in casing: -priority1 on bash is -Priority 1 on Windows.
Baseline Build
A successful baseline build of the affected component is required for incremental builds and tests. Without it you'll hit "missing testhost" and "shared framework" errors that cost 20+ minutes per occurrence. If a baseline build fails, STOP, report the failure, and do not attempt to work around it.
When a baseline is required
- Under CCA — always, before changing any of the product code in the table below. ⚠️ The environment is fresh, so there are no pre-existing artifacts and incremental builds fail in ways that waste significant compute. Skipping this step IS a task failure. Changes outside those paths — docs, markdown, workflow and instruction files — don't need one. Exception: if you are on a feature branch with commits upstream of main and the baseline build fails, make whatever code changes are needed to fix the build, then resume requiring a baseline.
- Under CLI (interactive) — only when needed. A usable baseline may already exist from prior work; don't re-run a 40-minute build unnecessarily. Check the component's baseline sentinel and build if it is missing. Otherwise attempt the work, and if it fails with a baseline-missing signature from Troubleshooting, run the baseline once and retry — do not loop. Trust volunteered user signals ("just built", "fresh checkout") over probing.
- Unsure which mode you're in? Follow the CCA rule.
Step 1: Build the Baseline (from repo root)
Pick the row matching the files you will modify:
| Files Changed | Component | Baseline Build |
|---|---|---|
src/coreclr/ |
CoreCLR | ./build.sh clr+libs+host |
src/mono/ |
Mono | ./build.sh mono+libs |
src/libraries/ (no Browser/WASM or WASI targets) |
Libraries | ./build.sh clr+libs -rc release |
src/libraries/ with Browser/WASM or WASI targets in the affected .csproj |
WASM/WASI Libraries | ./build.sh mono+libs -os browser |
src/native/corehost/, src/installer/ |
Host | ./build.sh clr+libs+host -rc release -lc release |
src/tools, src/native/managed |
Tools | ./build.sh clr+libs -rc release |
src/tasks |
Build Tasks | None — ./build.sh tasks is self-contained |
src/tests |
Runtime Tests | ./build.sh clr+libs -lc release -rc checked |
WASM/WASI Library Detection: A change under src/libraries/ is WASM/WASI-relevant if the library's .csproj has explicit Browser/WASM or WASI targets (TargetFrameworks, TARGET_BROWSER, TARGET_WASI constants, or Condition attributes referencing browser/wasi), and the changed file is not excluded from those targets via Condition on <ItemGroup> or <Compile>.
For System.Private.CoreLib changes, use -rc checked instead of -rc release for asserts.
Build on the branch you intend to modify — the baseline reflects your working tree at that moment. Baselining up front requires a clean HEAD; baselining after a probe failure means either stashing work-in-progress changes first or accepting that the baseline incorporates them.
⏱️ This build can take up to 40 minutes. Do not cancel unless no output for 5+ minutes.
Redirect it to a log and poll a bounded view rather than watching the console:
./build.sh clr+libs -rc release > artifacts/build.log 2>&1; echo "exit=$?" > artifacts/build.status
Then check artifacts/build.status, tail -20 artifacts/build.log, or grep the log for : error.
artifacts/log/ holds the per-project binlogs for reporting failures.
Baseline sentinels
A path under artifacts/ whose absence means the baseline is missing.
| Component | Sentinel |
|---|---|
| Libraries | artifacts/bin/testhost/ and artifacts/bin/microsoft.netcore.app.runtime.<RID>/<config>/. Building a single library usually works without one; running its tests does not. |
| CoreCLR | artifacts/bin/coreclr/<OS>.<arch>.<config>/, plus artifacts/tests/coreclr/<OS>.<arch>.<config>/Tests/Core_Root/ to run tests |
| Mono | artifacts/bin/mono/<OS>.<arch>.<config>/, plus artifacts/tests/coreclr/<OS>.<arch>.<config>/Tests/Core_Root/ to run tests (Mono reuses the Core_Root layout) |
| WASM Libraries | artifacts/bin/microsoft.netcore.app.runtime.browser-wasm/<config>/ |
| Host | artifacts/bin/coreclr/<OS>.<arch>.<config>/ and artifacts/bin/testhost/ |
| Tools | artifacts/bin/coreclr/<OS>.<arch>.<config>/ and artifacts/bin/testhost/ |
| Build Tasks | None — ./build.sh tasks is self-contained |
| Runtime Tests | artifacts/tests/coreclr/<OS>.<arch>.<config>/Tests/Core_Root/, produced by the baseline build plus src/tests/build.sh -GenerateLayoutOnly |
Step 2: Configure Environment
export PATH="$(pwd)/.dotnet:$PATH"
dotnet --version # Should match sdk.version in global.json
On Windows: $env:PATH = "$PWD\.dotnet;$env:PATH"
Component Workflows
Read only the file for the component you are working on. All commands must complete with exit code 0, and all tests must pass with zero failures.
| Component | Workflow |
|---|---|
| Libraries, WASM/WASI Libraries | components/libraries.md |
| CoreCLR, Mono | components/runtime.md |
Runtime Tests (src/tests) |
components/runtime-tests.md |
| Host, Tools, Build Tasks | components/host-and-tools.md |
Adding new tests
When creating a regression test for a bug fix:
- Verify the test FAILS without the fix — build and run against the unfixed code.
- Verify the test PASSES with the fix — apply the fix, rebuild, and run again.
- If the fix is not yet merged locally, manually apply the minimal changes from the PR/commit to verify.
Do not mark a regression test task as complete until both conditions are confirmed.
Troubleshooting
| Error | Solution |
|---|---|
| "shared framework must be built" | Run baseline build: ./build.sh clr+libs -rc release |
| "testhost" missing / FileNotFoundException | Run baseline build first (Step 1 above) |
| Build timeout | Wait up to 40 min; only fail if no output for 5 min |
| "Target does not exist" | Avoid specifying a target framework; the build will auto-select $(NetCoreAppCurrent) |
"0 test projects" after build.sh -Test |
The test has <CLRTestPriority> > 0; add -priority1 to the build command |
When reporting failures: Include logs from artifacts/log/ and console output for diagnostics.