1954 lines
80 KiB
CMake
1954 lines
80 KiB
CMake
cmake_minimum_required(VERSION 3.20)
|
|
|
|
# Option to use Conan for dependency management
|
|
# Check if we're running with a Conan preset (the toolchain file will be in generators/)
|
|
if(CMAKE_TOOLCHAIN_FILE MATCHES "conan_toolchain\.cmake$")
|
|
set(YAMS_USE_CONAN ON CACHE BOOL "Using Conan package manager" FORCE)
|
|
else()
|
|
option(YAMS_USE_CONAN "Use Conan package manager for dependencies" OFF)
|
|
endif()
|
|
|
|
# Derive version from git tags or allow override via -DYAMS_VERSION=MAJOR.MINOR.PATCH
|
|
set(YAMS_VERSION "" CACHE STRING "Override Yams version (semver: MAJOR.MINOR.PATCH)")
|
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
|
|
if(NOT YAMS_VERSION)
|
|
find_package(Git QUIET)
|
|
if(Git_FOUND)
|
|
execute_process(
|
|
COMMAND "${GIT_EXECUTABLE}" describe --tags --match "v[0-9]*" --dirty --always
|
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
|
OUTPUT_VARIABLE GIT_DESCRIBE
|
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
)
|
|
# Strip leading 'v' and any suffix like -123-gabcdef
|
|
string(REGEX REPLACE "^v" "" YAMS_VERSION_CANDIDATE "${GIT_DESCRIBE}")
|
|
string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" YAMS_SEMVER "${YAMS_VERSION_CANDIDATE}")
|
|
if(NOT YAMS_SEMVER)
|
|
set(YAMS_SEMVER "0.0.0")
|
|
endif()
|
|
else()
|
|
set(YAMS_SEMVER "0.0.0")
|
|
endif()
|
|
else()
|
|
set(YAMS_SEMVER "${YAMS_VERSION}")
|
|
endif()
|
|
|
|
project(YAMS
|
|
VERSION ${YAMS_SEMVER}
|
|
DESCRIPTION "Yet Another Memory System - High-performance content-addressed storage"
|
|
LANGUAGES C CXX
|
|
)
|
|
|
|
# CMake module path and portable toolchain/sanitizers (no-op unless supported)
|
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
|
include(ToolchainDetect)
|
|
include(Sanitizers)
|
|
|
|
# Generate version header from project version
|
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated/yams")
|
|
|
|
# Write template for version header and configure it
|
|
set(_YAMS_VERSION_TEMPLATE "${CMAKE_CURRENT_BINARY_DIR}/yams_version.hpp.in")
|
|
file(WRITE "${_YAMS_VERSION_TEMPLATE}" "/* Auto-generated by CMake - Do not edit */\n#pragma once\n\n#define YAMS_VERSION_MAJOR @PROJECT_VERSION_MAJOR@\n#define YAMS_VERSION_MINOR @PROJECT_VERSION_MINOR@\n#define YAMS_VERSION_PATCH @PROJECT_VERSION_PATCH@\n#define YAMS_VERSION_STRING \"@PROJECT_VERSION@\"
|
|
")
|
|
|
|
configure_file(
|
|
"${_YAMS_VERSION_TEMPLATE}"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/generated/yams/version.hpp"
|
|
@ONLY
|
|
)
|
|
|
|
# Compile out spdlog debug/trace in Release-like builds
|
|
add_compile_definitions(
|
|
$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO>
|
|
)
|
|
|
|
# Ensure default CMake package dir uses 'Yams' casing for consumers
|
|
include(GNUInstallDirs)
|
|
|
|
# Install generated version header
|
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/generated/yams/version.hpp"
|
|
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/yams"
|
|
COMPONENT development)
|
|
set(YAMS_CMAKE_PACKAGE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/Yams"
|
|
CACHE PATH "Install dir for YAMS CMake package files")
|
|
|
|
# Modern CMake policies (set only if available)
|
|
if(POLICY CMP0077) # option() honors normal variables
|
|
cmake_policy(SET CMP0077 NEW)
|
|
endif()
|
|
if(POLICY CMP0126) # file(GENERATE) supports generator expressions in OUTPUT
|
|
cmake_policy(SET CMP0126 NEW)
|
|
endif()
|
|
if(POLICY CMP0135) # IN_LIST behavior and other modern improvements
|
|
cmake_policy(SET CMP0135 NEW)
|
|
endif()
|
|
# Silence FetchContent_Populate deprecation warnings on newer CMake
|
|
# CMP0169 deprecates FetchContent_Populate with declared details; keep OLD to avoid noise
|
|
if(POLICY CMP0169)
|
|
cmake_policy(SET CMP0169 OLD)
|
|
endif()
|
|
|
|
# C++ standard configuration (C++20-only)
|
|
set(CMAKE_CXX_STANDARD 20)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
message(STATUS "Using C++20 mode")
|
|
|
|
# Compiler version checks for C++20 mode
|
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11)
|
|
message(FATAL_ERROR "GCC ${CMAKE_CXX_COMPILER_VERSION} detected. GCC 11+ is required for C++20 support.")
|
|
endif()
|
|
message(STATUS "Using GCC ${CMAKE_CXX_COMPILER_VERSION} in C++20 mode")
|
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12)
|
|
message(FATAL_ERROR "Clang ${CMAKE_CXX_COMPILER_VERSION} detected. Clang 12+ is required for C++20 support.")
|
|
endif()
|
|
message(STATUS "Using Clang ${CMAKE_CXX_COMPILER_VERSION} in C++20 mode")
|
|
endif()
|
|
|
|
## Kept above in unified C++20 configuration
|
|
|
|
# Tracy profiler integration (Debug or tests via generator expressions)
|
|
add_library(yams_profiling INTERFACE)
|
|
|
|
find_package(tracy QUIET)
|
|
|
|
if(tracy_FOUND)
|
|
# Enable profiling target properties only for Debug or when tests are built
|
|
set(YAMS_ENABLE_PROFILING ON)
|
|
# Disable Tracy when sanitizers are enabled to avoid conflicts
|
|
target_compile_definitions(yams_profiling INTERFACE
|
|
$<$<AND:$<OR:$<CONFIG:Debug>,$<BOOL:${YAMS_BUILD_TESTS}>>,$<NOT:$<BOOL:${YAMS_ENABLE_SANITIZERS}>>>:TRACY_ENABLE;TRACY_ON_DEMAND>
|
|
)
|
|
target_link_libraries(yams_profiling INTERFACE
|
|
$<$<OR:$<CONFIG:Debug>,$<BOOL:${YAMS_BUILD_TESTS}>>:Tracy::TracyClient>
|
|
)
|
|
target_compile_options(yams_profiling INTERFACE
|
|
$<$<AND:$<OR:$<CONFIG:Debug>,$<BOOL:${YAMS_BUILD_TESTS}>>,$<BOOL:${UNIX}>>:-g -fno-omit-frame-pointer>
|
|
)
|
|
message(STATUS "Tracy profiler available; enabled for Debug or tests")
|
|
else()
|
|
set(YAMS_ENABLE_PROFILING OFF)
|
|
message(STATUS "Tracy profiler not found - profiling disabled")
|
|
endif()
|
|
|
|
# Check for std::format availability (C++20 project; allow fmt fallback)
|
|
include(CheckCXXSourceCompiles)
|
|
check_cxx_source_compiles("
|
|
#include <version>
|
|
#ifdef __cpp_lib_format
|
|
#include <format>
|
|
int main() {
|
|
auto s = std::format(\"{}\", 42);
|
|
(void)s;
|
|
return 0;
|
|
}
|
|
#else
|
|
#error No format support
|
|
#endif
|
|
" YAMS_HAS_STD_FORMAT)
|
|
|
|
if(YAMS_HAS_STD_FORMAT)
|
|
add_compile_definitions(YAMS_HAS_STD_FORMAT=1)
|
|
message(STATUS "std::format is available")
|
|
else()
|
|
add_compile_definitions(YAMS_HAS_STD_FORMAT=0)
|
|
message(STATUS "std::format is NOT available - using fmt fallback")
|
|
endif()
|
|
|
|
# Export compile commands for tooling
|
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
|
|
# Build profile options
|
|
set(YAMS_BUILD_PROFILE "custom" CACHE STRING "Build profile: release, dev, or custom")
|
|
set_property(CACHE YAMS_BUILD_PROFILE PROPERTY STRINGS release dev custom)
|
|
|
|
# Individual component options
|
|
option(YAMS_BUILD_CLI "Build CLI tool" ON)
|
|
option(YAMS_ENABLE_TUI "Enable TUI features in CLI" OFF)
|
|
option(YAMS_BUILD_MCP_SERVER "Build MCP server" ON)
|
|
option(YAMS_BUILD_MAINTENANCE_TOOLS "Build maintenance tools (gc, stats)" OFF)
|
|
option(YAMS_BUILD_TESTS "Build unit tests" OFF)
|
|
option(YAMS_BUILD_STRESS_TESTS "Build stress tests" OFF)
|
|
option(YAMS_BUILD_BENCHMARKS "Build benchmarks" OFF)
|
|
option(YAMS_ENABLE_SANITIZERS "Enable sanitizers in Debug" ON)
|
|
option(YAMS_ENABLE_COVERAGE "Enable code coverage" OFF)
|
|
option(YAMS_USE_VCPKG "Use vcpkg for dependencies" OFF)
|
|
option(YAMS_ENABLE_NATIVE_OPTIMIZATIONS "Enable -march=native on supported platforms" OFF)
|
|
option(YAMS_ENABLE_APPLE_SILICON_OPTIMIZATIONS "Enable Apple Silicon optimizations (auto-enabled on ARM64 Macs)" ON)
|
|
option(YAMS_CLANG_USE_LIBCXX "Use libc++ with Clang (Linux requires libc++ dev packages)" OFF)
|
|
option(YAMS_BUILD_API_DOCS "Build Doxygen API documentation" OFF)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Docs build (Pandoc) and embedded CLI help assets
|
|
# ------------------------------------------------------------------------------
|
|
option(YAMS_BUILD_DOCS "Build CLI docs, generate manpages, and embed verbose help" OFF)
|
|
find_program(PANDOC_EXECUTABLE pandoc)
|
|
|
|
set(CLI_MD "${CMAKE_SOURCE_DIR}/docs/user_guide/cli.md")
|
|
set(GEN_DIR "${CMAKE_BINARY_DIR}/generated/cli")
|
|
set(MAN_DIR "${CMAKE_BINARY_DIR}/man")
|
|
|
|
if(YAMS_BUILD_DOCS AND PANDOC_EXECUTABLE AND EXISTS "${CLI_MD}")
|
|
# Ensure output directories
|
|
file(MAKE_DIRECTORY "${GEN_DIR}")
|
|
file(MAKE_DIRECTORY "${MAN_DIR}")
|
|
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")
|
|
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/cmake")
|
|
|
|
# 1) Generate full plain-text help from Markdown
|
|
add_custom_command(
|
|
OUTPUT "${GEN_DIR}/cli_help.txt"
|
|
COMMAND "${PANDOC_EXECUTABLE}" -f markdown -t plain "${CLI_MD}" -o "${GEN_DIR}/cli_help.txt"
|
|
DEPENDS "${CLI_MD}"
|
|
COMMENT "Generating plain-text CLI help from Markdown"
|
|
VERBATIM
|
|
)
|
|
|
|
# 2) Generate yams(1) manpage from Markdown
|
|
add_custom_command(
|
|
OUTPUT "${MAN_DIR}/yams.1"
|
|
COMMAND "${PANDOC_EXECUTABLE}" -s -f markdown -t man "${CLI_MD}" -o "${MAN_DIR}/yams.1"
|
|
DEPENDS "${CLI_MD}"
|
|
COMMENT "Generating yams(1) manpage from Markdown"
|
|
VERBATIM
|
|
)
|
|
|
|
# 3) Embed script (converts a text file to a C++ header with a constexpr string)
|
|
set(EMBED_SCRIPT "${CMAKE_BINARY_DIR}/cmake/embed_text.cmake")
|
|
file(WRITE "${EMBED_SCRIPT}" [=[
|
|
if(NOT DEFINED INPUT)
|
|
message(FATAL_ERROR "INPUT not set")
|
|
endif()
|
|
if(NOT DEFINED OUTPUT)
|
|
message(FATAL_ERROR "OUTPUT not set")
|
|
endif()
|
|
|
|
# Optional: caller can pass -DSYMBOL=<name>. Defaults to VERBOSE.
|
|
if(NOT DEFINED SYMBOL)
|
|
set(SYMBOL "VERBOSE")
|
|
endif()
|
|
set(_SYM "${SYMBOL}")
|
|
string(TOUPPER "${_SYM}" _SYM)
|
|
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" _SYM "${_SYM}")
|
|
# Prefix command-specific symbols for clarity (keep full doc as VERBOSE)
|
|
if(NOT _SYM STREQUAL "VERBOSE")
|
|
set(_SYM "CMD_${_SYM}")
|
|
endif()
|
|
|
|
set(INPUT_CLEAN "${INPUT}")
|
|
set(OUTPUT_CLEAN "${OUTPUT}")
|
|
|
|
# Check if input file exists
|
|
if(NOT EXISTS "${INPUT_CLEAN}")
|
|
message(FATAL_ERROR "Input file does not exist: '${INPUT_CLEAN}'")
|
|
endif()
|
|
|
|
# Ensure output directory exists
|
|
get_filename_component(OUTPUT_DIR "${OUTPUT_CLEAN}" DIRECTORY)
|
|
if(NOT EXISTS "${OUTPUT_DIR}")
|
|
file(MAKE_DIRECTORY "${OUTPUT_DIR}")
|
|
endif()
|
|
|
|
file(READ "${INPUT_CLEAN}" _c)
|
|
# Escape for C string literal
|
|
string(REPLACE "\\" "\\\\" _c "${_c}")
|
|
string(REPLACE "\"" "\\\"" _c "${_c}")
|
|
string(REPLACE "\n" "\\n" _c "${_c}")
|
|
|
|
file(WRITE "${OUTPUT_CLEAN}" "// Generated by embed_text.cmake - do not edit\n#pragma once\nnamespace yams { namespace cli_help {\nconstexpr const char ${_SYM}[] = \"${_c}\";\n} } // namespace yams::cli_help\n")
|
|
]=])
|
|
|
|
# 4) Embed full verbose help into a header
|
|
add_custom_command(
|
|
OUTPUT "${CMAKE_BINARY_DIR}/generated/cli_help.hpp"
|
|
COMMAND "${CMAKE_COMMAND}" -DINPUT="${GEN_DIR}/cli_help.txt" -DOUTPUT="${CMAKE_BINARY_DIR}/generated/cli_help.hpp" -DSYMBOL=VERBOSE -P "${EMBED_SCRIPT}"
|
|
DEPENDS "${GEN_DIR}/cli_help.txt" "${EMBED_SCRIPT}"
|
|
COMMENT "Embedding full CLI help into header"
|
|
VERBATIM
|
|
)
|
|
|
|
# 5) Split per-command sections from the Markdown for detailed command help
|
|
# The splitter scans lines and collects sections for known commands.
|
|
set(SPLIT_SCRIPT "${CMAKE_BINARY_DIR}/cmake/split_cli_sections.cmake")
|
|
file(WRITE "${SPLIT_SCRIPT}" [=[
|
|
set(CLI_MD "${CLI_MD}")
|
|
set(OUT_DIR "${GEN_DIR}")
|
|
# Commands to extract (must match headings like '## <name>' or '## <name> {#cmd-name}')
|
|
set(COMMANDS init add get delete list search config auth stats uninstall migrate browse serve)
|
|
|
|
file(STRINGS "${CLI_MD}" LINES)
|
|
set(current "")
|
|
set(in_section FALSE)
|
|
set(content "")
|
|
|
|
function(_flush_section name)
|
|
if(NOT "${name}" STREQUAL "")
|
|
set(path "${OUT_DIR}/cli_help_${name}.txt")
|
|
file(WRITE "${OUT_DIR}/tmp_${name}.txt" "${content}")
|
|
file(RENAME "${OUT_DIR}/tmp_${name}.txt" "${path}")
|
|
endif()
|
|
endfunction()
|
|
|
|
foreach(line IN LISTS LINES)
|
|
if(line MATCHES "^##[ ]+([A-Za-z0-9_-]+)[ ]*(\{#cmd-.*\})?")
|
|
string(REGEX REPLACE "^##[ ]+([A-Za-z0-9_-]+).*$" \1 heading "${line}")
|
|
# Close previous section if any
|
|
if(in_section)
|
|
_flush_section("${current}")
|
|
set(content "")
|
|
endif()
|
|
# Start a new section only if the heading is one of our commands
|
|
list(FIND COMMANDS "${heading}" idx)
|
|
if(NOT idx EQUAL -1)
|
|
set(current "${heading}")
|
|
set(in_section TRUE)
|
|
set(content "${line}\n")
|
|
else()
|
|
set(current "")
|
|
set(in_section FALSE)
|
|
set(content "")
|
|
endif()
|
|
else()
|
|
if(in_section)
|
|
set(content "${content}${line}\n")
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
|
|
# Flush the last open section
|
|
if(in_section)
|
|
_flush_section("${current}")
|
|
endif()
|
|
]=])
|
|
|
|
add_custom_command(
|
|
OUTPUT
|
|
"${GEN_DIR}/cli_help_init.txt"
|
|
"${GEN_DIR}/cli_help_add.txt"
|
|
"${GEN_DIR}/cli_help_get.txt"
|
|
"${GEN_DIR}/cli_help_delete.txt"
|
|
"${GEN_DIR}/cli_help_list.txt"
|
|
"${GEN_DIR}/cli_help_search.txt"
|
|
"${GEN_DIR}/cli_help_config.txt"
|
|
"${GEN_DIR}/cli_help_auth.txt"
|
|
"${GEN_DIR}/cli_help_stats.txt"
|
|
"${GEN_DIR}/cli_help_uninstall.txt"
|
|
"${GEN_DIR}/cli_help_migrate.txt"
|
|
"${GEN_DIR}/cli_help_browse.txt"
|
|
"${GEN_DIR}/cli_help_serve.txt"
|
|
COMMAND "${CMAKE_COMMAND}" -P "${SPLIT_SCRIPT}"
|
|
DEPENDS "${CLI_MD}" "${SPLIT_SCRIPT}"
|
|
COMMENT "Splitting per-command CLI sections from Markdown"
|
|
VERBATIM
|
|
)
|
|
|
|
# 6) Embed per-command verbose help headers
|
|
set(EMBED_HEADERS)
|
|
foreach(cmd init add get delete list search config auth stats uninstall migrate browse serve)
|
|
set(infile "${GEN_DIR}/cli_help_${cmd}.txt")
|
|
set(outfile "${CMAKE_BINARY_DIR}/generated/cli_help_${cmd}.hpp")
|
|
add_custom_command(
|
|
OUTPUT "${outfile}"
|
|
COMMAND "${CMAKE_COMMAND}" -DINPUT="${infile}" -DOUTPUT="${outfile}" -DSYMBOL=${cmd} -P "${EMBED_SCRIPT}"
|
|
DEPENDS "${infile}" "${EMBED_SCRIPT}"
|
|
COMMENT "Embedding ${cmd} CLI help into header"
|
|
VERBATIM
|
|
)
|
|
list(APPEND EMBED_HEADERS "${outfile}")
|
|
endforeach()
|
|
|
|
# 7) Aggregate docs target
|
|
add_custom_target(docs
|
|
DEPENDS
|
|
"${CMAKE_BINARY_DIR}/generated/cli_help.hpp"
|
|
${EMBED_HEADERS}
|
|
"${MAN_DIR}/yams.1"
|
|
)
|
|
|
|
# 8) Install generated manpage
|
|
include(GNUInstallDirs)
|
|
install(FILES "${MAN_DIR}/yams.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" COMPONENT runtime)
|
|
|
|
# 9) Install data files (magic_numbers.json)
|
|
install(FILES "${CMAKE_SOURCE_DIR}/data/magic_numbers.json"
|
|
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/yams/data"
|
|
COMPONENT runtime)
|
|
|
|
# 10) Global compile definition to indicate embedded help is available
|
|
add_compile_definitions(YAMS_EMBEDDED_VERBOSE_HELP=1)
|
|
|
|
else()
|
|
if(YAMS_BUILD_DOCS AND NOT PANDOC_EXECUTABLE)
|
|
message(WARNING "YAMS_BUILD_DOCS=ON but Pandoc was not found. Install Pandoc and re-configure CMake.
|
|
- macOS: brew install pandoc
|
|
- Ubuntu/Debian: sudo apt-get update && sudo apt-get install -y pandoc
|
|
- Fedora: sudo dnf install -y pandoc
|
|
- Arch: sudo pacman -S pandoc
|
|
- Windows (PowerShell): choco install pandoc or scoop install pandoc")
|
|
elseif(YAMS_BUILD_DOCS AND NOT EXISTS "${CLI_MD}")
|
|
message(WARNING "YAMS_BUILD_DOCS=ON but CLI markdown not found at ${CLI_MD}; skipping docs build.")
|
|
else()
|
|
message(STATUS "Docs build disabled; verbose help embedding and manpage generation are skipped.")
|
|
endif()
|
|
endif()
|
|
|
|
# Doxygen-based API docs
|
|
if(YAMS_BUILD_API_DOCS)
|
|
find_package(Doxygen QUIET)
|
|
if(DOXYGEN_FOUND)
|
|
set(DOXYGEN_OUTPUT_DIR "${CMAKE_BINARY_DIR}/docs/api")
|
|
file(MAKE_DIRECTORY "${DOXYGEN_OUTPUT_DIR}")
|
|
set(DOXYFILE_IN "${CMAKE_BINARY_DIR}/cmake/Doxyfile.in")
|
|
set(DOXYFILE_OUT "${CMAKE_BINARY_DIR}/Doxyfile")
|
|
file(WRITE "${DOXYFILE_IN}"
|
|
"PROJECT_NAME = \"YAMS\"
|
|
OUTPUT_DIRECTORY = \"${DOXYGEN_OUTPUT_DIR}\"
|
|
GENERATE_HTML = YES
|
|
GENERATE_LATEX = NO
|
|
QUIET = YES
|
|
WARN_AS_ERROR = NO
|
|
INPUT = \"${CMAKE_SOURCE_DIR}/include\"
|
|
RECURSIVE = YES
|
|
EXTRACT_PRIVATE = NO
|
|
")
|
|
configure_file("${DOXYFILE_IN}" "${DOXYFILE_OUT}" @ONLY)
|
|
add_custom_target(docs-api
|
|
COMMAND "${DOXYGEN_EXECUTABLE}" "${DOXYFILE_OUT}"
|
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
|
COMMENT "Generating API documentation with Doxygen"
|
|
VERBATIM
|
|
)
|
|
else()
|
|
message(WARNING "YAMS_BUILD_API_DOCS=ON but Doxygen not found. Install doxygen to enable API docs.")
|
|
endif()
|
|
endif()
|
|
|
|
# Configure based on build profile
|
|
if(YAMS_BUILD_PROFILE STREQUAL "release")
|
|
message(STATUS "Using RELEASE build profile - CLI and MCP server only")
|
|
set(YAMS_BUILD_CLI ON CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_MCP_SERVER ON CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_MAINTENANCE_TOOLS OFF CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_STRESS_TESTS OFF CACHE BOOL "" FORCE)
|
|
if(NOT CMAKE_BUILD_TYPE)
|
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
|
|
endif()
|
|
elseif(YAMS_BUILD_PROFILE STREQUAL "dev")
|
|
message(STATUS "Using DEVELOPMENT build profile - all components")
|
|
set(YAMS_BUILD_CLI ON CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_MCP_SERVER ON CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_MAINTENANCE_TOOLS ON CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_TESTS ON CACHE BOOL "" FORCE)
|
|
set(YAMS_BUILD_BENCHMARKS ON CACHE BOOL "" FORCE)
|
|
if(NOT CMAKE_BUILD_TYPE)
|
|
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
|
|
endif()
|
|
else()
|
|
message(STATUS "Using CUSTOM build profile - individual options apply")
|
|
# Set a sensible default build type for single-config generators when unset
|
|
if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
|
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel)
|
|
endif()
|
|
endif()
|
|
|
|
# Legacy compatibility
|
|
set(YAMS_BUILD_TOOLS ${YAMS_BUILD_CLI})
|
|
|
|
# C++20 feature detection
|
|
include(CheckCXXSourceCompiles)
|
|
check_cxx_source_compiles("
|
|
#include <concepts>
|
|
template<typename T>
|
|
concept Integral = std::integral<T>;
|
|
int main() { return 0; }
|
|
" YAMS_HAS_CXX20_CONCEPTS)
|
|
|
|
if(NOT YAMS_HAS_CXX20_CONCEPTS)
|
|
message(WARNING "concepts appear unavailable; features using <concepts> may be limited")
|
|
add_compile_definitions(YAMS_HAS_CXX20_CONCEPTS=0)
|
|
else()
|
|
add_compile_definitions(YAMS_HAS_CXX20_CONCEPTS=1)
|
|
message(STATUS "concepts available")
|
|
endif()
|
|
|
|
# Compiler warnings
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
add_compile_options(
|
|
-Wall -Wextra -Wpedantic
|
|
-Wcast-align -Wcast-qual
|
|
-Wdouble-promotion -Wformat=2
|
|
-Wshadow
|
|
$<$<COMPILE_LANGUAGE:CXX>:-Wnon-virtual-dtor>
|
|
$<$<CONFIG:Debug>:-Og>
|
|
$<$<CONFIG:Debug>:-g3>
|
|
$<$<CONFIG:Release>:-O3>
|
|
)
|
|
|
|
# Architecture-specific optimizations
|
|
if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
|
|
# Apple Silicon (M1/M2/M3) optimizations
|
|
if(YAMS_ENABLE_APPLE_SILICON_OPTIMIZATIONS)
|
|
add_compile_options($<$<CONFIG:Release>:-mcpu=apple-m1>)
|
|
add_compile_options($<$<CONFIG:Release>:-fvectorize>)
|
|
message(STATUS "Apple Silicon optimizations enabled (-mcpu=apple-m1)")
|
|
endif()
|
|
elseif(YAMS_ENABLE_NATIVE_OPTIMIZATIONS)
|
|
# x86_64 and other architectures
|
|
add_compile_options($<$<CONFIG:Release>:-march=native>)
|
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
|
add_compile_options($<$<CONFIG:Release>:-mtune=native>)
|
|
endif()
|
|
message(STATUS "Native optimizations enabled (-march=native)")
|
|
endif()
|
|
|
|
# Coroutine support
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|
add_compile_options(-fcoroutines)
|
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
# libc++ selection:
|
|
# * Apple toolchains default to libc++ (no flag needed, but harmless if passed)
|
|
# * On Linux we only switch to libc++ when the user explicitly opts in via
|
|
# YAMS_CLANG_USE_LIBCXX=ON because many dependency recipes are built
|
|
# against libstdc++ by default. Forcing -stdlib=libc++ unconditionally
|
|
# can cause ABI / link mismatches when mixing dependency stdlibs.
|
|
if(APPLE OR YAMS_CLANG_USE_LIBCXX)
|
|
add_compile_options(-stdlib=libc++)
|
|
add_link_options(-stdlib=libc++)
|
|
endif()
|
|
# Modern Clang (including Apple Clang 14+/LLVM 12+) implements C++20 coroutines
|
|
# without requiring the legacy experimental switch (-fcoroutines-ts). Using the
|
|
# flag now produces warnings or errors on some distributions. We rely on the
|
|
# C++20 standard mode and a probe below to warn if <coroutine> is unavailable.
|
|
# Defensive scrub: If an external toolchain file, cached build dir, or
|
|
# environment variable re-injects the legacy -fcoroutines-ts flag into
|
|
# the global CMake cache variables, remove it. CMake does not support
|
|
# calling commands inline inside an if() expression, so we perform the
|
|
# find + conditional replacement in two steps per variable.
|
|
foreach(_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG)
|
|
if(DEFINED ${_var})
|
|
string(FIND "${${_var}}" "-fcoroutines-ts" _yams_flag_pos)
|
|
if(_yams_flag_pos GREATER -1)
|
|
string(REPLACE "-fcoroutines-ts" "" ${_var} "${${_var}}")
|
|
# Optional: collapse any duplicate spaces introduced
|
|
string(REGEX REPLACE " +" " " ${_var} "${${_var}}")
|
|
message(STATUS "Removed legacy -fcoroutines-ts from ${_var}")
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
# Optional coroutine capability probe (warn-only, runs after flags above)
|
|
check_cxx_source_compiles("
|
|
#include <coroutine>
|
|
struct task {
|
|
struct promise_type {
|
|
task get_return_object() { return {}; }
|
|
std::suspend_never initial_suspend() noexcept { return {}; }
|
|
std::suspend_never final_suspend() noexcept { return {}; }
|
|
void return_void() noexcept {}
|
|
void unhandled_exception() {}
|
|
};
|
|
};
|
|
task foo() { co_return; }
|
|
int main() { foo(); return 0; }
|
|
" HAVE_CXX20_COROUTINES)
|
|
|
|
if(NOT HAVE_CXX20_COROUTINES)
|
|
message(WARNING "coroutines appear unsupported with current flags; features requiring <coroutine> may fail to build.")
|
|
endif()
|
|
elseif(MSVC)
|
|
add_compile_options(
|
|
/W4 /permissive- /Zc:__cplusplus
|
|
/Zc:preprocessor # For proper __VA_OPT__ support
|
|
$<$<CONFIG:Debug>:/Od /RTC1>
|
|
$<$<CONFIG:Release>:/O2 /GL>
|
|
)
|
|
add_link_options($<$<CONFIG:Release>:/LTCG>)
|
|
endif()
|
|
|
|
# Sanitizers
|
|
if(YAMS_ENABLE_SANITIZERS AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer)
|
|
add_link_options(-fsanitize=address,undefined)
|
|
endif()
|
|
endif()
|
|
|
|
# Code Coverage
|
|
if(YAMS_ENABLE_COVERAGE)
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|
add_compile_options(-fprofile-arcs -ftest-coverage)
|
|
add_link_options(-fprofile-arcs -ftest-coverage)
|
|
set(COVERAGE_LIBRARIES gcov)
|
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
|
|
add_link_options(-fprofile-instr-generate -fcoverage-mapping)
|
|
endif()
|
|
|
|
# Find coverage tools
|
|
find_program(GCOVR_PATH gcovr)
|
|
find_program(LCOV_PATH lcov)
|
|
find_program(GENHTML_PATH genhtml)
|
|
|
|
if(GCOVR_PATH)
|
|
message(STATUS "Found gcovr: ${GCOVR_PATH}")
|
|
elseif(LCOV_PATH AND GENHTML_PATH)
|
|
message(STATUS "Found lcov: ${LCOV_PATH}")
|
|
message(STATUS "Found genhtml: ${GENHTML_PATH}")
|
|
else()
|
|
message(WARNING "No coverage report generator found. Install gcovr or lcov for coverage reports.")
|
|
endif()
|
|
|
|
message(STATUS "Code coverage enabled")
|
|
endif()
|
|
|
|
|
|
|
|
|
|
# Dependencies
|
|
include(FetchContent)
|
|
set(FETCHCONTENT_QUIET FALSE)
|
|
|
|
# OpenSSL
|
|
if(APPLE)
|
|
# Help CMake find Homebrew's OpenSSL
|
|
set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT_DIR} /opt/homebrew/opt/openssl@3 /usr/local/opt/openssl@3)
|
|
endif()
|
|
find_package(OpenSSL REQUIRED)
|
|
message(STATUS "Found OpenSSL: ${OPENSSL_VERSION}")
|
|
message(STATUS "OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
|
message(STATUS "OpenSSL crypto library: ${OPENSSL_CRYPTO_LIBRARY}")
|
|
|
|
# ZLIB (for CRC32 and compression)
|
|
find_package(ZLIB REQUIRED)
|
|
message(STATUS "Found ZLIB: ${ZLIB_VERSION_STRING}")
|
|
|
|
# libcurl (HTTP client) - optional in non-Conan builds to ease local setup
|
|
set(YAMS_REQUIRE_CURL "ON" CACHE BOOL "Require libcurl for HTTP features (set OFF to skip)" )
|
|
if(YAMS_USE_CONAN OR YAMS_REQUIRE_CURL)
|
|
find_package(CURL REQUIRED)
|
|
message(STATUS "Found libcurl: ${CURL_VERSION_STRING}")
|
|
if(NOT TARGET CURL::libcurl)
|
|
message(FATAL_ERROR "CURL::libcurl target not found after find_package(CURL)")
|
|
endif()
|
|
|
|
# Provide a stable interface target for downloader code
|
|
add_library(yams_curl INTERFACE)
|
|
target_link_libraries(yams_curl INTERFACE CURL::libcurl)
|
|
add_library(yams::curl ALIAS yams_curl)
|
|
install(TARGETS yams_curl EXPORT YamsTargets)
|
|
else()
|
|
message(WARNING "libcurl not found or disabled (YAMS_REQUIRE_CURL=OFF). Downloader features will be unavailable.")
|
|
# Create a dummy interface to satisfy targets depending on yams::curl when HTTP is unused
|
|
add_library(yams_curl INTERFACE)
|
|
add_library(yams::curl ALIAS yams_curl)
|
|
endif()
|
|
|
|
# Threads (required for websocketpp)
|
|
find_package(Threads REQUIRED)
|
|
|
|
# Ncurses detection for Linux builds
|
|
if(YAMS_BUILD_CLI AND NOT YAMS_USE_CONAN)
|
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
# Look for ncurses with wide character support first
|
|
find_package(Curses)
|
|
if(NOT CURSES_FOUND)
|
|
# Try pkg-config as fallback
|
|
find_package(PkgConfig)
|
|
if(PKG_CONFIG_FOUND)
|
|
pkg_check_modules(NCURSES ncursesw)
|
|
if(NOT NCURSES_FOUND)
|
|
pkg_check_modules(NCURSES ncurses)
|
|
endif()
|
|
endif()
|
|
|
|
if(NOT NCURSES_FOUND)
|
|
message(FATAL_ERROR
|
|
"ncurses library not found. Please install it:\n"
|
|
" Ubuntu/Debian: sudo apt-get install libncurses-dev\n"
|
|
" RHEL/CentOS: sudo yum install ncurses-devel\n"
|
|
" Fedora: sudo dnf install ncurses-devel\n"
|
|
" Arch: sudo pacman -S ncurses")
|
|
endif()
|
|
else()
|
|
message(STATUS "Found Curses: ${CURSES_LIBRARIES}")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
# Google Test - only use FetchContent when not using Conan
|
|
if(YAMS_BUILD_TESTS AND NOT YAMS_USE_CONAN)
|
|
FetchContent_Declare(
|
|
googletest
|
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
GIT_TAG v1.14.0
|
|
)
|
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(googletest)
|
|
endif()
|
|
|
|
# Google Benchmark
|
|
# Handle benchmarks - only use FetchContent when not using Conan
|
|
if(YAMS_BUILD_BENCHMARKS AND NOT YAMS_USE_CONAN)
|
|
FetchContent_Declare(
|
|
benchmark
|
|
GIT_REPOSITORY https://github.com/google/benchmark.git
|
|
GIT_TAG v1.8.3
|
|
)
|
|
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
|
|
|
# Disable werror for benchmark library to avoid strict warning issues
|
|
set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE)
|
|
|
|
FetchContent_MakeAvailable(benchmark)
|
|
|
|
# Apply warning suppressions to benchmark target after it's created
|
|
if(TARGET benchmark)
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
target_compile_options(benchmark PRIVATE
|
|
-Wno-sign-conversion
|
|
-Wno-format-nonliteral
|
|
-Wno-double-promotion
|
|
-Wno-conversion
|
|
-Wno-old-style-cast)
|
|
endif()
|
|
endif()
|
|
|
|
if(TARGET benchmark_main)
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
target_compile_options(benchmark_main PRIVATE
|
|
-Wno-sign-conversion
|
|
-Wno-format-nonliteral
|
|
-Wno-double-promotion
|
|
-Wno-conversion
|
|
-Wno-old-style-cast)
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
# Dependency management
|
|
if(YAMS_USE_CONAN)
|
|
# When using Conan, dependencies are provided via find_package
|
|
find_package(spdlog REQUIRED)
|
|
find_package(nlohmann_json REQUIRED)
|
|
find_package(CLI11 REQUIRED)
|
|
find_package(ZLIB REQUIRED)
|
|
find_package(zstd REQUIRED)
|
|
find_package(SQLite3 REQUIRED)
|
|
find_package(Protobuf REQUIRED)
|
|
find_package(OpenSSL REQUIRED)
|
|
if(YAMS_ENABLE_ONNX)
|
|
find_package(onnxruntime REQUIRED)
|
|
endif()
|
|
find_package(tl-expected REQUIRED)
|
|
# Boost headers (for Asio) are provided by Conan; import targets to expose include dirs
|
|
find_package(Boost REQUIRED CONFIG)
|
|
# Provide a stable interface target to propagate Boost headers (Asio is header-only)
|
|
if(NOT TARGET yams_boost_headers)
|
|
add_library(yams_boost_headers INTERFACE)
|
|
# Prefer modern Boost CMake targets from Conan
|
|
if(TARGET Boost::headers)
|
|
target_link_libraries(yams_boost_headers INTERFACE Boost::headers)
|
|
get_target_property(_boost_inc_dirs Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
|
|
if(_boost_inc_dirs)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_inc_dirs})
|
|
endif()
|
|
# Some Conan configs store include dirs in INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
|
|
get_target_property(_boost_sys_inc_dirs Boost::headers INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
|
|
if(_boost_sys_inc_dirs)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_sys_inc_dirs})
|
|
endif()
|
|
elseif(TARGET Boost::boost)
|
|
target_link_libraries(yams_boost_headers INTERFACE Boost::boost)
|
|
get_target_property(_boost_inc_dirs2 Boost::boost INTERFACE_INCLUDE_DIRECTORIES)
|
|
if(_boost_inc_dirs2)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_inc_dirs2})
|
|
endif()
|
|
get_target_property(_boost_sys_inc_dirs2 Boost::boost INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
|
|
if(_boost_sys_inc_dirs2)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_sys_inc_dirs2})
|
|
endif()
|
|
elseif(TARGET boost::boost)
|
|
target_link_libraries(yams_boost_headers INTERFACE boost::boost)
|
|
get_target_property(_boost_inc_dirs3 boost::boost INTERFACE_INCLUDE_DIRECTORIES)
|
|
if(_boost_inc_dirs3)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_inc_dirs3})
|
|
endif()
|
|
get_target_property(_boost_sys_inc_dirs3 boost::boost INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
|
|
if(_boost_sys_inc_dirs3)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_sys_inc_dirs3})
|
|
endif()
|
|
endif()
|
|
# Some Conan profiles expose Boost include dirs on the internal aggregate target
|
|
if(TARGET boost::_libboost)
|
|
# Ensure we link and propagate include directories from the monolithic Boost target
|
|
target_link_libraries(yams_boost_headers INTERFACE boost::_libboost)
|
|
get_target_property(_boost_inc_dirs4 boost::_libboost INTERFACE_INCLUDE_DIRECTORIES)
|
|
if(_boost_inc_dirs4)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_inc_dirs4})
|
|
endif()
|
|
get_target_property(_boost_sys_inc_dirs4 boost::_libboost INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
|
|
if(_boost_sys_inc_dirs4)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_sys_inc_dirs4})
|
|
endif()
|
|
endif()
|
|
# Fallback to Boost_INCLUDE_DIRS variable if present
|
|
if(Boost_INCLUDE_DIRS)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${Boost_INCLUDE_DIRS})
|
|
endif()
|
|
|
|
# Hard fallback for Conan CMakeDeps profiles that don't populate INTERFACE include dirs:
|
|
# inject Conan package include roots discovered from boost_PACKAGE_FOLDER_<CONFIG> variables.
|
|
set(_yams_boost_pkg_roots)
|
|
foreach(_cfg DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
|
|
if(DEFINED boost_PACKAGE_FOLDER_${_cfg})
|
|
list(APPEND _yams_boost_pkg_roots "${boost_PACKAGE_FOLDER_${_cfg}}")
|
|
endif()
|
|
endforeach()
|
|
list(REMOVE_DUPLICATES _yams_boost_pkg_roots)
|
|
|
|
set(_yams_boost_include_candidates)
|
|
foreach(_pkg IN LISTS _yams_boost_pkg_roots)
|
|
if(EXISTS "${_pkg}/include/boost/version.hpp")
|
|
list(APPEND _yams_boost_include_candidates "${_pkg}/include")
|
|
elseif(EXISTS "${_pkg}/include/boost/asio.hpp")
|
|
list(APPEND _yams_boost_include_candidates "${_pkg}/include")
|
|
elseif(EXISTS "${_pkg}/include/boost/asio/local/stream_protocol.hpp")
|
|
list(APPEND _yams_boost_include_candidates "${_pkg}/include")
|
|
endif()
|
|
endforeach()
|
|
list(REMOVE_DUPLICATES _yams_boost_include_candidates)
|
|
|
|
if(_yams_boost_include_candidates)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_yams_boost_include_candidates})
|
|
message(STATUS "yams_boost_headers: injected Boost include dirs from Conan package: ${_yams_boost_include_candidates}")
|
|
else()
|
|
message(STATUS "yams_boost_headers: no Conan Boost include fallback injected (targets/variables likely provided includes)")
|
|
endif()
|
|
endif()
|
|
# Ensure the interface target is part of the export set when installing
|
|
if(TARGET yams_boost_headers)
|
|
install(TARGETS yams_boost_headers EXPORT YamsTargets)
|
|
endif()
|
|
# fmt for std::format fallback
|
|
if(NOT YAMS_HAS_STD_FORMAT)
|
|
find_package(fmt REQUIRED)
|
|
endif()
|
|
if(YAMS_BUILD_CLI)
|
|
find_package(Curses REQUIRED)
|
|
endif()
|
|
# MCP server no longer requires Drogon or Boost
|
|
if(YAMS_BUILD_TESTS)
|
|
# Try Conan-provided GTest packages first (CONFIG mode)
|
|
set(_yams_gtest_found FALSE)
|
|
find_package(GTest QUIET CONFIG)
|
|
if(GTest_FOUND)
|
|
set(_yams_gtest_found TRUE)
|
|
message(STATUS "Conan build: Found GTest package (GTest:: targets)")
|
|
else()
|
|
find_package(gtest QUIET CONFIG)
|
|
if(gtest_FOUND)
|
|
set(_yams_gtest_found TRUE)
|
|
message(STATUS "Conan build: Found gtest package (gtest:: targets)")
|
|
endif()
|
|
endif()
|
|
|
|
# Always make a local googletest available so we can prefer its targets
|
|
# to avoid picking up sanitized system installations (e.g., /usr/local)
|
|
include(FetchContent)
|
|
if(NOT TARGET gtest)
|
|
FetchContent_Declare(
|
|
googletest
|
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
GIT_TAG v1.14.0
|
|
GIT_SHALLOW TRUE
|
|
)
|
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(googletest)
|
|
endif()
|
|
# Provide compatibility aliases if only lowercase targets exist
|
|
if(TARGET gtest AND NOT TARGET GTest::gtest)
|
|
add_library(GTest::gtest ALIAS gtest)
|
|
endif()
|
|
if(TARGET gtest_main AND NOT TARGET GTest::gtest_main)
|
|
add_library(GTest::gtest_main ALIAS gtest_main)
|
|
endif()
|
|
if(TARGET gmock AND NOT TARGET GTest::gmock)
|
|
add_library(GTest::gmock ALIAS gmock)
|
|
endif()
|
|
if(TARGET gmock_main AND NOT TARGET GTest::gmock_main)
|
|
add_library(GTest::gmock_main ALIAS gmock_main)
|
|
endif()
|
|
|
|
# Log which targets ended up available
|
|
if(TARGET gtest::gtest)
|
|
message(STATUS "Tests: gtest::gtest target is available")
|
|
endif()
|
|
if(TARGET GTest::gtest)
|
|
message(STATUS "Tests: GTest::gtest target is available")
|
|
endif()
|
|
if(TARGET GTest::GTest)
|
|
message(STATUS "Tests: GTest::GTest target is available")
|
|
endif()
|
|
endif()
|
|
if(YAMS_BUILD_BENCHMARKS)
|
|
find_package(benchmark QUIET CONFIG)
|
|
if(NOT benchmark_FOUND)
|
|
message(WARNING "Conan build: Google Benchmark not found via CMake package. Falling back to FetchContent.")
|
|
include(FetchContent)
|
|
FetchContent_Declare(
|
|
benchmark
|
|
GIT_REPOSITORY https://github.com/google/benchmark.git
|
|
GIT_TAG v1.8.3
|
|
GIT_SHALLOW TRUE
|
|
)
|
|
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
|
set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(benchmark)
|
|
# Provide namespace alias if missing
|
|
if(TARGET benchmark AND NOT TARGET benchmark::benchmark)
|
|
add_library(benchmark::benchmark ALIAS benchmark)
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
# When not using Conan, use FetchContent for dependencies
|
|
message(STATUS "YAMS_USE_CONAN is: ${YAMS_USE_CONAN}")
|
|
if(NOT YAMS_USE_CONAN)
|
|
message(STATUS "Using FetchContent for dependencies (non-Conan mode)")
|
|
|
|
# fmt for std::format fallback when not available
|
|
if(NOT YAMS_HAS_STD_FORMAT)
|
|
FetchContent_Declare(
|
|
fmt
|
|
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
|
|
GIT_TAG 10.2.1
|
|
)
|
|
set(FMT_INSTALL OFF CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(fmt)
|
|
message(STATUS "Using fmt library as std::format fallback")
|
|
endif()
|
|
|
|
# spdlog for logging
|
|
FetchContent_Declare(
|
|
spdlog
|
|
GIT_REPOSITORY https://github.com/gabime/spdlog.git
|
|
GIT_TAG v1.12.0
|
|
)
|
|
# Configure spdlog for proper installation
|
|
# Enable installation so spdlog can be exported with YAMS targets
|
|
set(SPDLOG_INSTALL ON CACHE BOOL "" FORCE)
|
|
set(SPDLOG_BUILD_SHARED OFF CACHE BOOL "" FORCE)
|
|
set(SPDLOG_FMT_EXTERNAL OFF CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(spdlog)
|
|
|
|
# nlohmann_json for JSON parsing
|
|
find_package(nlohmann_json 3.11 QUIET)
|
|
if(NOT nlohmann_json_FOUND)
|
|
FetchContent_Declare(
|
|
nlohmann_json
|
|
GIT_REPOSITORY https://github.com/nlohmann/json.git
|
|
GIT_TAG v3.11.3
|
|
)
|
|
FetchContent_MakeAvailable(nlohmann_json)
|
|
|
|
# Export nlohmann_json with YamsTargets for downstream consumers
|
|
install(TARGETS nlohmann_json
|
|
EXPORT YamsTargets
|
|
)
|
|
endif()
|
|
|
|
# Install nlohmann_json headers (it's header-only)
|
|
install(DIRECTORY ${nlohmann_json_SOURCE_DIR}/include/
|
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
|
)
|
|
|
|
# Boost headers are needed for Asio-based CLI/MCP clients
|
|
message(STATUS "Fetching Boost headers (required for Asio)")
|
|
FetchContent_Declare(
|
|
Boost
|
|
URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz
|
|
URL_HASH SHA256=2e64e5d79a738d0fa6fb546c6e5c2bd28f88d268a2a080546f74e5ff98f29d0e
|
|
DOWNLOAD_EXTRACT_TIMESTAMP ON
|
|
)
|
|
FetchContent_MakeAvailable(Boost)
|
|
|
|
# Boost automatically creates boost_headers target, just need to make it available
|
|
# The Boost::headers alias should already exist from the Boost CMake configuration
|
|
if(NOT TARGET Boost::headers)
|
|
# Only create alias if it doesn't exist
|
|
if(TARGET boost_headers)
|
|
add_library(Boost::headers ALIAS boost_headers)
|
|
else()
|
|
# Fallback: create our own interface library
|
|
add_library(yams_boost_headers INTERFACE)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${boost_SOURCE_DIR})
|
|
add_library(Boost::headers ALIAS yams_boost_headers)
|
|
endif()
|
|
endif()
|
|
|
|
# Provide a stable interface target to propagate Boost headers (Asio is header-only)
|
|
if(NOT TARGET yams_boost_headers)
|
|
add_library(yams_boost_headers INTERFACE)
|
|
endif()
|
|
if(TARGET Boost::headers)
|
|
target_link_libraries(yams_boost_headers INTERFACE Boost::headers)
|
|
get_target_property(_boost_inc_dirs_nc Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
|
|
if(_boost_inc_dirs_nc)
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${_boost_inc_dirs_nc})
|
|
else()
|
|
# Fallback to the fetched source dir
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${boost_SOURCE_DIR})
|
|
endif()
|
|
else()
|
|
# As a last resort, include the fetched source dir
|
|
target_include_directories(yams_boost_headers SYSTEM INTERFACE ${boost_SOURCE_DIR})
|
|
endif()
|
|
endif()
|
|
|
|
# CLI11 for command line parsing
|
|
if(YAMS_BUILD_TOOLS)
|
|
if(NOT YAMS_USE_CONAN)
|
|
FetchContent_Declare(
|
|
CLI11
|
|
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
|
|
GIT_TAG v2.4.1
|
|
)
|
|
FetchContent_MakeAvailable(CLI11)
|
|
endif()
|
|
|
|
# ImTUI for Terminal User Interface (Immediate Mode TUI)
|
|
if(YAMS_BUILD_CLI AND YAMS_ENABLE_TUI)
|
|
if(YAMS_USE_CONAN)
|
|
# Use FetchContent for ImTUI with Conan as well, and provide a stable wrapper target
|
|
find_package(Curses REQUIRED)
|
|
# Workaround: imtui's CMake checks tinfo when CURSES_NCURSES_HAS_NODELAY is false,
|
|
# which can fail with CURSES_EXTRA_LIBRARY=NOTFOUND on some Conan/system combos.
|
|
# Seed the result to skip the tinfo probe; we still link Curses::Curses explicitly.
|
|
set(CURSES_NCURSES_HAS_NODELAY TRUE CACHE BOOL "Assume ncurses provides nodelay; avoid tinfo probe" FORCE)
|
|
|
|
FetchContent_Declare(
|
|
imtui
|
|
GIT_REPOSITORY https://github.com/ggerganov/imtui.git
|
|
GIT_TAG master
|
|
GIT_SHALLOW TRUE
|
|
GIT_SUBMODULES_RECURSE TRUE
|
|
)
|
|
set(IMTUI_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(imtui)
|
|
|
|
# Ensure consistent target name and link against Curses
|
|
if(TARGET imtui-ncurses)
|
|
target_link_libraries(imtui-ncurses INTERFACE Curses::Curses)
|
|
elseif(TARGET imtui::imtui-ncurses)
|
|
add_library(imtui-ncurses INTERFACE)
|
|
target_link_libraries(imtui-ncurses INTERFACE imtui::imtui-ncurses Curses::Curses)
|
|
else()
|
|
message(FATAL_ERROR "ImTUI target 'imtui-ncurses' not found after FetchContent.")
|
|
endif()
|
|
|
|
# Platform-specific link adjustments for ImTUI
|
|
if(APPLE)
|
|
target_link_libraries(imtui-ncurses INTERFACE "-framework System")
|
|
endif()
|
|
if(UNIX AND NOT APPLE)
|
|
# Link to tinfo if it exists as a separate library (common on Linux)
|
|
find_library(TINFO_LIBRARY NAMES tinfo tinfow)
|
|
if(TINFO_LIBRARY)
|
|
target_link_libraries(imtui-ncurses INTERFACE "${TINFO_LIBRARY}")
|
|
endif()
|
|
endif()
|
|
else()
|
|
# Use FetchContent for non-Conan builds
|
|
FetchContent_Declare(
|
|
imtui
|
|
GIT_REPOSITORY https://github.com/ggerganov/imtui.git
|
|
GIT_TAG master
|
|
GIT_SHALLOW TRUE
|
|
GIT_SUBMODULES_RECURSE TRUE
|
|
)
|
|
set(IMTUI_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(imtui)
|
|
# Ensure the wrapper target exists and link ncurses and tinfo as needed
|
|
if(TARGET imtui-ncurses)
|
|
if(TARGET Curses::Curses)
|
|
target_link_libraries(imtui-ncurses INTERFACE Curses::Curses)
|
|
elseif(DEFINED CURSES_LIBRARIES)
|
|
target_link_libraries(imtui-ncurses INTERFACE ${CURSES_LIBRARIES})
|
|
endif()
|
|
if(UNIX AND NOT APPLE)
|
|
find_library(TINFO_LIBRARY NAMES tinfo tinfow)
|
|
if(TINFO_LIBRARY)
|
|
target_link_libraries(imtui-ncurses INTERFACE "${TINFO_LIBRARY}")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
# Boost.Beast is part of Boost and will be available automatically
|
|
# No separate dependency needed for WebSocket transport
|
|
|
|
# Zstandard for compression - only if not using Conan
|
|
if(NOT YAMS_USE_CONAN)
|
|
FetchContent_Declare(
|
|
zstd
|
|
URL https://github.com/facebook/zstd/releases/download/v1.5.6/zstd-1.5.6.tar.gz
|
|
URL_HASH SHA256=8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1
|
|
SOURCE_SUBDIR build/cmake
|
|
)
|
|
set(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "" FORCE)
|
|
set(ZSTD_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|
set(ZSTD_BUILD_SHARED OFF CACHE BOOL "" FORCE)
|
|
set(ZSTD_BUILD_STATIC ON CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(zstd)
|
|
endif()
|
|
|
|
# LZMA SDK for compression - always needed since no good Conan package exists
|
|
# if(NOT YAMS_USE_CONAN)
|
|
FetchContent_Declare(
|
|
lzma
|
|
URL https://github.com/ip7z/7zip/releases/download/24.08/7z2408-src.tar.xz
|
|
URL_HASH SHA256=aa04aac906a04df59e7301f4c69e9f48808e6c8ecae4eb697703a47bfb0ac042
|
|
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
|
)
|
|
FetchContent_GetProperties(lzma)
|
|
if(NOT lzma_POPULATED)
|
|
FetchContent_Populate(lzma)
|
|
|
|
# Create LZMA library from SDK sources
|
|
add_library(lzma_sdk STATIC
|
|
${lzma_SOURCE_DIR}/C/LzmaEnc.c
|
|
${lzma_SOURCE_DIR}/C/LzmaDec.c
|
|
${lzma_SOURCE_DIR}/C/Lzma2Enc.c
|
|
${lzma_SOURCE_DIR}/C/Lzma2Dec.c
|
|
${lzma_SOURCE_DIR}/C/Lzma86Enc.c
|
|
${lzma_SOURCE_DIR}/C/Lzma86Dec.c
|
|
${lzma_SOURCE_DIR}/C/LzFind.c
|
|
${lzma_SOURCE_DIR}/C/LzFindMt.c
|
|
${lzma_SOURCE_DIR}/C/LzFindOpt.c
|
|
${lzma_SOURCE_DIR}/C/CpuArch.c
|
|
${lzma_SOURCE_DIR}/C/Alloc.c
|
|
${lzma_SOURCE_DIR}/C/Threads.c
|
|
${lzma_SOURCE_DIR}/C/MtCoder.c
|
|
${lzma_SOURCE_DIR}/C/MtDec.c
|
|
${lzma_SOURCE_DIR}/C/Lzma2DecMt.c
|
|
${lzma_SOURCE_DIR}/C/7zStream.c
|
|
)
|
|
|
|
# Disable clang-tidy for third-party C (and C++) sources in this target
|
|
# to avoid readability/identifier and other style checks on vendored code.
|
|
set_target_properties(lzma_sdk PROPERTIES
|
|
C_CLANG_TIDY ""
|
|
CXX_CLANG_TIDY ""
|
|
)
|
|
|
|
target_include_directories(lzma_sdk PUBLIC
|
|
$<BUILD_INTERFACE:${lzma_SOURCE_DIR}/C>
|
|
$<INSTALL_INTERFACE:include/lzma>
|
|
)
|
|
if(UNIX)
|
|
# Ensure pthread affinity APIs/macros are available for LZMA Threads.c
|
|
target_compile_definitions(lzma_sdk PRIVATE _GNU_SOURCE)
|
|
target_link_libraries(lzma_sdk PUBLIC pthread)
|
|
endif()
|
|
# Enable multi-threaded support for LZMA SDK
|
|
|
|
# Disable warnings for third-party code
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
target_compile_options(lzma_sdk PRIVATE -w)
|
|
elseif(MSVC)
|
|
target_compile_options(lzma_sdk PRIVATE /w)
|
|
endif()
|
|
endif()
|
|
# endif()
|
|
|
|
# SQLite3 with FTS5 - only if not using Conan
|
|
if(NOT YAMS_USE_CONAN)
|
|
FetchContent_Declare(
|
|
sqlite3
|
|
URL https://www.sqlite.org/2024/sqlite-amalgamation-3460100.zip
|
|
URL_HASH SHA256=77823cb110929c2bcb0f5d48e4833b5c59a8a6e40cdea3936b99e199dbbe5784
|
|
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
|
)
|
|
FetchContent_GetProperties(sqlite3)
|
|
if(NOT sqlite3_POPULATED)
|
|
FetchContent_Populate(sqlite3)
|
|
|
|
# Create SQLite library with FTS5 enabled
|
|
add_library(sqlite3 STATIC
|
|
${sqlite3_SOURCE_DIR}/sqlite3.c
|
|
)
|
|
|
|
target_include_directories(sqlite3 PUBLIC
|
|
$<BUILD_INTERFACE:${sqlite3_SOURCE_DIR}>
|
|
$<INSTALL_INTERFACE:include/sqlite3>
|
|
)
|
|
|
|
# Enable FTS5 and other useful extensions
|
|
target_compile_definitions(sqlite3 PRIVATE
|
|
SQLITE_ENABLE_FTS5=1
|
|
SQLITE_ENABLE_RTREE=1
|
|
SQLITE_ENABLE_JSON1=1
|
|
SQLITE_ENABLE_STAT4=1
|
|
SQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
|
|
SQLITE_ENABLE_COLUMN_METADATA=1
|
|
SQLITE_DEFAULT_MEMSTATUS=0
|
|
SQLITE_DEFAULT_WAL_SYNCHRONOUS=1
|
|
SQLITE_LIKE_DOESNT_MATCH_BLOBS=1
|
|
SQLITE_MAX_EXPR_DEPTH=0
|
|
SQLITE_OMIT_DEPRECATED=1
|
|
SQLITE_OMIT_SHARED_CACHE=1
|
|
SQLITE_USE_ALLOCA=1
|
|
SQLITE_THREADSAFE=2
|
|
)
|
|
|
|
# Disable warnings for third-party code
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
target_compile_options(sqlite3 PRIVATE -w)
|
|
elseif(MSVC)
|
|
target_compile_options(sqlite3 PRIVATE /w)
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
# Ensure standard install directory variables are available early (for install() below)
|
|
include(GNUInstallDirs)
|
|
# Provide FULL paths if not defined yet (older CMake)
|
|
if(NOT DEFINED CMAKE_INSTALL_FULL_LIBDIR)
|
|
set(CMAKE_INSTALL_FULL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
|
endif()
|
|
if(NOT DEFINED CMAKE_INSTALL_FULL_BINDIR)
|
|
set(CMAKE_INSTALL_FULL_BINDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
|
|
endif()
|
|
|
|
# PDFium for PDF text extraction
|
|
option(YAMS_ENABLE_PDF "Enable PDF text extraction support" ON)
|
|
# macOS runtime search path defaults for installed binaries
|
|
if(APPLE)
|
|
set(CMAKE_MACOSX_RPATH ON)
|
|
# Ensure installed executables/DYLIBs can find dependencies in lib/
|
|
set(CMAKE_INSTALL_RPATH "@loader_path/../lib")
|
|
endif()
|
|
|
|
# sqlite-vec extension for vector similarity search
|
|
# This provides fast KNN search for embeddings with minimal dependencies
|
|
message(STATUS "Fetching sqlite-vec for vector search support")
|
|
FetchContent_Declare(
|
|
sqlite_vec
|
|
GIT_REPOSITORY https://github.com/asg017/sqlite-vec.git
|
|
GIT_TAG v0.1.3 # Latest stable version as of Aug 2024
|
|
GIT_SHALLOW TRUE
|
|
)
|
|
FetchContent_GetProperties(sqlite_vec)
|
|
if(NOT sqlite_vec_POPULATED)
|
|
FetchContent_Populate(sqlite_vec)
|
|
|
|
# Export the source directory for use by yams_vector
|
|
set(sqlite_vec_SOURCE_DIR ${sqlite_vec_SOURCE_DIR} CACHE INTERNAL "sqlite-vec source directory")
|
|
|
|
message(STATUS "sqlite-vec source fetched successfully to ${sqlite_vec_SOURCE_DIR}")
|
|
endif()
|
|
|
|
if(YAMS_ENABLE_PDF)
|
|
message(STATUS "Configuring PDFium for PDF support")
|
|
|
|
# Try to find PDFium from Conan first if using Conan
|
|
set(PDFium_FOUND FALSE)
|
|
if(YAMS_USE_CONAN)
|
|
message(STATUS "Attempting to find PDFium via Conan...")
|
|
find_package(PDFium QUIET)
|
|
if(PDFium_FOUND)
|
|
message(STATUS "PDFium found via Conan")
|
|
add_library(pdfium::pdfium ALIAS PDFium::PDFium)
|
|
else()
|
|
message(STATUS "PDFium not available via Conan, falling back to FetchContent")
|
|
endif()
|
|
endif()
|
|
|
|
# If PDFium not found via Conan, use FetchContent (both Conan and non-Conan builds)
|
|
if(NOT PDFium_FOUND)
|
|
# Determine platform and architecture
|
|
if(APPLE)
|
|
# More robust architecture detection for macOS
|
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64" OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
|
|
set(PDFIUM_PLATFORM "mac-arm64")
|
|
message(STATUS "Detected macOS ARM64 architecture for PDFium")
|
|
else()
|
|
set(PDFIUM_PLATFORM "mac-x64")
|
|
message(STATUS "Detected macOS x64 architecture for PDFium")
|
|
endif()
|
|
elseif(UNIX)
|
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
|
set(PDFIUM_PLATFORM "linux-arm64")
|
|
else()
|
|
set(PDFIUM_PLATFORM "linux-x64")
|
|
endif()
|
|
elseif(WIN32)
|
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
set(PDFIUM_PLATFORM "win-x64")
|
|
else()
|
|
set(PDFIUM_PLATFORM "win-x86")
|
|
endif()
|
|
endif()
|
|
|
|
# Use prebuilt binaries from pdfium-binaries project
|
|
# Latest release: PDFium 141.0.7350.0 (chromium/7350)
|
|
FetchContent_Declare(
|
|
pdfium
|
|
URL https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F7350/pdfium-${PDFIUM_PLATFORM}.tgz
|
|
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
|
)
|
|
|
|
FetchContent_GetProperties(pdfium)
|
|
if(NOT pdfium_POPULATED)
|
|
message(STATUS "Downloading PDFium binaries for ${PDFIUM_PLATFORM}...")
|
|
FetchContent_Populate(pdfium)
|
|
|
|
# Verify PDFium was downloaded correctly
|
|
if(NOT EXISTS "${pdfium_SOURCE_DIR}/include/fpdfview.h")
|
|
message(WARNING "PDFium download failed or incomplete. PDF support will be disabled.")
|
|
set(YAMS_ENABLE_PDF OFF)
|
|
return()
|
|
endif()
|
|
|
|
# Additional verification for library file
|
|
if(APPLE)
|
|
if(NOT EXISTS "${pdfium_SOURCE_DIR}/lib/libpdfium.dylib")
|
|
message(WARNING "PDFium library not found at ${pdfium_SOURCE_DIR}/lib/libpdfium.dylib")
|
|
message(WARNING "PDF support will be disabled.")
|
|
set(YAMS_ENABLE_PDF OFF CACHE BOOL "Enable PDF text extraction support" FORCE)
|
|
set(YAMS_PDFIUM_FOUND FALSE CACHE BOOL "PDFium library found and configured" FORCE)
|
|
else()
|
|
# Verify it's the correct architecture
|
|
execute_process(
|
|
COMMAND file "${pdfium_SOURCE_DIR}/lib/libpdfium.dylib"
|
|
OUTPUT_VARIABLE DYLIB_INFO
|
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
ERROR_QUIET
|
|
)
|
|
message(STATUS "PDFium library info: ${DYLIB_INFO}")
|
|
|
|
# Check if the architecture matches what we expect
|
|
if(CMAKE_OSX_ARCHITECTURES MATCHES "arm64" AND NOT DYLIB_INFO MATCHES "arm64")
|
|
message(WARNING "PDFium library architecture mismatch - expected arm64")
|
|
message(WARNING "PDF support will be disabled for this build")
|
|
set(YAMS_ENABLE_PDF OFF CACHE BOOL "Enable PDF text extraction support" FORCE)
|
|
set(YAMS_PDFIUM_FOUND FALSE CACHE BOOL "PDFium library found and configured" FORCE)
|
|
elseif(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" AND NOT DYLIB_INFO MATCHES "x86_64")
|
|
message(WARNING "PDFium library architecture mismatch - expected x86_64")
|
|
message(WARNING "PDF support will be disabled for this build")
|
|
set(YAMS_ENABLE_PDF OFF CACHE BOOL "Enable PDF text extraction support" FORCE)
|
|
set(YAMS_PDFIUM_FOUND FALSE CACHE BOOL "PDFium library found and configured" FORCE)
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
# Only create imported target if PDF is still enabled
|
|
if(NOT YAMS_ENABLE_PDF)
|
|
message(STATUS "Skipping PDFium target creation due to disabled PDF support")
|
|
return()
|
|
endif()
|
|
|
|
# Create imported target for PDFium
|
|
add_library(pdfium SHARED IMPORTED GLOBAL)
|
|
|
|
# Set include directories
|
|
set_target_properties(pdfium PROPERTIES
|
|
INTERFACE_INCLUDE_DIRECTORIES "${pdfium_SOURCE_DIR}/include"
|
|
)
|
|
|
|
# Set library location based on platform
|
|
if(APPLE)
|
|
set_target_properties(pdfium PROPERTIES
|
|
IMPORTED_LOCATION "${pdfium_SOURCE_DIR}/lib/libpdfium.dylib"
|
|
)
|
|
# Ensure @rpath install_name on the fetched dylib for proper link-time embedding
|
|
execute_process(COMMAND install_name_tool -id @rpath/libpdfium.dylib "${pdfium_SOURCE_DIR}/lib/libpdfium.dylib")
|
|
elseif(UNIX)
|
|
set_target_properties(pdfium PROPERTIES
|
|
IMPORTED_LOCATION "${pdfium_SOURCE_DIR}/lib/libpdfium.so"
|
|
)
|
|
elseif(WIN32)
|
|
set_target_properties(pdfium PROPERTIES
|
|
IMPORTED_LOCATION "${pdfium_SOURCE_DIR}/bin/pdfium.dll"
|
|
IMPORTED_IMPLIB "${pdfium_SOURCE_DIR}/lib/pdfium.lib"
|
|
)
|
|
endif()
|
|
|
|
# Create alias for consistent naming
|
|
add_library(pdfium::pdfium ALIAS pdfium)
|
|
|
|
# Install the prebuilt PDFium library based on platform
|
|
if(APPLE)
|
|
install(FILES "${pdfium_SOURCE_DIR}/lib/libpdfium.dylib"
|
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
COMPONENT runtime)
|
|
elseif(UNIX)
|
|
install(FILES "${pdfium_SOURCE_DIR}/lib/libpdfium.so"
|
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
COMPONENT runtime)
|
|
elseif(WIN32)
|
|
install(FILES "${pdfium_SOURCE_DIR}/bin/pdfium.dll"
|
|
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
COMPONENT runtime)
|
|
install(FILES "${pdfium_SOURCE_DIR}/lib/pdfium.lib"
|
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
COMPONENT development)
|
|
endif()
|
|
|
|
if(APPLE)
|
|
# Ensure consumers load PDFium via @rpath; installed binaries use @loader_path/../lib
|
|
install(CODE [=[execute_process(COMMAND install_name_tool -id @rpath/libpdfium.dylib "${CMAKE_INSTALL_FULL_LIBDIR}/libpdfium.dylib")]=])
|
|
|
|
# Patch installed executables to reference @rpath/libpdfium.dylib and ensure rpath includes ../lib
|
|
install(CODE [=[
|
|
set(_bins yams yams-mcp-server)
|
|
foreach(_bin IN LISTS _bins)
|
|
set(_path "${CMAKE_INSTALL_FULL_BINDIR}/${_bin}")
|
|
if(EXISTS "${_path}")
|
|
execute_process(COMMAND install_name_tool -change libpdfium.dylib @rpath/libpdfium.dylib "${_path}")
|
|
execute_process(COMMAND install_name_tool -change ./libpdfium.dylib @rpath/libpdfium.dylib "${_path}")
|
|
execute_process(COMMAND install_name_tool -add_rpath "@loader_path/../lib" "${_path}")
|
|
endif()
|
|
endforeach()
|
|
]=])
|
|
endif()
|
|
|
|
message(STATUS "PDFium ${PDFIUM_PLATFORM} successfully configured")
|
|
set(YAMS_PDFIUM_FOUND TRUE CACHE BOOL "PDFium library found and configured")
|
|
endif() # end if(NOT pdfium_POPULATED)
|
|
endif() # end if(NOT PDFium_FOUND)
|
|
else()
|
|
message(STATUS "PDFium support disabled (YAMS_ENABLE_PDF=OFF)")
|
|
set(YAMS_PDFIUM_FOUND FALSE CACHE BOOL "PDFium library found and configured")
|
|
endif()
|
|
|
|
# Protocol Buffers (optional, for manifest serialization)
|
|
find_package(Protobuf QUIET)
|
|
if(Protobuf_FOUND)
|
|
message(STATUS "Found Protobuf: ${Protobuf_VERSION}")
|
|
|
|
# Find Abseil (required by modern Protocol Buffers)
|
|
find_package(absl QUIET)
|
|
if(absl_FOUND)
|
|
message(STATUS "Found Abseil for Protocol Buffers")
|
|
else()
|
|
# Try to find Abseil through pkg-config
|
|
find_package(PkgConfig QUIET)
|
|
if(PkgConfig_FOUND)
|
|
pkg_check_modules(ABSL absl_base absl_strings absl_log absl_log_internal)
|
|
if(ABSL_FOUND)
|
|
message(STATUS "Found Abseil via pkg-config")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
else()
|
|
message(STATUS "Protobuf not found, using custom serialization")
|
|
endif()
|
|
|
|
# Create core library
|
|
add_library(yams_core INTERFACE)
|
|
target_include_directories(yams_core INTERFACE
|
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
$<INSTALL_INTERFACE:include>
|
|
)
|
|
target_compile_features(yams_core INTERFACE cxx_std_20)
|
|
add_library(yams::core ALIAS yams_core)
|
|
|
|
# Add subdirectories
|
|
add_subdirectory(src)
|
|
|
|
# Plugins
|
|
add_subdirectory(plugins/object_storage_s3)
|
|
add_subdirectory(plugins/onnx)
|
|
add_subdirectory(plugins/pdf_extractor)
|
|
|
|
# Always create the plugins directory on install, even if no plugin was built
|
|
install(DIRECTORY DESTINATION ${CMAKE_INSTALL_LIBDIR}/yams/plugins)
|
|
|
|
# Optional CPack configuration to produce binary packages with plugins
|
|
if(YAMS_PACKAGE_PLUGINS)
|
|
set(CPACK_PACKAGE_NAME "yams")
|
|
set(CPACK_PACKAGE_VENDOR "YAMS")
|
|
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
|
|
set(CPACK_PACKAGE_CONTACT "support@yams.local")
|
|
# Default generators can be overridden by user/presets
|
|
if(NOT CPACK_GENERATOR)
|
|
set(CPACK_GENERATOR "TGZ")
|
|
endif()
|
|
include(CPack)
|
|
endif()
|
|
|
|
if(YAMS_BUILD_TESTS)
|
|
enable_testing()
|
|
message(STATUS "Building tests: YAMS_USE_CONAN=${YAMS_USE_CONAN}")
|
|
|
|
# Use FetchContent for GTest only when not using Conan
|
|
if(NOT YAMS_USE_CONAN)
|
|
message(STATUS "Non-Conan build: Using FetchContent for GoogleTest")
|
|
include(FetchContent)
|
|
FetchContent_Declare(
|
|
googletest
|
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
GIT_TAG v1.14.0
|
|
GIT_SHALLOW TRUE
|
|
)
|
|
# Prevent overriding the parent project's compiler/linker settings on Windows
|
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
FetchContent_MakeAvailable(googletest)
|
|
|
|
# Create alias targets for compatibility with find_package(GTest)
|
|
if(TARGET gtest AND NOT TARGET GTest::gtest)
|
|
add_library(GTest::gtest ALIAS gtest)
|
|
endif()
|
|
if(TARGET gtest_main AND NOT TARGET GTest::gtest_main)
|
|
add_library(GTest::gtest_main ALIAS gtest_main)
|
|
endif()
|
|
if(TARGET gmock AND NOT TARGET GTest::gmock)
|
|
add_library(GTest::gmock ALIAS gmock)
|
|
endif()
|
|
if(TARGET gmock_main AND NOT TARGET GTest::gmock_main)
|
|
add_library(GTest::gmock_main ALIAS gmock_main)
|
|
endif()
|
|
endif()
|
|
|
|
add_subdirectory(tests)
|
|
endif()
|
|
|
|
# CTest is included for test discovery
|
|
include(CTest)
|
|
|
|
|
|
|
|
# Build tools based on options
|
|
if(YAMS_BUILD_CLI OR YAMS_BUILD_MCP_SERVER OR YAMS_BUILD_MAINTENANCE_TOOLS)
|
|
add_subdirectory(tools)
|
|
endif()
|
|
|
|
# Installation
|
|
include(GNUInstallDirs)
|
|
include(CMakePackageConfigHelpers)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# RPATH best practices (for installed targets on macOS/Linux)
|
|
# ---------------------------------------------------------------------------
|
|
# Use build RPATH so binaries run from the build tree
|
|
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
|
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
|
|
|
# Add install directories to the install RPATH
|
|
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
|
|
|
# macOS specific
|
|
if(APPLE)
|
|
set(CMAKE_MACOSX_RPATH ON)
|
|
endif()
|
|
|
|
# If the install libdir isn't a system dir, add it to the install RPATH
|
|
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_LIBDIR}" _isSys)
|
|
if(_isSys EQUAL -1)
|
|
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
|
endif()
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Uninstall target (uses install_manifest.txt generated by "make install")
|
|
# ---------------------------------------------------------------------------
|
|
set(UNINSTALL_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
|
file(WRITE "${UNINSTALL_SCRIPT}" "if(NOT EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\")\n message(FATAL_ERROR \"Cannot find install manifest: ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\")\nendif()\nfile(READ \"${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\" _files)\nstring(REPLACE \"\\n\" \";\" _files \"${_files}\")\nforeach(_file IN LISTS _files)\n if(_file STREQUAL \"\")\n continue()\n endif()\n if(EXISTS \"${_file}\" OR IS_SYMLINK \"${_file}\")\n file(REMOVE \"${_file}\")\n endif()\nendforeach()\n")
|
|
if(NOT TARGET uninstall)
|
|
add_custom_target(uninstall
|
|
COMMAND ${CMAKE_COMMAND} -P "${UNINSTALL_SCRIPT}"
|
|
COMMENT "Removing installed files listed in install_manifest.txt"
|
|
)
|
|
endif()
|
|
|
|
# Package config (install location configurable)
|
|
|
|
|
|
# Export targets
|
|
# Ensure all PUBLIC deps of exported libs are themselves in an export set.
|
|
# yams_asio_client is a PUBLIC dependency of yams_vector; it is exported in src/CMakeLists.txt.
|
|
# Do not add it here to avoid duplicate entries in YamsTargets.
|
|
set(YAMS_EXPORT_TARGETS yams_core yams_indexing)
|
|
|
|
# Always export lzma_sdk since we always build it
|
|
if(TARGET lzma_sdk)
|
|
list(APPEND YAMS_EXPORT_TARGETS lzma_sdk)
|
|
endif()
|
|
|
|
# Only export sqlite3 if we built it (non-Conan)
|
|
if(NOT YAMS_USE_CONAN)
|
|
if(TARGET sqlite3)
|
|
list(APPEND YAMS_EXPORT_TARGETS sqlite3)
|
|
endif()
|
|
# Note: sqlite_vec is not exported as it's an implementation detail
|
|
endif()
|
|
|
|
install(TARGETS ${YAMS_EXPORT_TARGETS}
|
|
EXPORT YamsTargets
|
|
)
|
|
|
|
install(EXPORT YamsTargets
|
|
FILE YamsTargets.cmake
|
|
NAMESPACE yams::
|
|
DESTINATION ${YAMS_CMAKE_PACKAGE_DIR}
|
|
)
|
|
|
|
# Install headers
|
|
install(DIRECTORY include/
|
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
|
COMPONENT development
|
|
)
|
|
|
|
# Install data files
|
|
install(FILES
|
|
data/magic_numbers.json
|
|
DESTINATION ${CMAKE_INSTALL_DATADIR}/yams/data
|
|
COMPONENT runtime
|
|
)
|
|
|
|
install(FILES
|
|
sql/reference_schema.sql
|
|
DESTINATION ${CMAKE_INSTALL_DATADIR}/yams/sql
|
|
COMPONENT runtime
|
|
)
|
|
|
|
# CPack configuration for creating packages
|
|
set(CPACK_PACKAGE_NAME "YAMS")
|
|
set(CPACK_PACKAGE_VENDOR "YAMS Project")
|
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Yet Another Memory System - High-performance content-addressed storage")
|
|
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
|
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
|
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
|
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
|
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
|
|
|
# Components
|
|
set(CPACK_COMPONENTS_ALL runtime maintenance development)
|
|
set(CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "Runtime")
|
|
set(CPACK_COMPONENT_RUNTIME_DESCRIPTION "YAMS CLI and MCP server")
|
|
set(CPACK_COMPONENT_MAINTENANCE_DISPLAY_NAME "Maintenance Tools")
|
|
set(CPACK_COMPONENT_MAINTENANCE_DESCRIPTION "Garbage collection and statistics tools")
|
|
set(CPACK_COMPONENT_DEVELOPMENT_DISPLAY_NAME "Development")
|
|
set(CPACK_COMPONENT_DEVELOPMENT_DESCRIPTION "Headers and libraries for development")
|
|
|
|
# Package generation
|
|
if(YAMS_BUILD_PROFILE STREQUAL "release")
|
|
set(CPACK_COMPONENTS_ALL runtime)
|
|
endif()
|
|
|
|
# Platform-specific packaging
|
|
if(UNIX AND NOT APPLE)
|
|
# Determine Linux distribution
|
|
find_program(LSB_RELEASE_EXEC lsb_release)
|
|
if(LSB_RELEASE_EXEC)
|
|
execute_process(COMMAND ${LSB_RELEASE_EXEC} -is
|
|
OUTPUT_VARIABLE LSB_RELEASE_ID
|
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
)
|
|
endif()
|
|
|
|
# DEB packaging for Debian/Ubuntu
|
|
if(LSB_RELEASE_ID MATCHES "Debian|Ubuntu")
|
|
set(CPACK_GENERATOR "DEB;TGZ")
|
|
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "YAMS Team")
|
|
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
|
|
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libssl3, libstdc++6, libgcc-s1, zlib1g, libzstd1, liblzma5, libsqlite3-0")
|
|
set(CPACK_DEBIAN_PACKAGE_SUGGESTS "libmagic1")
|
|
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/trvon/yams")
|
|
# RPM packaging for RedHat/Fedora/CentOS
|
|
elseif(LSB_RELEASE_ID MATCHES "RedHat|Fedora|CentOS|Rocky|AlmaLinux")
|
|
set(CPACK_GENERATOR "RPM;TGZ")
|
|
set(CPACK_RPM_PACKAGE_GROUP "Applications/System")
|
|
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
|
set(CPACK_RPM_PACKAGE_URL "https://github.com/trvon/yams")
|
|
set(CPACK_RPM_PACKAGE_REQUIRES "openssl, libstdc++, libgcc, zlib, zstd, xz-libs, sqlite")
|
|
set(CPACK_RPM_PACKAGE_SUGGESTS "file-libs")
|
|
else()
|
|
# Generic Linux packaging
|
|
set(CPACK_GENERATOR "TGZ;STGZ")
|
|
endif()
|
|
|
|
# AppImage support
|
|
option(YAMS_BUILD_APPIMAGE "Build AppImage package" OFF)
|
|
if(YAMS_BUILD_APPIMAGE)
|
|
list(APPEND CPACK_GENERATOR "External")
|
|
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_SOURCE_DIR}/packaging/appimage/build_appimage.cmake")
|
|
endif()
|
|
elseif(APPLE)
|
|
set(CPACK_GENERATOR "DragNDrop;TGZ")
|
|
set(CPACK_DMG_VOLUME_NAME "YAMS")
|
|
set(CPACK_DMG_FORMAT "UDZO")
|
|
elseif(WIN32)
|
|
set(CPACK_GENERATOR "NSIS;ZIP")
|
|
set(CPACK_NSIS_DISPLAY_NAME "YAMS")
|
|
set(CPACK_NSIS_PACKAGE_NAME "YAMS")
|
|
set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/trvon/yams")
|
|
endif()
|
|
|
|
include(CPack)
|
|
|
|
# Create package config
|
|
configure_package_config_file(
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/YamsConfig.cmake.in"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/YamsConfig.cmake"
|
|
INSTALL_DESTINATION ${YAMS_CMAKE_PACKAGE_DIR}
|
|
)
|
|
|
|
write_basic_package_version_file(
|
|
"${CMAKE_CURRENT_BINARY_DIR}/YamsConfigVersion.cmake"
|
|
VERSION ${PROJECT_VERSION}
|
|
COMPATIBILITY SameMajorVersion
|
|
)
|
|
|
|
install(FILES
|
|
"${CMAKE_CURRENT_BINARY_DIR}/YamsConfig.cmake"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/YamsConfigVersion.cmake"
|
|
DESTINATION ${YAMS_CMAKE_PACKAGE_DIR}
|
|
)
|
|
|
|
# Coverage targets
|
|
if(YAMS_ENABLE_COVERAGE)
|
|
# Create coverage directory
|
|
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/coverage)
|
|
|
|
# Coverage target using gcovr (preferred)
|
|
if(GCOVR_PATH)
|
|
add_custom_target(coverage-html
|
|
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/coverage/html
|
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage/html
|
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR}
|
|
--exclude ${CMAKE_SOURCE_DIR}/tests/
|
|
--exclude ${CMAKE_SOURCE_DIR}/_deps/
|
|
--exclude ${CMAKE_SOURCE_DIR}/build/
|
|
--html --html-details
|
|
--output ${CMAKE_BINARY_DIR}/coverage/html/index.html
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Generating coverage HTML report with gcovr"
|
|
)
|
|
|
|
add_custom_target(coverage-xml
|
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR}
|
|
--exclude ${CMAKE_SOURCE_DIR}/tests/
|
|
--exclude ${CMAKE_SOURCE_DIR}/_deps/
|
|
--exclude ${CMAKE_SOURCE_DIR}/build/
|
|
--xml --output ${CMAKE_BINARY_DIR}/coverage/coverage.xml
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Generating coverage XML report with gcovr"
|
|
)
|
|
|
|
add_custom_target(coverage-summary
|
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR}
|
|
--exclude ${CMAKE_SOURCE_DIR}/tests/
|
|
--exclude ${CMAKE_SOURCE_DIR}/_deps/
|
|
--exclude ${CMAKE_SOURCE_DIR}/build/
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Displaying coverage summary"
|
|
)
|
|
|
|
# Coverage target using lcov (alternative)
|
|
elseif(LCOV_PATH AND GENHTML_PATH)
|
|
add_custom_target(coverage-html
|
|
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/coverage/html
|
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage/html
|
|
COMMAND ${LCOV_PATH} --capture --directory ${CMAKE_BINARY_DIR}
|
|
--output-file ${CMAKE_BINARY_DIR}/coverage/coverage.info
|
|
COMMAND ${LCOV_PATH} --remove ${CMAKE_BINARY_DIR}/coverage/coverage.info
|
|
'${CMAKE_SOURCE_DIR}/tests/*'
|
|
'${CMAKE_SOURCE_DIR}/_deps/*'
|
|
'${CMAKE_SOURCE_DIR}/build/*'
|
|
'/usr/*' '/opt/*'
|
|
--output-file ${CMAKE_BINARY_DIR}/coverage/coverage.info
|
|
COMMAND ${GENHTML_PATH} ${CMAKE_BINARY_DIR}/coverage/coverage.info
|
|
--output-directory ${CMAKE_BINARY_DIR}/coverage/html
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Generating coverage HTML report with lcov"
|
|
)
|
|
|
|
add_custom_target(coverage-summary
|
|
COMMAND ${LCOV_PATH} --summary ${CMAKE_BINARY_DIR}/coverage/coverage.info
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Displaying coverage summary"
|
|
)
|
|
endif()
|
|
|
|
# Combined coverage target that runs tests first
|
|
if(TARGET coverage-html)
|
|
add_custom_target(coverage
|
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
|
|
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
|
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target coverage-html
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Running tests and generating coverage report"
|
|
)
|
|
endif()
|
|
endif()
|
|
|
|
# =============================================================================
|
|
# Code Quality Targets
|
|
# =============================================================================
|
|
|
|
# Find cppcheck for static analysis (always define targets; degrade gracefully if missing)
|
|
find_program(CPPCHECK_PATH cppcheck)
|
|
set(QUALITY_SCRIPT "${CMAKE_SOURCE_DIR}/scripts/check-quality.sh")
|
|
|
|
if(CPPCHECK_PATH)
|
|
message(STATUS "Found cppcheck: ${CPPCHECK_PATH}")
|
|
else()
|
|
message(STATUS "cppcheck not found - quality targets will print an error at build time")
|
|
endif()
|
|
|
|
if(EXISTS ${QUALITY_SCRIPT})
|
|
message(STATUS "Quality script found: ${QUALITY_SCRIPT}")
|
|
else()
|
|
message(STATUS "Quality script not found at ${QUALITY_SCRIPT} - quality targets will print an error at build time")
|
|
endif()
|
|
|
|
# Ensure output directory exists for CI reports
|
|
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/quality-reports)
|
|
|
|
# Quick quality check (essential checks only)
|
|
add_custom_target(quality-quick
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile quick --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for quality-quick: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Running quick code quality analysis"
|
|
)
|
|
|
|
# Normal quality check (standard checks)
|
|
add_custom_target(quality
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile normal --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for quality: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Running standard code quality analysis"
|
|
)
|
|
|
|
# Strict quality check (all checks, warnings as errors)
|
|
add_custom_target(quality-strict
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile strict --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for quality-strict: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Running strict code quality analysis"
|
|
)
|
|
|
|
# CI quality check (optimized for continuous integration)
|
|
add_custom_target(quality-ci
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile ci --format xml --output \"${CMAKE_BINARY_DIR}/quality-reports/\" --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for quality-ci: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Running CI code quality analysis with XML output"
|
|
)
|
|
|
|
# Quality check with automatic formatting
|
|
add_custom_target(quality-format
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile normal --fix-format --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for quality-format: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Running code quality analysis with automatic formatting"
|
|
)
|
|
|
|
# Git-aware quality check (only modified files)
|
|
add_custom_target(quality-git
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile normal --git --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for quality-git: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Running code quality analysis on git-modified files"
|
|
)
|
|
|
|
# Generate quality baseline
|
|
add_custom_target(quality-baseline
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile normal --generate-baseline --output \"${CMAKE_BINARY_DIR}/quality-reports/\" --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for quality-baseline: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Generating code quality baseline"
|
|
)
|
|
|
|
message(STATUS "Code quality targets configured:")
|
|
message(STATUS " quality-quick - Quick essential checks")
|
|
message(STATUS " quality - Standard quality analysis")
|
|
message(STATUS " quality-strict - Strict analysis (warnings as errors)")
|
|
message(STATUS " quality-ci - CI-optimized with XML output")
|
|
message(STATUS " quality-format - Analysis with auto-formatting")
|
|
message(STATUS " quality-git - Analysis of git-modified files only")
|
|
message(STATUS " quality-baseline - Generate quality baseline")
|
|
|
|
# Add quality checks to development workflow targets (only if format-code exists)
|
|
if(TARGET format-code)
|
|
add_dependencies(quality format-code)
|
|
endif()
|
|
|
|
# Find clang-format for code formatting
|
|
find_program(CLANG_FORMAT_PATH clang-format)
|
|
if(CLANG_FORMAT_PATH AND EXISTS "${CMAKE_SOURCE_DIR}/scripts/format-code.sh")
|
|
add_custom_target(format-code
|
|
COMMAND ${CMAKE_SOURCE_DIR}/scripts/format-code.sh
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Formatting code with clang-format"
|
|
)
|
|
|
|
add_custom_target(format-check
|
|
COMMAND ${CMAKE_SOURCE_DIR}/scripts/format-code.sh --check
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Checking code formatting"
|
|
)
|
|
|
|
message(STATUS "Code formatting targets enabled:")
|
|
message(STATUS " format-code - Format all C++ files")
|
|
message(STATUS " format-check - Check if formatting is needed")
|
|
endif()
|
|
|
|
# Combined development targets (always defined; fail gracefully if prerequisites are missing)
|
|
add_custom_target(dev-check
|
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
|
|
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure --parallel
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile normal --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for dev-check: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
COMMENT "Running full development checks (build + test + quality)"
|
|
)
|
|
|
|
if(TARGET format-code)
|
|
add_dependencies(dev-check format-code)
|
|
endif()
|
|
|
|
if(TARGET quality)
|
|
add_dependencies(dev-check quality)
|
|
endif()
|
|
|
|
# Pre-commit hook target (always defined; fail gracefully if prerequisites are missing)
|
|
add_custom_target(pre-commit
|
|
COMMAND /bin/sh -c "[ -x \"${QUALITY_SCRIPT}\" ] && [ -x \"${CPPCHECK_PATH}\" ] && \"${QUALITY_SCRIPT}\" --profile normal --git --fix-format --build-dir \"${CMAKE_BINARY_DIR}\" || { echo \"Missing prerequisites for pre-commit: cppcheck='${CPPCHECK_PATH}', script='${QUALITY_SCRIPT}'. Install cppcheck and ensure the script exists and is executable.\"; exit 1; }"
|
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
COMMENT "Running pre-commit quality checks and formatting"
|
|
)
|
|
|
|
message(STATUS "Development workflow targets:")
|
|
# Package knobs
|
|
option(YAMS_PACKAGE_PLUGINS "Include plugins in CPack packages" ON)
|
|
message(STATUS " dev-check - Full development check (build + test + quality)")
|
|
message(STATUS " pre-commit - Pre-commit checks (git files only, with formatting)")
|
|
|
|
# CPack configuration
|
|
set(CPACK_PACKAGE_VENDOR "YAMS Project")
|
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION})
|
|
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
|
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
|
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
|
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
|
|
|
# CPack already included above at line 1306
|
|
# YamsConfig.cmake generation and installation already handled above
|
|
# Optional install of external (Python) Ghidra plugin artifacts
|
|
option(YAMS_INSTALL_GHIDRA_PLUGIN "Install external Ghidra plugin (Python) into plugins dir" OFF)
|
|
|
|
# Install external plugins (Python) when enabled
|
|
if(YAMS_INSTALL_GHIDRA_PLUGIN)
|
|
if(EXISTS ${CMAKE_SOURCE_DIR}/plugins/yams-ghidra-plugin/CMakeLists.txt)
|
|
add_subdirectory(${CMAKE_SOURCE_DIR}/plugins/yams-ghidra-plugin ${CMAKE_BINARY_DIR}/plugins/yams-ghidra-plugin)
|
|
elseif(EXISTS ${CMAKE_SOURCE_DIR}/examples/plugins/yams-ghidra-plugin/CMakeLists.txt)
|
|
add_subdirectory(${CMAKE_SOURCE_DIR}/examples/plugins/yams-ghidra-plugin ${CMAKE_BINARY_DIR}/examples/plugins/yams-ghidra-plugin)
|
|
endif()
|
|
endif()
|