build(scripts): add per-artifact build scripts staging into dist/

One script per main artifact, all staging into dist/ (overridable via
--dist or VOICECAT_DIST_DIR):

- build-server.sh        server-release preset -> dist/server/
- build-windows-client.sh  windows-client DLL + dotnet publish -> dist/windows-client/
- build-macos-client.sh    XCFramework + xcodebuild VoiceCatMac.app -> dist/macos-client/
- build-libs.sh           libvoicecat per platform -> dist/lib/{macos,ios*,windows}/
                         + dist/lib/VoiceCatCore.xcframework/
- build-all.sh            orchestrator (host-applicable artifacts only)
- common.sh               shared helpers: dist/VCPKG_ROOT resolution, platform
                         guards, logging, --help from header comments

Host-platform guards prevent running the wrong script on the wrong OS.
build-libs.sh and build-macos-client.sh drive the existing validated
clients/apple/scripts/build-xcframework.sh rather than duplicating it.
dist/ added to .gitignore.
This commit is contained in:
2026-06-18 18:52:54 +02:00
parent 56a6e4fab5
commit 06a68b441a
7 changed files with 558 additions and 0 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,9 @@
# Build output # Build output
/build/ /build/
/out/ /out/
# Staged distribution artifacts (scripts/build-*.sh output)
/dist/
*.o *.o
*.obj *.obj
*.a *.a

71
scripts/build-all.sh Executable file
View File

@@ -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

174
scripts/build-libs.sh Executable file
View File

@@ -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/<sub>.
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"

65
scripts/build-macos-client.sh Executable file
View File

@@ -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"

62
scripts/build-server.sh Executable file
View File

@@ -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 (<repo>/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"

73
scripts/build-windows-client.sh Executable file
View File

@@ -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"

110
scripts/common.sh Executable file
View File

@@ -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 <repo>/dist; override with
# --dist <path> 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 <name>: create (clean) and echo $DIST/<name>.
# - vc_print_help <file>: 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 <path>).
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"
}