Add CMake feature and dependency summary Use CMake's standard package discovery output and FeatureSummary instead of custom status messages, so configuration reports package availability, versions, and failure reasons consistently. Rename Ceres feature controls to use the WITH_ prefix to group them consistently and eliminate inconcistent naming. Remove retrospective cache updates so package discovery preserves caller-provided values and avoids unnecessary side effects. Fixes #876 Change-Id: Ib6e40d56a0ea89b292fe34554777593ffda95f42
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c47f1f8..dc3d8b4 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml
@@ -121,7 +121,7 @@ run: | cmake -S . -B build_${{matrix.build_type}} \ -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} \ - -DUSE_CUDA=${{matrix.gpu == 'cuda'}} \ + -DWITH_CUDA=${{matrix.gpu == 'cuda'}} \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_C_COMPILER_LAUNCHER=$(which ccache) \ -DCMAKE_CXX_COMPILER_LAUNCHER=$(which ccache) \
diff --git a/CMakeLists.txt b/CMakeLists.txt index 853a59b..f80f9cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,5 +1,5 @@ # Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2024 Google Inc. All rights reserved. +# Copyright 2026 Google Inc. All rights reserved. # http://ceres-solver.org/ # # Redistribution and use in source and binary forms, with or without @@ -29,14 +29,7 @@ # Authors: keir@google.com (Keir Mierle) # alexs.mac@gmail.com (Alex Stewart) -cmake_minimum_required(VERSION 3.16...3.29) -project(Ceres C CXX) - -# Always build position-independent code (PIC), even when building Ceres as a -# static library so that shared libraries can link against it, not just -# executables (PIC does not apply on Windows). Global variable can be overridden -# by the user whereas target properties can be not. -set(CMAKE_POSITION_INDEPENDENT_CODE ON) +cmake_minimum_required(VERSION 3.22...4.4) # NOTE: The 'generic' CMake variables CMAKE_[SOURCE/BINARY]_DIR should not be # used. Always use the project-specific variants (generated by CMake): @@ -50,24 +43,27 @@ # Make CMake aware of the cmake folder for local FindXXX scripts, # append rather than set in case the user has passed their own # additional paths via -D. -list(APPEND CMAKE_MODULE_PATH "${Ceres_SOURCE_DIR}/cmake") -include(AddCompileFlagsIfSupported) -include(CheckCXXCompilerFlag) -include(CheckLibraryExists) -include(GNUInstallDirs) -include(UpdateCacheVariable) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -check_cxx_compiler_flag(/bigobj HAVE_BIGOBJ) -check_library_exists(m pow "" HAVE_LIBM) +# Load the module early here to query Ceres' version. The remaining modules can +# be loaded later especially since some of them rely on enabled languages which +# is done by project() (and an explicit enable_language()). +include(ReadCeresVersionFromSource) -# Xcode 11.0-1 with macOS 10.15 (Catalina) broke alignment. -include(DetectBrokenStackCheckMacOSXcodePairing) -detect_broken_stack_check_macos_xcode_pairing() +read_ceres_version_from_source(${CMAKE_CURRENT_SOURCE_DIR}) -# Set up the git hook to make Gerrit Change-Id: lines in commit messages. -include(AddGerritCommitHook) -add_gerrit_commit_hook(${Ceres_SOURCE_DIR} ${Ceres_BINARY_DIR}) +project(Ceres + DESCRIPTION "A large scale non-linear optimization library" + HOMEPAGE_URL http://ceres-solver.org/ + LANGUAGES C CXX + VERSION ${CERES_VERSION} +) +# Always build position-independent code (PIC), even when building Ceres as a +# static library so that shared libraries can link against it, not just +# executables (PIC does not apply on Windows). Global variable can be overridden +# by the user whereas target properties can be not. +set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${Ceres_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${Ceres_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${Ceres_BINARY_DIR}/lib) @@ -75,52 +71,126 @@ set(CMAKE_RELEASE_POSTFIX "") set(CMAKE_DEBUG_POSTFIX "-debug") -# Read the Ceres version from the source, such that we only ever have a single -# definition of the Ceres version. -include(ReadCeresVersionFromSource) -read_ceres_version_from_source(${Ceres_SOURCE_DIR}) - -enable_testing() - +# Configure CMake checks and helper includes. +include(AddCompileFlagsIfSupported) +include(AddGerritCommitHook) +include(CeresCompileOptionsToComponents) +include(CheckCXXCompilerFlag) +include(CheckLibraryExists) include(CMakeDependentOption) +include(CMakePackageConfigHelpers) +include(CreateCeresConfig) +include(DetectBrokenStackCheckMacOSXcodePairing) +include(EnableSanitizer) +include(FeatureSummary) +include(GNUInstallDirs) include(PrettyPrintCMakeList) -option(SUITESPARSE "Enable SuiteSparse." OFF) -if (APPLE) - option(ACCELERATESPARSE - "Enable use of sparse solvers in Apple's Accelerate framework." ON) - option(ENABLE_BITCODE - "Enable bitcode for iOS builds (disables inline optimizations for Eigen)." OFF) -endif() -# We can't have an option called 'CUDA' since that is a reserved word -- a -# language definition. -set(USE_CUDA "default" CACHE STRING "Enable use of CUDA linear algebra solvers.") -option(LAPACK "Enable use of LAPACK directly within Ceres." ON) -# Template specializations for the Schur complement based solvers. If -# compile time, binary size or compiler performance is an issue, you -# may consider disabling this. -option(SCHUR_SPECIALIZATIONS "Enable fixed-size schur specializations." ON) -option(CUSTOM_BLAS - "Use handcoded BLAS routines (usually faster) instead of Eigen." - ON) -# Enable the use of Eigen as a sparse linear algebra library for -# solving the nonlinear least squares problems. -option(EIGENSPARSE "Enable Eigen as a sparse linear algebra library." ON) -cmake_dependent_option(EIGENMETIS "Enable Eigen METIS support." ON EIGENSPARSE OFF) -option(EXPORT_BUILD_DIR - "Export build directory using CMake (enables external use without install)." OFF) -option(BUILD_TESTING "Enable tests" ON) -option(BUILD_DOCUMENTATION "Build User's Guide (html)" OFF) -option(BUILD_EXAMPLES "Build examples" ON) +function(ceres_register_found_package PACKAGE_NAME) + set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND "${PACKAGE_NAME}") +endfunction() + +# Ceres configuration options. option(BUILD_BENCHMARKS "Build Ceres benchmarking suite" ON) +option(BUILD_DOCUMENTATION "Build User's Guide (html)" OFF) +cmake_dependent_option(BUILD_EXAMPLES "Build examples" ON "NOT IOS" OFF) option(BUILD_SHARED_LIBS "Build Ceres as a shared library." OFF) -option(PROVIDE_UNINSTALL_TARGET "Add a custom target to ease removal of installed targets" ON) -set(SANITIZERS "" CACHE STRING "Semicolon-separated list of sanitizers to use (e.g address, memory, thread)") -include(EnableSanitizer) -enable_sanitizer(${SANITIZERS}) -if (ANDROID) - option(ANDROID_STRIP_DEBUG_SYMBOLS "Strip debug symbols from Android builds (reduces file sizes)" ON) +option(BUILD_TESTING "Enable tests" ON) +cmake_dependent_option(WITH_ACCELERATESPARSE + "Enable use of sparse solvers in Apple's Accelerate framework." ON + APPLE OFF) +cmake_dependent_option(WITH_BITCODE + "Enable bitcode for iOS builds (disables inline optimizations for Eigen)." OFF + IOS OFF) +set(WITH_CUDA "default" CACHE STRING + "Enable use of CUDA linear algebra solvers.") +option(WITH_CUSTOM_BLAS + "Use handcoded BLAS routines (usually faster) instead of Eigen." ON) +cmake_dependent_option(WITH_EIGENMETIS "Enable Eigen METIS support." ON + "NOT DEFINED WITH_EIGENSPARSE OR WITH_EIGENSPARSE" OFF) +# The controlling option is declared later and defaults to ON. +option(WITH_EIGENSPARSE + "Enable Eigen as a sparse linear algebra library." ON) +# Apple considers the BLAS dsyrk_ symbol a private API and rejects iOS apps +# that contain it. +cmake_dependent_option(WITH_LAPACK + "Enable use of LAPACK directly within Ceres." ON "NOT IOS" OFF) +set(WITH_SANITIZERS "" CACHE STRING + "Semicolon-separated list of sanitizers to use (e.g address, memory, thread)") +option(WITH_SCHUR_SPECIALIZATIONS + "Enable fixed-size schur specializations." ON) +cmake_dependent_option(WITH_STRIPPED_DEBUG_SYMBOLS + "Strip debug symbols from Android builds (reduces file sizes)" ON + ANDROID OFF) +option(WITH_SUITESPARSE "Enable SuiteSparse." OFF) +option(WITH_UNINSTALL_TARGET + "Add a custom target to ease removal of installed targets" ON) + +set_property(CACHE WITH_CUDA PROPERTY STRINGS OFF default static) + +# Discover dependencies. +if (NOT EXISTS "${Ceres_SOURCE_DIR}/third_party/abseil-cpp/CMakeLists.txt") + find_package(absl REQUIRED 20240116) endif() +find_package(AccelerateSparse) +# Version 1.3 was first to provide import targets. +find_package(benchmark 1.3) +find_package(BLAS) +find_package(LAPACK) +find_package(CUDAToolkit) +find_package(cudss NO_MODULE) + +# Eigen3's CMake package prior to version 5.0.0 does not support version +# ranges with different major version components. +set (_Ceres_Eigen3_DIR) +if (Eigen3_DIR) + set (_Ceres_Eigen3_DIR "${Eigen3_DIR}") +endif (Eigen3_DIR) + +find_package(Eigen3 3.3.4...5 NO_MODULE) +if (NOT Eigen3_FOUND) + if (_Ceres_Eigen3_DIR) + set (Eigen3_DIR "${_Ceres_Eigen3_DIR}") + endif (_Ceres_Eigen3_DIR) + find_package(Eigen3 3.3.4 REQUIRED NO_MODULE) +endif (NOT Eigen3_FOUND) +unset (_Ceres_Eigen3_DIR) + +if (NOT EXISTS "${Ceres_SOURCE_DIR}/third_party/googletest/CMakeLists.txt") + find_package(GTest 1.14.0) +endif() + +find_package(METIS) +find_package(Sphinx COMPONENTS sphinx_rtd_theme) +# Ubuntu 22.04 provides SuiteSparse 5.10.1, which remains supported. +find_package(SuiteSparse 4.5.6 COMPONENTS CHOLMOD SPQR + OPTIONAL_COMPONENTS Partition) +find_package(Threads REQUIRED) + +# Show dependencies in importance order in the CMake feature summary. +set_property(GLOBAL PROPERTY FeatureSummary_PKG_TYPES + REQUIRED RECOMMENDED OPTIONAL) + +check_cxx_compiler_flag(/bigobj HAVE_BIGOBJ) +# Ceres C++ compiler flags can be too strict for an external library code +# which we do not maintain. +check_cxx_compiler_flag("-Wno-missing-declarations" + CHECK_CXX_FLAG_Wno_missing_declarations) +# Older versions of Clang (<= 2.9) do not support the 'return-type-c-linkage' +# option, so check for its presence before adding it to the default flags set. +check_cxx_compiler_flag("-Wno-return-type-c-linkage" + HAVE_RETURN_TYPE_C_LINKAGE) +check_library_exists(m pow "" HAVE_LIBM) + +detect_broken_stack_check_macos_xcode_pairing() +enable_sanitizer(${WITH_SANITIZERS}) + +if (BUILD_TESTING) + enable_testing() +endif() + +# Set up the git hook to make Gerrit Change-Id: lines in commit messages. +add_gerrit_commit_hook(${Ceres_SOURCE_DIR} ${Ceres_BINARY_DIR}) # Abseil if (EXISTS "${Ceres_SOURCE_DIR}/third_party/abseil-cpp/CMakeLists.txt") @@ -130,17 +200,12 @@ string(REGEX MATCH "VERSION[ ]+([0-9]+(\.(rc)?[0-9]+)?)" version_param "${project_call}") if (version_param) set(absl_VERSION "${CMAKE_MATCH_1}") - message("-- Using the version of abseil in ceres-solver/third_party/abseil-cpp with version ${absl_VERSION}") break () endif() endforeach() unset(project_calls) unset(project_call) unset(version_param) - if (NOT absl_VERSION) - message("-- Using the version of abseil in ceres-solver/third_party/abseil-cpp") - endif() - # In the normal course of things, we would not need the following constraint. # However, if we do not set the standard, then std::basic_string_view is not # available, which absl depends on and linking the test fails. @@ -157,6 +222,11 @@ option(ABSL_ENABLE_INSTALL "Enable install rules" ON) add_subdirectory(third_party/abseil-cpp) + export(EXPORT abslTargets + FILE "${Ceres_BINARY_DIR}/third_party/abseil-cpp/abslTargets.cmake" + NAMESPACE absl::) + set(absl_FOUND TRUE) + ceres_register_found_package(absl) unset(CMAKE_CXX_STANDARD) unset(CMAKE_CXX_VISIBILITY_PRESET) @@ -169,9 +239,6 @@ set(CERES_USE_SYSTEM_ABSL NO) else() - message("-- ceres-solver/third_party/abseil-cpp is empty, so falling back to system installed abseil") - find_package(absl REQUIRED 20240116) - message("-- Found abseil version ${absl_VERSION}: ${absl_DIR}") if (NOT absl_VERSION) # In some installs, abseil will not export the version number, in # which case we check for vlog_is_on, which is a feature we need @@ -183,16 +250,25 @@ set(CERES_USE_SYSTEM_ABSL YES) endif() if (NOT absl_VERSION) - if (EXPORT_BUILD_DIR) - message(FATAL_ERROR "Cannot generate CeresConfig.cmake due to missing Abseil version. Using resulting package configuration will likely result in integration errors. Set absl_VERSION manually to disable this error message") - endif() install (CODE [=[message (FATAL_ERROR "Cannot generate CeresConfig.cmake due to missing Abseil version. Using resulting package configuration will likely result in integration errors. Set absl_VERSION manually to disable this error message.")]=]) endif() # Google Test if (BUILD_TESTING) if (EXISTS "${Ceres_SOURCE_DIR}/third_party/googletest/CMakeLists.txt") - message("-- Using the version of googletest in ceres-solver/third_party/googletest") + file(STRINGS "${Ceres_SOURCE_DIR}/third_party/googletest/CMakeLists.txt" + googletest_version_lines REGEX "set\\([ \\t]*GOOGLETEST_VERSION") + foreach(googletest_version_line IN LISTS googletest_version_lines) + string(REGEX MATCH "GOOGLETEST_VERSION[ \\t]+([0-9]+(\\.[0-9]+)+)" + googletest_version_match "${googletest_version_line}") + if (googletest_version_match) + set(GOOGLETEST_VERSION "${CMAKE_MATCH_1}") + break() + endif() + endforeach() + unset(googletest_version_lines) + unset(googletest_version_line) + unset(googletest_version_match) # Now that we have absl as a dependency, it would make sense to tell # googletest to use absl also, but doing so seems to be problematic # for now, since if there is a version of absl and re2 installed in @@ -204,250 +280,181 @@ # set(GTEST_HAS_ABSL ON CACHE BOOL "Use ABSL when building gtest") add_subdirectory(${Ceres_SOURCE_DIR}/third_party/googletest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/googletest) + set(GTest_FOUND TRUE) + ceres_register_found_package(GTest) else() - message("-- ceres-solver/third_party/googletest is empty, so falling back to system installed googletest") - find_package(GTest 1.14.0 REQUIRED) + if (NOT GTest_FOUND) + message(FATAL_ERROR "GoogleTest 1.14.0 or newer is required to build tests") + endif() + set(GOOGLETEST_VERSION "${GTest_VERSION}") endif() endif(BUILD_TESTING) # IOS is defined iff using the iOS.cmake CMake toolchain to build a static # library for iOS. if (IOS) - message(STATUS "Building Ceres for iOS platform: ${IOS_PLATFORM}") + message(VERBOSE "Building Ceres for iOS platform: ${IOS_PLATFORM}") # Ceres requires at least iOS 7.0+. if (IOS_DEPLOYMENT_TARGET VERSION_LESS 7.0) message(FATAL_ERROR "Unsupported iOS version: ${IOS_DEPLOYMENT_TARGET}, Ceres " "requires at least iOS version 7.0") endif() - - # Apple claims that the BLAS call dsyrk_ is a private API, and will not allow - # you to submit to the Apple Store if the symbol is present. - update_cache_variable(LAPACK OFF) - message(STATUS "Building for iOS: SuiteSparse, LAPACK are not available.") - - update_cache_variable(BUILD_EXAMPLES OFF) - message(STATUS "Building for iOS: Will not build examples.") endif (IOS) +# Compute effective feature states once so build configuration and the summary +# cannot disagree about which optional components are available. +set(_Ceres_FEATURE_ACCELERATE OFF) +if (WITH_ACCELERATESPARSE AND AccelerateSparse_FOUND) + set(_Ceres_FEATURE_ACCELERATE ON) +endif() + +set(_Ceres_FEATURE_CUDA OFF) +if (WITH_CUDA AND CUDAToolkit_FOUND) + set(_Ceres_FEATURE_CUDA ON) +endif() + +set(_Ceres_FEATURE_CUDSS OFF) +if (_Ceres_FEATURE_CUDA AND cudss_FOUND) + set(_Ceres_FEATURE_CUDSS ON) +endif() + +set(_Ceres_FEATURE_EIGEN_METIS OFF) +if (WITH_EIGENMETIS AND METIS_FOUND) + set(_Ceres_FEATURE_EIGEN_METIS ON) +endif() + +set(_Ceres_FEATURE_EIGEN_SPARSE OFF) +if (WITH_EIGENSPARSE AND Eigen3_FOUND) + set(_Ceres_FEATURE_EIGEN_SPARSE ON) +endif() + +set(_Ceres_FEATURE_LAPACK OFF) +if (WITH_LAPACK AND LAPACK_FOUND) + set(_Ceres_FEATURE_LAPACK ON) +endif() + +set(_Ceres_FEATURE_SUITESPARSE OFF) +if (WITH_SUITESPARSE AND SuiteSparse_FOUND) + set(_Ceres_FEATURE_SUITESPARSE ON) +endif() + +set(_Ceres_FEATURE_CHOLMOD_PARTITION OFF) +if (_Ceres_FEATURE_SUITESPARSE AND SuiteSparse_Partition_FOUND) + set(_Ceres_FEATURE_CHOLMOD_PARTITION ON) +endif() + +set(_Ceres_FEATURE_DOCUMENTATION OFF) +if (BUILD_DOCUMENTATION AND Sphinx_FOUND) + set(_Ceres_FEATURE_DOCUMENTATION ON) +endif() + +set(_Ceres_FEATURE_BENCHMARKS OFF) +if (BUILD_BENCHMARKS AND benchmark_FOUND) + set(_Ceres_FEATURE_BENCHMARKS ON) +endif() + +set(WITH_CHOLMOD_FLOAT OFF) +if (_Ceres_FEATURE_SUITESPARSE AND + SuiteSparse_VERSION VERSION_GREATER_EQUAL 7.4.0) + set(WITH_CHOLMOD_FLOAT ON) +endif() + unset(CERES_COMPILE_OPTIONS) -# We call find_package multiple time with different versioning scheme to support -# both Eigen3 3.x and 5.x. If the first invocation fails and Eigen3_DIR was set -# to a possible location, e.g., during cross-compilation, failure to locate the -# package will unset the variable. To avoid losing the path hint, we save it and -# reuse it in the second find_package call. -set (_Ceres_Eigen3_DIR) +if (_Ceres_FEATURE_EIGEN_SPARSE) + list(APPEND CERES_COMPILE_OPTIONS CERES_USE_EIGEN_SPARSE) +else() + add_definitions(-DEIGEN_MPL2_ONLY) +endif() -if (Eigen3_DIR) - set (_Ceres_Eigen3_DIR "${Eigen3_DIR}") -endif (Eigen3_DIR) +if (_Ceres_FEATURE_CUDA) + set(CUDAToolkit_DEPENDENCY + "find_dependency(CUDAToolkit ${CUDAToolkit_VERSION})") + enable_language(CUDA) + set(CMAKE_CUDA_ARCHITECTURES "") + if (CUDAToolkit_VERSION VERSION_LESS "13.0") + # Support Maxwell GPUs. + list(APPEND CMAKE_CUDA_ARCHITECTURES "50") + if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "8.0") + # Support Pascal GPUs. + list(APPEND CMAKE_CUDA_ARCHITECTURES "60") + endif() + if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "9.0") + # Support Volta GPUs. + list(APPEND CMAKE_CUDA_ARCHITECTURES "70") + endif() + endif() + if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "10.0") + # Support Turing GPUs. + list(APPEND CMAKE_CUDA_ARCHITECTURES "75") + endif() + if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.0") + # Support Ampere GPUs. + list(APPEND CMAKE_CUDA_ARCHITECTURES "80") + endif() + if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "12.0") + # Support Hopper GPUs. + list(APPEND CMAKE_CUDA_ARCHITECTURES "90") + endif() -# Eigen. -# Eigen delivers Eigen3Config.cmake since v3.3.3 -find_package(Eigen3 3.3.4...5 NO_MODULE) - -if (NOT Eigen3_FOUND) - if (_Ceres_Eigen3_DIR) - set (Eigen3_DIR "${_Ceres_Eigen3_DIR}") - endif (_Ceres_Eigen3_DIR) - # Eigen3's CMake package config prior to 5.0.0 does not support version ranges - # with different major version components. To ensure backward compatibility, - # we locate the package using the previous version scheme as a fallback - # mechanism. - find_package(Eigen3 3.3.4 REQUIRED NO_MODULE) -endif (NOT Eigen3_FOUND) - -unset (_Ceres_Eigen3_DIR) - -if (Eigen3_FOUND) - message("-- Found Eigen version ${Eigen3_VERSION}: ${Eigen3_DIR}") - if (EIGENSPARSE) - message("-- Enabling use of Eigen as a sparse linear algebra library.") - list(APPEND CERES_COMPILE_OPTIONS CERES_USE_EIGEN_SPARSE) - else (EIGENSPARSE) - message("-- Disabling use of Eigen as a sparse linear algebra library.") - message(" This does not affect the covariance estimation algorithm ") - message(" which can still use the EIGEN_SPARSE_QR algorithm.") - add_definitions(-DEIGEN_MPL2_ONLY) - endif (EIGENSPARSE) -endif (Eigen3_FOUND) - -if (CMAKE_VERSION VERSION_LESS 3.17) - set_property(CACHE USE_CUDA PROPERTY STRINGS OFF default) -else (CMAKE_VERSION VERSION_LESS 3.17) - set_property(CACHE USE_CUDA PROPERTY STRINGS OFF default static) -endif (CMAKE_VERSION VERSION_LESS 3.17) - -if (USE_CUDA) - if (CMAKE_VERSION VERSION_LESS 3.17) - # On older versions of CMake (20.04 default is 3.16) FindCUDAToolkit was - # not available, but FindCUDA was deprecated. To avoid special-case handling - # elsewhere, emulate the effects of FindCUDAToolkit locally in terms of the - # expected CMake imported targets and defined variables. This can be removed - # from as soon as the min CMake version is >= 3.17. - find_package(CUDA QUIET) - if (CUDA_FOUND) - message("-- Found CUDA version ${CUDA_VERSION} installed in: " - "${CUDA_TOOLKIT_ROOT_DIR} via legacy (< 3.17) CMake module. " - "Using the legacy CMake module means that any installation of " - "Ceres will require that the CUDA libraries be installed in a " - "location included in the LD_LIBRARY_PATH.") - enable_language(CUDA) - - macro(DECLARE_IMPORTED_CUDA_TARGET COMPONENT) - add_library(CUDA::${COMPONENT} INTERFACE IMPORTED) - target_include_directories( - CUDA::${COMPONENT} INTERFACE ${CUDA_INCLUDE_DIRS}) - target_link_libraries( - CUDA::${COMPONENT} INTERFACE ${CUDA_${COMPONENT}_LIBRARY} ${ARGN}) - endmacro() - - declare_imported_cuda_target(cublas) - declare_imported_cuda_target(cusolver) - declare_imported_cuda_target(cusparse) - declare_imported_cuda_target(cudart ${CUDA_LIBRARIES}) - - set(CERES_CUDA_TARGET_SUFFIX "") - set(CUDAToolkit_BIN_DIR ${CUDA_TOOLKIT_ROOT_DIR}/bin) - - else (CUDA_FOUND) - message("-- Did not find CUDA, disabling CUDA support.") - update_cache_variable(USE_CUDA OFF) - endif (CUDA_FOUND) - else (CMAKE_VERSION VERSION_LESS 3.17) - find_package(CUDAToolkit QUIET) - if (CUDAToolkit_FOUND) - message("-- Found CUDA version ${CUDAToolkit_VERSION} installed in: " - "${CUDAToolkit_TARGET_DIR}") - set(CUDAToolkit_DEPENDENCY - "find_dependency(CUDAToolkit ${CUDAToolkit_VERSION})") - enable_language(CUDA) - if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") - set(CMAKE_CUDA_ARCHITECTURES "") - if (CUDAToolkit_VERSION VERSION_LESS "13.0") - # Support Maxwell GPUs. - list(APPEND CMAKE_CUDA_ARCHITECTURES "50") - if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "8.0") - # Support Pascal GPUs. - list(APPEND CMAKE_CUDA_ARCHITECTURES "60") - endif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "8.0") - if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "9.0") - # Support Volta GPUs. - list(APPEND CMAKE_CUDA_ARCHITECTURES "70") - endif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "9.0") - endif(CUDAToolkit_VERSION VERSION_LESS "13.0") - if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "10.0") - # Support Turing GPUs. - list(APPEND CMAKE_CUDA_ARCHITECTURES "75") - endif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "10.0") - if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.0") - # Support Ampere GPUs. - list(APPEND CMAKE_CUDA_ARCHITECTURES "80") - endif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.0") - if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "12.0") - # Support Hopper GPUs. - list(APPEND CMAKE_CUDA_ARCHITECTURES "90") - endif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "12.0") - message("-- Setting CUDA Architecture to ${CMAKE_CUDA_ARCHITECTURES}") - endif() - - if (USE_CUDA STREQUAL "static") - set(CERES_CUDA_TARGET_SUFFIX "_static") - else (USE_CUDA STREQUAL "static") - set(CERES_CUDA_TARGET_SUFFIX "") - endif (USE_CUDA STREQUAL "static") - else (CUDAToolkit_FOUND) - message("-- Did not find CUDA, disabling CUDA support.") - update_cache_variable(USE_CUDA OFF) - endif (CUDAToolkit_FOUND) - endif (CMAKE_VERSION VERSION_LESS 3.17) -endif (USE_CUDA) - -if (USE_CUDA) + if (WITH_CUDA STREQUAL "static") + set(CERES_CUDA_TARGET_SUFFIX "_static") + else() + set(CERES_CUDA_TARGET_SUFFIX "") + endif() list(APPEND CERES_CUDA_LIBRARIES CUDA::cublas${CERES_CUDA_TARGET_SUFFIX} CUDA::cudart${CERES_CUDA_TARGET_SUFFIX} CUDA::cusolver${CERES_CUDA_TARGET_SUFFIX} CUDA::cusparse${CERES_CUDA_TARGET_SUFFIX}) - find_package(cudss CONFIG) - if (cudss_FOUND) + if (_Ceres_FEATURE_CUDSS) set(cudss_DEPENDENCY "find_dependency(cudss ${cudss_VERSION})") list(APPEND CERES_CUDA_LIBRARIES cudss${CERES_CUDA_TARGET_SUFFIX}) - else (cudss_FOUND) + else() list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CUDSS) - message("-- Did not find cuDSS, SPARSE_SCHUR and SPARSE_NORMAL_CHOLESKY with CUDA_SPARSE will not be available.") - endif (cudss_FOUND) + endif() unset (CERES_CUDA_TARGET_SUFFIX) set(CMAKE_CUDA_RUNTIME_LIBRARY NONE) -else (USE_CUDA) - message("-- Building without CUDA.") +else() list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CUDSS) list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CUDA) -endif (USE_CUDA) +endif() -if (LAPACK) - find_package(LAPACK QUIET) - if (LAPACK_FOUND) - message("-- Found LAPACK library: ${LAPACK_LIBRARIES}") - else (LAPACK_FOUND) - message("-- Did not find LAPACK library, disabling LAPACK support.") - update_cache_variable(LAPACK OFF) - list(APPEND CERES_COMPILE_OPTIONS CERES_NO_LAPACK) - endif (LAPACK_FOUND) -else (LAPACK) - message("-- Building without LAPACK.") +if (NOT _Ceres_FEATURE_LAPACK) list(APPEND CERES_COMPILE_OPTIONS CERES_NO_LAPACK) -endif (LAPACK) +endif() # Set the install path for the installed CeresConfig.cmake configuration file # relative to CMAKE_INSTALL_PREFIX. set(RELATIVE_CMAKECONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/Ceres) +set(_Ceres_CONFIG_DEPENDENCY_MODULES) -if (SUITESPARSE) +if (_Ceres_FEATURE_SUITESPARSE) # SuiteSparse support is opt-in and was explicitly requested. If it and # all of its dependencies are found, Ceres is built with SuiteSparse # support. - # Check for SuiteSparse and dependencies. - find_package(SuiteSparse 4.5.6 COMPONENTS CHOLMOD SPQR - OPTIONAL_COMPONENTS Partition) - if (SuiteSparse_FOUND) - set(SuiteSparse_DEPENDENCY "find_dependency(SuiteSparse ${SuiteSparse_VERSION})") - message("-- Found SuiteSparse ${SuiteSparse_VERSION}, " - "building with SuiteSparse.") - - if (SuiteSparse_VERSION VERSION_LESS 7.4.0) - list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CHOLMOD_FLOAT) - else() - message("-- CHOLMOD supports single precision factorization") - endif() - if (SuiteSparse_NO_CMAKE OR NOT SuiteSparse_DIR) - install(FILES ${Ceres_SOURCE_DIR}/cmake/FindSuiteSparse.cmake - ${Ceres_SOURCE_DIR}/cmake/FindMETIS.cmake - DESTINATION ${RELATIVE_CMAKECONFIG_INSTALL_DIR}) - endif (SuiteSparse_NO_CMAKE OR NOT SuiteSparse_DIR) - else (SuiteSparse_FOUND) - # Disable use of SuiteSparse if it cannot be found and continue. - message("-- Did not find all SuiteSparse dependencies, disabling " - "SuiteSparse support.") - update_cache_variable(SUITESPARSE OFF) - list(APPEND CERES_COMPILE_OPTIONS CERES_NO_SUITESPARSE) - endif (SuiteSparse_FOUND) -else (SUITESPARSE) - message("-- Building without SuiteSparse.") + set(SuiteSparse_DEPENDENCY "find_dependency(SuiteSparse ${SuiteSparse_VERSION})") + if (NOT WITH_CHOLMOD_FLOAT) + list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CHOLMOD_FLOAT) + endif() + if (SuiteSparse_NO_CMAKE OR NOT SuiteSparse_DIR) + list(APPEND _Ceres_CONFIG_DEPENDENCY_MODULES + ${Ceres_SOURCE_DIR}/cmake/FindSuiteSparse.cmake + ${Ceres_SOURCE_DIR}/cmake/FindMETIS.cmake) + endif (SuiteSparse_NO_CMAKE OR NOT SuiteSparse_DIR) +else() list(APPEND CERES_COMPILE_OPTIONS CERES_NO_SUITESPARSE) -endif (SUITESPARSE) +endif() -if (NOT SuiteSparse_Partition_FOUND) +if (NOT _Ceres_FEATURE_CHOLMOD_PARTITION) list (APPEND CERES_COMPILE_OPTIONS CERES_NO_CHOLMOD_PARTITION) -endif (NOT SuiteSparse_Partition_FOUND) +endif() -if (EIGENMETIS) - find_package (METIS) - if (METIS_FOUND) +if (_Ceres_FEATURE_EIGEN_METIS) # Since METIS is a private dependency of Ceres, it requires access to the # link-only METIS::METIS target to avoid undefined linker errors in projects # relying on Ceres. We do not actually need to propagate anything besides @@ -455,94 +462,35 @@ set(METIS_DEPENDENCY "find_dependency(METIS ${METIS_VERSION})") # METIS find module must be installed unless a package config is being used. if (NOT METIS_DIR) - install(FILES ${Ceres_SOURCE_DIR}/cmake/FindMETIS.cmake - DESTINATION ${RELATIVE_CMAKECONFIG_INSTALL_DIR}) + list(APPEND _Ceres_CONFIG_DEPENDENCY_MODULES + ${Ceres_SOURCE_DIR}/cmake/FindMETIS.cmake) endif (NOT METIS_DIR) - else (METIS_FOUND) - message("-- Did not find METIS, disabling Eigen METIS support.") - update_cache_variable(EIGENMETIS OFF) - list (APPEND CERES_COMPILE_OPTIONS CERES_NO_EIGEN_METIS) - endif (METIS_FOUND) -else (EIGENMETIS) - message("-- Building without Eigen METIS support.") - list (APPEND CERES_COMPILE_OPTIONS CERES_NO_EIGEN_METIS) -endif (EIGENMETIS) - -if (ACCELERATESPARSE) - find_package(AccelerateSparse) - if (AccelerateSparse_FOUND) - message("-- Found Apple's Accelerate framework with sparse solvers, " - "building with Accelerate sparse support.") - else() - message("-- Failed to find Apple's Accelerate framework with sparse solvers, " - "building without Accelerate sparse support.") - update_cache_variable(ACCELERATESPARSE OFF) - list(APPEND CERES_COMPILE_OPTIONS CERES_NO_ACCELERATE_SPARSE) - endif() else() - message("-- Building without Apple's Accelerate sparse support.") + list (APPEND CERES_COMPILE_OPTIONS CERES_NO_EIGEN_METIS) +endif() + +if (NOT _Ceres_FEATURE_ACCELERATE) list(APPEND CERES_COMPILE_OPTIONS CERES_NO_ACCELERATE_SPARSE) mark_as_advanced(FORCE AccelerateSparse_INCLUDE_DIR AccelerateSparse_LIBRARY) endif() -# Ensure that the user understands they have disabled all sparse libraries. -if (NOT SUITESPARSE AND NOT EIGENSPARSE AND NOT ACCELERATESPARSE AND NOT cudss_FOUND) - message(" ===============================================================") - message(" Compiling without any sparse library: SuiteSparse, EigenSparse") - message(" Apple's Accelerate & cuDSS are all disabled or unavailable. ") - message(" No sparse linear solvers (SPARSE_NORMAL_CHOLESKY & SPARSE_SCHUR)") - message(" will be available when Ceres is used.") - message(" ===============================================================") -endif() - # ANDROID define is set by the Android CMake toolchain file. if (ANDROID) - message(" ================================================================") - if (ANDROID_STRIP_DEBUG_SYMBOLS) + if (WITH_STRIPPED_DEBUG_SYMBOLS) # Strip debug information unconditionally to avoid +200MB library file sizes. set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s" ) set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s" ) - message(" Stripping debug information from Android build of Ceres library ") - message(" to avoid +200MB library files.") - else() - message(" Warning: not stripping debug information from Android build of ") - message(" Ceres library. This will result in a large (+200MB) library.") endif() - message("") - message(" You can control whether debug information is stripped via the ") - message(" ANDROID_STRIP_DEBUG_SYMBOLS CMake option when configuring Ceres.") - message(" ================================================================") endif() -if (NOT SCHUR_SPECIALIZATIONS) +if (NOT WITH_SCHUR_SPECIALIZATIONS) list(APPEND CERES_COMPILE_OPTIONS CERES_RESTRICT_SCHUR_SPECIALIZATION) - message("-- Disabling Schur specializations (faster compiles)") -endif (NOT SCHUR_SPECIALIZATIONS) +endif (NOT WITH_SCHUR_SPECIALIZATIONS) -if (NOT CUSTOM_BLAS) +if (NOT WITH_CUSTOM_BLAS) list(APPEND CERES_COMPILE_OPTIONS CERES_NO_CUSTOM_BLAS) - message("-- Disabling custom blas") -endif (NOT CUSTOM_BLAS) - -if (BUILD_BENCHMARKS) - # Version 1.3 was first to provide import targets - find_package(benchmark 1.3 QUIET) - if (benchmark_FOUND) - message("-- Found Google benchmark library. Building Ceres benchmarks.") - else() - message("-- Failed to find Google benchmark library, disabling build of benchmarks.") - update_cache_variable(BUILD_BENCHMARKS OFF) - endif() - mark_as_advanced(benchmark_DIR) -endif() - -# TODO Report features using the FeatureSummary CMake module -if (BUILD_SHARED_LIBS) - message("-- Building Ceres as a shared library.") -else (BUILD_SHARED_LIBS) - message("-- Building Ceres as a static library.") -endif (BUILD_SHARED_LIBS) +endif (NOT WITH_CUSTOM_BLAS) # Change the default build type from Debug to Release, while still # supporting overriding the build type. @@ -550,16 +498,16 @@ # The CACHE STRING logic here and elsewhere is needed to force CMake # to pay attention to the value of these variables. if (NOT CMAKE_BUILD_TYPE) - message("-- No build type specified; defaulting to CMAKE_BUILD_TYPE=Release.") + message(STATUS "No build type specified; defaulting to CMAKE_BUILD_TYPE=Release.") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) else (NOT CMAKE_BUILD_TYPE) if (CMAKE_BUILD_TYPE STREQUAL "Debug") - message("\n=================================================================================") - message("\n-- Build type: Debug. Performance will be terrible!") - message("-- Add -DCMAKE_BUILD_TYPE=Release to the CMake command line to get an optimized build.") - message("\n=================================================================================") + message(STATUS "\n=================================================================================") + message(STATUS "\nBuild type: Debug. Performance will be terrible!") + message(STATUS "Add -DCMAKE_BUILD_TYPE=Release to the CMake command line to get an optimized build.") + message(STATUS "\n=================================================================================") endif (CMAKE_BUILD_TYPE STREQUAL "Debug") endif (NOT CMAKE_BUILD_TYPE) @@ -623,7 +571,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Matches Clang & AppleClang. # Optimize for Eigen OR enable bitcode; you cannot do both since bitcode is an # intermediate representation. - if (ENABLE_BITCODE) + if (WITH_BITCODE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode") else () @@ -634,10 +582,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -mllvm -inline-threshold=600") endif () - # Older versions of Clang (<= 2.9) do not support the 'return-type-c-linkage' - # option, so check for its presence before adding it to the default flags set. - check_cxx_compiler_flag("-Wno-return-type-c-linkage" - HAVE_RETURN_TYPE_C_LINKAGE) if (HAVE_RETURN_TYPE_C_LINKAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type-c-linkage") endif(HAVE_RETURN_TYPE_C_LINKAGE) @@ -652,30 +596,19 @@ # built against this configured header, it needs to have the same relative # include path as it would if it were in the source tree (or installed). list(REMOVE_DUPLICATES CERES_COMPILE_OPTIONS) -include(CreateCeresConfig) create_ceres_config("${CERES_COMPILE_OPTIONS}" ${Ceres_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/ceres/internal) add_subdirectory(internal/ceres) -if (BUILD_DOCUMENTATION) - find_package (Sphinx REQUIRED COMPONENTS sphinx_rtd_theme) - if (NOT Sphinx_FOUND) - message("-- Failed to find Sphinx and/or its dependencies, disabling build of documentation.") - update_cache_variable(BUILD_DOCUMENTATION OFF) - else() - # Generate the User's Guide (html). - # The corresponding target is ceres_docs, but is included in ALL. - message("-- Build the HTML documentation.") - add_subdirectory(docs) - endif() -endif (BUILD_DOCUMENTATION) +if (_Ceres_FEATURE_DOCUMENTATION) + # Generate the User's Guide (html). + # The corresponding target is ceres_docs, but is included in ALL. + add_subdirectory(docs) +endif (_Ceres_FEATURE_DOCUMENTATION) if (BUILD_EXAMPLES) - message("-- Build the examples.") add_subdirectory(examples) -else (BUILD_EXAMPLES) - message("-- Do not build any example.") endif (BUILD_EXAMPLES) # Setup installation of Ceres public headers. @@ -714,11 +647,53 @@ # Build the list of Ceres components for CeresConfig.cmake from the current set # of compile options. -include(CeresCompileOptionsToComponents) ceres_compile_options_to_components("${CERES_COMPILE_OPTIONS}" CERES_COMPILED_COMPONENTS) -include(CMakePackageConfigHelpers) +# Make dependency modules available relative to the package config. +list(REMOVE_DUPLICATES _Ceres_CONFIG_DEPENDENCY_MODULES) +set(_Ceres_FIND_MODULES + "${Ceres_SOURCE_DIR}/cmake/FindAccelerateSparse.cmake" + "${Ceres_SOURCE_DIR}/cmake/FindMETIS.cmake" + "${Ceres_SOURCE_DIR}/cmake/FindSphinx.cmake" + "${Ceres_SOURCE_DIR}/cmake/FindSuiteSparse.cmake") +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + ${_Ceres_FIND_MODULES}) +set(_Ceres_FIND_MODULES_LINK "${Ceres_BINARY_DIR}/CeresFindModules") +if (IS_SYMLINK "${_Ceres_FIND_MODULES_LINK}") + file(REMOVE "${_Ceres_FIND_MODULES_LINK}") +elseif(EXISTS "${_Ceres_FIND_MODULES_LINK}" + AND NOT IS_DIRECTORY "${_Ceres_FIND_MODULES_LINK}") + message(FATAL_ERROR + "CeresFindModules exists but is not a directory: " + "${_Ceres_FIND_MODULES_LINK}") +endif() +file(MAKE_DIRECTORY "${_Ceres_FIND_MODULES_LINK}") +foreach(_Ceres_FIND_MODULE IN LISTS _Ceres_FIND_MODULES) + get_filename_component(_Ceres_FIND_MODULE_NAME + "${_Ceres_FIND_MODULE}" NAME) + file(CREATE_LINK + "${_Ceres_FIND_MODULE}" + "${_Ceres_FIND_MODULES_LINK}/${_Ceres_FIND_MODULE_NAME}" + SYMBOLIC COPY_ON_ERROR + RESULT _Ceres_FIND_MODULE_LINK_RESULT) + if (_Ceres_FIND_MODULE_LINK_RESULT) + message(FATAL_ERROR + "Creating the link for '${_Ceres_FIND_MODULE}' returned " + "'${_Ceres_FIND_MODULE_LINK_RESULT}', expected '0'") + endif() +endforeach() +if (_Ceres_CONFIG_DEPENDENCY_MODULES) + install(FILES ${_Ceres_CONFIG_DEPENDENCY_MODULES} + DESTINATION ${RELATIVE_CMAKECONFIG_INSTALL_DIR}) +endif() +unset(_Ceres_CONFIG_DEPENDENCY_MODULES) +unset(_Ceres_FIND_MODULE) +unset(_Ceres_FIND_MODULE_LINK_RESULT) +unset(_Ceres_FIND_MODULE_NAME) +unset(_Ceres_FIND_MODULES) +unset(_Ceres_FIND_MODULES_LINK) + # Create a CeresConfigVersion.cmake file containing the version information, # used by both export() & install(). @@ -726,6 +701,13 @@ VERSION ${CERES_VERSION} COMPATIBILITY SameMajorVersion) +# Generate one package config for build-tree use and installation. +configure_package_config_file( + "${Ceres_SOURCE_DIR}/cmake/CeresConfig.cmake.in" + "${Ceres_BINARY_DIR}/CeresConfig.cmake" + INSTALL_DESTINATION "${RELATIVE_CMAKECONFIG_INSTALL_DIR}" + NO_SET_AND_CHECK_MACRO) + # Install method #1: Put Ceres in CMAKE_INSTALL_PREFIX: /usr/local or equivalent. # This "exports" for installation all targets which have been put into the @@ -738,35 +720,16 @@ NAMESPACE Ceres:: DESTINATION ${RELATIVE_CMAKECONFIG_INSTALL_DIR} FILE CeresTargets.cmake) -# Save the relative path from the installed CeresConfig.cmake file to the -# install prefix. We do not save an absolute path in case the installed package -# is subsequently relocated after installation (on Windows). -file(RELATIVE_PATH INSTALL_ROOT_REL_CONFIG_INSTALL_DIR - ${CMAKE_INSTALL_PREFIX}/${RELATIVE_CMAKECONFIG_INSTALL_DIR} - ${CMAKE_INSTALL_PREFIX}) - -# Configure a CeresConfig.cmake file for an installed version of Ceres from the -# template, reflecting the current build options. -# -# NOTE: The -install suffix is necessary to distinguish the install version from -# the exported version, which must be named CeresConfig.cmake in -# Ceres_BINARY_DIR to be detected. The suffix is removed when -# it is installed. -set(SETUP_CERES_CONFIG_FOR_INSTALLATION TRUE) -configure_file("${Ceres_SOURCE_DIR}/cmake/CeresConfig.cmake.in" - "${Ceres_BINARY_DIR}/CeresConfig-install.cmake" @ONLY) - # Install the configuration files into the same directory as the autogenerated # CeresTargets.cmake file. We include the find_package() scripts for libraries # whose headers are included in the public API of Ceres and should thus be # present in CERES_INCLUDE_DIRS. -install(FILES "${Ceres_BINARY_DIR}/CeresConfig-install.cmake" - RENAME CeresConfig.cmake +install(FILES "${Ceres_BINARY_DIR}/CeresConfig.cmake" DESTINATION ${RELATIVE_CMAKECONFIG_INSTALL_DIR}) install(FILES "${Ceres_BINARY_DIR}/CeresConfigVersion.cmake" DESTINATION ${RELATIVE_CMAKECONFIG_INSTALL_DIR}) -if (PROVIDE_UNINSTALL_TARGET) +if (WITH_UNINSTALL_TARGET) # Create an uninstall target to remove all installed files. configure_file("${Ceres_SOURCE_DIR}/cmake/uninstall.cmake.in" "${Ceres_BINARY_DIR}/cmake/uninstall.cmake" @@ -775,40 +738,165 @@ COMMAND ${CMAKE_COMMAND} -P ${Ceres_BINARY_DIR}/cmake/uninstall.cmake) endif() -# Install method #2: Put Ceres build into local CMake registry. +# Install method #2: Export the Ceres build directory. # -# Optionally export the Ceres build directory into the local CMake package -# registry (~/.cmake/packages on *nix & OS X). This allows the detection & -# use of Ceres without requiring that it be installed. -if (EXPORT_BUILD_DIR) - message("-- Export Ceres build directory to local CMake package registry.") +# The package files are always generated in the build tree. CMake's +# CMAKE_EXPORT_PACKAGE_REGISTRY variable controls whether export(PACKAGE) +# registers the package in the user package registry. +set (Ceres_EXPORT_TARGETS ceres) - # Save the relative path from the build directory to the source directory. - file(RELATIVE_PATH INSTALL_ROOT_REL_CONFIG_INSTALL_DIR - ${Ceres_BINARY_DIR} - ${Ceres_SOURCE_DIR}) +if (TARGET ceres_cuda_kernels) + # The target ceres depends on ceres_cuda_kernels requiring the latter to be + # exported as part of the same export set. + list (APPEND Ceres_EXPORT_TARGETS ceres_cuda_kernels) +endif (TARGET ceres_cuda_kernels) - set (Ceres_EXPORT_TARGETS ceres) +export(TARGETS ${Ceres_EXPORT_TARGETS} + NAMESPACE Ceres:: + FILE ${Ceres_BINARY_DIR}/CeresTargets.cmake) +export(PACKAGE ${CMAKE_PROJECT_NAME}) - if (TARGET ceres_cuda_kernels) - # The target ceres depends on ceres_cuda_kernels requiring the latter to be - # exported as part of the same export set. - list (APPEND Ceres_EXPORT_TARGETS ceres_cuda_kernels) - endif (TARGET ceres_cuda_kernels) +unset (Ceres_EXPORT_TARGETS) - # Analogously to install(EXPORT ...), export the Ceres target from the build - # directory as a package called Ceres into the local CMake package registry. - export(TARGETS ${Ceres_EXPORT_TARGETS} - NAMESPACE Ceres:: - FILE ${Ceres_BINARY_DIR}/CeresTargets.cmake) - export(PACKAGE ${CMAKE_PROJECT_NAME}) +add_feature_info("SuiteSparse solvers" _Ceres_FEATURE_SUITESPARSE + "Sparse solvers from SuiteSparse") +add_feature_info("CUDA solvers" _Ceres_FEATURE_CUDA + "CUDA-accelerated linear solvers") +add_feature_info("cuDSS solver" _Ceres_FEATURE_CUDSS + "Use the cuDSS sparse linear solver when available") +add_feature_info("Accelerate sparse solvers" _Ceres_FEATURE_ACCELERATE + "Sparse solvers from Apple's Accelerate framework") +add_feature_info("Eigen sparse solvers" _Ceres_FEATURE_EIGEN_SPARSE + "Sparse linear solvers using Eigen") +add_feature_info("Eigen METIS ordering" _Ceres_FEATURE_EIGEN_METIS + "METIS ordering for Eigen sparse solvers") +add_feature_info("CHOLMOD partitioning" _Ceres_FEATURE_CHOLMOD_PARTITION + "CHOLMOD graph partitioning") +add_feature_info("CHOLMOD single precision" WITH_CHOLMOD_FLOAT + "Single-precision CHOLMOD factorization") +add_feature_info("Use LAPACK" _Ceres_FEATURE_LAPACK + "Use LAPACK directly for linear algebra operations") +add_feature_info("Custom BLAS" WITH_CUSTOM_BLAS + "Use Ceres hand-written BLAS routines") +add_feature_info("Schur specializations" WITH_SCHUR_SPECIALIZATIONS + "Fixed-size Schur solver specializations") +add_feature_info("Shared library" BUILD_SHARED_LIBS + "Build Ceres as a shared library instead of a static library") +add_feature_info("Documentation" _Ceres_FEATURE_DOCUMENTATION + "Build the Ceres HTML documentation with Sphinx") +add_feature_info("Examples" BUILD_EXAMPLES "Build Ceres example programs") +add_feature_info("Benchmarks" _Ceres_FEATURE_BENCHMARKS + "Build Ceres microbenchmark programs") +add_feature_info("Tests" BUILD_TESTING "Build Ceres unit and integration tests") - unset (Ceres_EXPORT_TARGETS) +function(ceres_dependency_description OUTPUT DESCRIPTION VERSION) + if (VERSION) + set(DESCRIPTION "${DESCRIPTION} (version ${VERSION})") + endif() + set(${OUTPUT} "${DESCRIPTION}" PARENT_SCOPE) +endfunction() - # Configure a CeresConfig.cmake file for the export of the Ceres build - # directory from the template, reflecting the current build options. - set(SETUP_CERES_CONFIG_FOR_INSTALLATION FALSE) - configure_file("${Ceres_SOURCE_DIR}/cmake/CeresConfig.cmake.in" - "${Ceres_BINARY_DIR}/CeresConfig.cmake" @ONLY) +set(CERES_CUDA_VERSION "${CUDA_VERSION}") +if (CUDAToolkit_VERSION) + set(CERES_CUDA_VERSION "${CUDAToolkit_VERSION}") +endif() -endif (EXPORT_BUILD_DIR) +ceres_dependency_description(CERES_ABSL_DESCRIPTION + "C++ common libraries from Google" "${absl_VERSION}") +ceres_dependency_description(CERES_BENCHMARK_DESCRIPTION + "A microbenchmark support library" "${benchmark_VERSION}") +ceres_dependency_description(CERES_CUDA_DESCRIPTION + "NVIDIA CUDA toolkit" "${CERES_CUDA_VERSION}") +ceres_dependency_description(CERES_CUDSS_DESCRIPTION + "NVIDIA CUDA sparse solver library" "${cudss_VERSION}") +ceres_dependency_description(CERES_EIGEN3_DESCRIPTION + "C++ template library for linear algebra" "${Eigen3_VERSION}") +ceres_dependency_description(CERES_GTEST_DESCRIPTION + "Google's C++ testing framework" "${GOOGLETEST_VERSION}") +ceres_dependency_description(CERES_METIS_DESCRIPTION + "Serial graph partitioning and fill-reducing matrix ordering" + "${METIS_VERSION}") +ceres_dependency_description(CERES_PYTHON_DESCRIPTION + "Python interpreter" "${Python_VERSION}") +ceres_dependency_description(CERES_SPHINX_DESCRIPTION + "Documentation generator" "${Sphinx_VERSION}") +ceres_dependency_description(CERES_SUITESPARSE_DESCRIPTION + "A suite of sparse matrix algorithms" "${SuiteSparse_VERSION}") +ceres_dependency_description(CERES_TBB_DESCRIPTION + "Threading Building Blocks" "${TBB_VERSION}") + +set_package_properties(absl PROPERTIES + DESCRIPTION "${CERES_ABSL_DESCRIPTION}" + URL "https://abseil.io") +set_package_properties(AccelerateSparse PROPERTIES + DESCRIPTION "Apple's Accelerate framework with sparse linear solvers") +set_package_properties(benchmark PROPERTIES + DESCRIPTION "${CERES_BENCHMARK_DESCRIPTION}" + URL "https://github.com/google/benchmark" + PURPOSE "Benchmarking of Ceres") +set_package_properties(BLAS PROPERTIES + DESCRIPTION "Basic Linear Algebra Subprograms implementation" + PURPOSE "LAPACK dependency") +set_package_properties(CUDA PROPERTIES + DESCRIPTION "${CERES_CUDA_DESCRIPTION}" + URL "https://developer.nvidia.com/cuda-toolkit" + PURPOSE "CUDA solver acceleration") +set_package_properties(CUDAToolkit PROPERTIES + DESCRIPTION "${CERES_CUDA_DESCRIPTION}" + URL "https://developer.nvidia.com/cuda-toolkit" + PURPOSE "CUDA solver acceleration") +set_package_properties(cudss PROPERTIES + DESCRIPTION "${CERES_CUDSS_DESCRIPTION}" + URL "https://developer.nvidia.com/cudss" + PURPOSE "CUDA sparse solver support") +set_package_properties(Eigen3 PROPERTIES + DESCRIPTION "${CERES_EIGEN3_DESCRIPTION}" + TYPE REQUIRED + URL "https://eigen.tuxfamily.org") +set_package_properties(GTest PROPERTIES + DESCRIPTION "${CERES_GTEST_DESCRIPTION}" + URL "https://github.com/google/googletest" + PURPOSE "Ceres unit tests") +set_package_properties(LAPACK PROPERTIES + DESCRIPTION "Linear Algebra PACKage implementation" + TYPE RECOMMENDED) +set_package_properties(METIS PROPERTIES + DESCRIPTION "${CERES_METIS_DESCRIPTION}" + TYPE RECOMMENDED) +set_package_properties(Python PROPERTIES + DESCRIPTION "${CERES_PYTHON_DESCRIPTION}" + URL "https://www.python.org" + PURPOSE "Sphinx documentation generation") +set_package_properties(Sphinx PROPERTIES + DESCRIPTION "${CERES_SPHINX_DESCRIPTION}" + URL "https://www.sphinx-doc.org" + PURPOSE "Building the Ceres documentation") +set_package_properties(SuiteSparse PROPERTIES + DESCRIPTION "${CERES_SUITESPARSE_DESCRIPTION}" + TYPE RECOMMENDED + URL "https://github.com/DrTimothyAldenDavis/SuiteSparse") +set_package_properties(TBB PROPERTIES + DESCRIPTION "${CERES_TBB_DESCRIPTION}" + PURPOSE "SuiteSparseQR dependency") +set_package_properties(Threads PROPERTIES + DESCRIPTION "Native platform threading support" + PURPOSE "Multithreading support") + +foreach(CERES_PACKAGE_PROPERTY IN ITEMS PACKAGES_FOUND PACKAGES_NOT_FOUND) + # Keep package summary output unique and alphabetically ordered. + get_property(CERES_PACKAGE_NAMES GLOBAL PROPERTY ${CERES_PACKAGE_PROPERTY}) + list(REMOVE_DUPLICATES CERES_PACKAGE_NAMES) + list(SORT CERES_PACKAGE_NAMES COMPARE STRING CASE INSENSITIVE) + set_property(GLOBAL PROPERTY ${CERES_PACKAGE_PROPERTY} "${CERES_PACKAGE_NAMES}") +endforeach() + +feature_summary(WHAT + REQUIRED_PACKAGES_FOUND + RECOMMENDED_PACKAGES_FOUND + OPTIONAL_PACKAGES_FOUND + REQUIRED_PACKAGES_NOT_FOUND + RECOMMENDED_PACKAGES_NOT_FOUND + OPTIONAL_PACKAGES_NOT_FOUND + ENABLED_FEATURES + DISABLED_FEATURES + DESCRIPTION "Ceres ${CERES_VERSION} configuration summary")
diff --git a/cmake/AddGerritCommitHook.cmake b/cmake/AddGerritCommitHook.cmake index 070158c..49c4147 100644 --- a/cmake/AddGerritCommitHook.cmake +++ b/cmake/AddGerritCommitHook.cmake
@@ -66,7 +66,8 @@ if (EXISTS ${LOCAL_GIT_DIRECTORY}) if (NOT EXISTS ${LOCAL_GIT_DIRECTORY}/hooks/commit-msg) - message(STATUS "Detected Ceres being used as a git submodule, adding " + # Hook installation is an internal setup detail. + message(DEBUG "Detected Ceres being used as a git submodule, adding " "commit hook for Gerrit to: ${LOCAL_GIT_DIRECTORY}") # Download the hook only if it is not already present. file(DOWNLOAD https://ceres-solver-review.googlesource.com/tools/hooks/commit-msg
diff --git a/cmake/CeresConfig.cmake.in b/cmake/CeresConfig.cmake.in index 1d0b012..cfc866c 100644 --- a/cmake/CeresConfig.cmake.in +++ b/cmake/CeresConfig.cmake.in
@@ -1,5 +1,5 @@ # Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2025 Google Inc. All rights reserved. +# Copyright 2026 Google Inc. All rights reserved. # http://ceres-solver.org/ # # Redistribution and use in source and binary forms, with or without @@ -60,12 +60,14 @@ # CMake target already includes the definition of its public # include directories. +@PACKAGE_INIT@ + include(CMakeFindDependencyMacro) -# Called if we failed to find Ceres or any of its required dependencies, -# unsets all public (designed to be used externally) variables and reports -# error message at priority depending upon [REQUIRED/QUIET/<NONE>] argument. -macro(CERES_REPORT_NOT_FOUND REASON_MSG) +# Called if Ceres or one of its required dependencies cannot be used. Clears +# the public result variables and provides the standard find_package failure +# reason. find_package controls whether and how the reason is reported. +macro(ceres_set_not_found REASON_MSG) # FindPackage() only references Ceres_FOUND, and requires it to be # explicitly set FALSE to denote not found (not merely undefined). set(Ceres_FOUND FALSE) @@ -76,102 +78,38 @@ # Reset the CMake module path to its state when this script was called. set(CMAKE_MODULE_PATH ${CALLERS_CMAKE_MODULE_PATH}) - # Concatenate REASON_MSG and ARGN with each element separated by a space - string(JOIN " " ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "${REASON_MSG}" ${ARGN}) + string(JOIN " " Ceres_NOT_FOUND_MESSAGE "${REASON_MSG}" ${ARGN}) return() -endmacro(CERES_REPORT_NOT_FOUND) - - -# ceres_message([mode] "message text") -# -# Wraps the standard cmake 'message' command, but suppresses output -# if the QUIET flag was passed to the find_package(Ceres ...) call. -function(ceres_message) - if (NOT Ceres_FIND_QUIETLY) - message(${ARGN}) - endif() -endfunction() - - -# ceres_pretty_print_cmake_list( OUTPUT_VAR [item1 [item2 ... ]] ) -# -# Sets ${OUTPUT_VAR} in the caller's scope to a human-readable string -# representation of the list passed as the remaining arguments formed -# as: "[item1, item2, ..., itemN]". -function(ceres_pretty_print_cmake_list OUTPUT_VAR) - string(REPLACE ";" ", " PRETTY_LIST_STRING "[${ARGN}]") - set(${OUTPUT_VAR} "${PRETTY_LIST_STRING}" PARENT_SCOPE) -endfunction() +endmacro(ceres_set_not_found) # The list of (optional) components this version of Ceres was compiled with. set(CERES_COMPILED_COMPONENTS "@CERES_COMPILED_COMPONENTS@") -# If Ceres was not installed, then by definition it was exported -# from a build directory. -set(CERES_WAS_INSTALLED @SETUP_CERES_CONFIG_FOR_INSTALLATION@) - # Record the state of the CMake module path when this script was # called so that we can ensure that we leave it in the same state on # exit as it was on entry, but modify it locally. set(CALLERS_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}) -# Get the (current, i.e. installed) directory containing this file. +# Get the directory containing this file. get_filename_component(CERES_CURRENT_CONFIG_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -if (CERES_WAS_INSTALLED) - # Update the CMake module path to include the installation directory of this - # script, thus we will use the FindPackage() scripts shipped with Ceres to - # find Ceres' dependencies, even if the user has equivalently named - # FindPackage() scripts in their project. - list(PREPEND CMAKE_MODULE_PATH ${CERES_CURRENT_CONFIG_DIR}) - - # Build the absolute root install directory as a relative path - # (determined when Ceres was configured & built) from the current - # install directory for this this file. This allows for the install - # tree to be relocated, after Ceres was built, outside of CMake. - get_filename_component(CURRENT_ROOT_INSTALL_DIR - "${CERES_CURRENT_CONFIG_DIR}/@INSTALL_ROOT_REL_CONFIG_INSTALL_DIR@" - ABSOLUTE) - if (NOT EXISTS ${CURRENT_ROOT_INSTALL_DIR}) - ceres_report_not_found( - "Ceres install root: ${CURRENT_ROOT_INSTALL_DIR}, " - "determined from relative path from CeresConfig.cmake install location: " - "${CERES_CURRENT_CONFIG_DIR}, does not exist. Either the install " - "directory was deleted, or the install tree was only partially relocated " - "outside of CMake after Ceres was built.") - endif (NOT EXISTS ${CURRENT_ROOT_INSTALL_DIR}) - -else(CERES_WAS_INSTALLED) - # Ceres was exported from the build tree. - set(CERES_EXPORTED_BUILD_DIR ${CERES_CURRENT_CONFIG_DIR}) - get_filename_component(CERES_EXPORTED_SOURCE_DIR - "${CERES_EXPORTED_BUILD_DIR}/@INSTALL_ROOT_REL_CONFIG_INSTALL_DIR@" - ABSOLUTE) - if (NOT EXISTS ${CERES_EXPORTED_SOURCE_DIR}) - ceres_report_not_found( - "Ceres exported source directory: ${CERES_EXPORTED_SOURCE_DIR}, " - "determined from relative path from CeresConfig.cmake exported build " - "directory: ${CERES_EXPORTED_BUILD_DIR} does not exist.") - endif() - - # Update the CMake module path to include the cmake directory in the Ceres - # source tree which was exported, thus we will use the FindPackage() scripts - # shipped with Ceres to find Ceres' dependencies, even if the user has - # equivalently named FindPackage() scripts in their project. - list(PREPEND CMAKE_MODULE_PATH ${CERES_EXPORTED_SOURCE_DIR}/cmake) -endif(CERES_WAS_INSTALLED) +# Use dependency find modules available relative to this package config. +list(PREPEND CMAKE_MODULE_PATH + ${CERES_CURRENT_CONFIG_DIR}/CeresFindModules + ${CERES_CURRENT_CONFIG_DIR}) # Set the version. set(CERES_VERSION @CERES_VERSION@) -include(CMakeFindDependencyMacro) +# Required dependencies +find_dependency(Threads) + # Optional dependencies @METIS_DEPENDENCY@ @SuiteSparse_DEPENDENCY@ @CUDAToolkit_DEPENDENCY@ @cudss_DEPENDENCY@ -@Threads_DEPENDENCY@ # As imported CMake targets are not re-exported when a dependent target is # exported, we must invoke find_package(XXX) here to reload the definition @@ -187,27 +125,25 @@ find_dependency(absl ${_CERES_ABSL_VERSION}) if (NOT absl_VERSION) if (NOT TARGET absl::absl_vlog_is_on) - ceres_report_not_found("The version of abseil installed on the system " + ceres_set_not_found("The version of abseil installed on the system " "provides no version info and is missing TARGET 'absl::absl_vlog_is_on'") endif () endif () else () # absl from submodule, configs installed with ceres-solver - get_filename_component(_ABSL_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) - get_filename_component(_ABSL_IMPORT_PREFIX "${_ABSL_IMPORT_PREFIX}" PATH) - set(_ABSL_IMPORT_PREFIX "${_ABSL_IMPORT_PREFIX}/absl") - find_dependency(absl ${_CERES_ABSL_VERSION} HINTS "${_ABSL_IMPORT_PREFIX}") + set(_CERES_ABSL_HINTS + "${CERES_CURRENT_CONFIG_DIR}/third_party/abseil-cpp" + "${CERES_CURRENT_CONFIG_DIR}/../absl") + find_dependency(absl ${_CERES_ABSL_VERSION} HINTS ${_CERES_ABSL_HINTS}) + unset(_CERES_ABSL_HINTS) endif () -ceres_message(STATUS "Found required Ceres dependency: " - "absl version ${_CERES_ABSL_VERSION} in ${absl_DIR}") unset(_CERES_ABSL_VERSION) unset(_CERES_USE_SYSTEM_ABSL) # Eigen. # Flag set during configuration and build of Ceres. set(CERES_EIGEN_VERSION @Eigen3_VERSION@) -# Search quietly to control the timing of the error message if not found. The -# search should be for an exact match, but for usability reasons do a soft +# The search should be for an exact match, but for usability reasons do a soft # match and reject with an explanation below. find_package(Eigen3 ${CERES_EIGEN_VERSION} QUIET) if (Eigen3_FOUND) @@ -215,17 +151,15 @@ # CMake's VERSION check in FIND_PACKAGE() will accept any version >= the # specified version. However, only version = is supported. Improve # usability by explaining why we don't accept non-exact version matching. - ceres_report_not_found("Found Eigen dependency, but the version of Eigen " + ceres_set_not_found("Found Eigen dependency, but the version of Eigen " "found (${Eigen3_VERSION}) does not exactly match the version of Eigen " "Ceres was compiled with (${CERES_EIGEN_VERSION}). This can cause subtle " "bugs by triggering violations of the One Definition Rule. See the " "Wikipedia article http://en.wikipedia.org/wiki/One_Definition_Rule " "for more details") endif () - ceres_message(STATUS "Found required Ceres dependency: " - "Eigen version ${CERES_EIGEN_VERSION} in ${Eigen3_DIR}") else (Eigen3_FOUND) - ceres_report_not_found("Missing required Ceres " + ceres_set_not_found("Missing required Ceres " "dependency: Eigen version ${CERES_EIGEN_VERSION}, please set " "Eigen3_DIR.") endif (Eigen3_FOUND) @@ -240,59 +174,54 @@ # Reset CMake module path to its state when this script was called. set(CMAKE_MODULE_PATH ${CALLERS_CMAKE_MODULE_PATH}) -# Build the detected Ceres version string to correctly capture whether it -# was installed, or exported. -ceres_pretty_print_cmake_list(CERES_COMPILED_COMPONENTS_STRING - ${CERES_COMPILED_COMPONENTS}) -if (CERES_WAS_INSTALLED) - set(CERES_DETECTED_VERSION_STRING "Ceres version: ${CERES_VERSION} " - "installed in: ${CURRENT_ROOT_INSTALL_DIR} with components: " - "${CERES_COMPILED_COMPONENTS_STRING}") -else (CERES_WAS_INSTALLED) - set(CERES_DETECTED_VERSION_STRING "Ceres version: ${CERES_VERSION} " - "exported from build directory: ${CERES_EXPORTED_BUILD_DIR} with " - "components: ${CERES_COMPILED_COMPONENTS_STRING}") -endif() +# Advertise compiled components and let CMake apply required and optional +# component semantics. +foreach(_Ceres_component IN LISTS Ceres_FIND_COMPONENTS) + set(Ceres_${_Ceres_component}_FOUND FALSE) +endforeach() +foreach(_Ceres_component IN LISTS CERES_COMPILED_COMPONENTS) + set(Ceres_${_Ceres_component}_FOUND TRUE) +endforeach() -# If the user called this script through find_package() whilst specifying -# particular Ceres components that should be found via: -# find_package(Ceres COMPONENTS XXX YYY), check the requested components against -# those with which Ceres was compiled. In this case, we should only report -# Ceres as found if all the requested components have been found. -if (Ceres_FIND_COMPONENTS) - foreach (REQUESTED_COMPONENT ${Ceres_FIND_COMPONENTS}) - list(FIND CERES_COMPILED_COMPONENTS ${REQUESTED_COMPONENT} HAVE_REQUESTED_COMPONENT) - # list(FIND ..) returns -1 if the element was not in the list, but CMake - # interprets if (VAR) to be true if VAR is any non-zero number, even - # negative ones, hence we have to explicitly check for >= 0. - if (HAVE_REQUESTED_COMPONENT EQUAL -1) - # Check for the presence of all requested components before reporting - # not found, such that we report all of the missing components rather - # than just the first. - list(APPEND MISSING_CERES_COMPONENTS ${REQUESTED_COMPONENT}) - endif() - endforeach() - if (MISSING_CERES_COMPONENTS) - ceres_pretty_print_cmake_list(REQUESTED_CERES_COMPONENTS_STRING - ${Ceres_FIND_COMPONENTS}) - ceres_pretty_print_cmake_list(MISSING_CERES_COMPONENTS_STRING - ${MISSING_CERES_COMPONENTS}) - ceres_report_not_found("Missing requested Ceres components: " - "${MISSING_CERES_COMPONENTS_STRING} (components requested: " - "${REQUESTED_CERES_COMPONENTS_STRING}). Detected " - "${CERES_DETECTED_VERSION_STRING}.") +set(_Ceres_missing_required_components) +foreach(_Ceres_component IN LISTS Ceres_FIND_COMPONENTS) + if (Ceres_FIND_REQUIRED_${_Ceres_component} + AND NOT Ceres_${_Ceres_component}_FOUND) + list(APPEND _Ceres_missing_required_components ${_Ceres_component}) + endif() +endforeach() +unset(_Ceres_component) + +set(Ceres_FOUND TRUE) +check_required_components(Ceres) +if (NOT Ceres_FOUND) + list(LENGTH _Ceres_missing_required_components + _Ceres_missing_required_component_count) + if (_Ceres_missing_required_component_count EQUAL 1) + set(_Ceres_component_noun "component") + else() + set(_Ceres_component_noun "components") + endif() + string(JOIN ", " _Ceres_missing_required_components_string + ${_Ceres_missing_required_components}) + string(CONCAT _Ceres_missing_required_components_reason + "Missing required Ceres ${_Ceres_component_noun}: " + "${_Ceres_missing_required_components_string}.") + if (CERES_COMPILED_COMPONENTS) + string(JOIN ", " CERES_COMPILED_COMPONENTS_STRING + ${CERES_COMPILED_COMPONENTS}) + ceres_set_not_found( + "${_Ceres_missing_required_components_reason}" + "Available components: ${CERES_COMPILED_COMPONENTS_STRING}.") + else() + ceres_set_not_found( + "${_Ceres_missing_required_components_reason}" + "No Ceres components are available.") endif() endif() -# As we use CERES_REPORT_NOT_FOUND() to abort, if we reach this point we have -# found Ceres and all required dependencies. -ceres_message(STATUS "Found " ${CERES_DETECTED_VERSION_STRING}) - -# Set CERES_FOUND to be equivalent to Ceres_FOUND, which is set to -# TRUE by FindPackage() if this file is found and run, and after which -# Ceres_FOUND is not (explicitly, i.e. undefined does not count) set -# to FALSE. -set(CERES_FOUND TRUE) +# Preserve the legacy uppercase result variable. +set(CERES_FOUND ${Ceres_FOUND}) if (NOT TARGET ceres) # For backwards compatibility, create a local 'alias' target with the
diff --git a/cmake/CreateCeresConfig.cmake b/cmake/CreateCeresConfig.cmake index f0037cc..565e680 100644 --- a/cmake/CreateCeresConfig.cmake +++ b/cmake/CreateCeresConfig.cmake
@@ -56,9 +56,9 @@ # will be <src>/include/ceres/internal. function(CREATE_CERES_CONFIG CURRENT_CERES_COMPILE_OPTIONS CERES_CONFIG_OUTPUT_DIRECTORY) - # Create the specified output directory if it does not exist. + # Creating the generated directory is an internal setup detail. if (NOT EXISTS "${CERES_CONFIG_OUTPUT_DIRECTORY}") - message(STATUS "Creating configured Ceres config.h output directory: " + message(DEBUG "Creating configured Ceres config.h output directory: " "${CERES_CONFIG_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${CERES_CONFIG_OUTPUT_DIRECTORY}") endif() @@ -89,7 +89,6 @@ # interprets if (VAR) to be true if VAR is any non-zero number, even # negative ones, hence we have to explicitly check for >= 0. if (OPTION_ENABLED GREATER -1) - message(STATUS "Enabling ${CERES_OPTION} in Ceres config.h") set(${CERES_OPTION} "#define ${CERES_OPTION}") # Remove the item from the list of current options so that we can identify
diff --git a/cmake/EnableSanitizer.cmake b/cmake/EnableSanitizer.cmake index 9a8d484..8e7031f 100644 --- a/cmake/EnableSanitizer.cmake +++ b/cmake/EnableSanitizer.cmake
@@ -85,7 +85,7 @@ message(FATAL_ERROR "Specified sanitizer: ${REQUESTED_SANITIZER} is not " "supported by the compiler.") endif() - message(STATUS "Enabling sanitizer: ${REQUESTED_SANITIZER}") + message(DEBUG "Enabling sanitizer: ${REQUESTED_SANITIZER}") set(ADDED_SANITIZER TRUE) # As per the Clang documentation, the sanitizer flags must be added to both # the compiler and linker flags.
diff --git a/cmake/FindAccelerateSparse.cmake b/cmake/FindAccelerateSparse.cmake index 3a2e431..59da0ee 100644 --- a/cmake/FindAccelerateSparse.cmake +++ b/cmake/FindAccelerateSparse.cmake
@@ -1,5 +1,5 @@ # Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2023 Google Inc. All rights reserved. +# Copyright 2026 Google Inc. All rights reserved. # http://ceres-solver.org/ # # Redistribution and use in source and binary forms, with or without @@ -34,20 +34,20 @@ # Note that this is distinct from the Accelerate # framework on its own, which existed in previous # versions but without the sparse solvers. +# Apple does not provide a CMake package configuration for this API. This +# module verifies the headers, framework library, and sparse solver symbols. # -# This module defines the following variables which should be referenced -# by the caller to use the library. +# This module defines the following variables. # # AccelerateSparse_FOUND: TRUE iff an Accelerate framework including the sparse # solvers, and all dependencies, has been found. -# AccelerateSparse_INCLUDE_DIRS: Include directories for Accelerate framework. -# AccelerateSparse_LIBRARIES: Libraries for Accelerate framework and all -# dependencies. # -# The following variables are also defined by this module, but in line with -# CMake recommended FindPackage() module style should NOT be referenced directly -# by callers (use the plural variables detailed above instead). These variables -# do however affect the behaviour of the module via FIND_[PATH/LIBRARY]() which +# The following imported target is also defined when the package is found: +# +# AccelerateSparse::Accelerate: Accelerate framework with sparse solvers. +# +# The following variables affect the behavior of the module via +# FIND_[PATH/LIBRARY]() which # are NOT re-called (i.e. search for library is not repeated) if these variables # are set with valid values _in the CMake cache_. This means that if these # variables are set directly in the cache, either by the user in the CMake GUI, @@ -62,31 +62,24 @@ # any dependencies. # Called if we failed to find the Accelerate framework with the sparse solvers. -# Unsets all public (designed to be used externally) variables and reports -# error message at priority depending upon [REQUIRED/QUIET/<NONE>] argument. +# Lets the standard package helper report the failure. macro(accelerate_sparse_report_not_found REASON_MSG) unset(AccelerateSparse_FOUND) - unset(AccelerateSparse_INCLUDE_DIRS) - unset(AccelerateSparse_LIBRARIES) # Make results of search visible in the CMake GUI if Accelerate has not # been found so that user does not have to toggle to advanced view. mark_as_advanced(CLEAR AccelerateSparse_INCLUDE_DIR AccelerateSparse_LIBRARY) - # Note <package>_FIND_[REQUIRED/QUIETLY] variables defined by FindPackage() - # use the camelcase library name, not uppercase. - if (AccelerateSparse_FIND_QUIETLY) - message(STATUS "Failed to find Accelerate framework with sparse solvers - " - ${REASON_MSG} ${ARGN}) - elseif (AccelerateSparse_FIND_REQUIRED) - message(FATAL_ERROR "Failed to find Accelerate framework with sparse solvers - " - ${REASON_MSG} ${ARGN}) + if (ARGN) + string(JOIN " " ACCELERATE_SPARSE_FAILURE_REASON + "${REASON_MSG}" ${ARGN}) else() - # Neither QUIETLY nor REQUIRED, use no priority which emits a message - # but continues configuration and allows generation. - message("-- Failed to find Accelerate framework with sparse solvers - " - ${REASON_MSG} ${ARGN}) + set(ACCELERATE_SPARSE_FAILURE_REASON "${REASON_MSG}") endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(AccelerateSparse + REQUIRED_VARS AccelerateSparse_INCLUDE_DIR AccelerateSparse_LIBRARY + REASON_FAILURE_MESSAGE "${ACCELERATE_SPARSE_FAILURE_REASON}") return() endmacro() @@ -96,16 +89,14 @@ if (NOT AccelerateSparse_INCLUDE_DIR OR NOT EXISTS ${AccelerateSparse_INCLUDE_DIR}) accelerate_sparse_report_not_found( - "Could not find Accelerate framework headers. Set " - "AccelerateSparse_INCLUDE_DIR to the directory containing Accelerate.h") + "Could not find AccelerateSparse because the Accelerate framework headers were not found") endif() find_library(AccelerateSparse_LIBRARY NAMES Accelerate) if (NOT AccelerateSparse_LIBRARY OR NOT EXISTS ${AccelerateSparse_LIBRARY}) accelerate_sparse_report_not_found( - "Could not find Accelerate framework. Set AccelerateSparse_LIBRARY " - "to the Accelerate.framework directory") + "Could not find AccelerateSparse because the Accelerate framework was not found. Set AccelerateSparse_LIBRARY to the Accelerate.framework directory") endif() set(AccelerateSparse_FOUND TRUE) @@ -126,20 +117,20 @@ unset(CMAKE_REQUIRED_LIBRARIES) if (NOT ACCELERATE_FRAMEWORK_HAS_SPARSE_SOLVER) accelerate_sparse_report_not_found( - "Detected Accelerate framework: ${AccelerateSparse_LIBRARY} does not " - "include the sparse solvers.") -endif() - -if (AccelerateSparse_FOUND) - set(AccelerateSparse_INCLUDE_DIRS ${AccelerateSparse_INCLUDE_DIR}) - set(AccelerateSparse_LIBRARIES ${AccelerateSparse_LIBRARY}) + "Could not find AccelerateSparse because the Accelerate framework ${AccelerateSparse_LIBRARY} does not include the sparse solvers") endif() # Handle REQUIRED / QUIET optional arguments and version. include(FindPackageHandleStandardArgs) find_package_handle_standard_args(AccelerateSparse - REQUIRED_VARS AccelerateSparse_INCLUDE_DIRS AccelerateSparse_LIBRARIES) + REQUIRED_VARS AccelerateSparse_INCLUDE_DIR AccelerateSparse_LIBRARY) if (AccelerateSparse_FOUND) + if (NOT TARGET AccelerateSparse::Accelerate) + add_library(AccelerateSparse::Accelerate UNKNOWN IMPORTED) + set_target_properties(AccelerateSparse::Accelerate PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${AccelerateSparse_INCLUDE_DIR}" + IMPORTED_LOCATION "${AccelerateSparse_LIBRARY}") + endif() mark_as_advanced(FORCE AccelerateSparse_INCLUDE_DIR AccelerateSparse_LIBRARY) endif()
diff --git a/cmake/FindSuiteSparse.cmake b/cmake/FindSuiteSparse.cmake index d3936bc..aa4086b 100644 --- a/cmake/FindSuiteSparse.cmake +++ b/cmake/FindSuiteSparse.cmake
@@ -1,5 +1,5 @@ # Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2023 Google Inc. All rights reserved. +# Copyright 2026 Google Inc. All rights reserved. # http://ceres-solver.org/ # # Redistribution and use in source and binary forms, with or without @@ -35,6 +35,10 @@ Module for locating SuiteSparse libraries and its dependencies. +SuiteSparse 7 and later provide a unified CMake package configuration. This +module retains a component-based fallback for older supported releases such as +SuiteSparse 5.10.1 in Ubuntu 22.04. + This module defines the following variables: ``SuiteSparse_FOUND`` @@ -93,14 +97,30 @@ Serial Graph Partitioning and Fill-reducing Matrix Ordering (METIS) ]=======================================================================] +# Keep native package-config diagnostics deterministic. +if (SuiteSparse_FIND_COMPONENTS) + list(SORT SuiteSparse_FIND_COMPONENTS COMPARE STRING CASE INSENSITIVE) +endif () + if (NOT SuiteSparse_NO_CMAKE) find_package (SuiteSparse NO_MODULE QUIET) + if (SuiteSparse_FOUND) + # Report the main include directory instead of the package configuration + # file path in FindPackageHandleStandardArgs' standard success message. + get_target_property(SuiteSparse_INCLUDE_DIR SuiteSparse::Config + INTERFACE_INCLUDE_DIRECTORIES) + if (SuiteSparse_INCLUDE_DIR) + list(GET SuiteSparse_INCLUDE_DIR -1 SuiteSparse_INCLUDE_DIR) + endif () + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(SuiteSparse + REQUIRED_VARS SuiteSparse_INCLUDE_DIR + VERSION_VAR SuiteSparse_VERSION + HANDLE_COMPONENTS) + return () + endif (SuiteSparse_FOUND) endif (NOT SuiteSparse_NO_CMAKE) -if (SuiteSparse_FOUND) - return () -endif (SuiteSparse_FOUND) - # Push CMP0057 to enable support for IN_LIST, when cmake_minimum_required is # set to <3.3. cmake_policy (PUSH) @@ -121,10 +141,28 @@ endforeach (component IN LISTS SuiteSparse_FIND_COMPONENTS) endif (NOT SuiteSparse_FIND_COMPONENTS) -# Assume SuiteSparse was found and set it to false only if third-party -# dependencies could not be located. SuiteSparse components are handled by -# FindPackageHandleStandardArgs HANDLE_COMPONENTS option. +# Assume SuiteSparse was found and set it to false when a component or a +# third-party dependency could not be located. SuiteSparse component failures +# are reported by FindPackageHandleStandardArgs HANDLE_COMPONENTS. set (SuiteSparse_FOUND TRUE) +set (CMAKE_FIND_PACKAGE_REASON) + +# Keep nested dependency failures available for SuiteSparse's final reason. +macro (suitesparse_find_dependency DEPENDENCY) + find_package(${DEPENDENCY} ${ARGN} QUIET) + if (NOT ${DEPENDENCY}_FOUND) + set (SuiteSparse_${DEPENDENCY}_REASON + "${DEPENDENCY}: Could not find ${DEPENDENCY}.") + if (${DEPENDENCY}_NOT_FOUND_MESSAGE) + set (SuiteSparse_${DEPENDENCY}_REASON + "${DEPENDENCY}: ${${DEPENDENCY}_NOT_FOUND_MESSAGE}") + endif () + endif () +endmacro (suitesparse_find_dependency) + +# SuiteSparseQR optionally depends on TBB. Find it here so that it is treated +# as a SuiteSparse dependency rather than as a Ceres dependency. +suitesparse_find_dependency(TBB NO_MODULE) include (CheckLibraryExists) include (CheckSymbolExists) @@ -152,6 +190,7 @@ # Do not list components multiple times. list (REMOVE_DUPLICATES SuiteSparse_FIND_COMPONENTS) +list (SORT SuiteSparse_FIND_COMPONENTS COMPARE STRING CASE INSENSITIVE) # Reset CALLERS_CMAKE_FIND_LIBRARY_PREFIXES to its value when # FindSuiteSparse was invoked. @@ -161,12 +200,12 @@ endif (MSVC) endmacro(SuiteSparse_RESET_FIND_LIBRARY_PREFIX) -# Called if we failed to find SuiteSparse or any of it's required dependencies, -# unsets all public (designed to be used externally) variables and reports -# error message at priority depending upon [REQUIRED/QUIET/<NONE>] argument. +# Called if we failed to find SuiteSparse or any of its required dependencies. +# The standard package helper reports the failure after all components have +# been checked. macro(SuiteSparse_REPORT_NOT_FOUND REASON_MSG) - # Will be set to FALSE by find_package_handle_standard_args - unset (SuiteSparse_FOUND) + set (SuiteSparse_FOUND FALSE) + list (APPEND CMAKE_FIND_PACKAGE_REASON "${REASON_MSG}") # Do NOT unset SuiteSparse_REQUIRED_VARS here, as it is used by # FindPackageHandleStandardArgs() to generate the automatic error message on @@ -174,21 +213,7 @@ suitesparse_reset_find_library_prefix() - # Note <package>_FIND_[REQUIRED/QUIETLY] variables defined by FindPackage() - # use the camelcase library name, not uppercase. - if (SuiteSparse_FIND_QUIETLY) - message(STATUS "Failed to find SuiteSparse - " ${REASON_MSG} ${ARGN}) - elseif (SuiteSparse_FIND_REQUIRED) - message(FATAL_ERROR "Failed to find SuiteSparse - " ${REASON_MSG} ${ARGN}) - else() - # Neither QUIETLY nor REQUIRED, use no priority which emits a message - # but continues configuration and allows generation. - message("-- Failed to find SuiteSparse - " ${REASON_MSG} ${ARGN}) - endif (SuiteSparse_FIND_QUIETLY) - - # Do not call return(), s/t we keep processing if not called with REQUIRED - # and report all missing components, rather than bailing after failing to find - # the first. + # Do not return so all components can be checked before standard reporting. endmacro(SuiteSparse_REPORT_NOT_FOUND) # Handle possible presence of lib prefix for libraries on MSVC, see @@ -222,18 +247,13 @@ NAMES ${SuiteSparse_FIND_COMPONENT_${COMPONENT}_FILES} PATH_SUFFIXES ${SuiteSparse_CHECK_PATH_SUFFIXES}) if (SuiteSparse_${COMPONENT}_INCLUDE_DIR) - message(STATUS "Found ${COMPONENT} headers in: " - "${SuiteSparse_${COMPONENT}_INCLUDE_DIR}") mark_as_advanced(SuiteSparse_${COMPONENT}_INCLUDE_DIR) else() # Specified headers not found. set(SuiteSparse_${COMPONENT}_FOUND FALSE) if (SuiteSparse_FIND_REQUIRED_${COMPONENT}) - suitesparse_report_not_found( - "Did not find ${COMPONENT} header (required SuiteSparse component).") + set(SuiteSparse_FOUND FALSE) else() - message(STATUS "Did not find ${COMPONENT} header (optional " - "SuiteSparse component).") # Hide optional vars from CMake GUI even if not found. mark_as_advanced(SuiteSparse_${COMPONENT}_INCLUDE_DIR) endif() @@ -245,17 +265,13 @@ NAMES ${SuiteSparse_FIND_COMPONENT_${COMPONENT}_LIBRARIES} PATH_SUFFIXES ${SuiteSparse_CHECK_PATH_SUFFIXES}) if (SuiteSparse_${COMPONENT}_LIBRARY) - message(STATUS "Found ${COMPONENT} library: ${SuiteSparse_${COMPONENT}_LIBRARY}") mark_as_advanced(SuiteSparse_${COMPONENT}_LIBRARY) else () # Specified libraries not found. set(SuiteSparse_${COMPONENT}_FOUND FALSE) if (SuiteSparse_FIND_REQUIRED_${COMPONENT}) - suitesparse_report_not_found( - "Did not find ${COMPONENT} library (required SuiteSparse component).") + set(SuiteSparse_FOUND FALSE) else() - message(STATUS "Did not find ${COMPONENT} library (optional SuiteSparse " - "dependency)") # Hide optional vars from CMake GUI even if not found. mark_as_advanced(SuiteSparse_${COMPONENT}_LIBRARY) endif() @@ -290,18 +306,14 @@ unset(SuiteSparse_REQUIRED_VARS) # BLAS. -find_package(BLAS QUIET) -if (NOT BLAS_FOUND) - suitesparse_report_not_found( - "Did not find BLAS library (required for SuiteSparse).") -endif (NOT BLAS_FOUND) +if (NOT DEFINED BLAS_FOUND) + suitesparse_find_dependency(BLAS) +endif() # LAPACK. -find_package(LAPACK QUIET) -if (NOT LAPACK_FOUND) - suitesparse_report_not_found( - "Did not find LAPACK library (required for SuiteSparse).") -endif (NOT LAPACK_FOUND) +if (NOT DEFINED LAPACK_FOUND) + suitesparse_find_dependency(LAPACK) +endif() foreach (component IN LISTS SuiteSparse_FIND_COMPONENTS) if (component STREQUAL Partition) @@ -325,68 +337,14 @@ LIBRARIES ${component_library}) endforeach (component IN LISTS SuiteSparse_FIND_COMPONENTS) -if (TARGET SuiteSparse::SPQR) - # SuiteSparseQR may be compiled with Intel Threading Building Blocks, - # we assume that if TBB is installed, SuiteSparseQR was compiled with - # support for it, this will do no harm if it wasn't. - find_package(TBB QUIET NO_MODULE) - if (TBB_FOUND) - message(STATUS "Found Intel Thread Building Blocks (TBB) library " - "(${TBB_VERSION}). " - "Assuming SuiteSparseQR was compiled with TBB.") - # Add the TBB libraries to the SuiteSparseQR libraries (the only - # libraries to optionally depend on TBB). - set_property (TARGET SuiteSparse::SPQR APPEND PROPERTY - INTERFACE_LINK_LIBRARIES TBB::tbb) - else (TBB_FOUND) - message(STATUS "Did not find Intel TBB library, assuming SuiteSparseQR was " - "not compiled with TBB.") - endif (TBB_FOUND) -endif (TARGET SuiteSparse::SPQR) - check_library_exists(rt shm_open "" HAVE_LIBRT) if (TARGET SuiteSparse::Config) - # SuiteSparse_config (SuiteSparse version >= 4) requires librt library for - # timing by default when compiled on Linux or Unix, but not on OSX (which - # does not have librt). - if (HAVE_LIBRT) - message(STATUS "Adding librt to " - "SuiteSparse_config libraries (required on Linux & Unix [not OSX] if " - "SuiteSparse is compiled with timing).") - set_property (TARGET SuiteSparse::Config APPEND PROPERTY - INTERFACE_LINK_LIBRARIES $<LINK_ONLY:rt>) - else (HAVE_LIBRT) - message(STATUS "Could not find librt, but found SuiteSparse_config, " - "assuming that SuiteSparse was compiled without timing.") - endif (HAVE_LIBRT) - - # Add BLAS and LAPACK as dependencies of SuiteSparse::Config for convenience - # given that all components depend on it. - if (BLAS_FOUND) - if (TARGET BLAS::BLAS) - set_property (TARGET SuiteSparse::Config APPEND PROPERTY - INTERFACE_LINK_LIBRARIES $<LINK_ONLY:BLAS::BLAS>) - else (TARGET BLAS::BLAS) - set_property (TARGET SuiteSparse::Config APPEND PROPERTY - INTERFACE_LINK_LIBRARIES ${BLAS_LIBRARIES}) - endif (TARGET BLAS::BLAS) - endif (BLAS_FOUND) - - if (LAPACK_FOUND) - if (TARGET LAPACK::LAPACK) - set_property (TARGET SuiteSparse::Config APPEND PROPERTY - INTERFACE_LINK_LIBRARIES $<LINK_ONLY:LAPACK::LAPACK>) - else (TARGET LAPACK::LAPACK) - set_property (TARGET SuiteSparse::Config APPEND PROPERTY - INTERFACE_LINK_LIBRARIES ${LAPACK_LIBRARIES}) - endif (TARGET LAPACK::LAPACK) - endif (LAPACK_FOUND) - # SuiteSparse version >= 4. set(SuiteSparse_VERSION_FILE ${SuiteSparse_Config_INCLUDE_DIR}/SuiteSparse_config.h) if (NOT EXISTS ${SuiteSparse_VERSION_FILE}) + list(APPEND SuiteSparse_REQUIRED_VARS SuiteSparse_VERSION) suitesparse_report_not_found( "Could not find file: ${SuiteSparse_VERSION_FILE} containing version " "information for >= v4 SuiteSparse installs, but SuiteSparse_config was " @@ -423,6 +381,7 @@ unset (SuiteSparse_VERSION_MAJOR) unset (SuiteSparse_VERSION_MINOR) unset (SuiteSparse_VERSION_PATCH) + list(APPEND SuiteSparse_REQUIRED_VARS SuiteSparse_VERSION) endif (SuiteSparse_VERSION MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+") endif (NOT EXISTS ${SuiteSparse_VERSION_FILE}) endif (TARGET SuiteSparse::Config) @@ -436,6 +395,7 @@ else (TARGET SuiteSparse::${component}) # Consider CHOLMOD not found if COLAMD cannot be found set (SuiteSparse_CHOLMOD_FOUND FALSE) + set (SuiteSparse_FOUND FALSE) endif (TARGET SuiteSparse::${component}) endforeach (component IN ITEMS AMD CAMD CCOLAMD COLAMD) endif (TARGET SuiteSparse::CHOLMOD) @@ -447,7 +407,8 @@ INTERFACE_LINK_LIBRARIES SuiteSparse::CHOLMOD) else (TARGET SuiteSparse::CHOLMOD) # Consider SPQR not found if CHOLMOD cannot be found - set (SuiteSparse_SQPR_FOUND FALSE) + set (SuiteSparse_SPQR_FOUND FALSE) + set (SuiteSparse_FOUND FALSE) endif (TARGET SuiteSparse::CHOLMOD) endif (TARGET SuiteSparse::SPQR) @@ -465,13 +426,180 @@ endforeach (component IN LISTS SuiteSparse_FIND_COMPONENTS) endif (TARGET SuiteSparse::Config) +# Check whether the SuiteSparse libraries need their optional dependencies. +# This avoids adding libraries that are available but are not required by the +# installed SuiteSparse build. +function (suitesparse_check_link RESULT SYMBOL) + set (SuiteSparse_LINK_CHECK_SOURCE + "${CMAKE_BINARY_DIR}/CMakeFiles/SuiteSparseLinkCheck.cxx") + file (WRITE "${SuiteSparse_LINK_CHECK_SOURCE}" + "extern \"C\" void ${SYMBOL}(void);\n" + "int main(void) { ${SYMBOL}(); return 0; }\n") + + unset (SuiteSparse_LINK_CHECK_RESULT CACHE) + unset (SuiteSparse_LINK_CHECK_RESULT) + try_compile (SuiteSparse_LINK_CHECK_RESULT + "${CMAKE_BINARY_DIR}/CMakeFiles/SuiteSparseLinkCheck" + "${SuiteSparse_LINK_CHECK_SOURCE}" + LINK_LIBRARIES ${ARGN} + OUTPUT_VARIABLE SuiteSparse_LINK_CHECK_OUTPUT) + set (${RESULT} ${SuiteSparse_LINK_CHECK_RESULT} PARENT_SCOPE) + unset (SuiteSparse_LINK_CHECK_RESULT CACHE) + unset (SuiteSparse_LINK_CHECK_RESULT) +endfunction (suitesparse_check_link) + +set (SuiteSparse_LINK_TARGET) +set (SuiteSparse_LINK_SYMBOL) +if (TARGET SuiteSparse::SPQR) + set (SuiteSparse_LINK_TARGET SuiteSparse::SPQR) + set (SuiteSparse_LINK_SYMBOL SuiteSparseQR_C_symbolic) +elseif (TARGET SuiteSparse::CHOLMOD) + set (SuiteSparse_LINK_TARGET SuiteSparse::CHOLMOD) + set (SuiteSparse_LINK_SYMBOL cholmod_start) +endif () + +set (SuiteSparse_BLAS_LINK) +if (TARGET BLAS::BLAS) + set (SuiteSparse_BLAS_LINK BLAS::BLAS) +elseif (BLAS_LIBRARIES) + set (SuiteSparse_BLAS_LINK ${BLAS_LIBRARIES}) +endif () + +set (SuiteSparse_LAPACK_LINK) +if (TARGET LAPACK::LAPACK) + set (SuiteSparse_LAPACK_LINK LAPACK::LAPACK) +elseif (LAPACK_LIBRARIES) + set (SuiteSparse_LAPACK_LINK ${LAPACK_LIBRARIES}) +endif () + +set (SuiteSparse_TBB_LINK) +if (TARGET TBB::tbb) + set (SuiteSparse_TBB_LINK TBB::tbb) +elseif (TBB_LIBRARIES) + set (SuiteSparse_TBB_LINK ${TBB_LIBRARIES}) +endif () + +set (SuiteSparse_RT_LINK) +if (HAVE_LIBRT) + set (SuiteSparse_RT_LINK rt) +endif () + +set (SuiteSparse_OPTIONAL_DEPENDENCIES) +if (SuiteSparse_BLAS_LINK) + list (APPEND SuiteSparse_OPTIONAL_DEPENDENCIES BLAS) +endif () +if (SuiteSparse_LAPACK_LINK) + list (APPEND SuiteSparse_OPTIONAL_DEPENDENCIES LAPACK) +endif () +if (TARGET SuiteSparse::SPQR AND SuiteSparse_TBB_LINK) + list (APPEND SuiteSparse_OPTIONAL_DEPENDENCIES TBB) +endif () +if (SuiteSparse_RT_LINK) + list (APPEND SuiteSparse_OPTIONAL_DEPENDENCIES RT) +endif () + +if (SuiteSparse_FOUND AND SuiteSparse_LINK_TARGET) + get_target_property(SuiteSparse_ORIGINAL_CONFIG_LINK + SuiteSparse::Config INTERFACE_LINK_LIBRARIES) + if (SuiteSparse_ORIGINAL_CONFIG_LINK MATCHES "-NOTFOUND$") + set (SuiteSparse_ORIGINAL_CONFIG_LINK) + endif () + if (TARGET SuiteSparse::SPQR) + get_target_property(SuiteSparse_ORIGINAL_SPQR_LINK + SuiteSparse::SPQR INTERFACE_LINK_LIBRARIES) + if (SuiteSparse_ORIGINAL_SPQR_LINK MATCHES "-NOTFOUND$") + set (SuiteSparse_ORIGINAL_SPQR_LINK) + endif () + endif () + + function (suitesparse_set_link_dependencies) + set (SuiteSparse_CONFIG_LINK ${SuiteSparse_ORIGINAL_CONFIG_LINK}) + if (BLAS IN_LIST ARGN) + list (APPEND SuiteSparse_CONFIG_LINK ${SuiteSparse_BLAS_LINK}) + endif () + if (LAPACK IN_LIST ARGN) + list (APPEND SuiteSparse_CONFIG_LINK ${SuiteSparse_LAPACK_LINK}) + endif () + if (RT IN_LIST ARGN) + list (APPEND SuiteSparse_CONFIG_LINK ${SuiteSparse_RT_LINK}) + endif () + set_property (TARGET SuiteSparse::Config PROPERTY + INTERFACE_LINK_LIBRARIES "${SuiteSparse_CONFIG_LINK}") + + if (TARGET SuiteSparse::SPQR) + set (SuiteSparse_SPQR_LINK ${SuiteSparse_ORIGINAL_SPQR_LINK}) + if (TBB IN_LIST ARGN) + list (APPEND SuiteSparse_SPQR_LINK ${SuiteSparse_TBB_LINK}) + endif () + set_property (TARGET SuiteSparse::SPQR PROPERTY + INTERFACE_LINK_LIBRARIES "${SuiteSparse_SPQR_LINK}") + endif () + endfunction (suitesparse_set_link_dependencies) + + suitesparse_set_link_dependencies(${SuiteSparse_OPTIONAL_DEPENDENCIES}) + suitesparse_check_link(SuiteSparse_LINKS + ${SuiteSparse_LINK_SYMBOL} ${SuiteSparse_LINK_TARGET}) + if (SuiteSparse_LINKS) + set (SuiteSparse_REQUIRED_DEPENDENCIES) + foreach (dependency IN LISTS SuiteSparse_OPTIONAL_DEPENDENCIES) + set (SuiteSparse_DEPENDENCIES_WITHOUT + ${SuiteSparse_OPTIONAL_DEPENDENCIES}) + list (REMOVE_ITEM SuiteSparse_DEPENDENCIES_WITHOUT ${dependency}) + suitesparse_set_link_dependencies(${SuiteSparse_DEPENDENCIES_WITHOUT}) + suitesparse_check_link(SuiteSparse_LINKS_WITHOUT_DEPENDENCY + ${SuiteSparse_LINK_SYMBOL} ${SuiteSparse_LINK_TARGET}) + if (NOT SuiteSparse_LINKS_WITHOUT_DEPENDENCY) + list (APPEND SuiteSparse_REQUIRED_DEPENDENCIES ${dependency}) + endif () + endforeach () + + suitesparse_set_link_dependencies(${SuiteSparse_REQUIRED_DEPENDENCIES}) + else () + suitesparse_set_link_dependencies() + list (APPEND SuiteSparse_REQUIRED_VARS SuiteSparse_LINKS) + set (SuiteSparse_MISSING_DEPENDENCIES) + if (NOT SuiteSparse_BLAS_LINK) + list (APPEND SuiteSparse_MISSING_DEPENDENCIES BLAS) + endif () + if (NOT SuiteSparse_LAPACK_LINK) + list (APPEND SuiteSparse_MISSING_DEPENDENCIES LAPACK) + endif () + if (TARGET SuiteSparse::SPQR AND NOT SuiteSparse_TBB_LINK) + list (APPEND SuiteSparse_MISSING_DEPENDENCIES TBB) + endif () + if (NOT SuiteSparse_RT_LINK) + list (APPEND SuiteSparse_MISSING_DEPENDENCIES RT) + endif () + if (SuiteSparse_MISSING_DEPENDENCIES) + foreach (dependency IN LISTS SuiteSparse_MISSING_DEPENDENCIES) + if (SuiteSparse_${dependency}_REASON) + list (APPEND CMAKE_FIND_PACKAGE_REASON + "${SuiteSparse_${dependency}_REASON}") + endif () + endforeach () + set (SuiteSparse_LINK_FAILURE_REASON + "SuiteSparse libraries could not be linked with the detected " + "dependencies. The following dependencies were not found, so their " + "necessity could not be determined: ${SuiteSparse_MISSING_DEPENDENCIES}.") + suitesparse_report_not_found("${SuiteSparse_LINK_FAILURE_REASON}") + else () + set (SuiteSparse_LINK_FAILURE_REASON + "SuiteSparse libraries could not be linked with the detected " + "dependencies.") + suitesparse_report_not_found("${SuiteSparse_LINK_FAILURE_REASON}") + endif () + endif () +endif () + # Check whether CHOLMOD was compiled with METIS support. The check can be # performed only after the main components have been set up. if (TARGET SuiteSparse::CHOLMOD) # NOTE If SuiteSparse was compiled as a static library we'll need to link # against METIS already during the check. Otherwise, the check can fail due to # undefined references even though SuiteSparse was compiled with METIS. - find_package (METIS) + if (NOT DEFINED METIS_FOUND) + find_package (METIS) + endif() if (TARGET METIS::METIS) cmake_push_check_state (RESET) @@ -505,23 +633,18 @@ suitesparse_reset_find_library_prefix() -# Handle REQUIRED and QUIET arguments to FIND_PACKAGE +list(REMOVE_DUPLICATES SuiteSparse_REQUIRED_VARS) + +# Handle REQUIRED and QUIET arguments to FIND_PACKAGE. include(FindPackageHandleStandardArgs) -if (SuiteSparse_FOUND) - find_package_handle_standard_args(SuiteSparse - REQUIRED_VARS ${SuiteSparse_REQUIRED_VARS} - VERSION_VAR SuiteSparse_VERSION - FAIL_MESSAGE "Failed to find some/all required components of SuiteSparse." - HANDLE_COMPONENTS) -else (SuiteSparse_FOUND) - # Do not pass VERSION_VAR to FindPackageHandleStandardArgs() if we failed to - # find SuiteSparse to avoid a confusing autogenerated failure message - # that states 'not found (missing: FOO) (found version: x.y.z)'. - find_package_handle_standard_args(SuiteSparse - REQUIRED_VARS ${SuiteSparse_REQUIRED_VARS} - FAIL_MESSAGE "Failed to find some/all required components of SuiteSparse." - HANDLE_COMPONENTS) -endif (SuiteSparse_FOUND) +list(REMOVE_DUPLICATES CMAKE_FIND_PACKAGE_REASON) +string(JOIN "\n " CMAKE_FIND_PACKAGE_REASON + ${CMAKE_FIND_PACKAGE_REASON}) +find_package_handle_standard_args(SuiteSparse + REQUIRED_VARS ${SuiteSparse_REQUIRED_VARS} + VERSION_VAR SuiteSparse_VERSION + REASON_FAILURE_MESSAGE "${CMAKE_FIND_PACKAGE_REASON}" + HANDLE_COMPONENTS) # Pop CMP0057. cmake_policy (POP)
diff --git a/cmake/ReadCeresVersionFromSource.cmake b/cmake/ReadCeresVersionFromSource.cmake index 53e29c4..085be51 100644 --- a/cmake/ReadCeresVersionFromSource.cmake +++ b/cmake/ReadCeresVersionFromSource.cmake
@@ -75,7 +75,4 @@ # This is on a single line s/t CMake does not interpret it as a list of # elements and insert ';' separators which would result in 3.;2.;0 nonsense. set(CERES_VERSION "${CERES_VERSION_MAJOR}.${CERES_VERSION_MINOR}.${CERES_VERSION_PATCH}") - - message(STATUS "Detected Ceres version: ${CERES_VERSION} from " - "${CERES_VERSION_FILE}") endmacro()
diff --git a/cmake/UpdateCacheVariable.cmake b/cmake/UpdateCacheVariable.cmake deleted file mode 100644 index bf3b594..0000000 --- a/cmake/UpdateCacheVariable.cmake +++ /dev/null
@@ -1,48 +0,0 @@ -# Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2023 Google Inc. All rights reserved. -# http://ceres-solver.org/ -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Google Inc. nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# Author: alexs.mac@gmail.com (Alex Stewart) - -# By default, there is no easy way in CMake to set the value of a cache -# variable without reinitialising it, which involves resetting its -# associated help string. This is particularly annoying for CMake options -# where they need to programmatically updated. -# -# This function automates this process by getting the current help string -# for the cache variable to update, then reinitialising it with the new -# value, but with the original help string. -function(UPDATE_CACHE_VARIABLE VAR_NAME VALUE) - get_property(IS_DEFINED_IN_CACHE CACHE ${VAR_NAME} PROPERTY VALUE SET) - if (NOT IS_DEFINED_IN_CACHE) - message(FATAL_ERROR "Specified variable to update in cache: " - "${VAR_NAME} has not been set in the cache.") - endif() - get_property(HELP_STRING CACHE ${VAR_NAME} PROPERTY HELPSTRING) - get_property(VAR_TYPE CACHE ${VAR_NAME} PROPERTY TYPE) - set(${VAR_NAME} ${VALUE} CACHE ${VAR_TYPE} "${HELP_STRING}" FORCE) -endfunction()
diff --git a/cmake/iOS.cmake b/cmake/iOS.cmake index 0773d13..805ce3e 100644 --- a/cmake/iOS.cmake +++ b/cmake/iOS.cmake
@@ -82,7 +82,7 @@ OUTPUT_STRIP_TRAILING_WHITESPACE) string(REGEX MATCH "Xcode [0-9\\.]+" XCODE_VERSION "${XCODE_VERSION}") string(REGEX REPLACE "Xcode ([0-9\\.]+)" "\\1" XCODE_VERSION "${XCODE_VERSION}") -message(STATUS "Building with Xcode version: ${XCODE_VERSION}") +message(VERBOSE "Building with Xcode version: ${XCODE_VERSION}") # Default to building for iPhoneOS if not specified otherwise, and we cannot # determine the platform from the CMAKE_OSX_ARCHITECTURES variable. The use @@ -127,7 +127,7 @@ OUTPUT_VARIABLE CMAKE_OSX_SYSROOT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Using SDK: ${CMAKE_OSX_SYSROOT} for platform: ${IOS_PLATFORM}") + message(VERBOSE "Using SDK: ${CMAKE_OSX_SYSROOT} for platform: ${IOS_PLATFORM}") endif() if (NOT EXISTS ${CMAKE_OSX_SYSROOT}) message(FATAL_ERROR "Invalid CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} " @@ -158,14 +158,14 @@ OUTPUT_VARIABLE CMAKE_C_COMPILER ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Using C compiler: ${CMAKE_C_COMPILER}") + message(VERBOSE "Using C compiler: ${CMAKE_C_COMPILER}") endif() if (NOT CMAKE_CXX_COMPILER) execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang++ OUTPUT_VARIABLE CMAKE_CXX_COMPILER ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Using CXX compiler: ${CMAKE_CXX_COMPILER}") + message(VERBOSE "Using CXX compiler: ${CMAKE_CXX_COMPILER}") endif() # Find (Apple's) libtool. @@ -173,7 +173,7 @@ OUTPUT_VARIABLE IOS_LIBTOOL ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -message(STATUS "Using libtool: ${IOS_LIBTOOL}") +message(VERBOSE "Using libtool: ${IOS_LIBTOOL}") # Configure libtool to be used instead of ar + ranlib to build static libraries. # This is required on Xcode 7+, but should also work on previous versions of # Xcode. @@ -192,13 +192,14 @@ # Unless specified, the latest SDK version is used by default. set(IOS_DEPLOYMENT_TARGET "${IOS_SDK_VERSION}" CACHE STRING "Minimum iOS version to build for." ) -message(STATUS "Building for minimum iOS version: ${IOS_DEPLOYMENT_TARGET}" +# Toolchain selections are detailed user-facing diagnostics. +message(VERBOSE "Building for minimum iOS version: ${IOS_DEPLOYMENT_TARGET}" " (SDK version: ${IOS_SDK_VERSION})") if (NOT IOS_DEPLOYMENT_TARGET VERSION_LESS 11.0) # iOS 11+ does not support 32-bit architectures (armv7). foreach(ARCH ${IOS_ARCH}) if (ARCH MATCHES "armv7*") - message(STATUS "Removing iOS architecture: ${ARCH} from build as it is " + message(VERBOSE "Removing iOS architecture: ${ARCH} from build as it is " "not supported by the minimum iOS version to build for: " "${IOS_DEPLOYMENT_TARGET} (iOS >= 11 requires 64-bit).") else() @@ -208,7 +209,7 @@ set(IOS_ARCH ${VALID_IOS_ARCH_FOR_SDK_VERSION}) endif() -message(STATUS "Configuring iOS build for platform: ${IOS_PLATFORM}, " +message(VERBOSE "Configuring iOS build for platform: ${IOS_PLATFORM}, " "architecture(s): ${IOS_ARCH}") # Standard settings.
diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 6548281..ad9384d 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst
@@ -18,7 +18,7 @@ optional. For details on customizing the build process, see :ref:`section-customizing` . -- `CMake <http://www.cmake.org>`_ (**required**) 3.16 or later. +- `CMake <http://www.cmake.org>`_ (**required**) 3.22 or later. - `Eigen <http://eigen.tuxfamily.org/index.php?title=Main_Page>`_ (**Required**) 3.3.4 or later. @@ -26,7 +26,7 @@ .. NOTE :: Ceres can also use Eigen as a sparse linear algebra - library. Please see the documentation for ``EIGENSPARSE`` for + library. Please see the documentation for ``WITH_EIGENSPARSE`` for more details. - `Abseil <https://abseil.io/>`_ (**Required**) 20240116 or later. @@ -84,7 +84,7 @@ If you have an NVIDIA GPU then Ceres Solver can use it accelerate the solution of the Gauss-Newton linear systems using the CMake flag - ``USE_CUDA``. + ``WITH_CUDA``. This support depends on two libraries from NVIDIA `CUDA` and `cuDSS`. @@ -165,9 +165,8 @@ cmake ../ceres-solver-2.2.0 make -j3 make test - # Optionally install Ceres, it can also be exported using CMake which - # allows Ceres to be used without requiring installation, see the documentation - # for the EXPORT_BUILD_DIR option for more information. + # The build tree is also exported for use without installation. Registry + # registration can be disabled with CMAKE_EXPORT_PACKAGE_REGISTRY=OFF. make install You can also try running the command line bundling application with one of the @@ -294,9 +293,8 @@ cmake ../ceres-solver-2.2.0 make -j3 make test - # Optionally install Ceres, it can also be exported using CMake which - # allows Ceres to be used without requiring installation, see the - # documentation for the EXPORT_BUILD_DIR option for more information. + # The build tree is also exported for use without installation. Registry + # registration can be disabled with CMAKE_EXPORT_PACKAGE_REGISTRY=OFF. make install .. _section-windows: @@ -567,6 +565,18 @@ line. In general, you should only modify these options from their defaults if you know what you are doing. +For additional configure diagnostics, pass ``--log-level=VERBOSE``, +``--log-level=DEBUG`` or ``--log-level=TRACE`` to ``CMake``. To keep a log +level for subsequent configure runs in a build directory, set +``CMAKE_MESSAGE_LOG_LEVEL``, for example with +``-DCMAKE_MESSAGE_LOG_LEVEL=DEBUG``. The command-line option takes precedence +over the variable. + +``STATUS`` messages describe normal configuration progress. ``VERBOSE`` +messages add detailed toolchain and configuration choices. ``DEBUG`` messages +expose internal setup details. ``TRACE`` is reserved for temporary low-level +diagnostics. + .. NOTE:: If you are setting variables via ``-D<VARIABLE>=<VALUE>`` when @@ -620,16 +630,91 @@ Options controlling Ceres configuration --------------------------------------- -#. ``LAPACK [Default: ON]``: If this option is enabled, and the ``BLAS`` and - ``LAPACK`` libraries are found, Ceres will enable **direct** use of - ``LAPACK`` routines (i.e. Ceres itself will call them). If this option is - disabled, then Ceres will not require ``LAPACK`` or ``BLAS``. It is - however still possible that Ceres may call ``LAPACK`` routines indirectly - via SuiteSparse if ``LAPACK=OFF`` and ``SUITESPARSE=ON``. Finally - note that if ``LAPACK=ON`` and ``SUITESPARSE=ON``, the ``LAPACK`` and - ``BLAS`` libraries used by SuiteSparse and Ceres should be the same. +Ceres-specific feature options use the ``WITH_`` prefix. The configure step +prints a summary of enabled features and discovered dependencies at the end of +the output. The summary includes the Ceres version, package versions when they +are reported by the package finders, and optional packages that were not found. +An unavailable optional package does not change the corresponding cache option. -#. ``SUITESPARSE [Default: OFF]``: SuiteSparse support is opt-in. Turn this +#. ``BUILD_BENCHMARKS [Default: ON]``: Enable the Ceres benchmarking suite + when the benchmark dependency is available. + +#. ``BUILD_DOCUMENTATION [Default: OFF]``: Use this to enable building + the documentation. This requires `Sphinx <http://sphinx-doc.org/>`_ and + the `sphinx-rtd-theme + <https://pypi.org/project/sphinx-rtd-theme/>`_ package + available from the Python package index. Configuration fails if either is + unavailable. In addition, ``make ceres_docs`` can be used to build only the + documentation. + +#. ``BUILD_EXAMPLES [Default: ON]``: Build the Ceres example programs. + +#. ``BUILD_SHARED_LIBS [Default: OFF]``: By default Ceres is built as + a static library. Turn this ``ON`` to build Ceres as a shared library. + +#. ``BUILD_TESTING [Default: ON]``: Enable the Ceres unit and integration tests. + +#. ``CMAKE_EXPORT_PACKAGE_REGISTRY [Default: OFF]``: Ceres always generates a + build-tree package export, so clients can use the build directory without + installing Ceres. Set this standard CMake variable to ``ON`` to also have + ``export(PACKAGE)`` register the build directory in the `user's local + CMake package registry + <http://www.cmake.org/cmake/help/v3.5/manual/cmake-packages.7.html#user-package-registry>`_, + so that it is found automatically by ``find_package(Ceres)`` without + setting ``Ceres_DIR``. It is left disabled by default, matching CMake's + own default for ``export(PACKAGE)``. + +#. ``CMAKE_INSTALL_LIBDIR [Default: platform-dependent]``: Set this standard + CMake variable to choose the directory below ``CMAKE_INSTALL_PREFIX`` where + Ceres libraries are installed. It defaults according to CMake's + ``GNUInstallDirs`` module. + +#. ``CMAKE_MSVC_RUNTIME_LIBRARY [Default: compiler default]`` *Windows Only*: + Set this standard CMake variable to select the MSVC runtime library used to + build Ceres. When it is not set, CMake uses the compiler default. + +#. ``WITH_ACCELERATESPARSE [Default: ON]``: By default, Ceres will link to + Apple's Accelerate framework directly if a version of it is detected + which supports solving sparse linear systems. On Apple operating systems, + Accelerate usually also provides the BLAS and LAPACK implementations and + is linked irrespective of this option. + +#. ``WITH_BITCODE [Default: OFF]`` *Apple platforms*: Enable bitcode for iOS + builds. This disables Eigen's additional Clang inlining optimization. + +#. ``WITH_CUDA [Default: default]``: Enable CUDA linear algebra solvers. The + value can be ``OFF``, ``default``, or ``static``. The latter links against + static CUDA runtime libraries when supported. + +#. ``WITH_CUSTOM_BLAS [Default: ON]``: Use Ceres' custom BLAS routines instead + of Eigen's implementations where available. + +#. ``WITH_EIGENMETIS [Default: ON]``: Enable METIS support for Eigen's sparse + solvers when METIS is available. This option is available when + ``WITH_EIGENSPARSE`` is enabled. + +#. ``WITH_EIGENSPARSE [Default: ON]``: By default, Ceres will use Eigen's + sparse Cholesky factorization. + +#. ``WITH_LAPACK [Default: ON]``: If this option is enabled, and the ``BLAS`` and + ``LAPACK`` libraries are found, Ceres will enable **direct** use of + ``LAPACK`` routines. If this option is disabled, Ceres does not use + ``LAPACK`` directly. SuiteSparse determines its own ``BLAS`` and ``LAPACK`` + link dependencies independently. Direct LAPACK use is always disabled for + iOS builds because Apple treats ``dsyrk_`` as a private API. + +#. ``WITH_SANITIZERS [Default: empty]``: A semicolon-separated list of + sanitizers to enable, such as ``address`` or ``thread``. + +#. ``WITH_SCHUR_SPECIALIZATIONS [Default: ON]``: If you are concerned about + binary size or compilation time over some small performance gains in the + ``SPARSE_SCHUR`` solver, you can disable some of the template + specializations by turning this ``OFF``. + +#. ``WITH_STRIPPED_DEBUG_SYMBOLS [Default: ON]`` *Android platforms*: Strip + debug symbols from Android builds to reduce file sizes. + +#. ``WITH_SUITESPARSE [Default: OFF]``: SuiteSparse support is opt-in. Turn this ``ON`` to link Ceres against ``SuiteSparse``, provided it and all of its dependencies are present. @@ -639,75 +724,12 @@ Ceres requires the CHOLMOD supernodal factorization and SPQR components, which are only available under GPL/Commercial terms. Consequently, unless you hold a commercial SuiteSparse license, a Ceres build with - ``SUITESPARSE=ON`` is GPL licensed. This is why SuiteSparse support is + ``WITH_SUITESPARSE=ON`` is GPL licensed. This is why SuiteSparse support is opt-in rather than enabled by default. Obtaining a commercial SuiteSparse license removes this restriction. -#. ``ACCELERATESPARSE [Default: ON]``: By default, Ceres will link to - Apple's Accelerate framework directly if a version of it is detected - which supports solving sparse linear systems. Note that on Apple OSs - Accelerate usually also provides the BLAS/LAPACK implementations and - so would be linked against irrespective of the value of ``ACCELERATESPARSE``. - -#. ``EIGENSPARSE [Default: ON]``: By default, Ceres will use Eigen's - sparse Cholesky factorization. - -#. ``GFLAGS [Default: ON]``: Turn this ``OFF`` to build Ceres without - ``gflags``. This will also prevent some of the example code from - building. - -#. ``MINIGLOG [Default: OFF]``: Ceres includes a stripped-down, - minimal implementation of ``glog`` which can optionally be used as - a substitute for ``glog``, thus removing ``glog`` as a required - dependency. Turn this ``ON`` to use this minimal ``glog`` - implementation. - -#. ``SCHUR_SPECIALIZATIONS [Default: ON]``: If you are concerned about - binary size/compilation time over some small (10-20%) performance - gains in the ``SPARSE_SCHUR`` solver, you can disable some of the - template specializations by turning this ``OFF``. - -#. ``BUILD_SHARED_LIBS [Default: OFF]``: By default Ceres is built as - a static library, turn this ``ON`` to instead build Ceres as a - shared library. - -#. ``EXPORT_BUILD_DIR [Default: OFF]``: By default Ceres is configured - solely for installation, and so must be installed in order for - clients to use it. Turn this ``ON`` to export Ceres' build - directory location into the `user's local CMake package registry - <http://www.cmake.org/cmake/help/v3.5/manual/cmake-packages.7.html#user-package-registry>`_ - where it will be detected **without requiring installation** in a - client project using CMake when `find_package(Ceres) - <http://www.cmake.org/cmake/help/v3.5/command/find_package.html>`_ - is invoked. - -#. ``BUILD_DOCUMENTATION [Default: OFF]``: Use this to enable building - the documentation, requires `Sphinx <http://sphinx-doc.org/>`_ and - the `sphinx-rtd-theme - <https://pypi.org/project/sphinx-rtd-theme/>`_ package - available from the Python package index. In addition, ``make - ceres_docs`` can be used to build only the documentation. - -#. ``MSVC_USE_STATIC_CRT [Default: OFF]`` *Windows Only*: By default - Ceres will use the Visual Studio default, *shared* C-Run Time (CRT) - library. Turn this ``ON`` to use the *static* C-Run Time library - instead. - -#. ``LIB_SUFFIX [Default: "64" on non-Debian/Arch based 64-bit Linux, - otherwise: ""]``: The suffix to append to the library install - directory, built from: - ``${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}``. - - The filesystem hierarchy standard recommends that 64-bit systems - install native libraries to lib64 rather than lib. Most Linux - distributions follow this convention, but Debian and Arch based - distros do not. Note that the only generally sensible values for - ``LIB_SUFFIX`` are "" and "64". - - Although by default Ceres will auto-detect non-Debian/Arch based - 64-bit Linux distributions and default ``LIB_SUFFIX`` to "64", this - can always be overridden by manually specifying LIB_SUFFIX using: - ``-DLIB_SUFFIX=<VALUE>`` when invoking CMake. +#. ``WITH_UNINSTALL_TARGET [Default: ON]``: Add an ``uninstall`` target to + remove files installed by Ceres. Options controlling Ceres dependency locations @@ -788,12 +810,15 @@ :ref:`section-local-installations`. Note that if you are using a non-standard install location you - should consider exporting Ceres instead, as this will not require - any extra information to be provided in client code for Ceres to - be detected. + should consider exporting Ceres instead. If registry registration is + also enabled (see ``CMAKE_EXPORT_PACKAGE_REGISTRY``), this will not + require any extra information to be provided in client code for Ceres + to be detected. -#. Or Ceres' build directory must have been exported by enabling the - ``EXPORT_BUILD_DIR`` option when Ceres was configured. +#. Or Ceres' build directory can be used directly through its generated + build-tree package files. Unless registry registration was enabled + when Ceres was configured, set ``Ceres_DIR`` to the Ceres build + directory. As an example of how to use Ceres, to compile `examples/helloworld.cc @@ -843,21 +868,21 @@ The Ceres components which can be specified are: -#. ``LAPACK``: Ceres built using LAPACK (``LAPACK=ON``). +#. ``LAPACK``: Ceres built with direct LAPACK support. -#. ``SuiteSparse``: Ceres built with SuiteSparse (``SUITESPARSE=ON``). +#. ``SuiteSparse``: Ceres built with SuiteSparse support. -#. ``AccelerateSparse``: Ceres built with Apple's Accelerate sparse solvers (``ACCELERATESPARSE=ON``). +#. ``AccelerateSparse``: Ceres built with Apple's Accelerate sparse solver + support. #. ``EigenSparse``: Ceres built with Eigen's sparse Cholesky factorization - (``EIGENSPARSE=ON``). + support. #. ``SparseLinearAlgebraLibrary``: Ceres built with *at least one* sparse linear algebra library. This is equivalent to ``SuiteSparse`` **OR** ``AccelerateSparse`` **OR** ``EigenSparse``. -#. ``SchurSpecializations``: Ceres built with Schur specializations - (``SCHUR_SPECIALIZATIONS=ON``). +#. ``SchurSpecializations``: Ceres built with Schur specializations. To specify one/multiple Ceres components use the ``COMPONENTS`` argument to `find_package() @@ -902,10 +927,11 @@ Note that this can be used to have multiple versions of Ceres installed. However, particularly if you have only a single version of Ceres which you want to use but do not wish to install to a system -location, you should consider exporting Ceres using the -``EXPORT_BUILD_DIR`` option instead of a local install, as exported -versions of Ceres will be automatically detected by CMake, -irrespective of their location. +location, you should consider using Ceres' build-tree package export instead +of a local install. Build-tree exports are automatically detected, +irrespective of their location, only when registry registration was enabled +via ``CMAKE_EXPORT_PACKAGE_REGISTRY`` when Ceres was configured. Otherwise +set ``Ceres_DIR`` to the Ceres build directory. Understanding the CMake Package System ---------------------------------------- @@ -1011,9 +1037,10 @@ <http://www.cmake.org/cmake/help/v3.5/command/export.html>`_ (instead of `install() <http://www.cmake.org/cmake/help/v3.5/command/install.html>`_). Ceres -supports both installation and export of its build directory if the -``EXPORT_BUILD_DIR`` option is enabled, see -:ref:`section-customizing`. +supports both installation and export of its build directory. The build-tree +package files are always generated. The standard +``CMAKE_EXPORT_PACKAGE_REGISTRY`` variable controls whether the build directory +is added to the user package registry. .. _section-install-vs-export:
diff --git a/docs/source/version_history.rst b/docs/source/version_history.rst index a4581eb..32d6c89 100644 --- a/docs/source/version_history.rst +++ b/docs/source/version_history.rst
@@ -27,6 +27,12 @@ ``-DSUITESPARSE=ON`` restores the previous behavior, but makes the resulting Ceres build GPL licensed unless you hold a commercial SuiteSparse license. +#. CMake configuration now reports dependencies using standard package output + and ``FeatureSummary``, and feature options are renamed with a ``WITH_`` + prefix (e.g. ``SUITESPARSE`` is now ``WITH_SUITESPARSE``). The + ``LIB_SUFFIX``, ``MSVC_USE_STATIC_CRT``, and ``EXPORT_BUILD_DIR`` options + are removed in favor of standard CMake variables. CMake 3.22 or later is + now required. See :ref:`options-controlling-ceres-configuration`. 2.2.0 =====
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt index 2ea1cd3..e541e76 100644 --- a/internal/ceres/CMakeLists.txt +++ b/internal/ceres/CMakeLists.txt
@@ -1,5 +1,5 @@ # Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2025 Google Inc. All rights reserved. +# Copyright 2026 Google Inc. All rights reserved. # http://ceres-solver.org/ # # Redistribution and use in source and binary forms, with or without @@ -30,10 +30,7 @@ # Build the list of dependencies for Ceres based on the current configuration. -find_package(Threads REQUIRED) list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES Threads::Threads) -# Make dependency visible to the parent CMakeLists.txt -set(Threads_DEPENDENCY "find_dependency (Threads)" PARENT_SCOPE) # Generator expressions were introduced in CMake version 3.24. Without # them including absl::log_flags as an internal dependency causes @@ -82,12 +79,12 @@ ${Ceres_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/ceres/internal/*.h) # Include the specialized schur solvers. -if (SCHUR_SPECIALIZATIONS) +if (WITH_SCHUR_SPECIALIZATIONS) file(GLOB CERES_INTERNAL_SCHUR_FILES generated/*.cc) -else (SCHUR_SPECIALIZATIONS) +else (WITH_SCHUR_SPECIALIZATIONS) # Only the fully dynamic solver. The build is much faster this way. file(GLOB CERES_INTERNAL_SCHUR_FILES generated/*_d_d_d.cc) -endif (SCHUR_SPECIALIZATIONS) +endif (WITH_SCHUR_SPECIALIZATIONS) # The generated specializations of the Schur eliminator include # schur_eliminator_impl.h which defines EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD @@ -98,7 +95,7 @@ set_source_files_properties(${CERES_INTERNAL_SCHUR_FILES} PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) -if (SUITESPARSE AND SuiteSparse_FOUND) +if (_Ceres_FEATURE_SUITESPARSE) # Define version information for use in Solver::FullReport. add_definitions(-DCERES_SUITESPARSE_VERSION="${SuiteSparse_VERSION}") list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES SuiteSparse::CHOLMOD @@ -107,19 +104,19 @@ if (SuiteSparse_Partition_FOUND) list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES SuiteSparse::Partition) endif (SuiteSparse_Partition_FOUND) -endif (SUITESPARSE AND SuiteSparse_FOUND) +endif (_Ceres_FEATURE_SUITESPARSE) -if (SuiteSparse_Partition_FOUND OR EIGENMETIS) +if (_Ceres_FEATURE_CHOLMOD_PARTITION OR _Ceres_FEATURE_EIGEN_METIS) # Define version information for use in Solver::FullReport. add_definitions(-DCERES_METIS_VERSION="${METIS_VERSION}") list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES METIS::METIS) -endif (SuiteSparse_Partition_FOUND OR EIGENMETIS) +endif (_Ceres_FEATURE_CHOLMOD_PARTITION OR _Ceres_FEATURE_EIGEN_METIS) -if (ACCELERATESPARSE AND AccelerateSparse_FOUND) - list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${AccelerateSparse_LIBRARIES}) +if (_Ceres_FEATURE_ACCELERATE) + list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES AccelerateSparse::Accelerate) endif() -if (USE_CUDA) +if (_Ceres_FEATURE_CUDA) list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${CERES_CUDA_LIBRARIES}) set_source_files_properties(cuda_kernels_vector_ops.cu.cc PROPERTIES LANGUAGE CUDA) set_source_files_properties(cuda_kernels_bsm_to_crs.cu.cc PROPERTIES LANGUAGE CUDA) @@ -130,9 +127,9 @@ target_compile_options(ceres_cuda_kernels PRIVATE --expt-extended-lambda) target_include_directories(ceres_cuda_kernels PRIVATE ${Ceres_SOURCE_DIR}/include ${Ceres_SOURCE_DIR}/internal ${Ceres_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}) list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ceres_cuda_kernels) -endif (USE_CUDA) +endif (_Ceres_FEATURE_CUDA) -if (LAPACK_FOUND) +if (_Ceres_FEATURE_LAPACK) list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${LAPACK_LIBRARIES}) endif () @@ -253,13 +250,6 @@ ${CERES_PUBLIC_HDRS} ${CERES_PUBLIC_INTERNAL_HDRS}) - -# Ceres C++ compiler flags can be too strict for an external library code -# which we do not maintain. -include(CheckCXXCompilerFlag) -check_cxx_compiler_flag("-Wno-missing-declarations" - CHECK_CXX_FLAG_Wno_missing_declarations) - add_library(ceres $<TARGET_OBJECTS:ceres_internal> ${CERES_LIBRARY_SOURCE}) if(BUILD_SHARED_LIBS) @@ -331,35 +321,18 @@ # results in all compiler warnings from them being suppressed. target_link_libraries(ceres PUBLIC Eigen3::Eigen) -# Gather the list of public & private include locations for all enabled optional -# dependencies to be added to the Ceres target. -set(CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS "") -set(CERES_LIBRARY_PUBLIC_DEPENDENCIES_INCLUDE_DIRS "") - -if (ACCELERATESPARSE) - list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS - ${AccelerateSparse_INCLUDE_DIRS}) -endif() - -# Add include locations for optional dependencies to the Ceres target without -# duplication. -list(REMOVE_DUPLICATES CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS) -target_include_directories(ceres PRIVATE ${CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS}) -list(REMOVE_DUPLICATES CERES_LIBRARY_PUBLIC_DEPENDENCIES_INCLUDE_DIRS) -target_include_directories(ceres PUBLIC ${CERES_LIBRARY_PUBLIC_DEPENDENCIES_INCLUDE_DIRS}) - # Generate an export header for annotating symbols visibility include(GenerateExportHeader) generate_export_header(ceres EXPORT_FILE_NAME ${Ceres_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/ceres/internal/export.h) -if (USE_CUDA) +if (_Ceres_FEATURE_CUDA) install(TARGETS ceres_cuda_kernels EXPORT CeresExport RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -endif(USE_CUDA) +endif(_Ceres_FEATURE_CUDA) install(TARGETS ceres EXPORT CeresExport @@ -531,7 +504,7 @@ ${CERES_LIBRARY_PRIVATE_DEPENDENCIES}) endmacro() -if (BUILD_BENCHMARKS) +if (_Ceres_FEATURE_BENCHMARKS) add_executable(evaluation_benchmark evaluation_benchmark.cc) add_dependencies_to_benchmark(evaluation_benchmark) @@ -567,4 +540,4 @@ add_dependencies_to_benchmark(block_jacobi_preconditioner_benchmark) add_subdirectory(autodiff_benchmarks) -endif (BUILD_BENCHMARKS) +endif (_Ceres_FEATURE_BENCHMARKS)