#!/bin/bash set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Get script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$SCRIPT_DIR" # Build directories CPP_DIR="$PROJECT_ROOT/internal/cpp" BUILD_DIR="$CPP_DIR/cmake-build-release" RAGFLOW_SERVER_BINARY="$PROJECT_ROOT/bin/ragflow_server" ADMIN_SERVER_BINARY="$PROJECT_ROOT/bin/admin_server" INGESTOR_BINARY="$PROJECT_ROOT/bin/ingestor" RAGFLOW_CLI_BINARY="$PROJECT_ROOT/bin/ragflow-cli" # Strip symbols from Go binaries (set via --strip / -s) STRIP_SYMBOLS="" # office_oxide native library settings OFFICE_OXIDE_PREFIX="${HOME}/.office_oxide" OFFICE_OXIDE_VERSION="0.1.2" # pdfium native library settings (from pypdfium2_raw PyPI wheel) PDFIUM_PREFIX="${HOME}/.pdfium" PDFIUM_VERSION="0.5.0" # pdf_oxide native library settings (from GitHub Release) PDF_OXIDE_PREFIX="${HOME}/.pdf_oxide" PDF_OXIDE_VERSION="0.3.63" echo -e "${GREEN}=== RAGFlow Go Server Build Script ===${NC}" # Function to print section headers print_section() { echo -e "\n${YELLOW}>>> $1${NC}" } # Detect the package-install command for pcre2 development files. # Outputs the command on stdout; empty string if no supported manager is found. detect_pcre2_install_cmd() { if [ "$(uname)" = "Darwin" ]; then echo "brew install pcre2" elif command -v apt-get >/dev/null 2>&1; then echo "sudo apt-get install -y libpcre2-dev" elif command -v zypper >/dev/null 2>&1; then echo "sudo zypper install -y pcre2-devel" elif command -v dnf >/dev/null 2>&1; then echo "sudo dnf install -y pcre2-devel" elif command -v pacman >/dev/null 2>&1; then echo "sudo pacman -S --noconfirm pcre2" else echo "" fi } # Check whether libpcre2-8 is available (static or shared). check_pcre2() { # Prefer pkg-config when available — works across distros. if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists libpcre2-8; then return 0 fi # Fall back to known library paths: # Debian/Ubuntu -> /usr/lib/x86_64-linux-gnu # openSUSE/RHEL -> /usr/lib64 # generic Linux -> /usr/lib, /usr/local/lib # macOS Homebrew -> /opt/homebrew/lib (Apple Silicon), /usr/local/lib (Intel) for p in \ /usr/lib/x86_64-linux-gnu/libpcre2-8.a \ /usr/lib/x86_64-linux-gnu/libpcre2-8.so \ /usr/lib64/libpcre2-8.a \ /usr/lib64/libpcre2-8.so \ /usr/lib/libpcre2-8.a \ /usr/lib/libpcre2-8.so \ /usr/local/lib/libpcre2-8.a \ /usr/local/lib/libpcre2-8.so \ /usr/local/lib/libpcre2-8.dylib \ /opt/homebrew/lib/libpcre2-8.a \ /opt/homebrew/lib/libpcre2-8.dylib; do [ -f "$p" ] && return 0 done return 1 } # Check dependencies check_cpp_deps() { print_section "Checking c++ dependencies" command -v cmake >/dev/null 2>&1 || { echo -e "${RED}Error: cmake is required but not installed.${NC}"; exit 1; } command -v g++ >/dev/null 2>&1 || { echo -e "${RED}Error: g++ is required but not installed.${NC}"; exit 1; } if check_pcre2; then echo "✓ pcre2 library found" else install_cmd="$(detect_pcre2_install_cmd)" echo -e "${YELLOW}Warning: libpcre2-8 not found. You may need to install it:${NC}" if [ -n "$install_cmd" ]; then echo " $install_cmd" else echo " (No supported package manager detected — install pcre2 development files manually)" fi fi echo "✓ Required tools are available" } check_go_deps() { print_section "Checking go dependencies" command -v go >/dev/null 2>&1 || { echo -e "${RED}Error: go is required but not installed.${NC}"; exit 1; } echo "✓ Required tools are available" } # Download and extract a tar.gz from a URL to a target directory _download_and_extract() { local url="$1" target_dir="$2" echo "Downloading ${url} ..." local tmpfile tmpfile="$(mktemp)" if command -v curl >/dev/null 2>&1; then curl -fsSL "$url" -o "$tmpfile" elif command -v wget >/dev/null 2>&1; then wget -q "$url" -O "$tmpfile" else echo -e "${RED}Error: need curl or wget to download office_oxide${NC}" exit 1 fi tar xzf "$tmpfile" -C "$target_dir" rm -f "$tmpfile" } # Check / install office_oxide native library (Rust → C FFI library) check_office_oxide_deps() { print_section "Checking office_oxide native library" local lib_file header_path case "$(uname -s)" in Linux) lib_file="liboffice_oxide.so" ;; Darwin) lib_file="liboffice_oxide.dylib" ;; *) echo -e "${RED}Unsupported OS for office_oxide${NC}"; exit 1 ;; esac local lib_path="${OFFICE_OXIDE_PREFIX}/lib/${lib_file}" local header_path="${OFFICE_OXIDE_PREFIX}/include/office_oxide_c/office_oxide.h" if [ -f "$lib_path" ] && [ -f "$header_path" ]; then echo "✓ office_oxide native library found at ${OFFICE_OXIDE_PREFIX}" return 0 fi echo "office_oxide native library not found. Installing..." # Map platform to the release asset name. Note: the GitHub release archives # omit the version number from the native-* asset filenames. local asset_name case "$(uname -s)" in Linux) case "$(uname -m)" in x86_64) asset_name="native-linux-x86_64" ;; aarch64|arm64) asset_name="native-linux-aarch64" ;; *) echo -e "${RED}Unsupported arch: $(uname -m)${NC}"; exit 1 ;; esac ;; Darwin) case "$(uname -m)" in x86_64) asset_name="native-macos-x86_64" ;; aarch64|arm64) asset_name="native-macos-aarch64" ;; *) echo -e "${RED}Unsupported arch: $(uname -m)${NC}"; exit 1 ;; esac ;; esac local release_url="https://github.com/yfedoseev/office_oxide/releases/download/v${OFFICE_OXIDE_VERSION}/${asset_name}.tar.gz" mkdir -p "${OFFICE_OXIDE_PREFIX}" _download_and_extract "$release_url" "${OFFICE_OXIDE_PREFIX}" if [ ! -f "$lib_path" ]; then echo -e "${RED}Error: Failed to install office_oxide native library (missing ${lib_path})${NC}" echo " Try: curl -fsSL ${release_url} | tar xzf - -C ${OFFICE_OXIDE_PREFIX}" exit 1 fi echo -e "${GREEN}✓ office_oxide native library installed${NC}" } # Check / install pdfium native library (libpdfium.so from pypdfium2_raw wheel). check_pdfium_deps() { # 1. Check .venv (uv sync provides pypdfium2_raw). local venv_py="${PROJECT_ROOT}/.venv/bin/python3" if [ -x "$venv_py" ]; then local venv_so=$("$venv_py" -c "import pypdfium2_raw,os;print(os.path.join(os.path.dirname(pypdfium2_raw.__file__),'libpdfium.so'))" 2>/dev/null) if [ -n "$venv_so" ] && [ -f "$venv_so" ]; then echo " pdfium → ${venv_so} (.venv)" export CGO_LDFLAGS="$CGO_LDFLAGS -L$(dirname "$venv_so") -Wl,-rpath,$(dirname "$venv_so")" export LD_LIBRARY_PATH="$(dirname "$venv_so"):${LD_LIBRARY_PATH}" return 0 fi fi # 2. Check cache. local lib_path="${PDFIUM_PREFIX}/libpdfium.so" if [ -f "$lib_path" ]; then echo " pdfium → ${PDFIUM_PREFIX}" return 0 fi echo " pdfium not found, installing..." # 3. Map platform to PyPI wheel platform tag. local whl_platform case "$(uname -s)" in Linux) case "$(uname -m)" in x86_64) whl_platform="manylinux_2_17_x86_64.manylinux2014_x86_64" ;; aarch64|arm64) whl_platform="manylinux_2_17_aarch64.manylinux2014_aarch64" ;; *) echo " pdfium → unsupported arch"; return 1 ;; esac ;; Darwin) case "$(uname -m)" in x86_64) whl_platform="macosx_11_0_x86_64" ;; arm64) whl_platform="macosx_11_0_arm64" ;; *) echo " pdfium → unsupported arch"; return 1 ;; esac ;; *) echo " pdfium → unsupported OS"; return 1 ;; esac # 4. Download .whl from PyPI and extract libpdfium.so (zero pip dependency). local whl_url whl_url=$(curl -fsSL "https://pypi.org/pypi/pypdfium2_raw/${PDFIUM_VERSION}/json" 2>/dev/null \ | grep -o '"url":"[^"]*'${whl_platform}'[^"]*"' | head -1 | cut -d'"' -f4) if [ -n "$whl_url" ] && { command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1; }; then local tmp_whl="$(mktemp)" if command -v curl >/dev/null 2>&1; then curl -fsSL "$whl_url" -o "$tmp_whl" else wget -q "$whl_url" -O "$tmp_whl" fi mkdir -p "${PDFIUM_PREFIX}" # Wheel is a zip; extract libpdfium.so via python3 or unzip. if command -v python3 >/dev/null 2>&1; then python3 -c " import zipfile, os, shutil with zipfile.ZipFile('$tmp_whl') as z: for n in z.namelist(): if n.endswith('libpdfium.so'): z.extract(n, '${PDFIUM_PREFIX}') os.rename(os.path.join('${PDFIUM_PREFIX}', n), '$lib_path') # Remove empty pypdfium2_raw dir d = os.path.join('${PDFIUM_PREFIX}', 'pypdfium2_raw') if os.path.isdir(d): shutil.rmtree(d, ignore_errors=True) break " 2>/dev/null elif command -v unzip >/dev/null 2>&1; then unzip -q -o "$tmp_whl" -d "${PDFIUM_PREFIX}" 'pypdfium2_raw/libpdfium.so' 2>/dev/null [ -f "${PDFIUM_PREFIX}/pypdfium2_raw/libpdfium.so" ] && mv "${PDFIUM_PREFIX}/pypdfium2_raw/libpdfium.so" "$lib_path" rm -rf "${PDFIUM_PREFIX}/pypdfium2_raw" fi rm -f "$tmp_whl" fi if [ -f "$lib_path" ]; then echo -e "${GREEN}✓ pdfium installed to ${PDFIUM_PREFIX}${NC}" else echo " pdfium → install failed (requires .venv, curl/wget + python3, or pre-cached ~/.pdfium)" return 1 fi } # Check / install pdf_oxide native library (Rust -> C FFI library). check_pdf_oxide_deps() { local lib_path="${PDF_OXIDE_PREFIX}/libpdf_oxide.so" if [ -f "$lib_path" ]; then echo " pdf_oxide → ${PDF_OXIDE_PREFIX} (shared)" return 0 fi # Also check for static library (user's local installation). local static_path="${PDF_OXIDE_PREFIX}/libpdf_oxide.a" if [ -f "$static_path" ]; then echo " pdf_oxide → ${PDF_OXIDE_PREFIX} (static)" return 0 fi echo " pdf_oxide not found, installing..." # Map platform to the release asset name. local asset_name case "$(uname -s)" in Linux) case "$(uname -m)" in x86_64) asset_name="libpdf_oxide-v${PDF_OXIDE_VERSION}-linux-x86_64" ;; aarch64|arm64) asset_name="libpdf_oxide-v${PDF_OXIDE_VERSION}-linux-aarch64" ;; *) echo " pdf_oxide → unsupported arch"; return 1 ;; esac ;; Darwin) case "$(uname -m)" in x86_64) asset_name="libpdf_oxide-v${PDF_OXIDE_VERSION}-darwin-x86_64" ;; arm64) asset_name="libpdf_oxide-v${PDF_OXIDE_VERSION}-darwin-arm64" ;; *) echo " pdf_oxide → unsupported arch"; return 1 ;; esac ;; *) echo " pdf_oxide → unsupported OS"; return 1 ;; esac local release_url="https://github.com/yfedoseev/pdf_oxide/releases/download/v${PDF_OXIDE_VERSION}/${asset_name}.tar.gz" mkdir -p "${PDF_OXIDE_PREFIX}" _download_and_extract "$release_url" "${PDF_OXIDE_PREFIX}" if [ -f "$lib_path" ]; then echo -e "${GREEN}✓ pdf_oxide installed to ${PDF_OXIDE_PREFIX}${NC}" else echo " pdf_oxide → install failed" return 1 fi } # Build C++ static library build_cpp() { print_section "Building C++ static library" mkdir -p "$BUILD_DIR" cd "$BUILD_DIR" echo "Running cmake..." cmake .. -DCMAKE_BUILD_TYPE=Release echo "Building librag_tokenizer_c_api.a..." local jobs jobs="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1)" make rag_tokenizer_c_api -j"$jobs" if [ ! -f "$BUILD_DIR/librag_tokenizer_c_api.a" ]; then echo -e "${RED}Error: Failed to build C++ static library${NC}" exit 1 fi echo -e "${GREEN}✓ C++ static library built successfully${NC}" } # Build C++ test executable build_cpp_test() { print_section "Building C++ test executable" if [ ! -d "$BUILD_DIR" ]; then echo "Build directory not found, running cmake first..." mkdir -p "$BUILD_DIR" cd "$BUILD_DIR" cmake .. -DCMAKE_BUILD_TYPE=Release else cd "$BUILD_DIR" fi echo "Building rag_analyzer_c_test..." local jobs jobs="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1)" make rag_analyzer_c_test -j"$jobs" if [ ! -f "$BUILD_DIR/rag_analyzer_c_test" ]; then echo -e "${RED}Error: Failed to build rag_analyzer_c_test${NC}" exit 1 fi echo -e "${GREEN}✓ C++ test executable built successfully: $BUILD_DIR/rag_analyzer_c_test${NC}" } # Build Go server build_go() { print_section "Building RAGFlow go" cd "$PROJECT_ROOT" # Check if C++ library exists if [ ! -f "$BUILD_DIR/librag_tokenizer_c_api.a" ]; then echo -e "${RED}Error: C++ static library not found. Run with --cpp first.${NC}" exit 1 fi if check_pcre2; then echo "✓ pcre2 library found" else install_cmd="$(detect_pcre2_install_cmd)" if [ -z "$install_cmd" ]; then echo -e "${RED}Error: libpcre2-8 not found and no supported package manager detected.${NC}" echo "Please install pcre2 development files manually." exit 1 fi if [ "$(uname)" = "Darwin" ]; then echo -e "${RED}Error: libpcre2-8 not found. Install with: $install_cmd${NC}" exit 1 fi echo -e "${YELLOW}Warning: libpcre2-8 not found. Installing with: $install_cmd${NC}" eval "$install_cmd" fi setup_cgo_env local strip_flags=() [ -n "$STRIP_SYMBOLS" ] && strip_flags=(-ldflags="-s -w") echo "Building RAGFlow binary: $RAGFLOW_SERVER_BINARY, $ADMIN_SERVER_BINARY, $INGESTOR_BINARY, and $RAGFLOW_CLI_BINARY" GOPROXY=${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} CGO_ENABLED=1 \ CGO_CFLAGS="$CGO_CFLAGS" CGO_LDFLAGS="$CGO_LDFLAGS" \ go build "${strip_flags[@]}" -o "$RAGFLOW_SERVER_BINARY" cmd/server_main.go GOPROXY=${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} CGO_ENABLED=1 \ CGO_CFLAGS="$CGO_CFLAGS" CGO_LDFLAGS="$CGO_LDFLAGS" \ go build "${strip_flags[@]}" -o "$ADMIN_SERVER_BINARY" cmd/admin_server.go GOPROXY=${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} CGO_ENABLED=1 \ CGO_CFLAGS="$CGO_CFLAGS" CGO_LDFLAGS="$CGO_LDFLAGS" \ go build "${strip_flags[@]}" -o "$INGESTOR_BINARY" cmd/ingestor.go GOPROXY=${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} CGO_ENABLED=1 \ CGO_CFLAGS="$CGO_CFLAGS" CGO_LDFLAGS="$CGO_LDFLAGS" \ go build "${strip_flags[@]}" -o "$RAGFLOW_CLI_BINARY" cmd/ragflow-cli.go if [ ! -f "$RAGFLOW_SERVER_BINARY" ]; then echo -e "${RED}Error: Failed to build RAGFlow server binary${NC}" exit 1 fi if [ ! -f "$ADMIN_SERVER_BINARY" ]; then echo -e "${RED}Error: Failed to build Admin server binary${NC}" exit 1 fi if [ ! -f "$INGESTOR_BINARY" ]; then echo -e "${RED}Error: Failed to build Ingestor binary${NC}" exit 1 fi echo -e "${GREEN}✓ Go ragflow_server built successfully: $RAGFLOW_SERVER_BINARY${NC}" echo -e "${GREEN}✓ Go admin_server built successfully: $ADMIN_SERVER_BINARY${NC}" echo -e "${GREEN}✓ Go ragflow-cli built successfully: $RAGFLOW_CLI_BINARY${NC}" echo -e "${GREEN}✓ Go ingestor built successfully: $INGESTOR_BINARY${NC}" } # Configure CGO flags for native libraries (office_oxide, pdfium, pdf_oxide). # Call before any `go build` / `go test` step that links against these libraries. setup_cgo_env() { # ── office_oxide ────────────────────────────────────────────────── check_office_oxide_deps export CGO_CFLAGS="-I${OFFICE_OXIDE_PREFIX}/include/office_oxide_c${CGO_CFLAGS:+ $CGO_CFLAGS}" export CGO_LDFLAGS="-L${OFFICE_OXIDE_PREFIX}/lib -loffice_oxide -Wl,-rpath,${OFFICE_OXIDE_PREFIX}/lib" export LD_LIBRARY_PATH="${OFFICE_OXIDE_PREFIX}/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" # ── pdfium ──────────────────────────────────────────────────────── check_pdfium_deps || return 1 if [ -f "${PDFIUM_PREFIX}/libpdfium.so" ]; then export CGO_LDFLAGS="$CGO_LDFLAGS -L${PDFIUM_PREFIX} -Wl,-rpath,${PDFIUM_PREFIX}" export LD_LIBRARY_PATH="${PDFIUM_PREFIX}:${LD_LIBRARY_PATH}" fi # ── pdf_oxide ───────────────────────────────────────────────────── check_pdf_oxide_deps || return 1 if [ -f "${PDF_OXIDE_PREFIX}/libpdf_oxide.so" ]; then export CGO_LDFLAGS="$CGO_LDFLAGS -L${PDF_OXIDE_PREFIX} -lpdf_oxide -Wl,-rpath,${PDF_OXIDE_PREFIX}" export LD_LIBRARY_PATH="${PDF_OXIDE_PREFIX}:${LD_LIBRARY_PATH}" elif [ -f "${PDF_OXIDE_PREFIX}/libpdf_oxide.a" ]; then export CGO_LDFLAGS="$CGO_LDFLAGS ${PDF_OXIDE_PREFIX}/libpdf_oxide.a" fi echo "CGO_CFLAGS: $CGO_CFLAGS" echo "Exporting CGO_CFLAGS: $CGO_CFLAGS" echo "CGO_LDFLAGS: $CGO_LDFLAGS" echo "Exporting CGO_LDFLAGS: $CGO_LDFLAGS" } # Run Go unit tests with the same CGO env as `build_go`. Pass any extra args # to `go test`, e.g. `./build.sh --test -run TestFoo ./internal/admin/...`. run_go_tests() { print_section "Running Go tests" cd "$PROJECT_ROOT" setup_cgo_env if [ "$#" -eq 0 ]; then set -- ./... fi GOPROXY=${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} CGO_ENABLED=1 \ CGO_CFLAGS="$CGO_CFLAGS" CGO_LDFLAGS="$CGO_LDFLAGS" \ go test -count=1 "$@" } # Clean build artifacts clean() { print_section "Cleaning build artifacts" rm -rf "$BUILD_DIR" rm -f "$RAGFLOW_SERVER_BINARY" rm -f "$ADMIN_SERVER_BINARY" rm -f "$INGESTOR_BINARY" rm -f "$RAGFLOW_CLI_BINARY" echo -e "${GREEN}✓ Build artifacts cleaned${NC}" } # Run the server run() { if [ ! -f "$ADMIN_SERVER_BINARY" ]; then echo -e "${RED}Error: $ADMIN_SERVER_BINARY not found. Build first with --all or --go${NC}" exit 1 fi if [ ! -f "$RAGFLOW_SERVER_BINARY" ]; then echo -e "${RED}Error: $RAGFLOW_SERVER_BINARY not found. Build first with --all or --go${NC}" exit 1 fi if [ ! -f "$INGESTOR_BINARY" ]; then echo -e "${RED}Error: $INGESTOR_BINARY not found. Build first with --all or --go${NC}" exit 1 fi cd "$PROJECT_ROOT" # admin_server must be running before ragflow_server, otherwise ragflow_server's # heartbeats to admin will error out (see internal/development.md). print_section "Starting admin server (background)" "$ADMIN_SERVER_BINARY" & ADMIN_PID=$! trap 'kill "$ADMIN_PID" 2>/dev/null || true' EXIT INT TERM # Give admin_server a moment to bind its listening port (9383) before # ragflow_server starts sending heartbeats to it. sleep 1 print_section "Starting ingestor (background)" "$INGESTOR_BINARY" & INGESTOR_PID=$! trap 'kill "$INGESTOR_PID" 2>/dev/null || true' EXIT INT TERM sleep 1 print_section "Starting RAGFlow server (foreground)" "$RAGFLOW_SERVER_BINARY" } # Show help show_help() { # Quoted delimiter so backticks, `$var`, and `\$` in the help text are # printed literally instead of being interpreted as command substitution. cat << 'EOF' Usage: $0 [OPTIONS] Build script for RAGFlow Go server with C++ bindings. OPTIONS: --all, -a Build everything (C++ library + Go server) [default] --cpp, -c Build only C++ static library --cpp-test Build C++ test executable (requires --cpp first) --go, -g Build only Go server (requires C++ library to be built) --test, -t Run Go unit tests (sets up CGO env for office_oxide). Pass extra args after `--` to forward to `go test`, e.g. `$0 --test -- -run TestFoo ./internal/admin/...` --clean, -C Clean all build artifacts --run, -r Build and run the server --strip, -s Strip debug symbols from Go binaries (-ldflags="-s -w") (disabled by default, useful for smaller production binaries) --help, -h Show this help message EXAMPLES: $0 # Build everything $0 --cpp # Build only C++ library $0 --go # Build only Go server $0 --cpp-test # Build C++ test executable $0 --test # Run all Go tests $0 --test -- -run TestFoo ./internal/admin/... # Targeted Go tests $0 --run # Build and run $0 --clean # Clean build artifacts DEPENDENCIES: - cmake >= 4.0 - go >= 1.24 - g++ with C++17/23 support - office_oxide native library (auto-downloaded on first build) - pcre2 development files - Debian/Ubuntu: libpcre2-dev - openSUSE/RHEL/Fedora: pcre2-devel - macOS (Homebrew): pcre2 EOF } # Main function main() { # Parse --strip / -s before other arguments local args=() for arg in "$@"; do case "$arg" in --strip|-s) STRIP_SYMBOLS="1" ;; *) args+=("$arg") ;; esac done case "${args[0]:-}" in --cpp|-c) check_cpp_deps build_cpp ;; --cpp-test) check_cpp_deps build_cpp_test ;; --go|-g) check_go_deps build_go ;; --test|-t) check_go_deps # Forward any args after `--` to `go test`. if [ "${2:-}" = "--" ]; then shift 2 run_go_tests "$@" else run_go_tests fi ;; --clean|-C) clean ;; --run|-r) check_cpp_deps check_go_deps build_cpp build_go run ;; --help|-h) show_help ;; --all|-a|"") check_cpp_deps check_go_deps build_cpp build_go echo -e "\n${GREEN}=== Build completed successfully! ===${NC}" echo "Binary: $RAGFLOW_SERVER_BINARY, $ADMIN_SERVER_BINARY, $INGESTOR_BINARY, $RAGFLOW_CLI_BINARY" ;; *) echo -e "${RED}Unknown option: $1${NC}" show_help exit 1 ;; esac } main "$@"