Skip to content
Snippets Groups Projects
FindSuiteSparse.cmake 8.40 KiB
# .. cmake_module::
#
#    Find the SuiteSparse libraries like UMFPACK or SPQR.
#
#    Example which tries to find Suite Sparse's UMFPack component:
#
#    :code:`find_package(SuiteSparse OPTIONAL_COMPONENTS UMFPACK)`
#
#    `OPTIONAL_COMPONENTS`
#       A list of components. Components are:
#       AMD, BTF, CAMD, CCOLAMD, CHOLMOD, COLAMD, CXSPARSE,
#       KLU, LDL, RBIO, SPQR, UMFPACK
#
#    :ref:`SuiteSparse_ROOT`
#       Path list to search for SuiteSparse
#
#    Sets the following variables:
#
#    :code:`SuiteSparse_FOUND`
#       True if SuiteSparse was found.
#
#    :code:`SuiteSparse_INCLUDE_DIRS`
#       Path to the SuiteSparse include dirs.
#
#    :code:`SuiteSparse_LIBRARIES`
#       Name of the SuiteSparse libraries.
#
#    :code:`SuiteSparse_<COMPONENT>_FOUND`
#       Whether <COMPONENT> was found as part of SuiteSparse.
#
# .. cmake_variable:: SuiteSparse_ROOT
#
#   You may set this variable to have :ref:`FindSuiteSparse` look
#   for SuiteSparse in the given path before inspecting
#   system paths.
#

find_package(BLAS QUIET)

# look for desired componenents
set(SUITESPARSE_COMPONENTS ${SuiteSparse_FIND_COMPONENTS})

# resolve inter-component dependencies
list(FIND SUITESPARSE_COMPONENTS "UMFPACK" WILL_USE_UMFPACK)
if(NOT WILL_USE_UMFPACK EQUAL -1)
  list(APPEND SUITESPARSE_COMPONENTS AMD CHOLMOD)
endif()
list(FIND SUITESPARSE_COMPONENTS "CHOLMOD" WILL_USE_CHOLMOD)
if(NOT WILL_USE_CHOLMOD EQUAL -1)
  list(APPEND SUITESPARSE_COMPONENTS AMD CAMD COLAMD CCOLAMD)
endif()

if(SUITESPARSE_COMPONENTS)
  list(REMOVE_DUPLICATES SUITESPARSE_COMPONENTS)
endif()

# find SuiteSparse config:
# look for library at positions given by the user
find_library(SUITESPARSE_CONFIG_LIB
  NAMES "suitesparseconfig"
  PATHS ${SuiteSparse_ROOT}
  PATH_SUFFIXES "lib" "lib32" "lib64" "Lib"
  NO_DEFAULT_PATH
)
# now also include the default paths
find_library(SUITESPARSE_CONFIG_LIB
  NAMES "suitesparseconfig"
  PATH_SUFFIXES "lib" "lib32" "lib64" "Lib"
)
#look for header files at positions given by the user
find_path(SUITESPARSE_INCLUDE_DIR
  NAMES "SuiteSparse_config.h"
  PATHS ${SuiteSparse_ROOT}
  PATH_SUFFIXES "SuiteSparse_config" "SuiteSparse_config/include" "suitesparse" "include" "src" "SuiteSparse_config/Include"
  NO_DEFAULT_PATH
)
#now also look for default paths
find_path(SUITESPARSE_INCLUDE_DIR
  NAMES "SuiteSparse_config.h"
  PATH_SUFFIXES "SuiteSparse_config" "SuiteSparse_config/include" "suitesparse" "include" "src" "SuiteSparse_config/Include"
)

foreach(_component ${SUITESPARSE_COMPONENTS})
  string(TOLOWER ${_component} _componentLower)

  #look for library at positions given by the user
  find_library(${_component}_LIBRARY
    NAMES "${_componentLower}"
    PATHS ${SuiteSparse_ROOT}
    PATH_SUFFIXES "lib" "lib32" "lib64" "${_component}" "${_component}/Lib"
    NO_DEFAULT_PATH
  )
  #now also include the default paths
  find_library(${_component}_LIBRARY
    NAMES "${_componentLower}"
    PATH_SUFFIXES "lib" "lib32" "lib64" "${_component}" "${_component}/Lib"
  )

  #look for header files at positions given by the user
  find_path(${_component}_INCLUDE_DIR
    NAMES "${_componentLower}.h"
    PATHS ${SuiteSparse_ROOT}
    PATH_SUFFIXES "${_componentLower}" "include/${_componentLower}" "suitesparse" "include" "src" "${_component}" "${_component}/Include"
    NO_DEFAULT_PATH
  )
  #now also look for default paths
  find_path(${_component}_INCLUDE_DIR
    NAMES "${_componentLower}.h"
    PATH_SUFFIXES "${_componentLower}" "include/${_componentLower}" "suitesparse" "include" "${_component}" "${_component}/Include"
  )
endforeach()

# SPQR has different header file name SuiteSparseQR.hpp
#look for header files at positions given by the user
find_path(SPQR_INCLUDE_DIR
  NAMES "SuiteSparseQR.hpp"
  PATHS ${SuiteSparse_ROOT}
  PATH_SUFFIXES "spqr" "include/spqr" "suitesparse" "include" "src" "SPQR" "SPQR/Include"
  NO_DEFAULT_PATH
)
#now also look for default paths
find_path(SPQR_INCLUDE_DIR
  NAMES "SuiteSparseQR.hpp"
  PATH_SUFFIXES "spqr" "include/spqr" "suitesparse" "include" "SPQR" "SPQR/Include"
)

# resolve inter-modular dependencies

# CHOLMOD requires AMD, COLAMD; CAMD and CCOLAMD are optional
if(CHOLMOD_LIBRARY)
  if(NOT (AMD_LIBRARY AND COLAMD_LIBRARY))
    message(WARNING "CHOLMOD requires AMD and COLAMD which were not found, skipping the test.")
    set(SuiteSparse_CHOLMOD_FOUND "CHOLMOD requires AMD and COLAMD-NOTFOUND")
  endif()

  list(APPEND CHOLMOD_LIBRARY ${AMD_LIBRARY} ${COLAMD_LIBRARY})
  if(CAMD_LIBRARY)
    list(APPEND CHOLMOD_LIBRARY ${CAMD_LIBRARY})
  endif()
  if(CCOLAMD_LIBRARY)
    list(APPEND CHOLMOD_LIBRARY ${CCOLAMD_LIBRARY})
  endif()
  list(REVERSE CHOLMOD_LIBRARY)
  # remove duplicates
  list(REMOVE_DUPLICATES CHOLMOD_LIBRARY)
  list(REVERSE CHOLMOD_LIBRARY)
endif()

# UMFPack requires AMD, can depend on CHOLMOD
if(UMFPACK_LIBRARY)
  # check wether cholmod was found
  if(CHOLMOD_LIBRARY)
    list(APPEND UMFPACK_LIBRARY ${CHOLMOD_LIBRARY})
  else()
    list(APPEND UMFPACK_LIBRARY ${AMD_LIBRARY})
  endif()
  list(REVERSE UMFPACK_LIBRARY)
  # remove duplicates
  list(REMOVE_DUPLICATES UMFPACK_LIBRARY)
  list(REVERSE UMFPACK_LIBRARY)
endif()

# check wether everything was found
foreach(_component ${SUITESPARSE_COMPONENTS})
  # variable used for component handling
  if(${_component}_LIBRARY AND ${_component}_INCLUDE_DIR)
    set(SuiteSparse_${_component}_FOUND TRUE)
  else()
    set(SuiteSparse_${_component}_FOUND FALSE)
  endif()
  set(HAVE_SUITESPARSE_${_component} ${SuiteSparse_${_component}_FOUND})
  if(SuiteSparse_${_component}_FOUND)
    list(APPEND SUITESPARSE_INCLUDE_DIR "${${_component}_INCLUDE_DIR}")
    list(APPEND SUITESPARSE_LIBRARY "${${_component}_LIBRARY}")
  endif()

  mark_as_advanced(
    HAVE_SUITESPARSE_${_component}
    SuiteSparse_${_component}_FOUND
    ${_component}_INCLUDE_DIR
    ${_component}_LIBRARY)
endforeach()

# check version, for SPQR we need at least SuiteSparse 4.3
if(SuiteSparse_SPQR_FOUND)
  include(CheckCSourceCompiles)
  include(CMakePushCheckState)
  cmake_push_check_state()
  set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${SUITESPARSE_INCLUDE_DIR})
  set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${SUITESPARSE_LIBRARY})
  # check whether version is at least 4.3
  check_c_source_compiles("
    #include <SuiteSparse_config.h>
    int main(void)
    {
      #ifndef SUITESPARSE_HAS_VERSION_FUNCTION
        #error SuiteSparse <= 4.2.0 too old, required version 4.3 or newer for SPQR.
      #endif
      #if SUITESPARSE_VERSION <= 4003
        #error SuiteSparse too old, required version 4.3 or newer for SPQR.
      #endif
      return 0;
    }"
    SUITESPARSE_MIN_VERSION_4_3)

  if(NOT SUITESPARSE_MIN_VERSION_4_3)
    set(SuiteSparse_SPQR_FOUND FALSE)
    set(HAVE_SUITESPARSE_SPQR FALSE)
  endif()
  cmake_pop_check_state()
endif()

list(APPEND SUITESPARSE_LIBRARY ${SUITESPARSE_CONFIG_LIB})

# make them unique
if(SUITESPARSE_INCLUDE_DIR)
  list(REMOVE_DUPLICATES SUITESPARSE_INCLUDE_DIR)
endif()
if(SUITESPARSE_LIBRARY)
  list(REVERSE SUITESPARSE_LIBRARY)
  list(REMOVE_DUPLICATES SUITESPARSE_LIBRARY)
  list(REVERSE SUITESPARSE_LIBRARY)
endif()

# behave like a CMake module is supposed to behave
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
  "SuiteSparse"
  FOUND_VAR SuiteSparse_FOUND
  REQUIRED_VARS
  BLAS_FOUND
  SUITESPARSE_INCLUDE_DIR
  SUITESPARSE_LIBRARY
  HANDLE_COMPONENTS
)

mark_as_advanced(
  SUITESPARSE_INCLUDE_DIR
  SUITESPARSE_LIBRARY
  SUITESPARSE_CONFIG_LIB
  SUITESPARSE_MIN_VERSION_4_3
  WILL_USE_CHOLMOD
  WILL_USE_UMFPACK)

# if both headers and library are found, store results
if(SuiteSparse_FOUND)
  set(SuiteSparse_LIBRARIES ${SUITESPARSE_LIBRARY})
  set(SuiteSparse_INCLUDE_DIRS ${SUITESPARSE_INCLUDE_DIR})
  # log result
  file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
    "Determining location of SuiteSparse succeeded:\n"
    "Include directory: ${SuiteSparse_INCLUDE_DIRS}\n"
    "Library directory: ${SuiteSparse_LIBRARIES}\n\n")
  set(SuiteSparse_COMPILER_FLAGS)
  foreach(dir ${SuiteSparse_INCLUDE_DIRS})
    set(SuiteSparse_COMPILER_FLAGS "${SuiteSparse_COMPILER_FLAGS} -I${dir}/")
  endforeach()
else()
  # log errornous result
  file(APPEND ${CMAKE_BINARY_DIR}${CMAKES_FILES_DIRECTORY}/CMakeError.log
    "Determing location of SuiteSparse failed:\n"
    "Include directory: ${SuiteSparse_INCLUDE_DIRS}\n"
    "Library directory: ${SuiteSparse_LIBRARIES}\n\n")
endif()

#set HAVE_SUITESPARSE for config.h
set(HAVE_SUITESPARSE ${SuiteSparse_FOUND})
set(HAVE_UMFPACK ${SuiteSparse_UMFPACK_FOUND})