From 48acb46020e4b831753b80749a004750b632bbc1 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 17 Sep 2026 10:22:20 -0500 Subject: [PATCH 1/9] build(conda): split libcuopt into per-component outputs One output per component library, each installing only its own install component: libcuopt-client, libcuopt-mathopt, libcuopt-routing and cuopt-grpc-server. libcuopt becomes a metapackage pinning the three, so conda install libcuopt and the cuopt recipe that depends on it keep working. Run dependencies were split by measuring DT_NEEDED rather than by dividing the existing list: client needs grpc/protobuf/abseil and no CUDA math libraries, routing adds only cublas, and cusparse, cudss, nccl and tbb attach to mathopt alone. That is the routing-only install win. The server ships separately because it is the one artifact needing every engine and has its own lifecycle. libcuopt.so stays with the metapackage: the ld script names all three components, so it only resolves where all three are installed. Co-Authored-By: Claude Opus 5 --- conda/recipes/libcuopt/recipe.yaml | 202 +++++++++++++++++++++++++---- 1 file changed, 179 insertions(+), 23 deletions(-) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 85c2974dd3..d73c5c7251 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -100,25 +100,23 @@ cache: - re2 outputs: + # One output per component library. Each installs only its own install component, + # so a routing-only install no longer pulls the LP solver or its cusparse / cudss / + # nccl dependencies. See #1635. - package: - name: libcuopt + name: libcuopt-client version: ${{ version }} build: script: content: | - cmake --install cpp/build - # libcuopt.so is a linker script, so the components are what carry symbols. - # Only cuopt_mathopt provides the C API. - ./ci/check_symbols.sh cpp/build/libcuopt_mathopt.so - ./ci/check_symbols.sh cpp/build/libcuopt_routing.so --no-public-api-check + cmake --install cpp/build --component client + cmake --install cpp/build --component client-dev ./ci/check_symbols.sh cpp/build/libcuopt_client.so --no-public-api-check dynamic_linking: overlinking_behavior: "error" prefix_detection: ignore: # See https://github.com/rapidsai/build-planning/issues/160 - - lib/libcuopt_routing.so - - lib/libcuopt_mathopt.so - lib/libcuopt_client.so string: cuda${{ cuda_major }}_${{ datetime_string }}_${{ head_rev }} requirements: @@ -127,15 +125,9 @@ outputs: - ${{ stdlib("c") }} - binutils host: - - libboost-devel - cuda-version =${{ cuda_version }} - rapids-logger =0.3 - librmm =${{ minor_version }} - - libcublas - - libcudss-dev >=0.7,<0.8 - - libcusparse-dev - - libnvjitlink-dev - - nccl >=2.19 - openssl - c-ares - libuuid @@ -143,21 +135,71 @@ outputs: - libprotobuf - libabseil - re2 - - tbb-devel run: - ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }} - - libboost-devel - librmm =${{ minor_version }} - - cuda-nvrtc - - libcudss >=0.7,<0.8 - - libgomp >=9 - - nccl >=2.19 - openssl - c-ares - libuuid - libgrpc >=1.78.0,<1.80.0a0 - libprotobuf - libabseil + ignore_run_exports: + by_name: + - cuda-version + - librmm + tests: + - package_contents: + files: + - lib/libcuopt_client.so + - include/cuopt/error.hpp + - include/cuopt/export.hpp + about: + homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} + license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} + summary: cuOpt host-side client -- problem representation, parsers and gRPC client + + - package: + name: libcuopt-mathopt + version: ${{ version }} + build: + script: + content: | + cmake --install cpp/build --component mathopt + cmake --install cpp/build --component mathopt-dev + ./ci/check_symbols.sh cpp/build/libcuopt_mathopt.so + dynamic_linking: + overlinking_behavior: "error" + prefix_detection: + ignore: + - lib/libcuopt_mathopt.so + string: cuda${{ cuda_major }}_${{ datetime_string }}_${{ head_rev }} + requirements: + build: + - cmake ${{ cmake_version }} + - ${{ stdlib("c") }} + - binutils + host: + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - libboost-devel + - cuda-version =${{ cuda_version }} + - rapids-logger =0.3 + - librmm =${{ minor_version }} + - libcublas + - libcudss-dev >=0.7,<0.8 + - libcusparse-dev + - libnvjitlink-dev + - nccl >=2.19 + - tbb-devel + run: + - ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }} + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - libboost-devel + - librmm =${{ minor_version }} + - cuda-nvrtc + - libcudss >=0.7,<0.8 + - libgomp >=9 + - nccl >=2.19 - tbb ignore_run_exports: by_name: @@ -171,12 +213,126 @@ outputs: tests: - package_contents: files: - - lib/libcuopt.so - - lib/libcuopt_routing.so - lib/libcuopt_mathopt.so - - lib/libcuopt_client.so - bin/cuopt_cli + about: + homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} + license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} + summary: cuOpt LP / MILP / QP / SOCP solver + + - package: + name: libcuopt-routing + version: ${{ version }} + build: + script: + content: | + cmake --install cpp/build --component routing + cmake --install cpp/build --component routing-dev + ./ci/check_symbols.sh cpp/build/libcuopt_routing.so --no-public-api-check + dynamic_linking: + overlinking_behavior: "error" + prefix_detection: + ignore: + - lib/libcuopt_routing.so + string: cuda${{ cuda_major }}_${{ datetime_string }}_${{ head_rev }} + requirements: + build: + - cmake ${{ cmake_version }} + - ${{ stdlib("c") }} + - binutils + host: + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - libboost-devel + - cuda-version =${{ cuda_version }} + - rapids-logger =0.3 + - librmm =${{ minor_version }} + - libcublas + run: + - ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }} + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - libboost-devel + - librmm =${{ minor_version }} + ignore_run_exports: + by_name: + - cuda-nvtx + - cuda-version + - librmm + - libboost + - libboost_iostreams + tests: + - package_contents: + files: + - lib/libcuopt_routing.so + about: + homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} + license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} + summary: cuOpt VRP / TSP / PDP routing engine + + # The server is the one artifact that needs every engine, and it is a separately-run + # service with its own lifecycle, so it ships on its own rather than forcing every + # library user to install a daemon. + - package: + name: cuopt-grpc-server + version: ${{ version }} + build: + script: + content: | + cmake --install cpp/build --component grpc-server + dynamic_linking: + overlinking_behavior: "error" + string: cuda${{ cuda_major }}_${{ datetime_string }}_${{ head_rev }} + requirements: + build: + - cmake ${{ cmake_version }} + - ${{ stdlib("c") }} + host: + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - ${{ pin_subpackage("libcuopt-mathopt", exact=True) }} + - ${{ pin_subpackage("libcuopt-routing", exact=True) }} + - cuda-version =${{ cuda_version }} + run: + - ${{ pin_compatible("cuda-version", upper_bound="x", lower_bound="x") }} + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - ${{ pin_subpackage("libcuopt-mathopt", exact=True) }} + - ${{ pin_subpackage("libcuopt-routing", exact=True) }} + ignore_run_exports: + by_name: + - cuda-version + tests: + - package_contents: + files: - bin/cuopt_grpc_server + about: + homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} + license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} + summary: cuOpt gRPC remote solve server + + # Keeps `conda install libcuopt` working for anyone who wants the whole library, and + # carries libcuopt.so -- the ld script names all three components, so it is only + # resolvable where all three are installed. + - package: + name: libcuopt + version: ${{ version }} + build: + script: + content: | + cmake --install cpp/build --component dev + string: cuda${{ cuda_major }}_${{ datetime_string }}_${{ head_rev }} + requirements: + build: + - cmake ${{ cmake_version }} + host: + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - ${{ pin_subpackage("libcuopt-mathopt", exact=True) }} + - ${{ pin_subpackage("libcuopt-routing", exact=True) }} + run: + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - ${{ pin_subpackage("libcuopt-mathopt", exact=True) }} + - ${{ pin_subpackage("libcuopt-routing", exact=True) }} + tests: + - package_contents: + files: + - lib/libcuopt.so about: homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} From 5a8028d602e5c27eb3ff4254e9c2d0d7853acbc0 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 17 Sep 2026 10:23:07 -0500 Subject: [PATCH 2/9] build(conda): pin the component libraries explicitly in libcuopt-tests The test binaries link the components directly, so depending on the metapackage's run deps to pull them into the build environment would be implicit. libcuopt stays for the ld script that tests linking -lcuopt need. Co-Authored-By: Claude Opus 5 --- conda/recipes/libcuopt/recipe.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index d73c5c7251..c1d1f86c20 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -353,7 +353,13 @@ outputs: - cmake ${{ cmake_version }} - ${{ stdlib("c") }} host: + # libcuopt for the ld script that the tests linking -lcuopt need, and the three + # components explicitly: the test binaries link them directly, so relying on the + # metapackage's run deps to drag them in would be implicit. - ${{ pin_subpackage("libcuopt", exact=True) }} + - ${{ pin_subpackage("libcuopt-client", exact=True) }} + - ${{ pin_subpackage("libcuopt-mathopt", exact=True) }} + - ${{ pin_subpackage("libcuopt-routing", exact=True) }} - libboost-devel - libcublas - libcudss-dev >=0.7,<0.8 From 0e729b3fa6f56c905c8fb05587ca476795682062 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 17 Sep 2026 15:09:24 -0500 Subject: [PATCH 3/9] build(conda): test each component package in isolation Every existing CI path installs libcuopt, which pulls all three components, so a library that quietly depends on another engine would still pass. Each component output now dlopens its own library with RTLD_NOW in a test environment holding only that package and its declared dependencies, which forces every relocation to resolve there rather than somewhere else in the graph. cuopt-grpc-server runs --help, which exits without binding a port and proves it resolves all three. Co-Authored-By: Claude Opus 5 --- conda/recipes/libcuopt/recipe.yaml | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index c1d1f86c20..c287b230a5 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -154,6 +154,15 @@ outputs: - lib/libcuopt_client.so - include/cuopt/error.hpp - include/cuopt/export.hpp + # Loads the library in an environment holding only this package and its declared + # dependencies, with RTLD_NOW so every relocation has to resolve. A component that + # quietly needs another engine fails here rather than passing because some other + # package happened to pull it in. + - script: | + python -c "import ctypes, os; ctypes.CDLL(os.path.join(os.environ['PREFIX'], 'lib', 'libcuopt_client.so'), os.RTLD_NOW)" + requirements: + run: + - python about: homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} @@ -215,6 +224,15 @@ outputs: files: - lib/libcuopt_mathopt.so - bin/cuopt_cli + # Loads the library in an environment holding only this package and its declared + # dependencies, with RTLD_NOW so every relocation has to resolve. A component that + # quietly needs another engine fails here rather than passing because some other + # package happened to pull it in. + - script: | + python -c "import ctypes, os; ctypes.CDLL(os.path.join(os.environ['PREFIX'], 'lib', 'libcuopt_mathopt.so'), os.RTLD_NOW)" + requirements: + run: + - python about: homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} @@ -263,6 +281,15 @@ outputs: - package_contents: files: - lib/libcuopt_routing.so + # Loads the library in an environment holding only this package and its declared + # dependencies, with RTLD_NOW so every relocation has to resolve. A component that + # quietly needs another engine fails here rather than passing because some other + # package happened to pull it in. + - script: | + python -c "import ctypes, os; ctypes.CDLL(os.path.join(os.environ['PREFIX'], 'lib', 'libcuopt_routing.so'), os.RTLD_NOW)" + requirements: + run: + - python about: homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} @@ -302,6 +329,10 @@ outputs: - package_contents: files: - bin/cuopt_grpc_server + # --help exits without binding a port, so this only proves the binary starts: + # it resolves all three component libraries from its own dependencies. + - script: | + cuopt_grpc_server --help about: homepage: ${{ load_from_file("python/cuopt/pyproject.toml").project.urls.Homepage }} license: ${{ load_from_file("python/cuopt/pyproject.toml").project.license }} From f33622dce4e9a63474a1850d192a8481a8ccfc89 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 17 Sep 2026 15:20:09 -0500 Subject: [PATCH 4/9] build(conda): define the per-package dependencies in dependencies.yaml The component dependency sets only existed inline in recipe.yaml, so nothing else could reference them and the wheel side would define its own copy. Adds depends_on_libcuopt_{client,mathopt,routing}, which resolve to the conda package or the suffixed wheel depending on the output, so both channels and any consumer share one definition. Adds a test_libcuopt_ file key per component, which generates an environment holding that package alone -- every other test environment pulls the whole graph through the libcuopt metapackage, which is what hides a component depending on an engine it does not declare. rattler-build has no way to read these lists, so the recipe still spells its host and run sections out; what is single-sourced is everything that consumes the packages. Co-Authored-By: Claude Opus 5 --- dependencies.yaml | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/dependencies.yaml b/dependencies.yaml index 1995027c58..04eddaecbb 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -37,6 +37,30 @@ files: - depends_on_rapids_logger - depends_on_rmm - rapids_build_backend + # Installs libcuopt-client alone. A component that quietly needs another + # engine fails to resolve or to load here, where every other test environment + # pulls the whole graph through the libcuopt metapackage and hides it. + test_libcuopt_client: + output: none + includes: + - cuda_version + - depends_on_libcuopt_client + # Installs libcuopt-mathopt alone. A component that quietly needs another + # engine fails to resolve or to load here, where every other test environment + # pulls the whole graph through the libcuopt metapackage and hides it. + test_libcuopt_mathopt: + output: none + includes: + - cuda_version + - depends_on_libcuopt_mathopt + # Installs libcuopt-routing alone. A component that quietly needs another + # engine fails to resolve or to load here, where every other test environment + # pulls the whole graph through the libcuopt metapackage and hides it. + test_libcuopt_routing: + output: none + includes: + - cuda_version + - depends_on_libcuopt_routing test_cpp: output: none includes: @@ -435,6 +459,78 @@ dependencies: - output_types: [requirements, pyproject] packages: - cuopt-cu13==26.10.*,>=0.0.0a0 + depends_on_libcuopt_client: + common: + - output_types: conda + packages: + - &libcuopt_client_unsuffixed libcuopt-client==26.10.*,>=0.0.0a0 + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + - --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: + cuda: "12.*" + cuda_suffixed: "true" + packages: + - libcuopt-client-cu12==26.10.*,>=0.0.0a0 + - matrix: + cuda: "13.*" + cuda_suffixed: "true" + packages: + - libcuopt-client-cu13==26.10.*,>=0.0.0a0 + - {matrix: null, packages: [*libcuopt_client_unsuffixed]} + depends_on_libcuopt_mathopt: + common: + - output_types: conda + packages: + - &libcuopt_mathopt_unsuffixed libcuopt-mathopt==26.10.*,>=0.0.0a0 + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + - --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: + cuda: "12.*" + cuda_suffixed: "true" + packages: + - libcuopt-mathopt-cu12==26.10.*,>=0.0.0a0 + - matrix: + cuda: "13.*" + cuda_suffixed: "true" + packages: + - libcuopt-mathopt-cu13==26.10.*,>=0.0.0a0 + - {matrix: null, packages: [*libcuopt_mathopt_unsuffixed]} + depends_on_libcuopt_routing: + common: + - output_types: conda + packages: + - &libcuopt_routing_unsuffixed libcuopt-routing==26.10.*,>=0.0.0a0 + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + - --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: + cuda: "12.*" + cuda_suffixed: "true" + packages: + - libcuopt-routing-cu12==26.10.*,>=0.0.0a0 + - matrix: + cuda: "13.*" + cuda_suffixed: "true" + packages: + - libcuopt-routing-cu13==26.10.*,>=0.0.0a0 + - {matrix: null, packages: [*libcuopt_routing_unsuffixed]} depends_on_libcuopt: common: - output_types: conda From 5112f482e1999d3077d6ccaa41eb0b84557ad67b Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 18 Sep 2026 10:09:08 -0500 Subject: [PATCH 5/9] build: install the cuopt export component with the metapackage conda-python-build failed with "cuopt::routing not found" because find_package(cuopt) had no config to read: rapids_export installs cuopt-config.cmake and the export set under a component named after the project, `cuopt`, which nothing installed. Every split package shipped without it, and the .deb lost it too. The libcuopt output and the CPack Development group now install that component alongside dev. This slipped through because the check comparing the components against a full install filtered out lib/cmake -- exactly the files that were missing. Without that filter the split is 65 files in and 65 out. Co-Authored-By: Claude Opus 5 --- conda/recipes/libcuopt/recipe.yaml | 3 +++ cpp/CMakeLists.txt | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index c287b230a5..fa95cbebcd 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -348,6 +348,9 @@ outputs: script: content: | cmake --install cpp/build --component dev + # rapids_export puts cuopt-config.cmake and the export set in a component named + # after the project, so find_package(cuopt) breaks without this. + cmake --install cpp/build --component cuopt string: cuda${{ cuda_major }}_${{ datetime_string }}_${{ head_rev }} requirements: build: diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index e74ea9f26a..1e5b3f34e0 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1197,7 +1197,9 @@ set(CPACK_DEB_COMPONENT_INSTALL ON) # CPack silently drops any component missing from this list, which would leave the # runtime .deb empty now that nothing installs under `runtime`. -set(CPACK_COMPONENTS_ALL runtime dev client mathopt client-dev mathopt-dev) +# `cuopt` is rapids_export's own component, holding cuopt-config.cmake and the export +# set. It is easy to miss because it is named after the project rather than a library. +set(CPACK_COMPONENTS_ALL runtime dev cuopt client mathopt client-dev mathopt-dev) if(NOT SKIP_ROUTING_BUILD) list(APPEND CPACK_COMPONENTS_ALL routing routing-dev) endif() @@ -1605,7 +1607,7 @@ foreach(_runtime_component client mathopt routing grpc-server) string(TOUPPER "${_runtime_component}" _uc) set(CPACK_COMPONENT_${_uc}_GROUP "Runtime") endforeach() -foreach(_dev_component client-dev mathopt-dev routing-dev) +foreach(_dev_component client-dev mathopt-dev routing-dev cuopt) string(TOUPPER "${_dev_component}" _uc) set(CPACK_COMPONENT_${_uc}_GROUP "Development") endforeach() From 6e3d6094a51ea41e17afd896c1a2e61fb0663c61 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 18 Sep 2026 12:30:45 -0500 Subject: [PATCH 6/9] build(conda): stop the cache run exports pinning the CUDA minor version Every output inherits the run exports of the shared build cache, whose host holds the union of all three components' dependencies. `cuda-nvtx` and `libcusparse` export a `cuda-version >=` bound, and the metapackage -- which had no `ignore_run_exports` at all -- also picked up `cuda-version >=12.9,<13` from the compiler, so `conda install libcuopt` was unsolvable against any CUDA other than the one it was built with. Drop those exports from the outputs that do not link them, and give the metapackage an ignore list covering the whole cache: it ships the linker script, the headers and the cmake config, so it links nothing. Routing reached cuBLAS only through the leaked cuDSS export, so it now names cuBLAS itself; mathopt links cuBLAS directly and had been relying on cuDSS to drag it in. Co-Authored-By: Claude Sonnet 4.6 --- conda/recipes/libcuopt/recipe.yaml | 50 +++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index fa95cbebcd..384c98679b 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -145,8 +145,18 @@ outputs: - libprotobuf - libabseil ignore_run_exports: + # Every output inherits the run exports of the shared build cache, whose host + # holds the union of all three components' dependencies. The CUDA math libs + # export a `>=` bound that drags `cuda-version` to the minor + # version the package happened to be built against, so they have to be dropped + # from outputs that do not link them. by_name: + - cuda-nvtx - cuda-version + - libboost + - libboost_iostreams + - libcudss + - libcusparse - librmm tests: - package_contents: @@ -206,6 +216,7 @@ outputs: - libboost-devel - librmm =${{ minor_version }} - cuda-nvrtc + - libcublas - libcudss >=0.7,<0.8 - libgomp >=9 - nccl >=2.19 @@ -270,13 +281,18 @@ outputs: - ${{ pin_subpackage("libcuopt-client", exact=True) }} - libboost-devel - librmm =${{ minor_version }} + # Reaches routing through cuDSS today, whose run export the cache leaks into + # every output. Routing does not use cuDSS, so it has to name cuBLAS itself. + - libcublas ignore_run_exports: by_name: - cuda-nvtx - cuda-version - - librmm - libboost - libboost_iostreams + - libcudss + - libcusparse + - librmm tests: - package_contents: files: @@ -324,7 +340,13 @@ outputs: - ${{ pin_subpackage("libcuopt-routing", exact=True) }} ignore_run_exports: by_name: + - cuda-nvtx - cuda-version + - libboost + - libboost_iostreams + - libcudss + - libcusparse + - librmm tests: - package_contents: files: @@ -363,6 +385,32 @@ outputs: - ${{ pin_subpackage("libcuopt-client", exact=True) }} - ${{ pin_subpackage("libcuopt-mathopt", exact=True) }} - ${{ pin_subpackage("libcuopt-routing", exact=True) }} + ignore_run_exports: + # This output ships the linker script, the headers and the cmake config -- no + # compiled object, so it links nothing and the three components carry every + # real dependency. Left alone it would still inherit the whole build cache's + # run exports, including `cuda-version >=`, which would pin + # `conda install libcuopt` to the CUDA minor version it was built against. + by_name: + - bzip2 + - c-ares + - cuda-nvtx + - cuda-version + - libabseil + - libboost + - libboost_iostreams + - libcudss + - libcusparse + - libgrpc + - libnvjitlink + - libprotobuf + - libre2-11 + - librmm + - libzlib + - nccl + - openssl + - re2 + - tbb tests: - package_contents: files: From cefc51db1a7d81275bfc3ac3a1c350c1bb4c3aac Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 18 Sep 2026 13:21:05 -0500 Subject: [PATCH 7/9] build(deps): make cuda_suffixed explicit for unsuffixed libcuopt-* entries The `{matrix: null, ...}` shorthand is functionally equivalent but omits the `cuda_suffixed: "false"` key that all other unsuffixed fallback entries in the file carry explicitly. Expand the three new depends_on_libcuopt_{client, mathopt,routing} blocks to match the existing style. Co-Authored-By: Claude Sonnet 4.6 --- dependencies.yaml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 04eddaecbb..d02e93fe7f 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -482,7 +482,10 @@ dependencies: cuda_suffixed: "true" packages: - libcuopt-client-cu13==26.10.*,>=0.0.0a0 - - {matrix: null, packages: [*libcuopt_client_unsuffixed]} + - matrix: + cuda_suffixed: "false" + packages: + - *libcuopt_client_unsuffixed depends_on_libcuopt_mathopt: common: - output_types: conda @@ -506,7 +509,10 @@ dependencies: cuda_suffixed: "true" packages: - libcuopt-mathopt-cu13==26.10.*,>=0.0.0a0 - - {matrix: null, packages: [*libcuopt_mathopt_unsuffixed]} + - matrix: + cuda_suffixed: "false" + packages: + - *libcuopt_mathopt_unsuffixed depends_on_libcuopt_routing: common: - output_types: conda @@ -530,7 +536,10 @@ dependencies: cuda_suffixed: "true" packages: - libcuopt-routing-cu13==26.10.*,>=0.0.0a0 - - {matrix: null, packages: [*libcuopt_routing_unsuffixed]} + - matrix: + cuda_suffixed: "false" + packages: + - *libcuopt_routing_unsuffixed depends_on_libcuopt: common: - output_types: conda From e4166af370d88fee0926a3052fe3110254e0d5f9 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 18 Sep 2026 14:00:44 -0500 Subject: [PATCH 8/9] build(conda): add libcusparse to libcuopt-mathopt run requirements libcuopt_mathopt.so DT_NEEDs libcusparse.so.12. The ignore_run_exports entry suppressed the run export from libcusparse-dev (to avoid pinning the CUDA minor version), but also removed the package from the resolved test environment, causing the RTLD_NOW load test to fail with: OSError: libcusparse.so.12: cannot open shared object file The fix keeps libcusparse in ignore_run_exports (so the cache's cuda-minor- pinned run export does not apply) and adds it explicitly to run without a version constraint (matching the same approach used for libcublas). Co-Authored-By: Claude Sonnet 4.6 --- conda/recipes/libcuopt/recipe.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 384c98679b..80416732ed 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -218,6 +218,7 @@ outputs: - cuda-nvrtc - libcublas - libcudss >=0.7,<0.8 + - libcusparse - libgomp >=9 - nccl >=2.19 - tbb From d91189c95b4404d0209bd7443dd565bbc22fd1a4 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 18 Sep 2026 15:18:29 -0500 Subject: [PATCH 9/9] build(conda): add cuopt-grpc-server to libcuopt metapackage run deps The monolithic libcuopt shipped bin/cuopt_grpc_server and the conda-python- tests suite has a test (test_cuopt_grpc_server_on_path) that verifies the binary is on PATH after installing cuopt-server. The split omitted cuopt-grpc-server from the libcuopt metapackage's run requirements, so conda install libcuopt no longer pulled in the server binary, breaking that test: FAILED test_cuopt_grpc_server_on_path - cuopt_grpc_server should be on PATH FAILED test_cuopt_grpc_server_help - FileNotFoundError: cuopt_grpc_server Add pin_subpackage("cuopt-grpc-server", exact=True) to both host and run so the metapackage carries the same content as the former single-output package. Co-Authored-By: Claude Sonnet 4.6 --- conda/recipes/libcuopt/recipe.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 80416732ed..634250d85c 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -382,10 +382,12 @@ outputs: - ${{ pin_subpackage("libcuopt-client", exact=True) }} - ${{ pin_subpackage("libcuopt-mathopt", exact=True) }} - ${{ pin_subpackage("libcuopt-routing", exact=True) }} + - ${{ pin_subpackage("cuopt-grpc-server", exact=True) }} run: - ${{ pin_subpackage("libcuopt-client", exact=True) }} - ${{ pin_subpackage("libcuopt-mathopt", exact=True) }} - ${{ pin_subpackage("libcuopt-routing", exact=True) }} + - ${{ pin_subpackage("cuopt-grpc-server", exact=True) }} ignore_run_exports: # This output ships the linker script, the headers and the cmake config -- no # compiled object, so it links nothing and the three components carry every