From 44f3e6c37ebb3899c9c01e0f98b955eb3e86a4d6 Mon Sep 17 00:00:00 2001 From: Growl Date: Mon, 8 Jun 2026 03:13:06 +0800 Subject: [PATCH 1/8] Add CMake configuration of LibComm --- CMakeLists.txt | 123 +++++++++++++++++++++++++++++++++++ cmake/LibCommConfig.cmake.in | 17 +++++ 2 files changed, 140 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/LibCommConfig.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7d6db4e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,123 @@ +cmake_minimum_required(VERSION 3.23) + +set(LIBCOMM_PACKAGE_VERSION "0.2.0" CACHE STRING + "Package version used for generated LibCommConfigVersion.cmake when the source tree has no explicit release version.") + +project(LibComm + VERSION "${LIBCOMM_PACKAGE_VERSION}" + 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(LIBCOMM_ENABLE_HEADER_CHECK + "Add an explicit, non-default target that checks public headers for self-contained compilation." + ON) + +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) +add_library(LibComm::LibComm ALIAS LibComm) + +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") + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" + DESTINATION "${CMAKE_INSTALL_DOCDIR}") + +if(BUILD_TESTING AND LIBCOMM_ENABLE_HEADER_CHECK) + add_custom_target(libcomm_public_header_check) + 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_source + "${CMAKE_CURRENT_BINARY_DIR}/header_check_${_libcomm_header_check_index}.cpp") + file(WRITE "${_libcomm_header_check_source}" + "#include <${_libcomm_header_rel}>\nint main() { return 0; }\n") + add_executable(libcomm_header_check_${_libcomm_header_check_index} + EXCLUDE_FROM_ALL + "${_libcomm_header_check_source}") + target_link_libraries(libcomm_header_check_${_libcomm_header_check_index} + PRIVATE LibComm::LibComm) + add_dependencies(libcomm_public_header_check + libcomm_header_check_${_libcomm_header_check_index}) + endforeach() + + add_test(NAME libcomm_public_header_check + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" + --target libcomm_public_header_check + --config "$") +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) From 81e4da11c9ba255831c422062306c028fe66ba8b Mon Sep 17 00:00:00 2001 From: Growl Date: Tue, 16 Jun 2026 23:42:44 +0800 Subject: [PATCH 2/8] Add CMake policy CMP0144 --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d6db4e..cee8b57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,7 @@ 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() set(LIBCOMM_PACKAGE_VERSION "0.2.0" CACHE STRING "Package version used for generated LibCommConfigVersion.cmake when the source tree has no explicit release version.") From 673e10b1b0828fc046536278b140d6cac16acd8a Mon Sep 17 00:00:00 2001 From: Growl Date: Wed, 17 Jun 2026 00:01:37 +0800 Subject: [PATCH 3/8] Add testing workflow --- .github/workflows/cmake.yml | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/cmake.yml diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 0000000..98be95e --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,68 @@ +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 + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ + -DBUILD_TESTING=ON + + - name: Check public headers + run: cmake --build build --target libcomm_public_header_check -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 From 1860b94f7d90c20cf1d69394fc99426488360f26 Mon Sep 17 00:00:00 2001 From: Growl Date: Sat, 11 Jul 2026 11:31:30 +0800 Subject: [PATCH 4/8] Fix public header self-containment --- include/Comm/Comm_Keys/Comm_Keys_32-sr.hpp | 1 + include/Comm/Comm_Trans/Comm_Trans.h | 1 + include/Comm/Comm_Trans/Comm_Trans.hpp | 1 - include/Comm/example/Communicate_Map-2.h | 1 + include/Comm/example/Communicate_Set.h | 1 + include/Comm/example/Communicate_Vector.h | 1 + 6 files changed, 5 insertions(+), 1 deletion(-) 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 From 7036dfe6da6784b8f63b776e3f2f5f00b57b82e2 Mon Sep 17 00:00:00 2001 From: Growl Date: Sat, 11 Jul 2026 11:41:17 +0800 Subject: [PATCH 5/8] Don't make version an option --- .gitignore | 2 ++ CMakeLists.txt | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) 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 index cee8b57..f455694 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,11 +3,8 @@ if(POLICY CMP0144) # https://cmake.org/cmake/help/git-stage/policy/CMP0144.html cmake_policy(SET CMP0144 NEW) endif() -set(LIBCOMM_PACKAGE_VERSION "0.2.0" CACHE STRING - "Package version used for generated LibCommConfigVersion.cmake when the source tree has no explicit release version.") - project(LibComm - VERSION "${LIBCOMM_PACKAGE_VERSION}" + VERSION 0.2.0 DESCRIPTION "Header-only MPI communication helpers used by LibRI and ABACUS" LANGUAGES CXX) From 921464c888c1fae68f1380cdb4dd2ea2ad5495d6 Mon Sep 17 00:00:00 2001 From: Growl Date: Sat, 11 Jul 2026 11:52:53 +0800 Subject: [PATCH 6/8] Let BUILD_TESTING take on header checks It's more like a fake `ctest` as header checks are already done in the build stage with `BUILD_TESTING` enabled. --- .github/workflows/cmake.yml | 6 ++---- CMakeLists.txt | 33 +++++++++++++-------------------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 98be95e..076bf1f 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -25,15 +25,13 @@ jobs: ninja-build \ openmpi-bin - - name: Configure + - name: Configure & Build run: | cmake -S . -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" \ -DBUILD_TESTING=ON - - - name: Check public headers - run: cmake --build build --target libcomm_public_header_check -j $(nproc) + cmake --build build -j $(nproc) - name: Run tests run: ctest --test-dir build --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index f455694..71ff81f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,12 +14,8 @@ 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(LIBCOMM_ENABLE_HEADER_CHECK - "Add an explicit, non-default target that checks public headers for self-contained compilation." - ON) +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) @@ -87,31 +83,28 @@ install(FILES install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" DESTINATION "${CMAKE_INSTALL_DOCDIR}") -if(BUILD_TESTING AND LIBCOMM_ENABLE_HEADER_CHECK) - add_custom_target(libcomm_public_header_check) +if(BUILD_TESTING) 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}/header_check_${_libcomm_header_check_index}.cpp") + "${CMAKE_CURRENT_BINARY_DIR}/${_libcomm_header_check_target}.cpp") file(WRITE "${_libcomm_header_check_source}" - "#include <${_libcomm_header_rel}>\nint main() { return 0; }\n") - add_executable(libcomm_header_check_${_libcomm_header_check_index} - EXCLUDE_FROM_ALL + "#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_${_libcomm_header_check_index} + target_link_libraries(${_libcomm_header_check_target} PRIVATE LibComm::LibComm) - add_dependencies(libcomm_public_header_check - libcomm_header_check_${_libcomm_header_check_index}) + add_test( + NAME "${_libcomm_header_check_target}" + COMMAND ${_libcomm_header_check_target}) endforeach() - - add_test(NAME libcomm_public_header_check - COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" - --target libcomm_public_header_check - --config "$") endif() set(CPACK_PACKAGE_NAME "LibComm") From 3412698a6f4af8a28ab44a8d028d9060a63a6789 Mon Sep 17 00:00:00 2001 From: Growl Date: Sat, 11 Jul 2026 12:09:44 +0800 Subject: [PATCH 7/8] Move alias into CTest --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71ff81f..e910e0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,6 @@ if(NOT LIBCOMM_INSTALL_EXAMPLE_HEADERS) endif() add_library(LibComm INTERFACE) -add_library(LibComm::LibComm ALIAS LibComm) set_target_properties(LibComm PROPERTIES EXPORT_NAME LibComm) @@ -84,6 +83,7 @@ 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") From a01e3d6ecf004d8ff8d4debd283fc6e9f8265e54 Mon Sep 17 00:00:00 2001 From: Growl Date: Sat, 11 Jul 2026 12:14:52 +0800 Subject: [PATCH 8/8] Add pkg-config --- CMakeLists.txt | 8 ++++++++ cmake/libcomm.pc.in | 9 +++++++++ 2 files changed, 17 insertions(+) create mode 100644 cmake/libcomm.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt index e910e0c..063062d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,14 @@ install(FILES "${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}") 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