diff --git a/.gitignore b/.gitignore index 11ef11f..af62c5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Build output /build/ /out/ + +# Staged distribution artifacts (scripts/build-*.sh output) +/dist/ *.o *.obj *.a diff --git a/scripts/build-all.sh b/scripts/build-all.sh new file mode 100755 index 0000000..2fe865e --- /dev/null +++ b/scripts/build-all.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# +# build-all.sh — build every artifact this host can produce and stage it under +# dist/. Convenience wrapper over the per-artifact scripts in scripts/. +# +# On macOS: server + libs (macOS slice + xcframework) + macos-client (.app) +# On Windows: server + libs (windows DLL) + windows-client (.exe folder) +# On Linux: server only (no native client presets target Linux) +# +# Usage: +# scripts/build-all.sh # build everything host can, into ./dist +# scripts/build-all.sh --dist /out +# scripts/build-all.sh --no-configure # passed through to each step +# scripts/build-all.sh --skip-client # skip the GUI client(s) +# scripts/build-all.sh --skip-libs # skip the library artifacts +# scripts/build-all.sh --skip-server # skip the server +# scripts/build-all.sh -h|--help +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VC_SCRIPT_NAME="build-all" +source "$SCRIPT_DIR/common.sh" + +PASS_ARGS=() +SKIP_CLIENT=false +SKIP_LIBS=false +SKIP_SERVER=false +while [[ $# -gt 0 ]]; do + case "$1" in + --dist) PASS_ARGS+=( --dist "$2" ); vc_set_dist "$2"; shift 2 ;; + --no-configure) PASS_ARGS+=( --no-configure ); shift ;; + --skip-client) SKIP_CLIENT=true; shift ;; + --skip-libs) SKIP_LIBS=true; shift ;; + --skip-server) SKIP_SERVER=true; shift ;; + -h|--help) vc_print_help "$0"; exit 0 ;; + *) vc_die "unknown arg: $1 (try --help)" ;; + esac +done + +HOST="$(vc_host_os)" +vc_log "host: $HOST dist: $VC_DIST_DIR" + +run() { + local name="$1"; shift + vc_step "→ $name $*" + "$SCRIPT_DIR/$name" "$@" "${PASS_ARGS[@]}" +} + +if ! $SKIP_SERVER; then + run build-server.sh +fi + +if ! $SKIP_LIBS; then + if [[ "$HOST" == "macos" ]]; then + run build-libs.sh --platform macos + elif [[ "$HOST" == "windows" ]]; then + run build-libs.sh --platform windows + fi +fi + +if ! $SKIP_CLIENT; then + if [[ "$HOST" == "macos" ]]; then + run build-macos-client.sh + elif [[ "$HOST" == "windows" ]]; then + run build-windows-client.sh + fi +fi + +vc_ok "all done -> $VC_DIST_DIR" +vc_log "contents:" +( cd "$VC_DIST_DIR" && find . -maxdepth 3 | sort | sed 's/^/ /' ) || true diff --git a/scripts/build-libs.sh b/scripts/build-libs.sh new file mode 100755 index 0000000..5001a0f --- /dev/null +++ b/scripts/build-libs.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +# +# build-libs.sh — build libvoicecat for each supported client platform and +# stage the artifacts under dist/lib/. +# +# Artifacts per platform: +# macos → dist/lib/macos/ libvoicecat.a + libvoicecat-fat.a + voicecat.h + module.modulemap +# ios → dist/lib/ios-device/ (arm64-ios slice) +# ios-sim → dist/lib/ios-sim/ (arm64-ios-sim slice) +# windows → dist/lib/windows/ voicecat.dll + voicecat.h +# xcframework → dist/lib/VoiceCatCore.xcframework/ (stitched Apple slices) +# +# The Apple slices + XCFramework are produced by clients/apple/scripts/ +# build-xcframework.sh (the validated flow); this script drives it and stages +# the outputs. The Windows DLL comes from the `windows-client` preset. +# +# Usage: +# scripts/build-libs.sh # host default (macos on macOS, windows on Windows) +# scripts/build-libs.sh --platform macos +# scripts/build-libs.sh --platform ios +# scripts/build-libs.sh --platform ios-sim +# scripts/build-libs.sh --platform windows +# scripts/build-libs.sh --all-apple # macos + ios + ios-sim + xcframework (macOS host) +# scripts/build-libs.sh --all # everything buildable on this host +# scripts/build-libs.sh --dist /out --no-configure +# scripts/build-libs.sh -h|--help +# +# Platform availability: macos / ios / ios-sim require a macOS host; windows +# requires a Windows host. Requires VCPKG_ROOT. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VC_SCRIPT_NAME="build-libs" +source "$SCRIPT_DIR/common.sh" + +PLATFORMS=() +DO_CONFIGURE=true +while [[ $# -gt 0 ]]; do + case "$1" in + --dist) vc_set_dist "$2"; shift 2 ;; + --no-configure) DO_CONFIGURE=false; shift ;; + --platform) PLATFORMS+=( "$2" ); shift 2 ;; + --all-apple) PLATFORMs_all_apple=1; shift ;; + --all) PLATFORMS=( all-host ); shift ;; + -h|--help) vc_print_help "$0"; exit 0 ;; + *) vc_die "unknown arg: $1 (try --help)" ;; + esac +done + +HOST="$(vc_host_os)" + +# --all-apple expands to the three Apple platforms. +if [[ -n "${PLATFORMs_all_apple:-}" ]]; then + PLATFORMS=( macos ios ios-sim ) +fi + +# Default: host-appropriate single platform. +if [[ ${#PLATFORMS[@]} -eq 0 ]]; then + case "$HOST" in + macos) PLATFORMS=( macos ) ;; + windows) PLATFORMS=( windows ) ;; + *) vc_die "no default library platform for host '$HOST'. Pass --platform explicitly." ;; + esac +fi + +# Expand the meta-target. +if [[ " ${PLATFORMS[*]} " == *" all-host "* ]]; then + PLATFORMS=() + case "$HOST" in + macos) PLATFORMS=( macos ios ios-sim ) ;; + windows) PLATFORMS=( windows ) ;; + *) vc_die "--all: nothing buildable on host '$HOST'." ;; + esac +fi + +vc_log "dist dir: $VC_DIST_DIR" +vc_log "platforms: ${PLATFORMS[*]}" + +# Validate platform availability against host. +for p in "${PLATFORMS[@]}"; do + case "$p" in + macos|ios|ios-sim) [[ "$HOST" == "macos" ]] || vc_die "platform '$p' requires a macOS host." ;; + windows) [[ "$HOST" == "windows" ]] || vc_die "platform '$p' requires a Windows host." ;; + *) vc_die "unknown platform '$p' (try --help)" ;; + esac +done + +HAVE_APPLE=false +HAVE_WINDOWS=false +for p in "${PLATFORMS[@]}"; do + case "$p" in + macos|ios|ios-sim) HAVE_APPLE=true ;; + windows) HAVE_WINDOWS=true ;; + esac +done + +# ── Apple slices + XCFramework ────────────────────────────────────────────────── +# Stage one slice's .a + headers + module map into dist/lib/. +stage_apple_slice() { + local preset="$1" sub="$2" + local lib="$VC_REPO_ROOT/build/$preset/lib/libvoicecat.a" + local fat="$VC_REPO_ROOT/build/$preset/lib/libvoicecat-fat.a" + local out; out="$(vc_dist_subdir "lib/$sub")" + [[ -f "$lib" ]] || vc_die "expected $lib not found (did the xcframework build run?)" + cp "$lib" "$out/" + [[ -f "$fat" ]] && cp "$fat" "$out/" || true + cp "$VC_REPO_ROOT/core/include/voicecat.h" "$out/" + cat > "$out/module.modulemap" <<'MM' +module VoiceCatC { + header "voicecat.h" + export * +} +MM + vc_ok "$sub: libvoicecat.a ($(vc_file_size "$lib") bytes) -> $out" +} + +if $HAVE_APPLE; then + vc_resolve_vcpkg_root apple-dev + vc_step "build Apple slices via build-xcframework.sh" + + ALL_THREE=false + if [[ " ${PLATFORMS[*]} " == *" macos "* && " ${PLATFORMS[*]} " == *" ios "* && " ${PLATFORMS[*]} " == *" ios-sim "* ]]; then + ALL_THREE=true + fi + + xcfw_invoke() { + local args=() + $DO_CONFIGURE || args+=( --no-configure ) + args+=( "$@" ) + "$VC_REPO_ROOT/clients/apple/scripts/build-xcframework.sh" "${args[@]}" + } + + if $ALL_THREE; then + xcfw_invoke --all + stage_apple_slice apple-dev macos + stage_apple_slice apple-ios ios-device + stage_apple_slice apple-ios-sim ios-sim + else + for p in "${PLATFORMS[@]}"; do + case "$p" in + macos) xcfw_invoke --preset apple-dev; stage_apple_slice apple-dev macos ;; + ios) xcfw_invoke --preset apple-ios; stage_apple_slice apple-ios ios-device ;; + ios-sim) xcfw_invoke --preset apple-ios-sim; stage_apple_slice apple-ios-sim ios-sim ;; + esac + done + fi + + # Stage the stitched XCFramework (whatever slices the last call produced). + XCFW="$VC_REPO_ROOT/clients/apple/VoiceCatCore.xcframework" + [[ -d "$XCFW" ]] || vc_die "xcframework not found at $XCFW" + LIB_DIR="$VC_DIST_DIR/lib" + mkdir -p "$LIB_DIR" + rm -rf "$LIB_DIR/VoiceCatCore.xcframework" + cp -R "$XCFW" "$LIB_DIR/" + vc_ok "VoiceCatCore.xcframework -> $LIB_DIR/" +fi + +# ── Windows DLL ───────────────────────────────────────────────────────────────── +if $HAVE_WINDOWS; then + vc_resolve_vcpkg_root windows-client + vc_step "build Windows DLL (preset: windows-client)" + if $DO_CONFIGURE; then + cmake --preset windows-client + fi + cmake --build --preset windows-client + DLL="$VC_REPO_ROOT/build/windows-client/bin/voicecat.dll" + [[ -f "$DLL" ]] || vc_die "expected $DLL not found" + OUT="$(vc_dist_subdir lib/windows)" + cp "$DLL" "$OUT/" + cp "$VC_REPO_ROOT/core/include/voicecat.h" "$OUT/" + vc_ok "windows: voicecat.dll ($(vc_file_size "$DLL") bytes) -> $OUT" +fi + +vc_ok "done -> $VC_DIST_DIR/lib" diff --git a/scripts/build-macos-client.sh b/scripts/build-macos-client.sh new file mode 100755 index 0000000..f724b03 --- /dev/null +++ b/scripts/build-macos-client.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# +# build-macos-client.sh — build the macOS AppKit client (VoiceCatMac.app) and +# stage it into dist/macos-client/. +# +# Two steps: +# 1. Build VoiceCatCore.xcframework (macOS slice) by invoking the existing +# clients/apple/scripts/build-xcframework.sh — that builds libvoicecat.a +# (apple-dev preset), merges vcpkg deps into a fat .a, and stitches the +# XCFramework the Swift Package consumes. +# 2. xcodebuild the VoiceCatMac scheme into a known derivedDataPath, then +# copy VoiceCatMac.app into dist/macos-client/. +# +# Usage: +# scripts/build-macos-client.sh # build + stage into ./dist/macos-client +# scripts/build-macos-client.sh --dist /out +# scripts/build-macos-client.sh --no-configure # skip cmake configure (xcframework step) +# scripts/build-macos-client.sh --config Debug # Xcode build config (default Release) +# scripts/build-macos-client.sh -h|--help +# +# macOS only. Requires VCPKG_ROOT and Xcode. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VC_SCRIPT_NAME="build-macos-client" +source "$SCRIPT_DIR/common.sh" + +DO_CONFIGURE=true +XCODE_CONFIG="Release" +while [[ $# -gt 0 ]]; do + case "$1" in + --dist) vc_set_dist "$2"; shift 2 ;; + --no-configure) DO_CONFIGURE=false; shift ;; + --config) XCODE_CONFIG="$2"; shift 2 ;; + -h|--help) vc_print_help "$0"; exit 0 ;; + *) vc_die "unknown arg: $1 (try --help)" ;; + esac +done + +vc_require_macos +vc_log "dist dir: $VC_DIST_DIR" + +vc_step "build VoiceCatCore.xcframework (macOS slice)" +XCFW_ARGS=() +$DO_CONFIGURE || XCFW_ARGS+=( --no-configure ) +"$VC_REPO_ROOT/clients/apple/scripts/build-xcframework.sh" "${XCFW_ARGS[@]}" +XCFW="$VC_REPO_ROOT/clients/apple/VoiceCatCore.xcframework" +[[ -d "$XCFW" ]] || vc_die "xcframework not found at $XCFW" +vc_ok "xcframework ready -> $XCFW" + +vc_step "xcodebuild VoiceCatMac ($XCODE_CONFIG)" +XCODEPROJ="$VC_REPO_ROOT/clients/apple/macOS/VoiceCatMac.xcodeproj" +DERIVED="$VC_REPO_ROOT/build/macos-app" +xcodebuild -project "$XCODEPROJ" -scheme VoiceCatMac \ + -configuration "$XCODE_CONFIG" -derivedDataPath "$DERIVED" build + +APP="$DERIVED/Build/Products/$XCODE_CONFIG/VoiceCatMac.app" +[[ -d "$APP" ]] || vc_die "VoiceCatMac.app not found at $APP" +vc_ok "built -> $APP" + +vc_step "stage -> $VC_DIST_DIR/macos-client" +STAGE="$(vc_dist_subdir macos-client)" +cp -R "$APP" "$STAGE/" +vc_ok "VoiceCatMac.app -> $STAGE/ ($(du -sh "$APP" | cut -f1))" +vc_ok "done -> $STAGE" diff --git a/scripts/build-server.sh b/scripts/build-server.sh new file mode 100755 index 0000000..4715398 --- /dev/null +++ b/scripts/build-server.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# +# build-server.sh — build the production-shaped voicecat-server (+ voicecat-admin) +# and stage the binaries into dist/server/. +# +# Uses the `server-release` CMake preset (Release, stripped, no tests — see +# docs/building.md §5). Works on Windows, Linux, and macOS; the vcpkg triplet +# is auto-resolved by cmake/voicecat-toolchain.cmake. +# +# Usage: +# scripts/build-server.sh # build + stage into ./dist/server +# scripts/build-server.sh --dist /out # stage into /out/server +# scripts/build-server.sh --no-configure # skip cmake configure, just rebuild + stage +# scripts/build-server.sh -h|--help # show this help +# +# Override the default dist dir (/dist) with --dist or VOICECAT_DIST_DIR. +# Requires VCPKG_ROOT (or a pre-configured build/server-release cache). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VC_SCRIPT_NAME="build-server" +source "$SCRIPT_DIR/common.sh" + +DO_CONFIGURE=true +while [[ $# -gt 0 ]]; do + case "$1" in + --dist) vc_set_dist "$2"; shift 2 ;; + --no-configure) DO_CONFIGURE=false; shift ;; + -h|--help) vc_print_help "$0"; exit 0 ;; + *) vc_die "unknown arg: $1 (try --help)" ;; + esac +done + +vc_log "dist dir: $VC_DIST_DIR" +vc_resolve_vcpkg_root server-release + +vc_step "configure + build (preset: server-release)" +if $DO_CONFIGURE; then + cmake --preset server-release +fi +cmake --build --preset server-release + +BIN_DIR="$VC_REPO_ROOT/build/server-release/bin" +EXE_EXT="" +[[ "$(vc_host_os)" == "windows" ]] && EXE_EXT=".exe" + +vc_log "staging binaries -> $VC_DIST_DIR/server" +STAGE="$(vc_dist_subdir server)" + +for b in voicecat-server voicecat-admin; do + f="$BIN_DIR/$b$EXE_EXT" + if [[ ! -f "$f" ]]; then + # voicecat-admin only builds when vcpkg deps are on (server-release has + # them on), so it should exist — but be lenient about admin on odd configs. + [[ "$b" == "voicecat-admin" ]] && { vc_log "note: $b not found, skipping"; continue; } + vc_die "expected output not found: $f" + fi + cp "$f" "$STAGE/" + vc_ok "$b$EXE_EXT -> $STAGE/$(basename "$f") ($(vc_file_size "$f") bytes)" +done + +vc_ok "done -> $STAGE" diff --git a/scripts/build-windows-client.sh b/scripts/build-windows-client.sh new file mode 100755 index 0000000..9733228 --- /dev/null +++ b/scripts/build-windows-client.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# +# build-windows-client.sh — build the Windows WinForms client and stage a +# runnable folder into dist/windows-client/. +# +# Two steps: +# 1. Build the native voicecat.dll via the `windows-client` CMake preset +# (MinGW, static runtime — no libgcc_s_seh-1.dll / libstdc++-6.dll deps). +# 2. `dotnet publish` the C# app (clients/windows/VoiceCat.App). The app's +# Directory.Build.props copies voicecat.dll into the output automatically. +# +# The staged folder contains VoiceCat.App.exe + voicecat.dll + all .NET deps, +# ready to run. By default it is framework-dependent (needs the .NET 10 runtime +# installed); pass --self-contained for a folder that runs without .NET. +# +# Usage: +# scripts/build-windows-client.sh # build + stage into ./dist/windows-client +# scripts/build-windows-client.sh --dist /out +# scripts/build-windows-client.sh --no-configure # skip cmake configure (DLL step) +# scripts/build-windows-client.sh --self-contained +# scripts/build-windows-client.sh -h|--help +# +# Windows only (MSYS2/MinGW). Requires VCPKG_ROOT and the .NET 10 SDK. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VC_SCRIPT_NAME="build-windows-client" +source "$SCRIPT_DIR/common.sh" + +DO_CONFIGURE=true +SELF_CONTAINED=false +while [[ $# -gt 0 ]]; do + case "$1" in + --dist) vc_set_dist "$2"; shift 2 ;; + --no-configure) DO_CONFIGURE=false; shift ;; + --self-contained) SELF_CONTAINED=true; shift ;; + -h|--help) vc_print_help "$0"; exit 0 ;; + *) vc_die "unknown arg: $1 (try --help)" ;; + esac +done + +vc_require_windows +vc_log "dist dir: $VC_DIST_DIR" +vc_resolve_vcpkg_root windows-client + +vc_step "build native DLL (preset: windows-client)" +if $DO_CONFIGURE; then + cmake --preset windows-client +fi +cmake --build --preset windows-client +DLL="$VC_REPO_ROOT/build/windows-client/bin/voicecat.dll" +[[ -f "$DLL" ]] || vc_die "expected output not found: $DLL" +vc_ok "voicecat.dll -> $DLL ($(vc_file_size "$DLL") bytes)" + +vc_step "publish C# app (dotnet publish)" +APP_PROJ="$VC_REPO_ROOT/clients/windows/VoiceCat.App/VoiceCat.App.csproj" +PUBLISH_DIR="$VC_REPO_ROOT/build/windows-client/publish" +rm -rf "$PUBLISH_DIR" +mkdir -p "$PUBLISH_DIR" + +PUBLISH_ARGS=(dotnet publish "$APP_PROJ" -c Release -o "$PUBLISH_DIR") +if $SELF_CONTAINED; then + PUBLISH_ARGS+=( -r win-x64 --self-contained true ) +fi +"${PUBLISH_ARGS[@]}" +APP_EXE="$PUBLISH_DIR/VoiceCat.App.exe" +[[ -f "$APP_EXE" ]] || vc_die "publish output missing VoiceCat.App.exe in $PUBLISH_DIR" + +vc_step "stage -> $VC_DIST_DIR/windows-client" +STAGE="$(vc_dist_subdir windows-client)" +cp -R "$PUBLISH_DIR/." "$STAGE/" +vc_ok "VoiceCat.App.exe + deps -> $STAGE (voicecat.dll included)" +vc_ok "done -> $STAGE" diff --git a/scripts/common.sh b/scripts/common.sh new file mode 100755 index 0000000..f1170f7 --- /dev/null +++ b/scripts/common.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# +# common.sh — shared helpers for the scripts/ build scripts. +# +# Source this from the other scripts/ build scripts (after setting SCRIPT_DIR): +# +# SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# VC_SCRIPT_NAME="build-foo" # optional, for nicer log prefixes +# source "$SCRIPT_DIR/common.sh" +# +# What it provides: +# - repo-root + dist-dir resolution (default /dist; override with +# --dist via vc_set_dist, or the VOICECAT_DIST_DIR env var). +# - VCPKG_ROOT detection (env var first, then an existing CMake cache). +# - host-platform guards (vc_require_macos / vc_require_windows / vc_host_os). +# - uniform [tag] logging + a portable file-size helper. +# - vc_dist_subdir : create (clean) and echo $DIST/. +# - vc_print_help : print a script's leading "# " comment block as the +# --help text (so each script's header comment IS its help). +# +# Not meant to be run directly. + +# This file lives in scripts/, so the repo root is one level up. +VC_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VC_REPO_ROOT="$(cd "$VC_SCRIPT_DIR/.." && pwd)" + +# Display name for log lines (defaults to the caller's filename). +VC_SCRIPT_NAME="${VC_SCRIPT_NAME:-$(basename "${BASH_SOURCE[1]:-$0}")}" + +# Dist dir — env override resolved here; --dist updates it via vc_set_dist. +VC_DIST_DIR="${VOICECAT_DIST_DIR:-$VC_REPO_ROOT/dist}" + +# ── Logging ───────────────────────────────────────────────────────────────────── +vc_log() { printf '\033[1;34m[%s]\033[0m %s\n' "$VC_SCRIPT_NAME" "$*"; } +vc_step() { printf '\033[1;36m[%s] === %s ===\033[0m\n' "$VC_SCRIPT_NAME" "$*"; } +vc_ok() { printf '\033[1;32m[%s]\033[0m %s\n' "$VC_SCRIPT_NAME" "$*"; } +vc_err() { printf '\033[1;31m[%s] error:\033[0m %s\n' "$VC_SCRIPT_NAME" "$*" >&2; } +vc_die() { vc_err "$*"; exit 1; } + +# Portable file size (macOS `stat -f%z` vs coreutils `stat -c%s`). +vc_file_size() { + stat -f%z "$1" 2>/dev/null || stat -c%s "$1" 2>/dev/null +} + +# ── Dist dir ──────────────────────────────────────────────────────────────────── +# Set/override the dist directory (called when a script sees --dist ). +vc_set_dist() { + VC_DIST_DIR="$1" + mkdir -p "$VC_DIST_DIR" +} + +# Create (clean) and echo a subdirectory under dist/. +vc_dist_subdir() { + local path="$VC_DIST_DIR/$1" + rm -rf "$path" + mkdir -p "$path" + echo "$path" +} + +# ── Platform helpers ──────────────────────────────────────────────────────────── +vc_host_os() { + case "$(uname -s)" in + Darwin) echo macos ;; + MINGW*|MSYS*|CYGWIN*) echo windows ;; + Linux) echo linux ;; + *) echo unknown ;; + esac +} + +vc_require_macos() { + [[ "$(uname -s)" == "Darwin" ]] \ + || vc_die "this script must be run on macOS (uname -s = $(uname -s))." +} + +vc_require_windows() { + local s; s="$(uname -s)" + [[ "$s" == MINGW* || "$s" == MSYS* || "$s" == CYGWIN* ]] \ + || vc_die "this script must be run on Windows (MSYS2/MinGW). uname -s = $s." +} + +# ── VCPKG_ROOT ────────────────────────────────────────────────────────────────── +# Honor the env var, else try to read it from an existing CMake cache +# (Z_VCPKG_ROOT_DIR) so a developer who already configured a preset doesn't need +# VCPKG_ROOT in their shell env. $1 = the preset whose cache to probe as a +# fallback (e.g. "dev" or "apple-dev"). +vc_resolve_vcpkg_root() { + local fallback_preset="${1:-dev}" + if [[ -z "${VCPKG_ROOT:-}" ]]; then + local cache="$VC_REPO_ROOT/build/$fallback_preset/CMakeCache.txt" + if [[ -f "$cache" ]]; then + local detected + detected="$(grep -m1 '^Z_VCPKG_ROOT_DIR:INTERNAL=' "$cache" | cut -d= -f2- || true)" + if [[ -n "$detected" && -d "$detected" ]]; then + export VCPKG_ROOT="$detected" + fi + fi + fi + if [[ -z "${VCPKG_ROOT:-}" || ! -d "$VCPKG_ROOT" ]]; then + vc_die "VCPKG_ROOT is not set or does not exist. + bootstrap vcpkg (https://vcpkg.io) then: + export VCPKG_ROOT=/path/to/vcpkg" + fi + vc_log "VCPKG_ROOT=$VCPKG_ROOT" +} + +# ── Help ──────────────────────────────────────────────────────────────────────── +# Print a script's leading "# " comment block (skipping the shebang) as help. +vc_print_help() { + awk 'NR==1{next} /^#[[:space:]]?/{sub(/^#[[:space:]]?/,""); print; next} {exit}' "$1" +}