diff --git a/.editorconfig b/.editorconfig index 973587e..39498fe 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,3 +21,6 @@ indent_style = unset [*.{in,seed}] indent_style = tab trim_trailing_whitespace = false + +[cmake/*.cmake.in] +indent_style = space diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7512f92..d03a8f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,12 +30,13 @@ jobs: - name: Install dependencies run: | if test ${{ matrix.os }} = ubuntu-latest - then sudo apt-get install -y libgtest-dev libmemtailor-dev \ + then sudo apt-get install -y pkg-config libgtest-dev libmemtailor-dev \ libmathic-dev libtbb-dev - else brew install autoconf automake libtool googletest \ + else brew install autoconf automake libtool pkgconf googletest \ Macaulay2/tap/memtailor Macaulay2/tap/mathic tbb echo "CPPFLAGS=-I$(brew --prefix)/include" >> $GITHUB_ENV echo "LDFLAGS= -L$(brew --prefix)/lib" >> $GITHUB_ENV + echo "CMAKE_PREFIX_PATH=$(brew --prefix)" >> $GITHUB_ENV fi # Build in parallel. make reads MAKEFLAGS from the environment, # so this reaches the sub-makes distcheck runs as well. @@ -65,6 +66,50 @@ jobs: working-directory: build-dir run: make distcheck + - name: Test installed CMake packages + if: matrix.build-system == 'cmake' + shell: bash + env: + CMAKE_BUILD_PARALLEL_LEVEL: 2 + BUILD_CONFIG: ${{ matrix.debug && 'Debug' || 'Release' }} + EXPECT_DEBUG: ${{ matrix.debug && 'ON' || 'OFF' }} + WITH_TBB: ${{ matrix.tbb && 'ON' || 'OFF' }} + EXPECT_NO_TBB: ${{ matrix.tbb && 'OFF' || 'ON' }} + run: | + for shared in ON OFF; do + build="$RUNNER_TEMP/mathicgb-$shared" + prefix="$build/install" + cmake -S . -B "$build" -GNinja \ + -DCMAKE_BUILD_TYPE="$BUILD_CONFIG" -DBUILD_SHARED_LIBS="$shared" \ + -DBUILD_TESTING=OFF -Dwith_tbb="$WITH_TBB" \ + -DCMAKE_INSTALL_PREFIX="$prefix" + cmake --build "$build" --target install + if pkg-config --exists memtailor mathic && + { test "$WITH_TBB" = OFF || pkg-config --exists tbb; }; then + pc=$(find "$prefix" -name mathicgb.pc -print -quit) + flags=$(PKG_CONFIG_PATH="$(dirname "$pc")${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" pkg-config --cflags mathicgb) + if test "$EXPECT_DEBUG" = ON; then + [[ "$flags" == *-DMATHICGB_DEBUG* ]] + else + [[ "$flags" != *-DMATHICGB_DEBUG* ]] + fi + if test "$EXPECT_NO_TBB" = ON; then + [[ "$flags" == *-DMATHICGB_NO_TBB* ]] + else + [[ "$flags" != *-DMATHICGB_NO_TBB* ]] + fi + else + echo "Skipping pkg-config flags: dependency .pc files are unavailable" + fi + mv "$prefix" "$build/relocated" + cmake -S cmake/tests -B "$build/consumer" -GNinja \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=11 \ + -DCMAKE_PREFIX_PATH="$build/relocated" \ + -DEXPECT_DEBUG="$EXPECT_DEBUG" -DEXPECT_NO_TBB="$EXPECT_NO_TBB" + cmake --build "$build/consumer" + ctest --test-dir "$build/consumer" --output-on-failure + done + ubsan: name: undefined behaviour sanitizer runs-on: ubuntu-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c42df4..9fb0c51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,9 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.18) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message(FATAL_ERROR + "In-source CMake builds are not supported. Use cmake -S . -B build-dir instead.") +endif() set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -14,7 +19,7 @@ set_property(CACHE with_tbb PROPERTY STRINGS auto ON OFF) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) -find_package(Threads 2.1 REQUIRED QUIET) +find_package(Threads REQUIRED QUIET) find_package(Memtailor REQUIRED) find_package(Mathic REQUIRED) # The auto case has to be tested first, as if(with_tbb) is true for "auto". @@ -37,17 +42,12 @@ else() endif() message(STATUS "Multithreading with TBB: ${with_tbb}") -add_library(memtailor STATIC IMPORTED) -set_target_properties(memtailor PROPERTIES - IMPORTED_LOCATION "${MEMTAILOR_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${MEMTAILOR_INCLUDE_DIR}") - -add_library(mathic STATIC IMPORTED) -set_target_properties(mathic PROPERTIES - IMPORTED_LOCATION "${MATHIC_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${MATHIC_INCLUDE_DIR}") +add_library(memtailor ALIAS memtailor::memtailor) +add_library(mathic ALIAS mathic::mathic) include(GNUInstallDirs) +include(CMakePackageConfigHelpers) +include(CTest) add_subdirectory(src) @@ -61,7 +61,11 @@ set_target_properties(mathicgb PROPERTIES VERSION ${MATHICGB_SOVERSION}.${MATHICGB_SO_AGE}.${MATHICGB_SO_REVISION} SOVERSION ${MATHICGB_SOVERSION}) -install(TARGETS mathicgb LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(TARGETS mathicgb EXPORT mathicgbTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY src/mathicgb DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h" @@ -79,14 +83,30 @@ if(with_tbb) else() set(PC_CFLAGS "-DMATHICGB_NO_TBB") endif() -configure_file(mathicgb.pc.in mathicgb.pc @ONLY) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mathicgb.pc +string(APPEND PC_CFLAGS " $<$:-DMATHICGB_DEBUG>") +configure_file(mathicgb.pc.in mathicgb.pc.in @ONLY) +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$/mathicgb.pc" + INPUT "${CMAKE_CURRENT_BINARY_DIR}/mathicgb.pc.in") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$/mathicgb.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) install(FILES README.md gpl-2.0.txt gpl-3.0.txt - DESTINATION licenses/mathicgb + DESTINATION ${CMAKE_INSTALL_DOCDIR} ) +set(mathicgb_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mathicgb") +configure_package_config_file(cmake/mathicgbConfig.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/mathicgbConfig.cmake.in" + INSTALL_DESTINATION "${mathicgb_cmakedir}") +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$/mathicgbConfig.cmake" + INPUT "${CMAKE_CURRENT_BINARY_DIR}/mathicgbConfig.cmake.in") +write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/mathicgbConfigVersion.cmake" + VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion) +install(FILES cmake/FindMemtailor.cmake cmake/FindMathic.cmake cmake/FindTBB.cmake DESTINATION "${mathicgb_cmakedir}") +install(EXPORT mathicgbTargets NAMESPACE mathicgb:: DESTINATION "${mathicgb_cmakedir}") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/$/mathicgbConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/mathicgbConfigVersion.cmake" DESTINATION "${mathicgb_cmakedir}") + if(enable_mgb) add_executable(mgb src/cli/GBMain.cpp diff --git a/Makefile.am b/Makefile.am index fc7b780..4599a3e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -123,3 +123,6 @@ check: @echo "configured without gtest, so unittests cannot be run." endif + +EXTRA_DIST += cmake/mathicgbConfig.cmake.in \ + cmake/tests/CMakeLists.txt cmake/tests/consumer.cpp diff --git a/README.md b/README.md index b657f67..97d03c0 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,42 @@ Copyright 2012 2013 Bjarke Hammersholt Roune (http://www.broune.com) and Michael Mathicgb is licensed for use under the terms of GNU General Public License version 2 and under any later version; the option is yours. See the files gpl-*.txt in this directory. + +## Building and using the CMake package + +This build requires CMake 3.18 or newer and a C++17 compiler. + +```sh +cmake -S . -B build-dir -GNinja -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/your/install/prefix +cmake --build build-dir +ctest --test-dir build-dir --output-on-failure +cmake --install build-dir +``` + +`BUILD_TESTING=OFF` disables the GoogleTest dependency and unit tests. +Both shared and static installations provide headers, pkg-config metadata, +and a relocatable CMake package under the platform's library directory: + +```cmake +find_package(mathicgb 1.4 CONFIG REQUIRED) +target_link_libraries(my_program PRIVATE mathicgb::mathicgb) +``` + +The target supplies C++17, include paths and transitive dependencies. Dependency +CMake configs are preferred; pkg-config metadata is supported for existing +system installations. Older Homebrew CMake bottles may lack both metadata +formats; build the dependency with its released Autotools build, or install a +CMake package that supplies the config. Bare header/library discovery is insufficient to propagate +dependencies' ABI flags. The dependency find modules are shipped with the config. +Use a quoted semicolon-separated `-DCMAKE_PREFIX_PATH='/prefix/one;/prefix/two'` +for nonstandard prefixes; the Unix environment variable uses colons instead. + +The installed package exports `MATHICGB_DEBUG` if the library was built in +Debug mode, even when the consumer uses Release. It inherits dependencies' own +ABI flags instead of forcing their Debug layouts. `pkg-config --cflags mathicgb` +also reports the installed library's Debug flag; pkg-config consumers should +select C++17 or later explicitly. + +`with_tbb` retains its ON/OFF/auto behavior. Both metadata formats propagate +`MATHICGB_NO_TBB` for a build without TBB, or the TBB dependency when enabled. diff --git a/cmake/FindMathic.cmake b/cmake/FindMathic.cmake index 912063d..283a1a0 100644 --- a/cmake/FindMathic.cmake +++ b/cmake/FindMathic.cmake @@ -1,24 +1,40 @@ -# Try to find the mathic libraries -# See https://github.com/Macaulay2/mathic -# -# This file sets up mathic for CMake. Once done this will define -# MATHIC_FOUND - system has MATHIC lib -# MATHIC_INCLUDE_DIR - the MATHIC include directory -# MATHIC_LIBRARIES - Libraries needed to use MATHIC -# +# Prefer the upstream config; pkg-config also carries the dependency's ABI flags. # Copyright (c) 2020, Mahrud Sayrafi, -# # Redistribution and use is allowed according to the terms of the BSD license. - -find_path(MATHIC_INCLUDE_DIR NAMES mathic.h - PATHS ${INCLUDE_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}/include - PATH_SUFFIXES mathic - ) -find_library(MATHIC_LIBRARIES NAMES mathic - PATHS ${LIB_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}/lib - ) - +if(NOT TARGET mathic::mathic) + find_package(mathic CONFIG QUIET) +endif() +if(NOT TARGET mathic::mathic) + find_package(PkgConfig QUIET) + if(PKG_CONFIG_FOUND) + pkg_check_modules(MATHIC QUIET IMPORTED_TARGET mathic) + if(TARGET PkgConfig::MATHIC) + add_library(mathic::mathic INTERFACE IMPORTED) + set_property(TARGET mathic::mathic PROPERTY + INTERFACE_LINK_LIBRARIES PkgConfig::MATHIC) + endif() + endif() +endif() +# Older installations may provide only headers and a library. +if(NOT TARGET mathic::mathic) + find_path(MATHIC_INCLUDE_DIR NAMES mathic.h + PATHS ${INCLUDE_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}/include + PATH_SUFFIXES mathic) + find_library(MATHIC_LIBRARY NAMES mathic + PATHS ${LIB_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}/lib) + if(MATHIC_INCLUDE_DIR AND MATHIC_LIBRARY) + add_library(mathic::mathic UNKNOWN IMPORTED) + set_target_properties(mathic::mathic PROPERTIES + IMPORTED_LOCATION "${MATHIC_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${MATHIC_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES memtailor::memtailor) + endif() + mark_as_advanced(MATHIC_INCLUDE_DIR MATHIC_LIBRARY) +endif() +set(MATHIC_FOUND FALSE) +if(TARGET mathic::mathic) + set(MATHIC_FOUND TRUE) + set(MATHIC_LIBRARIES mathic::mathic) +endif() include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Mathic DEFAULT_MSG MATHIC_INCLUDE_DIR MATHIC_LIBRARIES) - -mark_as_advanced(MATHIC_INCLUDE_DIR MATHIC_LIBRARIES) +find_package_handle_standard_args(Mathic DEFAULT_MSG MATHIC_FOUND) diff --git a/cmake/FindMemtailor.cmake b/cmake/FindMemtailor.cmake index da04bdc..c453699 100644 --- a/cmake/FindMemtailor.cmake +++ b/cmake/FindMemtailor.cmake @@ -1,24 +1,39 @@ -# Try to find the MEMTAILOR libraries -# See https://github.com/Macaulay2/memtailor -# -# This file sets up GLPK for CMake. Once done this will define -# MEMTAILOR_FOUND - system has MEMTAILOR lib -# MEMTAILOR_INCLUDE_DIR - the MEMTAILOR include directory -# MEMTAILOR_LIBRARIES - Libraries needed to use MEMTAILOR -# +# Prefer the upstream config; pkg-config also carries the dependency's ABI flags. # Copyright (c) 2020, Mahrud Sayrafi, -# # Redistribution and use is allowed according to the terms of the BSD license. - -find_path(MEMTAILOR_INCLUDE_DIR NAMES memtailor.h - PATHS ${INCLUDE_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}/include - PATH_SUFFIXES memtailor - ) -find_library(MEMTAILOR_LIBRARIES NAMES memtailor - PATHS ${LIB_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}/lib - ) - +if(NOT TARGET memtailor::memtailor) + find_package(memtailor CONFIG QUIET) +endif() +if(NOT TARGET memtailor::memtailor) + find_package(PkgConfig QUIET) + if(PKG_CONFIG_FOUND) + pkg_check_modules(MEMTAILOR QUIET IMPORTED_TARGET memtailor) + if(TARGET PkgConfig::MEMTAILOR) + add_library(memtailor::memtailor INTERFACE IMPORTED) + set_property(TARGET memtailor::memtailor PROPERTY + INTERFACE_LINK_LIBRARIES PkgConfig::MEMTAILOR) + endif() + endif() +endif() +# Older installations may provide only headers and a library. +if(NOT TARGET memtailor::memtailor) + find_path(MEMTAILOR_INCLUDE_DIR NAMES memtailor.h + PATHS ${INCLUDE_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}/include + PATH_SUFFIXES memtailor) + find_library(MEMTAILOR_LIBRARY NAMES memtailor + PATHS ${LIB_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}/lib) + if(MEMTAILOR_INCLUDE_DIR AND MEMTAILOR_LIBRARY) + add_library(memtailor::memtailor UNKNOWN IMPORTED) + set_target_properties(memtailor::memtailor PROPERTIES + IMPORTED_LOCATION "${MEMTAILOR_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${MEMTAILOR_INCLUDE_DIR}") + endif() + mark_as_advanced(MEMTAILOR_INCLUDE_DIR MEMTAILOR_LIBRARY) +endif() +set(MEMTAILOR_FOUND FALSE) +if(TARGET memtailor::memtailor) + set(MEMTAILOR_FOUND TRUE) + set(MEMTAILOR_LIBRARIES memtailor::memtailor) +endif() include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Memtailor DEFAULT_MSG MEMTAILOR_INCLUDE_DIR MEMTAILOR_LIBRARIES) - -mark_as_advanced(MEMTAILOR_INCLUDE_DIR MEMTAILOR_LIBRARIES) +find_package_handle_standard_args(Memtailor DEFAULT_MSG MEMTAILOR_FOUND) diff --git a/cmake/mathicgbConfig.cmake.in b/cmake/mathicgbConfig.cmake.in new file mode 100644 index 0000000..ef60a8f --- /dev/null +++ b/cmake/mathicgbConfig.cmake.in @@ -0,0 +1,18 @@ +@PACKAGE_INIT@ +include(CMakeFindDependencyMacro) +find_dependency(Threads) +set(_mathicgb_module_path "${CMAKE_MODULE_PATH}") +list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_LIST_DIR}") +find_dependency(Memtailor) +find_dependency(Mathic) +if(@with_tbb@) + find_dependency(TBB) +endif() +set(CMAKE_MODULE_PATH "${_mathicgb_module_path}") +include("${CMAKE_CURRENT_LIST_DIR}/mathicgbTargets.cmake") +# The ABI is determined by the installed library, not the consumer build type. +if($) + set_property(TARGET mathicgb::mathicgb APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS MATHICGB_DEBUG) +endif() +check_required_components(mathicgb) diff --git a/cmake/tests/CMakeLists.txt b/cmake/tests/CMakeLists.txt new file mode 100644 index 0000000..aa19b40 --- /dev/null +++ b/cmake/tests/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.18) +project(mathicgb-consumer LANGUAGES CXX) +find_package(mathicgb 1.4 CONFIG REQUIRED) +add_executable(consumer consumer.cpp) +target_link_libraries(consumer PRIVATE mathicgb::mathicgb) +if(EXPECT_DEBUG) + target_compile_definitions(consumer PRIVATE EXPECT_DEBUG) +endif() +if(EXPECT_NO_TBB) + target_compile_definitions(consumer PRIVATE EXPECT_NO_TBB) +endif() +enable_testing() +add_test(NAME installed-consumer COMMAND consumer) diff --git a/cmake/tests/consumer.cpp b/cmake/tests/consumer.cpp new file mode 100644 index 0000000..82b17e1 --- /dev/null +++ b/cmake/tests/consumer.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +#if defined(EXPECT_DEBUG) != defined(MATHICGB_DEBUG) +#error Debug ABI must agree with the installed library +#endif +static_assert(__cplusplus >= 201703L, "C++17 must be inherited"); +#if defined(EXPECT_NO_TBB) != defined(MATHICGB_NO_TBB) +#error TBB setting must agree with the installed library +#endif + +int main() { + libmathicIsPresent(); + memt::Arena arena; + void* p = arena.alloc(32); + arena.freeTop(p); + mgb::GroebnerConfiguration config(101, 2, 1); + return config.modulus() == 101 && config.varCount() == 2 ? 0 : 1; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 740dea9..07a490d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,9 +56,12 @@ add_library(mathicgb mathicgb/stdinc.h ) +add_library(mathicgb::mathicgb ALIAS mathicgb) +target_compile_features(mathicgb PUBLIC cxx_std_17) + target_compile_definitions(mathicgb PUBLIC $<$>:MATHICGB_NO_TBB> - $<$:MATHICGB_DEBUG> + $:MATHICGB_DEBUG>> ) target_link_libraries(mathicgb @@ -68,7 +71,7 @@ target_link_libraries(mathicgb target_include_directories(mathicgb PUBLIC $ - $ + $ ) include(CTest)