Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .ci/scripts/wheel/cuda_arch_list.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

# GPU architectures to compile device code for, chosen per release row rather than detected
# from the build machine.
#
# Without this, the build compiles for whichever GPU the builder happens to have. The wheel
# then installs on every machine the row claims and fails when a model runs on a different
# generation. Detection is the right default for a local build and the wrong one for a
# published artifact.
#
# The value is published as TORCH_CUDA_ARCH_LIST rather than CMAKE_CUDA_ARCHITECTURES.
# PyTorch's own CMake explicitly rejects the latter and overrides it with OFF, so setting it
# alone silently reduces the build to a single detected architecture.

# Data center and desktop parts on the CUDA 13 trains: Ampere, Hopper, Blackwell data center,
# and Blackwell desktop.
_cuda_arch_x86_64_cu130="8.0 9.0 10.0 12.0"
_cuda_arch_x86_64_cu132="${_cuda_arch_x86_64_cu130}"

# Server-class ARM plus the Jetson modules whose CUDA train matches: Hopper for Grace-Hopper,
# Blackwell data center for GB200, and Thor.
_cuda_arch_aarch64_cu130="9.0 10.0 11.0"
_cuda_arch_aarch64_cu132="${_cuda_arch_aarch64_cu130}"

# The older CUDA train, where Orin is the target.
_cuda_arch_aarch64_cu126="8.7"
_cuda_arch_x86_64_cu126="8.0 9.0"

# The architectures for the current row, space separated in the dotted form PyTorch expects.
# Empty when the row is unknown, which leaves the build detecting as before.
executorch_cuda_arch_list() {
local machine
machine="$(uname -m)"
local train="${DESIRED_CUDA:-}"
if [ -z "${train}" ]; then
return 0
fi
# DESIRED_CUDA arrives as cu130; some callers pass 13.0 instead.
train="${train#cu}"
train="${train//./}"

case "${machine}" in
aarch64 | arm64)
case "${train}" in
126) printf '%s' "${_cuda_arch_aarch64_cu126}" ;;
130) printf '%s' "${_cuda_arch_aarch64_cu130}" ;;
132) printf '%s' "${_cuda_arch_aarch64_cu132}" ;;
esac
;;
x86_64)
case "${train}" in
126) printf '%s' "${_cuda_arch_x86_64_cu126}" ;;
130) printf '%s' "${_cuda_arch_x86_64_cu130}" ;;
132) printf '%s' "${_cuda_arch_x86_64_cu132}" ;;
esac
;;
esac
}

# The same architectures in CMake's own form, for targets outside PyTorch's CMake.
executorch_cuda_cmake_arch_list() {
local dotted
dotted="$(executorch_cuda_arch_list)"
if [ -z "${dotted}" ]; then
return 0
fi
local out="" entry
for entry in ${dotted}; do
entry="${entry//./}"
out="${out:+${out};}${entry}-real"
done
printf '%s' "${out}"
}
15 changes: 15 additions & 0 deletions .ci/scripts/wheel/envvar_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,18 @@
# any variables so that subprocesses will see them.

source "${GITHUB_WORKSPACE}/${REPOSITORY}/.ci/scripts/wheel/envvar_base.sh"

# Compile device code for the GPU architectures this release row claims, rather than for
# whichever GPU the builder happens to have. A wheel built with detection alone installs on
# every machine the row covers and then fails when a model runs on a different generation.
source "${GITHUB_WORKSPACE}/${REPOSITORY}/.ci/scripts/wheel/cuda_arch_list.sh"
_executorch_cuda_arch="$(executorch_cuda_arch_list)"
if [ -n "${_executorch_cuda_arch}" ]; then
# PyTorch's CMake rejects CMAKE_CUDA_ARCHITECTURES and overrides it with OFF, which leaves
# the build compiling for one detected architecture, so the list has to go through the
# variable PyTorch reads. Both are set: targets that go through PyTorch's CMake honour the
# first, and any that do not honour the second.
export TORCH_CUDA_ARCH_LIST="${_executorch_cuda_arch}"
export CMAKE_ARGS="${CMAKE_ARGS} -DCMAKE_CUDA_ARCHITECTURES=$(executorch_cuda_cmake_arch_list)"
echo "CUDA architectures for this row: ${_executorch_cuda_arch}"
fi
7 changes: 6 additions & 1 deletion .github/workflows/build-wheels-aarch64-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ jobs:
os: linux-aarch64
test-infra-repository: pytorch/test-infra
test-infra-ref: main
with-cuda: disabled
# CUDA enabled so the accelerator rows are built and published. The generator
# emits one cell per supported CUDA train, and each cell carries its own local
# version label so the CPU and accelerator artifacts stay distinguishable. A plain
# `pip install executorch` continues to resolve the CPU wheel from the default
# index; an accelerator wheel requires pointing at the matching index.
with-cuda: enable
with-rocm: disabled
python-versions: '["3.10", "3.11", "3.12", "3.13", "3.14"]'

Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/build-wheels-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ jobs:
os: linux
test-infra-repository: pytorch/test-infra
test-infra-ref: main
with-cuda: disabled
# CUDA enabled so the accelerator rows are built and published. The generator
# emits one cell per supported CUDA train, and each cell carries its own local
# version label so the CPU and accelerator artifacts stay distinguishable. A plain
# `pip install executorch` continues to resolve the CPU wheel from the default
# index; an accelerator wheel requires pointing at the matching index.
with-cuda: enable
with-rocm: disabled
python-versions: '["3.10", "3.11", "3.12", "3.13", "3.14"]'

Expand Down
36 changes: 35 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
# Copyright 2024, 2026 Arm Limited and/or its affiliates.
Expand Down Expand Up @@ -108,6 +108,40 @@
return _is_env_flag_enabled("EXECUTORCH_BUILD_MINIMAL")


def _cuda_train() -> str:
"""The CUDA train this wheel is being built for, as a bare number like "130".

Read from the wheel build environment rather than detected from an installed compiler.
A CPU wheel built on a machine that happens to have a CUDA toolkit must not declare CUDA
dependencies, and detection cannot tell the two cases apart.
"""
train = os.environ.get("DESIRED_CUDA", "").strip().lower()
if not train:
return ""
train = train.removeprefix("cu").replace(".", "")
return train if train.isdigit() else ""


def _cuda_dependencies() -> List[str]:
"""Runtime libraries a CUDA wheel needs but does not bundle.

Empty for a CPU wheel, and empty for a build whose CUDA train is unknown, so the CPU rows
are unaffected. The major decides the package suffix, matching how these are published.
"""
train = _cuda_train()
if not train or not _is_env_flag_enabled("EXECUTORCH_BUILD_CUDA"):
return []
major = train[:2] if train.startswith("13") else train[:2]
suffix = f"cu{major}"
# Only what the delegate and its shim actually link. A shorter list keeps a CUDA install
# from pulling in libraries nothing in this wheel references.
return [
f"nvidia-cuda-runtime-{suffix}; platform_system == 'Linux'",
f"nvidia-curand-{suffix}; platform_system == 'Linux'",
f"nvidia-cublas-{suffix}; platform_system == 'Linux'",
]


def _minimal_cmake_flags() -> List[str]:
return [
"-DEXECUTORCH_BUILD_COREML=OFF",
Expand Down Expand Up @@ -1205,7 +1239,7 @@
setup_kwargs["packages"] = _minimal_packages()
setup_kwargs["install_requires"] = _minimal_dependencies()
else:
setup_kwargs["install_requires"] = _base_dependencies()
setup_kwargs["install_requires"] = _base_dependencies() + _cuda_dependencies()


setup(
Expand Down
Loading