Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
683 lines
24 KiB
Meson
683 lines
24 KiB
Meson
project('yams', ['cpp', 'c'],
|
|
version: '0.12.0', # x-release-please-version
|
|
default_options: [
|
|
'buildtype=release',
|
|
'warning_level=2',
|
|
'cpp_std=c++20',
|
|
'c_std=c11',
|
|
],
|
|
)
|
|
|
|
fs = import('fs')
|
|
|
|
cpp = meson.get_compiler('cpp')
|
|
|
|
wasi_enabled = get_option('wasi')
|
|
if wasi_enabled
|
|
add_project_arguments('-DYAMS_WASI=1', language: 'cpp')
|
|
endif
|
|
|
|
_yams_include_dir = join_paths(meson.current_source_dir(), 'include')
|
|
if cpp.get_id() == 'msvc' or cpp.get_id() == 'clang-cl'
|
|
add_project_arguments('/I' + _yams_include_dir, language: 'cpp')
|
|
else
|
|
add_project_arguments('-I' + _yams_include_dir, language: 'cpp')
|
|
endif
|
|
|
|
if host_machine.system() == 'darwin'
|
|
add_global_link_arguments('-Wl,-no_warn_duplicate_libraries', language: 'cpp')
|
|
endif
|
|
|
|
if host_machine.system() == 'windows'
|
|
add_project_arguments('-D_WIN32_WINNT=0x0A00', language: 'cpp')
|
|
add_project_arguments('-D_CRT_SECURE_NO_WARNINGS', language: 'cpp')
|
|
add_project_arguments('-D_SILENCE_CXX20_OLD_SHARED_PTR_ATOMIC_SUPPORT_DEPRECATION_WARNING', language: 'cpp')
|
|
add_project_arguments('-DNOMINMAX', language: 'cpp')
|
|
add_project_arguments('-DWIN32_LEAN_AND_MEAN', language: 'cpp')
|
|
# Large source files like mcp_server.cpp exceed MSVC's default section limit
|
|
add_project_arguments('/bigobj', language: 'cpp')
|
|
endif
|
|
|
|
# --- libc++ hardening mode ---
|
|
# Apply hardening defines when using libc++ (Darwin always uses libc++, Linux when explicitly set)
|
|
libcxx_hardening = get_option('libcxx-hardening')
|
|
|
|
if libcxx_hardening != 'none'
|
|
# Check if we're actually using libc++
|
|
using_libcxx = false
|
|
if host_machine.system() == 'darwin'
|
|
# macOS always uses libc++
|
|
using_libcxx = true
|
|
elif cpp.get_id() == 'clang' or cpp.get_id() == 'clang-cl'
|
|
# On non-Darwin, check if -stdlib=libc++ is in use
|
|
# This is a heuristic; proper detection would check link flags
|
|
has_libcxx = cpp.has_header('__config', prefix: '#include <ciso646>')
|
|
if has_libcxx
|
|
using_libcxx = true
|
|
endif
|
|
endif
|
|
|
|
if using_libcxx
|
|
hardening_mode_map = {
|
|
'fast': '_LIBCPP_HARDENING_MODE_FAST',
|
|
'extensive': '_LIBCPP_HARDENING_MODE_EXTENSIVE',
|
|
'debug': '_LIBCPP_HARDENING_MODE_DEBUG',
|
|
}
|
|
hardening_define = hardening_mode_map.get(libcxx_hardening)
|
|
if hardening_define != ''
|
|
add_project_arguments('-D' + hardening_define, language: 'cpp')
|
|
message('Enabling libc++ hardening mode: ' + libcxx_hardening + ' (' + hardening_define + ')')
|
|
endif
|
|
else
|
|
warning('libc++ hardening requested but not using libc++ (detected: ' + cpp.get_id() + '). Hardening mode ignored.')
|
|
endif
|
|
endif
|
|
|
|
# --- ThreadSanitizer for race detection ---
|
|
enable_tsan = get_option('enable-tsan')
|
|
enable_asan = get_option('enable-asan')
|
|
|
|
if enable_tsan and enable_asan
|
|
error('TSAN and ASAN cannot be enabled in the same build. Configure separate build directories.')
|
|
endif
|
|
|
|
if enable_tsan
|
|
buildtype = get_option('buildtype')
|
|
# TSan is most useful in debug builds; warn if enabled in release
|
|
if buildtype == 'release' or buildtype == 'plain'
|
|
warning('ThreadSanitizer enabled in non-debug build. This will significantly impact performance.')
|
|
endif
|
|
|
|
# Check if compiler supports TSan
|
|
if cpp.has_argument('-fsanitize=thread')
|
|
# Use additional flags for better stack traces (sanitizer is set via b_sanitize option)
|
|
tsan_extra_flags = ['-fno-omit-frame-pointer']
|
|
add_project_arguments(tsan_extra_flags, language: 'cpp')
|
|
add_project_arguments(tsan_extra_flags, language: 'c')
|
|
message('ThreadSanitizer enabled - expect significant performance impact')
|
|
message('Note: TSan requires all code (including dependencies) to be compiled with -fsanitize=thread')
|
|
else
|
|
error('ThreadSanitizer requested but compiler does not support -fsanitize=thread')
|
|
endif
|
|
endif
|
|
|
|
if enable_asan
|
|
buildtype = get_option('buildtype')
|
|
# ASan is most useful in debug builds; warn if enabled in release
|
|
if buildtype == 'release' or buildtype == 'plain'
|
|
warning('AddressSanitizer enabled in non-debug build. This will significantly impact performance.')
|
|
endif
|
|
|
|
# Check if compiler supports ASan
|
|
if cpp.has_argument('-fsanitize=address')
|
|
# Use additional flags for better stack traces (sanitizer is set via b_sanitize option)
|
|
asan_extra_flags = ['-fno-omit-frame-pointer']
|
|
add_project_arguments(asan_extra_flags, language: 'cpp')
|
|
add_project_arguments(asan_extra_flags, language: 'c')
|
|
message('AddressSanitizer enabled - memory errors will fail fast')
|
|
else
|
|
error('AddressSanitizer requested but compiler does not support -fsanitize=address')
|
|
endif
|
|
endif
|
|
|
|
if host_machine.system() == 'darwin' and cpp.get_id() == 'clang'
|
|
# Apple clang + external fmt/spdlog can fail when fmt enables
|
|
# consteval format-string checking inside spdlog headers.
|
|
add_project_arguments('-DFMT_CONSTEVAL=', language: 'cpp')
|
|
endif
|
|
|
|
# --- C++20 Modules Support (Experimental) ---
|
|
# Detect and configure C++20 module compilation when enabled
|
|
yams_modules_enabled = get_option('enable-modules')
|
|
yams_modules_supported = false
|
|
yams_module_args = []
|
|
yams_module_flags = []
|
|
yams_use_modules_def = []
|
|
yams_module_cpp_args = []
|
|
|
|
if yams_modules_enabled
|
|
# Include module detection and configuration
|
|
subdir('meson')
|
|
endif
|
|
|
|
# --- Install prefix for plugin auto-trust ---
|
|
# Pass the install prefix to C++ code so the daemon knows where system plugins are installed
|
|
install_prefix = get_option('prefix')
|
|
# Escape backslashes for Windows paths to avoid compiler warnings about unrecognized escape sequences
|
|
install_prefix_escaped = install_prefix.replace('\\', '\\\\')
|
|
add_project_arguments('-DYAMS_INSTALL_PREFIX="' + install_prefix_escaped + '"', language: 'cpp')
|
|
message('YAMS install prefix: ' + install_prefix)
|
|
|
|
# --- Shared Conan/CMake discovery paths ---
|
|
conan_candidate_dirs = []
|
|
conan_gen_opt = get_option('conan-generators-dir')
|
|
if conan_gen_opt != ''
|
|
conan_candidate_dirs += [join_paths(meson.project_source_root(), conan_gen_opt)]
|
|
endif
|
|
|
|
base_build = meson.project_build_root()
|
|
default_candidates = [
|
|
base_build,
|
|
join_paths(base_build, 'conan'),
|
|
join_paths(base_build, 'conan', 'generators'),
|
|
join_paths(base_build, 'generators'),
|
|
join_paths(base_build, 'build-release', 'conan', 'generators'),
|
|
join_paths(base_build, 'build-debug', 'conan', 'generators'),
|
|
]
|
|
|
|
foreach variant : ['build-debug', 'build-release', 'debug', 'release']
|
|
variant_path = join_paths(base_build, variant)
|
|
if fs.exists(variant_path)
|
|
default_candidates += [variant_path]
|
|
default_candidates += [join_paths(variant_path, 'conan')]
|
|
default_candidates += [join_paths(variant_path, 'conan', 'generators')]
|
|
default_candidates += [join_paths(variant_path, 'generators')]
|
|
endif
|
|
endforeach
|
|
|
|
foreach cand : default_candidates
|
|
if fs.exists(cand)
|
|
conan_candidate_dirs += [cand]
|
|
endif
|
|
endforeach
|
|
|
|
cli11_cmake_paths = []
|
|
foreach base : conan_candidate_dirs
|
|
if fs.exists(base)
|
|
cli11_cmake_paths += base
|
|
foreach sub : ['CLI11', 'cli11']
|
|
cand = join_paths(base, sub)
|
|
if fs.exists(cand)
|
|
cli11_cmake_paths += cand
|
|
endif
|
|
endforeach
|
|
endif
|
|
endforeach
|
|
|
|
cli11_opt = get_option('cli11-cmake-path')
|
|
if cli11_opt != ''
|
|
cli11_cmake_paths += cli11_opt
|
|
endif
|
|
|
|
onnx_cmake_paths = []
|
|
foreach base : conan_candidate_dirs
|
|
if fs.exists(base)
|
|
onnx_cmake_paths += base
|
|
foreach sub : ['onnxruntime', 'onnxruntime/Release', 'onnxruntime/Debug']
|
|
cand = join_paths(base, sub)
|
|
if fs.exists(cand)
|
|
onnx_cmake_paths += cand
|
|
endif
|
|
endforeach
|
|
endif
|
|
endforeach
|
|
|
|
onnx_opt_path = get_option('onnxruntime-cmake-path')
|
|
if onnx_opt_path != ''
|
|
onnx_cmake_paths += onnx_opt_path
|
|
endif
|
|
|
|
# --- Shared dependency resolution ---
|
|
cli11_resolved_dep = dependency('cli11', required: false, method: 'pkg-config')
|
|
if not cli11_resolved_dep.found()
|
|
cli11_resolved_dep = dependency('CLI11', required: false, method: 'pkg-config')
|
|
endif
|
|
if not cli11_resolved_dep.found() and cli11_cmake_paths.length() > 0
|
|
cli11_resolved_dep = dependency('cli11', method: 'cmake', cmake_module_path: cli11_cmake_paths, required: false)
|
|
if not cli11_resolved_dep.found()
|
|
cli11_resolved_dep = dependency('CLI11', method: 'cmake', cmake_module_path: cli11_cmake_paths, required: false)
|
|
endif
|
|
endif
|
|
if not cli11_resolved_dep.found()
|
|
cli11_resolved_dep = dependency('CLI11', method: 'cmake', required: false)
|
|
endif
|
|
|
|
onnxruntime_resolved_dep = dependency('onnxruntime', method: 'pkg-config', required: false)
|
|
if not onnxruntime_resolved_dep.found()
|
|
onnxruntime_resolved_dep = dependency('onnxruntime', method: 'cmake', cmake_module_path: onnx_cmake_paths, required: false)
|
|
endif
|
|
if not onnxruntime_resolved_dep.found()
|
|
onnxruntime_resolved_dep = dependency('onnxruntime', required: false)
|
|
endif
|
|
if not onnxruntime_resolved_dep.found()
|
|
onnxruntime_resolved_dep = dependency('libonnxruntime', required: false)
|
|
endif
|
|
|
|
tbb_resolved_dep = dependency('tbb', required: false, method: 'pkg-config')
|
|
if not tbb_resolved_dep.found()
|
|
tbb_resolved_dep = dependency('onetbb', required: false, method: 'pkg-config')
|
|
endif
|
|
if not tbb_resolved_dep.found()
|
|
tbb_resolved_dep = dependency('tbb', required: false)
|
|
endif
|
|
if not tbb_resolved_dep.found()
|
|
tbb_resolved_dep = dependency('onetbb', required: false)
|
|
endif
|
|
|
|
# --- Windows DLL deployment ---
|
|
# On Windows, DLLs must be in the same directory as the executable or in PATH.
|
|
# We extract the DLL paths from the dependency's pkg-config `bindir` variable,
|
|
# then use fs.copyfile() to deploy them to the build directory.
|
|
#
|
|
# This is the Meson-idiomatic approach that doesn't rely on Conan's runtime_deploy.
|
|
windows_runtime_dlls = [] # List of [dll_name, dll_full_path] tuples
|
|
if host_machine.system() == 'windows'
|
|
# Get TBB dependency and extract bindir
|
|
tbb_dep = tbb_resolved_dep
|
|
if tbb_dep.found()
|
|
tbb_prefix = tbb_dep.get_variable(pkgconfig: 'prefix', default_value: '')
|
|
tbb_bindir = tbb_dep.get_variable(pkgconfig: 'bindir', default_value: '')
|
|
if tbb_bindir != ''
|
|
# Resolve ${prefix} placeholder if present
|
|
tbb_bindir = tbb_bindir.replace('${prefix}', tbb_prefix)
|
|
foreach dll_name : ['tbb12.dll', 'tbbbind_2_5.dll', 'tbbmalloc.dll', 'tbbmalloc_proxy.dll']
|
|
dll_path = tbb_bindir / dll_name
|
|
if fs.exists(dll_path)
|
|
windows_runtime_dlls += [[dll_name, dll_path]]
|
|
endif
|
|
endforeach
|
|
endif
|
|
endif
|
|
|
|
# Get ONNX Runtime dependency and extract bindir (prefer Conan CMake config)
|
|
onnx_dep_check = onnxruntime_resolved_dep
|
|
|
|
onnx_bindir_candidates = []
|
|
if onnx_dep_check.found()
|
|
onnx_prefix = onnx_dep_check.get_variable(pkgconfig: 'prefix', default_value: '')
|
|
onnx_bindir = onnx_dep_check.get_variable(pkgconfig: 'bindir', default_value: '')
|
|
if onnx_bindir != ''
|
|
onnx_bindir = onnx_bindir.replace('${prefix}', onnx_prefix)
|
|
onnx_bindir_candidates += [onnx_bindir]
|
|
endif
|
|
|
|
# CMake configs from Conan often expose PACKAGE_PREFIX_DIR; fall back to <prefix>/bin
|
|
cmake_prefix = onnx_dep_check.get_variable(cmake: 'PACKAGE_PREFIX_DIR', default_value: '')
|
|
if cmake_prefix != ''
|
|
onnx_bindir_candidates += [join_paths(cmake_prefix, 'bin')]
|
|
elif onnx_prefix != ''
|
|
onnx_bindir_candidates += [join_paths(onnx_prefix, 'bin')]
|
|
endif
|
|
endif
|
|
|
|
# As a last resort, probe the Conan cache directly (~/.conan2/p/**/p/bin) via a tiny Python helper
|
|
py_mod = import('python')
|
|
python3 = py_mod.find_installation('python3', required: false)
|
|
if not python3.found()
|
|
python3 = py_mod.find_installation('python', required: false)
|
|
endif
|
|
|
|
if python3.found()
|
|
conan_probe = run_command(python3, ['-c', '''import pathlib, os
|
|
root = pathlib.Path(os.path.expanduser("~/.conan2/p"))
|
|
dirs = []
|
|
if root.exists():
|
|
for child in root.iterdir():
|
|
bin_dir = child / "p" / "bin"
|
|
if bin_dir.exists():
|
|
dll = bin_dir / "onnxruntime.dll"
|
|
if dll.exists():
|
|
dirs.append(str(bin_dir))
|
|
print(";".join(dirs))'''], check: false)
|
|
if conan_probe.returncode() == 0
|
|
conan_dirs = conan_probe.stdout().strip().split(';')
|
|
foreach d : conan_dirs
|
|
if d != ''
|
|
onnx_bindir_candidates += [d]
|
|
endif
|
|
endforeach
|
|
endif
|
|
else
|
|
message('Python not found (python3/python); skipping Conan cache probe for ONNX Runtime DLLs')
|
|
endif
|
|
|
|
foreach bindir : onnx_bindir_candidates
|
|
foreach dll_name : ['onnxruntime.dll', 'onnxruntime_providers_shared.dll']
|
|
dll_path = bindir / dll_name
|
|
if fs.exists(dll_path)
|
|
windows_runtime_dlls += [[dll_name, dll_path]]
|
|
endif
|
|
endforeach
|
|
endforeach
|
|
|
|
# Deduplicate DLL entries by name to avoid duplicate targets
|
|
dedup_map = {}
|
|
deduped = []
|
|
foreach dll_info : windows_runtime_dlls
|
|
name = dll_info[0]
|
|
if not dedup_map.has_key(name)
|
|
dedup_map += {name: true}
|
|
deduped += [dll_info]
|
|
endif
|
|
endforeach
|
|
windows_runtime_dlls = deduped
|
|
|
|
# Log discovered DLLs
|
|
foreach dll_info : windows_runtime_dlls
|
|
message('Found runtime DLL: ' + dll_info[0] + ' at ' + dll_info[1])
|
|
endforeach
|
|
endif
|
|
|
|
if not wasi_enabled
|
|
# Database backend selection (libsql or sqlite)
|
|
db_backend = get_option('database-backend')
|
|
require_fts5 = get_option('require-fts5')
|
|
|
|
# Try to find the selected database backend
|
|
using_libsql = false
|
|
if db_backend == 'libsql'
|
|
# Try to find libsql (provides sqlite3-compatible API)
|
|
# 1. First try system-installed libsql via pkg-config
|
|
libsql_dep = dependency('libsql', required: false)
|
|
if not libsql_dep.found()
|
|
libsql_dep = dependency('libsql-sqlite3', required: false)
|
|
endif
|
|
|
|
# 2. If not found, try building from subproject (requires Rust toolchain)
|
|
if not libsql_dep.found()
|
|
cargo_prog = find_program('cargo', required: false)
|
|
if cargo_prog.found()
|
|
message('libSQL not found on system, building from subproject (requires Rust)...')
|
|
libsql_subproj = subproject('libsql', required: false)
|
|
if libsql_subproj.found()
|
|
libsql_dep = libsql_subproj.get_variable('libsql_dep')
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
if libsql_dep.found()
|
|
sqlite_dep = libsql_dep
|
|
using_libsql = true
|
|
message('Using libSQL database backend (MVCC concurrent writes enabled)')
|
|
else
|
|
warning('libSQL not found and subproject build failed. Falling back to standard SQLite3.')
|
|
warning('To use libSQL: install Rust toolchain (rustup) or system libsql package.')
|
|
sqlite_dep = dependency('sqlite3', required: true)
|
|
endif
|
|
else
|
|
sqlite_dep = dependency('sqlite3', required: true)
|
|
message('Using standard SQLite3 database backend')
|
|
endif
|
|
|
|
# Add compile definition for backend detection
|
|
if using_libsql
|
|
add_project_arguments('-DYAMS_LIBSQL_BACKEND=1', language: 'cpp')
|
|
else
|
|
add_project_arguments('-DYAMS_LIBSQL_BACKEND=0', language: 'cpp')
|
|
endif
|
|
|
|
# Skip runtime test when cross-compiling or using subproject sqlite (internal dep can't be used with cpp.run in newer meson)
|
|
if meson.is_cross_build()
|
|
warning('Cross-compiling: skipping SQLite FTS5 runtime check. Ensure FTS5 is enabled in your target SQLite build.')
|
|
elif sqlite_dep.type_name() == 'internal'
|
|
warning('Using subproject sqlite3: skipping FTS5 runtime check. Subproject sqlite3 has FTS5 enabled by default.')
|
|
elif using_libsql
|
|
message('Using libSQL: skipping FTS5 runtime check. libSQL includes FTS5 support.')
|
|
else
|
|
fts5_prog = cpp.run(files('meson/fts5_check.cpp'), dependencies: sqlite_dep, name: 'sqlite3 FTS5 check')
|
|
if fts5_prog.returncode() != 0
|
|
msg = 'SQLite3 built without FTS5 support. Enable FTS5 in your package manager or Conan (e.g., -o "sqlite3/*:fts5=True").'
|
|
if require_fts5
|
|
error(msg)
|
|
else
|
|
warning(msg)
|
|
endif
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
# Boost is a core requirement (e.g., Boost.Asio for daemon communication).
|
|
# Conan's PkgConfigDeps often doesn't emit boost.pc, so prefer CMake config.
|
|
boost_dep = dependency('boost', modules: ['system', 'thread'], method: 'cmake', required: true)
|
|
meson.override_dependency('boost', boost_dep)
|
|
|
|
# --- RE2 regex engine (optional, for high-performance grep) ---
|
|
re2_opt = get_option('enable-re2')
|
|
re2_dep = dependency('re2', required: re2_opt)
|
|
if re2_dep.found()
|
|
add_project_arguments('-DYAMS_HAS_RE2=1', language: 'cpp')
|
|
message('RE2 regex engine enabled (grep will use RE2 with std::regex fallback)')
|
|
else
|
|
add_project_arguments('-DYAMS_HAS_RE2=0', language: 'cpp')
|
|
message('RE2 not found; grep will use std::regex only')
|
|
endif
|
|
|
|
# --- Version header generation ---
|
|
_override_version = get_option('yams-version')
|
|
|
|
# Detect version from git tags if not overridden
|
|
_git_version = ''
|
|
_git_cmd = find_program('git', required: false)
|
|
if _git_cmd.found() and _override_version == ''
|
|
# Try to get the most recent semver tag (v*)
|
|
_r = run_command(_git_cmd, ['describe', '--tags', '--match', 'v*', '--abbrev=0'], check: false)
|
|
if _r.returncode() == 0
|
|
_tag = _r.stdout().strip()
|
|
# Strip leading 'v' if present
|
|
if _tag.startswith('v')
|
|
_git_version = _tag.substring(1)
|
|
else
|
|
_git_version = _tag
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
# Priority: command-line override > git tag > meson.project_version()
|
|
if _override_version != ''
|
|
_effective_version = _override_version
|
|
elif _git_version != ''
|
|
_effective_version = _git_version
|
|
else
|
|
_effective_version = meson.project_version()
|
|
endif
|
|
|
|
python_exe = find_program('python3', 'python')
|
|
_build_timestamp = run_command(python_exe, '-c', 'import datetime; print(datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"))', check: true).stdout().strip()
|
|
|
|
_git_desc = ''
|
|
_git_commit = ''
|
|
_git_cmd = find_program('git', required: false)
|
|
if _git_cmd.found()
|
|
# Capture short git describe (best-effort; ignore errors quietly)
|
|
_r = run_command(_git_cmd, ['describe', '--tags', '--always', '--dirty'], check: false)
|
|
if _r.returncode() == 0
|
|
_git_desc = _r.stdout().strip()
|
|
endif
|
|
# Capture short commit hash for dev builds
|
|
_r = run_command(_git_cmd, ['rev-parse', '--short', 'HEAD'], check: false)
|
|
if _r.returncode() == 0
|
|
_git_commit = _r.stdout().strip()
|
|
endif
|
|
endif
|
|
|
|
version_config = configuration_data()
|
|
version_config.set('YAMS_EFFECTIVE_VERSION', _effective_version)
|
|
version_config.set('YAMS_PROJECT_VERSION', meson.project_version())
|
|
version_config.set('YAMS_OVERRIDE_VERSION', _override_version)
|
|
version_config.set('YAMS_BUILD_TIMESTAMP', _build_timestamp)
|
|
version_config.set('YAMS_GIT_DESCRIBE', _git_desc)
|
|
version_config.set('YAMS_GIT_COMMIT', _git_commit)
|
|
|
|
# Generate version header - will be at builddir/version_generated.h
|
|
# CLI includes it via builddir in include path
|
|
version_header = configure_file(
|
|
output: 'version_generated.h',
|
|
install: true,
|
|
install_dir: get_option('includedir') / 'yams',
|
|
configuration: version_config,
|
|
# Header template uses @VAR@ tokens.
|
|
format: 'meson',
|
|
input: files('include/yams/version_generated.h.in')
|
|
)
|
|
|
|
# Make builddir available as include path for generated headers
|
|
# CLI will include as "version_generated.h" directly
|
|
generated_inc = include_directories('.')
|
|
|
|
yams_build_cli = get_option('build-cli') and not wasi_enabled
|
|
yams_build_mcp = get_option('build-mcp-server')
|
|
|
|
# Install public headers under the compiler's include prefix without duplicating
|
|
# the top-level 'include' directory (was producing prefix/include/include/...).
|
|
install_subdir('include', install_dir: get_option('includedir'), strip_directory: true)
|
|
|
|
# --- tl-expected global resolution ---
|
|
# tl-expected is a header-only library. Homebrew installs it without pkgconfig.
|
|
# Resolve it once here and override for all subdirs.
|
|
_tl_expected_dep = dependency('tl-expected', method: 'cmake', required: false)
|
|
if not _tl_expected_dep.found()
|
|
_tl_expected_dep = dependency('tl-expected', method: 'pkg-config', required: false)
|
|
endif
|
|
if not _tl_expected_dep.found()
|
|
# Manual fallback for homebrew installation
|
|
_tl_expected_inc = include_directories('/opt/homebrew/include', is_system: true)
|
|
_tl_expected_dep = declare_dependency(include_directories: _tl_expected_inc)
|
|
endif
|
|
meson.override_dependency('tl-expected', _tl_expected_dep)
|
|
|
|
if wasi_enabled
|
|
# WASI profile: compile a minimal subset (no daemon IPC, no plugins, no downloads).
|
|
# Keep this intentionally small so cross-compilers don't need system libs like
|
|
# OpenSSL/libcurl/sqlite/onnx.
|
|
subdir('src/core')
|
|
|
|
if get_option('build-mcp-server')
|
|
subdir('src/mcp')
|
|
endif
|
|
else
|
|
# Core libraries (ordered by dependency)
|
|
subdir('src/core')
|
|
subdir('src/crypto')
|
|
subdir('src/chunking')
|
|
subdir('src/compression')
|
|
subdir('src/storage')
|
|
subdir('src/wal')
|
|
subdir('src/manifest')
|
|
subdir('src/metadata')
|
|
subdir('src/integrity')
|
|
subdir('src/config')
|
|
subdir('src/extraction')
|
|
subdir('src/detection')
|
|
subdir('src/content')
|
|
subdir('src/downloader')
|
|
if get_option('build-plugins')
|
|
subdir('src/plugins')
|
|
endif
|
|
subdir('src/indexing')
|
|
subdir('src/genai')
|
|
subdir('src/daemon/client')
|
|
subdir('src/vector')
|
|
|
|
# Third-party libraries (must come before src/search which depends on symspell)
|
|
subdir('third_party/symspell')
|
|
|
|
subdir('src/search')
|
|
subdir('src/api')
|
|
subdir('src/ingest')
|
|
subdir('src/repair')
|
|
subdir('src/app/services')
|
|
subdir('src/daemon')
|
|
if get_option('enable-mobile-bindings')
|
|
subdir('src/mobile')
|
|
endif
|
|
if get_option('build-benchmarks')
|
|
subdir('src/benchmarks')
|
|
endif
|
|
|
|
if get_option('build-mcp-server')
|
|
subdir('src/mcp')
|
|
endif
|
|
endif
|
|
|
|
if get_option('build-plugins') and not wasi_enabled
|
|
subdir('plugins')
|
|
endif
|
|
|
|
if get_option('build-cli') and not wasi_enabled
|
|
subdir('src/cli')
|
|
subdir('tools/yams-cli')
|
|
if get_option('build-mcp-server')
|
|
subdir('tools/yams-mcp')
|
|
endif
|
|
endif
|
|
|
|
# Tests (enabled by default in Debug builds)
|
|
_tests_opt = get_option('build-tests')
|
|
_buildtype = get_option('buildtype')
|
|
_tests_enabled = (not wasi_enabled) and (_tests_opt or (_buildtype == 'debug' and not get_option('build-fuzzers')))
|
|
if _tests_enabled
|
|
subdir('tests')
|
|
endif
|
|
|
|
# Fuzzing harnesses (AFL++/libFuzzer)
|
|
if get_option('build-fuzzers') and not wasi_enabled
|
|
subdir('tools/fuzzing')
|
|
endif
|
|
|
|
sql_install_dir = join_paths(get_option('datadir'), 'yams', 'sql')
|
|
install_data('sql/reference_schema.sql', install_dir: sql_install_dir)
|
|
|
|
summary({
|
|
'project': meson.project_name(),
|
|
'version': meson.project_version(),
|
|
'cpp_std': get_option('cpp_std'),
|
|
'build_cli': yams_build_cli,
|
|
'build_mcp_server': yams_build_mcp,
|
|
}, section: 'YAMS Meson configuration', bool_yn: true)
|
|
|
|
if get_option('b_coverage')
|
|
gcovr = find_program('gcovr', required: false)
|
|
if gcovr.found()
|
|
_cpp_compiler = meson.get_compiler('cpp')
|
|
_gcov_executable = 'gcov'
|
|
if _cpp_compiler.get_id() == 'clang'
|
|
_llvm_cov = find_program('llvm-cov', required: false)
|
|
if _llvm_cov.found()
|
|
_gcov_executable = 'llvm-cov gcov -p'
|
|
endif
|
|
endif
|
|
|
|
coverage_report_target = custom_target('coverage-report',
|
|
output: 'coverage-report.stamp',
|
|
command: [
|
|
gcovr,
|
|
'--root', meson.project_source_root(),
|
|
'--gcov-executable', _gcov_executable,
|
|
'--object-directory', meson.project_build_root(),
|
|
'--filter', join_paths(meson.project_source_root(), 'src'),
|
|
'--filter', join_paths(meson.project_source_root(), 'include'),
|
|
'--exclude', join_paths(meson.project_source_root(), 'src', 'benchmarks'),
|
|
'--html', '--html-details',
|
|
'--output', join_paths(meson.project_build_root(), 'coverage', 'index.html'),
|
|
meson.project_build_root(),
|
|
],
|
|
build_by_default: false,
|
|
build_always_stale: true,
|
|
)
|
|
|
|
coverage_txt_target = custom_target('coverage-summary',
|
|
output: 'coverage.txt',
|
|
command: [
|
|
gcovr,
|
|
'--root', meson.project_source_root(),
|
|
'--gcov-executable', _gcov_executable,
|
|
'--object-directory', meson.project_build_root(),
|
|
'--filter', join_paths(meson.project_source_root(), 'src'),
|
|
'--filter', join_paths(meson.project_source_root(), 'include'),
|
|
'--exclude', join_paths(meson.project_source_root(), 'src', 'benchmarks'),
|
|
'--print-summary',
|
|
'--output', '@OUTPUT@',
|
|
meson.project_build_root(),
|
|
],
|
|
build_by_default: false,
|
|
build_always_stale: true,
|
|
)
|
|
|
|
message('Coverage reporting configured. Run tests then: ninja -C builddir coverage.txt && ninja -C builddir coverage-report.stamp')
|
|
else
|
|
warning('gcovr not found. Install gcovr to generate coverage reports.')
|
|
endif
|
|
endif
|
|
|
|
# Note: Runtime DLLs are now deployed via Conan's runtime_deploy deployer during conan install
|
|
# No custom meson install script needed
|