cmake_minimum_required (VERSION 3.10) # CMake utils list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/packages/cmake-modules") # Forbids in-source builds include(CheckNotInSources) #------------------------------------------------------ #----------------- Main configuration ----------------- #------------------------------------------------------ # custom variable allowing to define version suffixes such as -rc*, -beta*, ... set(PUGS_VERSION "0.4.1") # 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}) #------------------------------------------------------ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) #------------------------------------------------------ set(PUGS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(PUGS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") # Change RelWithDebInfo to compile assertions SET("CMAKE_CXX_FLAGS_RELWITHDEBINFO" "-g -O2" CACHE STRING "Flags used by the compiler during release builds with debug info and assertions" FORCE ) SET("CMAKE_C_FLAGS_RELWITHDEBINFO" "-g -O2" CACHE STRING "Flags used by the compiler during release builds with debug info and assertions" FORCE ) # 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 "(Debug|Release|RelWithDebInfo|MinSizeRel|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 RelWIthDebInfo if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build: Debug Release RelWithDebInfo MinSizeRel 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 "8.0.0") set(CLANG_CXX_MIN_VERSION "8.0.0") # Pugs default compiler flags set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS} -Wall -Wextra -pedantic") 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} -Wsign-compare -Wunused -Wunused-member-function -Wunused-private-field") endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0") set(PUGS_STD_LINK_FLAGS "-lstdc++fs") endif() 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 if(${MPI_FOUND}) find_package(ParMETIS) 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 (${CMAKE_VERSION} VERSION_GREATER "3.12") cmake_policy(SET CMP0075 OLD) endif() find_package(PETSc) set(PUGS_HAS_PETSC ${PETSC_FOUND}) else() unset(PUGS_HAS_PETSC) endif() if (${PETSC_FOUND}) include_directories(SYSTEM ${PETSC_INCLUDES}) else() if (PUGS_ENABLE_PETSC MATCHES "^ON$") message(FATAL_ERROR "Could not find PETSc!") 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 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++ 17 flags set(CMAKE_CXX_STANDARD "17") #------------------------------------------------------ # Kokkos configuration set(KOKKOS_SOURCE_DIR "${PUGS_SOURCE_DIR}/packages/kokkos") set(KOKKOS_BINARY_DIR "${PUGS_BINARY_DIR}/packages/kokkos") # 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_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}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${PUGS_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") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -D_GLIBCXX_DEBUG -D_LIBCPP_DEBUG=1") endif() #------------------------------------------------------ # Rang (colors? Useless thus necessary!) include_directories(${PUGS_SOURCE_DIR}/packages/rang/include) # CLI11 include_directories(${PUGS_SOURCE_DIR}/packages/CLI11/include) # 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") include("${CATCH_MODULE_PATH}/contrib/ParseAndAddCatchTests.cmake") add_subdirectory("${CATCH_MODULE_PATH}") add_subdirectory(tests) enable_testing() add_custom_target(run_unit_tests COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unit_tests mpi_unit_tests COMMENT "Executing unit tests." ) # unit tests coverage if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") 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) if (FASTCOV AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "9")) add_custom_target(coverage ALL # in coverage mode we do coverage! # zero all counters COMMAND ${FASTCOV} -q -z # Run tests COMMAND ${CMAKE_CTEST_COMMAND} COMMAND ${FASTCOV} -q --gcov "${GCOV_BIN}" --include "${PUGS_SOURCE_DIR}/src" --exclude "${PUGS_SOURCE_DIR}/src/main.cpp" "${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.*" --lcov -o coverage.info -n COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info DEPENDS unit_tests mpi_unit_tests COMMENT "Running test coverage." WORKING_DIRECTORY "${PUGS_BINARY_DIR}" ) else() add_custom_target(coverage 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 # Run tests COMMAND ${CMAKE_CTEST_COMMAND} # 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 unit_tests mpi_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() # ----------------------------------------------------- link_libraries("-rdynamic") # ------------------- Source files -------------------- # Pugs binary add_executable( pugs src/main.cpp) # Libraries target_link_libraries( pugs PugsMesh PugsAlgebra PugsUtils PugsLanguage PugsLanguageAST PugsLanguageModules PugsLanguageAlgorithms PugsMesh PugsAlgebra PugsUtils PugsLanguageUtils kokkos ${PETSC_LIBRARIES} ${PARMETIS_LIBRARIES} ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} ${KOKKOS_CXX_FLAGS} ${OPENMP_LINK_FLAGS} ${PUGS_STD_LINK_FLAGS} stdc++fs ) # ---------------------- Doxygen ---------------------- include(PugsDoxygen) # ------------------- Installation -------------------- # temporary version workaround if(${CMAKE_VERSION} VERSION_LESS "3.13.0") install(TARGETS pugs RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) else() install(TARGETS pugs PugsMesh PugsUtils PugsLanguage RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) endif() 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(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(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: no found!") endif() if (DOXYGEN_FOUND) message(" doxygen: ${DOXYGEN_EXECUTABLE}") else() message(" doxygen: no found!") endif() message("================================") message("")