From 92791c7656daac7d0721a6d51ab287a7c1658cdf Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Wed, 22 Jul 2026 12:40:15 -0400 Subject: [PATCH 1/2] Fix Windows pip packaging and cross-platform libHalide shadowing Fixes #8866. The Python bindings could load a foreign libHalide.so/ Halide.dll that shadows the one bundled in the wheel, because halide_ depends on it by soname/module name alone, and the dynamic linker's search order (RUNPATH < LD_LIBRARY_PATH on Linux; PATH/default dirs on Windows) lets a same-named library elsewhere on the system take precedence. Confirmed via readelf (halide_.so has RUNPATH, not RPATH) and reproduced end-to-end in WSL: a garbage libHalide.so on LD_LIBRARY_PATH broke `import halide` before this fix, and no longer does after it. The fix is to explicitly load our own copy via ctypes with an absolute path before importing halide_. Both Linux's and Windows's loaders recognize a module already mapped into the process by its soname/module name and reuse it instead of searching again, so halide_'s implicit dependency resolves to our copy regardless of what else is on the search path. Separately, cibuildwheel 4.0 made delvewheel repair the Windows default, and it was erroring out trying to locate Halide.dll itself (it doesn't search inside the not-yet-repaired wheel), breaking the Windows pip packaging workflow. Re-enable it with --ignore-existing, which tells delvewheel to treat DLLs already bundled in the wheel as satisfying their own dependencies rather than erroring out trying to locate them itself. Verified against the current published win_amd64 wheel: with this flag, delvewheel leaves Halide.dll alone but still correctly vendors msvcp140.dll, a genuine external MSVC runtime dependency that isn't currently bundled at all -- so this isn't just a no-op safety net, it fixes a real gap in the Windows wheel's self-containedness. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/pip.yml | 1 + python_bindings/src/halide/__init__.py | 30 +++++++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index f4219ae1b0f6..f118350f6953 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -139,6 +139,7 @@ jobs: CMAKE_GENERATOR=Ninja CMAKE_PREFIX_PATH='${{ github.workspace }}\opt' SETUPTOOLS_SCM_OVERRIDES_FOR_HALIDE='{local_scheme="no-local-version"}' + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: delvewheel repair --ignore-existing -w {dest_dir} {wheel} CIBW_BEFORE_TEST_LINUX: pip install cmake ninja CIBW_TEST_COMMAND: > cmake -G Ninja -S {project}/python_bindings/apps -B build -DCMAKE_BUILD_TYPE=Release && diff --git a/python_bindings/src/halide/__init__.py b/python_bindings/src/halide/__init__.py index 4bbe643b9924..6e03b9683c29 100644 --- a/python_bindings/src/halide/__init__.py +++ b/python_bindings/src/halide/__init__.py @@ -1,16 +1,30 @@ -def patch_dll_dirs(): +def _preload_bundled_halide_library(): + # Force-load our own copy of the Halide runtime library by absolute path before + # importing halide_, so that halide_'s implicit load of the same library (by + # soname/module name) resolves to this already-loaded instance instead of + # searching LD_LIBRARY_PATH / PATH / the default dynamic linker paths, where a + # foreign, incompatible libHalide could shadow ours. + # See: https://github.com/halide/Halide/issues/8866 + import ctypes import os - if hasattr(os, "add_dll_directory"): - from pathlib import Path + from pathlib import Path - bin_dir = Path(__file__).parent / "bin" - if bin_dir.exists(): - os.add_dll_directory(str(bin_dir)) + root = Path(__file__).parent + bin_dir = root / "bin" + if hasattr(os, "add_dll_directory") and bin_dir.is_dir(): + os.add_dll_directory(str(bin_dir)) -patch_dll_dirs() -del patch_dll_dirs + for relpath in ("bin/Halide.dll", "lib/libHalide.dylib", "lib64/libHalide.so", "lib/libHalide.so"): + lib_path = root / relpath + if lib_path.exists(): + ctypes.CDLL(str(lib_path)) + return + + +_preload_bundled_halide_library() +del _preload_bundled_halide_library from .halide_ import * # noqa: E402, F403 From 51a3ea2effe35253bead4bfa48803e50a9b19142 Mon Sep 17 00:00:00 2001 From: "halide-ci[bot]" <266445882+halide-ci[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:47:53 +0000 Subject: [PATCH 2/2] Apply pre-commit auto-fixes --- python_bindings/src/halide/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python_bindings/src/halide/__init__.py b/python_bindings/src/halide/__init__.py index 6e03b9683c29..b0438e6bff68 100644 --- a/python_bindings/src/halide/__init__.py +++ b/python_bindings/src/halide/__init__.py @@ -16,7 +16,12 @@ def _preload_bundled_halide_library(): if hasattr(os, "add_dll_directory") and bin_dir.is_dir(): os.add_dll_directory(str(bin_dir)) - for relpath in ("bin/Halide.dll", "lib/libHalide.dylib", "lib64/libHalide.so", "lib/libHalide.so"): + for relpath in ( + "bin/Halide.dll", + "lib/libHalide.dylib", + "lib64/libHalide.so", + "lib/libHalide.so", + ): lib_path = root / relpath if lib_path.exists(): ctypes.CDLL(str(lib_path))