Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion ci/build_wheel_librapidsmpf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ export SITE_PACKAGES

./ci/build_wheel.sh "${package_name}" "${package_dir}"

# patchelf (used internally by auditwheel) corrupts ELF executables when it
# must grow PT_LOAD segments to fit new RPATH data. Shared libraries are fine,
# but standalone binaries like rrun end up with .dynstr/.dynamic outside any
# loadable segment, causing a dynamic-linker crash before main().
# The -Wl,-z,noseparate-code linker flag mitigates this on most toolchains but
# is not universally sufficient, so we also save the original binary before
# auditwheel runs and re-inject it afterward. The _rrun.py entry-point shim
# sets LD_LIBRARY_PATH so the restored binary finds the excluded RAPIDS libs.
UNREPAIRED_WHEEL=$(ls "${package_dir}"/dist/*.whl)
RRUN_ORIG=$(mktemp)
python3 - "${UNREPAIRED_WHEEL}" "${RRUN_ORIG}" <<'EOF'
import shutil, sys, zipfile
whl, dst = sys.argv[1], sys.argv[2]
with zipfile.ZipFile(whl) as z, z.open("librapidsmpf/bin/rrun") as src, open(dst, "wb") as f:
shutil.copyfileobj(src, f)
EOF
chmod 755 "${RRUN_ORIG}"

python -m auditwheel repair \
--exclude libcudf.so \
--exclude libkvikio.so \
Expand All @@ -45,6 +63,18 @@ python -m auditwheel repair \
--exclude libucp.so.0 \
--exclude libucxx.so \
-w "${RAPIDS_WHEEL_BLD_OUTPUT_DIR}" \
${package_dir}/dist/*
"${UNREPAIRED_WHEEL}"

REPAIRED_WHEEL=$(ls "${RAPIDS_WHEEL_BLD_OUTPUT_DIR}"/*.whl)
python3 - "${REPAIRED_WHEEL}" "${RRUN_ORIG}" <<'EOF'
import os, sys, zipfile
whl, rrun = sys.argv[1], sys.argv[2]
tmp = whl + ".tmp"
with zipfile.ZipFile(whl) as zin, zipfile.ZipFile(tmp, "w") as zout:
for item in zin.infolist():
data = open(rrun, "rb").read() if item.filename == "librapidsmpf/bin/rrun" else zin.read(item.filename)
zout.writestr(item, data)
os.replace(tmp, whl)
EOF

./ci/validate_wheel.sh "${package_dir}" "${RAPIDS_WHEEL_BLD_OUTPUT_DIR}"
4 changes: 4 additions & 0 deletions ci/test_wheel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ rapids-pip-retry install \
"${CPP_WHEELHOUSE}"/*.whl \
"$(echo "${PYTHON_WHEELHOUSE}"/rapidsmpf_"${RAPIDS_PY_CUDA_SUFFIX}"*.whl)[test]"

# Verify the rrun entry point was installed and works.
which rrun
rrun --help

python -m pytest ./python/rapidsmpf/rapidsmpf/tests
64 changes: 64 additions & 0 deletions python/librapidsmpf/librapidsmpf/_rrun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Entry-point shim for the bundled ``rrun`` executable."""

from __future__ import annotations

import importlib.metadata
import os
import sys
from importlib.resources import as_file, files
from pathlib import Path
from typing import NoReturn


def _iter_rapids_lib_dirs() -> list[str]:
lib_dirs: list[str] = []
seen: set[str] = set()

# Collect lib/ and lib64/ directories from every installed RAPIDS C++
# wheel. These packages register a "cmake.prefix" entry point (the RAPIDS
# convention for C++ library wheels). rrun is a standalone executable so
# no Python process pre-loads the excluded shared libraries (libcudf.so,
# librmm.so, …); prepending them to LD_LIBRARY_PATH lets the dynamic
# linker resolve them when the new process starts.
for dist in importlib.metadata.distributions():
for ep in dist.entry_points:
if ep.group != "cmake.prefix":
continue

try:
pkg_dir = Path(str(files(ep.value)))
except Exception:
break

for subdir in ("lib", "lib64"):
lib_dir = pkg_dir / subdir
lib_dir_str = str(lib_dir)
if lib_dir.is_dir() and lib_dir_str not in seen:
seen.add(lib_dir_str)
lib_dirs.append(lib_dir_str)
break

return lib_dirs


def main() -> NoReturn:
# rrun is a standalone executable. Prepend RAPIDS wheel library directories
# to LD_LIBRARY_PATH so the dynamic linker can resolve bundled shared
# libraries before starting the process.
env = os.environ.copy()

lib_dirs = _iter_rapids_lib_dirs()
existing = env.get("LD_LIBRARY_PATH")
if existing:
lib_dirs.append(existing)

if lib_dirs:
env["LD_LIBRARY_PATH"] = ":".join(lib_dirs)

rrun = files("librapidsmpf").joinpath("bin/rrun")
with as_file(rrun) as binary:
os.execvpe(str(binary), [str(binary), *sys.argv[1:]], env)

raise AssertionError("os.execvpe returned unexpectedly")
3 changes: 3 additions & 0 deletions python/librapidsmpf/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ test = [
[project.urls]
Homepage = "https://github.com/rapidsai/rapidsmpf"

[project.scripts]
rrun = "librapidsmpf._rrun:main"

[project.entry-points."cmake.prefix"]
librapidsmpf = "librapidsmpf"

Expand Down
Loading