Skip to content
Snippets Groups Projects
Select Git revision
  • feature/serraille
  • feature/reconstruction
  • feature/variational-hydro
  • feature/composite-scheme-sources
  • feature/composite-scheme-other-fluxes
  • feature/composite-scheme
  • develop default protected
  • hyperplastic
  • feature/local-dt-fsi
  • feature/kinetic-schemes
  • feature/polynomials
  • feature/gks
  • feature/implicit-solver-o2
  • feature/coupling_module
  • feature/implicit-solver
  • feature/merge-local-dt-fsi
  • master protected
  • feature/escobar-smoother
  • feature/hypoelasticity-clean
  • feature/hypoelasticity
  • v0.5.0 protected
  • v0.4.1 protected
  • v0.4.0 protected
  • v0.3.0 protected
  • v0.2.0 protected
  • v0.1.0 protected
  • Kidder
  • v0.0.4 protected
  • v0.0.3 protected
  • v0.0.2 protected
  • v0 protected
  • v0.0.1 protected
32 results

CMakeLists.txt

Blame
  • CMakeLists.txt 26.22 KiB
    cmake_minimum_required (VERSION 3.19)
    
    # CMake utils
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
    
    # Forbids in-source builds
    include(CheckNotInSources)
    
    # use PkgConfig to find packages
    find_package(PkgConfig REQUIRED)
    
    #------------------------------------------------------
    #----------------- Main configuration -----------------
    #------------------------------------------------------
    
    # custom variable allowing to define version suffixes such as -rc*, -beta*, ...
    set(PUGS_VERSION "0.5.0")
    
    # deduce PUGS_SHORT_VERSION using regex
    string(REGEX MATCH "^[0-9]+\.[0-9]+\.[0-9]+" PUGS_SHORT_VERSION ${PUGS_VERSION})
    if("${PUGS_SHORT_VERSION}" STREQUAL "")
      message(FATAL_ERROR "Unable to compute short version from PUGS_VERSION=${PUGS_VERSION}")
    endif()
    
    # set project version as PUGS_SHORT_VERSION
    project (Pugs VERSION ${PUGS_SHORT_VERSION})
    
    # -----------------------------------------------------
    # dynamic libraries
    
    set(CMAKE_POSITION_INDEPENDENT_CODE ON)
    link_libraries("-rdynamic")
    set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
    
    #------------------------------------------------------
    
    set(CMAKE_CONFIGURATION_TYPES "Release;Debug;Coverage" CACHE STRING INTERNAL FORCE )
    
    #------------------------------------------------------
    
    set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
    
    #------------------------------------------------------
    
    set(PUGS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
    set(PUGS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
    
    # Add new build types
    set(CMAKE_CXX_FLAGS_COVERAGE
      "-g -O0 --coverage"
      CACHE STRING "Flags used by the C++ compiler during coverage builds."
      FORCE )
    set(CMAKE_C_FLAGS_COVERAGE
      "-g -O0 --coverage"
      CACHE STRING "Flags used by the C compiler during coverage builds."
      FORCE )
    set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
      "--coverage"
      CACHE STRING "Flags used for linking binaries during coverage builds."
      FORCE )
    set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
      "--coverage"
      CACHE STRING "Flags used by the shared libraries linker during coverage builds."
      FORCE )
    mark_as_advanced(
      CMAKE_CXX_FLAGS_COVERAGE
      CMAKE_C_FLAGS_COVERAGE
      CMAKE_EXE_LINKER_FLAGS_COVERAGE
      CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
    
    if(CMAKE_BUILD_TYPE)
      string(REGEX MATCH "^(Release|Debug|Coverage)$" VALID_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
      if(NOT VALID_BUILD_TYPE)
        message(FATAL_ERROR "Invalid CMAKE_BUILD_TYPE: '${CMAKE_BUILD_TYPE}'")
      endif()
    endif()
    
    # Default build type is Release
    if(NOT CMAKE_BUILD_TYPE)
      set(CMAKE_BUILD_TYPE "Release" CACHE STRING
          "Choose the type of build: Release Debug Coverage."
          FORCE)
    endif()
    
    #------------------------------------------------------
    # default build shared libraries
    if (NOT BUILD_SHARED_LIBS)
      set(BUILD_SHARED_LIBS ON CACHE STRING "" FORCE)
    endif()
    
    #------------------------------------------------------
    
    # Checks if compiler version is compatible with Pugs sources
    set(GNU_CXX_MIN_VERSION "10.0.0")
    set(CLANG_CXX_MIN_VERSION "11.0.0")
    
    #------------------------------------------------------
    # Change Kokkos namespace to avoid conflicts
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DKokkos=InlineKokkos")
    
    #------------------------------------------------------
    # Pugs default compiler flags
    set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS} -Wall -Wextra -pedantic -Wshadow -Wsign-compare -Wunused")
    
    if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
      if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${GNU_CXX_MIN_VERSION}")
        message(FATAL_ERROR "Pugs build requires at least g++-${GNU_CXX_MIN_VERSION}")
      endif()
    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
      if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${CLANG_CXX_MIN_VERSION}")
        message(FATAL_ERROR "Pugs build requires at least llvm/clang ++-${CLANG_CXX_MIN_VERSION}")
      endif()
      set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS}  -Wunused-member-function -Wunused-private-field")
    endif()
    
    #------------------------------------------------------
    
    include (TestBigEndian)
    TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
    if(IS_BIG_ENDIAN)
      set(PUGS_LITTLE_ENDIAN FALSE)
    else()
      set(PUGS_LITTLE_ENDIAN TRUE)
    endif()
    
    #------------------------------------------------------
    # defaults use of MPI
    set(PUGS_ENABLE_MPI AUTO CACHE STRING
      "Choose one of: AUTO ON OFF")
    
    if (NOT PUGS_ENABLE_MPI MATCHES "^(AUTO|ON|OFF)$")
      message(FATAL_ERROR "PUGS_ENABLE_MPI='${PUGS_ENABLE_MPI}'. Must be set to one of AUTO, ON or OFF")
    endif()
    
    # Check for MPI
    if (PUGS_ENABLE_MPI MATCHES "^(AUTO|ON)$")
      set(MPI_DETERMINE_LIBRARY_VERSION TRUE)
      find_package(MPI)
    endif()
    
    #------------------------------------------------------
    # Search for ParMETIS
    
    find_package(ParMETIS)
    if(PARMETIS_LIBRARIES)
      add_library(PkgConfig::ParMETIS STATIC IMPORTED)
      set_property(TARGET PkgConfig::ParMETIS PROPERTY
        IMPORTED_LOCATION "${PARMETIS_LIBRARIES}")
    
      set(PARMETIS_TARGET PkgConfig::ParMETIS)
    endif()
    
    if(${MPI_FOUND})
      if (NOT PARMETIS_LIBRARIES)
        if(PUGS_ENABLE_MPI MATCHES "^AUTO$")
          message(STATUS "MPI support deactivated: ParMETIS cannot be found!")
          unset(MPI_FOUND)
          unset(MPI_CXX_LINK_FLAGS)
          unset(MPI_CXX_LIBRARIES)
        else()
          message(FATAL_ERROR "MPI support requires ParMETIS which cannot be found!")
        endif()
      endif()
    else()
      if(PUGS_ENABLE_MPI MATCHES "^ON$")
        message(FATAL_ERROR "Cannot find MPI!")
      endif()
    endif()
    
    #------------------------------------------------------
    # Check for PETSc
    # defaults use PETSc
    set(PUGS_ENABLE_PETSC AUTO CACHE STRING
      "Choose one of: AUTO ON OFF")
    
    if (PUGS_ENABLE_PETSC MATCHES "^(AUTO|ON)$")
      if (MPI_FOUND)
        # PETSc support is deactivated if MPI is not found
        pkg_check_modules(PETSC IMPORTED_TARGET GLOBAL PETSc)
    
        set_property(TARGET PkgConfig::PETSC PROPERTY
          IMPORTED_LOCATION "${PETSC_LIBRARIES}"
        )
        set_property(TARGET PkgConfig::PETSC PROPERTY
          INTERFACE_INCLUDE_DIRECTORIES "${PETSC_INCLUDE_DIRS}"
        )
    
        set(PETSC_TARGET PkgConfig::PETSC)
      else()
        message(STATUS "PETSc support is deactivated since pugs will not be build with MPI support")
        set(PETSC_FOUND FALSE)
        unset(PUGS_HAS_PETSC)
      endif()
      set(PUGS_HAS_PETSC ${PETSC_FOUND})
    else()
      unset(PUGS_HAS_PETSC)
    endif()
    
    if (${PETSC_FOUND})
      include_directories(SYSTEM "${PETSC_INCLUDE_DIRS}")
    else()
      if (PUGS_ENABLE_PETSC MATCHES "^ON$")
        message(FATAL_ERROR "Could not find PETSc!")
      endif()
    endif()
    
    #------------------------------------------------------
    # Check for SLEPc
    # defaults use SLEPc
    set(PUGS_ENABLE_SLEPC AUTO CACHE STRING
      "Choose one of: AUTO ON OFF")
    
    if (PUGS_ENABLE_SLEPC MATCHES "^(AUTO|ON)$")
      if (PETSC_FOUND)
        # SLEPc support is deactivated if PETSc is not found
        pkg_check_modules(SLEPC IMPORTED_TARGET GLOBAL SLEPc)
    
        set_property(TARGET PkgConfig::SLEPC PROPERTY
          IMPORTED_LOCATION "${SLEPC_LIBRARIES}"
        )
        set_property(TARGET PkgConfig::SLEPC PROPERTY
          INTERFACE_INCLUDE_DIRECTORIES "${SLEPC_INCLUDE_DIRS}"
        )
    
        set(SLEPC_TARGET PkgConfig::SLEPC)
      else()
        message(STATUS "SLEPc support is deactivated since pugs will not be build with PETSc support")
        set(SLEPC_FOUND FALSE)
        unset(PUGS_HAS_SLEPC)
      endif()
      set(PUGS_HAS_SLEPC ${SLEPC_FOUND})
    else()
      unset(PUGS_HAS_SLEPC)
    endif()
    
    if (${SLEPC_FOUND})
      include_directories(SYSTEM "${SLEPC_INCLUDE_DIRS}")
    else()
      if (PUGS_ENABLE_SLEPC MATCHES "^ON$")
        message(FATAL_ERROR "Could not find SLEPc!")
      endif()
    endif()
    
    # -----------------------------------------------------
    
    if (${MPI_FOUND})
      set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS} ${MPI_CXX_COMPILER_FLAGS}")
      include_directories(SYSTEM "${MPI_CXX_INCLUDE_DIRS}")
    elseif(PUGS_ENABLE_MPI STREQUAL "ON")
      message(FATAL_ERROR "Could not find MPI library while requested")
    endif()
    
    set(PUGS_HAS_MPI ${MPI_FOUND})
    
    #------------------------------------------------------
    # search for HDF5
    
    set(PUGS_ENABLE_HDF5 AUTO CACHE STRING
      "Choose one of: AUTO ON OFF")
    
    if (PUGS_ENABLE_HDF5 MATCHES "^(AUTO|ON)$")
      if (MPI_FOUND)
        # May be risky. (make to show pugs build options)
        set(HDF5_PREFER_PARALLEL TRUE)
        find_package(HDF5)
        if (HDF5_FOUND)
          # HighFive
          set(HIGHFIVE_USE_BOOST  OFF)   # no Boost
          set(HIGHFIVE_BUILD_DOCS OFF)   # no doc
          set(HIGHFIVE_UNIT_TESTS OFF)   # no unit tests
          set(HIGHFIVE_UNIT_TESTS OFF)   # no unit tests
          set(HIGHFIVE_EXAMPLES OFF)     # no examples
          add_subdirectory(${PUGS_SOURCE_DIR}/packages/HighFive/)
          set(HIGHFIVE_TARGET HighFive::HighFive)
    
          add_library(PkgConfig::HDF5 STATIC IMPORTED)
          set_property(TARGET PkgConfig::HDF5 PROPERTY
    	IMPORTED_LOCATION "${HDF5_LIBRARIES}")
    
          set_property(TARGET PkgConfig::HDF5 PROPERTY
    	INTERFACE_INCLUDE_DIRECTORIES "${HDF5_INCLUDE_DIRS}")
    
          set(HDF5_TARGET PkgConfig::HDF5)
        endif()
      else()
        message(STATUS "HDF5 support is deactivated since pugs will not be build with MPI support")
        set(HDF5_FOUND FALSE)
      endif()
      set(PUGS_HAS_HDF5 ${HDF5_FOUND})
    else()
      unset(HIGHFIVE_TARGET)
      unset(HDF5_TARGET)
      unset(PUGS_HAS_HDF5)
    endif()
    
    #------------------------------------------------------
    # search for libslurm
    
    find_package(Slurm)
    
    set(PUGS_HAS_SLURM ${SLURM_FOUND})
    
    if (SLURM_FOUND)
      add_library(PkgConfig::Slurm STATIC IMPORTED)
      set_property(TARGET PkgConfig::Slurm PROPERTY
        IMPORTED_LOCATION "${SLURM_LIBRARY}")
      set_property(TARGET PkgConfig::Slurm PROPERTY
        INTERFACE_INCLUDE_DIRECTORIES "${SLURM_INCLUDE_DIR}")
    
      set(SLURM_TARGET PkgConfig::Slurm)
      include_directories(SYSTEM "${SLURM_INCLUDE_DIR}")
    else()
      set(SLURM_LIBRARY "")
    endif()
    
    #------------------------------------------------------
    # search for clang-format
    
    find_program(CLANG_FORMAT clang-format)
    if (CLANG_FORMAT)
      add_custom_target(clang-format
        COMMAND ${CMAKE_COMMAND}
        -DPUGS_SOURCE_DIR="${PUGS_SOURCE_DIR}"
        -DCLANG_FORMAT="${CLANG_FORMAT}"
        -P ${PUGS_SOURCE_DIR}/cmake/ClangFormatProcess.cmake
        COMMENT "running ${CLANG_FORMAT} ...")
    else ()
      message(STATUS "clang-format no found!")
    endif()
    
    #------------------------------------------------------
    # search for clazy-standalone
    
    find_program(CLAZY_STANDALONE clazy-standalone)
    if (CLAZY_STANDALONE)
      add_custom_target(clazy-standalone
        COMMAND ${CMAKE_COMMAND}
        -DPUGS_SOURCE_DIR="${PUGS_SOURCE_DIR}"
        -DPUGS_BINARY_DIR="${PUGS_BINARY_DIR}"
        -DCLAZY_STANDALONE="${CLAZY_STANDALONE}"
        -P ${PUGS_SOURCE_DIR}/cmake/ClazyStandaloneProcess.cmake
        COMMENT "running ${CLAZY_STANDALONE} ..."
        )
    else ()
      message(STATUS "clazy-standalone no found!")
    endif()
    
    #------------------------------------------------------
    # C++ 20 flags
    set(CMAKE_CXX_STANDARD "20")
    
    #------------------------------------------------------
    # Kokkos configuration
    
    set(KOKKOS_SOURCE_DIR "${PUGS_SOURCE_DIR}/packages/kokkos")
    set(KOKKOS_BINARY_DIR "${PUGS_BINARY_DIR}/packages/kokkos")
    
    # disable Kokkos deprecation warnings by default
    set(Kokkos_ENABLE_DEPRECATION_WARNINGS OFF CACHE BOOL "")
    
    # setting Kokkos defaults to OpenMP when available
    find_package(OpenMP)
    if(OpenMP_CXX_FOUND)
      set(Kokkos_ENABLE_OPENMP ON CACHE BOOL "")
    endif()
    
    if("${Kokkos_ENABLE_OPENMP}" STREQUAL "ON")
      set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
      set(OPENMP_LINK_FLAGS OpenMP::OpenMP_CXX)
    endif()
    
    add_subdirectory("${PUGS_SOURCE_DIR}/packages/kokkos")
    
    # set as SYSTEM for static analysis
    include_directories(SYSTEM "${KOKKOS_SOURCE_DIR}/core/src")
    include_directories(SYSTEM "${KOKKOS_SOURCE_DIR}/containers/src")
    include_directories(SYSTEM "${KOKKOS_SOURCE_DIR}/tpls/desul/include")
    include_directories(SYSTEM "${KOKKOS_BINARY_DIR}/core/src")
    include_directories(SYSTEM "${KOKKOS_BINARY_DIR}")
    
    set(PUGS_BUILD_KOKKOS_DEVICES "")
    if(${Kokkos_ENABLE_PTHREAD})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "Threads")
    endif()
    
    if(${Kokkos_ENABLE_CUDA})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "CUDA")
    endif()
    
    if(${Kokkos_ENABLE_QTHREADS})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "QThreads")
    endif()
    
    if(${Kokkos_ENABLE_HPX})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "HPX")
    endif()
    
    if(${Kokkos_ENABLE_OPENMP})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "OpenMP")
    endif()
    
    if(${Kokkos_ENABLE_OPENMPTARGET})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "OpenMPTarget")
    endif()
    
    if(${Kokkos_ENABLE_HWLOC})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "HWLoc")
    endif()
    
    if(${Kokkos_ENABLE_MPI})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "Kokkos/MPI")
    endif()
    
    if(${Kokkos_ENABLE_CUDA_UVM})
      list(APPEND PUGS_BUILD_KOKKOS_DEVICES "CUDA UVM")
    endif()
    
    #------------------------------------------------------
    # Compiler flags
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PUGS_CXX_FLAGS}")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${PUGS_CXX_FLAGS}")
    
    
    message(STATUS "CMAKE_CXX_FLAGS = ${CMAKE_CXX_FLAGS}")
    
    
    # Add debug mode for Standard C++ library (not for AppleClang since it is broken)
    if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
      set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_LIBCPP_DEBUG=1")
    endif()
    
    #------------------------------------------------------
    
    # Rang (colors? Useless thus necessary!)
    add_subdirectory(${PUGS_SOURCE_DIR}/packages/rang/)
    include_directories(SYSTEM "${PUGS_SOURCE_DIR}/packages/rang/include")
    
    # CLI11
    include_directories(SYSTEM "${PUGS_SOURCE_DIR}/packages/CLI11/include")
    
    # PEGTL
    add_subdirectory(${PUGS_SOURCE_DIR}/packages/PEGTL/)
    include_directories(SYSTEM "${PUGS_SOURCE_DIR}/packages/PEGTL/include/tao")
    
    # Pugs src
    add_subdirectory("${PUGS_SOURCE_DIR}/src")
    include_directories("${PUGS_SOURCE_DIR}/src")
    include_directories("${PUGS_BINARY_DIR}/src")
    
    # Pugs tests
    set(CATCH_MODULE_PATH "${PUGS_SOURCE_DIR}/packages/Catch2")
    add_subdirectory("${CATCH_MODULE_PATH}")
    target_compile_options(Catch2 PRIVATE "-w")
    target_compile_options(Catch2WithMain PRIVATE "-w")
    
    add_subdirectory(tests)
    
    if(${PUGS_HAS_MPI})
      set(MPIEXEC_OPTION_FLAGS --oversubscribe)
      if (NOT "$ENV{GITLAB_CI}" STREQUAL "")
        set(MPIEXEC_OPTION_FLAGS ${MPIEXEC_OPTION_FLAGS} --allow-run-as-root)
      endif()
      set(MPIEXEC_NUMPROC 3)
      set(MPIEXEC_PATH_FLAG --path)
      set(MPI_LAUNCHER ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC} ${MPIEXEC_OPTION_FLAGS})
    endif()
    
    add_custom_target(all_unit_tests
      DEPENDS unit_tests mpi_unit_tests
    )
    
    add_custom_target(check
      DEPENDS test
      )
    
    add_custom_target(test
      DEPENDS run_all_unit_tests
      )
    
    add_custom_target(run_all_unit_tests
      DEPENDS run_mpi_unit_tests
      )
    
    if(PUGS_HAS_MPI)
      set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [using ${MPIEXEC_NUMPROC} procs]")
    else()
      set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [sequentially]")
    endif()
    
    add_custom_target(run_mpi_unit_tests
      COMMAND ${MPI_LAUNCHER} "${PUGS_BINARY_DIR}/mpi_unit_tests"
      WORKING_DIRECTORY ${PUGS_BINARY_DIR}
      DEPENDS run_unit_tests
      COMMENT ${RUN_MPI_UNIT_TESTS_COMMENT}
      )
    
    add_custom_target(run_unit_tests
      COMMAND "${PUGS_BINARY_DIR}/unit_tests"
      DEPENDS all_unit_tests
      COMMENT "Running unit_tests"
      )
    
    # unit tests coverage
    
    if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")
      file(GLOB_RECURSE GCNO_FILE_LIST RELATIVE "${PUGS_BINARY_DIR}"  "*.gcno")
      list(FILTER GCNO_FILE_LIST EXCLUDE REGEX "^\.\./.*" )
      file(GLOB_RECURSE GCDA_FILE_LIST RELATIVE "${PUGS_BINARY_DIR}"  "*.gcda")
      list(FILTER GCDA_FILE_LIST EXCLUDE REGEX "^\.\./.*" )
      foreach(COV_INFO_FILE IN LISTS GCNO_FILE_LIST GCDA_FILE_LIST)
        string(REGEX REPLACE "/CMakeFiles/.*\.dir/" "/" COV_SRC_FILE "${PUGS_SOURCE_DIR}/${COV_INFO_FILE}")
        string(REGEX REPLACE "\.gcda$" "" COV_SRC_FILE "${COV_SRC_FILE}")
        string(REGEX REPLACE "\.gcno$" "" COV_SRC_FILE "${COV_SRC_FILE}")
        if (NOT EXISTS "${COV_SRC_FILE}")
          file(REMOVE "${PUGS_BINARY_DIR}/${COV_INFO_FILE}")
          message(STATUS "removed file ${COV_INFO_FILE}: no longer needed")
        endif()
      endforeach()
    
      find_program(LCOV lcov)
      if(NOT LCOV)
        message(FATAL_ERROR "lcov not found, cannot perform coverage.")
      endif()
    
      if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
        string(REGEX MATCH "^[0-9]+" GCC_VERSION
          "${CMAKE_CXX_COMPILER_VERSION}")
        find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov
          HINTS ${COMPILER_PATH})
      elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
        string(REGEX MATCH "^[0-9]+" LLVM_VERSION
          "${CMAKE_CXX_COMPILER_VERSION}")
    
        if(LLVM_VERSION VERSION_GREATER 7)
          find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}"
            "llvm-cov" HINTS ${COMPILER_PATH})
          mark_as_advanced(LLVM_COV_BIN)
    
          if (LLVM_COV_BIN)
            file(MAKE_DIRECTORY "${PUGS_BINARY_DIR}/tools/tmp")
    
            file(WRITE "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh" "#! /bin/sh\n")
            file(APPEND "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh" "'${LLVM_COV_BIN}' gcov \"\$@\"\n")
    
            file(COPY "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh"
                 DESTINATION "${PUGS_BINARY_DIR}/tools"
                 FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
    
            set(GCOV_BIN "${PUGS_BINARY_DIR}/tools/llvm-cov.sh")
          endif()
        endif()
      endif()
    
      if(NOT GCOV_BIN)
         message(FATAL_ERROR "Cannot find a proper gcov tool, cannot perform coverage.")
      endif()
    
      find_program(FASTCOV fastcov fastcov.py)
    
      add_custom_target(coverage_unit_tests
        ALL # in coverage mode we do coverage!
    
        COMMAND "${PUGS_BINARY_DIR}/unit_tests"
    
        COMMENT "Running unit_tests"
        DEPENDS coverage_zero_counters
        )
    
      add_custom_target(coverage_mpi_unit_tests
        ALL # in coverage mode we do coverage!
    
        COMMAND ${MPI_LAUNCHER} "mpi_unit_tests"
    
        COMMENT "Running mpi_unit_tests"
        DEPENDS coverage_unit_tests
        )
    
      add_custom_target(coverage_run_all_unit_tests
        ALL # in coverage mode we do coverage!
    
        DEPENDS coverage_mpi_unit_tests
        )
    
      if (FASTCOV AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "9"))
    
        add_custom_target(coverage_zero_counters
          ALL # in coverage mode we do coverage!
    
          # zero all counters
          COMMAND ${FASTCOV} -q -z --gcov "${GCOV_BIN}"
          DEPENDS all_unit_tests
          )
    
        add_custom_target(coverage
          ALL # in coverage mode we do coverage!
    
          COMMAND ${FASTCOV} --gcov "${GCOV_BIN}"
          --include "${PUGS_SOURCE_DIR}/src"
          --exclude "${PUGS_SOURCE_DIR}/src/main.cpp" "${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.*" "${PUGS_SOURCE_DIR}/src/utils/FPEManager.*" "${PUGS_SOURCE_DIR}/src/utils/SignalManager.*" "${PUGS_SOURCE_DIR}/src/utils/checkpointing/pugs_checkpoint_main.cpp"
          --lcov -o coverage.info -n
    
          COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info
    
          DEPENDS coverage_run_all_unit_tests
          COMMENT "Running test coverage."
          WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
          )
    
      else()
    
        add_custom_target(coverage_zero_counters
          ALL # in coverage mode we do coverage!
          # Cleanup previously generated profiling data
          COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --zerocounters
          # Initialize profiling data with zero coverage for every instrumented line of the project
          # This way the percentage of total lines covered will always be correct, even when not all source code files were loaded during the test(s)
          COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --initial --output-file coverage_base.info
          DEPENDS all_unit_tests
          )
    
    
        add_custom_target(coverage
          # Collect data from executions
          COMMAND ${LCOV} --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --output-file coverage_ctest.info
          # Combine base and ctest results
          COMMAND ${LCOV} --gcov "${GCOV_BIN}" -q --add-tracefile coverage_base.info --add-tracefile coverage_ctest.info --output-file coverage_full.info
          # Extract only project data (--no-capture or --remove options may be used to select collected data)
          COMMAND ${LCOV} --gcov "${GCOV_BIN}" -q --extract coverage_full.info "'${PUGS_SOURCE_DIR}/src/*'" --output-file coverage_extract.info
    
          # Remove unwanted stuff
          COMMAND ${LCOV} --gcov "${GCOV_BIN}" --remove coverage_extract.info  --output-file coverage.info
          '${PUGS_SOURCE_DIR}/src/main.cpp'
          '${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.*'
    
          COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info
    
          DEPENDS coverage_run_all_unit_tests
          COMMENT "Running test coverage."
          WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
          )
      endif()
    
      find_program(GENHTML genhtml)
      if(NOT GENHTML)
        message(WARNING "genhtml not found, cannot perform report-coverage.")
      else()
        add_custom_target(coverage-report
          ALL
          COMMAND ${CMAKE_COMMAND} -E remove_directory "${PUGS_BINARY_DIR}/coverage"
          COMMAND ${CMAKE_COMMAND} -E make_directory "${PUGS_BINARY_DIR}/coverage"
          COMMAND ${GENHTML} --demangle-cpp -q -o coverage -t "${CMAKE_PROJECT_NAME} test coverage" --ignore-errors source --legend --num-spaces 2 coverage.info
          DEPENDS coverage
          COMMENT "Building coverage html report."
          WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
          )
      endif()
    endif()
    
    # ------------------- Source files --------------------
    # Pugs binary
    add_executable(
      pugs
      src/main.cpp)
    
    # Libraries
    target_link_libraries(
      pugs
      PugsMesh
      PugsAlgebra
      PugsAnalysis
      PugsCheckpointing
      PugsDev
      PugsUtils
      PugsLanguage
      PugsLanguageAST
      PugsLanguageModules
      PugsLanguageAlgorithms
      PugsMesh
      PugsAlgebra
      PugsScheme
      PugsUtils
      PugsOutput
      PugsLanguageUtils
      PugsCheckpointing
      Kokkos::kokkos
      ${PETSC_TARGET}
      ${SLEPC_TARGET}
      ${PARMETIS_TARGET}
      ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES}
      ${KOKKOS_CXX_FLAGS}
      ${OPENMP_LINK_FLAGS}
      ${PUGS_STD_LINK_FLAGS}
      ${HIGHFIVE_TARGET}
      ${SLURM_TARGET}
      stdc++fs
    )
    
    target_include_directories(pugs PUBLIC ${PETSC_INCLUDE_DIRS})
    target_include_directories(pugs PUBLIC ${HDF5_INCLUDE_DIRS})
    
    # Checkpoint management tool
    add_executable(
      pugs_checkpoint
      src/utils/checkpointing/pugs_checkpoint_main.cpp
      )
    
    target_link_libraries(
      pugs_checkpoint
      PugsCheckpointing
      PugsUtils
      PugsMesh
      PugsOutput
      PugsLanguage
      PugsLanguageAST
      PugsLanguageModules
      PugsLanguageUtils
      PugsScheme
      PugsDev
      PugsAnalysis
      PugsAlgebra
      PugsMesh
      PugsOutput
      Kokkos::kokkos
      ${PETSC_TARGET}
      ${SLEPC_TARGET}
      ${PARMETIS_TARGET}
      ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES}
      ${KOKKOS_CXX_FLAGS}
      ${OPENMP_LINK_FLAGS}
      ${PUGS_STD_LINK_FLAGS}
      ${HIGHFIVE_TARGET}
      ${SLURM_TARGET}
      stdc++fs
      )
    
    # -------------------- Documentation --------------------
    
    include(PugsDoc)
    
    # -------------------- Doxygen --------------------------
    
    include(PugsDoxygen)
    
    # -------------------- Installation ---------------------
    install(TARGETS
      pugs
      pugs_checkpoint
      PugsAlgebra
      PugsAnalysis
      PugsCheckpointing
      PugsDev
      PugsUtils
      PugsLanguage
      PugsLanguageAST
      PugsLanguageModules
      PugsLanguageUtils
      PugsMesh
      PugsOutput
      PugsScheme
      kokkos
      Catch2
    
      EXPORT "${PROJECT_NAME}Targets"
    
      RUNTIME DESTINATION bin
      LIBRARY DESTINATION lib
      ARCHIVE DESTINATION lib
    )
    
    include(CMakePackageConfigHelpers)
    
    write_basic_package_version_file(
      PugsConfigVersion.cmake
      VERSION ${PACKAGE_VERSION}
      COMPATIBILITY AnyNewerVersion
    )
    
    install(EXPORT PugsTargets
      FILE PugsTargets.cmake
      NAMESPACE Pugs::
      DESTINATION lib/cmake/pugs
    )
    
    # ------------------- Build options -------------------
    message("")
    message("====== pugs build options ======")
    message(" version: ${PUGS_VERSION}")
    message(" build type: ${CMAKE_BUILD_TYPE}")
    message(" compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
    message(" kokkos devices: ${PUGS_BUILD_KOKKOS_DEVICES}")
    
    if (MPI_FOUND)
      message(" MPI: ${MPI_CXX_LIBRARY_VERSION_STRING}")
    else()
      if (PUGS_ENABLE_MPI MATCHES "^OFF$")
        message(" MPI: deactivated!")
      elseif(NOT PARMETIS_LIBRARIES)
        message(" MPI: deactivated: ParMETIS cannot be found!")
      else()
        if (PUGS_ENABLE_MPI MATCHES "^(AUTO|ON)$")
          message(" MPI: not found!")
        else()
          message(" MPI: explicitly deactivated!")
        endif()
      endif()
    endif()
    
    if (PETSC_FOUND)
      message(" PETSc: ${PETSC_VERSION}")
    else()
      if (PUGS_ENABLE_PETSC MATCHES "^(AUTO|ON)$")
        message(" PETSc: not found!")
      else()
          message(" PETSc: explicitly deactivated!")
      endif()
    endif()
    
    if (SLEPC_FOUND)
      message(" SLEPc: ${SLEPC_VERSION}")
    else()
      if (PUGS_ENABLE_SLEPC MATCHES "^(AUTO|ON)$")
        message(" SLEPc: not found!")
      else()
          message(" SLEPc: explicitly deactivated!")
      endif()
    endif()
    
    if (HDF5_FOUND)
      message(" HDF5: ${HDF5_VERSION} parallel: ${HDF5_IS_PARALLEL}")
    else()
      if (PUGS_ENABLE_HDF5 MATCHES "^(AUTO|ON)$")
        message(" HDF5: not found!")
      else()
          message(" HDF5: explicitly deactivated!")
      endif()
    endif()
    
    if (SLURM_FOUND)
      message(" SLURM library: ${SLURM_LIBRARY}")
    else()
      message(" SLURM library: not found!")
    endif()
    
    message("----------- utilities ----------")
    
    if(CLANG_FORMAT)
      message(" clang-format: ${CLANG_FORMAT}")
    else()
      message(" clang-format: not found!")
    endif()
    
    if(CLAZY_STANDALONE)
      message(" clazy-standalone: ${CLAZY_STANDALONE}")
    else()
      message(" clazy-standalone: not found!")
    endif()
    
    if (DOXYGEN_FOUND)
      message(" doxygen: ${DOXYGEN_EXECUTABLE}")
    else()
      message(" doxygen: not found!")
    endif()
    
    if (EMACS)
      message(" emacs: ${EMACS}")
    else()
      message(" emacs: not found!")
    endif()
    
    if (GNUPLOT_FOUND)
      message(" gnuplot: ${GNUPLOT}")
    else()
      message(" gnuplot: not found!")
    endif()
    
    if (GMSH)
      message(" gmsh: ${GMSH}")
    else()
      message(" gmsh: not found!")
    endif()
    
    if (PYGMENTIZE)
      message(" pygmentize: ${PYGMENTIZE}")
    else()
      message(" pygmentize: not found!")
    endif()
    
    if (LATEX_PDFLATEX_FOUND)
      message(" pdflatex: ${PDFLATEX_COMPILER}")
    else()
      message(" pdflatex: not found!")
    endif()
    
    if (NOT EMACS OR NOT GNUPLOT_FOUND OR NOT GMSH)
      message(" ** Cannot build documentation: missing ")
    elseif(NOT LATEX_PDFLATEX_FOUND OR NOT PYGMENTIZE)
      message(" ** Cannot build pdf documentation: missing")
    endif()
    if (NOT EMACS)
      message("    - emacs")
    endif()
    if (NOT GNUPLOT_FOUND)
      message("    - gnuplot")
    endif()
    if (NOT GMSH)
      message("    - gmsh")
    endif()
    if (NOT LATEX_PDFLATEX_FOUND)
      message("    - pdflatex")
    endif()
    if (NOT PYGMENTIZE)
      message("    - pygmentize")
    endif()
    
    message("================================")
    message("")
    
    configure_file(
      ${PUGS_SOURCE_DIR}/cmake/PugsCompileFlags.cmake.in
      ${PUGS_BINARY_DIR}/cmake/PugsCompileFlags.cmake
      @ONLY
    )
    
    install(
      FILES ${PUGS_BINARY_DIR}/cmake/PugsCompileFlags.cmake
      DESTINATION lib/cmake/pugs
    )
    
    # Ugly patch to install user headers for Catch2 (for plugins)
    install(
      DIRECTORY
      "${PUGS_BINARY_DIR}/packages/Catch2/generated-includes/catch2" # Also install the generated header
      DESTINATION
      "include"
      FILES_MATCHING
      PATTERN "*.hpp"
      )