From 00da8e63ebb3bea5cf4382ea37ad1ae5598ac90d Mon Sep 17 00:00:00 2001 From: iremyux Date: Wed, 23 Jul 2025 16:12:17 +0000 Subject: [PATCH] CI for Windows Arm64 (#148753) This pull request adds a new CI workflow for Windows Arm64, named win-arm64-build-test.yml. It can be triggered on any pull request by including the ciflow/win-arm64 tag. Pull Request resolved: https://github.com/pytorch/pytorch/pull/148753 Approved by: https://github.com/malfet --- .ci/pytorch/win-arm64-build.ps1 | 34 ++++ .ci/pytorch/win-arm64-test.sh | 24 +++ .../win-test-helpers/arm64/build_pytorch.ps1 | 98 +++++++++ .github/pytorch-probot.yml | 1 + .github/workflows/win-arm64-build-test.yml | 187 ++++++++++++++++++ 5 files changed, 344 insertions(+) create mode 100644 .ci/pytorch/win-arm64-build.ps1 create mode 100644 .ci/pytorch/win-arm64-test.sh create mode 100644 .ci/pytorch/win-test-helpers/arm64/build_pytorch.ps1 create mode 100644 .github/workflows/win-arm64-build-test.yml diff --git a/.ci/pytorch/win-arm64-build.ps1 b/.ci/pytorch/win-arm64-build.ps1 new file mode 100644 index 00000000000..2cb162b8a30 --- /dev/null +++ b/.ci/pytorch/win-arm64-build.ps1 @@ -0,0 +1,34 @@ +# If you want to rebuild, run this with $env:REBUILD=1 +# If you want to build with CUDA, run this with $env:USE_CUDA=1 +# If you want to build without CUDA, run this with $env:USE_CUDA=0 + +# Check for setup.py in the current directory +if (-not (Test-Path "setup.py")) { + Write-Host "ERROR: Please run this build script from PyTorch root directory." + exit 1 +} + +# Get the script's parent directory +$ScriptParentDir = Split-Path -Parent $MyInvocation.MyCommand.Definition + +# Set TMP_DIR and convert to Windows path +$env:TMP_DIR = Join-Path (Get-Location) "build\win_tmp" +$env:TMP_DIR_WIN = $env:TMP_DIR # Already in Windows format, no cygpath needed + +# Set final package directory with default fallback +if (-not $env:PYTORCH_FINAL_PACKAGE_DIR) { + $env:PYTORCH_FINAL_PACKAGE_DIR = "C:\w\build-results" +} + +# Create the final package directory if it doesn't exist +if (-not (Test-Path $env:PYTORCH_FINAL_PACKAGE_DIR)) { + New-Item -Path $env:PYTORCH_FINAL_PACKAGE_DIR -ItemType Directory -Force | Out-Null +} + +# Set script helpers directory +$env:SCRIPT_HELPERS_DIR = Join-Path $ScriptParentDir "win-test-helpers\arm64" + +# Run the main build script +& "$env:SCRIPT_HELPERS_DIR\build_pytorch.ps1" + +Write-Host "BUILD PASSED" diff --git a/.ci/pytorch/win-arm64-test.sh b/.ci/pytorch/win-arm64-test.sh new file mode 100644 index 00000000000..662c561aa89 --- /dev/null +++ b/.ci/pytorch/win-arm64-test.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -ex -o pipefail + +SCRIPT_PARENT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +# shellcheck source=./common.sh +source "$SCRIPT_PARENT_DIR/common.sh" + +run_tests() { + echo Running smoke_test.py... + python ./.ci/pytorch/smoke_test/smoke_test.py --package torchonly + + echo Running test_autograd.oy, test_nn.py, test_torch.py... + cd test + + CORE_TEST_LIST=("test_autograd.py" "test_nn.py" "test_modules.py") + + for t in "${CORE_TEST_LIST[@]}"; do + echo "Running test: $t" + python "$t" --verbose --save-xml --use-pytest -vvvv -rfEsxXP -p no:xdist + done +} + +run_tests +echo "TEST PASSED" diff --git a/.ci/pytorch/win-test-helpers/arm64/build_pytorch.ps1 b/.ci/pytorch/win-test-helpers/arm64/build_pytorch.ps1 new file mode 100644 index 00000000000..29b3e913439 --- /dev/null +++ b/.ci/pytorch/win-test-helpers/arm64/build_pytorch.ps1 @@ -0,0 +1,98 @@ +# TODO: we may can use existing build_pytorch.bat for arm64 + +if ($env:DEBUG -eq "1") { + $env:BUILD_TYPE = "debug" +} else { + $env:BUILD_TYPE = "release" +} + +# This inflates our log size slightly, but it is REALLY useful to be +# able to see what our cl.exe commands are. (since you can actually +# just copy-paste them into a local Windows setup to just rebuild a +# single file.) +# log sizes are too long, but leaving this here in case someone wants to use it locally +# $env:CMAKE_VERBOSE_MAKEFILE = "1" + +$env:INSTALLER_DIR = Join-Path $env:SCRIPT_HELPERS_DIR "installation-helpers" + +cd .. + +# Environment variables +$env:SCCACHE_IDLE_TIMEOUT = "0" +$env:SCCACHE_IGNORE_SERVER_IO_ERROR = "1" +$env:CMAKE_BUILD_TYPE = $env:BUILD_TYPE +$env:CMAKE_C_COMPILER_LAUNCHER = "sccache" +$env:CMAKE_CXX_COMPILER_LAUNCHER = "sccache" +$env:libuv_ROOT = Join-Path $env:DEPENDENCIES_DIR "libuv\install" +$env:MSSdk = "1" + +if ($env:PYTORCH_BUILD_VERSION) { + $env:PYTORCH_BUILD_VERSION = $env:PYTORCH_BUILD_VERSION + $env:PYTORCH_BUILD_NUMBER = "1" +} + +$env:CMAKE_POLICY_VERSION_MINIMUM = "3.5" + +# Set BLAS type +if ($env:ENABLE_APL -eq "1") { + $env:BLAS = "APL" + $env:USE_LAPACK = "1" +} elseif ($env:ENABLE_OPENBLAS -eq "1") { + $env:BLAS = "OpenBLAS" + $env:OpenBLAS_HOME = Join-Path $env:DEPENDENCIES_DIR "OpenBLAS\install" +} + +# Change to source directory +Set-Location $env:PYTORCH_ROOT + +# Copy libuv.dll +Copy-Item -Path (Join-Path $env:libuv_ROOT "lib\Release\uv.dll") -Destination "torch\lib\uv.dll" -Force + +# Create virtual environment +python -m venv .venv +.\.venv\Scripts\Activate.ps1 +where.exe python + +# Python install dependencies +python -m pip install --upgrade pip +pip install setuptools pyyaml +pip install -r requirements.txt + +# Set after installing psutil +$env:DISTUTILS_USE_SDK = "1" + +# Print all environment variables +Get-ChildItem Env: + +# Start and inspect sccache +sccache --start-server +sccache --zero-stats +sccache --show-stats + +# Build the wheel +python setup.py bdist_wheel +if ($LASTEXITCODE -ne 0) { exit 1 } + +# Install the wheel locally +$whl = Get-ChildItem -Path "dist\*.whl" | Select-Object -First 1 +if ($whl) { + python -mpip install --no-index --no-deps $whl.FullName +} + +# Copy final wheel +robocopy "dist" "$env:PYTORCH_FINAL_PACKAGE_DIR" *.whl + +# Export test times +python tools/stats/export_test_times.py + +# Copy additional CI files +robocopy ".additional_ci_files" "$env:PYTORCH_FINAL_PACKAGE_DIR\.additional_ci_files" /E + +# Save ninja log +Copy-Item -Path "build\.ninja_log" -Destination $env:PYTORCH_FINAL_PACKAGE_DIR -Force + +# Final sccache stats and stop +sccache --show-stats +sccache --stop-server + +exit 0 diff --git a/.github/pytorch-probot.yml b/.github/pytorch-probot.yml index 5288aca8529..a5982b63b70 100644 --- a/.github/pytorch-probot.yml +++ b/.github/pytorch-probot.yml @@ -31,6 +31,7 @@ ciflow_push_tags: - ciflow/pull - ciflow/h100 - ciflow/h100-distributed +- ciflow/win-arm64 - ciflow/h100-symm-mem - ciflow/h100-cutlass-backend retryable_workflows: diff --git a/.github/workflows/win-arm64-build-test.yml b/.github/workflows/win-arm64-build-test.yml new file mode 100644 index 00000000000..627a43b56bf --- /dev/null +++ b/.github/workflows/win-arm64-build-test.yml @@ -0,0 +1,187 @@ +name: windows-arm64-build-test + +on: + push: + tags: + - ciflow/win-arm64/* + +env: + GIT_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + PYTHON_VERSION: "3.12" + PYTORCH_ROOT: ${{ github.workspace }}/pytorch + DOWNLOADS_DIR: c:\temp\downloads + DEPENDENCIES_DIR: c:\temp\dependencies + ENABLE_APL: 1 + ENABLE_OPENBLAS: 0 + BUILD_TYPE: release + +permissions: + id-token: write + contents: read + +jobs: + build: + # Don't run on forked repos. + if: github.repository_owner == 'pytorch' + runs-on: "windows-11-arm64-preview" + timeout-minutes: 240 + steps: + - name: configure aws credentials + id: aws_creds + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::308535385114:role/gha_workflow_sscache + aws-region: us-east-1 + role-duration-seconds: 18000 + + - name: Enable long paths + shell: cmd + run: | + git config --system --get core.longpaths || echo "core.longpaths is not set, setting it now" + git config --system core.longpaths true + + - name: Git checkout PyTorch + uses: actions/checkout@v4 + with: + path: pytorch + submodules: recursive + + - name: Bootstrap Python + shell: cmd + run: | + "pytorch/.ci/pytorch/windows/arm64/bootstrap_python.bat" + + - name: Parse ref + id: parse-ref + shell: bash + run: python pytorch/.github/scripts/parse_ref.py + + - name: Get workflow job id + shell: bash + id: get-job-id + run: | + set -eux + python pytorch/.github/scripts/get_workflow_job_id.py "${GITHUB_RUN_ID}" "${RUNNER_NAME}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Bootstrap APL + shell: cmd + run: | + "pytorch/.ci/pytorch/windows/arm64/bootstrap_apl.bat" + + - name: Bootstrap Rust + shell: cmd + run: | + "pytorch/.ci/pytorch/windows/arm64/bootstrap_rust.bat" + + - name: Bootstrap sccache + shell: cmd + run: | + "pytorch/.ci/pytorch/windows/arm64/bootstrap_sccache.bat" + + - name: Bootstrap Libuv + shell: cmd + run: | + "pytorch/.ci/pytorch/windows/arm64/bootstrap_libuv.bat" + + - name: Build + id: build + shell: cmd + env: + PYTORCH_FINAL_PACKAGE_DIR: C:/${{ github.run_id }}/build-results/ + BRANCH: ${{ steps.parse-ref.outputs.branch }} + BUILD_WHEEL: 1 + MAX_JOBS: 8 + PYTHON_VERSION: "3.12" + SCCACHE_BUCKET: "ossci-compiler-cache" + SCCACHE_S3_KEY_PREFIX: ${{ github.workflow }} + SCCACHE_REGION: us-east-1 + VC_PRODUCT: "BuildTools" + VC_VERSION: "" + ALPINE_IMAGE: "308535385114.dkr.ecr.us-east-1.amazonaws.com/tool/alpine" + AWS_DEFAULT_REGION: us-east-1 + USE_CUDA: '0' + USE_XPU: '0' + OUR_GITHUB_JOB_ID: ${{ steps.get-job-id.outputs.job-id }} + run: | + cd pytorch + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" arm64 + powershell -ExecutionPolicy Bypass -File ".ci/pytorch/win-arm64-build.ps1" + + - name: Upload artifacts + uses: actions/upload-artifact@v4.4.0 + if: always() + with: + name: torch-wheel-win-arm64-py3-12 + retention-days: 14 + if-no-files-found: error + path: C:\${{ github.run_id }}\build-results + + test: + if: github.repository_owner == 'pytorch' + strategy: + fail-fast: false + runs-on: "windows-11-arm64-preview" + needs: build + steps: + - name: Enable long paths + shell: cmd + run: | + git config --system --get core.longpaths || echo "core.longpaths is not set, setting it now" + git config --system core.longpaths true + + - name: Git checkout PyTorch + uses: actions/checkout@v4 + with: + path: pytorch + submodules: recursive + + - name: Bootstrap Python + shell: cmd + run: | + "pytorch/.ci/pytorch/windows/arm64/bootstrap_python.bat" + + - name: Bootstrap Rust + shell: cmd + run: | + "pytorch/.ci/pytorch/windows/arm64/bootstrap_rust.bat" + + - name: Get workflow job id + shell: bash + id: get-job-id + run: | + set -eux + python pytorch/.github/scripts/get_workflow_job_id.py "${GITHUB_RUN_ID}" "${RUNNER_NAME}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Download Build Artifacts + uses: actions/download-artifact@v4.1.7 + with: + name: torch-wheel-win-arm64-py3-12 + path: C:\${{ github.run_id }}\build-results + + - name: Test + id: test + shell: cmd + env: + USE_CUDA: '0' + INSTALL_WINDOWS_SDK: 1 + PYTHON_VERSION: "3.12" + VC_PRODUCT: "BuildTools" + AWS_DEFAULT_REGION: us-east-1 + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_WORKFLOW: ${{ github.workflow }} + GITHUB_JOB: ${{ github.job }} + GITHUB_RUN_ID: ${{ github.run_id }} + GITHUB_RUN_NUMBER: ${{ github.run_number }} + GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }} + JOB_ID: ${{ steps.get-job-id.outputs.job-id }} + JOB_NAME: ${{ steps.get-job-id.outputs.job-name }} + PYTORCH_FINAL_PACKAGE_DIR: C:/${{ github.run_id }}/build-results/ + run: | + mkdir "%PYTORCH_FINAL_PACKAGE_DIR%" + call pytorch/.ci/pytorch/windows/arm64/bootstrap_tests.bat + set GIT_BASH=C:\Program Files\Git\usr\bin\bash.exe + "%GIT_BASH%" -c "bash --noprofile --norc .ci/pytorch/win-arm64-test.sh" \ No newline at end of file