From f9c346d5a97870a7328b3fb8386280822cd600fa Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 21 Sep 2026 11:57:23 -0500 Subject: [PATCH] build(cmake): stop compiling routing/mathopt sources twice cuopt_routing and cuopt_mathopt were built directly from source, while cuopt_objs separately recompiled the same files (CUOPT_ROUTING_SRC_FILES + CUOPT_MATHOPT_SRC_FILES) to feed cuopt_static. Give routing and mathopt the same OBJECT-library treatment cuopt_client already has (cuopt_routing_objs / cuopt_mathopt_objs), compiled once and reused by both the shared component library and cuopt_static. Removes cuopt_objs. Also fixes an ordering bug: CUDSS_MT_LIB_FILE_NAME was recomputed (basename -> full path, #1883) after cuopt_mathopt's own target_compile_definitions had already captured the stale value. Verified locally: full build succeeds, ninja target count drops 631 -> 350. Co-Authored-By: Claude Sonnet 5 --- cpp/CMakeLists.txt | 294 ++++++++++++++++++++++----------------------- 1 file changed, 147 insertions(+), 147 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index b168df7240..2fb971ad78 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -686,8 +686,7 @@ function(cuopt_configure_component target) CXX_SCAN_FOR_MODULES OFF BUILD_RPATH_USE_ORIGIN TRUE INSTALL_RPATH "\$ORIGIN" - # Same hidden-by-default visibility that #1625 applies to cuopt_objs. The - # components are the shipped artifacts, so they need it too. + # Same hidden-by-default visibility that #1625 applies to the *_objs libraries. CXX_VISIBILITY_PRESET hidden CUDA_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON @@ -747,14 +746,11 @@ get_filename_component(CUDSS_MT_LIB_FILE_NAME "${CUDSS_MT_LIB_FILE}" NAME) # modules that never call the GPU can link this instead of libcuopt.so. # # LANGUAGES is deliberately not CUDA: adding a .cu file here should fail loudly rather than -# quietly reintroduce a CUDA dependency. Built as an OBJECT library first, mirroring -# cuopt_objs/cuopt, so cuopt_static can link the objects directly for tests that reach -# parser internals the shared library does not export. +# quietly reintroduce a CUDA dependency. Built as an OBJECT library first (mirrored below by +# cuopt_routing_objs / cuopt_mathopt_objs) so cuopt_static can link the objects directly. add_library(cuopt_client_objs OBJECT ${CUOPT_CLIENT_SRC_FILES}) -# Default visibility, deliberately unlike cuopt_objs: this library was carved out of the -# internals rather than designed as an export surface, so libcuopt resolves ~239 symbols -# from it. Hiding them would mean annotating essentially every host-side method with -# CUOPT_EXPORT. +# Default visibility, deliberately unlike the other *_objs: carved out of the internals rather +# than designed as an export surface, so libcuopt resolves ~239 symbols from it. set_target_properties(cuopt_client_objs PROPERTIES POSITION_INDEPENDENT_CODE ON CXX_SCAN_FOR_MODULES OFF @@ -863,9 +859,75 @@ target_link_libraries(cuopt_client ) +# ################################################################################################## +# - cuDSS threading layer -------------------------------------------------------------------------- +message(STATUS "target include directories CUDSS_INCLUDES = ${CUDSS_INCLUDE}") + +# Build our own cuDSS threading layer against our own libgomp instead of cuDSS's prebuilt one, +# which can resolve a different libgomp instance (#1219). Off by default; enabled for wheel +# builds (ci/build_wheel_libcuopt.sh). +option(CUOPT_BUILD_CUSTOM_CUDSS_MTLAYER "Build a cuDSS threading layer linked against our own libgomp" OFF) + +if (CUOPT_BUILD_CUSTOM_CUDSS_MTLAYER) + add_library(cudss_mtlayer_cuopt SHARED src/barrier/cudss_mtlayer_cuopt.cpp) + target_include_directories(cudss_mtlayer_cuopt PRIVATE "${CUDSS_INCLUDE}") + target_link_libraries(cudss_mtlayer_cuopt PRIVATE OpenMP::OpenMP_CXX CUDA::cudart) + set_target_properties(cudss_mtlayer_cuopt + PROPERTIES BUILD_RPATH "\$ORIGIN" + INSTALL_RPATH "\$ORIGIN" + ) + set(CUDSS_MT_LIB_FILE_NAME "libcudss_mtlayer_cuopt.so") +else () + # Full path, not just the basename, so the runtime dlopen pin (sparse_cholesky.cuh) and + # cudssSetThreadingLayer() resolve the exact same file CMake found (#1883). + set(CUDSS_MT_LIB_FILE_NAME "${CUDSS_MT_LIB_FILE}") +endif () + # cuopt_routing: VRP / routing engine (omitted when SKIP_ROUTING_BUILD=ON) +# +# OBJECT library first, mirroring cuopt_client_objs, so cuopt_static links the same compiled +# objects instead of recompiling routing a second time. if(NOT SKIP_ROUTING_BUILD) - add_library(cuopt_routing SHARED ${CUOPT_ROUTING_SRC_FILES}) + add_library(cuopt_routing_objs OBJECT ${CUOPT_ROUTING_SRC_FILES}) + set_target_properties(cuopt_routing_objs + PROPERTIES POSITION_INDEPENDENT_CODE ON + CXX_SCAN_FOR_MODULES OFF + CXX_VISIBILITY_PRESET hidden + CUDA_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + ) + target_compile_options(cuopt_routing_objs + PRIVATE "$<$:${CUOPT_CXX_FLAGS}>" + "$<$:${CUOPT_CUDA_FLAGS}>" + ) + target_compile_definitions(cuopt_routing_objs + PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}" + ) + target_include_directories(cuopt_routing_objs + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_BINARY_DIR}" + PUBLIC + "$" + "$" + INTERFACE + "$" + ) + # Repeated on cuopt_routing below: $ doesn't inherit an OBJECT + # library's PUBLIC link interface. + target_link_libraries(cuopt_routing_objs + PUBLIC + cuopt_client + rmm::rmm + rapids_logger::rapids_logger + CCCL::CCCL + raft::raft + CUDA::cublas + CUDA::cusparse + PRIVATE simde::simde OpenMP::OpenMP_CXX OpenMP::OpenMP_CUDA + ) + + add_library(cuopt_routing SHARED $) cuopt_configure_component(cuopt_routing) target_link_libraries(cuopt_routing PUBLIC @@ -886,9 +948,34 @@ if(NOT SKIP_ROUTING_BUILD) endif() # cuopt_mathopt: LP / MIP / numerical optimization engine -add_library(cuopt_mathopt SHARED ${CUOPT_MATHOPT_SRC_FILES}) -cuopt_configure_component(cuopt_mathopt) -target_include_directories(cuopt_mathopt PRIVATE +# +# OBJECT library first, same reason as cuopt_routing_objs above. +add_library(cuopt_mathopt_objs OBJECT ${CUOPT_MATHOPT_SRC_FILES}) +set_target_properties(cuopt_mathopt_objs + PROPERTIES POSITION_INDEPENDENT_CODE ON + CXX_SCAN_FOR_MODULES OFF + CXX_VISIBILITY_PRESET hidden + CUDA_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON +) +target_compile_options(cuopt_mathopt_objs + PRIVATE "$<$:${CUOPT_CXX_FLAGS}>" + "$<$:${CUOPT_CUDA_FLAGS}>" +) +target_compile_definitions(cuopt_mathopt_objs + PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}" +) +target_include_directories(cuopt_mathopt_objs + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_BINARY_DIR}" + PUBLIC + "$" + "$" + INTERFACE + "$" +) +target_include_directories(cuopt_mathopt_objs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty" "${CMAKE_CURRENT_SOURCE_DIR}/src/io" "${CUDSS_INCLUDE}" @@ -896,27 +983,27 @@ target_include_directories(cuopt_mathopt PRIVATE $<$:${ZLIB_INCLUDE_DIRS}> ) # Adding Papilo as a system include messes up clang's include resolution if papilo is already installed as a conda package -target_include_directories(cuopt_mathopt PRIVATE +target_include_directories(cuopt_mathopt_objs PRIVATE "${papilo_SOURCE_DIR}/src" "${papilo_BINARY_DIR}" ) -target_include_directories(cuopt_mathopt SYSTEM PRIVATE +target_include_directories(cuopt_mathopt_objs SYSTEM PRIVATE "${pslp_SOURCE_DIR}/include" "${dejavu_SOURCE_DIR}" # utilities/version_info.cpp includes highway, and utilities builds into this # component since cuopt_base was removed. "${highway_SOURCE_DIR}" ) -target_compile_definitions(cuopt_mathopt +target_compile_definitions(cuopt_mathopt_objs PUBLIC CUSPARSE_ENABLE_EXPERIMENTAL_API # utilities/version_info.cpp reports these; it builds here now that cuopt_base is gone. CUOPT_CUDA_ARCHITECTURES="${JOINED_CUDA_ARCHITECTURES}" CUOPT_CPU_ARCHITECTURE="${CMAKE_SYSTEM_PROCESSOR}" PRIVATE CUDSS_MT_LIB_FILE_NAME="${CUDSS_MT_LIB_FILE_NAME}" ) -target_link_libraries(cuopt_mathopt +# Repeated on cuopt_mathopt below -- see the note on cuopt_routing_objs above. +target_link_libraries(cuopt_mathopt_objs PUBLIC - # The parsers and the host-side problem representation live in cuopt_client. cuopt_client rmm::rmm rapids_logger::rapids_logger @@ -932,153 +1019,61 @@ target_link_libraries(cuopt_mathopt nccl_external ${CMAKE_DL_LIBS} ) -target_link_libraries(cuopt_mathopt PRIVATE $) -add_dependencies(cuopt_mathopt PSLP) -target_link_libraries(cuopt_mathopt PRIVATE $) -add_dependencies(cuopt_mathopt hwy) -target_include_directories(cuopt_mathopt SYSTEM PRIVATE +# partitioner.cpp includes ; KaMinPar is linked by file below so its PUBLIC +# defines aren't inherited -- replicate the ones needs. KAMINPAR_64BIT_EDGE_IDS +# must stay in sync with get_kaminpar.cmake or the prebuilt archive's ABI mismatches. +target_include_directories(cuopt_mathopt_objs SYSTEM PRIVATE $) -target_compile_definitions(cuopt_mathopt PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS) -target_link_libraries(cuopt_mathopt PRIVATE $) +target_compile_definitions(cuopt_mathopt_objs PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS) if (TARGET KaMinPar) - add_dependencies(cuopt_mathopt KaMinPar) + add_dependencies(cuopt_mathopt_objs KaMinPar) endif () -add_library(cuopt::mathopt ALIAS cuopt_mathopt) -# ################################################################################################## -# - cuopt_objs: OBJECT library used by cuopt_static for internal test builds --------------------- -add_library(cuopt_objs OBJECT - ${CUOPT_SRC_FILES} -) -set_target_properties(cuopt_objs - PROPERTIES POSITION_INDEPENDENT_CODE ON - CXX_VISIBILITY_PRESET hidden - CUDA_VISIBILITY_PRESET hidden - VISIBILITY_INLINES_HIDDEN ON - CXX_SCAN_FOR_MODULES OFF -) -target_compile_definitions(cuopt_objs - PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}" - PUBLIC CUSPARSE_ENABLE_EXPERIMENTAL_API -) -target_compile_options(cuopt_objs - PRIVATE "$<$:${CUOPT_CXX_FLAGS}>" - "$<$:${CUOPT_CUDA_FLAGS}>" -) - -# ################################################################################################## -# - include paths --------------------------------------------------------------------------------- -message(STATUS "target include directories CUDSS_INCLUDES = ${CUDSS_INCLUDE}") - -# Adding Papilo as a system include messes up clang's include resolution if papilo is already installed as a conda package -target_include_directories(cuopt_objs PRIVATE - "${papilo_SOURCE_DIR}/src" - "${papilo_BINARY_DIR}" -) - -target_include_directories(cuopt_objs SYSTEM PRIVATE - "${pslp_SOURCE_DIR}/include" - "${dejavu_SOURCE_DIR}" - "${highway_SOURCE_DIR}" -) - -target_include_directories(cuopt_objs - PRIVATE +add_library(cuopt_mathopt SHARED $) +cuopt_configure_component(cuopt_mathopt) +target_include_directories(cuopt_mathopt PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty" - "${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}/src/io" - "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc" - "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client" - "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/codegen/generated" - "${CMAKE_CURRENT_BINARY_DIR}" "${CUDSS_INCLUDE}" $<$:${BZIP2_INCLUDE_DIRS}> $<$:${ZLIB_INCLUDE_DIRS}> - PUBLIC - "$" - "$" - INTERFACE - "$" ) - -# Link PSLP by file to avoid export dependency tracking -target_link_libraries(cuopt_objs PRIVATE $) -add_dependencies(cuopt_objs PSLP) - -target_link_libraries(cuopt_objs PRIVATE $) -add_dependencies(cuopt_objs hwy) - -# Link KaMinPar by file to avoid export dependency tracking (mirrors PSLP above). -# KaMinPar is a from-source static library fully embedded into libcuopt.so; it is never -# installed (INSTALL_KAMINPAR OFF) and consumers of cuopt::cuopt never use it, so it must -# not leak into cuopt's exported link interface (otherwise rapids_export fails with -# "target KaMinPar is not in any export set"). libKaMinPar.a is self-contained (the -# kaminpar-common OBJECT lib is archived into it); we only need its public headers -# (, which pulls in stdlib + TBB) at compile time. -target_include_directories(cuopt_objs SYSTEM PRIVATE - $) -# partitioner.cpp includes . Because KaMinPar is linked by file, cuopt -# does NOT inherit KaMinPar's PUBLIC compile definitions, so we must replicate the ones that -# affect what compiles to: -# * TBB_PREVIEW_GLOBAL_CONTROL - includes , which older -# TBB gates behind this macro. -# * KAMINPAR_64BIT_EDGE_IDS - selects EdgeID = uint64 in . MUST stay in sync -# with the KAMINPAR_64BIT_* options in get_kaminpar.cmake; -# otherwise the public API types (e.g. copy_graph's span<> -# widths) mismatch the prebuilt archive => undefined refs. -target_compile_definitions(cuopt_objs PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS) -# Real (non-imported) target only exists when KaMinPar is built from source via CPM; -# force it to build before the objects that include its headers since it is EXCLUDE_FROM_ALL. -if (TARGET KaMinPar) - add_dependencies(cuopt_objs KaMinPar) -endif () - -# Build our own cuDSS threading layer against cuopt_objs's own libgomp instead of cuDSS's -# prebuilt one, which can resolve a different libgomp instance (#1219). Off by default; -# enabled for wheel builds (ci/build_wheel_libcuopt.sh). -option(CUOPT_BUILD_CUSTOM_CUDSS_MTLAYER "Build a cuDSS threading layer linked against our own libgomp" OFF) - -if (CUOPT_BUILD_CUSTOM_CUDSS_MTLAYER) - add_library(cudss_mtlayer_cuopt SHARED src/barrier/cudss_mtlayer_cuopt.cpp) - target_include_directories(cudss_mtlayer_cuopt PRIVATE "${CUDSS_INCLUDE}") - target_link_libraries(cudss_mtlayer_cuopt PRIVATE OpenMP::OpenMP_CXX CUDA::cudart) - set_target_properties(cudss_mtlayer_cuopt - PROPERTIES BUILD_RPATH "\$ORIGIN" - INSTALL_RPATH "\$ORIGIN" - ) - set(CUDSS_MT_LIB_FILE_NAME "libcudss_mtlayer_cuopt.so") -else () - # Full path, not just the basename, so the runtime dlopen pin (sparse_cholesky.cuh) and - # cudssSetThreadingLayer() resolve the exact same file CMake found (#1883). - set(CUDSS_MT_LIB_FILE_NAME "${CUDSS_MT_LIB_FILE}") -endif () - -target_compile_definitions(cuopt_objs - PRIVATE CUDSS_MT_LIB_FILE_NAME="${CUDSS_MT_LIB_FILE_NAME}" - PUBLIC +target_compile_definitions(cuopt_mathopt + PUBLIC CUSPARSE_ENABLE_EXPERIMENTAL_API CUOPT_CUDA_ARCHITECTURES="${JOINED_CUDA_ARCHITECTURES}" CUOPT_CPU_ARCHITECTURE="${CMAKE_SYSTEM_PROCESSOR}" + PRIVATE CUDSS_MT_LIB_FILE_NAME="${CUDSS_MT_LIB_FILE_NAME}" ) -target_include_directories(cuopt_objs PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) - -target_link_libraries(cuopt_objs +target_link_libraries(cuopt_mathopt PUBLIC - cuopt::cuopt_client - CUDA::cublas - CUDA::cusparse + # The parsers and the host-side problem representation live in cuopt_client. + cuopt_client rmm::rmm rapids_logger::rapids_logger CCCL::CCCL raft::raft + CUDA::cublas + CUDA::cusparse ${CUDSS_LIB_FILE} PRIVATE ${CUOPT_PRIVATE_CUDA_LIBS} - simde::simde - OpenMP::OpenMP_CXX OpenMP::OpenMP_CUDA - $<$:protobuf::libprotobuf> - $<$:gRPC::grpc++> + simde::simde + nccl_external + ${CMAKE_DL_LIBS} ) +# Linked by file (not target_link_libraries(... KaMinPar)) so it doesn't leak into cuopt's +# exported link interface -- headers are already visible via cuopt_mathopt_objs above. +target_link_libraries(cuopt_mathopt PRIVATE $) +add_dependencies(cuopt_mathopt PSLP) +target_link_libraries(cuopt_mathopt PRIVATE $) +add_dependencies(cuopt_mathopt hwy) +target_link_libraries(cuopt_mathopt PRIVATE $) +if (TARGET KaMinPar) + add_dependencies(cuopt_mathopt KaMinPar) +endif () +add_library(cuopt::mathopt ALIAS cuopt_mathopt) + # ################################################################################################## # - generate tests -------------------------------------------------------------------------------- @@ -1092,11 +1087,16 @@ endif () # single self-contained shared object rather than linking the shared libcuopt (see the Java # classifier JARs). if (BUILD_TESTS OR (BUILD_MIP_BENCHMARKS AND NOT BUILD_LP_ONLY) OR CUOPT_BUILD_STATIC_LIB) - # Embeds cuopt_client_objs directly rather than linking libcuopt_client.so: internal test - # binaries and the Java classifier JAR both reach parser/data-model internals that the - # shared library deliberately does not export. Do not also link cuopt::cuopt_client here -- - # that would duplicate every symbol. - add_library(cuopt_static STATIC $ $) + # Embeds the *_objs directly rather than linking the shared component libraries: internal + # tests and the Java classifier JAR reach internals the shared libraries don't export. + if(NOT SKIP_ROUTING_BUILD) + set(_cuopt_static_routing_objs $) + endif() + add_library(cuopt_static STATIC + $ + $ + ${_cuopt_static_routing_objs} + ) target_link_libraries(cuopt_static PUBLIC CUDA::cublas