yams/setup.sh

429 lines
14 KiB
Bash
Executable file

#!/bin/bash
# Unified build script for YAMS
#
# Usage: ./setup.sh [Debug|Release|Profiling|Fuzzing] [--coverage]
# build_type: Release (default), Debug, Profiling, or Fuzzing (TODO)
# --coverage: Enable code coverage instrumentation (Debug builds only)
#
# Environment variables (for CI/advanced use):
# YAMS_CONAN_HOST_PROFILE - Path to Conan host profile (bypasses auto-detection)
# YAMS_CONAN_ARCH - Target architecture (x86_64, armv8, etc.)
# YAMS_EXTRA_MESON_FLAGS - Additional Meson setup flags
# YAMS_COMPILER - Force compiler (clang or gcc)
# YAMS_CPPSTD - C++ standard (17, 20, 23)
# YAMS_LIBCXX_HARDENING - libc++ hardening mode (none, fast, extensive, debug)
# YAMS_INSTALL_PREFIX - Installation prefix (default: /usr/local or Homebrew)
#
# The script prefers Clang when available, falling back to GCC otherwise. It
# ensures Conan is given a concrete C++ standard so dependencies resolve cleanly
# and keeps Meson in sync with the generated toolchain file.
set -euo pipefail
ENABLE_COVERAGE=false
BUILD_TYPE_INPUT=""
while [[ $# -gt 0 ]]; do
case "$1" in
--coverage)
ENABLE_COVERAGE=true
shift
;;
Debug|Release|Profiling|Fuzzing|debug|release|profiling|fuzzing)
if [[ -n "${BUILD_TYPE_INPUT}" ]]; then
echo "Error: Build type specified multiple times" >&2
exit 1
fi
BUILD_TYPE_INPUT="$1"
shift
;;
*)
echo "Usage: $0 [Debug|Release|Profiling|Fuzzing] [--coverage]" >&2
exit 1
;;
esac
done
BUILD_TYPE_INPUT=${BUILD_TYPE_INPUT:-Release}
BUILD_TYPE_LOWER=$(echo "${BUILD_TYPE_INPUT}" | tr '[:upper:]' '[:lower:]')
case "${BUILD_TYPE_LOWER}" in
debug)
BUILD_TYPE="Debug"
;;
release)
BUILD_TYPE="Release"
;;
profiling)
BUILD_TYPE="Debug" # Use Debug as base for Conan/Meson
ENABLE_PROFILING=true
;;
fuzzing)
BUILD_TYPE="Debug" # Use Debug as base for Conan/Meson
ENABLE_FUZZING=true
echo "Fuzzing build: AFL++/libFuzzer instrumentation enabled"
;;
*)
echo "Unknown build type: ${BUILD_TYPE_INPUT}. Expected Debug, Release, Profiling, or Fuzzing." >&2
exit 1
;;
esac
if [[ "${ENABLE_COVERAGE}" == "true" ]] && [[ "${BUILD_TYPE}" != "Debug" ]]; then
echo "Error: --coverage flag requires Debug build type" >&2
exit 1
fi
# Select desired C++ standard (defaults to C++23). Override with YAMS_CPPSTD=20/23.
CPPSTD_INPUT=${YAMS_CPPSTD:-23}
case "${CPPSTD_INPUT}" in
17|20|23)
CPPSTD="${CPPSTD_INPUT}"
;;
c++17|c++20|c++23)
CPPSTD="${CPPSTD_INPUT#c++}"
;;
*)
echo "Unknown C++ standard: ${CPPSTD_INPUT}. Expected 17|20|23 or c++17|c++20|c++23." >&2
exit 1
;;
esac
# Value for Meson project option
MESON_CPPSTD="c++${CPPSTD}"
# libc++ hardening mode (only applies when using libc++)
# Options: none, fast, extensive, debug
# Override with YAMS_LIBCXX_HARDENING=fast|extensive|debug|none
LIBCXX_HARDENING=${YAMS_LIBCXX_HARDENING:-none}
# Check if using explicit Conan profile (CI mode)
CONAN_HOST_PROFILE=${YAMS_CONAN_HOST_PROFILE:-}
CONAN_ARCH=${YAMS_CONAN_ARCH:-}
CONAN_EXTRA_OPTIONS=${YAMS_CONAN_EXTRA_OPTIONS:-}
USE_PROFILE=false
if [[ -n "${CONAN_HOST_PROFILE}" ]]; then
if [[ ! -f "${CONAN_HOST_PROFILE}" ]]; then
echo "ERROR: Conan host profile not found: ${CONAN_HOST_PROFILE}" >&2
exit 1
fi
USE_PROFILE=true
echo "Using explicit Conan host profile: ${CONAN_HOST_PROFILE}"
fi
CONAN_ARGS=(-s "build_type=${BUILD_TYPE}" -b missing --update)
# Add common Conan options (sqlite3 with FTS5, etc.)
CONAN_ARGS+=(-o "sqlite3/*:fts5=True")
# Fuzzing builds: disable programs in zstd to avoid symlink issues in Docker
if [[ "${ENABLE_FUZZING:-false}" == "true" ]]; then
CONAN_ARGS+=(-o "zstd/*:build_programs=False")
fi
# Add extra Conan options from environment (for CI)
if [[ -n "${CONAN_EXTRA_OPTIONS}" ]]; then
# shellcheck disable=SC2086
read -ra EXTRA_OPTS <<< "${CONAN_EXTRA_OPTIONS}"
CONAN_ARGS+=("${EXTRA_OPTS[@]}")
echo "Extra Conan options: ${CONAN_EXTRA_OPTIONS}"
fi
detect_version() {
local bin="$1"
if command -v "${bin}" >/dev/null 2>&1; then
if "${bin}" -dumpfullversion >/dev/null 2>&1; then
"${bin}" -dumpfullversion
elif "${bin}" -dumpversion >/dev/null 2>&1; then
"${bin}" -dumpversion
else
"${bin}" --version | head -n1 | grep -oE '[0-9]+(\.[0-9]+)*' | head -n1
fi
fi
}
COERCE_MAJOR() {
echo "$1" | cut -d. -f1
}
COMPILER_OVERRIDE=${YAMS_COMPILER:-}
# When using explicit profile, skip auto-detection of compiler settings
if [[ "${USE_PROFILE}" == "true" ]]; then
echo "Skipping compiler auto-detection (using profile: ${CONAN_HOST_PROFILE})"
# Still set CC/CXX for Meson if not already set
if [[ -z "${CC:-}" ]] && command -v clang >/dev/null 2>&1; then
export CC="clang"
export CXX="clang++"
elif [[ -z "${CC:-}" ]] && command -v gcc >/dev/null 2>&1; then
export CC="gcc"
export CXX="g++"
fi
# Add profile to Conan args
CONAN_ARGS+=(-pr:h "${CONAN_HOST_PROFILE}" -pr:b default)
# Add architecture if specified
if [[ -n "${CONAN_ARCH}" ]]; then
CONAN_ARGS+=(-s:h "arch=${CONAN_ARCH}")
echo "Target architecture: ${CONAN_ARCH}"
fi
else
# Auto-detection mode (original behavior)
if [[ "${COMPILER_OVERRIDE}" == clang ]] || { [[ -z "${COMPILER_OVERRIDE}" ]] && command -v clang >/dev/null 2>&1 && command -v clang++ >/dev/null 2>&1; }; then
echo "--- Using Clang toolchain ---"
export CC="clang"
export CXX="clang++"
# macOS requires libc++, Linux can use libstdc++11
if [[ "$(uname -s)" == "Darwin" ]]; then
LIBCXX="libc++"
else
LIBCXX="libstdc++11"
fi
CLANG_VERSION=$(detect_version clang++)
CLANG_MAJOR=$(COERCE_MAJOR "${CLANG_VERSION:-0}")
if [[ -z "${CLANG_MAJOR}" || "${CLANG_MAJOR}" == 0 ]]; then
echo "Unable to detect clang version." >&2
exit 1
fi
if [[ "$(uname -s)" == "Darwin" ]]; then
# On macOS use Conan's apple-clang compiler model
CONAN_ARGS+=(
-s "compiler=apple-clang"
-s "compiler.version=${CLANG_MAJOR}"
-s "compiler.libcxx=${LIBCXX}"
-s "compiler.cppstd=${CPPSTD}"
)
else
CONAN_ARGS+=(
-s "compiler=clang"
-s "compiler.version=${CLANG_MAJOR}"
-s "compiler.libcxx=${LIBCXX}"
-s "compiler.cppstd=${CPPSTD}"
)
fi
else
echo "--- Using GCC toolchain ---"
export CC="gcc"
export CXX="g++"
if ! command -v g++ >/dev/null 2>&1; then
echo "g++ not found in PATH." >&2
exit 1
fi
GCC_VERSION=$(detect_version g++)
GCC_MAJOR=$(COERCE_MAJOR "${GCC_VERSION:-0}")
if [[ -z "${GCC_MAJOR}" || "${GCC_MAJOR}" == 0 ]]; then
echo "Unable to detect gcc version." >&2
exit 1
fi
CONAN_ARGS+=(
-s "compiler=gcc"
-s "compiler.version=${GCC_MAJOR}"
-s "compiler.libcxx=libstdc++11"
-s "compiler.cppstd=${CPPSTD}"
)
fi
fi
if [[ "${ENABLE_PROFILING:-false}" == "true" ]]; then
BUILD_DIR="build/profiling"
CONAN_SUBDIR="build-profiling"
elif [[ "${ENABLE_FUZZING:-false}" == "true" ]]; then
BUILD_DIR="build/fuzzing"
CONAN_SUBDIR="build-fuzzing"
elif [[ "${BUILD_TYPE}" == "Debug" ]]; then
BUILD_DIR="builddir"
CONAN_SUBDIR="build-debug"
else
BUILD_DIR="build/${BUILD_TYPE_LOWER}"
CONAN_SUBDIR="build-${BUILD_TYPE_LOWER}"
fi
# Detect install prefix based on platform
if [[ "$(uname -s)" == "Darwin" ]]; then
# macOS: check for Homebrew installation
if command -v brew >/dev/null 2>&1; then
BREW_PREFIX=$(brew --prefix 2>/dev/null || echo "/usr/local")
INSTALL_PREFIX="${BREW_PREFIX}"
else
INSTALL_PREFIX="/usr/local"
fi
else
# Linux: use standard prefix
INSTALL_PREFIX="/usr/local"
fi
# Allow override via environment
INSTALL_PREFIX="${YAMS_INSTALL_PREFIX:-${INSTALL_PREFIX}}"
echo "Build Type: ${BUILD_TYPE_INPUT}"
if [[ "${ENABLE_PROFILING:-false}" == "true" ]]; then
echo "Profiling: Tracy enabled"
fi
if [[ "${ENABLE_FUZZING:-false}" == "true" ]]; then
echo "Fuzzing: AFL++/libFuzzer enabled"
fi
echo "Build Dir: ${BUILD_DIR}"
echo "Install Prefix: ${INSTALL_PREFIX}"
echo "C++ Std: ${MESON_CPPSTD} (Conan: ${CPPSTD})"
if [[ "${LIBCXX_HARDENING}" != "none" ]]; then
echo "libc++ Hardening: ${LIBCXX_HARDENING}"
fi
echo "--- Exporting custom Conan recipes... ---"
# Export custom qpdf recipe if it exists
if [[ -f "conan/qpdf/conanfile.py" ]]; then
echo "Exporting qpdf/11.9.0 from conan/qpdf/"
conan export conan/qpdf --name=qpdf --version=11.9.0
fi
# Export custom onnxruntime recipe if it exists
if [[ -f "conan/onnxruntime/conanfile.py" ]]; then
echo "Exporting onnxruntime/1.23.2 from conan/onnxruntime/"
conan export conan/onnxruntime --name=onnxruntime --version=1.23.2
fi
echo "--- Running conan install... ---"
# Add policy toolchain for legacy recipes if in Docker or CI
POLICY_TC=""
if [[ -n "${DOCKERFILE_CONF_REV:-}" ]] || [[ -n "${CI:-}" ]]; then
POLICY_TC="/tmp/yams_policy_toolchain.cmake"
echo 'cmake_policy(VERSION 3.5)' > "$POLICY_TC"
CONAN_ARGS+=(-c "tools.cmake.cmaketoolchain:user_toolchain+=${POLICY_TC}")
fi
# Enable tests for Debug builds in Conan (needed for Catch2/gtest dependencies)
if [[ "${BUILD_TYPE}" == "Debug" ]] || [[ "${ENABLE_PROFILING:-false}" == "true" ]] || [[ "${ENABLE_FUZZING:-false}" == "true" ]]; then
CONAN_ARGS+=(-o build_tests=True)
fi
# Enable Tracy profiling for profiling builds
if [[ "${ENABLE_PROFILING:-false}" == "true" ]]; then
echo "Tracy profiling enabled"
CONAN_ARGS+=(-o "tracy/*:enable=True")
fi
# Handle optional feature flags from environment
if [[ "${YAMS_DISABLE_ONNX:-}" == "true" ]]; then
echo "ONNX support disabled (YAMS_DISABLE_ONNX=true)"
CONAN_ARGS+=(-o "yams/*:enable_onnx=False")
fi
if [[ "${YAMS_DISABLE_SYMBOL_EXTRACTION:-}" == "true" ]]; then
echo "Symbol extraction disabled (YAMS_DISABLE_SYMBOL_EXTRACTION=true)"
CONAN_ARGS+=(-o "yams/*:enable_symbol_extraction=False")
fi
if [[ "${YAMS_DISABLE_PDF:-}" == "true" ]]; then
echo "PDF support disabled (YAMS_DISABLE_PDF=true)"
CONAN_ARGS+=(-o "yams/*:enable_pdf=False")
fi
# Force building missing packages to ensure ABI compatibility
# This is especially important for C++23 with Clang + libstdc++
CONAN_ARGS+=(--build=missing)
conan install . -of "${BUILD_DIR}" "${CONAN_ARGS[@]}"
# Check for either native or cross file (Conan generates cross file for cross-compilation)
NATIVE_FILE="${BUILD_DIR}/${CONAN_SUBDIR}/conan/conan_meson_native.ini"
CROSS_FILE="${BUILD_DIR}/${CONAN_SUBDIR}/conan/conan_meson_cross.ini"
if [[ -f "${NATIVE_FILE}" ]]; then
MESON_TOOLCHAIN_ARG="--native-file"
MESON_TOOLCHAIN_FILE="${NATIVE_FILE}"
elif [[ -f "${CROSS_FILE}" ]]; then
MESON_TOOLCHAIN_ARG="--cross-file"
MESON_TOOLCHAIN_FILE="${CROSS_FILE}"
else
echo "Error: Conan meson toolchain file not found" >&2
echo "Expected either: ${NATIVE_FILE}" >&2
echo " or: ${CROSS_FILE}" >&2
echo "Please check the output path from 'conan install'." >&2
exit 1
fi
MESON_ARGS=(
"${BUILD_DIR}"
"--prefix" "${INSTALL_PREFIX}"
"${MESON_TOOLCHAIN_ARG}" "${MESON_TOOLCHAIN_FILE}"
"--buildtype" "${BUILD_TYPE_LOWER}"
)
# Detect previous configured cpp_std to decide on reconfigure vs wipe
PREV_CPPSTD=""
INTRO_OPTS_JSON="${BUILD_DIR}/meson-info/intro-buildoptions.json"
if [[ -f "${INTRO_OPTS_JSON}" ]]; then
PREV_CPPSTD=$(awk '/"name"\s*:\s*"cpp_std"/{flag=1} flag && /"value"/{gsub(/.*"value"\s*:\s*"|".*/,"",$0); print; exit}' "${INTRO_OPTS_JSON}" || true)
fi
MESON_OPTIONS=("-Dbuild-cli=true" "-Dcpp_std=${MESON_CPPSTD}")
# Add libc++ hardening mode if specified
if [[ "${LIBCXX_HARDENING}" != "none" ]]; then
MESON_OPTIONS+=("-Dlibcxx-hardening=${LIBCXX_HARDENING}")
fi
# Handle optional feature flags for Meson (must match Conan options)
if [[ "${YAMS_DISABLE_ONNX:-}" == "true" ]]; then
MESON_OPTIONS+=("-Denable-onnx=disabled")
MESON_OPTIONS+=("-Dplugin-onnx=false")
fi
if [[ "${YAMS_DISABLE_SYMBOL_EXTRACTION:-}" == "true" ]]; then
MESON_OPTIONS+=("-Denable-symbol-extraction=false")
MESON_OPTIONS+=("-Dplugin-symbols=false")
fi
if [[ "${YAMS_DISABLE_PDF:-}" == "true" ]]; then
MESON_OPTIONS+=("-Dplugin-pdf=false")
fi
if [[ "${BUILD_TYPE}" == "Debug" ]] || [[ "${ENABLE_PROFILING:-false}" == "true" ]] || [[ "${ENABLE_FUZZING:-false}" == "true" ]]; then
MESON_OPTIONS+=(
"-Dbuild-tests=true"
"-Denable-bench-tests=true"
)
fi
if [[ "${ENABLE_PROFILING:-false}" == "true" ]]; then
MESON_OPTIONS+=(
"-Denable-profiling=true"
)
echo "Tracy profiling enabled for Meson build"
fi
if [[ "${ENABLE_FUZZING:-false}" == "true" ]]; then
MESON_OPTIONS+=(
"-Dbuild-fuzzers=true"
)
echo "Fuzzing targets enabled for Meson build"
fi
if [[ "${ENABLE_COVERAGE}" == "true" ]]; then
MESON_OPTIONS+=("-Db_coverage=true")
echo "Coverage instrumentation enabled"
fi
# Add extra Meson flags from environment (for CI)
if [[ -n "${YAMS_EXTRA_MESON_FLAGS:-}" ]]; then
# shellcheck disable=SC2086
read -ra EXTRA_FLAGS <<< "${YAMS_EXTRA_MESON_FLAGS}"
MESON_OPTIONS+=("${EXTRA_FLAGS[@]}")
echo "Extra Meson flags: ${YAMS_EXTRA_MESON_FLAGS}"
fi
echo "--- Running meson setup... ---"
if [[ -n "${PREV_CPPSTD}" ]]; then
if [[ "${PREV_CPPSTD}" != "${MESON_CPPSTD}" ]]; then
echo "cpp_std changed (${PREV_CPPSTD} -> ${MESON_CPPSTD}); wiping build directory configuration..."
meson setup "${MESON_ARGS[@]}" "${MESON_OPTIONS[@]}" --wipe
else
meson setup "${MESON_ARGS[@]}" "${MESON_OPTIONS[@]}" --reconfigure
fi
else
meson setup "${MESON_ARGS[@]}" "${MESON_OPTIONS[@]}"
fi
echo
echo "--- Setup complete! ---"
echo "To compile, run: meson compile -C ${BUILD_DIR}"