usd-exchange: Add version 3.0.0 #6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: 2026 The RISE Project | |
| # SPDX-License-Identifier: MIT | |
| --- | |
| # Mirrors upstream's repo_man build (tools/repoman/cmake_build.py, install_usdex.py, | |
| # py_package.py) and its whl test suite (tools/pyproject/pytest.sh), narrowed to riscv64. | |
| # Upstream links prebuilt packman OpenUSD/MaterialX/oneTBB packages with no riscv64 | |
| # flavor, so the same stock OpenUSD release is built here with build_usd.py instead. | |
| name: Build usd-exchange wheels (riscv64) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version glob to (re)build; empty builds every version of docs/packages/usd-exchange.yaml not released yet' | |
| required: false | |
| default: '' | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - '.github/workflows/build-usd-exchange.yml' | |
| - 'docs/packages/usd-exchange.yaml' | |
| - 'patches/usd-exchange/**' | |
| push: | |
| branches: [main] | |
| paths: | |
| - '.github/workflows/build-usd-exchange.yml' | |
| - 'docs/packages/usd-exchange.yaml' | |
| - 'patches/usd-exchange/**' | |
| 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-exchange | |
| version: ${{ inputs.version }} | |
| build_wheels: | |
| needs: [setup] | |
| if: needs.setup.outputs.versions != '[]' | |
| name: Build usd-exchange ${{ 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's usd_flavors.json and requires-python stop at 3.13. | |
| python: ["cp310", "cp311", "cp312", "cp313"] | |
| env: | |
| USD_EXCHANGE_VERSION: ${{ matrix.version }} | |
| steps: | |
| - name: Checkout usd-exchange v${{ env.USD_EXCHANGE_VERSION }} | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: NVIDIA-Omniverse/usd-exchange | |
| ref: v${{ env.USD_EXCHANGE_VERSION }} | |
| persist-credentials: false | |
| - name: Resolve the OpenUSD release of the default flavor | |
| id: usd | |
| run: | | |
| set -euo pipefail | |
| usd_ver="$(sed -n 's/^default_flavor = "usd_\([0-9.]*\)_py_.*"$/\1/p' repo.toml)" | |
| test -n "${usd_ver}" | |
| printf 'ver=%s\ntag=v%s\n' "${usd_ver}" "${usd_ver}" >> "$GITHUB_OUTPUT" | |
| - name: Checkout OpenUSD ${{ steps.usd.outputs.tag }} | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: PixarAnimationStudios/OpenUSD | |
| ref: ${{ steps.usd.outputs.tag }} | |
| path: OpenUSD | |
| 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 -C OpenUSD apply "${GITHUB_WORKSPACE}"/python-wheels/patches/usd-exchange/${{ env.USD_EXCHANGE_VERSION }}/00*.patch | |
| - name: Pull manylinux_riscv64 image | |
| run: docker pull "${MANYLINUX_RISCV64_IMAGE}" | |
| - name: Build OpenUSD, usd-exchange and the wheel | |
| run: | | |
| mkdir -p dist | |
| docker run -i --rm \ | |
| -v "${GITHUB_WORKSPACE}:/work" \ | |
| -w /work \ | |
| -e PYTHON_TAG="${{ matrix.python }}" \ | |
| -e USD_EXCHANGE_VERSION \ | |
| -e USD_VER="${{ steps.usd.outputs.ver }}" \ | |
| "${MANYLINUX_RISCV64_IMAGE}" bash -s <<'USDEX_BUILD_EOF' | |
| #!/usr/bin/env bash | |
| set -euxo pipefail | |
| PY_ROOT="/opt/python/${PYTHON_TAG}-${PYTHON_TAG}" | |
| PY="${PY_ROOT}/bin/python" | |
| PY_VER="$("$PY" -c 'import sys; print("%d.%d" % sys.version_info[:2])')" | |
| "$PY" -m pip install build toml wheel "pybind11==2.11.1" | |
| # The manylinux image ships no libpython (PEP 513); FindPython's Development | |
| # component needs the file to exist, and nothing ends up linking against it. | |
| "$PY" -c "import pathlib,sysconfig; pathlib.Path(sysconfig.get_config_var('LIBDIR'), sysconfig.get_config_var('LDLIBRARY')).touch()" | |
| # MaterialX's installed MaterialXConfig.cmake runs find_dependency(X11 COMPONENTS Xt) | |
| # on every non-Apple Unix, even with MATERIALX_BUILD_RENDER=OFF, so OpenUSD's and | |
| # usdex's find_package(MaterialX) need the headers; nothing links against X11. | |
| dnf install -y --setopt=install_weak_deps=False libX11-devel libXt-devel | |
| USD=/work/_build/usd | |
| # usdex only ships MaterialXCore/Format; the render modules want OpenGL and X11. | |
| # OpenUSD 26.08 defaults PXR_PYTHON_INSTALL_DIR to lib/pythonX.Y/site-packages; | |
| # pin the lib/python layout of upstream's packman OpenUSD, which install_usdex.py | |
| # (and the staging below) reads pxr from. | |
| "$PY" OpenUSD/build_scripts/build_usd.py --onetbb \ | |
| --build-args USD,"-DPXR_PY_UNDEFINED_DYNAMIC_LOOKUP=ON -DPXR_BUILD_EXEC=OFF -DPXR_PYTHON_INSTALL_DIR=lib/python" \ | |
| MaterialX,"-DMATERIALX_BUILD_RENDER=OFF" \ | |
| --no-imaging --no-tools --no-examples --no-tutorials \ | |
| --build /work/_build/usd-build --src /work/_build/usd-src "${USD}" -v | |
| cmake -S /work -B /work/_build/cmake \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_INSTALL_LIBDIR=lib \ | |
| -DUSDEX_INSTALL_PYTHONDIR=python \ | |
| -DUSDEX_USD_ROOT="${USD}" \ | |
| -DUSDEX_TBB_ROOT="${USD}" \ | |
| -DUSDEX_MATERIALX_ROOT="${USD}" \ | |
| -DUSDEX_VERSION_STRING="${USD_EXCHANGE_VERSION}" \ | |
| -DUSDEX_PYBIND11_INCLUDE_DIR="$("$PY" -c 'import pybind11; print(pybind11.get_include())')" \ | |
| -DUSDEX_PYTHON_VERSION="${PY_VER}" \ | |
| -DUSDEX_PYTHON_ROOT="${PY_ROOT}" | |
| cmake --build /work/_build/cmake --config Release -j "$(nproc)" | |
| cmake --install /work/_build/cmake --config Release --prefix /work/_build/install | |
| "$PY" - <<'STAGE_EOF' | |
| import glob, importlib.metadata, os, shutil, sys, toml | |
| usd ="/work/_build/usd" | |
| usdex = "/work/_build/install" | |
| staging = "/work/_build/pyproject" | |
| libs = f"{staging}/usd_exchange.libs" | |
| usd_ver = os.environ["USD_VER"] | |
| major, minor = (int(x) for x in usd_ver.split(".")) | |
| def copy(src, dst): | |
| os.makedirs(os.path.dirname(dst), exist_ok=True) | |
| (shutil.copytree if os.path.isdir(src) else shutil.copyfile)(src, dst) | |
| for sub in ("core", "rtx", "test"): | |
| shutil.copytree(f"{usdex}/python/usdex/{sub}", f"{staging}/usdex/{sub}") | |
| plugins = ["ar", "sdf", "sdr", "usd", "usdGeom", "usdLux", "usdMtlx", "usdPhysics", "usdShade", "usdShaders", "usdUI"] | |
| schemas = ["usdMedia", "usdProc", "usdRender", "usdSemantics", "usdSkel", "usdVol"] | |
| validators = ["usdValidation", "usdGeomValidators", "usdPhysicsValidators", "usdShadeValidators", "usdSkelValidators", "usdUtilsValidators"] | |
| modules = ["Ar", "Gf", "Kind", "Pcp", "Plug", "Sdf", "Sdr", "Tf", "Trace", "Ts", "Usd", "UsdGeom", "UsdLux", "UsdPhysics", "UsdShade", "UsdUI", "UsdUtils", "Vt", "Work", | |
| "UsdMedia", "UsdMtlx", "UsdProc", "UsdRender", "UsdSemantics", "UsdSkel", "UsdVol", "UsdValidation"] | |
| if (major, minor) < (25, 8): | |
| plugins.append("ndr") | |
| modules.append("Ndr") | |
| if (major, minor) >= (26, 8): | |
| schemas += ["usdLod", "usdProfiles"] | |
| validators.append("usdLuxValidators") | |
| modules += ["UsdLod", "UsdProfiles"] | |
| copy(f"{usd}/lib/usd/plugInfo.json", f"{libs}/usd/plugInfo.json") | |
| for plugin in plugins + schemas + validators: | |
| for root in (f"{usd}/lib/usd", f"{usd}/plugin/usd"): | |
| if os.path.exists(f"{root}/{plugin}"): | |
| copy(f"{root}/{plugin}", f"{libs}/usd/{plugin}") | |
| break | |
| else: | |
| sys.exit(f"plugin {plugin} not found") | |
| copy(f"{usd}/plugin/usd/usdShaders.so", f"{libs}/usd/usdShaders.so") | |
| for name in validators: | |
| if name.endswith("Validators"): | |
| copy(f"{usd}/lib/libusd_{name}.so", f"{libs}/libusd_{name}.so") | |
| mtlx = f"{libs}/usd/usdMtlx/resources/libraries" | |
| for src in glob.glob(f"{usd}/libraries/bxdf/*open_pbr_surface.mtlx"): | |
| copy(src, f"{mtlx}/bxdf/{os.path.basename(src)}") | |
| for name in ("stdlib_defs.mtlx", "stdlib_ng.mtlx"): | |
| copy(f"{usd}/libraries/stdlib/{name}", f"{mtlx}/stdlib/{name}") | |
| for module in modules: | |
| src = f"{usd}/lib/python/pxr/{module}" | |
| lib = "_" + module[0].lower() + module[1:] | |
| for path in glob.glob(f"{src}/*.py") + glob.glob(f"{src}/*.pyi") + glob.glob(f"{src}/{lib}*.so"): | |
| copy(path, f"{staging}/pxr/{module}/{os.path.basename(path)}") | |
| if not glob.glob(f"{staging}/pxr/{module}/{lib}*.so"): | |
| sys.exit(f"pxr.{module} has no {lib} binding") | |
| notices = { | |
| "materialx-LICENSE.txt": glob.glob("/work/_build/usd-src/MaterialX-*/LICENSE")[0], | |
| "onetbb-LICENSE.txt": glob.glob("/work/_build/usd-src/oneTBB-*/LICENSE.txt")[0], | |
| "openusd-LICENSE.txt": "/work/OpenUSD/LICENSE.txt", | |
| "pybind11-LICENSE.txt": [f for f in importlib.metadata.files("pybind11") if f.name == "LICENSE"][0].locate(), | |
| "pyboost11-LICENSE.txt": "/work/tools/internal-licenses/pyboost11-LICENSE.txt", | |
| "usd-exchange-LICENSE.md": "/work/LICENSE.md", | |
| } | |
| for name, src in notices.items(): | |
| copy(src, f"{staging}/{name}") | |
| validator_version = toml.load("/work/tools/repoman/repo_tools.toml")["repo_install_usdex"]["usd_validation_version"] | |
| data = toml.load("/work/tools/pyproject/pyproject.toml") | |
| data["project"]["version"] = os.environ["USD_EXCHANGE_VERSION"] | |
| data["project"]["optional-dependencies"].update({ | |
| "usd" + usd_ver.replace(".", ""): [], | |
| "test": [f"usd-validation-nvidia>={validator_version},<{int(validator_version.split('.')[0]) + 1}"], | |
| }) | |
| with open(f"{staging}/pyproject.toml", "w") as f: | |
| toml.dump(data, f) | |
| with open("/work/README.md") as f: | |
| readme = f.readlines()[4:7] | |
| with open("/work/tools/pyproject/pypi-notice.md") as f: | |
| notice = f.read() | |
| with open(f"{staging}/README.md", "w") as f: | |
| f.writelines(readme) | |
| f.write(f"\n{notice}") | |
| shutil.copyfile("/work/tools/pyproject/hatch_build.py", f"{staging}/hatch_build.py") | |
| STAGE_EOF | |
| # Upstream strips before auditwheel's patchelf, never after it. | |
| find /work/_build/pyproject /work/_build/usd/lib /work/_build/install/lib -name '*.so*' -type f \ | |
| -exec sh -c 'head -c4 "$1" | grep -q ELF && strip "$1"' _ {} \; | |
| "$PY" -m build --wheel --outdir /work/_build/pyproject/dist /work/_build/pyproject | |
| LD_LIBRARY_PATH="/work/_build/install/lib:/work/_build/usd/lib:${LD_LIBRARY_PATH:-}" \ | |
| auditwheel repair -w /work/_build/wheelhouse /work/_build/pyproject/dist/*.whl | |
| "$PY" - <<'PLUGINFO_EOF' | |
| import glob, json, os, re, subprocess, sys, tempfile | |
| wheel = glob.glob("/work/_build/wheelhouse/*.whl")[0] | |
| with tempfile.TemporaryDirectory() as tmp: | |
| subprocess.run([sys.executable, "-m", "wheel", "unpack", wheel, "--dest", tmp], check=True) | |
| unpacked = glob.glob(f"{tmp}/*/")[0].rstrip("/") | |
| libs_root = f"{unpacked}/usd_exchange.libs" | |
| hashed = {} | |
| for lib in glob.glob(f"{libs_root}/*.so*"): | |
| match = re.match(r"^(lib.+?)-[0-9a-f]{6,}\.so", os.path.basename(lib)) | |
| if match: | |
| hashed[match.group(1)] = os.path.basename(lib) | |
| for plug_info in glob.glob(f"{libs_root}/usd/*/resources/plugInfo.json"): | |
| with open(plug_info) as f: | |
| data = json.loads("".join(line for line in f if not line.lstrip().startswith("#"))) | |
| modified = False | |
| for plug in data.get("Plugins", []): | |
| lib = hashed.get(f"libusd_{plug.get('Name')}") | |
| if lib and "LibraryPath" in plug: | |
| plug["LibraryPath"] = f"../../{lib}" | |
| modified = True | |
| if modified: | |
| with open(plug_info, "w") as f: | |
| json.dump(data, f, indent=4) | |
| subprocess.run([sys.executable, "-m", "wheel", "pack", unpacked, "--dest-dir", "/work/dist"], check=True) | |
| PLUGINFO_EOF | |
| ls -la /work/dist | |
| USDEX_BUILD_EOF | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: usd-exchange-${{ env.USD_EXCHANGE_VERSION }}-${{ matrix.python }}-manylinux_riscv64 | |
| path: dist/*.whl | |
| if-no-files-found: error | |
| - name: Test usd-exchange wheel | |
| run: | | |
| docker run -i --rm \ | |
| -v "${GITHUB_WORKSPACE}:/work" \ | |
| -w /work \ | |
| -e PYTHON_TAG="${{ matrix.python }}" \ | |
| "${MANYLINUX_RISCV64_IMAGE}" bash -s <<'USDEX_TEST_EOF' | |
| #!/usr/bin/env bash | |
| set -euxo pipefail | |
| VENV=/tmp/venv | |
| "/opt/python/${PYTHON_TAG}-${PYTHON_TAG}/bin/python" -m venv "${VENV}" | |
| wheel="$(echo /work/dist/*.whl)" | |
| "${VENV}/bin/python" -m pip install "${wheel}[test]" | |
| # usdex's own test extra declares usd-validation-nvidia as an open range (py_package.py), so pip installs | |
| # whatever is newest; usdex.test's ValidationRules only recognizes the exact release pinned in | |
| # repo_tools.toml (a newer one can rename the native-validator adapter it registers, which breaks the | |
| # ShaderSdrCompliance issue filter in testMaterialAlgo.py - see CLAUDE.md gotcha 629), so repin exactly. | |
| usd_validation_version="$(sed -n 's/^usd_validation_version = "\(.*\)"$/\1/p' tools/repoman/repo_tools.toml)" | |
| "${VENV}/bin/python" -m pip install "usd-validation-nvidia==${usd_validation_version}" | |
| unset PYTHONPATH | |
| "${VENV}/bin/python" tools/pyproject/check_wheel_imports.py | |
| "${VENV}/bin/python" -m unittest discover -v -s source/core/tests/unittest | |
| "${VENV}/bin/python" -m unittest discover -v -s source/rtx/tests/unittest | |
| USDEX_TEST_EOF | |
| publish: | |
| name: Publish usd-exchange ${{ 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-exchange-${{ matrix.version }}-*-manylinux_riscv64 |