Add Windows ARM64 support to CI (#5544)

Summary:
Hi team, I'm from Microsoft and recently I'm working on improving Python ecosystem support for Windows on Arm.
So I updates the CI workflows to add support for Windows ARM64 build and release. Could you please help to review? Thanks.

**Windows ARM64 support and OpenBLAS installation improvements:**

* Added a new PowerShell script `install-openblas.ps1` that automatically detects the runner architecture (x64 or ARM64), downloads the appropriate OpenBLAS binary, verifies its hash, and installs it to `C:\openblas`. This ensures consistent and secure OpenBLAS setup on all Windows runners.
* Updated the build matrix in `.github/workflows/build-pip.yml` to include `windows-11-arm`, enabling CI builds and tests on Windows ARM64 runners.
* Refactored the Windows `before-all` build step in `pyproject.toml` to use the new PowerShell script, replacing the previous hardcoded x64-only download command. This allows seamless support for both x64 and ARM64 architectures.

Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5544

Reviewed By: mnorris11

Differential Revision: D117818430

Pulled By: trang-nm-nguyen

fbshipit-source-id: dac342878d063828437952e1b19f76e6699e1b6b
This commit is contained in:
Chao Zhang
2026-08-28 10:06:58 -07:00
committed by meta-codesync[bot]
parent 02ea14372c
commit 3a716ead1a
3 changed files with 132 additions and 6 deletions
+112
View File
@@ -0,0 +1,112 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
param(
[string]$InstallPath = "C:\openblas"
)
$ErrorActionPreference = "Stop"
# Windows PowerShell 5.1 redraws the progress bar on every read block, which
# costs an order of magnitude of wall-clock on a download this size.
$ProgressPreference = "SilentlyContinue"
Set-StrictMode -Version Latest
# Bumping OpenBLAS means changing $openBlasVersion and every Hash below.
# Regenerate a hash with: Get-FileHash -Algorithm SHA256 <downloaded zip>
$openBlasVersion = "0.3.30"
$packages = @{
arm64 = @{
Asset = "woa64-dll"
Hash = "5BBD8C6CA5A4C415FF58D5E18378B35BC1E81F46E6C385753EFF367FF7474819"
Subdirectory = "OpenBLAS"
}
x64 = @{
Asset = "x64"
Hash = "8B04387766EFC05C627E26D24797EC0D4ED4C105EC14FA7400AA84A02DB22B66"
Subdirectory = ""
}
}
$nativeArchitectures = @(
$env:PROCESSOR_ARCHITECTURE
$env:PROCESSOR_ARCHITEW6432
)
if ($nativeArchitectures -contains "ARM64") {
$architecture = "arm64"
} elseif ($nativeArchitectures -contains "AMD64") {
$architecture = "x64"
} else {
throw "Unsupported Windows architecture: $($nativeArchitectures -join ', ')"
}
$package = $packages[$architecture]
$archiveUrl = "https://github.com/OpenMathLib/OpenBLAS/releases/download/v$openBlasVersion/OpenBLAS-$openBlasVersion-$($package.Asset).zip"
$expectedHash = $package.Hash
$archiveSubdirectory = $package.Subdirectory
$workingPath = Join-Path ([System.IO.Path]::GetTempPath()) "faiss-openblas-$architecture-$PID"
$archivePath = Join-Path $workingPath "openblas.zip"
$extractPath = Join-Path $workingPath "extract"
New-Item -ItemType Directory -Path $workingPath | Out-Null
try {
# A blip here aborts before-all, losing every interpreter's wheel in this
# job. Retrying is safe: the hash check below rejects a partial download.
# -UseBasicParsing keeps PS 5.1 off the Internet Explorer parsing engine,
# which is absent on freshly provisioned images.
for ($attempt = 1; $attempt -le 3; $attempt++) {
try {
Invoke-WebRequest -Uri $archiveUrl -OutFile $archivePath -UseBasicParsing
break
} catch {
if ($attempt -eq 3) {
throw
}
Start-Sleep -Seconds (5 * $attempt)
}
}
$archiveStream = [System.IO.File]::OpenRead($archivePath)
try {
$hasher = [System.Security.Cryptography.SHA256]::Create()
try {
$actualHash = [System.BitConverter]::ToString(
$hasher.ComputeHash($archiveStream)
).Replace("-", "")
} finally {
$hasher.Dispose()
}
} finally {
$archiveStream.Dispose()
}
if ($actualHash -ne $expectedHash) {
throw "OpenBLAS archive hash mismatch: expected $expectedHash, got $actualHash"
}
Expand-Archive -LiteralPath $archivePath -DestinationPath $extractPath
$sourcePath = if ($archiveSubdirectory) {
Join-Path $extractPath $archiveSubdirectory
} else {
$extractPath
}
foreach ($subdirectory in @("bin", "include", "lib")) {
if (-not (Test-Path -LiteralPath (Join-Path $sourcePath $subdirectory))) {
throw "OpenBLAS archive is missing the '$subdirectory' directory"
}
}
if (Test-Path -LiteralPath $InstallPath) {
Remove-Item -LiteralPath $InstallPath -Recurse -Force
}
Move-Item -LiteralPath $sourcePath -Destination $InstallPath
} finally {
if (Test-Path -LiteralPath $workingPath) {
Remove-Item -LiteralPath $workingPath -Recurse -Force
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-14, macos-15-intel, windows-2022, 2-core-ubuntu-arm]
os: [ubuntu-latest, macos-14, macos-15-intel, windows-2022, windows-11-arm, 2-core-ubuntu-arm]
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+19 -5
View File
@@ -119,9 +119,11 @@ cmake.define.FAISS_OPT_LEVEL = "generic"
[tool.cibuildwheel]
# Build all Python versions on Windows (no abi3); cp310 only on Linux/macOS (abi3).
# Skip 32-bit and free-threaded builds. musllinux is built (see override below).
# Skip Windows arm64 for Python 3.10 because Python 3.10 is not available as a
# supported Windows arm64 target. Also skip 32-bit and free-threaded builds.
# musllinux is built below.
build = "cp310-* cp311-* cp312-* cp313-* cp314-*"
skip = "*-win32 *-manylinux_i686 cp3*t-*"
skip = "cp310-win_arm64 *-win32 *-manylinux_i686 cp3*t-*"
test-requires = ["numpy", "pytest", "scipy"]
test-command = "python -m pytest {project}/tests/test_wheel_smoke.py -v"
@@ -167,14 +169,26 @@ environment = {MACOSX_DEPLOYMENT_TARGET = "14.0"}
select = "*-macosx_x86_64"
environment = {MACOSX_DEPLOYMENT_TARGET = "15.0"}
# Windows arm64: drop scipy for the same reason as musllinux above — the smoke
# test does not import it, and a missing win_arm64 scipy wheel would force a
# failing sdist build (needs a Fortran toolchain and a BLAS on the runner).
[[tool.cibuildwheel.overrides]]
select = "*-win_arm64"
test-requires = ["numpy", "pytest"]
[tool.cibuildwheel.windows]
# Install prebuilt OpenBLAS (statically-linked MinGW runtime, no external deps).
before-all = "powershell -Command \"Invoke-WebRequest -Uri 'https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.30/OpenBLAS-0.3.30-x64.zip' -OutFile openblas.zip; Expand-Archive openblas.zip -DestinationPath C:/openblas -Force\""
# Install the native x64 or arm64 OpenBLAS package for the runner.
# delvewheel only has to vendor the OpenBLAS DLL itself, because the x64 package
# links the MinGW runtime statically. Re-check that property when bumping the
# OpenBLAS version or adding an architecture: `delvewheel show` on a repaired
# wheel lists what was vendored, and the PATH below hides a missed DLL from the
# smoke test, so a wheel broken this way still passes CI.
before-all = 'powershell -NoProfile -ExecutionPolicy Bypass -File "{package}\.github\scripts\install-openblas.ps1"'
repair-wheel-command = "pip install delvewheel && delvewheel repair -w {dest_dir} {wheel} --add-path C:/openblas/bin --ignore-in-wheel"
[tool.cibuildwheel.windows.environment]
# BLAS_ROOT tells CMake's FindBLAS (via CMP0074) where to find OpenBLAS.
# PATH includes the bin/ dir so delvewheel and tests can find libopenblas.dll.
# PATH includes the bin/ dir so delvewheel and tests can find the OpenBLAS DLL.
BLAS_ROOT = "C:/openblas"
LAPACK_ROOT = "C:/openblas"
PATH = "C:/openblas/bin;$PATH"