diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e11ce4..e391ded 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,11 @@ -cmake_minimum_required(VERSION 3.12.0 FATAL_ERROR) -project(Ygor LANGUAGES CXX) -#set(Ygor_VERSION_MAJOR 0) -#set(Ygor_VERSION_MINOR 0) -#set(Ygor_VERSION_PATCH 0) +cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR) +project(Ygor + VERSION 0.1.0 + DESCRIPTION "Support library with scientific emphasis" + HOMEPAGE_URL "https://github.com/hdclark/Ygor" + LANGUAGES CXX +) #################################################################################### # User Options @@ -24,28 +26,24 @@ option(USE_LTO "Use link-time optimization when available." OFF) option(BUILD_SHARED_LIBS "Build shared-object/dynamically-loaded binaries." ON) +option(FETCHCONTENT_FALLBACK "Use FetchContent to download missing dependencies." ON) + #################################################################################### # Configuration #################################################################################### -# High-level configuration. -if(NOT BUILD_SHARED_LIBS) - #set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - link_libraries("-static") - set(CMAKE_EXE_LINKER_FLAGS "-static") -endif() - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_EXTENSIONS OFF) # Disable GNU extensions (e.g., std=gnu++14). +# Modern CMake settings +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # For use with clang-tidy et al. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(THREADS_PREFER_PTHREAD_FLAG ON) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -set(POSITION_INDEPENDENT_CODE TRUE) -if(NOT CMAKE_BUILD_TYPE) - # Default to debug builds. - set(CMAKE_BUILD_TYPE Debug CACHE STRING "default to debug" FORCE) +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Debug' as no build type was specified.") + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() if(USE_LTO) @@ -64,154 +62,235 @@ endif() #################################################################################### # Note: Dependencies are listed in CPACK list below. +include(FetchContent) + find_package(Threads REQUIRED) +# Boost (optional) if(WITH_BOOST) if(NOT BUILD_SHARED_LIBS) set(Boost_USE_STATIC_RUNTIME ON) set(Boost_USE_STATIC_LIBS ON) endif() - find_package(Boost REQUIRED) - include_directories(${Boost_INCLUDE_DIRS}) + find_package(Boost QUIET) + if(NOT Boost_FOUND) + message(STATUS "Boost not found. Disabling WITH_BOOST.") + set(WITH_BOOST OFF CACHE BOOL "" FORCE) + endif() endif() +# Eigen (optional, with FetchContent fallback) if(WITH_EIGEN) - find_package(PkgConfig REQUIRED) - pkg_check_modules(EIGEN3 REQUIRED eigen3) - include_directories( ${EIGEN3_INCLUDE_DIRS} ) + find_package(Eigen3 3.3 QUIET CONFIG) + if(NOT Eigen3_FOUND) + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(EIGEN3 QUIET eigen3) + endif() + endif() + + if(NOT Eigen3_FOUND AND NOT EIGEN3_FOUND) + if(FETCHCONTENT_FALLBACK) + message(STATUS "Eigen3 not found locally, attempting FetchContent...") + # Use URL method which can be more reliable than git + FetchContent_Declare( + Eigen + URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz + URL_HASH SHA256=8586084f71f9bde545ee7fa6d00288b264a2b7ac3607b974e54d13e7162c1c72 + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + set(EIGEN_BUILD_DOC OFF CACHE BOOL "" FORCE) + set(EIGEN_BUILD_TESTING OFF CACHE BOOL "" FORCE) + set(BUILD_TESTING OFF CACHE BOOL "" FORCE) + + FetchContent_GetProperties(Eigen) + if(NOT eigen_POPULATED) + message(STATUS "Downloading Eigen3 (this may take a moment)...") + # Attempt to populate - will fail if network unavailable + FetchContent_MakeAvailable(Eigen) + if(TARGET Eigen3::Eigen) + set(EIGEN_FETCHED TRUE) + message(STATUS "Eigen3 fetched successfully via FetchContent.") + else() + message(WARNING "FetchContent for Eigen3 failed. Disabling WITH_EIGEN.") + set(WITH_EIGEN OFF CACHE BOOL "" FORCE) + endif() + endif() + else() + message(STATUS "Eigen not found and FETCHCONTENT_FALLBACK=OFF. Disabling WITH_EIGEN.") + set(WITH_EIGEN OFF CACHE BOOL "" FORCE) + endif() + endif() endif() +# GNU GSL (optional) if(WITH_GNU_GSL) - find_package(PkgConfig REQUIRED) - pkg_check_modules(GNU_GSL REQUIRED gsl) - include_directories( ${GNU_GSL_INCLUDE_DIRS} ) + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(GNU_GSL QUIET gsl) + endif() + if(NOT GNU_GSL_FOUND) + # Try to find GSL using CMake's FindGSL module + find_package(GSL QUIET) + if(GSL_FOUND) + set(GNU_GSL_FOUND TRUE) + set(GNU_GSL_LIBRARIES ${GSL_LIBRARIES}) + set(GNU_GSL_INCLUDE_DIRS ${GSL_INCLUDE_DIRS}) + endif() + endif() + if(NOT GNU_GSL_FOUND) + message(STATUS "GNU GSL not found. Disabling WITH_GNU_GSL.") + set(WITH_GNU_GSL OFF CACHE BOOL "" FORCE) + endif() endif() #################################################################################### -# Compiler Flags +# Compiler Flags Interface #################################################################################### +# Create an INTERFACE library to encapsulate common compile options -# Override the default CXX flags, which are controlled by the release type. -# -# Note: The '_DEBUG' flags are only applied when the release mode is 'Debug' -- likewise for the other flags. -# -# Note: If you want to fully override the CXX_FLAGS, then do not supply a build type and specify your CXX_FLAGS by -# defining CMAKE_CXX_FLAGS when calling cmake. -set(CMAKE_CXX_FLAGS_DEBUG "-O2 -g") -set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") -set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g") +add_library(ygor_compile_options INTERFACE) +add_library(Ygor::CompileOptions ALIAS ygor_compile_options) -if(MEMORY_CONSTRAINED_BUILD) - # Do not overwrite user-provided flags, but do provide sane defaults. - if(NOT CMAKE_CXX_FLAGS) - set(CMAKE_CXX_FLAGS "-O0 -DNDEBUG") - endif() - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -DNDEBUG") - set(CMAKE_CXX_FLAGS_MINSIZEREL "-O0 -DNDEBUG") - set(CMAKE_CXX_FLAGS_RELEASE "-O0 -DNDEBUG") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O0 -DNDEBUG") +# C++ standard +target_compile_features(ygor_compile_options INTERFACE cxx_std_17) + +# Position independent code +set_target_properties(ygor_compile_options PROPERTIES + INTERFACE_POSITION_INDEPENDENT_CODE ON +) + +# Static linking setup +if(NOT BUILD_SHARED_LIBS) + target_link_options(ygor_compile_options INTERFACE "-static") endif() -# Add other appropriate CXX flags. -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frounding-math") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-finite-math-only") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-var-tracking-assignments") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing") - - if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - # Add gprof profiling flag. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-check") - endif() +# Compiler-specific flags +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(ygor_compile_options INTERFACE + -ffast-math + -frounding-math + -fno-finite-math-only + -fdiagnostics-color=always + -fno-var-tracking-assignments + -Wno-deprecated + -Wall + -Wextra + -Wpedantic + -Wno-strict-aliasing + ) + + target_compile_options(ygor_compile_options INTERFACE + $<$:-pg> + $<$:-fno-omit-frame-pointer> + $<$:-fstack-check> + ) if(MEMORY_CONSTRAINED_BUILD) - # Trigger garbage collection more frequently. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --param ggc-min-expand=10") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --param ggc-min-heapsize=32768") + target_compile_options(ygor_compile_options INTERFACE + --param=ggc-min-expand=10 + --param=ggc-min-heapsize=32768 + ) endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic") - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ignored-optimization-argument") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(ygor_compile_options INTERFACE + -fdiagnostics-color=always + -Wno-deprecated + -Wall + -Wextra + -Wpedantic + ) if(WITH_IWYU) - set(IWYU_INVOCATION iwyu) # Location of the iwyu binary. - list(APPEND IWYU_INVOCATION "-Xiwyu;--no_comments") - set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${IWYU_INVOCATION}) + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "iwyu;-Xiwyu;--no_comments") endif() endif() +# Memory constrained build options +if(MEMORY_CONSTRAINED_BUILD) + # For memory-constrained builds, minimize optimization but keep Debug assertions enabled + target_compile_options(ygor_compile_options INTERFACE + $<$:-O0 -g> + $<$:-O0 -DNDEBUG> + $<$:-O0 -DNDEBUG> + $<$:-O0 -g> + ) +else() + target_compile_options(ygor_compile_options INTERFACE + $<$:-O2 -g> + $<$:-O3 -DNDEBUG> + $<$:-Os -DNDEBUG> + $<$:-O3 -g> + ) +endif() -# Sanitizers. -if(WITH_ASAN OR WITH_TSAN OR WITH_MSAN) - #set(CMAKE_CXX_FLAGS " ") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common") - # Also enable coverage instrumentation, since sanitizers will typically be used for testing. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") - # Clang only? Need to confirm ... TODO - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-coverage=trace-pc-guard") - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-instr-generate") - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoverage-mapping") +#################################################################################### +# Sanitizers +#################################################################################### - add_definitions(-U_FORTIFY_SOURCE) +add_library(ygor_sanitizer_options INTERFACE) +add_library(Ygor::SanitizerOptions ALIAS ygor_sanitizer_options) + +if(WITH_ASAN OR WITH_TSAN OR WITH_MSAN) + target_compile_options(ygor_sanitizer_options INTERFACE + -O2 + -g + -fno-omit-frame-pointer + -fno-common + --coverage + ) + target_compile_definitions(ygor_sanitizer_options INTERFACE -U_FORTIFY_SOURCE) endif() -if(WITH_ASAN) - add_compile_options( -fsanitize=address - -fsanitize-address-use-after-scope ) - add_link_options(-fsanitize=address) - - add_compile_options( -fsanitize=undefined - -fno-sanitize-recover=undefined ) - add_link_options(-fsanitize=undefined) +if(WITH_ASAN) + target_compile_options(ygor_sanitizer_options INTERFACE + -fsanitize=address + -fsanitize-address-use-after-scope + -fsanitize=undefined + -fno-sanitize-recover=undefined + ) + target_link_options(ygor_sanitizer_options INTERFACE + -fsanitize=address + -fsanitize=undefined + ) elseif(WITH_TSAN) message(WARNING "TSan may not support exceptions (depends on the compiler version and platform).") - add_compile_options(-fsanitize=thread) - add_link_options(-fsanitize=thread) + target_compile_options(ygor_sanitizer_options INTERFACE -fsanitize=thread) + target_link_options(ygor_sanitizer_options INTERFACE -fsanitize=thread) elseif(WITH_MSAN) message(WARNING "MSan may not be available on your system.") - add_compile_options( -fsanitize=memory - -fPIE - -pie - -fsanitize-memory-track-origins ) - add_link_options(-fsanitize=memory) + target_compile_options(ygor_sanitizer_options INTERFACE + -fsanitize=memory + -fPIE + -pie + -fsanitize-memory-track-origins + ) + target_link_options(ygor_sanitizer_options INTERFACE -fsanitize=memory) endif() +#################################################################################### +# ARM Architecture Support +#################################################################################### + include(CheckCXXSymbolExists) check_cxx_symbol_exists(__arm__ "cstdio" ARCH_IS_ARM) check_cxx_symbol_exists(__aarch64__ "cstdio" ARCH_IS_ARM64) + if(ARCH_IS_ARM OR ARCH_IS_ARM64) message(STATUS "Detected ARM architecture.") - if(CMAKE_CXX_FLAGS MATCHES "-march=|-mcpu=|-mtune=") - message(STATUS "Architecture set by user.") - else() - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # Enable to fix linking errors for toolchains that do not auto-detect atomic intrinsics (e.g., some ARM systems). - # Note: Binaries built this way should not be distributed. + # Only add flags if not already set by user + get_target_property(_current_opts ygor_compile_options INTERFACE_COMPILE_OPTIONS) + if(NOT _current_opts MATCHES "-march=|-mcpu=|-mtune=") + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") message(STATUS "No architecture set, adding march=native flag") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") - elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_compile_options(ygor_compile_options INTERFACE -march=native) + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") message(STATUS "No architecture set, adding mcpu=generic flag") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=generic") + target_compile_options(ygor_compile_options INTERFACE -mcpu=generic) else() message(WARNING "Not able to set architecture, if compilation errors occur set architecture manually") endif() @@ -219,53 +298,57 @@ if(ARCH_IS_ARM OR ARCH_IS_ARM64) endif() +#################################################################################### +# std::filesystem workaround +#################################################################################### # Workaround for GCCv8 std::filesystem sidecar library -- can be removed when GCCv8 no longer relevant. -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + +set(YGOR_STD_FS_LIB "") +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") foreach(lib_dir ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}) - #message("Searching directory ${lib_dir}") - file(GLOB_RECURSE STD_FS_LIB "${lib_dir}*stdc++fs*") - if(STD_FS_LIB) + file(GLOB_RECURSE _found_fs_lib "${lib_dir}*stdc++fs*") + if(_found_fs_lib) + set(YGOR_STD_FS_LIB "stdc++fs") + message(STATUS "Assuming libstdc++fs library is needed.") break() endif() endforeach() - # - if(STD_FS_LIB) - message("Assuming libstdc++fs library '${STD_FS_LIB}' is needed.") - set(STD_FS_LIB "stdc++fs") - else() - set(STD_FS_LIB "") - endif() endif() #################################################################################### # Definitions #################################################################################### -#add_definitions(-U__STRICT_ANSI__) # Used to compile on MSYS2. - # Alternatively define _GNU_SOURCE or _XOPEN_SOURCE - # or enable GNU extensions via CMake mechanism? +# Log which features are enabled if(WITH_LINUX_SYS) - message(STATUS "Assuming Linux-specific sys interfaces are available.") - set(YGOR_USE_LINUX_SYS 1) + message(STATUS "Linux-specific sys interfaces: ENABLED") +else() + message(STATUS "Linux-specific sys interfaces: DISABLED") endif() + if(WITH_EIGEN) - message(STATUS "Assuming Eigen is available.") - set(YGOR_USE_EIGEN 1) + message(STATUS "Eigen support: ENABLED") +else() + message(STATUS "Eigen support: DISABLED") endif() + if(WITH_GNU_GSL) - message(STATUS "Assuming GNU GSL is available.") - set(YGOR_USE_GNU_GSL 1) + message(STATUS "GNU GSL support: ENABLED") +else() + message(STATUS "GNU GSL support: DISABLED") endif() + if(WITH_BOOST) - message(STATUS "Assuming Boost libraries are available.") - set(YGOR_USE_BOOST 1) + message(STATUS "Boost support: ENABLED") +else() + message(STATUS "Boost support: DISABLED") endif() -# Use the directory where CMakeLists.txt is for inclusions. -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) +#################################################################################### +# Output Directories +#################################################################################### include(GNUInstallDirs) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) @@ -281,22 +364,69 @@ add_subdirectory(src) add_subdirectory(binaries) +#################################################################################### +# CMake Package Configuration +#################################################################################### + +include(CMakePackageConfigHelpers) + +# Generate version file +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/YgorConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) + +# Export targets to build tree +export(TARGETS ygor ygor_compile_options ygor_sanitizer_options + NAMESPACE Ygor:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/YgorTargets.cmake" +) + +# Install targets +install(TARGETS ygor ygor_compile_options ygor_sanitizer_options + EXPORT YgorTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +# Install export file +install(EXPORT YgorTargets + FILE YgorTargets.cmake + NAMESPACE Ygor:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Ygor +) + +# Generate and install config file +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/YgorConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/YgorConfig.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Ygor +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/YgorConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/YgorConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Ygor +) + + #################################################################################### # Packaging #################################################################################### set(CPACK_GENERATOR "DEB") -#set(CPACK_PACKAGE_NAME "ygor") -string(TIMESTAMP INVOCATION_TIMESTAMP "%Y%m%d.%H%M%S") # For a time-based version number. +string(TIMESTAMP INVOCATION_TIMESTAMP "%Y%m%d.%H%M%S") set(CPACK_PACKAGE_VERSION "${INVOCATION_TIMESTAMP}") -#set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") # i386, amd64, armel, armhf, ... -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Support library with scientific emphasis.") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}") set(CPACK_PACKAGE_CONTACT "hdeanclark@gmail.com") set(CPACK_PACKAGE_MAINTAINER "Haley Clark ") set(CPACK_DEBIAN_PACKAGE_SECTION "Science") -# Ygor build dependencies, e.g., "libc6 (>= 2.3.1-6), libgcc1 (>= 1:3.4.2-12)" +# Ygor build dependencies set(BUILD_DEPENDENCY_PACKAGES "") if(WITH_BOOST) list(APPEND BUILD_DEPENDENCY_PACKAGES "libboost-dev") @@ -306,7 +436,7 @@ if(WITH_GNU_GSL) endif() list(JOIN BUILD_DEPENDENCY_PACKAGES ", " CPACK_DEBIAN_PACKAGE_DEPENDS) -# Recommended or optional packages, e.g., "liboptional-dev (>= 1.2.3-1), libmaybe-dev (>= 1:1.3.2-10)" +# Recommended packages set(RECOMMENDED_PACKAGES "") if(WITH_EIGEN) list(APPEND RECOMMENDED_PACKAGES "libeigen3-dev") diff --git a/binaries/CMakeLists.txt b/binaries/CMakeLists.txt index 19c8483..44f022c 100644 --- a/binaries/CMakeLists.txt +++ b/binaries/CMakeLists.txt @@ -1,60 +1,33 @@ - -add_executable(fits_replace_nans - Replace_NaNs.cc -) -target_include_directories(fits_replace_nans - SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(fits_replace_nans - ygor - m - Threads::Threads -) - -add_executable(twot_pvalue - TwoT_PValue.cc -) -target_include_directories(twot_pvalue - SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(twot_pvalue - ygor - m - Threads::Threads -) - -add_executable(regex_tester - Regex_Tester.cc -) -target_include_directories(regex_tester - SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(regex_tester - ygor - m - Threads::Threads -) - -add_executable(parse_TAR_files - Parse_TAR_Files.cc -) -target_include_directories(parse_TAR_files - SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(parse_TAR_files - ygor - m - Threads::Threads -) - -install(TARGETS fits_replace_nans - twot_pvalue - regex_tester - parse_TAR_files - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +# Helper function to create a binary target +function(ygor_add_executable target_name source_file) + add_executable(${target_name} ${source_file}) + + target_link_libraries(${target_name} + PRIVATE + Ygor::ygor + ) + + set_target_properties(${target_name} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + ) +endfunction() + +# Create executables +ygor_add_executable(fits_replace_nans Replace_NaNs.cc) +ygor_add_executable(twot_pvalue TwoT_PValue.cc) +ygor_add_executable(regex_tester Regex_Tester.cc) +ygor_add_executable(parse_TAR_files Parse_TAR_Files.cc) + +# Install targets +install(TARGETS + fits_replace_nans + twot_pvalue + regex_tester + parse_TAR_files + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) diff --git a/cmake/YgorConfig.cmake.in b/cmake/YgorConfig.cmake.in new file mode 100644 index 0000000..321cda0 --- /dev/null +++ b/cmake/YgorConfig.cmake.in @@ -0,0 +1,41 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +# Required dependencies +find_dependency(Threads) + +# Optional dependencies based on build configuration +set(YGOR_WITH_BOOST @WITH_BOOST@) +set(YGOR_WITH_EIGEN @WITH_EIGEN@) +set(YGOR_WITH_GNU_GSL @WITH_GNU_GSL@) +set(YGOR_WITH_LINUX_SYS @WITH_LINUX_SYS@) + +if(YGOR_WITH_BOOST) + find_dependency(Boost) +endif() + +if(YGOR_WITH_EIGEN) + find_dependency(Eigen3 3.3 CONFIG QUIET) + if(NOT TARGET Eigen3::Eigen) + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(EIGEN3 QUIET eigen3) + endif() + endif() +endif() + +if(YGOR_WITH_GNU_GSL) + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(GNU_GSL QUIET gsl) + endif() + if(NOT GNU_GSL_FOUND) + find_dependency(GSL QUIET) + endif() +endif() + +# Include the targets file +include("${CMAKE_CURRENT_LIST_DIR}/YgorTargets.cmake") + +check_required_components(Ygor) diff --git a/cmake/toolchains/aarch64-linux-gnu.cmake b/cmake/toolchains/aarch64-linux-gnu.cmake new file mode 100644 index 0000000..4cc10f0 --- /dev/null +++ b/cmake/toolchains/aarch64-linux-gnu.cmake @@ -0,0 +1,27 @@ +# Example CMake toolchain file for cross-compiling to ARM64/aarch64 +# +# Usage: +# cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/aarch64-linux-gnu.cmake .. +# +# Prerequisites: +# - aarch64-linux-gnu cross-compiler +# - Cross-compiled dependencies + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +# Cross-compiler +set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) + +# Target environment +set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu) + +# Search paths +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Architecture flag for aarch64 +set(CMAKE_CXX_FLAGS_INIT "-mcpu=generic") diff --git a/cmake/toolchains/armv7-linux-gnueabihf.cmake b/cmake/toolchains/armv7-linux-gnueabihf.cmake new file mode 100644 index 0000000..29a8512 --- /dev/null +++ b/cmake/toolchains/armv7-linux-gnueabihf.cmake @@ -0,0 +1,27 @@ +# Example CMake toolchain file for cross-compiling to ARMv7 (hard float) +# +# Usage: +# cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/armv7-linux-gnueabihf.cmake .. +# +# Prerequisites: +# - arm-linux-gnueabihf cross-compiler +# - Cross-compiled dependencies + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +# Cross-compiler +set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) + +# Target environment +set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf) + +# Search paths +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Architecture flag for ARMv7 +set(CMAKE_CXX_FLAGS_INIT "-march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard") diff --git a/cmake/toolchains/macos-x86_64.cmake b/cmake/toolchains/macos-x86_64.cmake new file mode 100644 index 0000000..9ddebfa --- /dev/null +++ b/cmake/toolchains/macos-x86_64.cmake @@ -0,0 +1,34 @@ +# Example CMake toolchain file for cross-compiling to macOS (osxcross) +# +# Usage: +# cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/macos-x86_64.cmake .. +# +# Prerequisites: +# - osxcross toolchain (https://github.com/tpoechtrager/osxcross) +# - Cross-compiled dependencies + +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +# Adjust these paths based on your osxcross installation +set(OSXCROSS_ROOT "$ENV{HOME}/osxcross" CACHE PATH "Path to osxcross installation") +set(OSXCROSS_TARGET "darwin20.4" CACHE STRING "osxcross target") + +# Cross-compiler +set(CMAKE_C_COMPILER "${OSXCROSS_ROOT}/bin/x86_64-apple-${OSXCROSS_TARGET}-clang") +set(CMAKE_CXX_COMPILER "${OSXCROSS_ROOT}/bin/x86_64-apple-${OSXCROSS_TARGET}-clang++") + +# Target environment +set(CMAKE_FIND_ROOT_PATH "${OSXCROSS_ROOT}/SDK/MacOSX11.3.sdk") + +# Search paths +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# macOS deployment target +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum macOS version") + +# Disable Linux-specific features for macOS target +set(WITH_LINUX_SYS OFF CACHE BOOL "Disable Linux-specific sys interfaces for macOS") diff --git a/cmake/toolchains/mingw-w64-x86_64.cmake b/cmake/toolchains/mingw-w64-x86_64.cmake new file mode 100644 index 0000000..a889463 --- /dev/null +++ b/cmake/toolchains/mingw-w64-x86_64.cmake @@ -0,0 +1,31 @@ +# Example CMake toolchain file for cross-compiling to Windows (MinGW-w64) +# +# Usage: +# cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64-x86_64.cmake .. +# +# Prerequisites: +# - MinGW-w64 cross-compiler (x86_64-w64-mingw32-g++) +# - Cross-compiled dependencies + +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +# Cross-compiler +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +# Target environment +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) + +# Search paths +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Executable suffix +set(CMAKE_EXECUTABLE_SUFFIX ".exe") + +# Disable Linux-specific features for Windows target +set(WITH_LINUX_SYS OFF CACHE BOOL "Disable Linux-specific sys interfaces for Windows") diff --git a/cmake/toolchains/musl-linux.cmake b/cmake/toolchains/musl-linux.cmake new file mode 100644 index 0000000..e4abbf8 --- /dev/null +++ b/cmake/toolchains/musl-linux.cmake @@ -0,0 +1,31 @@ +# Example CMake toolchain file for musl libc (Alpine Linux / embedded) +# +# Usage: +# cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/musl-linux.cmake .. +# +# Prerequisites: +# - musl-gcc or a musl-based cross-compiler +# - Cross-compiled dependencies + +set(CMAKE_SYSTEM_NAME Linux) + +# Option 1: Using musl-gcc wrapper (on systems with musl installed) +# set(CMAKE_C_COMPILER musl-gcc) +# set(CMAKE_CXX_COMPILER musl-g++) + +# Option 2: Using musl cross-compiler (adjust prefix as needed) +set(MUSL_PREFIX "x86_64-linux-musl" CACHE STRING "musl cross-compiler prefix") +set(CMAKE_C_COMPILER "${MUSL_PREFIX}-gcc") +set(CMAKE_CXX_COMPILER "${MUSL_PREFIX}-g++") + +# Target environment (adjust based on your setup) +set(CMAKE_FIND_ROOT_PATH "/usr/${MUSL_PREFIX}") + +# Search paths +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# musl typically prefers static linking +set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build static libraries for musl") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1bb01c6..7849493 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,43 +1,174 @@ +# Set configuration variables for YgorDefinitions.h +# These must be set before configure_file is called +if(WITH_LINUX_SYS) + set(YGOR_USE_LINUX_SYS 1) +endif() +if(WITH_EIGEN) + set(YGOR_USE_EIGEN 1) +endif() +if(WITH_GNU_GSL) + set(YGOR_USE_GNU_GSL 1) +endif() +if(WITH_BOOST) + set(YGOR_USE_BOOST 1) +endif() -configure_file(YgorDefinitions.h.in - ${CMAKE_CURRENT_BINARY_DIR}/YgorDefinitions.h) - -FILE(GLOB ygor_sources "*cc") -FILE(GLOB ygor_headers "*.h" - ${CMAKE_CURRENT_BINARY_DIR}/YgorDefinitions.h) +# Generate configuration header +configure_file(YgorDefinitions.h.in + ${CMAKE_CURRENT_BINARY_DIR}/YgorDefinitions.h +) -add_library(ygor - ${ygor_sources} +# Source files - explicitly listed for better dependency tracking +set(YGOR_SOURCES + YgorAlgorithms.cc + YgorBase64.cc + YgorCONFIGTools.cc + YgorContainers.cc + YgorDICOMTools.cc + YgorEnvironment.cc + YgorFilesDirs.cc + YgorImages.cc + YgorImagesIO.cc + YgorLog.cc + YgorMath.cc + YgorMathBSpline.cc + YgorMathChebyshev.cc + YgorMathChebyshevFunctions.cc + YgorMathIOOBJ.cc + YgorMathIOOFF.cc + YgorMathIOPLY.cc + YgorMathIOSTL.cc + YgorMathIOSVG.cc + YgorMathIOXYZ.cc + YgorMath_Samples.cc + YgorNetworking.cc + YgorNoise.cc + YgorPerformance.cc + YgorPlot.cc + YgorSerialize.cc + YgorStats.cc + YgorString.cc + YgorTAR.cc + YgorTime.cc External/SpookyHash/SpookyV2.cpp External/MD5/md5.cc ) -#set_target_properties(ygor PROPERTIES -# POSITION_INDEPENDENT_CODE TRUE -#) + +# Header files for installation +set(YGOR_HEADERS + YgorAlgorithms.h + YgorArguments.h + YgorBase64.h + YgorCONFIGTools.h + YgorContainers.h + YgorDICOMTools.h + YgorEnvironment.h + YgorFilesDirs.h + YgorIO.h + YgorImages.h + YgorImagesIO.h + YgorImagesIOBoostSerialization.h + YgorImagesPlotting.h + YgorLog.h + YgorMath.h + YgorMathBSpline.h + YgorMathChebyshev.h + YgorMathChebyshevFunctions.h + YgorMathChebyshevIOBoostSerialization.h + YgorMathIOBoostSerialization.h + YgorMathIOOBJ.h + YgorMathIOOFF.h + YgorMathIOPLY.h + YgorMathIOSTL.h + YgorMathIOSVG.h + YgorMathIOXYZ.h + YgorMathPlotting.h + YgorMathPlottingGnuplot.h + YgorMathPlottingVTK.h + YgorMath_Samples.h + YgorMisc.h + YgorNetworking.h + YgorNoise.h + YgorPerformance.h + YgorPlot.h + YgorSerialize.h + YgorStats.h + YgorString.h + YgorTAR.h + YgorTime.h + ${CMAKE_CURRENT_BINARY_DIR}/YgorDefinitions.h +) + +# Create the library +add_library(ygor ${YGOR_SOURCES}) +add_library(Ygor::ygor ALIAS ygor) + +# Set library properties +set_target_properties(ygor PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + POSITION_INDEPENDENT_CODE ON + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF +) + +# Include directories with proper visibility target_include_directories(ygor - SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC + $ + $ + $ ) +# Link compile options target_link_libraries(ygor - m - "${STD_FS_LIB}" - Threads::Threads + PUBLIC + Ygor::CompileOptions + PRIVATE + $<$,$,$>:Ygor::SanitizerOptions> ) -if(WITH_GNU_GSL) + +# Core dependencies target_link_libraries(ygor - "$<$:${GNU_GSL_LIBRARIES}>" + PUBLIC + Threads::Threads + PRIVATE + m + $<$:${YGOR_STD_FS_LIB}> ) + +# Optional Boost dependency +if(WITH_BOOST) + if(TARGET Boost::headers) + target_link_libraries(ygor PUBLIC Boost::headers) + elseif(Boost_INCLUDE_DIRS) + target_include_directories(ygor SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) + endif() endif() -# Installation info. -install(TARGETS ygor - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) +# Optional Eigen dependency +if(WITH_EIGEN) + if(TARGET Eigen3::Eigen) + target_link_libraries(ygor PUBLIC Eigen3::Eigen) + elseif(EIGEN3_INCLUDE_DIRS) + target_include_directories(ygor SYSTEM PUBLIC ${EIGEN3_INCLUDE_DIRS}) + endif() +endif() + +# Optional GNU GSL dependency +if(WITH_GNU_GSL) + if(GNU_GSL_LIBRARIES) + target_link_libraries(ygor PRIVATE ${GNU_GSL_LIBRARIES}) + endif() + if(GNU_GSL_INCLUDE_DIRS) + target_include_directories(ygor SYSTEM PRIVATE ${GNU_GSL_INCLUDE_DIRS}) + endif() +endif() -install(FILES ${ygor_headers} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +# Install headers +install(FILES ${YGOR_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} )