Skip to content

fix(rocm): install libcuopt.so and libmps_parser.so into the wheels - #5

Merged
awelling2801 merged 1 commit into
AMD-Ecosystem:amd-integrationfrom
awelling2801:fix/rocm-wheel-missing-shared-libs
Oct 6, 2026
Merged

awelling2801 merged 1 commit into
AMD-Ecosystem:amd-integrationfrom
awelling2801:fix/rocm-wheel-missing-shared-libs

Conversation

@awelling2801

Copy link
Copy Markdown

Problem

Installing the published ROCm wheels (rocopt-mps-parser, librocopt, rocopt, rocopt-server, rocopt-sh-client) from pypi.amd.com and running import cuopt fails on a clean environment:

OSError: libmps_parser.so: cannot open shared object file: No such file or directory

Even if that's patched around, the next failure is the same thing for libcuopt.so. Neither library is actually present anywhere in the published wheels.

Root cause

Two separate missing install() rules, both ROCm-specific:

1. python/libcuopt/CMakeLists.txt — the USE_ROCM branch returns early with zero install rules:

if(USE_ROCM)
    ...
    return()  # nothing installed, ever
endif()

libcuopt.so is already compiled by the C++ build step (cpp/build/libcuopt.so), but this function never copies it into the wheel. load.py's _load_wheel_installation() looks for it at <package>/lib64/libcuopt.so — exactly matching the layout of NVIDIA's own CUDA wheel (libcuopt-cu12, which bundles libcuopt/lib64/libcuopt.so) — but that path is never populated on the ROCm side. This is why the project's own Docker image (dockerfile.rocm) works around it by hardcoding cpp/build/ onto LD_LIBRARY_PATH instead of relying on the wheel being self-contained.

2. python/cuopt/cuopt/linear_programming/data_model/CMakeLists.txt — data_model_wrapper's compiled extension links against the cuopt::mps_parser IMPORTED target but never installs it:

set(linked_libraries cuopt::mps_parser)
rapids_cython_create_modules(... LINKED_LIBRARIES "${linked_libraries}" ...)
# no install_aliased_imported_targets() call

WheelHelpers.cmake already defines install_aliased_imported_targets() for exactly this purpose ("Making libraries available inside wheels by installing the associated targets") — it's just never called anywhere in the repo, on either the CUDA or ROCm path.

Fix

  • Add the missing install(FILES ...) call for libcuopt.so in python/libcuopt/CMakeLists.txt, with a FATAL_ERROR guard if the prebuilt .so doesn't exist yet (fail loudly at build time instead of silently shipping an empty wheel).
  • Add the missing install_aliased_imported_targets(TARGETS cuopt::mps_parser DESTINATION cuopt/linear_programming/data_model) call so libmps_parser.so lands next to data_model_wrapper's compiled module, matching its INSTALL_RPATH=$ORIGIN.

Verification

Confirmed against the actual published wheels at pypi.amd.com/rocm-7.2.3/packages/rocopt/:

  • librocopt-1.0.0 contains no compiled binaries at all (full wheel content listing, ~10KB total).
  • rocopt-1.0.0's data_model_wrapper*.so has RPATH=$ORIGIN per readelf -d, with no libmps_parser.so anywhere in the wheel.
  • Manually placing libmps_parser.so at that exact path (as a local shim wheel layered on top) was confirmed to resolve the import error, isolating this as the precise root cause.

I wasn't able to do a full ROCm build in this environment to produce a rebuilt wheel end-to-end, so this PR is scoped to the CMake install-rule fix itself; CI should pick it up on rebuild.

Related

Also checked python/cuopt/cuopt/linear_programming/internals/CMakeLists.txt, which has the same cuopt::cuopt linking pattern without an explicit install. This one appears to be fine as-is: cuopt/__init__.py already does import libcuopt; libcuopt.load_library() before importing internals/linear_programming, which preloads libcuopt.so into the process via ctypes.CDLL ahead of time (the same pattern already used for librmm/libraft/rapids_logger). Once libcuopt.so actually ships (fix #1 above), that preload should let internals's DT_NEEDED=libcuopt.so resolve against the already-loaded instance without needing its own bundled copy.

Made with Cursor

Both the librocopt and rocopt wheels are missing compiled shared
libraries that their own Python loaders / RPATH=$ORIGIN extension
modules expect to find bundled alongside them, so `import cuopt`
fails on a clean install with errors like:

    libmps_parser.so: cannot open shared object file

- python/libcuopt/CMakeLists.txt: the USE_ROCM branch returned early
  with zero install() rules, so libcuopt.so (already compiled by the
  C++ build step) never made it into the wheel at all. load.py's
  _load_wheel_installation() looks for it at <pkg>/lib64/libcuopt.so,
  matching the layout of NVIDIA's own CUDA wheel; this adds the
  missing install(FILES ...) call for that exact path, with a
  FATAL_ERROR guard if the prebuilt .so is missing so this fails
  loudly at build time instead of silently shipping an empty wheel.

- python/cuopt/cuopt/linear_programming/data_model/CMakeLists.txt:
  data_model_wrapper's compiled extension links against the
  cuopt::mps_parser IMPORTED target but never installs it, even
  though WheelHelpers.cmake's install_aliased_imported_targets()
  exists in this repo for exactly this purpose (and is otherwise
  unused anywhere in the codebase). This adds the missing call,
  installing libmps_parser.so next to data_model_wrapper's compiled
  module where its INSTALL_RPATH=$ORIGIN expects to find it.

Verified against the actual published wheels
(pypi.amd.com/rocm-7.2.3/packages/rocopt/): librocopt-1.0.0 currently
contains no compiled binaries at all (confirmed via full wheel content
listing), and rocopt-1.0.0's data_model_wrapper*.so has
RPATH=$ORIGIN per readelf -d, with no libmps_parser.so present
alongside it in the wheel. Manually placing libmps_parser.so at that
exact path was confirmed to resolve the import error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant