Skip to content

Commit 197862e

Browse files
committed
ci: Exercise pybind11 and nanobind Python bindings in CI
- Add workflow input and env for OIIO_PYTHON_BINDINGS_BACKEND; run with backend=both on selected Linux, macOS, and Windows matrix rows. - Pass the flag through ci-build.bash into CMake. - Install pinned nanobind for CMake discovery (pip --require-hashes on Linux and Windows; Homebrew on macOS). - Point nanobind Python tests at lib/python/nanobind (not per-$<CONFIG>). - Extend the nanobind package __path__ on Windows for MSVC config output dirs. Assisted-by: Cursor/Composer-2 Signed-off-by: Aleksandr Motsjonov soswow@gmail.com
1 parent 85cca81 commit 197862e

9 files changed

Lines changed: 49 additions & 4 deletions

File tree

.github/workflows/build-steps.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ on:
8888
type: string
8989
build_local_deps:
9090
type: string
91+
oiio_python_bindings_backend:
92+
type: string
93+
default: ''
9194
secrets:
9295
PASSED_GITHUB_TOKEN:
9396
required: false
@@ -136,6 +139,7 @@ jobs:
136139
OpenImageIO_BUILD_LOCAL_DEPS: ${{inputs.build_local_deps}}
137140
SETENVS: ${{inputs.setenvs}}
138141
DEPCMDS: ${{inputs.depcmds}}
142+
OIIO_PYTHON_BINDINGS_BACKEND: ${{ inputs.oiio_python_bindings_backend }}
139143

140144
steps:
141145
- name: install nodejs20glibc2.17

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ jobs:
355355
required_deps: ${{ matrix.required_deps || 'all' }}
356356
optional_deps: ${{ matrix.optional_deps || 'CUDAToolkit;DCMTK;JXL;Nuke;OpenGL;openjph;OpenVDB;Ptex;pystring;Qt5;R3DSDK;' }}${{matrix.optional_deps_append}}
357357
build_local_deps: ${{ matrix.build_local_deps }}
358+
oiio_python_bindings_backend: ${{ matrix.oiio_python_bindings_backend || '' }}
358359
strategy:
359360
fail-fast: false
360361
matrix:
@@ -381,6 +382,7 @@ jobs:
381382

382383
- desc: latest releases gcc13 C++20 py3.12 avx2 exr3.4 ocio2.4
383384
nametag: linux-latest-releases
385+
oiio_python_bindings_backend: both
384386
runner: ubuntu-24.04
385387
cc_compiler: gcc-13
386388
cxx_compiler: g++-13
@@ -656,6 +658,7 @@ jobs:
656658
required_deps: ${{ matrix.required_deps || 'all' }}
657659
optional_deps: ${{ matrix.optional_deps || 'Nuke;R3DSDK;' }}${{matrix.optional_deps_append}}
658660
build_local_deps: ${{ matrix.build_local_deps }}
661+
oiio_python_bindings_backend: ${{ matrix.oiio_python_bindings_backend || '' }}
659662
strategy:
660663
fail-fast: false
661664
matrix:
@@ -676,6 +679,7 @@ jobs:
676679
- desc: MacOS-14-ARM aclang15/C++20/py3.13
677680
runner: macos-14
678681
nametag: macos14-arm-py313
682+
oiio_python_bindings_backend: both
679683
cc_compiler: /usr/bin/clang
680684
cxx_compiler: /usr/bin/clang++
681685
cxx_std: 20
@@ -739,6 +743,7 @@ jobs:
739743
required_deps: ${{ matrix.required_deps || 'all' }}
740744
optional_deps: ${{ matrix.optional_deps || 'BZip2;CUDAToolkit;DCMTK;FFmpeg;GIF;JXL;Libheif;LibRaw;Nuke;OpenCV;OpenGL;OpenJPEG;openjph;OpenCV;OpenVDB;Ptex;pystring;Qt5;Qt6;TBB;R3DSDK;${{matrix.optional_deps_append}}' }}
741745
build_local_deps: ${{ matrix.build_local_deps }}
746+
oiio_python_bindings_backend: ${{ matrix.oiio_python_bindings_backend || '' }}
742747
strategy:
743748
fail-fast: false
744749
matrix:
@@ -753,6 +758,7 @@ jobs:
753758
- desc: Windows-2025 VS2022
754759
runner: windows-2025
755760
nametag: windows-2025
761+
oiio_python_bindings_backend: both
756762
generator: "Visual Studio 17 2022"
757763
python_ver: "3.12"
758764
ctest_test_timeout: "240"

src/build-scripts/ci-build.bash

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ if [[ "$USE_SIMD" != "" ]] ; then
2525
OIIO_CMAKE_FLAGS="$OIIO_CMAKE_FLAGS -DUSE_SIMD=$USE_SIMD"
2626
fi
2727

28+
if [[ -n "${OIIO_PYTHON_BINDINGS_BACKEND:-}" ]] ; then
29+
OIIO_CMAKE_FLAGS="$OIIO_CMAKE_FLAGS -DOIIO_PYTHON_BINDINGS_BACKEND=${OIIO_PYTHON_BINDINGS_BACKEND}"
30+
fi
31+
2832
if [[ -n "$CODECOV" ]] ; then
2933
OIIO_CMAKE_FLAGS="$OIIO_CMAKE_FLAGS -DCODECOV=${CODECOV}"
3034
fi
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# CI-only: nanobind for CMake (`python -m nanobind --cmake_dir`). Used on Linux
2+
# (gh-installdeps) and Windows (gh-win-installdeps) with pip --require-hashes.
3+
# macOS CI uses `brew install nanobind` instead (install_homebrew_deps.bash).
4+
# When bumping pip pin: https://pypi.org/pypi/nanobind/<ver>/json → wheel sha256.
5+
nanobind==2.12.0 \
6+
--hash=sha256:a10d3d88e691dcdf22696f9acd893fda3c5a05635763aea238829d274fcad480

src/build-scripts/gh-installdeps.bash

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,5 +230,16 @@ fi
230230
df -h .
231231
df -h /host/root || true
232232

233+
# nanobind's CMake config is discovered via `python -m nanobind --cmake_dir`.
234+
# Version + wheel hash: src/build-scripts/ci-requirements-nanobind.txt (CodeQL / supply chain).
235+
if [[ "${OIIO_PYTHON_BINDINGS_BACKEND:-}" == "both" || "${OIIO_PYTHON_BINDINGS_BACKEND:-}" == "nanobind" ]] ; then
236+
_oiio_nanobind_requirements_file="$PWD/src/build-scripts/ci-requirements-nanobind.txt"
237+
if [[ "$ASWF_ORG" != "" ]] ; then
238+
time pip3 install -r "$_oiio_nanobind_requirements_file" --require-hashes || true
239+
else
240+
time pip3${PIP_SUFFIX} install -r "$_oiio_nanobind_requirements_file" --require-hashes
241+
fi
242+
fi
243+
233244
# Save the env for use by other stages
234245
src/build-scripts/save-env.bash

src/build-scripts/gh-win-installdeps.bash

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ elif [[ "$PYTHON_VERSION" == "3.14" ]] ; then
3939
fi
4040
pip install numpy
4141

42+
if [[ "${OIIO_PYTHON_BINDINGS_BACKEND:-}" == "both" || "${OIIO_PYTHON_BINDINGS_BACKEND:-}" == "nanobind" ]] ; then
43+
_oiio_nanobind_requirements_file="$PWD/src/build-scripts/ci-requirements-nanobind.txt"
44+
"${Python_EXECUTABLE:-python}" -m pip install -r "$_oiio_nanobind_requirements_file" --require-hashes
45+
fi
46+
4247

4348
# In case we need vcpkg, example:
4449
# echo "All pre-installed VCPkg installs:"

src/build-scripts/install_homebrew_deps.bash

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ if [[ "$OIIO_BREW_INSTALL_PACKAGES" == "" ]] ; then
5757
if [[ "${USE_QT:=1}" != "0" ]] && [[ "${INSTALL_QT:=1}" != "0" ]] ; then
5858
OIIO_BREW_INSTALL_PACKAGES+=" qt${QT_VERSION}"
5959
fi
60+
if [[ "${OIIO_PYTHON_BINDINGS_BACKEND:-}" == "both" || "${OIIO_PYTHON_BINDINGS_BACKEND:-}" == "nanobind" ]] ; then
61+
OIIO_BREW_INSTALL_PACKAGES+=" nanobind"
62+
fi
6063
fi
6164
brew install --display-times -q $OIIO_BREW_INSTALL_PACKAGES $OIIO_BREW_EXTRA_INSTALL_PACKAGES || true
6265

src/cmake/testing.cmake

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,14 +253,12 @@ macro (oiio_add_all_tests)
253253
"${CMAKE_INSTALL_PREFIX}/${PYTHON_SITE_ROOT_DIR}")
254254
oiio_tests_pythonpath_env_entry (_pybind_tests_pythonpath
255255
"${_installed_python_site_packages}")
256-
oiio_tests_pythonpath_env_entry (_nanobind_tests_pythonpath
257-
"${CMAKE_BINARY_DIR}/lib/python/nanobind/$<CONFIG>")
258256
else ()
259257
oiio_tests_pythonpath_env_entry (_pybind_tests_pythonpath
260258
"${CMAKE_BINARY_DIR}/lib/python/site-packages")
261-
oiio_tests_pythonpath_env_entry (_nanobind_tests_pythonpath
262-
"${CMAKE_BINARY_DIR}/lib/python/nanobind")
263259
endif ()
260+
oiio_tests_pythonpath_env_entry (_nanobind_tests_pythonpath
261+
"${CMAKE_BINARY_DIR}/lib/python/nanobind")
264262
set (nanobind_python_tests
265263
python-imagespec
266264
python-paramlist

src/python-nanobind/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414
os.environ["OpenImageIO_ROOT"] = _here
1515

1616
if platform.system() == "Windows":
17+
# MSVC multi-config builds often put _OpenImageIO*.pyd under OpenImageIO/<Config>/
18+
# while this __init__.py lives in OpenImageIO/. Extend the package search path
19+
# so `from . import _OpenImageIO` resolves (no CMake per-config output juggling).
20+
for _cfg in ("Release", "Debug", "RelWithDebInfo", "MinSizeRel"):
21+
_subdir = os.path.join(_here, _cfg)
22+
if os.path.isdir(_subdir) and _subdir not in __path__:
23+
__path__.append(_subdir)
24+
1725
_bin_dir = os.path.join(_here, "bin")
1826
if os.path.exists(_bin_dir):
1927
os.add_dll_directory(_bin_dir)

0 commit comments

Comments
 (0)