From 3a3efba85b983b1b46e64d366c6e701d9562d2c7 Mon Sep 17 00:00:00 2001 From: knightmare2600 Date: Mon, 10 Aug 2026 09:37:59 +0100 Subject: [PATCH 1/4] Add native windows arm64 support to the build/CI wiring Adds arm64 to the windows build matrix (tools/ci.py) and fixes the architecture-detection and build-plumbing bugs that blocked it from actually working: - tools/pkg/build.py: arm64 accepted by the windows build command's --arch choices; fixed onedir-dependencies wrongly remapping arm64 -> aarch64 for windows too - that's a linux-only relenv convention, and applying it to windows made relenv reject the arch outright. - pkg/windows/build.ps1, build_python.ps1: ValidateSet now allows arm64. build_python.ps1's arch mapping was a binary if-x64-else-x86, so arm64 silently fell into the x86 branch. - pkg/windows/prep_salt.ps1, install_vs_buildtools.ps1, nsis/build_pkg.ps1: architecture was derived from platform.architecture()[0] (pointer width only - "64bit" for both amd64 and arm64), switched to platform.machine(). No native arm64 build of ssm.exe or the VC++ redistributable exists in salt-windows-deps yet; prep_salt.ps1 falls back to the x64 builds, which run under Windows 11 on Arm's x64 emulation, tracked separately from the true reported architecture via DOWNLOAD_ARCH. install_vs_buildtools.ps1 also had a VS-detection gap: it relies solely on the MSFT_VSInstance CIM class, which isn't registered on the windows-11-arm runner image even though VS 2022 ships pre-installed there - it was wrongly concluding VS was missing and bootstrapping an unrelated VS 2017 install from scratch. Falls back to vswhere.exe, present on every GitHub-hosted Windows image regardless of CIM provider support. - build-salt-onedir.yml, build-deps-ci-action.yml: windows jobs route the arm64 matrix entry to the windows-11-arm hosted runner. The dependency-cache job needed this too or it would hard-fail: it runs the onedir's own python to build a venv, and x64 runners cannot execute arm64 binaries at all (no emulation in that direction). --- .github/workflows/build-deps-ci-action.yml | 2 +- .github/workflows/build-salt-onedir.yml | 24 +++++++++++++++++++- pkg/windows/build_python.ps1 | 7 ++++-- pkg/windows/install_vs_buildtools.ps1 | 19 ++++++++++++++-- pkg/windows/nsis/build_pkg.ps1 | 12 +++++++--- pkg/windows/prep_salt.ps1 | 26 +++++++++++++++++----- tools/ci.py | 1 + tools/pkg/build.py | 14 ++++++++++-- 8 files changed, 89 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-deps-ci-action.yml b/.github/workflows/build-deps-ci-action.yml index 0e4957cb20cc..2cd933025d18 100644 --- a/.github/workflows/build-deps-ci-action.yml +++ b/.github/workflows/build-deps-ci-action.yml @@ -250,7 +250,7 @@ jobs: windows-dependencies: name: Windows - runs-on: windows-latest + runs-on: ${{ matrix.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }} if: ${{ toJSON(fromJSON(inputs.matrix)['windows']) != '[]' }} env: USE_S3_CACHE: 'false' diff --git a/.github/workflows/build-salt-onedir.yml b/.github/workflows/build-salt-onedir.yml index 992c1e809296..b20472e92485 100644 --- a/.github/workflows/build-salt-onedir.yml +++ b/.github/workflows/build-salt-onedir.yml @@ -167,7 +167,7 @@ jobs: max-parallel: 2 matrix: include: ${{ fromJSON(inputs.matrix)['windows'] }} - runs-on: windows-latest + runs-on: ${{ matrix.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }} env: PIP_INDEX_URL: https://pypi.org/simple USE_S3_CACHE: 'false' @@ -196,6 +196,28 @@ jobs: with: salt-version: "${{ inputs.salt-version }}" + # TEMPORARY: arm64 has no released relenv build to fetch yet (see + # setup-relenv), so this job compiles CPython from source here + # instead, which needs the VC++ toolchain present. Remove alongside + # the setup-relenv arm64 branch once a real release exists. + - name: Install VS Build Tools + if: ${{ matrix.arch == 'arm64' }} + shell: pwsh + run: | + pkg\windows\install_vs_buildtools.ps1 -CICD + + - name: Install ARM64 VC++ Tools + if: ${{ matrix.arch == 'arm64' }} + shell: pwsh + run: | + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vsPath = & $vswhere -latest -products * -property installationPath + $installer = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vs_installer.exe" + & $installer modify --installPath "$vsPath" ` + --add Microsoft.VisualStudio.Component.VC.Tools.ARM64 ` + --quiet --norestart --nocache + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + - name: Setup Relenv id: setup-relenv uses: ./.github/actions/setup-relenv diff --git a/pkg/windows/build_python.ps1 b/pkg/windows/build_python.ps1 index cc5971580877..fd2cfcfbde25 100644 --- a/pkg/windows/build_python.ps1 +++ b/pkg/windows/build_python.ps1 @@ -28,10 +28,11 @@ param( [String] $RelenvVersion, [Parameter(Mandatory=$false)] - [ValidateSet("x64", "x86", "amd64")] + [ValidateSet("x64", "x86", "amd64", "arm64")] [Alias("a")] # The System Architecture to build. "x86" will build a 32-bit installer. - # "x64" will build a 64-bit installer. Default is: x64 + # "x64" will build a 64-bit installer. "arm64" will build a native + # ARM64 installer. Default is: x64 [String] $Architecture = "x64", [Parameter(Mandatory=$false)] @@ -175,6 +176,8 @@ $BLD_PY_BIN = "$BUILD_DIR\Scripts\python.exe" if ( $Architecture -eq "x64" ) { $ARCH = "amd64" +} elseif ( $Architecture -eq "arm64" ) { + $ARCH = "arm64" } else { $ARCH = "x86" } diff --git a/pkg/windows/install_vs_buildtools.ps1 b/pkg/windows/install_vs_buildtools.ps1 index 5988ae5a1aeb..6b10c7a89a10 100644 --- a/pkg/windows/install_vs_buildtools.ps1 +++ b/pkg/windows/install_vs_buildtools.ps1 @@ -97,8 +97,23 @@ try { $VS_INST_LOC = $(Get-CimInstance MSFT_VSInstance -Namespace root/cimv2/vs).InstallLocation $MSBUILD_BIN = $(Get-ChildItem "$VS_INST_LOC\MSBuild\*\Bin\msbuild.exe").FullName } catch { - # If VS is not installed, this is the fallback for this installation - $MSBUILD_BIN = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\msbuild.exe" + # The MSFT_VSInstance CIM class isn't registered on every runner image + # (confirmed absent on windows-11-arm even though VS 2022 ships + # pre-installed there) -- before assuming VS needs to be installed + # from scratch, fall back to vswhere.exe, which is present on every + # GitHub-hosted Windows image regardless of CIM provider support. + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $VS_INST_LOC = $null + if ( Test-Path -Path $vswhere ) { + $VS_INST_LOC = & $vswhere -latest -products * -property installationPath + } + if ( $VS_INST_LOC ) { + $MSBUILD_BIN = $(Get-ChildItem "$VS_INST_LOC\MSBuild\*\Bin\msbuild.exe").FullName + } else { + # Genuinely no VS install found by either method - this is the + # fallback for a from-scratch installation. + $MSBUILD_BIN = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\msbuild.exe" + } } #------------------------------------------------------------------------------- diff --git a/pkg/windows/nsis/build_pkg.ps1 b/pkg/windows/nsis/build_pkg.ps1 index 1734e068e003..b6c01711eb1b 100644 --- a/pkg/windows/nsis/build_pkg.ps1 +++ b/pkg/windows/nsis/build_pkg.ps1 @@ -63,9 +63,15 @@ $PYTHON_BIN = "$SCRIPTS_DIR\python.exe" $PY_VERSION = [Version]((Get-Command $PYTHON_BIN).FileVersionInfo.ProductVersion) $PY_VERSION = "$($PY_VERSION.Major).$($PY_VERSION.Minor)" $NSIS_BIN = "$( ${env:ProgramFiles(x86)} )\NSIS\makensis.exe" -$ARCH = $(. $PYTHON_BIN -c "import platform; print(platform.architecture()[0])") - -if ( $ARCH -eq "64bit" ) { +# platform.architecture()[0] only reports pointer width (64bit/32bit), +# which can't distinguish arm64 from amd64 - both are "64bit". Use +# platform.machine() instead, which reports the actual ISA the running +# interpreter was built for. +$ARCH = $(. $PYTHON_BIN -c "import platform; print(platform.machine())") + +if ( $ARCH -eq "ARM64" ) { + $ARCH = "ARM64" +} elseif ( $ARCH -eq "AMD64" ) { $ARCH = "AMD64" } else { $ARCH = "x86" diff --git a/pkg/windows/prep_salt.ps1 b/pkg/windows/prep_salt.ps1 index 7dea332a91c7..ff9192adfc8e 100644 --- a/pkg/windows/prep_salt.ps1 +++ b/pkg/windows/prep_salt.ps1 @@ -69,15 +69,31 @@ $SITE_PKGS_DIR = "$BUILD_DIR\Lib\site-packages" $PYTHON_BIN = "$SCRIPTS_DIR\python.exe" $PY_VERSION = [Version]((Get-Command $PYTHON_BIN).FileVersionInfo.ProductVersion) $PY_VERSION = "$($PY_VERSION.Major).$($PY_VERSION.Minor)" -$PY_ARCH = $(. $PYTHON_BIN -c "import platform; print(platform.architecture()[0])") +# platform.architecture()[0] only reports pointer width (64bit/32bit), +# which can't distinguish arm64 from amd64 - both are "64bit". Use +# platform.machine() instead, which reports the actual ISA the running +# interpreter was built for. +$PY_MACHINE = $(. $PYTHON_BIN -c "import platform; print(platform.machine())") $DEPS_URL = "https://github.com/saltstack/salt-windows-deps/raw/refs/heads/main" -if ( $PY_ARCH -eq "64bit" ) { +if ( $PY_MACHINE -eq "ARM64" ) { + $ARCH = "arm64" + # No native arm64 build of ssm.exe or vcredist exists in + # salt-windows-deps yet. Windows 11 on Arm runs x64 binaries under + # emulation, so fall back to the x64 builds until native ones are + # published upstream. DOWNLOAD_ARCH tracks the artifact to fetch, + # separately from ARCH (the true target architecture). + $DOWNLOAD_ARCH = "x64" + $SSM_URL = "$DEPS_URL/ssm/64/ssm-2.24-103-gdee49fc.exe" + $VCREDIST_URL = "$DEPS_URL/vcredist" +} elseif ( $PY_MACHINE -eq "AMD64" ) { $ARCH = "x64" + $DOWNLOAD_ARCH = "x64" $SSM_URL = "$DEPS_URL/ssm/64/ssm-2.24-103-gdee49fc.exe" $VCREDIST_URL = "$DEPS_URL/vcredist" } else { $ARCH = "x86" + $DOWNLOAD_ARCH = "x86" $SSM_URL = "$DEPS_URL/ssm/32/ssm-2.24-103-gdee49fc.exe" $VCREDIST_URL = "$DEPS_URL/vcredist" } @@ -155,7 +171,7 @@ if ( $PKG ) { # Make sure ssm.exe is present. This is needed for VMtools if ( ! (Test-Path -Path "$BUILD_DIR\ssm.exe") ) { - Write-Host "Copying SSM $ARCH to Root: " -NoNewline + Write-Host "Copying SSM $DOWNLOAD_ARCH to Root: " -NoNewline Invoke-WebRequest -Uri "$SSM_URL" -OutFile "$BUILD_DIR\ssm.exe" if ( Test-Path -Path "$BUILD_DIR\ssm.exe" ) { Write-Result "Success" -ForegroundColor Green @@ -185,8 +201,8 @@ $scripts | ForEach-Object { # Copy VCRedist 2022 to the prereqs directory New-Item -Path $PREREQ_DIR -ItemType Directory | Out-Null -Write-Host "Copying VCRedist 2022 $ARCH to prereqs: " -NoNewline -$file = "vcredist_$ARCH`_2022.exe" +Write-Host "Copying VCRedist 2022 $DOWNLOAD_ARCH to prereqs: " -NoNewline +$file = "vcredist_$DOWNLOAD_ARCH`_2022.exe" Invoke-WebRequest -Uri "$VCREDIST_URL\$file" -OutFile "$PREREQ_DIR\$file" if ( Test-Path -Path "$PREREQ_DIR\$file" ) { Write-Result "Success" -ForegroundColor Green diff --git a/tools/ci.py b/tools/ci.py index 1439f4c3e606..2f2bab324a97 100644 --- a/tools/ci.py +++ b/tools/ci.py @@ -160,6 +160,7 @@ def _build_matrix(os_kind, linux_arm_runner): _matrix = [ {"arch": "amd64"}, {"arch": "x86"}, + {"arch": "arm64"}, ] elif os_kind == "macos": _matrix.append({"arch": "arm64"}) diff --git a/tools/pkg/build.py b/tools/pkg/build.py index 8fa97d1773bf..58633cedc7d5 100644 --- a/tools/pkg/build.py +++ b/tools/pkg/build.py @@ -526,7 +526,7 @@ def macos( }, "arch": { "help": "The architecture to build the package for", - "choices": ("x86", "amd64"), + "choices": ("x86", "amd64", "arm64"), "required": True, }, "sign": { @@ -763,7 +763,9 @@ def onedir_dependencies( if platform == "darwin": platform = "macos" - if platform != "macos" and arch == "arm64": + if platform == "linux" and arch == "arm64": + # relenv's linux arches use "aarch64"; macos and windows both use + # "arm64" literally (confirmed against relenv's arches dict). arch = "aarch64" shared_constants = tools.utils.get_cicd_shared_context() @@ -845,6 +847,14 @@ def onedir_dependencies( # Cryptography needs openssl dir set to link to the proper openssl libs. if platform == "macos": env["OPENSSL_DIR"] = f"{dest}" + elif platform == "windows" and arch == "arm64": + # No prebuilt wheel exists for cryptography on this target yet, + # so pip falls back to compiling it, and openssl-sys has no + # other way to find an OpenSSL to link against. relenv bundles a + # copy of the OpenSSL dev tree it built from source for arm64 + # into the onedir at OpenSSL/ for exactly this. amd64/x86 always + # have a prebuilt wheel and never reach this build path. + env["OPENSSL_DIR"] = str(dest / "OpenSSL") if platform == "linux": # This installs the ppbt package. We'll remove it after installing all From 70a3bde28839d995a21918c746e37b59c2c43c5e Mon Sep 17 00:00:00 2001 From: knightmare2600 Date: Mon, 10 Aug 2026 09:38:09 +0100 Subject: [PATCH 2/4] Exclude pymssql on windows arm64 pymssql has no win_arm64 wheel at any version, and Salt's onedir build forbids compiling it from source (--only-binary), so pip has nothing to install and the arm64 onedir build fails outright: "Could not find a version that satisfies the requirement pymssql==2.3.11 (from versions: none)". Add a platform_machine != 'ARM64' marker so pip just skips it there instead. The mssql execution/state module is unavailable on arm64 minions until pymssql publishes an arm64 build; every other module is unaffected. --- requirements/base.txt | 10 +++++++--- requirements/static/pkg/py3.14/windows.lock | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index fc22f9c4b27f..3f78d7a23849 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -77,9 +77,13 @@ pycparser>=2.23,<3.0; python_version < '3.10' pycparser>=3.0; python_version >= '3.10' # pymssql 2.3.12+ dropped win32 (32-bit Windows) wheels; 3008.x still # builds a Windows x86 onedir so keep the pin at the last release that -# ships cp3X-win32 wheels. -pymssql>=2.2.1,<=2.3.11; sys_platform == 'win32' and python_version < '3.11' -pymssql==2.3.11; sys_platform == 'win32' and python_version >= '3.11' +# ships cp3X-win32 wheels. No win_arm64 wheel exists at all (from any +# version) and the build forbids compiling it from source (it links +# FreeTDS), so exclude it there entirely rather than fail the build - +# the mssql execution/state module is simply unavailable on arm64 until +# pymssql publishes an arm64 build. +pymssql>=2.2.1,<=2.3.11; sys_platform == 'win32' and python_version < '3.11' and platform_machine != 'ARM64' +pymssql==2.3.11; sys_platform == 'win32' and python_version >= '3.11' and platform_machine != 'ARM64' # pyopenssl 26.3.0 requires cryptography>=49 which drops Python 3.9; keep the # last 3.9-compatible release there and let py>=3.10 float forward. pyopenssl>=26.2.0,<26.3.0; python_version < '3.10' diff --git a/requirements/static/pkg/py3.14/windows.lock b/requirements/static/pkg/py3.14/windows.lock index 330b830ddff0..5bb39777e1d7 100644 --- a/requirements/static/pkg/py3.14/windows.lock +++ b/requirements/static/pkg/py3.14/windows.lock @@ -168,8 +168,10 @@ pycryptodomex==3.23.0 # -r requirements/crypto.txt pygments==2.19.2 # via rich -pymssql==2.3.11 +pymssql==2.3.11; platform_machine != 'ARM64' # via -r requirements/base.txt + # No win_arm64 wheel exists and the build forbids compiling it from + # source (links FreeTDS) - see requirements/base.txt. pyopenssl==26.2.0 # via -r requirements/base.txt python-dateutil==2.9.0.post0 From 4156047505f8b32197dd2e7b9d07e7c6e56ebdb8 Mon Sep 17 00:00:00 2001 From: knightmare2600 Date: Mon, 10 Aug 2026 09:38:19 +0100 Subject: [PATCH 3/4] Surface pip's real output/exit code in install_salt.ps1 Installing Salt ran pip via Start-Process -WindowStyle Hidden with no output redirection at all, so any pip failure showed only bare "Failed" with zero diagnostic information - success was inferred solely from whether salt-minion.exe existed afterward, not from pip's actual exit code. A real gap for every architecture, found while diagnosing arm64 onedir build failures. --- pkg/windows/install_salt.ps1 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pkg/windows/install_salt.ps1 b/pkg/windows/install_salt.ps1 index bb31b6c6e57f..38b09d8d2ba2 100644 --- a/pkg/windows/install_salt.ps1 +++ b/pkg/windows/install_salt.ps1 @@ -257,14 +257,23 @@ if ( ! $SkipInstall ) { New-Item -Path .\doc\man\salt-cp.1 -Type File -Force | Out-Null New-Item -Path .\doc\man\salt-minion.1 -Type File -Force | Out-Null } + # Start-Process -WindowStyle Hidden discards pip's output entirely, so a + # failure here used to show only "Failed" with no indication of why. + # Push-Location/call-operator streams pip's real output to the log + # instead, and $LASTEXITCODE lets us fail on the actual pip error + # rather than only inferring failure from a missing salt-minion.exe. + Push-Location "$PROJECT_DIR" try { $env:RELENV_PIP_DIR = "yes" - Start-Process -FilePath $SCRIPTS_DIR\pip3.exe ` - -ArgumentList "install", $InstallPath ` - -WorkingDirectory "$PROJECT_DIR" ` - -Wait -WindowStyle Hidden + & "$SCRIPTS_DIR\pip3.exe" install $InstallPath + $pipExitCode = $LASTEXITCODE } finally { Remove-Item env:\RELENV_PIP_DIR + Pop-Location + } + if ( $pipExitCode -ne 0 ) { + Write-Result "Failed" -ForegroundColor Red + exit 1 } if ( Test-Path -Path "$BUILD_DIR\salt-minion.exe" ) { Write-Result "Success" -ForegroundColor Green From a1b61b223904ae0e607791a9f8f9cdbbedf141bc Mon Sep 17 00:00:00 2001 From: knightmare2600 Date: Mon, 10 Aug 2026 09:39:45 +0100 Subject: [PATCH 4/4] Make the MSI build architecture-aware for arm64 pkg/windows/build.ps1, msi/build_pkg.ps1: architecture was derived from platform.architecture()[0] (pointer width only - "64bit" for both amd64 and arm64), switched to platform.machine(). The WIN64/ ARCHITECTURE/ARCH_AKA/PROGRAMFILES parallel arrays used to drive WiX extended to a real third (arm64) value so the resulting MSI is correctly tagged as native ARM64 rather than mislabeled AMD64. installer.nsi's own architecture detection (separate from build_pkg.ps1's) only recognizes x64/AMD64/x86 and silently falls back to x86 for anything else, producing an installer whose filename build_pkg.ps1 can't find - build.ps1 runs NSIS before the MSI and aborts the whole script on failure, so this blocked the MSI build entirely. NSIS was never actually requested for arm64; skip building it there rather than widen scope into fixing installer.nsi for a component nobody asked for (build-packages.yml's NSIS upload step skips accordingly, since there's nothing to upload). Product.wxs never declared Package/@Platform at all. WiX's implicit default handles the existing amd64/x86 builds fine (this pipeline has shipped MSIs for years without it) but doesn't recognize arm64 as a value to infer from candle's -arch switch, silently defaulting to x86 - light.exe then rejected the 64-bit VC++ CRT merge module with "is a 64-bit merge module but the product consuming it is 32-bit." Declare it explicitly via an ARCHITECTURE preprocessor define build_pkg.ps1 passes to candle (exactly the string passed to -arch), removing the ambiguity for every architecture rather than relying on inference for whichever ones WiX happens to recognize. Separately: merge modules enforce exact architecture matching at build time (a Windows Installer rule, not a WiX limitation) - an x64 merge module cannot be merged into an arm64-declared product even though the resulting DLLs run fine under Windows 11 on Arm's x64 emulation. No native arm64 VC++ CRT merge module is published anywhere yet, so for arm64, embed vcredist_x64_2022.exe (the standalone installer, for the same reason already relied on as a fallback elsewhere in this pipeline) as Binary data and launch it via a deferred CustomAction after InstallFiles instead of merging an .msm. amd64/x86 are unaffected - still use the existing merge-module path. --- .github/workflows/build-packages.yml | 6 +++- pkg/windows/build.ps1 | 26 ++++++++++++----- pkg/windows/msi/Product.wxs | 42 +++++++++++++++++++++++++++- pkg/windows/msi/build_pkg.ps1 | 40 ++++++++++++++++++++------ 4 files changed, 96 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index ac0caa430d96..f50bd279c6e4 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -482,7 +482,7 @@ jobs: matrix: include: ${{ fromJSON(inputs.matrix)['windows'] }} runs-on: - - windows-latest + - ${{ matrix.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }} env: SM_HOST: "${{ secrets.WIN_SIGN_HOST_PROD }}" SM_API_KEY: "${{ secrets.WIN_SIGN_API_KEY }}" @@ -585,6 +585,10 @@ jobs: fi - name: Upload ${{ matrix.arch }} NSIS Packages + # TEMPORARY: build.ps1 skips NSIS for arm64 (installer.nsi's own + # architecture detection doesn't support it yet, and NSIS was + # never actually requested for this target) - see build.ps1. + if: ${{ matrix.arch != 'arm64' }} uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: ${{ steps.set-artifact-name.outputs.artifact-name-nsis }} diff --git a/pkg/windows/build.ps1 b/pkg/windows/build.ps1 index 70c0400a0885..71bd9c9d0aa4 100644 --- a/pkg/windows/build.ps1 +++ b/pkg/windows/build.ps1 @@ -30,10 +30,11 @@ param( [String] $Version, [Parameter(Mandatory=$false)] - [ValidateSet("x86", "x64", "amd64")] + [ValidateSet("x86", "x64", "amd64", "arm64")] [Alias("a")] # The System Architecture to build. "x86" will build a 32-bit installer. - # "x64" will build a 64-bit installer. Default is: x64 + # "x64" will build a 64-bit installer. "arm64" will build a native + # ARM64 installer. Default is: x64 $Architecture = "x64", [Parameter(Mandatory=$false)] @@ -250,11 +251,22 @@ if ( $CICD ) { $KeywordArguments["CICD"] = $true } -& "$SCRIPT_DIR\nsis\build_pkg.ps1" @KeywordArguments - -if ( ! $? ) { - Write-Host "Failed to build NSIS package" - exit 1 +# TEMPORARY: the .nsi script's own architecture detection (CPUARCH, +# separate from this script's) only recognizes x64/AMD64/x86 and +# silently falls back to x86 for anything else, producing an installer +# whose filename build_pkg.ps1 then can't find. NSIS was never actually +# requested for arm64 - skip it there rather than widen scope into +# auditing/fixing the .nsi script for a component nobody asked for. +# $KeywordArguments is reused below for the MSI build either way. +if ( $Architecture -eq "arm64" ) { + Write-Host "Skipping NSIS package for arm64 (not yet supported by installer.nsi)" +} else { + & "$SCRIPT_DIR\nsis\build_pkg.ps1" @KeywordArguments + + if ( ! $? ) { + Write-Host "Failed to build NSIS package" + exit 1 + } } #------------------------------------------------------------------------------- diff --git a/pkg/windows/msi/Product.wxs b/pkg/windows/msi/Product.wxs index 6190dba297f6..05b8eb20a75f 100644 --- a/pkg/windows/msi/Product.wxs +++ b/pkg/windows/msi/Product.wxs @@ -22,11 +22,20 @@ IMCAC - Immediate Custom Action - It's immediate Language = "1033"> + @@ -246,6 +255,11 @@ IMCAC - Immediate Custom Action - It's immediate WIX_UPGRADE_DETECTED WIX_UPGRADE_DETECTED + + + (NOT VCREDIST_INSTALLED) AND (NOT Installed) + + @@ -271,14 +285,26 @@ IMCAC - Immediate Custom Action - It's immediate + + - + @@ -286,15 +312,29 @@ IMCAC - Immediate Custom Action - It's immediate + + + + + + + + diff --git a/pkg/windows/msi/build_pkg.ps1 b/pkg/windows/msi/build_pkg.ps1 index eaa612c0666c..33c8167fe8d7 100644 --- a/pkg/windows/msi/build_pkg.ps1 +++ b/pkg/windows/msi/build_pkg.ps1 @@ -82,12 +82,18 @@ $SCRIPTS_DIR = "$BUILDENV_DIR\Scripts" $SITE_PKGS_DIR = "$BUILDENV_DIR\Lib\site-packages" $BUILD_SALT_DIR = "$SITE_PKGS_DIR\salt" $PYTHON_BIN = "$SCRIPTS_DIR\python.exe" -$BUILD_ARCH = $(. $PYTHON_BIN -c "import platform; print(platform.architecture()[0])") +# platform.architecture()[0] only reports pointer width (64bit/32bit), +# which can't distinguish arm64 from amd64 - both are "64bit". Use +# platform.machine() instead, which reports the actual ISA the running +# interpreter was built for. +$BUILD_ARCH = $(. $PYTHON_BIN -c "import platform; print(platform.machine())") $SCRIPT_DIR = (Get-ChildItem "$($myInvocation.MyCommand.Definition)").DirectoryName $RUNTIME_DIR = [System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory() $CSC_BIN = "$RUNTIME_DIR\csc.exe" -if ( $BUILD_ARCH -eq "64bit" ) { +if ( $BUILD_ARCH -eq "ARM64" ) { + $BUILD_ARCH = "ARM64" +} elseif ( $BUILD_ARCH -eq "AMD64" ) { $BUILD_ARCH = "AMD64" } else { $BUILD_ARCH = "x86" @@ -169,6 +175,17 @@ $RUNTIMES | ForEach-Object { VerifyOrDownload "$WEBCACHE_DIR\$name" "$DEPS_URL/$name" "$hash" } +if ( $BUILD_ARCH -eq "ARM64" ) { + # Merge modules enforce exact architecture matching at build time + # (a Windows Installer rule, not a WiX limitation) - an x64 merge + # module cannot be merged into an arm64-declared product even + # though the resulting DLLs run fine under Windows 11 on Arm's x64 + # emulation. No native arm64 merge module is published anywhere + # yet, so arm64 launches the standalone installer EXE via a + # CustomAction (see Product.wxs) instead of merging an .msm. + VerifyOrDownload "$WEBCACHE_DIR\vcredist_x64_2022.exe" "$DEPS_URL/vcredist_x64_2022.exe" "1AD7988C17663CC742B01BEF1A6DF2ED1741173009579AD50A94434E54F56073" +} + #------------------------------------------------------------------------------- # Converting to MSI Version #------------------------------------------------------------------------------- @@ -195,15 +212,19 @@ $MANUFACTURER = "Salt Project" $PRODUCT = "Salt Minion" $PRODUCTFILE = "Salt-Minion-$Version" $PRODUCTDIR = "Salt" -$DISCOVER_INSTALLDIR = "$BUILDENV_DIR", "$BUILDENV_DIR" +$DISCOVER_INSTALLDIR = "$BUILDENV_DIR", "$BUILDENV_DIR", "$BUILDENV_DIR" $DISCOVER_CONFDIR = Get-Item "$BUILDENV_DIR\configs" -# MSI related arrays for 64 and 32 bit values, selected by BUILD_ARCH -if ($BUILD_ARCH -eq "AMD64") {$i = 0} else {$i = 1} -$WIN64 = "yes", "no" # Used in wxs -$ARCHITECTURE = "x64", "x86" # WiX dictionary values -$ARCH_AKA = "AMD64", "x86" # For filename -$PROGRAMFILES = "ProgramFiles64Folder", "ProgramFilesFolder" # msi dictionary values +# MSI related arrays for 64, 32, and arm64 bit values, selected by BUILD_ARCH. +# arm64 reuses the amd64 WIN64/PROGRAMFILES values (it's still a native +# 64-bit package installing under the 64-bit Program Files) but needs its +# own WiX -arch tag and filename tag so the resulting MSI is correctly +# identified as ARM64 rather than mislabeled as AMD64. +if ($BUILD_ARCH -eq "ARM64") {$i = 2} elseif ($BUILD_ARCH -eq "AMD64") {$i = 0} else {$i = 1} +$WIN64 = "yes", "no", "yes" # Used in wxs +$ARCHITECTURE = "x64", "x86", "arm64" # WiX dictionary values +$ARCH_AKA = "AMD64", "x86", "ARM64" # For filename +$PROGRAMFILES = "ProgramFiles64Folder", "ProgramFilesFolder", "ProgramFiles64Folder" # msi dictionary values function CheckExitCode() { # Exit on failure if ($LastExitCode -ne 0) { @@ -521,6 +542,7 @@ Write-Host "Compiling *.wxs to $($ARCHITECTURE[$i]) *.wixobj: " -NoNewline Push-Location $SCRIPT_DIR & "$($ENV:WIX)bin\candle.exe" -nologo -sw1150 ` -arch $ARCHITECTURE[$i] ` + -dARCHITECTURE="$($ARCHITECTURE[$i])" ` -dWIN64="$($WIN64[$i])" ` -dPROGRAMFILES="$($PROGRAMFILES[$i])" ` -dMANUFACTURER="$MANUFACTURER" `