diff --git a/.github/workflows/build-torch.yml b/.github/workflows/build-torch.yml index f33fca2b5..5d30cc286 100644 --- a/.github/workflows/build-torch.yml +++ b/.github/workflows/build-torch.yml @@ -8,18 +8,18 @@ on: workflow_dispatch: inputs: version: - description: 'torch version to build (git tag without leading v, e.g. 2.13.0)' + description: 'torch version to build (git tag without leading v)' required: true - default: '2.13.0' + default: '2.14.0' pull_request: paths: - '.github/workflows/build-torch.yml' - 'patches/torch/**' -run-name: build-torch - ${{ inputs.version || '2.13.0' }} +run-name: build-torch - ${{ inputs.version || '2.14.0' }} concurrency: - group: ${{ github.workflow }}-${{ inputs.version || '2.13.0' }}-${{ github.head_ref || github.run_id }} + group: ${{ github.workflow }}-${{ inputs.version || '2.14.0' }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true permissions: @@ -31,7 +31,7 @@ defaults: shell: bash --noprofile --norc -exo pipefail {0} env: - TORCH_VERSION: ${{ inputs.version || '2.13.0' }} + TORCH_VERSION: ${{ inputs.version || '2.14.0' }} jobs: setup: @@ -67,9 +67,7 @@ jobs: - name: Patch pytorch source working-directory: pytorch run: | - git apply ../python-wheels/patches/torch/${{ env.TORCH_VERSION }}+cpu/0001-CI-Add-manywheel-Dockerfile-for-riscv64.patch - git apply ../python-wheels/patches/torch/${{ env.TORCH_VERSION }}+cpu/0002-CI-Add-cpu-riscv64-support-to-manywheel-scripts.patch - git apply ../python-wheels/patches/torch/${{ env.TORCH_VERSION }}+cpu/0003-CI-Add-native-build-image-for-linux-riscv64.patch + git apply ../python-wheels/patches/torch/${{ env.TORCH_VERSION }}+cpu/*.patch - name: Login to GitHub Container Registry uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 @@ -106,7 +104,7 @@ jobs: docker push "${DOCKER_IMAGE}" build_wheels: - name: Build torch ${{ inputs.version || '2.13.0' }} ${{ matrix.python-version }}-manylinux_riscv64 + name: Build torch ${{ inputs.version || '2.14.0' }} ${{ matrix.python-version }}-manylinux_riscv64 runs-on: ubuntu-24.04-riscv timeout-minutes: 1440 # 24h needs: @@ -151,9 +149,7 @@ jobs: - name: Patch pytorch source working-directory: pytorch run: | - git apply ../python-wheels/patches/torch/${{ env.TORCH_VERSION }}+cpu/0001-CI-Add-manywheel-Dockerfile-for-riscv64.patch - git apply ../python-wheels/patches/torch/${{ env.TORCH_VERSION }}+cpu/0002-CI-Add-cpu-riscv64-support-to-manywheel-scripts.patch - git apply ../python-wheels/patches/torch/${{ env.TORCH_VERSION }}+cpu/0003-CI-Add-native-build-image-for-linux-riscv64.patch + git apply ../python-wheels/patches/torch/${{ env.TORCH_VERSION }}+cpu/*.patch - name: Populate binary env working-directory: pytorch @@ -194,11 +190,11 @@ jobs: path: ${{ steps.artifact-name.outputs.wheel-dir }}/*.whl publish: - name: Publish torch ${{ inputs.version || '2.13.0' }} + name: Publish torch ${{ inputs.version || '2.14.0' }} needs: [setup, build_wheels] permissions: contents: write pull-requests: write uses: $/.github/workflows/_publish-wheel.yml with: - artifact-pattern: torch-${{ inputs.version || '2.13.0' }}-*-manylinux_riscv64 + artifact-pattern: torch-${{ inputs.version || '2.14.0' }}-*-manylinux_riscv64 diff --git a/patches/torch/2.14.0+cpu/0001-CI-Add-manywheel-Dockerfile-for-riscv64.patch b/patches/torch/2.14.0+cpu/0001-CI-Add-manywheel-Dockerfile-for-riscv64.patch new file mode 100644 index 000000000..4c9d1446b --- /dev/null +++ b/patches/torch/2.14.0+cpu/0001-CI-Add-manywheel-Dockerfile-for-riscv64.patch @@ -0,0 +1,120 @@ +From 0000000000000000000000000000000000000001 Mon Sep 17 00:00:00 2001 +From: RISE Project CI +Date: Thu, 10 Jul 2026 00:00:00 +0000 +Subject: [PATCH 1/2] [CI] Add manywheel Dockerfile for riscv64 + +Add Dockerfile_2_39_riscv64 for building riscv64 manywheel images using +quay.io/pypa/manylinux_2_39_riscv64 as base, and register the new +manylinux2_39_riscv64-builder:cpu-riscv64 image in build.sh. + +Pin OPENBLAS_VERSION to v0.3.34: v0.3.33 (install_openblas.sh's default) +fails to build on riscv64 with "'coremsg' undeclared" in +dynamic_riscv64.c; v0.3.34 declares that buffer. Set as a plain shell +variable (not folded into DOCKER_GPU_BUILD_ARG) since build.sh +unconditionally appends its own `--build-arg "OPENBLAS_VERSION=..."` +after DOCKER_GPU_BUILD_ARG on the docker build command line, and a +later --build-arg for the same key silently wins. + +Upstream-Status: To upstream + +Signed-off-by: RISE Project CI +--- + .ci/docker/manywheel/Dockerfile_2_39_riscv64 | 66 +++++++++++++++++++++++++++++++++ + .ci/docker/manywheel/build.sh | 11 +++++++ + 2 files changed, 77 insertions(+) + +diff --git a/.ci/docker/manywheel/Dockerfile_2_39_riscv64 b/.ci/docker/manywheel/Dockerfile_2_39_riscv64 +new file mode 100644 +--- /dev/null ++++ b/.ci/docker/manywheel/Dockerfile_2_39_riscv64 +@@ -0,0 +1,66 @@ ++FROM quay.io/pypa/manylinux_2_39_riscv64 as base ++ ++ARG GCCTOOLSET_VERSION=14 ++ ++# Language variables ++ENV LC_ALL=en_US.UTF-8 ++ENV LANG=en_US.UTF-8 ++ENV LANGUAGE=en_US.UTF-8 ++ ++ARG PIP_EXTRA_INDEX_URL ++ENV PIP_EXTRA_INDEX_URL=${PIP_EXTRA_INDEX_URL} ++ARG PIP_PREFER_BINARY ++ENV PIP_PREFER_BINARY=${PIP_PREFER_BINARY} ++ ++# Installed needed OS packages. This is to support all ++# the binary builds (torch, vision, audio, text, data) ++RUN yum -y update ++RUN yum install -y \ ++ autoconf \ ++ automake \ ++ bison \ ++ bzip2 \ ++ curl \ ++ diffutils \ ++ file \ ++ git \ ++ less \ ++ libffi-devel \ ++ libgomp \ ++ make \ ++ openssl-devel \ ++ patch \ ++ perl \ ++ unzip \ ++ util-linux \ ++ wget \ ++ which \ ++ xz \ ++ zstd \ ++ sudo \ ++ gcc \ ++ gcc-c++ \ ++ gcc-gfortran \ ++ gdb ++RUN yum install -y ninja-build ++ ++# git236+ would refuse to run git commands in repos owned by other users ++# Which causes version check to fail, as pytorch repo is bind-mounted into the image ++# Override this behaviour by treating every folder as safe ++# For more details see https://github.com/pytorch/pytorch/issues/78659#issuecomment-1144107327 ++RUN git config --global --add safe.directory "*" ++ ++# Build CPython 3.15.0 / 3.15.0t from source; quay.io/pypa does not ship them yet ++COPY ./common/install_cpython.sh install_cpython.sh ++RUN CPYTHON_VERSIONS="3.15.0 3.15.0t" bash ./install_cpython.sh && rm install_cpython.sh ++ ++FROM base as openblas ++# Install openblas ++ARG OPENBLAS_VERSION ++ADD ./common/install_openblas.sh install_openblas.sh ++RUN bash ./install_openblas.sh && rm install_openblas.sh ++ ++FROM base as final ++ ++# remove unnecessary python versions ++COPY --from=openblas /opt/OpenBLAS/ /opt/OpenBLAS/ + +diff --git a/.ci/docker/manywheel/build.sh b/.ci/docker/manywheel/build.sh +--- a/.ci/docker/manywheel/build.sh ++++ b/.ci/docker/manywheel/build.sh +@@ -43,6 +43,17 @@ + DOCKER_GPU_BUILD_ARG=" --build-arg DEVTOOLSET_VERSION=13" + MANY_LINUX_VERSION="2_28_aarch64" + ;; ++ manylinux2_39_riscv64-builder:cpu-riscv64) ++ TARGET=final ++ GPU_IMAGE=riscv64/almalinux:10-kitten ++ # Use a custom PyPI index to get pre-built wheels for RISC-V ++ # See https://riseproject-dev.github.io/python-wheels/ ++ # v0.3.33 (install_openblas.sh's default) fails to build on riscv64 ++ # with "'coremsg' undeclared" in dynamic_riscv64.c; v0.3.34 fixes it. ++ OPENBLAS_VERSION="v0.3.34" ++ DOCKER_GPU_BUILD_ARG=" --platform linux/riscv64 --build-arg DEVTOOLSET_VERSION=14 --build-arg PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple --build-arg PIP_PREFER_BINARY=1" ++ MANY_LINUX_VERSION="2_39_riscv64" ++ ;; + manylinuxs390x-builder:cpu-s390x) + TARGET=final + GPU_IMAGE=s390x/almalinux:8 +-- +2.39.0 diff --git a/patches/torch/2.14.0+cpu/0002-CI-Add-cpu-riscv64-support-to-manywheel-scripts.patch b/patches/torch/2.14.0+cpu/0002-CI-Add-cpu-riscv64-support-to-manywheel-scripts.patch new file mode 100644 index 000000000..7c635a1ae --- /dev/null +++ b/patches/torch/2.14.0+cpu/0002-CI-Add-cpu-riscv64-support-to-manywheel-scripts.patch @@ -0,0 +1,213 @@ +From 0000000000000000000000000000000000000002 Mon Sep 17 00:00:00 2001 +From: RISE Project CI +Date: Thu, 10 Jul 2026 00:00:00 +0000 +Subject: [PATCH 2/2] [CI] Add cpu-riscv64 support to manywheel scripts + +Add riscv64 support to the manywheel scripts: +- build.sh: add cpu-riscv64 to GPU_ARCH_TYPE case +- build_env_setup.py: add manylinux_2_39_riscv64 PLATFORM_TAG and cpu-riscv64 env setup +- build_wheel.py: add riscv64 BLAS configuration (OpenBLAS, no MKLDNN) +- repair_wheel.py: generalise aarch64_extra_deps -> arch_extra_deps for multi-arch + +Upstream-Status: Submitted [https://github.com/pytorch/pytorch/pull/191657] + +Signed-off-by: RISE Project CI +--- +diff --git a/.ci/manywheel/build.sh b/.ci/manywheel/build.sh +index 2f4abd2..8a0b0db 100755 +--- a/.ci/manywheel/build.sh ++++ b/.ci/manywheel/build.sh +@@ -6,7 +6,7 @@ SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + PYTORCH_ROOT="${PYTORCH_ROOT:-$(cd "${SCRIPTPATH}/../.." && pwd)}" + + case "${GPU_ARCH_TYPE:-BLANK}" in +- cuda|cuda-aarch64|cpu|cpu-aarch64|cpu-cxx11-abi|xpu|rocm) ++ cuda|cuda-aarch64|cpu|cpu-aarch64|cpu-riscv64|cpu-cxx11-abi|xpu|rocm) + # New pipeline: pyproject-driven build via `python -m build` + # then patchelf-based wheel repair. + source "${SCRIPTPATH}/set_desired_python.sh" +diff --git a/.ci/manywheel/build_env_setup.py b/.ci/manywheel/build_env_setup.py +index 776fde3..f8edfd2 100755 +--- a/.ci/manywheel/build_env_setup.py ++++ b/.ci/manywheel/build_env_setup.py +@@ -227,6 +227,7 @@ def discover_rocm_home() -> str: + PLATFORM_TAGS: dict[str, str] = { + "x86_64": "manylinux_2_28_x86_64", + "aarch64": "manylinux_2_28_aarch64", ++ "riscv64": "manylinux_2_39_riscv64", + } + + +@@ -494,7 +495,13 @@ def main() -> None: + setup_cuda(cuda_version) + env_out.update(cuda_build_env(cuda_version, arch)) + print(f"CUDA {cuda_version} environment configured") +- elif gpu_arch_type in ("cpu", "cpu-aarch64", "cpu-s390x", "cpu-cxx11-abi"): ++ elif gpu_arch_type in ( ++ "cpu", ++ "cpu-aarch64", ++ "cpu-riscv64", ++ "cpu-s390x", ++ "cpu-cxx11-abi", ++ ): + cleanup_cuda_for_cpu_build() + env_out.update(CPU_BUILD_ENV) + print("CPU environment configured") +diff --git a/.ci/manywheel/build_wheel.py b/.ci/manywheel/build_wheel.py +index 4b33397..b39298e 100755 +--- a/.ci/manywheel/build_wheel.py ++++ b/.ci/manywheel/build_wheel.py +@@ -36,26 +36,34 @@ def configure_blas_env() -> None: + os.environ["CMAKE_LIBRARY_PATH"] = "/opt/intel/lib:/lib" + return + +- if arch != "aarch64": +- return ++ elif arch == "aarch64": ++ if not Path("/acl").is_dir(): ++ sys.exit("ERROR: ARM Compute Library not found at /acl") ++ os.environ["USE_MKLDNN"] = "1" ++ os.environ["USE_MKLDNN_ACL"] = "1" ++ os.environ["ACL_ROOT_DIR"] = "/acl" ++ ++ if gpu_arch_type == "cuda-aarch64": ++ nvpl = Path("/usr/local/lib/libnvpl_blas_lp64_gomp.so.0") ++ if not nvpl.is_file(): ++ sys.exit(f"ERROR: NVPL BLAS not found at {nvpl}") ++ print("Using NVPL BLAS/LAPACK and ACL for MKLDNN on CUDA aarch64") ++ os.environ["BLAS"] = "NVPL" ++ elif gpu_arch_type in ("cpu-aarch64", "cpu"): ++ openblas = Path("/opt/OpenBLAS/lib/libopenblas.so.0") ++ if not openblas.is_file(): ++ sys.exit(f"ERROR: OpenBLAS not found at {openblas}") ++ print("Using OpenBLAS and ACL for MKLDNN on CPU aarch64") ++ os.environ["BLAS"] = "OpenBLAS" ++ os.environ["OpenBLAS_HOME"] = "/opt/OpenBLAS" ++ ++ elif arch == "riscv64": ++ os.environ["USE_MKLDNN"] = "0" + +- if not Path("/acl").is_dir(): +- sys.exit("ERROR: ARM Compute Library not found at /acl") +- os.environ["USE_MKLDNN"] = "1" +- os.environ["USE_MKLDNN_ACL"] = "1" +- os.environ["ACL_ROOT_DIR"] = "/acl" +- +- if gpu_arch_type == "cuda-aarch64": +- nvpl = Path("/usr/local/lib/libnvpl_blas_lp64_gomp.so.0") +- if not nvpl.is_file(): +- sys.exit(f"ERROR: NVPL BLAS not found at {nvpl}") +- print("Using NVPL BLAS/LAPACK and ACL for MKLDNN on CUDA aarch64") +- os.environ["BLAS"] = "NVPL" +- elif gpu_arch_type in ("cpu-aarch64", "cpu"): + openblas = Path("/opt/OpenBLAS/lib/libopenblas.so.0") + if not openblas.is_file(): + sys.exit(f"ERROR: OpenBLAS not found at {openblas}") +- print("Using OpenBLAS and ACL for MKLDNN on CPU aarch64") ++ print("Using OpenBLAS for MKLDNN on CPU riscv64") + os.environ["BLAS"] = "OpenBLAS" + os.environ["OpenBLAS_HOME"] = "/opt/OpenBLAS" + +diff --git a/.ci/manywheel/repair_wheel.py b/.ci/manywheel/repair_wheel.py +index 5cc1122..a66444e 100755 +--- a/.ci/manywheel/repair_wheel.py ++++ b/.ci/manywheel/repair_wheel.py +@@ -29,6 +29,8 @@ from pathlib import Path + + from auditwheel.wheeltools import add_platforms, InWheelCtx + ++from build_env_setup import PLATFORM_TAGS ++ + + PATCHELF = "/usr/local/bin/patchelf" + +@@ -116,19 +118,22 @@ def rocm_rpaths() -> str: + ) + + +-def aarch64_extra_deps(use_cuda: bool) -> list[Path]: ++def arch_extra_deps(arch: str, use_cuda: bool) -> list[Path]: + """Libraries to bundle into torch/lib/ on aarch64. + +- CPU builds link against OpenBLAS + libgfortran; CUDA builds link against +- NVPL. Both pick up ARM Compute Library (ACL) for oneDNN acceleration. ++ CPU builds link against OpenBLAS + libgfortran ++ CUDA builds link against NVPL. + """ +- deps: list[Path] = [] + candidates: list[Path] = [Path("/usr/lib64/libgfortran.so.5")] +- if Path("/acl/build").is_dir(): +- candidates += [ +- Path("/acl/build/libarm_compute.so"), +- Path("/acl/build/libarm_compute_graph.so"), +- ] ++ if arch == "aarch64": ++ # Both CPU and CUDA builds pick up ARM Compute Library (ACL) for ++ # oneDNN acceleration on AArch64. ++ if Path("/acl/build").is_dir(): ++ candidates += [ ++ Path("/acl/build/libarm_compute.so"), ++ Path("/acl/build/libarm_compute_graph.so"), ++ ] ++ + if use_cuda: + candidates += [ + Path(f"/usr/local/lib/{name}") +@@ -141,7 +146,8 @@ def aarch64_extra_deps(use_cuda: bool) -> list[Path]: + ] + else: + candidates.append(Path("/opt/OpenBLAS/lib/libopenblas.so.0")) +- deps = [p for p in candidates if p.is_file()] ++ ++ deps: list[Path] = [p for p in candidates if p.is_file()] + return deps + + +@@ -352,7 +358,7 @@ def repair_wheel( + output_dir: Path, + platform_tag: str, + libgomp_path: Path, +- aarch64_deps: list[Path], ++ arch_deps: list[Path], + bundled_libs: list[BundledLib], + aux_files: list[AuxFile], + c_so_rpath: str, +@@ -379,7 +385,7 @@ def repair_wheel( + ) + + # Bundle aarch64 BLAS/LAPACK/ACL dependencies (no-op on x86) +- for dep in aarch64_deps: ++ for dep in arch_deps: + shutil.copy(dep, torch_lib / dep.name) + + # TODO: Remove when switching to ROCm wheels +@@ -487,21 +493,23 @@ def main() -> None: + lib_so_rpath = "$ORIGIN" + force_rpath = False + +- aarch64_deps = aarch64_extra_deps(use_cuda) if arch == "aarch64" else [] ++ arch_deps = arch_extra_deps(arch, use_cuda) + + args.output_dir.mkdir(parents=True, exist_ok=True) + wheels = sorted(args.input_dir.glob("*.whl")) + if not wheels: + sys.exit(f"No wheels found in {args.input_dir}") + +- platform_tag = f"manylinux_2_28_{arch}" ++ if arch not in PLATFORM_TAGS: ++ sys.exit(f"Unknown arch {arch}") ++ platform_tag = PLATFORM_TAGS[arch] + for whl in wheels: + repair_wheel( + whl, + args.output_dir, + platform_tag, + libgomp_path, +- aarch64_deps, ++ arch_deps, + bundled_libs, + aux_files, + c_so_rpath, + +-- +2.39.0