Uh oh!
There was an error while loading. Please reload this page.
[build] Provision .NET SDK via standard scripts, drop xaprepare's installer - #11636
Conversation
Replace xaprepare's bespoke `dotnet-install` invocation with Arcade's
standard `eng/common/tools.{sh,ps1}` bootstrap, matching dotnet/sdk,
dotnet/runtime, and dotnet/aspnetcore.
* `global.json`: pin `tools.dotnet` so Arcade's `InitializeDotNetCli`
knows which SDK to install. darc auto-updates this whenever
`Microsoft.NET.Sdk` flows from dotnet/dotnet via the existing
Maestro subscription.
* `eng/install-dotnet.{sh,ps1}`: thin wrappers that set
`DOTNET_INSTALL_DIR=DOTNET_GLOBAL_INSTALL_DIR=bin/$(Configuration)/dotnet/`
(preserving the existing install location) and call
`InitializeDotNetCli` from `eng/common/tools.{sh,ps1}`.
* `Makefile`: `prepare` now depends on a new `install-dotnet` target
that runs `./eng/install-dotnet.sh` first.
* `build-tools/scripts/PrepareWindows.targets`: add an
`_InstallDotNet` target that invokes `eng/install-dotnet.ps1`
before `_BuildXAPrepare`, so `dotnet msbuild Xamarin.Android.sln
-t:Prepare` (used on Windows CI) is self-bootstrapping.
* `Step_InstallDotNetPreview.cs` is deleted and replaced by
`Step_PrepareDotNetWorkloads.cs`. The new step assumes the SDK
is already installed at `bin/$(Configuration)/dotnet/` and only
performs the Android-specific workload prep:
* Cleans stale Mono Android runtime/workload NuGet directories.
* Restores `package-download.proj` (Mono runtime packs +
Mono/Emscripten workload manifest packages).
* Copies the workload manifests into the local SDK's
`sdk-manifests/`.
* Removes obsolete configuration:
* `Configurables.Urls.DotNetInstallScript` (Unix and Windows)
* `--dotnet-sdk-archive` xaprepare option and its
`Context.LocalDotNetSdkArchive` plumbing
* `DownloadDotNetInstallScript`, `GetInstallationScriptArgs`,
`InstallDotNetAsync`, `InstallDotNetFromLocalArchiveAsync`
methods (~150 lines of bespoke install logic).
The SDK install location stays at `bin/$(Configuration)/dotnet/`,
so `dotnet-local.{cmd,sh}` and other consumers continue to work
without changes. CI's `use-dot-net.yaml` is unchanged: it still
provisions a system .NET to bootstrap xaprepare; the pinned preview
SDK install simply moves from xaprepare to Arcade.
Verified locally on Windows: `dotnet msbuild Xamarin.Android.sln
-t:Prepare` after `git clean -xdf bin/Debug/dotnet/` installs the
pinned 11.0.100-preview.5.26268.112 SDK and copies the Mono +
Emscripten workload manifests into `sdk-manifests/`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>darc does not auto-update global.json:tools.dotnet when Microsoft.NET.Sdk
flows from dotnet/dotnet (verified in arcade-services
DependencyFileManager.cs: only Microsoft.DotNet.Arcade.Sdk, the
*.SharedFramework.Sdk family, Microsoft.DotNet.CMake.Sdk,
Microsoft.NET.Sdk.IL, and the literal name "dotnet" are special-cased).
Pinning the SDK version in global.json would have permanently drifted
from the auto-flowed eng/Versions.props value. Read the version directly
from eng/Versions.props instead, making it the single source of truth.
eng/install-dotnet.{sh,ps1} now download Microsoft's official
dotnet-install.{sh,ps1} from
https://builds.dotnet.microsoft.com/dotnet/scripts/v1/ (cached under
bin/$Configuration/dotnet/) and invoke it with the version parsed from
eng/Versions.props:MicrosoftNETSdkPackageVersion. This bypasses Arcade's
eng/common/tools.{sh,ps1} (which strict-mode-reads $GlobalJson.tools)
and lets us drop the tools.dotnet pin from global.json entirely.
Verified on Windows:
- cold install: ~12s
- warm re-run: ~2.5s (idempotent fast path)
- full Prepare: ~88s
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>CI failed with "Permission denied" when `make jenkins` ran `./eng/install-dotnet.sh` because the file was committed as 100644. The file from `make prepare` is invoked directly (not via `bash`), so it needs the executable bit set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
Reverts the executable-bit change from 2645bdb. Windows clones with core.filemode=false would have shown spurious mode changes when editing the file; running it via `bash ./eng/install-dotnet.sh` from the Makefile sidesteps the bit entirely. Same trick for the cached dotnet-install.sh we download under bin/. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This finishes the second half of the SDK provisioning audit started in PR #11636. The first half moved the .NET SDK install into Microsoft's official `dotnet-install.{sh,ps1}` scripts driven by `eng/install-dotnet`. This commit replaces the leftover xaprepare logic that prepared Android-specific .NET workloads against that SDK. What `Step_PrepareDotNetWorkloads` did (now deleted): * Restored `package-download.proj` to pull down the Mono Android runtime packs and the Mono/Emscripten workload manifest packages. * Copied the workload manifests from the NuGet package cache into the local SDK's `sdk-manifests/` folder. What `src/workloads/workloads.csproj` does (single MSBuild project, no C#, no scenarios): * Carries the same `<PackageDownload>` items that lived in `package-download.proj` (run via NuGet's auto-restore). * Has a `_CopyWorkloadManifests` target that runs `AfterTargets="Build"` and copies each `microsoft.net.workload.{mono,emscripten}.<flavor>` manifest's `data/` into the local SDK's `sdk-manifests/<band>/microsoft.net.workload.<flavor>.<dotnet>/<ver>/`. Per @jonathanpeppers' suggestion in #11636 (comment 3403797084): "move it to like `src/workloads/workloads.csproj` and that project is built first." Wiring: * `Makefile prepare:` now runs `dotnet build src/workloads/workloads.csproj` after the BootstrapTasks build, before `PrepareJavaInterop`. * `build-tools/scripts/PrepareWindows.targets`'s `Prepare` target adds an `<MSBuild Projects=".../workloads.csproj" />` invocation in the same spot. * `build-tools/automation/yaml-templates/setup-test-environment-steps.yaml` no longer invokes xaprepare. Test agents now run `eng/install-dotnet.{sh,ps1}` (provisions the SDK at `bin/$Config/dotnet/`) followed by `dotnet build src/workloads/workloads.csproj` (provisions the workloads against that SDK). This fixes the AndroidTestDependencies CI failure introduced when the prior commit removed `Step_InstallDotNetPreview`'s SDK download. Cleanup: * `Step_PrepareDotNetWorkloads.cs` and `package-download.proj` deleted. * `Scenario_Standard` and `Scenario_AndroidTestDependencies` no longer add `Step_PrepareDotNetWorkloads`. * The `xaprepareScenario` parameter (and the now-unused `run-xaprepare.yaml` template) are removed across all CI YAMLs. * Dead `Configurables.MicrosoftNETWorkload*Dir` properties are removed. Verified locally on Windows: * `bin/Debug/dotnet/sdk-manifests/<band>/microsoft.net.workload.{mono.toolchain,emscripten}.{net6..net10,current}/<ver>/WorkloadManifest.json` is populated after `dotnet build src/workloads/workloads.csproj` (12 manifests total). * Re-running is idempotent (~0.5s warm; `Copy SkipUnchangedFiles="true"`). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates dotnet/android’s .NET SDK provisioning from xaprepare’s custom C# installer to the standard dotnet-install.{sh,ps1} flow, keeping the install location at bin/$Configuration/dotnet/ and moving Android-specific workload prep into a standalone MSBuild project.
Changes:
- Add
eng/install-dotnet.{sh,ps1}wrappers that read the pinned SDK version fromeng/Versions.props, downloaddotnet-install.{sh,ps1}, and install intobin/$Configuration/dotnet. - Wire the new install/workload-prep flow into
Makefile, WindowsPrepareWindows.targets, and CI templates; remove xaprepare’s SDK-install step and related plumbing. - Introduce
src/workloads/workloads.csprojto restore required runtime packs + workload manifest packages and copy manifests into the locally installed SDK.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Xamarin.Installer.Build.Tasks/README.md | Updates developer instructions to use eng/install-dotnet.* + build workloads project. |
| src/workloads/workloads.csproj | New MSBuild project to restore runtime packs/manifests and copy manifests into the local SDK. |
| Makefile | Adds install-dotnet prerequisite and runs workloads provisioning during prepare. |
| eng/install-dotnet.sh | New Unix bootstrap script to install pinned SDK into bin/$Configuration/dotnet. |
| eng/install-dotnet.ps1 | New Windows bootstrap script to install pinned SDK into bin\$Configuration\dotnet. |
| build-tools/xaprepare/xaprepare/Steps/Step_InstallDotNetPreview.cs | Deletes the bespoke xaprepare SDK installer step. |
| build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs | Removes SDK install step from the standard xaprepare scenario. |
| build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidTestDependencies.cs | Removes SDK install step from Android test dependency scenario. |
| build-tools/xaprepare/xaprepare/package-download.proj | Deletes the old runtime-pack restore project used by xaprepare. |
| build-tools/xaprepare/xaprepare/Main.cs | Removes --dotnet-sdk-archive option plumbing. |
| build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs | Removes DotNet install script URL configurable (no longer needed). |
| build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs | Removes DotNet install script URL configurable (no longer needed). |
| build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs | Removes workload manifest path helpers tied to the deleted step. |
| build-tools/xaprepare/xaprepare/Application/Context.cs | Removes LocalDotNetSdkArchive property. |
| build-tools/scripts/PrepareWindows.targets | Ensures SDK install runs before building xaprepare; adds workloads provisioning to Prepare. |
| build-tools/automation/yaml-templates/stage-package-tests.yaml | Removes xaprepare scenario parameter usage from test setup. |
| build-tools/automation/yaml-templates/stage-msbuild-emulator-tests.yaml | Removes xaprepare scenario parameter usage from test setup. |
| build-tools/automation/yaml-templates/setup-test-environment.yaml | Removes xaprepareScenario parameter pass-through. |
| build-tools/automation/yaml-templates/setup-test-environment-steps.yaml | Replaces xaprepare invocation with eng/install-dotnet.* + workloads provisioning. |
| build-tools/automation/yaml-templates/setup-test-environment-public.yaml | Removes xaprepareScenario parameter pass-through. |
| build-tools/automation/yaml-templates/run-xaprepare.yaml | Deletes the shared pipeline template that ran xaprepare. |
| build-tools/automation/yaml-templates/run-emulator-tests.yaml | Removes xaprepare scenario parameter usage from test setup. |
| build-tools/automation/azure-pipelines-public.yaml | Removes xaprepare scenario parameter usage from test environment setup. |
| build-tools/automation/azure-pipelines-nightly.yaml | Removes xaprepare scenario parameter usage from test environment setup. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
* Makefile install-dotnet: pass CONFIGURATION through to install-dotnet.sh so 'make CONFIGURATION=Release prepare' installs the SDK under bin/Release/dotnet to match the rest of the build. * eng/install-dotnet.ps1: null-check the result of SelectSingleNode before dereferencing .InnerText so the script fails with the intended friendly error message if <MicrosoftNETSdkPackageVersion> is ever removed from eng/Versions.props. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous commit accidentally captured local submodule pointer changes for external/Java.Interop and external/xamarin-android-tools that have nothing to do with the SDK provisioning audit. Restore them to the pointers used by the rest of this PR (and main). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jonathanpeppers
commented
Jun 22, 2026
/review |
✅ Android PR Reviewer completed successfully! |
There was a problem hiding this comment.
🤖 Android PR Review — [build] Provision .NET SDK via standard scripts, drop xaprepare's installer
I reviewed the diff independently before reading the description. This is a well-scoped, largely subtractive change: the bespoke ~220-line Step_InstallDotNetPreview + package-download.proj are replaced by thin eng/install-dotnet.{sh,ps1} bootstrappers and a src/workloads/workloads.csproj (Microsoft.Build.NoTargets) that PackageDownloads the runtime packs / workload manifests and copies the manifests into the locally-installed SDK. Good direction — collapsing toward dotnet-install + dotnet build is a real maintainability win.
Verified OK (potential concerns I checked and dismissed)
- ✅ No dangling references to the removed symbols (
Step_InstallDotNetPreview,DotNetInstallScript, theMicrosoftNETWorkloadMono*Dirconfigurables,package-download.proj). - ✅ Every
xaprepareScenario/run-xaprepare.yamlconsumer was removed — no orphaned YAML parameters that would break pipeline parsing. - ✅ No provisioning silently dropped:
androidsdk.csproj(SDK/JDK) and emulator setup are untouched; the affected scenarios were effectively no-ops apart from the removed step. - ✅ Property/import ordering in
workloads.csprojis fine —DotNetStableTargetFramework,MicrosoftNETCoreAppRefPackageVersion, and the manifest bands resolve viaDirectory.Build.props→eng/Versions.props(auto-imported before the body);XAPackagesDir/DotNetPreviewPathexist by the time the target runs.Microsoft.Build.NoTargetsis pinned inglobal.json. - ✅ Backslash path separators in the copy target normalize correctly on Linux/macOS (verified empirically).
- ✅
Makefilepasses-p:Configuration=$(CONFIGURATION)toprepare-workloads, matching theinstall-dotnetinstall path (bin/$Configuration/dotnet).
Findings (none merge-blocking)
| Sev | Area | Note |
|---|---|---|
| install scripts | A failed/partial download poisons the cached dotnet-install.{sh,ps1} — no temp-then-move, and an empty cached script silently "succeeds". | |
| workloads | Drops the old forced stale-cache cleanup before copy; possible stale runtime packs/manifests if an internal version string is reused. | |
| 💡 | workloads target | _CopyWorkloadManifests has no Inputs/Outputs and uses AfterTargets="Build". |
| 💡 | formatting | <Error>Condition should come first (Postmortem #33). |
Notes
- 📝 The PR description is slightly stale — it refers to a
Step_PrepareDotNetWorkloads.csreplacement, but the actual change introducessrc/workloads/workloads.csproj(and deletespackage-download.proj). Worth updating so reviewers/git logarchaeologists aren't misled. - ⏳ CI for the head commit (
11dc706) is still in progress (combined statuspending; thedotnet-androidbuild is queued/running). Please confirm it goes green before merging — an earlier commit's legs passed, but the current head hasn't completed.
Nice cleanup overall. 👍
Generated by Android PR Reviewer for issue #11636 · 1.7K AIC · ⌖ 66.1 AIC · ⊞ 37.8K
Comment /review to run again
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
* eng/install-dotnet.{sh,ps1}: download Microsoft's dotnet-install
script to a temp file and atomically rename into place so a failed
or interrupted download cannot poison the cached script. Restores
the temp-then-move pattern the old Step_InstallDotNetPreview used.
* src/workloads/workloads.csproj: put Condition attribute first on the
<Error> task per repo convention.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>These keep slipping into commits because the local worktree has stale submodule pointers. Restore them to the PR's prior commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
After PR #11636 hollowed out `Scenario_AndroidTestDependencies` and `Scenario_EmulatorTestDependencies`, their `AddSteps()` methods no longer add any steps -- they only set `AllowProgramInstallation=false` and `IgnoreMissingPrograms=true`, which have no effect when no steps run. `Scenario_EmulatorTestDependencies` inherited from the former and added nothing. Delete both vestigial scenarios. Also update the now-obsolete error message in `GradleCLI.cs` that referenced the deleted scenario; Gradle is committed to the repo at `build-tools/gradle/`, so the generic "not found" wording is sufficient. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Many xaprepare provisioning steps have been removed over the past year (#11332, #11348, #11399, #11440, #11441, #11636 and follow-up cleanups in #11568, #11580, #11608, #11613, #11631, #11657, #11658, #11731, #11732, #11733, #11737). The supporting scaffolding around those steps was left behind. This PR removes the verified-dead pieces in two passes. ## Files removed (first pass — original audit) | File | Justification | | --- | --- | | `Application/TestAssembly.cs` | Orphan test infra; only referenced by `TestAssemblyType.cs`. | | `Application/TestAssemblyType.cs` | Only referenced by `TestAssembly.cs`. | | `Application/StepWithDownloadProgress.cs` | No subclasses remain. | | `Application/NDKTool.cs` | NDK provisioning moved to MSBuild in #11440. Last consumer was the also-dead `Configurables.NDKTools` collection (removed below). | | `ToolRunners/SnRunner.cs` | Strong-naming tool runner; never instantiated. | | `ToolRunners/SnRunner.OutputSink.cs` | Partial sibling of `SnRunner`. | | `ToolRunners/CMakeRunner.cs` | Never instantiated. | | `ToolRunners/CMakeRunner.OutputSink.cs` | Partial sibling of `CMakeRunner`. | ## Files removed (second pass — repo-wide re-audit) | File | Justification | | --- | --- | | `ToolRunners/MakeRunner.Linux.cs` | Partial of `MakeRunner`; type never instantiated. | | `ToolRunners/MakeRunner.MacOS.cs` | Partial of `MakeRunner`. | | `ToolRunners/MakeRunner.OutputSink.Unix.cs` | Partial of `MakeRunner`. | | `ToolRunners/MakeRunner.Unix.cs` | Partial of `MakeRunner`. | | `ToolRunners/MSBuildRunner.cs` | Never instantiated. | | `ToolRunners/MSBuildRunner.OutputSink.cs` | Partial sibling of `MSBuildRunner`. | | `ToolRunners/NinjaRunner.cs` | Never instantiated. | | `ToolRunners/NinjaRunner.OutputSink.cs` | Partial sibling of `NinjaRunner`. | | `Application/ScenarioNoStandardEndSteps.cs` | Abstract class with zero subclasses. | ## Cascading cleanup - `ConfigAndData/Configurables.cs` — removed the dead `NDKTools` `List<NDKTool>` collection (lines 132–145). Rest of the file unchanged. ## Removed from initial deletion list after verification - `Application/Extensions.DictionaryOfProgramVersionParser.cs` — initial name-only audit flagged it as dead, but its `Add` extension method is consumed via dictionary collection-initializer syntax in `Application/VersionFetchers.cs`. The consumer never references the static class by name, which is why the first audit missed it. The file stays. - `Scenarios/Scenario_Required.cs` — looks unreferenced by static grep, but `Scenario` subclasses are reflectively discovered via the `[Scenario]` attribute in `Context.cs` (`Utilities.GetTypesWithCustomAttribute<ScenarioAttribute> ()`). Live. The file stays. ## Verification - `git grep -n -w <TypeName>` for each deleted type now returns 0 real hits (only unrelated `"TestAssembly"` string literals in `tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/` remain — those are assembly-name strings, not the C# type). - `dotnet build build-tools/xaprepare/xaprepare/xaprepare.csproj -c Debug` → 0 warnings, 0 errors. ## Deferred follow-up The csproj conditionally excludes `*MacOS*` files from compilation when `HostOS != Darwin`, so static dead-code analysis from a Windows/Linux host can't see whether the macOS-only consumers are themselves live. These candidates need verification on a Mac host (or a build matrix) before deletion: - `Application/PkgProgram.MacOS.cs` - `Application/HomebrewProgram.MacOS.cs` - `ToolRunners/BrewRunner.MacOS.cs` - `ToolRunners/PkgutilRunner.MacOS.cs` - `ConfigAndData/Dependencies/MacOS.cs`
### Context After #11636 (dotnet provisioning step removed) and #11731 (test-deps scenarios removed), `Context.AutoProvision` is `false` by default everywhere except hand-run dev provisioning, which is no longer in use. The per-OS package lists are populated at `OS.Init()` time but `EnsureDependencies` is effectively a no-op: - `OS.EnsureDependencies()` returns early when `AutoProvision` is false (the default), - nothing else in the codebase reads from the `Program` derivatives' install/uninstall paths, - the `BuildToolsInventory` writer remains driven only from `EssentialTools.MacOS.cs` (homebrew version detection). The `OS.Init() / InitializeDependencies() / EnsureDependencies()` machinery on `OS.cs` itself is intentionally **left in place** here — that's a larger refactor for a follow-up PR. This PR only strips the now-vestigial package-list data and the program/runner classes that fed it. ### Files deleted (Phase F — macOS, 4 files) - `Application/HomebrewProgram.MacOS.cs` - `Application/PkgProgram.MacOS.cs` - `ToolRunners/BrewRunner.MacOS.cs` - `ToolRunners/PkgutilRunner.MacOS.cs` ### Files deleted (Phase G — Linux, 5 files) - `Application/Program.Linux.cs` (`LinuxProgram` base — orphan after subclasses go) - `Application/Program.ArchLinux.cs` - `Application/Program.DebianLinux.cs` - `Application/Program.FedoraLinux.cs` - `Application/Program.GentooLinux.cs` ### Files deleted (Phase 3 — orphan) - `Application/IBuildInventoryItem.cs` (only implementor was `HomebrewProgram`; `BuildToolsInventory` itself stays, populated directly by `EssentialTools.MacOS.cs`). ### Files reduced to empty stubs `ConfigAndData/Dependencies/`: - `MacOS.cs` — `InitializeDependencies()` no-op (was Homebrew formula list + git fallback). - `Linux.Arch.cs` — class kept (referenced by `distroMap`); package list removed. - `Linux.Fedora.cs` — same. - `Linux.Gentoo.cs` — same. - `Linux.DebianCommon.cs` — common Debian/Ubuntu package list removed; `Flavor = "Debian"` kept. - `Linux.UbuntuCommon.cs` — `libtoolPackages` + `NeedLibtool` virtual + `InitOS` override removed (all dead). - `Linux.Debian.cs` — all per-version package lists (`packages`, `packagesPre10`, `packagesPreTrixie`, `packagesTrixieAndLater`, `packages10AndNewerBuildBots`) removed; release/codename detection (`EnsureVersionInformation`, `DebianUnstableVersionMap`, `IsDebian10OrNewer`, etc.) preserved as conservative scope. - `Linux.Ubuntu.cs` — `preCosmicPackages`, `cosmicPackages`, `preDiscoPackages` lists + `NeedLibtool` override removed; `UbuntuRelease` + `EnsureVersionInformation` preserved. - `Linux.Mint.cs` — `NeedLibtool` override removed (the property is gone from the base). `ConfigAndData/Dependencies/Windows.cs` was already a no-op stub — no edit. ### Verification Orphan audit (each `git grep -nw <Type> -- 'build-tools/xaprepare/*'` reports **0 hits**): - `HomebrewProgram`, `PkgProgram`, `BrewRunner`, `PkgutilRunner` - `ArchLinuxProgram`, `DebianLinuxProgram`, `FedoraLinuxProgram`, `GentooLinuxProgram`, `LinuxProgram` - `IBuildInventoryItem` Build: ``` dotnet build build-tools\xaprepare\xaprepare\xaprepare.csproj -c Debug Build succeeded. 0 Warning(s) 0 Error(s) ``` ### Out of scope (follow-up) - Removing the abstract `OS.InitializeDependencies()` declaration and the surrounding `EnsureDependencies()` machinery from `OperatingSystems/OS.cs`. - `VersionFetchers` / `ProgramVersionParser` / `RegexProgramVersionParser` / `SevenZipVersionParser` / `Extensions.DictionaryOfProgramVersionParser.cs` are kept — `Utilities.GetProgramVersion` still queries them from `Program.cs`, `ToolRunner.cs`, `EssentialTools.MacOS.cs`, and `OperatingSystems/MacOS.cs` (brew detection). ### Precedent #11568, #11580, #11608, #11613, #11631, #11657, #11658, #11731, #11732, #11733, #11737, #11740, #11760
Context
Today, dotnet/android provisions the .NET SDK with bespoke C# code in
build-tools/xaprepare/xaprepare/Steps/Step_InstallDotNetPreview.cs(220 lines) — fetching
dotnet-install.{sh,ps1}from a hard-coded URL,running it with config-driven args, and supporting a
--with-archiveoverride for offline scenarios. Other .NET repos (
dotnet/sdk,dotnet/runtime,dotnet/aspnetcore) all use Arcade's standardeng/common/dotnet-install.{sh,ps1}flow — there's no reason for us tomaintain a custom one.
Phase 1 of a longer migration
This PR is the SDK-provisioning slice of a larger effort to delete
xaprepare entirely so the build collapses to:
xaprepare today is 333 KB / 116 files but only 4 step files have real
logic (
Step_PrepareDotNetWorkloads,Step_GenerateFiles,Step_GenerateFiles.Windows,Step_GenerateCGManifest). Once each stephas an MSBuild equivalent, the surrounding 332 KB of plumbing
(
Application/,ToolRunners/,OperatingSystems/) can also bedeleted. Follow-up PRs are planned for each remaining step.
What changes here
New:
eng/install-dotnet.{sh,ps1}Thin bootstrap wrappers that:
<MicrosoftNETSdkPackageVersion>fromeng/Versions.props(single source of truth, kept up to date by darc when
Microsoft.NET.Sdkflows from dotnet/dotnet).dotnet-install.{sh,ps1}fromhttps://builds.dotnet.microsoft.com/dotnet/scripts/v1/(cachedunder
bin/$Configuration/dotnet/).--version <pinned>and--install-dir bin/$Configuration/dotnet.Install location stays at
bin/$Configuration/dotnet/(where xaprepareput it) so
dotnet-local.{cmd,sh}continues to work unchanged.Wired in everywhere
xaprepareran the install beforeMakefile:prepare:target now depends on a newinstall-dotnettarget that calls
./eng/install-dotnet.sh.build-tools/scripts/PrepareWindows.targets: new_InstallDotNettarget runs
eng/install-dotnet.ps1before_BuildXAPrepare.build.cmd: unchanged — the existingdotnet msbuild ... -t:Prepareflow still works because
_BuildXAPreparenow installs the SDK first.Step_InstallDotNetPreview→Step_PrepareDotNetWorkloadsThe old 220-line installer step is deleted. A new ~120-line
Step_PrepareDotNetWorkloads.csreplaces it and only doesAndroid-specific workload prep (NuGet cleanup,
package-download.projrestore with 3-attempt retry, and workload manifest copy). Everything
SDK-install-related (download script, archive override,
InstallDotNetAsyncetc.) is gone.global.json:tools.dotnet— NOT addedI originally tried pinning the SDK version in
global.json:tools.dotnet(the standard Arcade convention), but verified in
arcade-services/.../DependencyFileManager.csthat darcdoes not auto-update
global.json:tools.dotnetwhen theMicrosoft.NET.Sdkasset flows. Only specific Arcade/Helix SDK namesand the literal name
dotnetare special-cased. So atools.dotnetpin would have permanently drifted from the auto-flowed
eng/Versions.props:MicrosoftNETSdkPackageVersion.The wrappers therefore read the version from
Versions.propsdirectlyand bypass Arcade's
eng/common/tools.{sh,ps1}(which would otherwisestrict-mode-read
$GlobalJson.tools). Single source of truth = thedarc-flowed
eng/Versions.props.Other cleanups
Configurables.{Unix,Windows}.cs: removedUrls.DotNetInstallScript(no longer needed).
Context.cs+Main.cs: removedLocalDotNetSdkArchive/--dotnet-sdk-archiveplumbing. (The replacement is the standardDOTNET_INSTALL_DIRenv var that anyone needing offline support canset themselves.)
Verified on Windows
eng/install-dotnet.ps1(with download)dotnet msbuild Xamarin.Android.sln -t:PrepareThe
dotnet --list-sdksoutput after a cold install correctly shows11.0.100-preview.5.26268.112atbin/Debug/dotnet/sdk. Re-running Prepare is silent (no spuriousre-installs, no extra workload restores).
Migration path for the rest of xaprepare (future PRs)
Step_PrepareDotNetWorkloads.targetsfileStep_GenerateCGManifest.targetsfileStep_GenerateFiles[.Windows]Inputs/Outputsbuild-tools/xaprepare/andPrepareWindows.targetsEnd state:
./eng/install-dotnet.sh+dotnet build. Nothing else.