cmake_minimum_required(VERSION 3.20) # 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)") 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 ) # 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 */ #pragma once #define YAMS_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ #define YAMS_VERSION_MINOR @PROJECT_VERSION_MINOR@ #define YAMS_VERSION_PATCH @PROJECT_VERSION_PATCH@ #define YAMS_VERSION_STRING \"@PROJECT_VERSION@\" ") configure_file( "${_YAMS_VERSION_TEMPLATE}" "${CMAKE_CURRENT_BINARY_DIR}/generated/yams/version.hpp" @ONLY ) # Install generated version header install(FILES "${CMAKE_CURRENT_BINARY_DIR}/generated/yams/version.hpp" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/yams" COMPONENT development) # Ensure default CMake package dir uses 'Yams' casing for consumers include(GNUInstallDirs) 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() # C++20 is required set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # 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_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) # ------------------------------------------------------------------------------ # Docs build (Pandoc) and embedded CLI help assets # ------------------------------------------------------------------------------ option(YAMS_BUILD_DOCS "Build CLI docs, generate manpages, and embed verbose help" ON) 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=. 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() # Strip quotes from INPUT and OUTPUT if present string(REGEX REPLACE \"^\\\"(.*)\\\"$\" \"\\\\1\" INPUT_CLEAN \"\${INPUT}\") string(REGEX REPLACE \"^\\\"(.*)\\\"$\" \"\\\\1\" OUTPUT_CLEAN \"\${OUTPUT}\") # Check if input file exists if(NOT EXISTS \"\${INPUT_CLEAN}\") message(FATAL_ERROR \"Input file does not exist: '\${INPUT_CLEAN}'\") endif() # Check if 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) string(REPLACE \"\\\\\" \"\\\\\\\\\" _c \"\${_c}\") string(REPLACE \"\\\"\" \"\\\\\\\"\" _c \"\${_c}\") string(REPLACE \"\\n\" \"\\\\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 '## ' or '## {#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) 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() # 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 checks include(CheckCXXSourceCompiles) check_cxx_source_compiles(" #include #include #include #include #include template concept Integral = std::integral; int main() { std::span s; return 0; } " HAVE_CXX20_FEATURES) if(NOT HAVE_CXX20_FEATURES) message(FATAL_ERROR "C++20 features not fully supported by compiler") endif() # Compiler warnings if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_compile_options( -Wall -Wextra -Wpedantic -Wcast-align -Wcast-qual -Wconversion -Wsign-conversion -Wdouble-promotion -Wformat=2 -Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual -Wshadow $<$:-Og> $<$:-g3> $<$:-O3> ) # Architecture-specific optimizations (not on Apple Silicon) if(NOT APPLE OR NOT CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") add_compile_options($<$:-march=native>) endif() # Coroutine support if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") add_compile_options(-fcoroutines) elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-stdlib=libc++) add_link_options(-stdlib=libc++) # Apple Clang has coroutines enabled by default in C++20 if(NOT APPLE) add_compile_options(-fcoroutines-ts) endif() endif() elseif(MSVC) add_compile_options( /W4 /permissive- /Zc:__cplusplus /Zc:preprocessor # For proper __VA_OPT__ support $<$:/Od /RTC1> $<$:/O2 /GL> ) add_link_options($<$:/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}") # Threads (required for websocketpp) find_package(Threads REQUIRED) # Google Test if(YAMS_BUILD_TESTS) 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 if(YAMS_BUILD_BENCHMARKS) 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() # spdlog for logging FetchContent_Declare( spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git GIT_TAG v1.12.0 ) set(SPDLOG_INSTALL ON 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) endif() # CLI11 for command line parsing if(YAMS_BUILD_TOOLS) FetchContent_Declare( CLI11 GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git GIT_TAG v2.4.1 ) FetchContent_MakeAvailable(CLI11) # FTXUI for Terminal User Interface FetchContent_Declare( ftxui GIT_REPOSITORY https://github.com/ArthurSonzogni/FTXUI.git GIT_TAG v5.0.0 ) set(FTXUI_BUILD_DOCS OFF CACHE BOOL "" FORCE) set(FTXUI_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) set(FTXUI_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(FTXUI_BUILD_TESTS_FUZZER OFF CACHE BOOL "" FORCE) set(FTXUI_ENABLE_INSTALL ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(ftxui) endif() # Boost.Beast is part of Boost and will be available automatically # No separate dependency needed for WebSocket transport # Zstandard for compression 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) # LZMA SDK for compression 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/LzFind.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/CpuArch.c ${lzma_SOURCE_DIR}/C/Alloc.c ${lzma_SOURCE_DIR}/C/LzFindMt.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/LzFindOpt.c ${lzma_SOURCE_DIR}/C/7zStream.c ) target_include_directories(lzma_sdk PUBLIC $ $ ) # 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() # Install lzma_sdk install(TARGETS lzma_sdk EXPORT YamsTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) endif() # Use built-in CRC32 from zstd instead of separate library # SQLite3 with FTS5 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 $ $ ) # 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() # Install sqlite3 install(TARGETS sqlite3 EXPORT YamsTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) 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 $ $ ) target_compile_features(yams_core INTERFACE cxx_std_20) add_library(yams::core ALIAS yams_core) # Add subdirectories add_subdirectory(src) if(YAMS_BUILD_TESTS) enable_testing() 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 install(TARGETS yams_core yams_indexing 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 ) # 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() 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() # 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}) include(CPack) # Generate and install CMake package config for find_package(Yams) include(CMakePackageConfigHelpers) 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} )