diff --git a/CMakeLists.txt b/CMakeLists.txt index 10cf5c437..2d12dbb9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,9 +33,25 @@ set(libROM_CMAKE_PATH ${CMAKE_SOURCE_DIR}/cmake) set(libROM_CMAKE_MODULE_PATH ${libROM_CMAKE_PATH}/modules) list(APPEND CMAKE_MODULE_PATH ${libROM_CMAKE_MODULE_PATH}) -option(USE_MFEM "Build libROM with MFEM" OFF) -option(MFEM_USE_GSLIB "Build libROM with MFEM using GSLIB" OFF) -option(BUILD_STATIC "Build libROM as a static library" OFF) +# Load libROM CMake utilities. +include(libROMCmakeUtilities) + +# Get some version-related names +string(TOUPPER "${PROJECT_NAME}" PROJECT_NAME_UC) +librom_version_to_int(${${PROJECT_NAME}_VERSION} ${PROJECT_NAME_UC}_VERSION) +set(${PROJECT_NAME_UC}_VERSION_STRING ${${PROJECT_NAME}_VERSION}) +if (EXISTS ${PROJECT_SOURCE_DIR}/.git) + execute_process( + COMMAND git describe --all --long --abbrev=40 --dirty --always + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + OUTPUT_VARIABLE ${PROJECT_NAME_UC}_GIT_STRING + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +if (NOT ${PROJECT_NAME_UC}_GIT_STRING) + set(${PROJECT_NAME_UC}_GIT_STRING "(unknown)") +endif() + +option(LIBROM_USE_MFEM "Build libROM with MFEM" OFF) option(ENABLE_EXAMPLES "Build examples and regression tests" ON) ## Set a bunch of variables to generate a configure header @@ -111,14 +127,18 @@ find_package(Doxygen 1.8.5) find_package(GTest 1.6.0) -if (USE_MFEM) - find_library(MFEM mfem "${CMAKE_SOURCE_DIR}/dependencies/mfem" "${MFEM_DIR}/lib") - find_library(HYPRE HYPRE "${CMAKE_SOURCE_DIR}/dependencies/hypre/src/hypre/lib" "${HYPRE_DIR}/lib") - find_library(PARMETIS parmetis "${CMAKE_SOURCE_DIR}/dependencies/parmetis-4.0.3/build/lib/libparmetis" "${PARMETIS_DIR}/lib") - find_library(METIS metis "${CMAKE_SOURCE_DIR}/dependencies/parmetis-4.0.3/build/lib/libmetis" "${METIS_DIR}/lib") - find_path(MFEM_INCLUDES mfem.hpp "${CMAKE_SOURCE_DIR}/dependencies/mfem" "${MFEM_DIR}/include") - find_path(HYPRE_INCLUDES HYPRE.h "${CMAKE_SOURCE_DIR}/dependencies/hypre/src/hypre/include" "${HYPRE_DIR}/include") - find_path(PARMETIS_INCLUDES metis.h "${CMAKE_SOURCE_DIR}/dependencies/parmetis-4.0.3/metis/include" "${PARMETIS_DIR}/metis/include") +set(DEPS HDF5::HDF5 BLAS::BLAS LAPACK::LAPACK + MPI::MPI_C MPI::MPI_CXX MPI::MPI_Fortran ZLIB::ZLIB) + +if (LIBROM_USE_MFEM) + find_package(MFEM 4.5.0 REQUIRED) + if (NOT ${MFEM_USE_MPI}) + message(FATAL_ERROR, "MFEM does not have MPI support") + endif() + if (NOT ${MFEM_USE_MUMPS}) + message(FATAL_ERROR, "MFEM does not have MUMPS support") + endif() + list(APPEND DEPS MFEM::mfem) endif() add_subdirectory(lib) @@ -128,7 +148,7 @@ add_subdirectory(lib) target_compile_features(ROM PRIVATE cxx_std_11) if (ENABLE_EXAMPLES) - if (USE_MFEM) + if (LIBROM_USE_MFEM) set(examples poisson_global_rom poisson_local_rom_greedy @@ -171,11 +191,9 @@ if (ENABLE_EXAMPLES) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples/${example_dir}) add_executable(${name} examples/${example_dir}/${name}.cpp) - target_link_libraries(${name} - PRIVATE ROM ${MPI_C_LINK_FLAGS} ${MPI_C_LIBRARIES} MPI::MPI_C ${MPI_FORTRAN_LINK_FLAGS} ${MPI_FORTRAN_LIBRARIES} MPI::MPI_Fortran) + target_link_libraries(${name} PRIVATE ROM) target_include_directories(${name} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ${MPI_C_INCLUDE_DIRS}) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/lib) target_compile_features(${name} PRIVATE cxx_std_11) endforeach() # IN LISTS examples file(COPY examples/data DESTINATION ${CMAKE_BINARY_DIR}/examples) @@ -190,11 +208,9 @@ if (ENABLE_EXAMPLES) foreach(name IN LISTS misc_example_names) add_executable(${name} examples/misc/${name}.cpp) - target_link_libraries(${name} - PRIVATE ROM ${MPI_C_LINK_FLAGS} ${MPI_C_LIBRARIES} MPI::MPI_C ${MPI_FORTRAN_LINK_FLAGS} ${MPI_FORTRAN_LIBRARIES} MPI::MPI_Fortran) + target_link_libraries(${name} PRIVATE ROM) target_include_directories(${name} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ${MPI_C_INCLUDE_DIRS}) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/lib) target_compile_features(${name} PRIVATE cxx_std_11) endforeach(name) # IN LISTS misc_exmaple_names @@ -212,11 +228,9 @@ if (ENABLE_EXAMPLES) foreach(name IN LISTS regression_test_names) add_executable(${name} tests/${name}.cpp) - target_link_libraries(${name} - PRIVATE ROM ${MPI_C_LINK_FLAGS} ${MPI_C_LIBRARIES} MPI::MPI_C ${MPI_FORTRAN_LINK_FLAGS} ${MPI_FORTRAN_LIBRARIES} MPI::MPI_Fortran) + target_link_libraries(${name} PRIVATE ROM) target_include_directories(${name} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ${MPI_C_INCLUDE_DIRS}) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/lib) target_compile_features(${name} PRIVATE cxx_std_11) endforeach(name) # IN LISTS regression_test_names endif(ENABLE_EXAMPLES) @@ -237,11 +251,9 @@ if(GTEST_FOUND) GreedyCustomSampler) foreach(stem IN LISTS unit_test_stems) add_executable(test_${stem} tests/test_${stem}.cpp) - target_link_libraries(test_${stem} PRIVATE ROM - ${MPI_C_LINK_FLAGS} ${MPI_C_LIBRARIES} MPI::MPI_C ${MPI_FORTRAN_LINK_FLAGS} ${MPI_FORTRAN_LIBRARIES} MPI::MPI_Fortran GTest::GTest) + target_link_libraries(test_${stem} PRIVATE ROM ${DEPS} GTest::GTest) target_include_directories(test_${stem} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ${MPI_C_INCLUDE_DIRS}) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/lib) target_compile_features(test_${stem} PRIVATE cxx_std_11) target_compile_definitions(test_${stem} PRIVATE CAROM_HAS_GTEST) endforeach(stem) # IN LISTS unit_test_stems @@ -270,3 +282,99 @@ if(DOXYGEN_FOUND) add_dependencies(doxygen_tagfile documentation) endif(DOXYGEN_FOUND) + +#------------------------------------------------------------------------------- +# Installation +#------------------------------------------------------------------------------- + +message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}") +set(INSTALL_INCLUDE_DIR include + CACHE PATH "Relative path for installing header files.") +set(INSTALL_LIB_DIR lib + CACHE PATH "Relative path for installing the library.") +set(INSTALL_CMAKE_DIR lib/cmake/librom + CACHE PATH "Relative path for installing cmake config files.") + +# The "INSTALL_INTERFACE" folder shall be +# $) + +set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Development) + +# Install the library +install(TARGETS ROM + EXPORT libROMTargets + DESTINATION ${INSTALL_LIB_DIR}) + +# Install the headers +foreach(Header librom.h FCMangle.h) + install(FILES ${CMAKE_SOURCE_DIR}/lib/${Header} + DESTINATION ${INSTALL_INCLUDE_DIR}/librom/) +endforeach() +install(FILES ${PROJECT_BINARY_DIR}/lib/CAROM_config.h + DESTINATION ${INSTALL_INCLUDE_DIR}/librom/) + +foreach(Folder algo hyperreduction linalg utils) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/${Folder} + DESTINATION ${INSTALL_INCLUDE_DIR}/librom/ + FILES_MATCHING PATTERN "*.h") +endforeach() + +if (LIBROM_USE_MFEM) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib/mfem + DESTINATION ${INSTALL_INCLUDE_DIR}/librom/ + FILES_MATCHING PATTERN "*.hpp") +endif() + +# Package the whole thing up nicely +include(CMakePackageConfigHelpers) + +# Add all targets to the build-tree export set +export(TARGETS ROM + FILE "${PROJECT_BINARY_DIR}/libROMTargets.cmake") + +# Export the package for use from the build-tree (this registers the build-tree +# with the CMake user package registry.) +# TODO: How do we register the install-tree? Replacing the build-tree? +export(PACKAGE ${PROJECT_NAME}) + +# This is the build-tree version +set(INCLUDE_INSTALL_DIRS ${PROJECT_BINARY_DIR}) +set(LIB_INSTALL_DIR ${PROJECT_BINARY_DIR}) +configure_package_config_file(cmake/libROMConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/libROMConfig.cmake + INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR} + PATH_VARS INCLUDE_INSTALL_DIRS LIB_INSTALL_DIR) + +# This is the version that will be installed +set(INCLUDE_INSTALL_DIRS ${INSTALL_INCLUDE_DIR}) +set(LIB_INSTALL_DIR ${INSTALL_LIB_DIR}) +configure_package_config_file(cmake/libROMConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/libROMConfig.cmake + INSTALL_DESTINATION ${INSTALL_CMAKE_DIR} + PATH_VARS INCLUDE_INSTALL_DIRS LIB_INSTALL_DIR) + +# Write the version file (same for build and install tree) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/libROMConfigVersion.cmake + VERSION ${${PROJECT_NAME}_VERSION} + COMPATIBILITY SameMajorVersion ) + +# Install the config files +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/libROMConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libROMConfigVersion.cmake + DESTINATION ${INSTALL_CMAKE_DIR}) + +# Install the export set for use with the install-tree +install(EXPORT libROMTargets + NAMESPACE libROM:: + DESTINATION ${INSTALL_CMAKE_DIR}) + +# Install the modules directory, so it can be used to find the dependencies +install(DIRECTORY config/cmake/modules + DESTINATION ${INSTALL_CMAKE_DIR} + FILES_MATCHING PATTERN "*.cmake") diff --git a/cmake/libROMConfig.cmake.in b/cmake/libROMConfig.cmake.in new file mode 100644 index 000000000..c4371b79c --- /dev/null +++ b/cmake/libROMConfig.cmake.in @@ -0,0 +1,48 @@ +# Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced +# at the Lawrence Livermore National Laboratory. All Rights reserved. See files +# LICENSE and NOTICE for details. LLNL-CODE-806117. +# +# This file is part of the libROM library. For more information and source code +# availability visit https://mfem.org. +# +# libROM is free software; you can redistribute it and/or modify it under the +# terms of the BSD-3 license. We welcome feedback and contributions, see file +# CONTRIBUTING.md for details. + +include(CMakeFindDependencyMacro) +include(${CMAKE_CURRENT_LIST_DIR}/libROMConfigVersion.cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/modules) + +set(libROM_VERSION ${PACKAGE_VERSION}) + +# MPI needs to link against MPI::C MPI::CXX or MPI::Fortran +enable_language(C) +enable_language(CXX) +enable_language(Fortran) +find_dependency(MPI) +find_dependency(HDF5) +find_dependency(BLAS) +find_dependency(LAPACK) +find_dependency(ZLIB) + +set(LIBROM_USE_MFEM @LIBROM_USE_MFEM@) +if(LIBROM_USE_MFEM) + find_dependency(MFEM) +endif() + +@PACKAGE_INIT@ + +set(libROM_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIRS@") +foreach (dir ${libROM_INCLUDE_DIRS}) + set_and_check(libROM_INCLUDE_DIR "${dir}") +endforeach (dir "${libROM_INCLUDE_DIRS}") + +set_and_check(libROM_LIBRARY_DIR "@PACKAGE_LIB_INSTALL_DIR@") + +check_required_components(libROM) + +if (NOT TARGET librom) + include(${CMAKE_CURRENT_LIST_DIR}/libROMTargets.cmake) +endif (NOT TARGET librom) + +set(libROM_LIBRARIES librom) diff --git a/cmake/modules/libROMCmakeUtilities.cmake b/cmake/modules/libROMCmakeUtilities.cmake new file mode 100644 index 000000000..d09b933fa --- /dev/null +++ b/cmake/modules/libROMCmakeUtilities.cmake @@ -0,0 +1,34 @@ +# Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced +# at the Lawrence Livermore National Laboratory. All Rights reserved. See files +# LICENSE and NOTICE for details. LLNL-CODE-806117. +# +# This file is part of the MFEM library. For more information and source code +# availability visit https://mfem.org. +# +# MFEM is free software; you can redistribute it and/or modify it under the +# terms of the BSD-3 license. We welcome feedback and contributions, see file +# CONTRIBUTING.md for details. + +# Function that converts a version string of the form 'major[.minor[.patch]]' to +# the integer ((major * 100) + minor) * 100 + patch. +function(librom_version_to_int VersionString VersionIntVar) + if ("${VersionString}" MATCHES "^([0-9]+)(.*)$") + set(Major "${CMAKE_MATCH_1}") + set(MinorPatchString "${CMAKE_MATCH_2}") + else() + set(Major 0) + endif() + if ("${MinorPatchString}" MATCHES "^\\.([0-9]+)(.*)$") + set(Minor "${CMAKE_MATCH_1}") + set(PatchString "${CMAKE_MATCH_2}") + else() + set(Minor 0) + endif() + if ("${PatchString}" MATCHES "^\\.([0-9]+)(.*)$") + set(Patch "${CMAKE_MATCH_1}") + else() + set(Patch 0) + endif() + math(EXPR VersionInt "(${Major}*100+${Minor})*100+${Patch}") + set(${VersionIntVar} ${VersionInt} PARENT_SCOPE) +endfunction() diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 15411f3af..451486855 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -13,7 +13,7 @@ FortranCInterface_HEADER(${CMAKE_CURRENT_SOURCE_DIR}/FCMangle.h MACRO_NAMESPACE "CAROM_FC_") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CAROM_config.h.in - ${CMAKE_CURRENT_SOURCE_DIR}/CAROM_config.h @ONLY) + ${CMAKE_CURRENT_BINARY_DIR}/CAROM_config.h @ONLY) # While it is tempting to use file globbing here, file globbing is # considered a "modern CMake anti-pattern" because "CMake is not a @@ -64,7 +64,7 @@ list(APPEND source_files linalg/Options.h librom.h) -if (USE_MFEM) +if (LIBROM_USE_MFEM) list(APPEND source_files mfem/PointwiseSnapshot.hpp mfem/PointwiseSnapshot.cpp @@ -77,28 +77,9 @@ endif() list(APPEND source_files linalg/scalapack_c_wrapper.c linalg/scalapack_f_wrapper.f90) -if (BUILD_STATIC) - add_library(ROM ${source_files}) -else() - add_library(ROM SHARED ${source_files}) -endif() - -# If MKL libraries not found, search for reference ScaLAPACK. If MKL -# libraries found, search for MKL ScaLAPACK; if MKL ScaLAPACK not -# found, search for reference ScaLAPACK. It seems that only -if (BLAS_LIBRARIES MATCHES ".*mkl.*") - find_package(MKL COMPONENTS BLACS ScaLAPACK) - if (NOT MKL_ScaLAPACK_FOUND) - find_package(ScaLAPACK REQUIRED) - target_link_libraries(ROM PUBLIC ${ScaLAPACK_LIBRARIES}) - else() - target_link_libraries(ROM PUBLIC ${MKL_ScaLAPACK_LIBRARY} ${MKL_BLACS_LIBRARY} ${MKL_LIBRARIES}) - target_include_directories(ROM PUBLIC ${MKL_INCLUDE_DIRS}) - endif() -else() # BLAS or LAPACK isn't MKL - find_package(ScaLAPACK REQUIRED) - target_link_libraries(ROM PUBLIC ${ScaLAPACK_LIBRARIES}) -endif() +# The type of library can be set by means of BUILD_SHARED_LIBS CMake global +# variable +add_library(ROM ${source_files}) # PUBLIC dependencies are transitive; these dependencies are used in # the API headers *and* in their implementations @@ -114,17 +95,9 @@ endif() # (i.e., "MPI_C_LINK_FLAGS, MPI_C_LIBRARIES) is probably redundant, # but is done here to ease a potential rollback to CMake 2.8 or CMake # 3.0. -target_link_libraries(ROM - PUBLIC ${MPI_C_LINK_FLAGS} ${MPI_C_LIBRARIES} MPI::MPI_C ${MPI_FORTRAN_LINK_FLAGS} ${MPI_FORTRAN_LIBRARIES} MPI::MPI_Fortran ${HDF5_LIBRARIES} - ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ${MFEM} ${HYPRE} ${PARMETIS} ${METIS} - PRIVATE ${ZLIB_LIBRARIES} ZLIB::ZLIB) +target_link_libraries(ROM PUBLIC ${DEPS}) target_include_directories(ROM PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - ${MFEM_INCLUDES} - ${HYPRE_INCLUDES} - ${PARMETIS_INCLUDES} - ${HDF5_C_INCLUDE_DIRS} - ${MPI_C_INCLUDE_DIRS} - ${MFEM_C_INCLUDE_DIRS} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + PUBLIC + $ + $)