cmake_minimum_required(VERSION 3.15) include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) get_filename_component(root ${CMAKE_CURRENT_LIST_DIR} ABSOLUTE) # The `binroot` path includes a platform+profile segment get_filename_component(binroot ${CMAKE_CURRENT_BINARY_DIR}/.. ABSOLUTE) # Rust takes care of the different platform+profiles on its own, # therefore we don't need to use separate target directories for different # platform+profiles combinations. get_filename_component(rust_binroot ${binroot}/.. ABSOLUTE) # Platform detection if(APPLE) set(OS "macos") elseif(UNIX) set(OS "linux") endif() message(STATUS "OS detected: ${OS}") # Suppress 'has no symbols' warnings from ranlib on macOS. # Cross-platform deps (cpu_features, hiredis) compile platform stubs that are # empty on irrelevant architectures, producing harmless noise. # CMake may pick up LLVM's llvm-ranlib from PATH, which doesn't support # -no_warning_for_no_symbols. Use xcrun to locate Apple's native ranlib. if(APPLE) execute_process( COMMAND xcrun --find ranlib OUTPUT_VARIABLE APPLE_RANLIB OUTPUT_STRIP_TRAILING_WHITESPACE ) set(CMAKE_C_ARCHIVE_FINISH "${APPLE_RANLIB} -no_warning_for_no_symbols ") set(CMAKE_CXX_ARCHIVE_FINISH "${APPLE_RANLIB} -no_warning_for_no_symbols ") endif() # Always use .so extension even on macOS set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") # Export the underlying compiler invocations in a compile_commands.json, located in the bin directory. # It'll be picked up by clangd to provide LSP support in editors like Zed and VSCode set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Define compiler setup function function(setup_cc_options) message("# CMAKE_C_COMPILER_ID: " ${CMAKE_C_COMPILER_ID}) # Accumulate C/CXX flags locally and publish once. set(... PARENT_SCOPE) # does NOT update the function-local variable, so a later read of # ${CMAKE_C_FLAGS} would miss earlier additions (MOD-14916). set(_common_c_flags "${CMAKE_C_FLAGS} -fPIC -g -pthread -fno-strict-aliasing -Wno-unused-function -Wno-unused-variable -Wno-sign-compare -fcommon -funsigned-char") set(_common_cxx_flags "${CMAKE_CXX_FLAGS} -fPIC -g -pthread -fno-strict-aliasing -Wno-unused-function -Wno-unused-variable -Wno-sign-compare") set(CMAKE_CXX_STANDARD 20) # musl compatibility headers emit #warning for non-portable include paths. # Treat those diagnostics as build failures so Alpine catches them early. execute_process( COMMAND ${CMAKE_C_COMPILER} -dumpmachine OUTPUT_VARIABLE C_COMPILER_TARGET OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) if(C_COMPILER_TARGET MATCHES "musl" OR EXISTS "/etc/alpine-release") check_c_compiler_flag("-Werror=cpp" HAS_C_WERROR_CPP) check_cxx_compiler_flag("-Werror=cpp" HAS_CXX_WERROR_CPP) if(HAS_C_WERROR_CPP) set(_common_c_flags "${_common_c_flags} -Werror=cpp") endif() if(HAS_CXX_WERROR_CPP) set(_common_cxx_flags "${_common_cxx_flags} -Werror=cpp") endif() endif() # MOD-14916: inline LSE atomics on Linux AArch64 -- avoid libgcc # __aarch64_* outline-atomics helpers on every atomic RMW. # -march=armv8-a+lse adds LSE without raising -march, so deps that # probe `-march=armv8-a` (e.g. VecSim's CXX_ARMV8A # gate on NEON_HP.cpp) still pass. # -mno-outline-atomics canonical disable spelling (the "=no" form is # silently ignored by GCC). # Apple excluded: Apple clang rejects -mno-outline-atomics and Apple # Silicon already emits inline LSE. if(NOT APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$" AND INLINE_LSE_ATOMICS) include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) set(_aarch64_lse_flags "-march=armv8-a+lse -mtune=neoverse-n1 -mno-outline-atomics") check_c_compiler_flag("${_aarch64_lse_flags}" _c_lse_ok) check_cxx_compiler_flag("${_aarch64_lse_flags}" _cxx_lse_ok) if(_c_lse_ok AND _cxx_lse_ok) set(_common_c_flags "${_common_c_flags} ${_aarch64_lse_flags}") set(_common_cxx_flags "${_common_cxx_flags} ${_aarch64_lse_flags}") else() message(WARNING "AArch64 LSE build flags not accepted by compiler; " "outline atomics will remain in the .so") endif() endif() set(CMAKE_C_FLAGS "${_common_c_flags}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS "${_common_cxx_flags}" PARENT_SCOPE) # Config-specific flags only — CMake appends these to CMAKE_C/CXX_FLAGS automatically if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_C_FLAGS_DEBUG "-O0 -fno-omit-frame-pointer -ggdb" PARENT_SCOPE) set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fno-omit-frame-pointer -ggdb" PARENT_SCOPE) elseif(PROFILE) set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -fno-omit-frame-pointer" PARENT_SCOPE) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -fno-omit-frame-pointer" PARENT_SCOPE) else() set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3" PARENT_SCOPE) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3" PARENT_SCOPE) endif() endfunction() # Define shared object setup function function(setup_shared_object_target target) if(APPLE) set_target_properties(${target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") # Force .so extension on macOS instead of .dylib set_target_properties(${target} PROPERTIES SUFFIX ".so") else() # We are building a shared library and want to verify that any reference to a symbol within the library will resolve to # the library's own definition, rather than to a definition in another shared library or the main executable. set_target_properties(${target} PROPERTIES LINK_FLAGS "-pthread -shared -Wl,-Bsymbolic,-Bsymbolic-functions") endif() set_target_properties(${target} PROPERTIES PREFIX "") endfunction() # Define debug symbols extraction function function(extract_debug_symbols target) if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND NOT APPLE) add_custom_command(TARGET ${target} POST_BUILD COMMAND cp $ $.debug COMMAND objcopy --add-gnu-debuglink=$.debug $ COMMAND strip -g $ COMMENT "Extracting debug symbols from ${target}" ) endif() endfunction() #---------------------------------------------------------------------------------------------- # Command line options with default values option(USE_REDIS_ALLOCATOR "Use redis allocator" ON) option(BUILD_SEARCH_UNIT_TESTS "Build unit tests" OFF) option(VERBOSE_UTESTS "Enable verbose unit tests" OFF) option(ENABLE_ASSERT "Enable assertions" OFF) set(MAX_WORKER_THREADS "" CACHE STRING "Override the maximum parallel worker threads allowed in thread-pool") option(BUILD_TESTING "Enable testing for cpu-features dep" OFF) # Inline LSE atomics on Linux AArch64 (Armv8.1-a+). Disable for pre-Armv8.1-a # cores (Cortex-A72, AWS Graviton1, Raspberry Pi 4) to avoid SIGILL on load. option(INLINE_LSE_ATOMICS "Inline LSE atomics on Linux AArch64 (Armv8.1-a+)" ON) # Regenerate Rust→C FFI headers via cheadergen during the build. # Default: ON when RediSearch is the top-level project, OFF when consumed as a # subdirectory. Downstream consumers pulling a master commit don't need to # regenerate — checked-in headers are guaranteed fresh by CI — and cheadergen # may not be installed in their environment. if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(_redisearch_generate_headers_default ON) else() set(_redisearch_generate_headers_default OFF) endif() option(REDISEARCH_GENERATE_HEADERS "Regenerate Rust→C FFI headers via cheadergen during the build" ${_redisearch_generate_headers_default}) #---------------------------------------------------------------------------------------------- project(redisearch) # Configure output paths based on build configuration set(MODULE_NAME "search" CACHE STRING "Module name" FORCE) if(NOT DEFINED COORD_TYPE) set(COORD_TYPE "oss") endif() set(RUST_BINROOT "${rust_binroot}") if(COORD_TYPE STREQUAL "oss") set(BINDIR "${binroot}/search-community") elseif(COORD_TYPE STREQUAL "rlec") set(BINDIR "${binroot}/search-enterprise") add_compile_definitions(PRIVATE RS_CLUSTER_ENTERPRISE) else() message(FATAL_ERROR "Invalid COORD_TYPE (='${COORD_TYPE}'). Should be either 'oss' or 'rlec'") endif() #---------------------------------------------------------------------------------------------- # Configure compiler options setup_cc_options() # Sanitizer settings message(STATUS "SAN: ${SAN}") if(SAN) if(SAN STREQUAL "address") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize-recover=all") set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fsanitize=address") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") message(STATUS "CMAKE_LINKER_FLAGS: ${CMAKE_LINKER_FLAGS}") message(STATUS "CMAKE_SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}") endif() endif() # Coverage settings message(STATUS "COV: ${COV}") if (COV) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") add_compile_definitions(COVERAGE=1) endif() # Get Git version info - to be printed in log upon loading the module execute_process( COMMAND git describe --abbrev=7 --always WORKING_DIRECTORY ${root} OUTPUT_VARIABLE GIT_VERSPEC OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) execute_process( COMMAND git rev-parse HEAD WORKING_DIRECTORY ${root} OUTPUT_VARIABLE GIT_SHA OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) # ugly hack for cpu_features::list_cpu_features coming from VecSim set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} ${CMAKE_LD_FLAGS}") # Treat all format-string warnings as errors set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=format") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=format") # Detect available compiler flags for subsequent use. These flags are global and available # throughout the build. Some flags below form GCC/Clang equivalence pairs (only one will be # available on a given compiler), others stand alone. check_c_compiler_flag("-Werror=discarded-qualifiers" HAS_WERROR_DISCARDED_QUALIFIERS) check_c_compiler_flag("-Werror=implicit-function-declaration" HAS_WERROR_IMPLICIT_FUNCTION_DECLARATION) check_c_compiler_flag("-Werror=incompatible-pointer-types-discards-qualifiers" HAS_WERROR_INCOMPATIBLE_POINTER_TYPES_DISCARDS_QUALIFIERS) check_c_compiler_flag("-Werror=jump-misses-init" HAS_WERROR_JUMP_MISSES_INIT) check_c_compiler_flag("-Werror=sometimes-uninitialized" HAS_WERROR_SOMETIMES_UNINITIALIZED) # Homebrew silently removes '-Werror' flags which breaks the regular form of detection. # Instead we specifically check for the '-Wno-error' variants here separately. check_c_compiler_flag("-Wno-error=discarded-qualifiers" HAS_WNO_ERROR_DISCARDED_QUALIFIERS) check_c_compiler_flag("-Wno-error=incompatible-pointer-types-discards-qualifiers" HAS_WNO_ERROR_INCOMPATIBLE_POINTER_TYPES_DISCARDS_QUALIFIERS) # Promote specific warnings to errors. These use add_compile_options() which # propagates to ALL subdirectories, including deps (libuv, VectorSimilarity, etc.). # Each flag is compiler-specific (gcc vs clang), so if a dep needs an override # (e.g. -Wno-error=X via target_compile_options), guard it with the same # compiler check or a $ generator expression. # Promote dropped const/volatile qualifier warnings to errors if(HAS_WERROR_DISCARDED_QUALIFIERS) add_compile_options($<$:-Werror=discarded-qualifiers>) endif() if(HAS_WERROR_INCOMPATIBLE_POINTER_TYPES_DISCARDS_QUALIFIERS) add_compile_options($<$:-Werror=incompatible-pointer-types-discards-qualifiers>) endif() # Promote jumps/paths that skip a variable's initialization to errors if(HAS_WERROR_JUMP_MISSES_INIT) add_compile_options($<$:-Werror=jump-misses-init>) endif() if(HAS_WERROR_SOMETIMES_UNINITIALIZED) add_compile_options($<$:-Werror=sometimes-uninitialized>) endif() # Promote implicit function declarations to errors if(HAS_WERROR_IMPLICIT_FUNCTION_DECLARATION) add_compile_options($<$:-Werror=implicit-function-declaration>) endif() # NOTE: GIT_VERSPEC and GIT_SHA are intentionally NOT defined globally. They # change on every commit, so a global -D... would be part of every C/C++ # compile command and bust the sccache key for the whole tree on every push. # They are scoped to the only two TUs that read them in src/CMakeLists.txt. add_compile_definitions( "REDISEARCH_MODULE_NAME=\"${MODULE_NAME}\"" REDISMODULE_SDK_RLEC _GNU_SOURCE) if(BUILD_INTEL_SVS_OPT) add_compile_definitions("BUILD_INTEL_SVS_OPT=1") endif() if(USE_REDIS_ALLOCATOR) add_compile_definitions(REDIS_MODULE_TARGET) endif() if(VERBOSE_UTESTS) add_compile_definitions(VERBOSE_UTESTS=1) endif() # Platform-specific settings if(APPLE) # Find OpenSSL on macOS. Prefer explicit paths, then known Homebrew prefixes. if(NOT OPENSSL_ROOT_DIR AND DEFINED LIBSSL_DIR) set(OPENSSL_ROOT_DIR "${LIBSSL_DIR}") endif() if(NOT OPENSSL_ROOT_DIR) foreach(_openssl_prefix "/opt/homebrew/opt/openssl@3" "/usr/local/opt/openssl@3" "/opt/homebrew/opt/openssl" "/usr/local/opt/openssl") if(EXISTS "${_openssl_prefix}") set(OPENSSL_ROOT_DIR "${_openssl_prefix}") break() endif() endforeach() endif() find_package(OpenSSL REQUIRED) include_directories(${OPENSSL_INCLUDE_DIR}) if(DEFINED LIBSSL_DIR) include_directories(${LIBSSL_DIR}/include) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L${LIBSSL_DIR}/lib") endif() set(SSL_LIBS ${OPENSSL_LIBRARIES}) set(CMAKE_LD_FLAGS "${CMAKE_LD_FLAGS} -dynamiclib") else() set(SSL_LIBS crypto crypt ssl) endif() set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_LD_FLAGS}") # On debug artifacts, enable assertions if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR ENABLE_ASSERT) add_compile_definitions(ENABLE_ASSERT=1) endif() #---------------------------------------------------------------------------------------------- # Include external dependencies (hiredis and boost - needed before src/) include(${root}/build/hiredis/hiredis.cmake) if (NOT DEFINED BOOST_DIR) set(BOOST_DIR "${root}/.install/boost") endif() include(${root}/build/boost/boost.cmake) if (NOT IS_DIRECTORY ${BOOST_DIR}) message(FATAL_ERROR "BOOST_DIR is not defined or does not point to a valid directory ${BOOST_DIR}") endif() message(STATUS "BOOST_DIR: ${BOOST_DIR}") set(BOOST_ROOT ${BOOST_DIR}) set(Boost_NO_WARN_NEW_VERSIONS ON) #---------------------------------------------------------------------------------------------- # Include directories (needed by both src/ and tests/) include_directories( ${root}/src ${root}/src/buffer ${root}/src/dict ${root}/src/coord ${root}/src/redisearch_rs/headers ${root}/deps/libuv/include ${root}/deps ${root}/deps/VectorSimilarity/src ${root}/deps/rmalloc ${BOOST_DIR} ${root}) #---------------------------------------------------------------------------------------------- # Build the C code and its dependencies add_subdirectory(src) # Build the Rust code add_subdirectory(src/redisearch_rs) # C/C++ code includes headers generated by cheadergen from Rust FFI crates. # Ensure header generation completes before any C/C++ compilation begins. # Every target that transitively includes generated headers (via redisearch.h etc.) # must wait for cheadergen. add_dependencies(rscore cheadergen_generate) add_dependencies(redisearch-geometry cheadergen_generate) add_dependencies(iterators cheadergen_generate) add_dependencies(index_result cheadergen_generate) add_dependencies(coordinator-core cheadergen_generate) add_dependencies(rmr cheadergen_generate) #---------------------------------------------------------------------------------------------- # Build the final shared library by merging C and Rust static libraries # Default behavior: SHARED when main project, STATIC when subdirectory if(NOT DEFINED REDISEARCH_BUILD_SHARED) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(REDISEARCH_BUILD_SHARED ON) else() set(REDISEARCH_BUILD_SHARED OFF) endif() endif() option(REDISEARCH_BUILD_SHARED "Build RediSearch as shared library" OFF) # Create library with determined type if(REDISEARCH_BUILD_SHARED) add_library(redisearch SHARED ${REDISEARCH_C_FINAL_OBJECTS}) message(STATUS "Building RediSearch as SHARED library") else() add_library(redisearch STATIC ${REDISEARCH_C_FINAL_OBJECTS}) message(STATUS "Building RediSearch as STATIC library") endif() if(COORD_TYPE STREQUAL "rlec") set_target_properties(redisearch PROPERTIES OUTPUT_NAME "module-enterprise") endif() set_target_properties(redisearch PROPERTIES LINKER_LANGUAGE CXX C_STANDARD 17 C_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON) setup_shared_object_target(redisearch "") # Link the final library - redisearch_c brings in all C dependencies transitively target_link_libraries(redisearch redisearch_c redisearch_rs ${SSL_LIBS} ${CMAKE_LD_LIBS}) extract_debug_symbols(redisearch) add_dependencies(redisearch redisearch_rs) #---------------------------------------------------------------------------------------------- # Unit tests configuration if(BUILD_SEARCH_UNIT_TESTS) enable_testing() # Disable coverage on C++ tests/benchmarks to avoid geninfo mismatch errors if (COV) string(REPLACE "--coverage" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endif() add_subdirectory(tests/cpptests/redismock) set(BUILD_GTEST ON CACHE BOOL "enable gtest" FORCE) set(BUILD_GMOCK OFF CACHE BOOL "disable gmock" FORCE) add_subdirectory(deps/googletest) add_subdirectory(tests/cpptests) add_subdirectory(tests/cpptests/micro-benchmarks) add_subdirectory(tests/ctests) add_subdirectory(tests/ctests/ext-example example_extension) add_subdirectory(tests/ctests/coord_tests) endif()