diff --git a/eng/restore-toolset.ps1 b/eng/restore-toolset.ps1 index 12f8be7c5e8b..082b08b39ed1 100644 --- a/eng/restore-toolset.ps1 +++ b/eng/restore-toolset.ps1 @@ -242,11 +242,23 @@ function InstallDotNetSharedFrameworksWithInstallScript([string[]]$runtimeSpecs, $installArgs.Architecture = $architecture } - if (-not (Test-Path Variable:LASTEXITCODE)) { $global:LASTEXITCODE = 0 } + $global:LASTEXITCODE = 0 & $installScript @installArgs - if ($lastExitCode -ne 0) { - throw "Failed to install shared framework $version to '$dotNetRoot' using dotnet install script for architecture '$architecture' (exit code '$lastExitCode')." + $installScriptExitCode = $LASTEXITCODE + + # The install script is a PowerShell script, so $LASTEXITCODE is only updated when it + # invokes a native process or calls `exit`. A successful run that returns normally can + # leave a stale exit code, and a soft failure can leave 0. We reset $LASTEXITCODE above to + # neutralize a stale non-zero (false failure); pair that with a filesystem check that the + # expected shared-framework version actually landed to catch a false success (exit 0 but + # nothing installed). + $sharedFrameworkName = if ($component -eq 'aspnetcore') { 'Microsoft.AspNetCore.App' } elseif ($component -eq 'windowsdesktop') { 'Microsoft.WindowsDesktop.App' } else { 'Microsoft.NETCore.App' } + $frameworkInstalled = Test-Path -PathType Container (Join-Path $dotNetRoot "shared\$sharedFrameworkName\$version*") + + if ($installScriptExitCode -ne 0 -or -not $frameworkInstalled) { + $architectureMessage = if ([string]::IsNullOrEmpty($architecture)) { "" } else { " for architecture '$architecture'" } + throw "Failed to install shared framework $version to '$dotNetRoot' using dotnet install script$architectureMessage (exit code '$installScriptExitCode', installed '$frameworkInstalled')." } } } diff --git a/eng/restore-toolset.sh b/eng/restore-toolset.sh index c25a2dee59d3..9af757af6fa3 100755 --- a/eng/restore-toolset.sh +++ b/eng/restore-toolset.sh @@ -151,13 +151,14 @@ function InstallDotNetSharedFrameworksWithInstallScript { for spec in "$@"; do local component="dotnet" - local install_version="$spec" + local version="$spec" if [[ "$spec" == *@* ]]; then component="${spec%@*}" - install_version="${spec#*@}" + version="${spec#*@}" fi # Map dotnetup channel (e.g. "9.0") to the specific version the install # script's --version parameter expects (e.g. "9.0.0"). + local install_version="$version" if [[ "$install_version" =~ ^[0-9]+\.[0-9]+$ ]]; then install_version="$install_version.0" fi @@ -167,11 +168,45 @@ function InstallDotNetSharedFrameworksWithInstallScript { install_args+=(--architecture "$arch") fi + # Disable errexit around the install-script call so the exit-code and filesystem checks + # below always run (mirrors the dotnetup invocation above). Capture $? before restoring + # errexit, since the restore itself runs commands that overwrite $?. + local restore_errexit=false + if [[ $- == *e* ]]; then + restore_errexit=true + set +e + fi bash "$install_script" "${install_args[@]}" local lastexitcode=$? + if [[ "$restore_errexit" == true ]]; then + set -e + fi + + # bash $? is never stale (unlike PowerShell's $LASTEXITCODE), so the false-failure case + # does not apply here. But a soft failure could still exit 0 without installing, so pair + # the exit code with a filesystem check that the expected shared-framework version landed + # to catch a false success. + local shared_framework_name="Microsoft.NETCore.App" + case "$component" in + aspnetcore) shared_framework_name="Microsoft.AspNetCore.App" ;; + windowsdesktop) shared_framework_name="Microsoft.WindowsDesktop.App" ;; + esac + local framework_installed=false + if compgen -G "$dotnet_root/shared/$shared_framework_name/$version*" > /dev/null 2>&1; then + framework_installed=true + fi + + # Promote a false success (exit 0 but nothing on disk) to a real failure. + if [[ $lastexitcode == 0 && "$framework_installed" != true ]]; then + lastexitcode=1 + fi if [[ $lastexitcode != 0 ]]; then - echo "Failed to install shared framework spec '$spec' to '$dotnet_root' using dotnet install script for architecture '$arch' (exit code '$lastexitcode')." + local architecture_message="" + if [[ -n "$arch" ]]; then + architecture_message=" for architecture '$arch'" + fi + echo "Failed to install shared framework spec '$spec' to '$dotnet_root' using dotnet install script${architecture_message} (exit code '$lastexitcode', installed '$framework_installed')." ExitWithExitCode $lastexitcode fi done