diff --git a/Makefile b/Makefile index 85c28361c68..99427070154 100644 --- a/Makefile +++ b/Makefile @@ -121,11 +121,15 @@ list-nunit-tests: include build-tools/scripts/runtime-helpers.mk .PHONY: prepare -prepare: +prepare: install-dotnet $(call SYSTEM_DOTNET_BINLOG,prepare-run,run) $(PREPARE_MSBUILD_FLAGS) --project "$(PREPARE_PROJECT)" --framework $(PREPARE_NET_FX) -- $(_PREPARE_ARGS) $(call SYSTEM_DOTNET_BINLOG,prepare-bootstrap) Xamarin.Android.BootstrapTasks.sln $(call DOTNET_BINLOG,prepare-java.interop) $(SOLUTION) -t:PrepareJavaInterop +.PHONY: install-dotnet +install-dotnet: + bash ./eng/install-dotnet.sh + .PHONY: prepare-help prepare-help: $(call SYSTEM_DOTNET_BINLOG,prepare-help,run) --project "$(PREPARE_PROJECT)" --framework $(PREPARE_NET_FX) -- -h diff --git a/build-tools/scripts/PrepareWindows.targets b/build-tools/scripts/PrepareWindows.targets index bcd6144a4f3..0875e4bf558 100644 --- a/build-tools/scripts/PrepareWindows.targets +++ b/build-tools/scripts/PrepareWindows.targets @@ -9,7 +9,13 @@ <_XAPrepareStandardArgs Condition=" '$(XA_FORCE_COMPONENT_REFRESH)' == 'true' ">$(_XAPrepareStandardArgs) -refresh - + + + + - /// Path to a local .NET SDK archive to use instead of downloading. - /// - public string? LocalDotNetSdkArchive { get; set; } - /// /// Determines whether or not we are running on a hosted azure pipelines agent. /// These agents have certain limitations, the most pressing being the amount of available storage. diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs index 01b91fbcf68..b1654a89b42 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Unix.cs @@ -9,14 +9,5 @@ partial class Defaults { public const string DefaultCompiler = "cc"; } - - partial class Urls - { - // This is the "public" url that we really should be using, but it keeps failing with: - // AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: RevocationStatusUnknown - // For now we'll grab it directly from GitHub - // public static readonly Uri DotNetInstallScript = new Uri ("https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh"); - public static readonly Uri DotNetInstallScript = new Uri ("https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh"); - } } } diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs index 7c7a025398f..d59cfe637d6 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.Windows.cs @@ -13,10 +13,5 @@ partial class Defaults partial class Paths { } - - partial class Urls - { - public static readonly Uri DotNetInstallScript = new Uri ("https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1"); - } } } diff --git a/build-tools/xaprepare/xaprepare/Main.cs b/build-tools/xaprepare/xaprepare/Main.cs index d9157457413..4a65478c8be 100644 --- a/build-tools/xaprepare/xaprepare/Main.cs +++ b/build-tools/xaprepare/xaprepare/Main.cs @@ -27,7 +27,6 @@ sealed class ParsedOptions public string? Configuration { get; set; } public bool AutoProvision { get; set; } public bool AutoProvisionUsesSudo { get; set; } - public string? LocalDotNetSdkArchive { get; set; } } public static int Main (string[] args) @@ -98,7 +97,6 @@ static async Task Run (string[] args) "", {"auto-provision=", $"Automatically install software required by .NET for Android", v => parsedOptions.AutoProvision = ParseBoolean (v)}, {"auto-provision-uses-sudo=", $"Allow use of sudo(1) when provisioning", v => parsedOptions.AutoProvisionUsesSudo = ParseBoolean (v)}, - {"dotnet-sdk-archive=", "Path to a local .NET SDK archive (zip or tar.gz) to use instead of downloading from the internet.", v => parsedOptions.LocalDotNetSdkArchive = v?.Trim () }, "", {"h|help", "Show this help message", v => parsedOptions.ShowHelp = true }, }; @@ -128,7 +126,6 @@ static async Task Run (string[] args) Context.Instance.DebugFileExtension = parsedOptions.DebugFileExtension; Context.Instance.AutoProvision = parsedOptions.AutoProvision; Context.Instance.AutoProvisionUsesSudo = parsedOptions.AutoProvisionUsesSudo; - Context.Instance.LocalDotNetSdkArchive = parsedOptions.LocalDotNetSdkArchive; if (!String.IsNullOrEmpty (parsedOptions.Configuration)) Context.Instance.Configuration = parsedOptions.Configuration!; diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidTestDependencies.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidTestDependencies.cs index ca0c311f2d2..0c9ec4b62c0 100644 --- a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidTestDependencies.cs +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_AndroidTestDependencies.cs @@ -15,7 +15,7 @@ protected Scenario_AndroidTestDependencies (string name, string description) protected override void AddSteps (Context context) { - Steps.Add (new Step_InstallDotNetPreview ()); + Steps.Add (new Step_PrepareDotNetWorkloads ()); // disable installation of missing programs... context.SetCondition (KnownConditions.AllowProgramInstallation, false); diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs index 862c752e07a..dbc8015809b 100644 --- a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs @@ -18,7 +18,7 @@ protected override void AddSteps (Context context) if (context == null) throw new ArgumentNullException (nameof (context)); - Steps.Add (new Step_InstallDotNetPreview ()); + Steps.Add (new Step_PrepareDotNetWorkloads ()); Steps.Add (new Step_GenerateFiles (atBuildStart: true)); Steps.Add (new Step_GenerateCGManifest ()); } diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_InstallDotNetPreview.cs b/build-tools/xaprepare/xaprepare/Steps/Step_InstallDotNetPreview.cs deleted file mode 100644 index 7d53a9b5330..00000000000 --- a/build-tools/xaprepare/xaprepare/Steps/Step_InstallDotNetPreview.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Xamarin.Android.Prepare -{ - class Step_InstallDotNetPreview : StepWithDownloadProgress, IBuildInventoryItem - { - public string BuildToolName => "dotnet-preview"; - public string BuildToolVersion => Context.Instance.Properties.GetRequiredValue (KnownProperties.MicrosoftDotnetSdkInternalPackageVersion); - - public Step_InstallDotNetPreview () - : base ("Install required .NET Preview SDK locally") - { } - - protected override async Task Execute (Context context) - { - var dotnetPath = Configurables.Paths.DotNetPreviewPath; - dotnetPath = dotnetPath.TrimEnd (new char [] { Path.DirectorySeparatorChar }); - - // Check if a local SDK archive was specified - if (!String.IsNullOrEmpty (context.LocalDotNetSdkArchive)) { - if (!await InstallDotNetFromLocalArchiveAsync (context, dotnetPath, context.LocalDotNetSdkArchive!)) { - Log.ErrorLine ($"Installation of dotnet SDK from local archive '{context.LocalDotNetSdkArchive}' failed."); - return false; - } - } else if (!await InstallDotNetAsync (context, dotnetPath, BuildToolVersion, useCachedInstallScript: true) && - !await InstallDotNetAsync (context, dotnetPath, BuildToolVersion, useCachedInstallScript: false)) { - Log.ErrorLine ($"Installation of dotnet SDK '{BuildToolVersion}' failed."); - return false; - } - - AddToInventory (); - - // Delete all relevant NuGet package install directories, as we could possibly be using a new runtime commit with a previously installed version (6.0.0) - var runtimeDirs = Directory.GetDirectories (Configurables.Paths.XAPackagesDir, "microsoft.netcore.app.runtime.mono.android*"); - var packageDirsToRemove = new List (runtimeDirs); - packageDirsToRemove.Add (Configurables.Paths.MicrosoftNETWorkloadMonoPackageDir); - foreach (var packageDir in packageDirsToRemove) { - if (Directory.Exists (packageDir)) { - Utilities.DeleteDirectory (packageDir); - } - } - - // Install runtime packs associated with the SDK previously installed. - var packageDownloadProj = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "build-tools", "xaprepare", "xaprepare", "package-download.proj"); - var logPathBase = Path.Combine (Configurables.Paths.BuildBinDir, $"msbuild-{context.BuildTimeStamp}-download-runtime-packs"); - - const int maxAttempts = 3; - const int initialBackoffDelayMilliseconds = 2000; - bool restoreSucceeded = false; - for (int attempt = 1; attempt <= maxAttempts; attempt++) { - var logPath = $"{logPathBase}-attempt{attempt}.binlog"; - var runner = new ProcessRunner (Configurables.Paths.DotNetPreviewTool, "restore", - packageDownloadProj, - "--configfile", Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "NuGet.config"), - $"-bl:{logPath}", - "--verbosity", "normal" - ) { - EchoStandardOutput = true, - EchoStandardError = true, - }; - if (runner.Run ()) { - restoreSucceeded = true; - break; - } - if (attempt < maxAttempts) { - Log.WarningLine ($"Failed to restore runtime packs (attempt {attempt}/{maxAttempts}), retrying..."); - var delayMilliseconds = initialBackoffDelayMilliseconds * (1 << (attempt - 1)); - await Task.Delay (delayMilliseconds); - } - } - if (!restoreSucceeded) { - Log.ErrorLine ($"Failed to restore runtime packs using '{packageDownloadProj}' after {maxAttempts} attempts."); - return false; - } - - var sdk_manifests = Path.Combine (dotnetPath, "sdk-manifests"); - - // Copy the WorkloadManifest.* files from the latest Microsoft.NET.Workload.* listed in package-download.proj - var dotnets = new [] { "net6", "net7", "net8", "net9", "net10", "current" }; - foreach (var dotnet in dotnets) { - var destination = Path.Combine (sdk_manifests, - context.Properties.GetRequiredValue (KnownProperties.DotNetMonoManifestVersionBand), - $"microsoft.net.workload.mono.toolchain.{dotnet}", - context.Properties.GetRequiredValue (KnownProperties.MicrosoftNETWorkloadMonoToolChainPackageVersion)); - Utilities.DeleteDirectory (destination, recurse: true); - foreach (var file in Directory.GetFiles (string.Format (Configurables.Paths.MicrosoftNETWorkloadMonoToolChainDir, dotnet), "*")) { - Utilities.CopyFileToDir (file, destination); - } - destination = Path.Combine (sdk_manifests, - context.Properties.GetRequiredValue (KnownProperties.DotNetEmscriptenManifestVersionBand), - $"microsoft.net.workload.emscripten.{dotnet}", - context.Properties.GetRequiredValue (KnownProperties.MicrosoftNETWorkloadEmscriptenPackageVersion)); - Utilities.DeleteDirectory (destination, recurse: true); - foreach (var file in Directory.GetFiles (string.Format (Configurables.Paths.MicrosoftNETWorkloadEmscriptenDir, dotnet), "*")) { - Utilities.CopyFileToDir (file, destination); - } - } - - return true; - } - - async Task DownloadDotNetInstallScript (Context context, string dotnetScriptPath, Uri dotnetScriptUrl, bool useCachedInstallScript) - { - string tempDotnetScriptPath = dotnetScriptPath + "-tmp"; - Utilities.DeleteFile (tempDotnetScriptPath); - - Log.StatusLine ("Downloading dotnet-install script..."); - - if (useCachedInstallScript && File.Exists (dotnetScriptPath)) { - Log.StatusLine ($"Using cached installation script found in '{dotnetScriptPath}'"); - return true; - } - Utilities.DeleteFile (dotnetScriptPath); - - Log.StatusLine ($" {context.Characters.Link} {dotnetScriptUrl}", ConsoleColor.White); - await Utilities.Download (dotnetScriptUrl, tempDotnetScriptPath, DownloadStatus.Empty); - - if (File.Exists (tempDotnetScriptPath)) { - Utilities.CopyFile (tempDotnetScriptPath, dotnetScriptPath); - Utilities.DeleteFile (tempDotnetScriptPath); - return true; - } - - if (File.Exists (dotnetScriptPath)) { - Log.WarningLine ($"Download of dotnet-install from '{dotnetScriptUrl}' failed"); - Log.StatusLine ($"Using cached installation script found in '{dotnetScriptPath}'"); - return true; - } else { - Log.ErrorLine ($"Download of dotnet-install from '{dotnetScriptUrl}' failed"); - return false; - } - } - - string[] GetInstallationScriptArgs (string version, string dotnetPath, string dotnetScriptPath, bool runtimeOnly) - { - List args; - if (Context.IsWindows) { - args = new List { - "-NoProfile", "-ExecutionPolicy", "unrestricted", "-file", dotnetScriptPath, - "-Version", version, "-InstallDir", dotnetPath, "-Verbose" - }; - if (runtimeOnly) { - args.AddRange (new string [] { "-Runtime", "dotnet" }); - } - return args.ToArray (); - } - - args = new List { - dotnetScriptPath, "--version", version, "--install-dir", dotnetPath, "--verbose" - }; - - if (runtimeOnly) { - args.AddRange (new string [] { "--runtime", "dotnet" }); - } - return args.ToArray (); - } - - async Task InstallDotNetFromLocalArchiveAsync (Context context, string dotnetPath, string archivePath) - { - if (!File.Exists (archivePath)) { - Log.ErrorLine ($"Local .NET SDK archive not found: '{archivePath}'"); - return false; - } - - Log.StatusLine ($"Installing .NET SDK from local archive: {archivePath}"); - - // Always delete the bin/$(Configuration)/dotnet/ directory - Utilities.DeleteDirectory (dotnetPath); - - return await Utilities.Unpack (archivePath, dotnetPath); - } - - // Standardize on dotnet/arcade's usage of the dotnet-install scripts: - // invoke dotnet-install.{sh,ps1} directly with --version/--install-dir - // rather than harvesting URLs and downloading/extracting the archive - // ourselves. This matches Arcade's eng/common/tools.sh and our CI - // (build-tools/automation/yaml-templates/use-dot-net.yaml), lets the - // script perform its built-in SHA-512 verification of the archive, and - // relies on the script's own "already installed" check for incremental - // re-runs (no need to re-download when the requested version is present). - async Task InstallDotNetAsync (Context context, string dotnetPath, string version, bool useCachedInstallScript, bool runtimeOnly = false) - { - string cacheDir = context.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory); - - Uri dotnetScriptUrl = Configurables.Urls.DotNetInstallScript; - string scriptFileName = Path.GetFileName (dotnetScriptUrl.LocalPath); - string cachedDotnetScriptPath = Path.Combine (cacheDir, scriptFileName); - if (!await DownloadDotNetInstallScript (context, cachedDotnetScriptPath, dotnetScriptUrl, useCachedInstallScript)) { - return false; - } - - Directory.CreateDirectory (dotnetPath); - string dotnetScriptPath = Path.Combine (dotnetPath, scriptFileName); - Utilities.CopyFile (cachedDotnetScriptPath, dotnetScriptPath); - - var type = runtimeOnly ? "runtime" : "SDK"; - Log.StatusLine ($"Installing dotnet {type} '{version}'..."); - - string scriptCommand = Context.IsWindows ? "powershell.exe" : "bash"; - string[] scriptArgs = GetInstallationScriptArgs (version, dotnetPath, dotnetScriptPath, runtimeOnly); - return Utilities.RunCommand (scriptCommand, scriptArgs); - } - - bool TestDotNetSdk (string dotnetTool) - { - return Utilities.RunCommand (dotnetTool, new string [] { "--version" }); - } - - public void AddToInventory () - { - if (!string.IsNullOrEmpty (BuildToolName) && !string.IsNullOrEmpty (BuildToolVersion) && !Context.Instance.BuildToolsInventory.ContainsKey (BuildToolName)) { - Context.Instance.BuildToolsInventory.Add (BuildToolName, BuildToolVersion); - } - } - - } -} diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_PrepareDotNetWorkloads.cs b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareDotNetWorkloads.cs new file mode 100644 index 00000000000..cf26a254ac8 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_PrepareDotNetWorkloads.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + // Prepares the Android-specific workloads against the .NET SDK that + // `eng/install-dotnet.{sh,ps1}` (Arcade's `eng/common/tools.{sh,ps1}`) + // has already provisioned at `bin/$(Configuration)/dotnet/`: + // * Cleans stale runtime/workload NuGet directories. + // * Restores `package-download.proj` to pull down the Mono Android + // runtime packs and the Mono/Emscripten workload manifest packages. + // * Copies the workload manifests into the local SDK's `sdk-manifests/` + // so the SDK can resolve the workloads against the locally installed + // runtime packs. + // + // The SDK itself is installed by Arcade's `eng/common/tools.{sh,ps1}` + // via `eng/install-dotnet.{sh,ps1}` invoked from the `Prepare` MSBuild + // target (Windows) and `make prepare` (Unix). This step assumes the SDK + // already exists at `Configurables.Paths.DotNetPreviewPath`. + class Step_PrepareDotNetWorkloads : StepWithDownloadProgress, IBuildInventoryItem + { + public string BuildToolName => "dotnet-preview"; + public string BuildToolVersion => Context.Instance.Properties.GetRequiredValue (KnownProperties.MicrosoftDotnetSdkInternalPackageVersion); + + public Step_PrepareDotNetWorkloads () + : base ("Prepare .NET workloads against the locally installed SDK") + { } + + protected override async Task Execute (Context context) + { + var dotnetPath = Configurables.Paths.DotNetPreviewPath; + dotnetPath = dotnetPath.TrimEnd (new char [] { Path.DirectorySeparatorChar }); + + var dotnetTool = Configurables.Paths.DotNetPreviewTool; + if (!Directory.Exists (dotnetPath)) { + Log.ErrorLine ($"Expected .NET SDK at '{dotnetPath}' but the directory does not exist. Run `eng/install-dotnet.{{sh,ps1}}` (or `make prepare`) first."); + return false; + } + + AddToInventory (); + + // Delete all relevant NuGet package install directories, as we could possibly be using a new runtime commit with a previously installed version (6.0.0) + var runtimeDirs = Directory.GetDirectories (Configurables.Paths.XAPackagesDir, "microsoft.netcore.app.runtime.mono.android*"); + var packageDirsToRemove = new List (runtimeDirs); + packageDirsToRemove.Add (Configurables.Paths.MicrosoftNETWorkloadMonoPackageDir); + foreach (var packageDir in packageDirsToRemove) { + if (Directory.Exists (packageDir)) { + Utilities.DeleteDirectory (packageDir); + } + } + + // Install runtime packs associated with the SDK previously installed. + var packageDownloadProj = Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "build-tools", "xaprepare", "xaprepare", "package-download.proj"); + var logPathBase = Path.Combine (Configurables.Paths.BuildBinDir, $"msbuild-{context.BuildTimeStamp}-download-runtime-packs"); + + const int maxAttempts = 3; + const int initialBackoffDelayMilliseconds = 2000; + bool restoreSucceeded = false; + for (int attempt = 1; attempt <= maxAttempts; attempt++) { + var logPath = $"{logPathBase}-attempt{attempt}.binlog"; + var runner = new ProcessRunner (dotnetTool, "restore", + packageDownloadProj, + "--configfile", Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "NuGet.config"), + $"-bl:{logPath}", + "--verbosity", "normal" + ) { + EchoStandardOutput = true, + EchoStandardError = true, + }; + if (runner.Run ()) { + restoreSucceeded = true; + break; + } + if (attempt < maxAttempts) { + Log.WarningLine ($"Failed to restore runtime packs (attempt {attempt}/{maxAttempts}), retrying..."); + var delayMilliseconds = initialBackoffDelayMilliseconds * (1 << (attempt - 1)); + await Task.Delay (delayMilliseconds); + } + } + if (!restoreSucceeded) { + Log.ErrorLine ($"Failed to restore runtime packs using '{packageDownloadProj}' after {maxAttempts} attempts."); + return false; + } + + var sdk_manifests = Path.Combine (dotnetPath, "sdk-manifests"); + + // Copy the WorkloadManifest.* files from the latest Microsoft.NET.Workload.* listed in package-download.proj + var dotnets = new [] { "net6", "net7", "net8", "net9", "net10", "current" }; + foreach (var dotnet in dotnets) { + var destination = Path.Combine (sdk_manifests, + context.Properties.GetRequiredValue (KnownProperties.DotNetMonoManifestVersionBand), + $"microsoft.net.workload.mono.toolchain.{dotnet}", + context.Properties.GetRequiredValue (KnownProperties.MicrosoftNETWorkloadMonoToolChainPackageVersion)); + Utilities.DeleteDirectory (destination, recurse: true); + foreach (var file in Directory.GetFiles (string.Format (Configurables.Paths.MicrosoftNETWorkloadMonoToolChainDir, dotnet), "*")) { + Utilities.CopyFileToDir (file, destination); + } + destination = Path.Combine (sdk_manifests, + context.Properties.GetRequiredValue (KnownProperties.DotNetEmscriptenManifestVersionBand), + $"microsoft.net.workload.emscripten.{dotnet}", + context.Properties.GetRequiredValue (KnownProperties.MicrosoftNETWorkloadEmscriptenPackageVersion)); + Utilities.DeleteDirectory (destination, recurse: true); + foreach (var file in Directory.GetFiles (string.Format (Configurables.Paths.MicrosoftNETWorkloadEmscriptenDir, dotnet), "*")) { + Utilities.CopyFileToDir (file, destination); + } + } + + return true; + } + + public void AddToInventory () + { + if (!string.IsNullOrEmpty (BuildToolName) && !string.IsNullOrEmpty (BuildToolVersion) && !Context.Instance.BuildToolsInventory.ContainsKey (BuildToolName)) { + Context.Instance.BuildToolsInventory.Add (BuildToolName, BuildToolVersion); + } + } + } +} diff --git a/eng/install-dotnet.ps1 b/eng/install-dotnet.ps1 new file mode 100644 index 00000000000..6eaa2a4e44d --- /dev/null +++ b/eng/install-dotnet.ps1 @@ -0,0 +1,43 @@ +<# +.SYNOPSIS + Provisions the .NET SDK into bin\$Configuration\dotnet\. + +.DESCRIPTION + The SDK version is read from eng\Versions.props (single source of truth + kept up to date by darc when Microsoft.NET.Sdk flows from dotnet/dotnet), + so global.json does not need a 'tools.dotnet' pin. +#> +[CmdletBinding(PositionalBinding=$false)] +param( + [string][Alias('c')] $configuration = $(if ($env:CONFIGURATION) { $env:CONFIGURATION } else { 'Debug' }) +) + +$ErrorActionPreference = 'Stop' + +$scriptroot = Split-Path -Parent $MyInvocation.MyCommand.Path +$repoRoot = (Resolve-Path (Join-Path $scriptroot '..')).Path + +$versionsProps = Join-Path $repoRoot 'eng\Versions.props' +[xml] $versionsXml = Get-Content -LiteralPath $versionsProps +$sdkVersion = $versionsXml.SelectSingleNode('//MicrosoftNETSdkPackageVersion').InnerText +if ([string]::IsNullOrWhiteSpace($sdkVersion)) { + Write-Error "Could not read from $versionsProps" + exit 1 +} + +$installDir = Join-Path $repoRoot "bin\$configuration\dotnet" +New-Item -ItemType Directory -Force -Path $installDir | Out-Null + +# Download Microsoft's official dotnet-install.ps1 (cached under $installDir +# to avoid hitting the CDN on idempotent re-runs). +$installScript = Join-Path $installDir 'dotnet-install.ps1' +if (-not (Test-Path $installScript)) { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + Invoke-WebRequest -Uri 'https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1' -OutFile $installScript -UseBasicParsing +} + +Write-Host "Installing .NET SDK $sdkVersion into $installDir" +& $installScript -Version $sdkVersion -InstallDir $installDir -NoPath +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} diff --git a/eng/install-dotnet.sh b/eng/install-dotnet.sh new file mode 100644 index 00000000000..da3fe042a62 --- /dev/null +++ b/eng/install-dotnet.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# +# Provisions the .NET SDK into bin/$Configuration/dotnet/. +# +# The SDK version is read from eng/Versions.props (single source of truth +# kept up to date by darc when Microsoft.NET.Sdk flows from dotnet/dotnet), +# so global.json does not need a 'tools.dotnet' pin. +# +# Inputs (env vars): +# CONFIGURATION - Debug (default) or Release; controls install path. +# + +set -euo pipefail + +scriptroot="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +repo_root="$( cd -P "$scriptroot/.." && pwd )" + +configuration="${CONFIGURATION:-Debug}" + +versions_props="$repo_root/eng/Versions.props" +sdk_version="$(sed -n 's|.*\([^<]*\).*|\1|p' "$versions_props" | head -n 1)" +if [[ -z "$sdk_version" ]]; then + echo "error: could not read from $versions_props" >&2 + exit 1 +fi + +install_dir="$repo_root/bin/$configuration/dotnet" +mkdir -p "$install_dir" + +# Download Microsoft's official dotnet-install.sh (cached under +# $install_dir to avoid hitting the CDN on idempotent re-runs). Invoke +# via `bash` so the executable bit isn't needed (Windows clones often +# strip it). +install_script="$install_dir/dotnet-install.sh" +if [[ ! -f "$install_script" ]]; then + curl -fsSL "https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh" -o "$install_script" +fi + +echo "Installing .NET SDK $sdk_version into $install_dir" +bash "$install_script" --version "$sdk_version" --install-dir "$install_dir" --no-path