From 8d245ada9f3d9f72115060bce503fc8b17073722 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 31 Jul 2026 13:58:53 -0500 Subject: [PATCH 1/8] build: don't install libcuopt into conda env by default `build.sh` previously ran `cmake --install` into `$CONDA_PREFIX` on every build, so agents or developers that called `ninja` directly (or rebuilt without reinstalling) would run stale installed libraries rather than their freshly compiled code. Change the default to build-only and add `--install` to opt in when an explicit conda-env install is wanted. The `-n` (no-install) flag is removed since it is now the default behaviour. Also simplify the cmake invocation: the separate `make ninja-build` code path that was only reachable via `-n` is replaced by a single `cmake --build` call with an optional `--target install`. The conda recipe for libcuopt already passed `-n` explicitly; that flag is dropped since it is now redundant. Co-Authored-By: Claude Sonnet 4.6 (1M context) Signed-off-by: Ramakrishna Prabhu --- build.sh | 17 +++++++---------- conda/recipes/libcuopt/recipe.yaml | 2 +- .../references/build_and_test.md | 8 +++++++- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/build.sh b/build.sh index 8293818dfa..6c7cef7627 100755 --- a/build.sh +++ b/build.sh @@ -14,7 +14,7 @@ ARGS=$* REPODIR=$(cd "$(dirname "$0")"; pwd) LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build} -VALIDARGS="clean codegen libcuopt cuopt_grpc_server cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" +VALIDARGS="clean codegen libcuopt cuopt_grpc_server cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] --install --allgpuarch --ci-only-arch --show_depr_warn -h --help" HELP="$0 [ ...] [ ...] where is: clean - remove all existing build artifacts and configuration (start over) @@ -34,7 +34,7 @@ HELP="$0 [ ...] [ ...] -fsanitize - Build with AddressSanitizer and UndefinedBehaviorSanitizer -tsan - Build with ThreadSanitizer (cannot be used with -fsanitize or -msan) -msan - Build with MemorySanitizer (cannot be used with -fsanitize or -tsan) - -n - no install step + --install - install built libraries into the active conda environment (default: build only, no install) --no-fetch-rapids - don't fetch rapids dependencies -l= - log level. Options are: TRACE | DEBUG | INFO | WARN | ERROR | CRITICAL | OFF. Default=INFO --verbose-pdlp - verbose mode for pdlp solver @@ -53,7 +53,7 @@ HELP="$0 [ ...] [ ...] --show_depr_warn - show cmake deprecation warnings -h - print this text - default action (no args) is to build and install 'libcuopt', 'cuopt', 'cuopt_server', and 'cuopt_sh_client' targets (pass 'docs' explicitly to build documentation) + default action (no args) is to build 'libcuopt', 'cuopt', 'cuopt_server', and 'cuopt_sh_client' targets without installing into the conda environment (pass --install to also install libcuopt into the active conda environment; pass 'docs' explicitly to build documentation) libcuopt build dir is: ${LIBCUOPT_BUILD_DIR} @@ -71,7 +71,7 @@ VERBOSE_FLAG="" BUILD_TYPE=Release DEFINE_ASSERT=False DEFINE_PDLP_VERBOSE_MODE=False -INSTALL_TARGET=install +INSTALL_TARGET="" BUILD_DISABLE_DEPRECATION_WARNING=ON BUILD_ALL_GPU_ARCH=0 BUILD_CI_ONLY=0 @@ -213,8 +213,8 @@ fi if hasArg --verbose-pdlp; then DEFINE_PDLP_VERBOSE_MODE=true fi -if hasArg -n; then - INSTALL_TARGET="" +if hasArg --install; then + INSTALL_TARGET=install fi if hasArg --no-fetch-rapids; then FETCH_RAPIDS=OFF @@ -388,11 +388,8 @@ if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then if hasArg cuopt_grpc_server && ! hasArg libcuopt && ! buildAll; then # Build only the gRPC server (ninja resolves libcuopt as a dependency) cmake --build "${LIBCUOPT_BUILD_DIR}" --target cuopt_grpc_server ${VERBOSE_FLAG} ${JFLAG} - elif hasArg -n; then - # Manual make invocation to start its jobserver - make ${JFLAG} -C "${REPODIR}/cpp" LIBCUOPT_BUILD_DIR="${LIBCUOPT_BUILD_DIR}" VERBOSE_FLAG="${VERBOSE_FLAG}" PARALLEL_LEVEL="${PARALLEL_LEVEL}" ninja-build else - cmake --build "${LIBCUOPT_BUILD_DIR}" --target ${INSTALL_TARGET} ${VERBOSE_FLAG} ${JFLAG} + cmake --build "${LIBCUOPT_BUILD_DIR}" ${INSTALL_TARGET:+--target ${INSTALL_TARGET}} ${VERBOSE_FLAG} ${JFLAG} fi fi diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 268c1eaef0..559bb83e73 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -29,7 +29,7 @@ cache: export CXXFLAGS=$(echo $CXXFLAGS | sed -E 's@\-fdebug\-prefix\-map[^ ]*@@g') set +x - ./build.sh -n -v ${BUILD_EXTRA_FLAGS} libcuopt deb --allgpuarch --cmake-args=\"-DCMAKE_INSTALL_LIBDIR=lib -DBUILD_LP_BENCHMARKS=ON -DBUILD_MIP_BENCHMARKS=ON\" + ./build.sh -v ${BUILD_EXTRA_FLAGS} libcuopt deb --allgpuarch --cmake-args=\"-DCMAKE_INSTALL_LIBDIR=lib -DBUILD_LP_BENCHMARKS=ON -DBUILD_MIP_BENCHMARKS=ON\" secrets: - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY diff --git a/skills/cuopt-developer/references/build_and_test.md b/skills/cuopt-developer/references/build_and_test.md index d2b7cf9d27..7adbe0ec71 100644 --- a/skills/cuopt-developer/references/build_and_test.md +++ b/skills/cuopt-developer/references/build_and_test.md @@ -32,11 +32,17 @@ cat "$CG/cpu.max" # " "; effective cores = quota / period ./build.sh ``` +By default, `build.sh` builds without installing `libcuopt` into the conda environment. This prevents stale installed libraries from shadowing freshly compiled code. Pass `--install` to explicitly install into the active conda environment: + +```bash +./build.sh --install # build and install libcuopt into conda env +``` + ## Build Specific Components ```bash ./build.sh --help # Lists build options -./build.sh libcuopt # C++ library +./build.sh libcuopt # C++ library (no conda install) ./build.sh libcuopt --skip-routing-build --skip-tests-build --skip-c-python-adapters --cache-tool=ccache # native LP/MIP-focused build without routing/tests/adapters ./build.sh cuopt # Python package ./build.sh cuopt_server # Server From 024cd08d2430ffe6790d109ef12430a4a5079ea4 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 31 Jul 2026 14:01:05 -0500 Subject: [PATCH 2/8] build: restore make ninja-build as the default build path The previous commit accidentally removed the `make ninja-build` path that was used for build-only (no-install) invocations. Restore it as the default path; the `--install` flag takes the cmake install path. Co-Authored-By: Claude Sonnet 4.6 (1M context) Signed-off-by: Ramakrishna Prabhu --- build.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 6c7cef7627..15c8856b4b 100755 --- a/build.sh +++ b/build.sh @@ -388,8 +388,11 @@ if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then if hasArg cuopt_grpc_server && ! hasArg libcuopt && ! buildAll; then # Build only the gRPC server (ninja resolves libcuopt as a dependency) cmake --build "${LIBCUOPT_BUILD_DIR}" --target cuopt_grpc_server ${VERBOSE_FLAG} ${JFLAG} + elif hasArg --install; then + cmake --build "${LIBCUOPT_BUILD_DIR}" --target ${INSTALL_TARGET} ${VERBOSE_FLAG} ${JFLAG} else - cmake --build "${LIBCUOPT_BUILD_DIR}" ${INSTALL_TARGET:+--target ${INSTALL_TARGET}} ${VERBOSE_FLAG} ${JFLAG} + # Manual make invocation to start its jobserver + make ${JFLAG} -C "${REPODIR}/cpp" LIBCUOPT_BUILD_DIR="${LIBCUOPT_BUILD_DIR}" VERBOSE_FLAG="${VERBOSE_FLAG}" PARALLEL_LEVEL="${PARALLEL_LEVEL}" ninja-build fi fi From 935f423b1233d7b20be7a2db5741fb5c9ddc8999 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 31 Jul 2026 14:03:49 -0500 Subject: [PATCH 3/8] docs: update CONTRIBUTING.md to reflect no-default-install behaviour Co-Authored-By: Claude Sonnet 4.6 (1M context) Signed-off-by: Ramakrishna Prabhu --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 95f5e0b327..daca4f87ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -173,10 +173,10 @@ conda activate ./.cuopt_env pinnings are changed. - A `build.sh` script is provided in `$CUOPT_HOME`. Running the script with no additional arguments - will install the `libcuopt`, `cuopt`, `cuopt-server`, `cuopt-sh-client` libraries and build the`documentation`. By default, the libraries are - installed to the `$CONDA_PREFIX` directory. To install into a different location, set the location - in `$INSTALL_PREFIX`. Finally, note that the script depends on the `nvcc` executable being on your - path, or defined in `$CUDACXX`. + will build the `libcuopt`, `cuopt`, `cuopt-server`, and `cuopt-sh-client` libraries without + installing them into the conda environment. Pass `--install` to also install `libcuopt` into the + active conda environment (`$CONDA_PREFIX`, or a custom location via `$INSTALL_PREFIX`). Note that + the script depends on the `nvcc` executable being on your path, or defined in `$CUDACXX`. ```bash cd $CUOPT_HOME From f85de73650a16c9c3adcd503902fa2495d60b085 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 31 Jul 2026 14:11:40 -0500 Subject: [PATCH 4/8] review: fix CONTRIBUTING.md env var name and quote shell vars - CONTRIBUTING.md referenced \$INSTALL_PREFIX but build.sh derives the install prefix from \$PREFIX (falling back to \$CONDA_PREFIX); use the correct variable name - Double-quote \$VERBOSE_FLAG and \$JFLAG in the cmake/make invocations to silence shellcheck SC2086 and prevent word splitting Co-Authored-By: Claude Sonnet 4.6 (1M context) Signed-off-by: Ramakrishna Prabhu --- CONTRIBUTING.md | 2 +- build.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index daca4f87ab..53ff2ce9dc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -175,7 +175,7 @@ conda activate ./.cuopt_env - A `build.sh` script is provided in `$CUOPT_HOME`. Running the script with no additional arguments will build the `libcuopt`, `cuopt`, `cuopt-server`, and `cuopt-sh-client` libraries without installing them into the conda environment. Pass `--install` to also install `libcuopt` into the - active conda environment (`$CONDA_PREFIX`, or a custom location via `$INSTALL_PREFIX`). Note that + active conda environment (`$CONDA_PREFIX`, or a custom location via `$PREFIX`). Note that the script depends on the `nvcc` executable being on your path, or defined in `$CUDACXX`. ```bash diff --git a/build.sh b/build.sh index 15c8856b4b..95c0ae5066 100755 --- a/build.sh +++ b/build.sh @@ -387,12 +387,12 @@ if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then JFLAG="${PARALLEL_LEVEL:+-j${PARALLEL_LEVEL}}" if hasArg cuopt_grpc_server && ! hasArg libcuopt && ! buildAll; then # Build only the gRPC server (ninja resolves libcuopt as a dependency) - cmake --build "${LIBCUOPT_BUILD_DIR}" --target cuopt_grpc_server ${VERBOSE_FLAG} ${JFLAG} + cmake --build "${LIBCUOPT_BUILD_DIR}" --target cuopt_grpc_server ${VERBOSE_FLAG} "${JFLAG}" elif hasArg --install; then - cmake --build "${LIBCUOPT_BUILD_DIR}" --target ${INSTALL_TARGET} ${VERBOSE_FLAG} ${JFLAG} + cmake --build "${LIBCUOPT_BUILD_DIR}" --target "${INSTALL_TARGET}" ${VERBOSE_FLAG} "${JFLAG}" else # Manual make invocation to start its jobserver - make ${JFLAG} -C "${REPODIR}/cpp" LIBCUOPT_BUILD_DIR="${LIBCUOPT_BUILD_DIR}" VERBOSE_FLAG="${VERBOSE_FLAG}" PARALLEL_LEVEL="${PARALLEL_LEVEL}" ninja-build + make "${JFLAG}" -C "${REPODIR}/cpp" LIBCUOPT_BUILD_DIR="${LIBCUOPT_BUILD_DIR}" VERBOSE_FLAG="${VERBOSE_FLAG}" PARALLEL_LEVEL="${PARALLEL_LEVEL}" ninja-build fi fi From 2bdcf0cdbec4c908a61647ff04583a0db7562b3c Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 31 Jul 2026 14:37:24 -0500 Subject: [PATCH 5/8] build: print libcuopt.so path in version info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the resolved library path to the version line that cuopt_cli prints at the start of every solve. Makes it immediately visible which library is actually loaded — build-dir or conda-installed. Co-Authored-By: Claude Sonnet 4.6 (1M context) Signed-off-by: Ramakrishna Prabhu --- cpp/src/utilities/version_info.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cpp/src/utilities/version_info.cpp b/cpp/src/utilities/version_info.cpp index dfab609cc5..9f6ec0c06a 100644 --- a/cpp/src/utilities/version_info.cpp +++ b/cpp/src/utilities/version_info.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -173,13 +175,18 @@ void print_version_info(int num_devices) int major = version / 1000; int minor = (version % 1000) / 10; - CUOPT_LOG_INFO("cuOpt version: %d.%d.%d, git hash: %s, host arch: %s, device archs: %s", + Dl_info dl_info{}; + const char* lib_path = (dladdr(reinterpret_cast(&print_version_info), &dl_info) && dl_info.dli_fname) + ? dl_info.dli_fname + : ""; + CUOPT_LOG_INFO("cuOpt version: %d.%d.%d, git hash: %s, host arch: %s, device archs: %s, lib: %s", CUOPT_VERSION_MAJOR, CUOPT_VERSION_MINOR, CUOPT_VERSION_PATCH, CUOPT_GIT_COMMIT_HASH, CUOPT_CPU_ARCHITECTURE, - CUOPT_CUDA_ARCHITECTURES); + CUOPT_CUDA_ARCHITECTURES, + lib_path); CUOPT_LOG_INFO("CPU: %s, threads (physical/logical): %d/%d, RAM: %.2f GiB", get_cpu_model().c_str(), get_physical_cores(), From 4cb218d0b593874928d4ad9d56ee9283e65fd226 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 31 Jul 2026 14:47:44 -0500 Subject: [PATCH 6/8] build: remove stale conda libcuopt when building without --install conda's \$LDFLAGS injects -rpath,\$CONDA_PREFIX/lib into every linked binary, which means a previously installed libcuopt.so in the conda env always shadows the freshly compiled build-dir copy regardless of RUNPATH ordering. When building without --install, explicitly remove any libcuopt*.so* from the conda prefix so the build-dir library is the only copy available. Co-Authored-By: Claude Sonnet 4.6 (1M context) Signed-off-by: Ramakrishna Prabhu --- build.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build.sh b/build.sh index 95c0ae5066..c909ba8a50 100755 --- a/build.sh +++ b/build.sh @@ -355,6 +355,17 @@ if hasArg codegen; then echo "Done. Remember to commit the generated files." fi +################################################################################ +# When not installing, remove any stale libcuopt.so from the conda prefix. +# conda's $LDFLAGS injects -rpath,$CONDA_PREFIX/lib into every binary, so a +# previously installed copy would shadow the freshly compiled build-dir one. +if [ -z "${INSTALL_TARGET}" ] && [ -n "${INSTALL_PREFIX}" ]; then + if ls "${INSTALL_PREFIX}/lib/libcuopt"*.so* 2>/dev/null | grep -q .; then + echo "Removing stale libcuopt from ${INSTALL_PREFIX}/lib to prevent shadowing the build-dir library..." + rm -f "${INSTALL_PREFIX}"/lib/libcuopt*.so* + fi +fi + ################################################################################ # Configure and build libcuopt (and optionally just the gRPC server) if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then From 1e7efd5d49827f4c73423d348fdb926a81ca9dc9 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 31 Jul 2026 14:58:53 -0500 Subject: [PATCH 7/8] build: fix pre-commit and remove debug lib-path print - Remove dladdr/lib-path debug print added during local testing - Fix shellcheck SC2010: replace ls|grep with compgen glob check Co-Authored-By: Claude Sonnet 4.6 (1M context) Signed-off-by: Ramakrishna Prabhu --- build.sh | 2 +- cpp/src/utilities/version_info.cpp | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/build.sh b/build.sh index c909ba8a50..da806fe56e 100755 --- a/build.sh +++ b/build.sh @@ -360,7 +360,7 @@ fi # conda's $LDFLAGS injects -rpath,$CONDA_PREFIX/lib into every binary, so a # previously installed copy would shadow the freshly compiled build-dir one. if [ -z "${INSTALL_TARGET}" ] && [ -n "${INSTALL_PREFIX}" ]; then - if ls "${INSTALL_PREFIX}/lib/libcuopt"*.so* 2>/dev/null | grep -q .; then + if compgen -G "${INSTALL_PREFIX}/lib/libcuopt*.so*" > /dev/null 2>&1; then echo "Removing stale libcuopt from ${INSTALL_PREFIX}/lib to prevent shadowing the build-dir library..." rm -f "${INSTALL_PREFIX}"/lib/libcuopt*.so* fi diff --git a/cpp/src/utilities/version_info.cpp b/cpp/src/utilities/version_info.cpp index 9f6ec0c06a..dfab609cc5 100644 --- a/cpp/src/utilities/version_info.cpp +++ b/cpp/src/utilities/version_info.cpp @@ -12,8 +12,6 @@ #include #include -#include - #include #include #include @@ -175,18 +173,13 @@ void print_version_info(int num_devices) int major = version / 1000; int minor = (version % 1000) / 10; - Dl_info dl_info{}; - const char* lib_path = (dladdr(reinterpret_cast(&print_version_info), &dl_info) && dl_info.dli_fname) - ? dl_info.dli_fname - : ""; - CUOPT_LOG_INFO("cuOpt version: %d.%d.%d, git hash: %s, host arch: %s, device archs: %s, lib: %s", + CUOPT_LOG_INFO("cuOpt version: %d.%d.%d, git hash: %s, host arch: %s, device archs: %s", CUOPT_VERSION_MAJOR, CUOPT_VERSION_MINOR, CUOPT_VERSION_PATCH, CUOPT_GIT_COMMIT_HASH, CUOPT_CPU_ARCHITECTURE, - CUOPT_CUDA_ARCHITECTURES, - lib_path); + CUOPT_CUDA_ARCHITECTURES); CUOPT_LOG_INFO("CPU: %s, threads (physical/logical): %d/%d, RAM: %.2f GiB", get_cpu_model().c_str(), get_physical_cores(), From f77b8503e824c2d42124ab98a9836dcfc8279b7a Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 31 Jul 2026 15:41:16 -0500 Subject: [PATCH 8/8] build: only remove stale libcuopt when rebuilding the C++ library The unconditional removal broke conda-python-build: rattler-build legitimately installs libcuopt into the host prefix before running ./build.sh cuopt, and we were deleting it immediately. Scope the removal to invocations that actually rebuild libcuopt (default/libcuopt/cuopt_grpc_server targets); Python-only builds are left untouched. Co-Authored-By: Claude Sonnet 4.6 (1M context) Signed-off-by: Ramakrishna Prabhu --- build.sh | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/build.sh b/build.sh index da806fe56e..e6ed479192 100755 --- a/build.sh +++ b/build.sh @@ -356,13 +356,18 @@ if hasArg codegen; then fi ################################################################################ -# When not installing, remove any stale libcuopt.so from the conda prefix. -# conda's $LDFLAGS injects -rpath,$CONDA_PREFIX/lib into every binary, so a -# previously installed copy would shadow the freshly compiled build-dir one. +# When not installing and rebuilding the C++ library, remove any stale +# libcuopt.so from the conda prefix. conda's $LDFLAGS injects +# -rpath,$CONDA_PREFIX/lib into every binary, so a previously installed copy +# would shadow the freshly compiled build-dir one. +# Only done when actually building libcuopt (not Python-only builds) to avoid +# removing a legitimately conda-installed libcuopt that Python packages depend on. if [ -z "${INSTALL_TARGET}" ] && [ -n "${INSTALL_PREFIX}" ]; then - if compgen -G "${INSTALL_PREFIX}/lib/libcuopt*.so*" > /dev/null 2>&1; then - echo "Removing stale libcuopt from ${INSTALL_PREFIX}/lib to prevent shadowing the build-dir library..." - rm -f "${INSTALL_PREFIX}"/lib/libcuopt*.so* + if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then + if compgen -G "${INSTALL_PREFIX}/lib/libcuopt*.so*" > /dev/null 2>&1; then + echo "Removing stale libcuopt from ${INSTALL_PREFIX}/lib to prevent shadowing the build-dir library..." + rm -f "${INSTALL_PREFIX}"/lib/libcuopt*.so* + fi fi fi