diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 0000000..076bf1f --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,66 @@ +name: Test with CMake + +on: + push: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends -y \ + libcereal-dev \ + libopenmpi-dev \ + ninja-build \ + openmpi-bin + + - name: Configure & Build + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ + -DBUILD_TESTING=ON + cmake --build build -j $(nproc) + + - name: Run tests + run: ctest --test-dir build --output-on-failure + + - name: Install + run: cmake --install build + + - name: Test installed package + run: | + mkdir -p consumer + cat > consumer/CMakeLists.txt <<'EOF' + cmake_minimum_required(VERSION 3.23) + project(LibCommConsumer LANGUAGES CXX) + + find_package(LibComm CONFIG REQUIRED) + + add_executable(libcomm_consumer main.cpp) + target_link_libraries(libcomm_consumer PRIVATE LibComm::LibComm) + EOF + + cat > consumer/main.cpp <<'EOF' + #include + + int main() + { + return 0; + } + EOF + + cmake -S consumer -B consumer/build -G Ninja \ + -DCMAKE_PREFIX_PATH="${{ github.workspace }}/install" + cmake --build consumer/build --parallel 2 diff --git a/.gitignore b/.gitignore index 39812da..435fec4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .envrc .clangd +/build/ +/install/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..063062d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,124 @@ +cmake_minimum_required(VERSION 3.23) +if(POLICY CMP0144) # https://cmake.org/cmake/help/git-stage/policy/CMP0144.html + cmake_policy(SET CMP0144 NEW) +endif() + +project(LibComm + VERSION 0.2.0 + DESCRIPTION "Header-only MPI communication helpers used by LibRI and ABACUS" + LANGUAGES CXX) + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) +include(CTest) + +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +option(LIBCOMM_INSTALL_EXAMPLE_HEADERS "Install example helper headers under include/Comm/example." ON) +option(BUILD_TESTING "Enable unittest: check public headers for self-contained compilation." OFF) + +find_package(Threads REQUIRED) +find_package(MPI REQUIRED COMPONENTS CXX) +find_package(OpenMP REQUIRED COMPONENTS CXX) +find_package(cereal REQUIRED) + +file(GLOB_RECURSE LIBCOMM_PUBLIC_HEADERS CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/include/Comm/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/Comm/*.hpp") + +if(NOT LIBCOMM_INSTALL_EXAMPLE_HEADERS) + list(FILTER LIBCOMM_PUBLIC_HEADERS EXCLUDE REGEX "/include/Comm/example/") +endif() + +add_library(LibComm INTERFACE) + +set_target_properties(LibComm PROPERTIES + EXPORT_NAME LibComm) + +target_sources(LibComm + INTERFACE + FILE_SET public_headers + TYPE HEADERS + BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" + FILES ${LIBCOMM_PUBLIC_HEADERS}) + +target_compile_features(LibComm INTERFACE cxx_std_17) + +target_include_directories(LibComm + INTERFACE + "$" + "$") + +target_link_libraries(LibComm + INTERFACE + MPI::MPI_CXX + OpenMP::OpenMP_CXX + Threads::Threads + cereal::cereal) + +install(TARGETS LibComm + EXPORT LibCommTargets + FILE_SET public_headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + +install(EXPORT LibCommTargets + NAMESPACE LibComm:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/LibComm") + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibCommConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/LibCommConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/LibComm") + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/LibCommConfigVersion.cmake" + VERSION "${PROJECT_VERSION}" + COMPATIBILITY SameMajorVersion) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/LibCommConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/LibCommConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/LibComm") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libcomm.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/libcomm.pc" + @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libcomm.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" + DESTINATION "${CMAKE_INSTALL_DOCDIR}") + +if(BUILD_TESTING) + add_library(LibComm::LibComm ALIAS LibComm) + set(_libcomm_header_check_index 0) + foreach(_libcomm_header IN LISTS LIBCOMM_PUBLIC_HEADERS) + math(EXPR _libcomm_header_check_index "${_libcomm_header_check_index} + 1") + file(RELATIVE_PATH _libcomm_header_rel + "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${_libcomm_header}") + set(_libcomm_header_check_target + "libcomm_public_header_check_${_libcomm_header_check_index}") + set(_libcomm_header_check_source + "${CMAKE_CURRENT_BINARY_DIR}/${_libcomm_header_check_target}.cpp") + file(WRITE "${_libcomm_header_check_source}" + "#include <${_libcomm_header_rel}>\n" + "int main() { return 0; }\n") + add_executable(${_libcomm_header_check_target} + "${_libcomm_header_check_source}") + target_link_libraries(${_libcomm_header_check_target} + PRIVATE LibComm::LibComm) + add_test( + NAME "${_libcomm_header_check_target}" + COMMAND ${_libcomm_header_check_target}) + endforeach() +endif() + +set(CPACK_PACKAGE_NAME "LibComm") +set(CPACK_PACKAGE_VENDOR "ABACUS") +set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Header-only MPI communication helpers") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_GENERATOR "TGZ") +include(CPack) diff --git a/cmake/LibCommConfig.cmake.in b/cmake/LibCommConfig.cmake.in new file mode 100644 index 0000000..3ebb1ab --- /dev/null +++ b/cmake/LibCommConfig.cmake.in @@ -0,0 +1,17 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +find_dependency(Threads) +find_dependency(MPI COMPONENTS CXX) +find_dependency(OpenMP COMPONENTS CXX) +find_package(cereal CONFIG QUIET) +if(NOT cereal_FOUND) + find_dependency(Cereal) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/LibCommTargets.cmake") + +check_required_components(LibComm) diff --git a/cmake/libcomm.pc.in b/cmake/libcomm.pc.in new file mode 100644 index 0000000..17f60c6 --- /dev/null +++ b/cmake/libcomm.pc.in @@ -0,0 +1,9 @@ +# LibComm is header-only. Use an MPI C++ compiler wrapper (for example, mpicxx). +prefix=@CMAKE_INSTALL_PREFIX@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: LibComm +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ +Cflags: -I${includedir} -fopenmp -pthread +Libs: -fopenmp -pthread diff --git a/include/Comm/Comm_Keys/Comm_Keys_32-sr.hpp b/include/Comm/Comm_Keys/Comm_Keys_32-sr.hpp index 006899a..2cd27ea 100644 --- a/include/Comm/Comm_Keys/Comm_Keys_32-sr.hpp +++ b/include/Comm/Comm_Keys/Comm_Keys_32-sr.hpp @@ -9,6 +9,7 @@ #include "../global/Cereal_Func.h" #include +#include #include #define MPI_CHECK(x) if((x)!=MPI_SUCCESS) throw std::runtime_error(std::string(__FILE__)+" line "+std::to_string(__LINE__)); diff --git a/include/Comm/Comm_Trans/Comm_Trans.h b/include/Comm/Comm_Trans/Comm_Trans.h index 29090a6..d54cc9f 100644 --- a/include/Comm/Comm_Trans/Comm_Trans.h +++ b/include/Comm/Comm_Trans/Comm_Trans.h @@ -6,6 +6,7 @@ #pragma once #include "../Comm_Tools.h" +#include "../global/Cereal_Func.h" #include "../global/Global_Func.h" #include #include diff --git a/include/Comm/Comm_Trans/Comm_Trans.hpp b/include/Comm/Comm_Trans/Comm_Trans.hpp index 0bbd141..1a2e989 100644 --- a/include/Comm/Comm_Trans/Comm_Trans.hpp +++ b/include/Comm/Comm_Trans/Comm_Trans.hpp @@ -6,7 +6,6 @@ #pragma once #include "Comm_Trans.h" -#include "../global/Cereal_Func.h" #include #include diff --git a/include/Comm/example/Communicate_Map-2.h b/include/Comm/example/Communicate_Map-2.h index d01c4ce..784ac7d 100644 --- a/include/Comm/example/Communicate_Map-2.h +++ b/include/Comm/example/Communicate_Map-2.h @@ -5,6 +5,7 @@ #pragma once +#include #include #include #include diff --git a/include/Comm/example/Communicate_Set.h b/include/Comm/example/Communicate_Set.h index 26396a7..00c2375 100644 --- a/include/Comm/example/Communicate_Set.h +++ b/include/Comm/example/Communicate_Set.h @@ -5,6 +5,7 @@ #pragma once +#include #include namespace Comm diff --git a/include/Comm/example/Communicate_Vector.h b/include/Comm/example/Communicate_Vector.h index 8ed6f61..a66cec7 100644 --- a/include/Comm/example/Communicate_Vector.h +++ b/include/Comm/example/Communicate_Vector.h @@ -5,6 +5,7 @@ #pragma once +#include #include namespace Comm