diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 6d684af8d99..891a9b202b9 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -58,6 +58,32 @@ option(CUDA_ENABLE_LINEINFO ) option(CUDA_WARNINGS_AS_ERRORS "Enable -Werror=all-warnings for all CUDA compilation" ON) +# CUDF_BUILD_STATIC_DEPS should almost never be set to OFF since there is no reason to prefer +# building shared libraries for dependencies under normal circumstances. FORCE is a necessary +# setting for building a standalone binary with no external dependencies. +set(CUDF_BUILD_STATIC_DEPS + "ON" + CACHE + STRING + "Three-state control for static library dependencies. ON (default) = use static when a CPM build is triggered, OFF = prefer shared (rarely useful), FORCE = guarantee using a static build by rebuilding or downloading even when local copies of a library exist" +) +set_property(CACHE CUDF_BUILD_STATIC_DEPS PROPERTY STRINGS "ON" "OFF" "FORCE") + +# cudart can be statically linked or dynamically linked. The python ecosystem wants dynamic linking +option(CUDA_STATIC_RUNTIME "Statically link the CUDA runtime" OFF) +# FORCE mode implies static CUDA runtime +if(CUDF_BUILD_STATIC_DEPS STREQUAL "FORCE") + set(CUDA_STATIC_RUNTIME + ON + CACHE BOOL "Statically link the CUDA runtime" FORCE + ) +else() + set(CUDA_STATIC_RUNTIME + OFF + CACHE BOOL "Statically link the CUDA runtime" FORCE + ) +endif() + set(DEFAULT_CUDF_BUILD_STREAMS_TEST_UTIL ON) if(NOT BUILD_SHARED_LIBS) @@ -98,6 +124,44 @@ message(VERBOSE "CUDF: Build with remote IO (e.g. AWS S3) support through KvikIO: ${CUDF_KVIKIO_REMOTE_IO}" ) +# CUDF_INSTALL_LIBRARY_DEPS: Derived variable indicating whether to install library dependencies +if(NOT BUILD_SHARED_LIBS) + # A static libcudf.a will not contain the code for its dependencies, so we must install them to + # ensure downstream projects can find them + set(CUDF_INSTALL_LIBRARY_DEPS ON) +elseif(CUDF_BUILD_STATIC_DEPS STREQUAL "OFF") + # If dependencies are built as shared they will be needed at runtime + set(CUDF_INSTALL_LIBRARY_DEPS ON) +else() + # If dependencies are statically linked into a libcudf shared library they aren't needed at + # runtime + set(CUDF_INSTALL_LIBRARY_DEPS OFF) +endif() + +# Derived flags for EXCLUDE_FROM_ALL handling across dependency fetching. +# CUDF_EXCLUDE_DEPS_FROM_ALL: ON/OFF value for rapids_cpm_find() calls (key-value style) +# CUDF_EXCLUDE_DEPS_FROM_ALL_FLAG: "EXCLUDE_FROM_ALL" or "" for rapids_cpm_* wrapper calls (flag +# style) We need both because of https://github.com/cpm-cmake/CPM.cmake/issues/693 +if(CUDF_INSTALL_LIBRARY_DEPS) + set(CUDF_EXCLUDE_DEPS_FROM_ALL OFF) + set(CUDF_EXCLUDE_DEPS_FROM_ALL_FLAG) +else() + set(CUDF_EXCLUDE_DEPS_FROM_ALL ON) + set(CUDF_EXCLUDE_DEPS_FROM_ALL_FLAG EXCLUDE_FROM_ALL) +endif() + +# CUDF_DEPS_BUILD_SHARED: Boolean reduction of the 3-state CUDF_BUILD_STATIC_DEPS for passing to +# dependency configuration functions that accept a BUILD_SHARED parameter. +if(CUDF_BUILD_STATIC_DEPS STREQUAL "OFF") + set(CUDF_DEPS_BUILD_SHARED ON) +else() + set(CUDF_DEPS_BUILD_SHARED OFF) +endif() + +message(VERBOSE "CUDF: Build static library dependencies: ${CUDF_BUILD_STATIC_DEPS}") +message(VERBOSE "CUDF: Install library dependencies: ${CUDF_INSTALL_LIBRARY_DEPS}") +message(VERBOSE "CUDF: Build dependencies as shared libraries: ${CUDF_DEPS_BUILD_SHARED}") + # Set a default build type if none was specified rapids_cmake_build_type("Release") set(CUDF_BUILD_TESTS ${BUILD_TESTS}) @@ -253,11 +317,32 @@ if(CUDF_BUILD_TESTUTIL) ) endif() +# Set CPM_DOWNLOAD_ALL if CUDF_BUILD_STATIC_DEPS is FORCE to skip find_package for all CPM packages +if(CUDF_BUILD_STATIC_DEPS STREQUAL "FORCE") + set(CPM_DOWNLOAD_ALL ON) +endif() + # add third party dependencies using CPM rapids_cpm_init() include(${rapids-cmake-dir}/cpm/rapids_logger.cmake) -rapids_cpm_rapids_logger(BUILD_EXPORT_SET cudf-exports INSTALL_EXPORT_SET cudf-exports) +set(_rapids_logger_args BUILD_EXPORT_SET cudf-exports) +if(NOT CUDF_BUILD_STATIC_DEPS STREQUAL "OFF") + list(APPEND _rapids_logger_args ${CUDF_EXCLUDE_DEPS_FROM_ALL_FLAG} CPM_ARGS OPTIONS + "BUILD_SHARED_LIBS OFF" + ) +endif() +rapids_cpm_rapids_logger(${_rapids_logger_args}) + +# If dynamically linking to a preexisting rapids_logger library then find_dependency(rapids_logger) +# must be in the installed config when CUDF_INSTALL_LIBRARY_DEPS is OFF. If it's ON then we always +# export +get_target_property(_rapids_logger_type rapids_logger::rapids_logger TYPE) +if(CUDF_INSTALL_LIBRARY_DEPS OR NOT _rapids_logger_type STREQUAL "STATIC_LIBRARY") + include("${rapids-cmake-dir}/export/package.cmake") + rapids_export_package(INSTALL rapids_logger cudf-exports VERSION ${rapids_logger_VERSION}) +endif() + create_logger_macros(CUDF "cudf::default_logger()" include/cudf) # find jitify @@ -998,11 +1083,87 @@ add_dependencies(cudf jitify_preprocess_run) # Specify the target module library dependencies target_link_libraries( cudf - PUBLIC CCCL::CCCL rapids_logger::rapids_logger rmm::rmm $ + PUBLIC CCCL::CCCL $ PRIVATE $ $ ZLIB::ZLIB - nvcomp::nvcomp kvikio::kvikio nanoarrow::nanoarrow zstd + ${CUDF_nvcomp_TARGET} kvikio::kvikio ${CUDF_nanoarrow_TARGET} zstd ) +# When building a shared libcudf with static deps, we absorb them via whole-archive linking so that +# their symbols are available to downstream users linking to libcudf.so without needing to link +# against them directly. In this scenario, its public transitive dependencies must be promoted into +# cudf's installed interface (consumers still need them at link time), and its export set metadata +# (find_dependency calls, global targets) is merged into cudf-exports. +set(_absorbed_deps rmm rapids_logger) +foreach(_dep IN LISTS _absorbed_deps) + set(_${_dep}_link ${_dep}::${_dep}) +endforeach() +if(BUILD_SHARED_LIBS) + # When rmm is a static library being absorbed via whole-archive, strip nvtx3 from its public + # interface. We bundle nvtx3 headers directly into cudf's install tree, so consumers get them from + # cudf's include path without needing the nvtx3 target or find_dependency(nvtx3). + get_target_property(_rmm_type rmm::rmm TYPE) + if(_rmm_type STREQUAL "STATIC_LIBRARY") + get_target_property(_rmm_real rmm::rmm ALIASED_TARGET) + if(_rmm_real) + get_target_property(_rmm_libs ${_rmm_real} INTERFACE_LINK_LIBRARIES) + if(_rmm_libs) + list(REMOVE_ITEM _rmm_libs nvtx3::nvtx3-cpp) + set_property(TARGET ${_rmm_real} PROPERTY INTERFACE_LINK_LIBRARIES ${_rmm_libs}) + endif() + endif() + endif() + foreach(_dep IN LISTS _absorbed_deps) + get_target_property(_target_type ${_dep}::${_dep} TYPE) + if(NOT _target_type STREQUAL "STATIC_LIBRARY") + continue() + endif() + + # Wrap with WHOLE_ARCHIVE + BUILD_INTERFACE so the static lib is absorbed into libcudf.so and + # hidden from the installed interface. + set(_${_dep}_link $>) + + # Promote the absorbed library's public transitive deps into cudf's public interface. Filter out + # deps that are themselves absorbed — they are already linked via whole-archive. Also filter + # LINK_ONLY entries — those are private link deps already statically linked in. + get_target_property(_iface_libs ${_dep}::${_dep} INTERFACE_LINK_LIBRARIES) + if(_iface_libs) + foreach(_absorbed IN LISTS _absorbed_deps) + list(REMOVE_ITEM _iface_libs ${_absorbed}::${_absorbed}) + endforeach() + list(FILTER _iface_libs EXCLUDE REGEX "\\\$