From 526296aaadbe9f3b1a167d3a4b7b1b315d067ca2 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 21 Sep 2026 01:55:16 +0000 Subject: [PATCH] usd-core: Add version 26.8 Builds Pixar's OpenUSD core libraries and their pxr Python bindings for riscv64, the same configuration upstream publishes as the usd-core wheel: build_usd.py with imaging, MaterialX, examples and tutorials off, so nothing in the wheel touches OpenGL, Vulkan, Metal or X11 - every GPU find_package in cmake/defaults/Packages.cmake sits inside if (PXR_BUILD_IMAGING). There is no usd-core sdist on PyPI, so the workflow builds from the v26.08 tag the way upstream's pypi.yml does, including its dummy-libpython workaround for the manylinux image. The Python bindings need no external Boost: since 24.11 USD vendors its own Boost.Python fork at pxr/external/boost/python, and PXR_USE_BOOST_PYTHON is gone from 26.08 entirely. TBB 2020.3.1 - the only dependency build_usd.py compiles for a core build - needs no help on riscv64: its makefiles fall back to uname -m for the arch name and to lin64 for the export list, and tbb_machine.h picks machine/gcc_generic.h from GCC's builtin atomics. One patch is required, for pxr/base/arch: defines.h stops the build outright with "Unsupported architecture. x86_64 or ARM64 required.", and once that is past, math.h and stackTrace.cpp have the same shape of x86/ARM-only gate. --- .github/workflows/build-usd-core.yml | 194 ++++++++++++++++++ docs/packages/usd-core.yaml | 6 + ...001-Support-riscv64-in-pxr-base-arch.patch | 121 +++++++++++ 3 files changed, 321 insertions(+) create mode 100644 .github/workflows/build-usd-core.yml create mode 100644 docs/packages/usd-core.yaml create mode 100644 patches/usd-core/26.8/0001-Support-riscv64-in-pxr-base-arch.patch diff --git a/.github/workflows/build-usd-core.yml b/.github/workflows/build-usd-core.yml new file mode 100644 index 00000000000..0d5ac6e0265 --- /dev/null +++ b/.github/workflows/build-usd-core.yml @@ -0,0 +1,194 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# Mirrors the Linux job of upstream's .github/workflows/pypi.yml: build_usd.py with +# imaging/MaterialX/examples/tutorials off, then build_scripts/pypi/{setup.py, +# updatePluginfos.py}, narrowed to riscv64. +name: Build usd-core wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/usd-core.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-usd-core.yml' + - 'docs/packages/usd-core.yaml' + - 'patches/usd-core/**' + push: + branches: [main] + paths: + - '.github/workflows/build-usd-core.yml' + - 'docs/packages/usd-core.yaml' + - 'patches/usd-core/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: usd-core + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build usd-core ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 1440 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + # Upstream publishes cp39-cp314 and no free-threaded wheel; the + # pxr_boost::python bindings never declare Py_mod_gil, so a cp314t wheel + # would only re-enable the GIL. + python: ["cp312", "cp313", "cp314"] + + env: + USD_CORE_VERSION: ${{ matrix.version }} + + steps: + - name: Resolve the OpenUSD release tag + id: tag + run: | + set -euo pipefail + printf 'tag=v%s\n' \ + "$(awk -F. '{printf "%s.%02d", $1, $2}' <<<"${USD_CORE_VERSION}")" >> "$GITHUB_OUTPUT" + + - name: Checkout OpenUSD ${{ steps.tag.outputs.tag }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: PixarAnimationStudios/OpenUSD + ref: ${{ steps.tag.outputs.tag }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + fetch-depth: 1 + persist-credentials: false + + - name: Patch OpenUSD + run: git apply python-wheels/patches/usd-core/${{ env.USD_CORE_VERSION }}/00*.patch + + - name: Pull manylinux_riscv64 image + run: docker pull "${MANYLINUX_RISCV64_IMAGE}" + + - name: Build USD and the wheel + run: | + mkdir -p dist + docker run -i --rm \ + -v "${GITHUB_WORKSPACE}:/work" \ + -w /work \ + -e PYTHON_TAG="${{ matrix.python }}" \ + "${MANYLINUX_RISCV64_IMAGE}" bash -s <<'USD_BUILD_EOF' + #!/usr/bin/env bash + set -euxo pipefail + + PY="/opt/python/${PYTHON_TAG}-${PYTHON_TAG}/bin/python" + "$PY" -m pip install setuptools wheel + + # Upstream's own pypi.yml hack: the manylinux image ships no libpython + # (PEP 513), and FindPython's Development component refuses to configure + # without the file even though PXR_PY_UNDEFINED_DYNAMIC_LOOKUP means USD + # never links against it. + "$PY" -c "import pathlib,sysconfig; pathlib.Path(sysconfig.get_config_var('LIBDIR'), sysconfig.get_config_var('LDLIBRARY')).touch()" + + "$PY" build_scripts/build_usd.py --build-monolithic \ + --build-args USD,"-DPXR_PY_UNDEFINED_DYNAMIC_LOOKUP=ON -DPXR_BUILD_USD_TOOLS=OFF -DPXR_BUILD_EXEC=OFF -DPXR_INSTALL_LOCATION=../pxr/pluginfo -DPXR_PYTHON_INSTALL_DIR=lib/python" \ + --no-materialx --no-imaging --no-examples --no-tutorials \ + --build /work/gen/build --src /work/gen/src /work/inst -v + + mkdir packaging + cp -R /work/inst ./packaging + cp build_scripts/pypi/package_files/* ./packaging + cp LICENSE.txt ./packaging + # auditwheel vendors the TBB shared library into the wheel, so its + # Apache-2.0 text has to travel with it; setuptools' default + # license_files glob picks LICENSE* up without a setup.py edit. + cp gen/src/oneTBB-*/LICENSE ./packaging/LICENSE.oneTBB.txt + + cd packaging + "$PY" setup.py bdist_wheel --python-tag "${PYTHON_TAG}" + cd /work + + PYTHONPATH=/work/packaging/pypi/lib/python \ + LD_LIBRARY_PATH="/work/packaging/pypi/lib:${LD_LIBRARY_PATH:-}" \ + auditwheel repair packaging/dist/*.whl + + repaired="$(echo wheelhouse/*.whl)" + "$PY" build_scripts/pypi/updatePluginfos.py \ + "${repaired}" "/work/dist/$(basename "${repaired}")" + ls -la /work/dist + USD_BUILD_EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: usd-core-${{ env.USD_CORE_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: dist/*.whl + if-no-files-found: error + + - name: Test usd-core wheel + run: | + docker run -i --rm \ + -v "${GITHUB_WORKSPACE}:/work" \ + -w /work \ + -e PYTHON_TAG="${{ matrix.python }}" \ + "${MANYLINUX_RISCV64_IMAGE}" bash -s <<'USD_TEST_EOF' + #!/usr/bin/env bash + set -euxo pipefail + + PY="/opt/python/${PYTHON_TAG}-${PYTHON_TAG}/bin/python" + "$PY" -m pip install pytest + "$PY" -m pip install --no-index --find-links=/work/dist usd-core + + # /work/pxr is the checkout's C++ source tree and would shadow the + # installed package as a namespace package, so run from elsewhere and + # prove which pxr was imported. + cd /tmp + "$PY" -c " + import pathlib, pxr + p = pathlib.Path(pxr.__file__) + print(p) + assert 'site-packages' in p.parts + import importlib, pkgutil + mods = sorted(m.name for m in pkgutil.iter_modules(pxr.__path__)) + for m in mods: + importlib.import_module('pxr.' + m) + print(len(mods), 'modules imported:', ' '.join(mods)) + assert 'Usd' in mods and 'Sdf' in mods and 'UsdGeom' in mods + " + "$PY" -m pytest -v /work/build_scripts/pypi/test.py + USD_TEST_EOF + + publish: + name: Publish usd-core ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + artifact-pattern: usd-core-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/usd-core.yaml b/docs/packages/usd-core.yaml new file mode 100644 index 00000000000..83190bcdc1d --- /dev/null +++ b/docs/packages/usd-core.yaml @@ -0,0 +1,6 @@ +package-name: usd-core +source-code: https://github.com/PixarAnimationStudios/OpenUSD +license: LicenseRef-TOST-1.0 +versions: +- version: 26.8 + patched: true diff --git a/patches/usd-core/26.8/0001-Support-riscv64-in-pxr-base-arch.patch b/patches/usd-core/26.8/0001-Support-riscv64-in-pxr-base-arch.patch new file mode 100644 index 00000000000..a022f2e8a1c --- /dev/null +++ b/patches/usd-core/26.8/0001-Support-riscv64-in-pxr-base-arch.patch @@ -0,0 +1,121 @@ +From e346d5ee838ae79333ed9e0f6242891c841ed1b8 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Mon, 21 Sep 2026 01:53:09 +0000 +Subject: [PATCH] Support riscv64 in pxr/base/arch + +pxr/base/arch/defines.h recognises only x86 and ARM, and its "Bits" block +ends in #error "Unsupported architecture. x86_64 or ARM64 required." - so +every translation unit of USD fails on riscv64 before anything else is +compiled. Two further sites are reached once that is fixed: arch/math.h +guards ArchSign/ArchFloatToBitPattern/... on +ARCH_CPU_INTEL || ARCH_CPU_ARM || ARCH_OS_WASM_VM and ends in #error Unknown +system architecture (the code it guards is portable IEEE-754 bit twiddling, +not x86 or ARM specific), and nonLockingLinux__execve() in +arch/stackTrace.cpp has hand-written syscall asm for aarch64 and x86_64 with +#error Unknown architecture in the #else. + +Add an ARCH_CPU_RISCV define for __riscv, admit __riscv_xlen == 64 to +ARCH_BITS_64, let math.h's gate accept it, and give +nonLockingLinux__execve() a riscv64 branch. The syscall branch mirrors the +aarch64 one exactly: the Linux riscv64 ABI passes arguments in a0-a2, takes +the syscall number in a7, uses the same generic __NR_execve == 221 as +aarch64, and traps with ecall instead of svc. Everything else in arch is +already portable: ARCH_SPIN_PAUSE() has a no-op #else, timing.h only reaches +rdtsc behind PXR_ARCH_PREFER_TSC_TIMING and otherwise uses +std::chrono::steady_clock, and the remaining #error branches in +systemInfo.cpp/fileSystem.cpp/assumptions.cpp select on the OS, not the CPU. + +With this, the core (non-imaging) USD libraries and their Python bindings +build and run on riscv64; pxr/base/gf's SIMD paths are already gated on +__SSE2__/__ARM_NEON with scalar fallbacks, and the vendored +double-conversion already lists __riscv. + +Upstream-Status: To upstream [not submitted from this automated port run; needs a pull request against PixarAnimationStudios/OpenUSD] +--- + pxr/base/arch/defines.h | 7 +++++-- + pxr/base/arch/math.h | 1 + + pxr/base/arch/overview.dox | 1 + + pxr/base/arch/stackTrace.cpp | 14 ++++++++++++++ + 4 files changed, 21 insertions(+), 2 deletions(-) + +diff --git a/pxr/base/arch/defines.h b/pxr/base/arch/defines.h +index b7ed634..58e860c 100644 +--- a/pxr/base/arch/defines.h ++++ b/pxr/base/arch/defines.h +@@ -39,6 +39,8 @@ + #elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || \ + defined(_M_ARM64) + #define ARCH_CPU_ARM ++#elif defined(__riscv) ++#define ARCH_CPU_RISCV + #endif + + // +@@ -46,12 +48,13 @@ + // + + #if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64) || \ +- defined(_M_ARM64) || defined(__wasm64__) ++ defined(_M_ARM64) || defined(__wasm64__) || \ ++ (defined(__riscv) && __riscv_xlen == 64) + #define ARCH_BITS_64 + #elif defined(__wasm32__) + #define ARCH_BITS_32 + #else +-#error "Unsupported architecture. x86_64 or ARM64 required." ++#error "Unsupported architecture. x86_64, ARM64 or RISC-V 64 required." + #endif + + // +diff --git a/pxr/base/arch/math.h b/pxr/base/arch/math.h +index ccd53a6..8191023 100644 +--- a/pxr/base/arch/math.h ++++ b/pxr/base/arch/math.h +@@ -30,6 +30,7 @@ PXR_NAMESPACE_OPEN_SCOPE + ///@{ + + #if defined (ARCH_CPU_INTEL) || defined (ARCH_CPU_ARM) || \ ++ defined (ARCH_CPU_RISCV) || \ + defined(ARCH_OS_WASM_VM) || defined (doxygen) + + /// This is the smallest value e such that 1+e^2 == 1, using floats. +diff --git a/pxr/base/arch/overview.dox b/pxr/base/arch/overview.dox +index b8eaf9b..045375b 100644 +--- a/pxr/base/arch/overview.dox ++++ b/pxr/base/arch/overview.dox +@@ -64,6 +64,7 @@ For processor: + + + For bits: +diff --git a/pxr/base/arch/stackTrace.cpp b/pxr/base/arch/stackTrace.cpp +index 384959e..6a90d38 100644 +--- a/pxr/base/arch/stackTrace.cpp ++++ b/pxr/base/arch/stackTrace.cpp +@@ -878,6 +878,20 @@ nonLockingLinux__execve (const char *file, + ); + result = __file_result; + } ++#elif defined (ARCH_CPU_RISCV) && defined(ARCH_BITS_64) ++ { ++ long __file_result asm ("a0") = (long)file; ++ char* const* __argv asm ("a1") = argv; ++ char* const* __envp asm ("a2") = envp; ++ long __num_execve asm ("a7") = 221; ++ __asm__ __volatile__ ( ++ "ecall" ++ : "=r" (__file_result) ++ : "r"(__num_execve), "r" (__file_result), "r" (__argv), "r" (__envp) ++ : "memory" ++ ); ++ result = __file_result; ++ } + #elif defined(ARCH_CPU_INTEL) && defined(ARCH_BITS_64) + + /* +-- +2.43.0 +