@@ -550,11 +550,12 @@ if (BUILD_TESTS)
550550endif ()
551551
552552set (CUOPT_SRC_FILES)
553+ set (CUOPT_CLIENT_SRC_FILES)
553554set (MPS_FAST_SRC_FILES)
554555add_subdirectory (src )
555556
556557if (HOST_LINEINFO)
557- set_source_files_properties (${CUOPT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR } PROPERTIES COMPILE_OPTIONS "-g1" )
558+ set_source_files_properties (${CUOPT_SRC_FILES} ${CUOPT_CLIENT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR } PROPERTIES COMPILE_OPTIONS "-g1" )
558559endif ()
559560
560561# Needed for the fast MPS parser, available on all x86-64-v3 compliant x86 CPUs (essentially since Haswell ~2013)
@@ -567,11 +568,11 @@ endif ()
567568# TODO: figure out a set of flags for ARM that fits the range of CPUs we wish to support (neoverse?)
568569# NEON should be universal on aarch64 and enough for our purposes (parsing) though
569570
570- # Apply -UNDEBUG only to solver source files (not gRPC infrastructure).
571- # Must happen before gRPC files are appended to CUOPT_SRC_FILES .
571+ # Apply -UNDEBUG only to solver and parser source files (not gRPC infrastructure).
572+ # Must happen before gRPC files are appended to CUOPT_CLIENT_SRC_FILES .
572573# Uses APPEND to preserve any existing per-file options (e.g. -g1 from HOST_LINEINFO).
573574if (DEFINE_ASSERT)
574- set_property (SOURCE ${CUOPT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR }
575+ set_property (SOURCE ${CUOPT_SRC_FILES} ${CUOPT_CLIENT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR }
575576 APPEND PROPERTY COMPILE_OPTIONS "-UNDEBUG" )
576577endif ()
577578
@@ -598,9 +599,7 @@ if (NOT SKIP_GRPC_BUILD)
598599 src/grpc/client/grpc_client.cpp
599600 src/grpc/client/grpc_client_env.cpp
600601 src/grpc/client/cython_grpc_client.cpp
601- src/grpc/client/solve_remote.cpp
602602 )
603-
604603 # Routing (VRP) arm: everything that depends on the routing engine. Kept as
605604 # its own list so a routing-only gRPC client can be split out of the
606605 # cuopt_grpc component without moving code around again.
@@ -616,7 +615,17 @@ if (NOT SKIP_GRPC_BUILD)
616615 if (CUOPT_ENABLE_GRPC_ROUTING)
617616 list (APPEND GRPC_INFRA_FILES ${GRPC_ROUTING_FILES} )
618617 endif ()
619- list (APPEND CUOPT_SRC_FILES ${GRPC_INFRA_FILES} )
618+
619+ # Both arms are CUDA-free -- the routing mappers reference no raft/rmm/thrust
620+ # either -- so the wire protocol and clients build into cuopt_client, shared by
621+ # libcuopt, cuopt_grpc_server and the Python client extensions. One mapper
622+ # implementation, not a client-side fork of it.
623+ list (APPEND CUOPT_CLIENT_SRC_FILES ${GRPC_INFRA_FILES} )
624+
625+ # solve_remote.cpp is the local-vs-remote dispatcher: it calls into the GPU
626+ # solver, so it stays in cuopt_objs rather than moving down to cuopt_client.
627+ list (APPEND CUOPT_SRC_FILES src/grpc/client/solve_remote.cpp)
628+ list (APPEND GRPC_INFRA_FILES src/grpc/client/solve_remote.cpp)
620629
621630 # Always keep NDEBUG defined for gRPC infrastructure files so that abseil
622631 # headers inline Mutex::Dtor() instead of emitting an external call.
@@ -631,6 +640,120 @@ if (NOT SKIP_GRPC_BUILD)
631640 APPEND PROPERTY COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE :CXX >:-fvisibility =default >" )
632641endif (NOT SKIP_GRPC_BUILD )
633642
643+ # ##################################################################################################
644+ # - cuopt_client - CPU-only support library ----------------------------------------------------------
645+ #
646+ # Holds the host-side problem representation (parsers, data_model_view, mps_data_model,
647+ # writers), the gRPC wire protocol (generated protos + mappers), and the gRPC client.
648+ # None of it touches CUDA, so this library links no CUDA runtime.
649+ #
650+ # It exists so the Python extension modules that never call into the GPU -- data_model,
651+ # solver_settings, io, and the gRPC client -- can link something other than libcuopt.so,
652+ # which is what makes a GPU-free client install possible. libcuopt and cuopt_grpc_server
653+ # both link it, so there is exactly one implementation of the mappers, not a client fork.
654+ #
655+ # LANGUAGES is deliberately not CUDA here: adding a .cu file to CUOPT_CLIENT_SRC_FILES
656+ # should fail loudly rather than quietly reintroduce a CUDA dependency.
657+ # Built as an OBJECT library first, mirroring cuopt_objs/cuopt. The shared library below
658+ # keeps hidden visibility and exports only the curated CUOPT_EXPORT surface, while
659+ # cuopt_static (for internal tests) links the objects directly -- internal symbols such as
660+ # the fast MPS parser's mps_phase_registry_t are not exported, and the internal test
661+ # binaries need them.
662+ add_library (cuopt_client_objs OBJECT ${CUOPT_CLIENT_SRC_FILES} )
663+ # NOTE: default visibility, deliberately unlike cuopt_objs.
664+ #
665+ # cuopt_objs can hide everything not marked CUOPT_EXPORT because libcuopt has a curated
666+ # public C++ API. cuopt_client is different: it was carved out of the *internals*, so
667+ # libcuopt itself depends on ~214 of its symbols (the whole cpu_optimization_problem_t /
668+ # data_model_view_t / mps_data_model_t / grpc_client_t surface). Those are internal
669+ # cross-library references, not a public API, and hiding them makes libcuopt.so fail to
670+ # load with e.g. "undefined symbol: grpc_client_t::solve_mip".
671+ #
672+ # Curating them behind CUOPT_EXPORT would mean annotating essentially every host-side
673+ # method, so default visibility is the right trade here.
674+ set_target_properties (cuopt_client_objs
675+ PROPERTIES POSITION_INDEPENDENT_CODE ON
676+ CXX_SCAN_FOR_MODULES OFF
677+ )
678+
679+ add_library (cuopt_client SHARED $<TARGET_OBJECTS :cuopt_client_objs >)
680+ add_library (cuopt::cuopt_client ALIAS cuopt_client )
681+
682+ target_include_directories (cuopt_client
683+ PUBLIC
684+ "$<BUILD_INTERFACE :${CMAKE_CURRENT_SOURCE_DIR } /include >"
685+ "$<BUILD_INTERFACE :${CMAKE_CURRENT_BINARY_DIR } /include >"
686+ INTERFACE
687+ "$<INSTALL_INTERFACE :include >"
688+ )
689+
690+ target_compile_definitions (cuopt_client
691+ PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL} "
692+ )
693+
694+ set_target_properties (cuopt_client
695+ PROPERTIES POSITION_INDEPENDENT_CODE ON
696+ CXX_SCAN_FOR_MODULES OFF
697+ BUILD_RPATH "\$ ORIGIN"
698+ INSTALL_RPATH "\$ ORIGIN"
699+ LINKER_LANGUAGE CXX
700+ )
701+
702+ target_compile_definitions (cuopt_client_objs
703+ PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL} "
704+ )
705+
706+ target_compile_options (cuopt_client_objs
707+ PRIVATE "$<$<COMPILE_LANGUAGE :CXX >:${CUOPT_CXX_FLAGS} >"
708+ )
709+
710+ target_include_directories (cuopt_client_objs
711+ PRIVATE
712+ "${CMAKE_CURRENT_SOURCE_DIR } /../thirdparty"
713+ "${CMAKE_CURRENT_SOURCE_DIR } /src"
714+ "${CMAKE_CURRENT_SOURCE_DIR } /src/io"
715+ "${CMAKE_CURRENT_SOURCE_DIR } /src/grpc"
716+ "${CMAKE_CURRENT_SOURCE_DIR } /src/grpc/client"
717+ "${CMAKE_CURRENT_SOURCE_DIR } /src/grpc/codegen/generated"
718+ "${CMAKE_CURRENT_BINARY_DIR } "
719+ "${CMAKE_CURRENT_BINARY_DIR } /include"
720+ $<$<BOOL :${CUOPT_PARSER_WITH_BZIP2} >:${BZIP2_INCLUDE_DIRS} >
721+ $<$<BOOL :${CUOPT_PARSER_WITH_ZLIB} >:${ZLIB_INCLUDE_DIRS} >
722+ PUBLIC
723+ "$<BUILD_INTERFACE :${CMAKE_CURRENT_SOURCE_DIR } /include >"
724+ "$<BUILD_INTERFACE :${CMAKE_CURRENT_BINARY_DIR } /include >"
725+ INTERFACE
726+ "$<INSTALL_INTERFACE :include >"
727+ )
728+
729+ # CCCL is a compile-time (header-only) dependency here: the fast MPS parser uses
730+ # host helpers from <cuda/cmath> (ceil_div, round_up). It pulls in no CUDA runtime.
731+ # bzip2 / zlib / lz4 are dlopen'd at runtime by file_to_string.cpp, so they are
732+ # header-only here too and deliberately absent from the link line.
733+ # The OBJECT library needs these for their INTERFACE include dirs / defines at compile time.
734+ target_link_libraries (cuopt_client_objs
735+ PUBLIC
736+ rapids_logger::rapids_logger
737+ CCCL::CCCL
738+ PRIVATE
739+ simde::simde
740+ OpenMP::OpenMP_CXX
741+ $<$<BOOL :${CUOPT_ENABLE_GRPC} >:protobuf ::libprotobuf >
742+ $<$<BOOL :${CUOPT_ENABLE_GRPC} >:gRPC ::grpc ++>
743+ )
744+
745+ target_link_libraries (cuopt_client
746+ PUBLIC
747+ rapids_logger::rapids_logger
748+ CCCL::CCCL
749+ PRIVATE
750+ simde::simde
751+ OpenMP::OpenMP_CXX
752+ ${CMAKE_DL_LIBS }
753+ $<$<BOOL :${CUOPT_ENABLE_GRPC} >:protobuf ::libprotobuf >
754+ $<$<BOOL :${CUOPT_ENABLE_GRPC} >:gRPC ::grpc ++>
755+ )
756+
634757add_library (cuopt_objs OBJECT
635758 ${CUOPT_SRC_FILES}
636759)
@@ -757,6 +880,7 @@ target_compile_definitions(cuopt_objs PUBLIC
757880
758881target_link_libraries (cuopt_objs
759882 PUBLIC
883+ cuopt::cuopt_client
760884 CUDA::cublas
761885 CUDA::cusparse
762886 rmm::rmm
@@ -777,7 +901,10 @@ target_link_libraries(cuopt_objs
777901# - generate tests --------------------------------------------------------------------------------
778902if (BUILD_TESTS)
779903 include (CTest )
780- add_library (cuopt_static STATIC $<TARGET_OBJECTS :cuopt_objs >)
904+ # Embeds cuopt_client_objs directly rather than linking libcuopt_client.so: the internal
905+ # test binaries reach parser internals that the shared library deliberately does not
906+ # export. Do not also link cuopt::cuopt_client here -- that would duplicate every symbol.
907+ add_library (cuopt_static STATIC $<TARGET_OBJECTS :cuopt_objs > $<TARGET_OBJECTS :cuopt_client_objs >)
781908 target_link_libraries (cuopt_static
782909 PUBLIC
783910 CUDA::cublas
@@ -839,6 +966,7 @@ target_include_directories(cuopt
839966)
840967target_link_libraries (cuopt
841968 PUBLIC
969+ cuopt::cuopt_client
842970 CUDA::cublas
843971 CUDA::cusparse
844972 rmm::rmm
@@ -904,14 +1032,14 @@ else ()
9041032endif ()
9051033
9061034# adds the .so files to the runtime deb package
907- install (TARGETS cuopt
1035+ install (TARGETS cuopt cuopt_client
9081036 DESTINATION ${_LIB_DEST}
9091037 COMPONENT runtime
9101038 EXPORT cuopt-exports
9111039)
9121040
9131041# adds the .so files to the development deb package
914- install (TARGETS cuopt
1042+ install (TARGETS cuopt cuopt_client
9151043 DESTINATION ${_LIB_DEST}
9161044 COMPONENT dev
9171045)
@@ -939,7 +1067,7 @@ cuOpt library is a collection of GPU accelerated combinatorial optimization algo
9391067
9401068rapids_export (INSTALL cuopt
9411069 EXPORT_SET cuopt-exports
942- GLOBAL_TARGETS cuopt
1070+ GLOBAL_TARGETS cuopt cuopt_client
9431071 NAMESPACE cuopt::
9441072 DOCUMENTATION doc_string
9451073)
@@ -948,7 +1076,7 @@ rapids_export(INSTALL cuopt
9481076# - build export -------------------------------------------------------------------------------
9491077rapids_export (BUILD cuopt
9501078 EXPORT_SET cuopt-exports
951- GLOBAL_TARGETS cuopt
1079+ GLOBAL_TARGETS cuopt cuopt_client
9521080 NAMESPACE cuopt::
9531081 DOCUMENTATION doc_string
9541082)
0 commit comments