Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,4 @@ docs/cuopt/build
cpp/include/cuopt/semantic_version.hpp
!datasets/quadratic_programming
!datasets/quadratic_programming/**
dev_scripts/test_c_api
82 changes: 82 additions & 0 deletions GRPC_ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# gRPC-based Remote Solve Architecture

## Overview

cuOpt remote solve uses gRPC for transport and protobuf-generated stubs for the service API.
The request/response payloads are serialized with a protobuf-based serializer that maps
cuOpt data structures to protobuf messages. This preserves existing semantics while
moving the network layer to a standard, well-supported RPC stack.

## Service Definition

The gRPC service is defined in `cpp/src/linear_programming/utilities/cuopt_remote_service.proto`
and imports the message schema in `cuopt_remote.proto`. Code is generated by `protoc`
plus `grpc_cpp_plugin` during the build.

Core RPCs include:

- `SubmitJob` / `UploadAndSubmit`
- `CheckStatus`
- `GetResult` / `StreamResult`
- `StreamLogs`
- `CancelJob`
- `DeleteResult`
- `GetIncumbents`

## Components

### gRPC Server (`cuopt_grpc_server`)

- Source: `cpp/cuopt_grpc_server.cpp`
- Implements `CuOptRemoteService` and owns the worker process pool.
- Workers communicate with the main server process via shared memory + pipes.
- For results, the server calls `to_host()` before serialization.
- Supports streaming logs and incumbents through gRPC streaming endpoints.

### gRPC Client Path (C++)

- Client logic lives in `cpp/src/linear_programming/utilities/remote_solve_grpc.cpp`
and is used by `remote_solve.cu` and `cuopt_cli`.
- The client serializes problems using the protobuf serializer, submits them
via gRPC, and deserializes results back into cuOpt solution objects.

### Serialization Layer

- Default serializer: `cpp/src/linear_programming/utilities/protobuf_serializer.cu`
- Interface: `cpp/include/cuopt/linear_programming/utilities/remote_serialization.hpp`
- Optional plugin override: `CUOPT_SERIALIZER_LIB` can load a custom serializer.
- The serializer uses protobuf message types defined in `cuopt_remote.proto`.

## Data Flow (LP/MIP)

1. Client builds a problem (LP/MIP).
2. Serializer converts the problem + settings into protobuf bytes.
3. gRPC `SubmitJob` or `UploadAndSubmit` sends the bytes to the server.
4. Server deserializes to cuOpt data structures.
5. Server runs `solve_lp` / `solve_mip` in a worker process.
6. Server calls `to_host()` and serializes the solution to protobuf bytes.
7. Client retrieves results via `GetResult` / `StreamResult` and deserializes.

## Generated Code (protoc output)

Generated files are written to the CMake binary directory (not checked into source):

- `cuopt_remote.pb.cc/.h`
- `cuopt_remote_service.pb.cc/.h`
- `cuopt_remote_service.grpc.pb.cc/.h`

## Build Integration

`cpp/CMakeLists.txt` drives code generation:

- Locates `protoc` and `grpc_cpp_plugin`
- Runs `protoc` to generate the `*.pb.cc/.h` sources
- Adds generated sources to the `cuopt` library
- Builds `cuopt_grpc_server` only when gRPC is available

## Security Notes

- Service stubs and message parsing are generated by `protoc` and `grpc_cpp_plugin`.
- Payload serialization uses protobuf message APIs rather than hand-written parsing.
- gRPC provides HTTP/2 framing, flow control, and standard status codes.
```
Comment on lines +77 to +82
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove stray code fence at end of file.

There's an orphan code fence (```) at line 82 that appears to be a formatting error. The Security Notes section is prose, not code, so this should be removed.

📝 Suggested fix
 - gRPC provides HTTP/2 framing, flow control, and standard status codes.
-```
🤖 Prompt for AI Agents
In `@GRPC_ARCHITECTURE.md` around lines 77 - 82, The Security Notes section ends
with an orphan triple backtick; open the GRPC_ARCHITECTURE.md file, locate the
"Security Notes" header and the trailing ``` after the prose (the stray code
fence shown in the diff) and remove that triple-backtick so the section is plain
text without a stray code fence.

137 changes: 132 additions & 5 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ REPODIR=$(cd "$(dirname "$0")"; pwd)
LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build}
LIBMPS_PARSER_BUILD_DIR=${LIBMPS_PARSER_BUILD_DIR:=${REPODIR}/cpp/libmps_parser/build}

VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser 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-fatbin-write --host-lineinfo [--cmake-args=\\\"<args>\\\"] [--cache-tool=<tool>] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help"
VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client cuopt_grpc_server 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-fatbin-write --host-lineinfo [--cmake-args=\\\"<args>\\\"] [--cache-tool=<tool>] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help"
HELP="$0 [<target> ...] [<flag> ...]
where <target> is:
clean - remove all existing build artifacts and configuration (start over)
Expand All @@ -25,6 +25,7 @@ HELP="$0 [<target> ...] [<flag> ...]
cuopt - build the cuopt Python package
cuopt_server - build the cuopt_server Python package
cuopt_sh_client - build cuopt self host client
cuopt_grpc_server - build the cuopt gRPC server executable (prototype)
docs - build the docs
deb - build deb package (requires libcuopt to be built first)
and <flag> is:
Expand Down Expand Up @@ -53,7 +54,7 @@ HELP="$0 [<target> ...] [<flag> ...]
--show_depr_warn - show cmake deprecation warnings
-h - print this text

default action (no args) is to build and install 'libcuopt' then 'cuopt' then 'docs' targets
default action (no args) is to build and install 'libcuopt', 'cuopt', 'cuopt_grpc_server', then 'docs'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Step 1: Inspect how CUOPT_ENABLE_GRPC is handled in CMakeLists.txt files
rg -n "CUOPT_ENABLE_GRPC|find_package\(gRPC|gRPC_FOUND" -t cmake -A 3

Repository: NVIDIA/cuopt

Length of output: 2406


🏁 Script executed:

#!/bin/bash
# Step 2: Examine build.sh lines around 57 and 461-483 to understand the gRPC build block
head -75 build.sh | tail -25

Repository: NVIDIA/cuopt

Length of output: 1343


🏁 Script executed:

#!/bin/bash
# Step 3: Check lines 461+ in build.sh for the gRPC build block
sed -n '450,490p' build.sh

Repository: NVIDIA/cuopt

Length of output: 1901


Gate cuopt_grpc_server build on gRPC availability, or remove it from default builds.
Lines 57 and 455–483: The default build (no arguments) includes cuopt_grpc_server and unconditionally forces -DCUOPT_ENABLE_GRPC=ON (line 468). Since gRPC is designed as optional in CMakeLists.txt, this forces gRPC as a hard dependency for default builds, causing failures when gRPC is not installed. Either gate this block on gRPC availability detection or make it explicit via user argument.

🤖 Prompt for AI Agents
In `@build.sh` at line 57, The default build unconditionally enables and builds
cuopt_grpc_server by forcing -DCUOPT_ENABLE_GRPC=ON; change build.sh so the
cuopt_grpc_server build is only executed when gRPC is detected or when the user
explicitly requests it: add a detection step (e.g., run pkg-config --exists grpc
or try cmake -DgRPC_DIR=.../ find_package(grpc) check) and gate the creation of
the cuopt_grpc_server target and the -DCUOPT_ENABLE_GRPC=ON flag on that
detection, otherwise skip adding cuopt_grpc_server to the default build (or
require an explicit CLI arg like --enable-grpc) so the script no longer forces
gRPC as a hard dependency.


libcuopt build dir is: ${LIBCUOPT_BUILD_DIR}

Expand Down Expand Up @@ -171,6 +172,13 @@ function cmakeArgs {
ARGS=${ARGS//$EXTRA_CMAKE_ARGS/}
# Filter the full argument down to just the extra string that will be added to cmake call
EXTRA_CMAKE_ARGS=$(echo "$EXTRA_CMAKE_ARGS" | grep -Eo "\".+\"" | sed -e 's/^"//' -e 's/"$//')
else
# Support unquoted --cmake-args=VALUE form.
EXTRA_CMAKE_ARGS=$(echo "$ARGS" | { grep -Eo "\-\-cmake\-args=[^ ]+" || true; })
if [[ -n ${EXTRA_CMAKE_ARGS} ]]; then
ARGS=${ARGS//$EXTRA_CMAKE_ARGS/}
EXTRA_CMAKE_ARGS=${EXTRA_CMAKE_ARGS#--cmake-args=}
fi
fi
fi

Expand Down Expand Up @@ -280,6 +288,52 @@ if ! contains_string "DFIND_MPS_PARSER_CPP" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DFIND_MPS_PARSER_CPP=ON")
fi

# Prefer config packages to avoid mixing system and conda find modules.
if ! contains_string "CMAKE_FIND_PACKAGE_PREFER_CONFIG" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON")
fi

# Default to the active install prefix for dependency lookup unless overridden.
if ! contains_string "CMAKE_PREFIX_PATH" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX}")
fi

# If conda-provided protobuf/grpc configs are available, prefer them by default.
if [ -d "${INSTALL_PREFIX}/lib/cmake/protobuf" ]; then
if ! contains_string "Protobuf_DIR" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DProtobuf_DIR=${INSTALL_PREFIX}/lib/cmake/protobuf")
fi
fi
if [ -d "${INSTALL_PREFIX}/lib/cmake/grpc" ]; then
if ! contains_string "gRPC_DIR" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DgRPC_DIR=${INSTALL_PREFIX}/lib/cmake/grpc")
fi
fi

# Prefer conda's ZLIB config if available to avoid system static zlib references.
if [ -d "${INSTALL_PREFIX}/lib/cmake/ZLIB" ]; then
if ! contains_string "ZLIB_DIR" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DZLIB_DIR=${INSTALL_PREFIX}/lib/cmake/ZLIB")
fi
fi

# Avoid pulling system ZLIB config that references missing libz.a.
if [ -d "/usr/lib64/cmake/ZLIB" ] || [ -d "/lib64/cmake/ZLIB" ]; then
if ! contains_string "CMAKE_IGNORE_PATH" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DCMAKE_IGNORE_PATH=/usr/lib64/cmake/ZLIB;/lib64/cmake/ZLIB")
fi
fi

# Prefer shared zlib if FindZLIB is used.
if ! contains_string "ZLIB_USE_STATIC_LIBS" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DZLIB_USE_STATIC_LIBS=OFF")
fi

# Hint FindZLIB to use the active prefix if no config is found.
if ! contains_string "ZLIB_ROOT" "${EXTRA_CMAKE_ARGS[@]}"; then
EXTRA_CMAKE_ARGS+=("-DZLIB_ROOT=${INSTALL_PREFIX}")
fi

# If clean given, run it prior to any other steps
if hasArg clean; then
# If the dirs to clean are mounted dirs in a container, the
Expand Down Expand Up @@ -362,6 +416,13 @@ fi
if buildAll || hasArg libcuopt; then
mkdir -p "${LIBCUOPT_BUILD_DIR}"
cd "${LIBCUOPT_BUILD_DIR}"
# If the cache points at system ZLIB, clear it so updated hints take effect.
if [ -f "${LIBCUOPT_BUILD_DIR}/CMakeCache.txt" ]; then
if grep -Eq "/(usr/)?lib64/cmake/ZLIB" "${LIBCUOPT_BUILD_DIR}/CMakeCache.txt"; then
rm -f "${LIBCUOPT_BUILD_DIR}/CMakeCache.txt"
rm -rf "${LIBCUOPT_BUILD_DIR}/CMakeFiles"
fi
fi
cmake -DDEFINE_ASSERT=${DEFINE_ASSERT} \
-DDEFINE_BENCHMARK="${DEFINE_BENCHMARK}" \
-DDEFINE_PDLP_VERBOSE_MODE=${DEFINE_PDLP_VERBOSE_MODE} \
Expand Down Expand Up @@ -394,6 +455,49 @@ if buildAll || hasArg libcuopt; then
fi
fi

################################################################################
# Build the cuopt gRPC server (prototype)
if buildAll || hasArg cuopt_grpc_server; then
mkdir -p "${LIBCUOPT_BUILD_DIR}"
cd "${LIBCUOPT_BUILD_DIR}"

# Ensure gRPC is enabled and configured in this build directory.
cmake -DDEFINE_ASSERT=${DEFINE_ASSERT} \
-DDEFINE_BENCHMARK="${DEFINE_BENCHMARK}" \
-DDEFINE_PDLP_VERBOSE_MODE=${DEFINE_PDLP_VERBOSE_MODE} \
-DLIBCUOPT_LOGGING_LEVEL="${LOGGING_ACTIVE_LEVEL}" \
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \
-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES} \
-DDISABLE_DEPRECATION_WARNING=${BUILD_DISABLE_DEPRECATION_WARNING} \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
-DFETCH_RAPIDS=${FETCH_RAPIDS} \
-DBUILD_LP_ONLY=${BUILD_LP_ONLY} \
-DBUILD_SANITIZER=${BUILD_SANITIZER} \
-DBUILD_TSAN=${BUILD_TSAN} \
-DBUILD_MSAN=${BUILD_MSAN} \
-DSKIP_C_PYTHON_ADAPTERS=${SKIP_C_PYTHON_ADAPTERS} \
-DBUILD_TESTS=$((1 - ${SKIP_TESTS_BUILD})) \
-DSKIP_ROUTING_BUILD=${SKIP_ROUTING_BUILD} \
-DWRITE_FATBIN=${WRITE_FATBIN} \
-DHOST_LINEINFO=${HOST_LINEINFO} \
-DINSTALL_TARGET="${INSTALL_TARGET}" \
-DCUOPT_ENABLE_GRPC=ON \
"${CACHE_ARGS[@]}" \
"${EXTRA_CMAKE_ARGS[@]}" \
"${REPODIR}"/cpp

# Build the server target
cmake --build "${LIBCUOPT_BUILD_DIR}" --target cuopt_grpc_server ${VERBOSE_FLAG} -j"${PARALLEL_LEVEL}"

# Install the server executable
if [ -z "${INSTALL_TARGET}" ]; then
echo "Skipping install of cuopt_grpc_server (-n flag set)"
else
install -m 755 "${LIBCUOPT_BUILD_DIR}/cuopt_grpc_server" "${INSTALL_PREFIX}/bin/"
echo "Installed cuopt_grpc_server to ${INSTALL_PREFIX}/bin/"
fi
fi

################################################################################
# Build deb package
if hasArg deb; then
Expand All @@ -414,16 +518,39 @@ fi
if buildAll || hasArg cuopt; then
cd "${REPODIR}"/python/cuopt

# $EXTRA_CMAKE_ARGS gets concatenated into a string with [*] and then we find/replace spaces with semi-colons
SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES};${EXTRA_CMAKE_ARGS[*]// /;}" \
# Convert EXTRA_CMAKE_ARGS into a semicolon-delimited list, escaping
# any semicolons in values so scikit-build-core treats each -D as one arg.
SKBUILD_EXTRA_ARGS=()
for extra_arg in "${EXTRA_CMAKE_ARGS[@]}"; do
SKBUILD_EXTRA_ARGS+=("${extra_arg//;/\\;}")
done
SKBUILD_EXTRA_ARGS_JOINED=""
if [ ${#SKBUILD_EXTRA_ARGS[@]} -gt 0 ]; then
SKBUILD_EXTRA_ARGS_JOINED="$(IFS=';'; echo "${SKBUILD_EXTRA_ARGS[*]}")"
fi
SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES}"
if [ -n "${SKBUILD_EXTRA_ARGS_JOINED}" ]; then
SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS};${SKBUILD_EXTRA_ARGS_JOINED}"
fi
python "${PYTHON_ARGS_FOR_INSTALL[@]}" .
Comment on lines +521 to 535
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

SKBUILD_CMAKE_ARGS is constructed but never exported.

SKBUILD_CMAKE_ARGS is set as a local shell variable (line 531) but scikit-build-core reads it from the environment. Without export, the Python build won't receive the CMake arguments.

🔧 Suggested fix
     SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES}"
     if [ -n "${SKBUILD_EXTRA_ARGS_JOINED}" ]; then
         SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS};${SKBUILD_EXTRA_ARGS_JOINED}"
     fi
+    export SKBUILD_CMAKE_ARGS
         python "${PYTHON_ARGS_FOR_INSTALL[@]}" .

Apply the same fix to the cuopt_mps_parser build block (around line 553).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Convert EXTRA_CMAKE_ARGS into a semicolon-delimited list, escaping
# any semicolons in values so scikit-build-core treats each -D as one arg.
SKBUILD_EXTRA_ARGS=()
for extra_arg in "${EXTRA_CMAKE_ARGS[@]}"; do
SKBUILD_EXTRA_ARGS+=("${extra_arg//;/\\;}")
done
SKBUILD_EXTRA_ARGS_JOINED=""
if [ ${#SKBUILD_EXTRA_ARGS[@]} -gt 0 ]; then
SKBUILD_EXTRA_ARGS_JOINED="$(IFS=';'; echo "${SKBUILD_EXTRA_ARGS[*]}")"
fi
SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES}"
if [ -n "${SKBUILD_EXTRA_ARGS_JOINED}" ]; then
SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS};${SKBUILD_EXTRA_ARGS_JOINED}"
fi
python "${PYTHON_ARGS_FOR_INSTALL[@]}" .
# Convert EXTRA_CMAKE_ARGS into a semicolon-delimited list, escaping
# any semicolons in values so scikit-build-core treats each -D as one arg.
SKBUILD_EXTRA_ARGS=()
for extra_arg in "${EXTRA_CMAKE_ARGS[@]}"; do
SKBUILD_EXTRA_ARGS+=("${extra_arg//;/\\;}")
done
SKBUILD_EXTRA_ARGS_JOINED=""
if [ ${`#SKBUILD_EXTRA_ARGS`[@]} -gt 0 ]; then
SKBUILD_EXTRA_ARGS_JOINED="$(IFS=';'; echo "${SKBUILD_EXTRA_ARGS[*]}")"
fi
SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES}"
if [ -n "${SKBUILD_EXTRA_ARGS_JOINED}" ]; then
SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS};${SKBUILD_EXTRA_ARGS_JOINED}"
fi
export SKBUILD_CMAKE_ARGS
python "${PYTHON_ARGS_FOR_INSTALL[@]}" .
🤖 Prompt for AI Agents
In `@build.sh` around lines 521 - 535, SKBUILD_CMAKE_ARGS is built locally but not
exported, so scikit-build-core won't see the CMake options; export
SKBUILD_CMAKE_ARGS (and ensure SKBUILD_EXTRA_ARGS_JOINED is exported if relied
upon) after constructing it (i.e., after the conditional that appends
SKBUILD_EXTRA_ARGS_JOINED) so the subsequent python
"${PYTHON_ARGS_FOR_INSTALL[@]}" . call inherits it; apply the same export step
in the cuopt_mps_parser build block where SKBUILD_CMAKE_ARGS is also created.

fi

# Build and install the cuopt MPS parser Python package
if buildAll || hasArg cuopt_mps_parser; then
cd "${REPODIR}"/python/cuopt/cuopt/linear_programming

SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES};${EXTRA_CMAKE_ARGS[*]// /;}" \
SKBUILD_EXTRA_ARGS=()
for extra_arg in "${EXTRA_CMAKE_ARGS[@]}"; do
SKBUILD_EXTRA_ARGS+=("${extra_arg//;/\\;}")
done
SKBUILD_EXTRA_ARGS_JOINED=""
if [ ${#SKBUILD_EXTRA_ARGS[@]} -gt 0 ]; then
SKBUILD_EXTRA_ARGS_JOINED="$(IFS=';'; echo "${SKBUILD_EXTRA_ARGS[*]}")"
fi
SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES}"
if [ -n "${SKBUILD_EXTRA_ARGS_JOINED}" ]; then
SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS};${SKBUILD_EXTRA_ARGS_JOINED}"
fi
python "${PYTHON_ARGS_FOR_INSTALL[@]}" .
Comment on lines +542 to 554
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same issue: SKBUILD_CMAKE_ARGS not exported for mps_parser build.

The cuopt_mps_parser build has the same issue - SKBUILD_CMAKE_ARGS needs to be exported.

🔧 Suggested fix
     SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES}"
     if [ -n "${SKBUILD_EXTRA_ARGS_JOINED}" ]; then
         SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS};${SKBUILD_EXTRA_ARGS_JOINED}"
     fi
+    export SKBUILD_CMAKE_ARGS
         python "${PYTHON_ARGS_FOR_INSTALL[@]}" .
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SKBUILD_EXTRA_ARGS=()
for extra_arg in "${EXTRA_CMAKE_ARGS[@]}"; do
SKBUILD_EXTRA_ARGS+=("${extra_arg//;/\\;}")
done
SKBUILD_EXTRA_ARGS_JOINED=""
if [ ${#SKBUILD_EXTRA_ARGS[@]} -gt 0 ]; then
SKBUILD_EXTRA_ARGS_JOINED="$(IFS=';'; echo "${SKBUILD_EXTRA_ARGS[*]}")"
fi
SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES}"
if [ -n "${SKBUILD_EXTRA_ARGS_JOINED}" ]; then
SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS};${SKBUILD_EXTRA_ARGS_JOINED}"
fi
python "${PYTHON_ARGS_FOR_INSTALL[@]}" .
SKBUILD_EXTRA_ARGS=()
for extra_arg in "${EXTRA_CMAKE_ARGS[@]}"; do
SKBUILD_EXTRA_ARGS+=("${extra_arg//;/\\;}")
done
SKBUILD_EXTRA_ARGS_JOINED=""
if [ ${`#SKBUILD_EXTRA_ARGS`[@]} -gt 0 ]; then
SKBUILD_EXTRA_ARGS_JOINED="$(IFS=';'; echo "${SKBUILD_EXTRA_ARGS[*]}")"
fi
SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES}"
if [ -n "${SKBUILD_EXTRA_ARGS_JOINED}" ]; then
SKBUILD_CMAKE_ARGS="${SKBUILD_CMAKE_ARGS};${SKBUILD_EXTRA_ARGS_JOINED}"
fi
export SKBUILD_CMAKE_ARGS
python "${PYTHON_ARGS_FOR_INSTALL[@]}" .
🤖 Prompt for AI Agents
In `@build.sh` around lines 542 - 554, The SKBUILD_CMAKE_ARGS variable created for
the cuopt_mps_parser build is not exported, so subprocesses (the python install
step) can't see the CMake args; update the build step that sets
SKBUILD_CMAKE_ARGS (the block building SKBUILD_CMAKE_ARGS and the subsequent
python "${PYTHON_ARGS_FOR_INSTALL[@]}" . invocation) to export
SKBUILD_CMAKE_ARGS (e.g., use export SKBUILD_CMAKE_ARGS after composing it) so
the python/skbuild process receives the variable during the cuopt_mps_parser
build.

fi

Expand Down
7 changes: 5 additions & 2 deletions ci/build_wheel_libcuopt.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail
Expand All @@ -17,7 +17,10 @@ fi
# Install Boost and TBB
bash ci/utils/install_boost_tbb.sh

export SKBUILD_CMAKE_ARGS="-DCUOPT_BUILD_WHEELS=ON;-DDISABLE_DEPRECATION_WARNING=ON"
# Install Protobuf + gRPC (protoc + grpc_cpp_plugin)
bash ci/utils/install_protobuf_grpc.sh

export SKBUILD_CMAKE_ARGS="-DCUOPT_BUILD_WHEELS=ON;-DDISABLE_DEPRECATION_WARNING=ON;-DCUOPT_ENABLE_GRPC=ON"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if protobuf/grpc installation exists in other CI build scripts
rg -l "install_protobuf_grpc" ci/
# Check what's in the install script
cat ci/utils/install_protobuf_grpc.sh 2>/dev/null || echo "Script not found"
# Check the full build_wheel_libcuopt.sh file to see all dependencies
echo "=== Full ci/build_wheel_libcuopt.sh ==="
cat ci/build_wheel_libcuopt.sh
# Check other CI scripts for dependency installation patterns
echo "=== Checking other CI build scripts ==="
fd "\.sh$" ci/ --type f | head -10

Repository: NVIDIA/cuopt

Length of output: 4455


Add missing protobuf/gRPC installation for gRPC-enabled wheel build.

The script enables CUOPT_ENABLE_GRPC=ON but doesn't install the required protobuf and gRPC C++ development libraries. Add bash ci/utils/install_protobuf_grpc.sh before the CMake arguments are set, similar to the existing calls to install_boost_tbb.sh and install_cudss.sh.

🤖 Prompt for AI Agents
In `@ci/build_wheel_libcuopt.sh` at line 20, Add a call to the protobuf/gRPC
installer before you set SKBUILD_CMAKE_ARGS: invoke the existing script bash
ci/utils/install_protobuf_grpc.sh (same pattern as install_boost_tbb.sh and
install_cudss.sh) so protobuf and gRPC C++ dev libs are present when
CUOPT_ENABLE_GRPC=ON; ensure this invocation appears prior to exporting
SKBUILD_CMAKE_ARGS in the build_wheel_libcuopt.sh flow.


# For pull requests we are enabling assert mode.
if [ "$RAPIDS_BUILD_TYPE" = "pull-request" ]; then
Expand Down
40 changes: 40 additions & 0 deletions ci/utils/install_protobuf_grpc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash

# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail

# Install Protobuf and gRPC C++ development libraries
if [ -f /etc/os-release ]; then
. /etc/os-release
if [[ "$ID" == "rocky" ]]; then
echo "Detected Rocky Linux. Installing Protobuf + gRPC via dnf..."
# Enable PowerTools (Rocky 8) or CRB (Rocky 9) repository for protobuf-devel
if [[ "${VERSION_ID%%.*}" == "8" ]]; then
dnf config-manager --set-enabled powertools || dnf config-manager --set-enabled PowerTools || true
elif [[ "${VERSION_ID%%.*}" == "9" ]]; then
dnf config-manager --set-enabled crb || true
fi
# Protobuf (headers + protoc)
dnf install -y protobuf-devel protobuf-compiler

# gRPC C++ (headers/libs + grpc_cpp_plugin for codegen)
# Package names can vary by repo; try the common ones first.
dnf install -y grpc-devel grpc-plugins || dnf install -y grpc-devel || true
Comment on lines +22 to +24
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat ci/utils/install_protobuf_grpc.sh

Repository: NVIDIA/cuopt

Length of output: 1751


🏁 Script executed:

# Search for patterns related to grpc_cpp_plugin in the repo
rg "grpc_cpp_plugin" --type py --type cpp --type bash

Repository: NVIDIA/cuopt

Length of output: 84


🏁 Script executed:

# Search for grpc_cpp_plugin references without type filter
rg "grpc_cpp_plugin" --type py --type cpp || rg "grpc_cpp_plugin"

Repository: NVIDIA/cuopt

Length of output: 580


🏁 Script executed:

# Verify the exact fallback logic and what packages grpc-plugins vs grpc-devel provide
# Check if there's a pattern of similar fallbacks in the script
rg "dnf install" ci/utils/install_protobuf_grpc.sh -A 1

Repository: NVIDIA/cuopt

Length of output: 238


Don't silently ignore gRPC install failures; breaks the script's fail-fast contract.

The script uses set -euo pipefail but the Rocky Linux gRPC install ends with || true, bypassing this contract. This causes silent success even when gRPC packages are unavailable, deferring the error to a later CMake build failure with obscured context. The Ubuntu path correctly fails hard on missing packages—Rocky should do the same.

Replace || true with explicit error handling and validation:

🔧 Suggested fix
-        dnf install -y grpc-devel grpc-plugins || dnf install -y grpc-devel || true
+        dnf install -y grpc-devel grpc-plugins || dnf install -y grpc-devel || {
+            echo "gRPC packages not available in enabled repos; aborting."
+            exit 1
+        }
+        command -v grpc_cpp_plugin >/dev/null || {
+            echo "grpc_cpp_plugin not found after install; aborting."
+            exit 1
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# gRPC C++ (headers/libs + grpc_cpp_plugin for codegen)
# Package names can vary by repo; try the common ones first.
dnf install -y grpc-devel grpc-plugins || dnf install -y grpc-devel || true
# gRPC C++ (headers/libs + grpc_cpp_plugin for codegen)
# Package names can vary by repo; try the common ones first.
dnf install -y grpc-devel grpc-plugins || dnf install -y grpc-devel || {
echo "gRPC packages not available in enabled repos; aborting."
exit 1
}
command -v grpc_cpp_plugin >/dev/null || {
echo "grpc_cpp_plugin not found after install; aborting."
exit 1
}
🤖 Prompt for AI Agents
In `@ci/utils/install_protobuf_grpc.sh` around lines 22 - 24, The Rocky Linux gRPC
install line uses "dnf install -y grpc-devel grpc-plugins || dnf install -y
grpc-devel || true", which silences failures and violates set -euo pipefail;
update the dnf installation logic in ci/utils/install_protobuf_grpc.sh so it
does not end with "|| true": run the first dnf install attempt, if it fails try
the fallback install, and if that also fails print a clear error via echo or >&2
with context ("gRPC packages missing: grpc-devel/grpc-plugins") and exit
non‑zero; ensure this explicit check replaces the trailing "|| true" so the
script fails fast on missing gRPC packages.

elif [[ "$ID" == "ubuntu" ]]; then
echo "Detected Ubuntu. Installing Protobuf + gRPC via apt..."
apt-get update
# Protobuf (headers + protoc)
apt-get install -y libprotobuf-dev protobuf-compiler

# gRPC C++ (headers/libs + grpc_cpp_plugin for codegen)
apt-get install -y libgrpc++-dev protobuf-compiler-grpc
else
echo "Unknown OS: $ID. Please install Protobuf + gRPC development libraries manually."
exit 1
fi
else
echo "/etc/os-release not found. Cannot determine OS. Please install Protobuf + gRPC development libraries manually."
exit 1
fi
2 changes: 2 additions & 0 deletions conda/environments/all_cuda-129_arch-aarch64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ dependencies:
- doxygen=1.9.1
- fastapi
- gcc_linux-aarch64=14.*
- grpc-cpp
- ipython
- jsonref==1.1.0
- libboost-devel
- libcudss-dev >=0.7
- libcurand-dev
- libcusolver-dev
- libcusparse-dev
- libprotobuf
- libraft-headers==26.2.*,>=0.0.0a0
- librmm==26.2.*,>=0.0.0a0
- make
Expand Down
2 changes: 2 additions & 0 deletions conda/environments/all_cuda-129_arch-x86_64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ dependencies:
- doxygen=1.9.1
- fastapi
- gcc_linux-64=14.*
- grpc-cpp
- ipython
- jsonref==1.1.0
- libboost-devel
- libcudss-dev >=0.7
- libcurand-dev
- libcusolver-dev
- libcusparse-dev
- libprotobuf
- libraft-headers==26.2.*,>=0.0.0a0
- librmm==26.2.*,>=0.0.0a0
- make
Expand Down
2 changes: 2 additions & 0 deletions conda/environments/all_cuda-131_arch-aarch64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ dependencies:
- doxygen=1.9.1
- fastapi
- gcc_linux-aarch64=14.*
- grpc-cpp
- ipython
- jsonref==1.1.0
- libboost-devel
- libcudss-dev >=0.7
- libcurand-dev
- libcusolver-dev
- libcusparse-dev
- libprotobuf
- libraft-headers==26.2.*,>=0.0.0a0
- librmm==26.2.*,>=0.0.0a0
- make
Expand Down
Loading
Loading