Skip to content
Snippets Groups Projects
CMakeLists.txt 17.8 KiB
Newer Older
cmake_minimum_required(VERSION 3.18)

# --------------------------------------------------------------------------------------------------
# Imagefusion project and default build type
# --------------------------------------------------------------------------------------------------

project(imagefusion VERSION 0.0.1)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Release or Debug")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
    message("debug build")
# --------------------------------------------------------------------------------------------------
# Include some additional CMake modules
# --------------------------------------------------------------------------------------------------

Christof Kaufmann's avatar
Christof Kaufmann committed
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
include(TargetArch)           # architecture
include(MiscUtils)            # OS (is_symbol_defined)
include(CheckCXXCompilerFlag) # CHECK_CXX_COMPILER_FLAG
include(CheckCXXSymbolExists)
# --------------------------------------------------------------------------------------------------
# Fix RPATH
# --------------------------------------------------------------------------------------------------

# use, i.e. don't skip the full RPATH for the build tree
set(CMAKE_SKIP_BUILD_RPATH FALSE)
# when building, don't use the install RPATH already (but later on when installing)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
# the RPATH to be used when installing, but only if it's not a system directory
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
    set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
endif("${isSystemDir}" STREQUAL "-1")
# --------------------------------------------------------------------------------------------------
# Set source and header files
# --------------------------------------------------------------------------------------------------
include_directories(include)
include_directories(tests)
Christof Kaufmann's avatar
Christof Kaufmann committed
set(SOURCES_LIB
    src/optionparser.cpp
    src/spstfm.cpp
    src/spstfm_impl.cpp
# --------------------------------------------------------------------------------------------------
# General compiler settings
# --------------------------------------------------------------------------------------------------

# release implies -O3 -DNDEBUG (on gcc)
# debug implies -g (on gcc)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_compile_options(-Wall -Wextra -Wno-missing-braces)

CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()

if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
    add_compile_options(-LC_ALL=en_US.UTF-8)
endif()

set(CMAKE_POSITION_INDEPENDENT_CODE ON) # required for spdlog, since it is compiled as static lib, but linked into imagefusion, which is a dynamic lib.
add_compile_options(-pthread)
# set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) # Link Time Optimization (-flto -fno-fat-lto-objects) # does not work
check_cxx_symbol_exists(std::filesystem::path::preferred_separator filesystem HAS_FILESYSTEM_HEADER)
if(NOT HAS_FILESYSTEM_HEADER)
    add_definitions(-DNO_FS_SUPPORT)
else() # filesystem header is there, but might have to be linked
    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) # gcc and clang require to link explicitly against filesystem before version 9
        link_libraries(stdc++fs)
    endif()
# --------------------------------------------------------------------------------------------------
# Find external libs
# --------------------------------------------------------------------------------------------------

option(IMAGEFUSION_FIND_LIB "Try to find and use a preinstalled libimagefusion, instead of building it" ON)
if(DEFINED ENV{IMAGEFUSION_FIND_LIB})
    set(IMAGEFUSION_FIND_LIB $ENV{IMAGEFUSION_FIND_LIB})
endif()
if(IMAGEFUSION_FIND_LIB)
    find_package(Imagefusion QUIET)
    if(Imagefusion_FOUND)
        message(STATUS "Found Imagefusion: ${Imagefusion_CONFIG}, Lib: ${Imagefusion_LIBRARY}")
    endif()
endif()

# add site-packages to search path, since for python build, we prefer to use python dependencies... this is e. g. miniconda3/lib/python3.8/site-packages/pybind11/share/cmake/pybind11... does this help finding other packages??
if(SKBUILD)
    execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import pybind11; print(pybind11.get_cmake_dir())"
                    OUTPUT_VARIABLE _tmp_dir
                    OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ECHO STDOUT)
    # message("Searching also in ${_tmp_dir} for CMake files.")
    list(APPEND CMAKE_PREFIX_PATH "${_tmp_dir}")
# directly fetch pybind11, since distribution version (Ubuntu 20.04) is probably way too old for C++17
FetchContent_Declare(pybind11
                     GIT_REPOSITORY https://github.com/pybind/pybind11.git
find_package(OpenCV REQUIRED core imgproc)
include_directories(${OpenCV_INCLUDE_DIRS})
set(LIBS ${LIBS} ${OpenCV_LIBS})
#message(STATUS "Found OpenCV: ${OpenCV_INCLUDE_DIRS}, ${OpenCV_LIBS}")
find_package(GDAL REQUIRED) # custom cmake module in cmake/Modules
set(TARGETS ${TARGETS} "GDAL::GDAL")
find_package(Boost OPTIONAL_COMPONENTS unit_test_framework)
set(TARGETS ${TARGETS} "Boost::boost")       # include headers
find_package(Armadillo REQUIRED)
include_directories(${ARMADILLO_INCLUDE_DIRS})
set(LIBS ${LIBS} ${ARMADILLO_LIBRARIES})
#message(STATUS "Found Armadillo: ${ARMADILLO_INCLUDE_DIRS}, ${ARMADILLO_LIBRARIES}")
if(OPENMP_CXX_FOUND OR OPENMP_FOUND)
    set(TARGETS ${TARGETS} "OpenMP::OpenMP_CXX")
    add_definitions(-DWITH_OMP)
    set(SOURCES_TEST
        ${SOURCES_TEST}
        tests/parallelizer_test.cpp
find_package(spdlog QUIET)
    if(fmt_FOUND)
        set(SPDLOG_TEST_LIBS spdlog fmt)
    else()
        set(SPDLOG_TEST_LIBS spdlog)
    endif()

    # # This syntax requires CMake 3.25, which ships with Ubuntu 23.04 and Debian bookworm...
    # try_compile(SPDLOG_COMPILE_SUCCESS
    #             PROJECT "spdlogtest"
    #             SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/spdlog-compile-test")
    # # ...use older syntax for now.
    try_compile(SPDLOG_COMPILE_SUCCESS
                "${CMAKE_CURRENT_BINARY_DIR}/spdlog-compile-test"
                "${CMAKE_CURRENT_SOURCE_DIR}/cmake/spdlog-compile-test/main.cpp"
                LINK_LIBRARIES ${SPDLOG_TEST_LIBS}
                OUTPUT_VARIABLE TESTOUTPUT
                CXX_STANDARD ${CMAKE_CXX_STANDARD}
                CXX_STANDARD_REQUIRED ON)
endif()

if(spdlog_FOUND AND SPDLOG_COMPILE_SUCCESS)
    get_target_property(spdlog_LOCATION spdlog::spdlog IMPORTED_LOCATION_NONE)
    message(STATUS "Found spdlog v${spdlog_VERSION}: ${spdlog_LOCATION}")
    FetchContent_Declare(spdlog
                         GIT_REPOSITORY https://github.com/gabime/spdlog.git
    FetchContent_MakeAvailable(spdlog) # checks also for compiler options, so set them before!
set(TARGETS ${TARGETS} "spdlog::spdlog")
# --------------------------------------------------------------------------------------------------
# Output directories
# --------------------------------------------------------------------------------------------------

# for all targets except testimagefusion, testimagefusion outputs will be overwritten in tests/CMakeLists.txt
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)


# --------------------------------------------------------------------------------------------------
# Build targets
# --------------------------------------------------------------------------------------------------
# documentation targets (doc and docinternal)
find_package(Doxygen)
option(IMAGEFUSION_BUILD_DOC "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
if(IMAGEFUSION_BUILD_DOC)
    if(NOT DOXYGEN_FOUND)
        message(FATAL_ERROR "Doxygen is needed to build the documentation. If you do not want to build the documentation, just deselect the CMake option IMAGEFUSION_BUILD_DOC.")
    set(doxyfile ${CMAKE_CURRENT_SOURCE_DIR}/doc/doxyfile)
    set(doxyfile_internal ${CMAKE_CURRENT_SOURCE_DIR}/doc/doxyfileinternal)

    add_custom_target(doc
                      COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile}
                      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc
                      COMMENT "Generating API documentation with Doxygen"
                      VERBATIM)

    add_custom_target(docinternal
                      COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile_internal}
                      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc
                      COMMENT "Generating API documentation with Doxygen"
                      VERBATIM)
endif()
if(Boost_UNIT_TEST_FRAMEWORK_FOUND AND CMAKE_PROJECT_NAME STREQUAL imagefusion)
    # message("Found Boost unit_test_framework: ${Boost_INCLUDE_DIRS}, ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}")
    enable_testing()
    add_subdirectory(tests)
if(NOT Imagefusion_FOUND)
    add_library(imagefusion SHARED ${SOURCES_LIB})
    if(SKBUILD)
        # make lib relocateable, i. e. searches at install location for dependend libs, like libgdal.so even if copied in miniconda3/lib/
        set_target_properties(imagefusion PROPERTIES INSTALL_RPATH \$ORIGIN)
    endif()
    target_include_directories(imagefusion INTERFACE "$<INSTALL_INTERFACE:${GDAL_INCLUDE_DIR};include/imagefusion>")
    target_link_libraries(imagefusion PUBLIC ${TARGETS} ${LIBS})
    add_library(imagefusion::imagefusion ALIAS imagefusion)
# uninstall target (see https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake)
if(NOT TARGET uninstall)
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/uninstall.cmake.in"
                   "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
                   IMMEDIATE @ONLY)

    add_custom_target(uninstall
        COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()

# python interface target
option(IMAGEFUSION_BUILD_PYTHON_INTERFACE "Build the python interface (target 'imfupython') by default in target all" ON)
if(DEFINED ENV{IMAGEFUSION_BUILD_PYTHON_INTERFACE})
    set(IMAGEFUSION_BUILD_PYTHON_INTERFACE $ENV{IMAGEFUSION_BUILD_PYTHON_INTERFACE})
endif()
if(IMAGEFUSION_BUILD_PYTHON_INTERFACE)
    add_library(imfupython MODULE python/src/interface.cpp)
    set_target_properties(imfupython PROPERTIES PREFIX "")
    set_target_properties(imfupython PROPERTIES OUTPUT_NAME "_imagefusion")
    set_target_properties(imfupython PROPERTIES CXX_VISIBILITY_PRESET "hidden")
    if(SKBUILD)
        # make interface lib relocateable, i. e. searches relative to install location for libimagefusion.so, e. g. miniconda3/lib/libimagefusion.so is found from miniconda3/lib/python3.8/site-packages/imagefusion/
        set_target_properties(imfupython PROPERTIES INSTALL_RPATH \$ORIGIN/../../..)
    endif()
    target_link_libraries(imfupython PRIVATE pybind11::module
                                     PRIVATE imagefusion::imagefusion
                                     PRIVATE GDAL::GDAL)
# --------------------------------------------------------------------------------------------------
# Installation and packaging
# --------------------------------------------------------------------------------------------------

if(NOT DEFINED IMAGEFUSION_MASTER_PROJECT)
    if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
        set(IMAGEFUSION_MASTER_PROJECT ON)
    else()
        set(IMAGEFUSION_MASTER_PROJECT OFF)
    endif()
endif()

option(IMAGEFUSION_INSTALL "Generate the install target" ${IMAGEFUSION_MASTER_PROJECT})

if(IMAGEFUSION_INSTALL)
    # install documentation
    is_symbol_defined(symdef __linux__)
    if(symdef)
        # install directory structure
        if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/doc/html/)
            install(FILES doc/doc.html
                    DESTINATION share/doc/imagefusion
                    COMPONENT doc)
            install(DIRECTORY doc/html
                    DESTINATION share/doc/imagefusion
                    COMPONENT doc)
        endif()
    endif()
    is_symbol_defined(symdef _WIN32)
    if(symdef)
        if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/doc/html/")
            install(FILES doc/doc.html
                    DESTINATION doc
                    COMPONENT doc)
            install(DIRECTORY doc/html
                    DESTINATION doc
                    COMPONENT doc)
        endif()
    endif()
    is_symbol_defined(symdef __APPLE__)
    if(symdef)
        # FIXME: Don't know where macOS likes to have documentation
    endif()

    # install library and header
    if(NOT Imagefusion_FOUND)
        install(TARGETS imagefusion
                EXPORT ImagefusionTargets
                INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
                ARCHIVE  DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev
                LIBRARY  DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT lib
                RUNTIME  DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT lib)

        install(DIRECTORY include/
                DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
                COMPONENT dev
                FILES_MATCHING PATTERN "*.h")
    endif()

    # install python interface library into package dir (where also __init__.py resides)
    if(IMAGEFUSION_BUILD_PYTHON_INTERFACE)
        if(SKBUILD)
            # find relative python path, like './lib/python3.8/site-packages' (destination with pip as user), from: https://stackoverflow.com/a/40006251/2414411
            execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "if True:
                                     from distutils import sysconfig as sc
                                     print(sc.get_python_lib(prefix='.', plat_specific=True))" # the prefix '.' makes the difference
                            OUTPUT_VARIABLE PYTHON_SITE
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
        else()
            # find relative python path, like 'lib/python3/dist-packages' (destination for make install as root user), from: https://stackoverflow.com/a/40006251/2414411
            execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "if True:
                                     from distutils import sysconfig as sc
                                     print(sc.get_python_lib(prefix='', plat_specific=True))"
                            OUTPUT_VARIABLE PYTHON_SITE
                            OUTPUT_STRIP_TRAILING_WHITESPACE)

            # only required when not installing with pip
            install(FILES "python/__init__.py"
                    DESTINATION "${PYTHON_SITE}/${PROJECT_NAME}"
                    COMPONENT python)
        endif()

        install(TARGETS imfupython
                LIBRARY DESTINATION "${PYTHON_SITE}/${PROJECT_NAME}"
                COMPONENT python)
    endif()

    # Export CMake config file (for use in external projects)
    if(NOT Imagefusion_FOUND)
        include(CMakePackageConfigHelpers)
        write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/imagefusion/ImagefusionConfigVersion.cmake"
                                         VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}"
                                         COMPATIBILITY AnyNewerVersion
        )
        export(EXPORT ImagefusionTargets
               FILE "${CMAKE_CURRENT_BINARY_DIR}/imagefusion/ImagefusionTargets.cmake"
               NAMESPACE imagefusion::
        )
        configure_file(cmake/ImagefusionConfig.cmake
                       "${CMAKE_CURRENT_BINARY_DIR}/imagefusion/ImagefusionConfig.cmake"
                       COPYONLY
        )

        set(ConfigPackageLocation "${CMAKE_INSTALL_LIBDIR}/cmake/imagefusion")
        install(EXPORT ImagefusionTargets
                FILE ImagefusionTargets.cmake
                NAMESPACE imagefusion::
                DESTINATION ${ConfigPackageLocation}
                COMPONENT   dev
        )
        install(FILES cmake/ImagefusionConfig.cmake
                      "${CMAKE_CURRENT_BINARY_DIR}/imagefusion/ImagefusionConfigVersion.cmake"
                DESTINATION ${ConfigPackageLocation}
                COMPONENT   dev
        )
    endif()

    # packaging with CPack
    include(cmake/ImagefusionCPack.cmake)


# --------------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------------

if(SKBUILD)
    # make binaries relocateable, i. e. searches relative to install location for libimagefusion
    set(CMAKE_INSTALL_RPATH \$ORIGIN/../lib)
endif()

# must be added after the installation part
option(IMAGEFUSION_BUILD_UTILS "Build the utilities (starfm, imggeocrop, ...) by default in target all" ON)
if(DEFINED ENV{IMAGEFUSION_BUILD_UTILS})
    set(IMAGEFUSION_BUILD_UTILS $ENV{IMAGEFUSION_BUILD_UTILS})
endif()
if(IMAGEFUSION_BUILD_UTILS)
    add_subdirectory("utils")
endif()
# --------------------------------------------------------------------------------------------------
# Fix for QtCreator IDE, meaningless for CMake
# --------------------------------------------------------------------------------------------------

# if you add a header file run "cmake .." in build again to update QTCreator
file(GLOB_RECURSE HeaderFiles "include/*.h")
file(GLOB_RECURSE TestHeaderFiles "tests/*.h")
file(GLOB_RECURSE TestHeaderFiles "utils/*.h")
add_custom_target(headers SOURCES ${HeaderFiles} ${TestHeaderFiles})