# Building & Manual Testing This doc explains what each CMake preset in [`CMakePresets.json`](../CMakePresets.json) is *for*, which one to actually use day-to-day, and the commands to stand up a real server + `vccli` clients against each other for manual testing. For the one-paragraph quick-start see [`CLAUDE.md`](../CLAUDE.md); for `ctest` targets see [`AGENTS.md`](../AGENTS.md). This doc is the missing middle: *how the presets relate to each other*, *which platform each targets*, and *how to drive the binaries by hand*. **Quick navigation:** | What you want to build | Section | Key command | |------------------------|---------|-------------| | Server + `vccli` + tests (all platforms) | [§3](#3-build--test-the-loop-youll-run-constantly) | `cmake --preset dev && cmake --build --preset dev && ctest --preset dev` | | Production server (stripped, no tests) | [§5](#5-server-release-production-shaped-build) | `cmake --preset server-release && cmake --build --preset server-release` | | Windows client (C# / WinForms) | [§7](#7-windows-client-c--winforms) | `dotnet build clients/windows/VoiceCat.slnx` | | macOS client (AppKit) | [§8](#8-macos-client-appkit) | `xcodebuild -project clients/apple/macOS/VoiceCatMac.xcodeproj -scheme VoiceCatMac build` | | Swift core + tests | [§8](#8-macos-client-appkit) | `cd clients/apple && swift test` | ## 1. What each preset is for | Preset | Binary dir | Deps | Build type | Server | Tools | Tests | Strip | Platform | What it's for | |--------|-----------|------|------------|--------|-------|-------|-------|----------|---------------| | `vcpkg-common` | — | vcpkg | — | — | — | — | — | all | Hidden base. Sets the vcpkg toolchain wrapper ([`cmake/voicecat-toolchain.cmake`](../cmake/voicecat-toolchain.cmake)) which auto-resolves the triplet from the host platform. Not used directly. | | `skeleton` | `build/skeleton` | none | Debug | ON | ON | ON | no | all | The M0 skeleton. Compiles with just a C++20 compiler — no `VCPKG_ROOT` needed. Subsystems are stubs (`VC_ERR_NOT_IMPLEMENTED`). Good for "does the repo even build" sanity checks. Runs 2 tests (smoke + frame_codec). | | `dev` | `build/dev` | vcpkg | Debug | ON | ON | ON | no | all | **The one you actually want.** Day-to-day development: real protocol, crypto, voice, server — everything. Builds server + tools + tests (21 tests). Works on Windows, Linux, and macOS (triplet auto-resolved). | | `release` | `build/release` | vcpkg | Release | ON | ON | ON | no | all | Optimized build with the full test suite. Use to run tests against optimized code, profile, or catch optimizer-sensitive bugs. Symbols kept (not stripped) so stack traces and profiling remain useful. | | `server-release` | `build/server-release` | vcpkg | Release | ON | ON | OFF | **yes** | all | Production-shaped build for deployment. Optimized + stripped binaries (`-s`), no tests. This is what you'd ship/run — see [docs/deployment.md](deployment.md). | | `windows-client` | `build/windows-client` | vcpkg | Release | OFF | OFF | OFF | no | Windows | Produces a redistributable `voicecat.dll` for the C# WinForms client (M4). Static MinGW runtime — no `libgcc_s_seh-1.dll` etc. See [clients/windows/README.md](../clients/windows/README.md). | | `apple-dev` | `build/apple-dev` | vcpkg | Release | OFF | OFF | OFF | no | macOS | Static `libvoicecat.a` for the Swift Package / XCFramework (macOS slice). Validated on macOS 26.5 / Apple Silicon — builds green, produces valid arm64 `.a` + XCFramework. See [clients/apple/README.md](../clients/apple/README.md). | | `apple-ios` | `build/apple-ios` | vcpkg | Release | OFF | OFF | OFF | no | macOS→iOS | **Scaffolding** — cross-compiled static `libvoicecat.a` for iOS device (`arm64-ios`). One XCFramework slice. Not yet CI-validated. | | `apple-ios-sim` | `build/apple-ios-sim` | vcpkg | Release | OFF | OFF | OFF | no | macOS→iOS sim | **Scaffolding** — cross-compiled static `libvoicecat.a` for iOS simulator (`arm64-ios-sim`). One XCFramework slice. Not yet CI-validated. | So in practice there are three presets that matter for day-to-day work: - **`dev`** — everything: real protocol, real voice, real manual testing. This is the loop you run constantly. - **`release`** — same suite, optimized. Run it when you want to check optimized behavior or profile. - **`skeleton`** — fast no-deps build to confirm the stub path still compiles (CI smoke check). The rest are purpose-specific: `server-release` for deployment, `windows-client` for the DLL, `apple-*` for Apple platform slices. ### Platform matrix The vcpkg presets (`dev`, `release`, `server-release`, `windows-client`, `apple-*`) auto-resolve the vcpkg triplet via [`cmake/voicecat-toolchain.cmake`](../cmake/voicecat-toolchain.cmake): | Host platform | Auto-resolved triplet | Notes | |---------------|----------------------|-------| | Windows (MinGW/MSYS2) | `x64-mingw-static` | The project's Windows toolchain. MSVC users must set `VCPKG_TARGET_TRIPLET=x64-windows` explicitly. | | Linux x64 | `x64-linux` | Server's primary deployment target (Docker, systemd). | | Linux arm64 | `arm64-linux` | Raspberry Pi / ARM VPS. | | macOS (Apple Silicon) | `arm64-osx` | `apple-dev` uses this automatically. | | macOS (Intel) | `x64-osx` | `apple-dev` uses this automatically. | Cross-compile presets (`apple-ios`, `apple-ios-sim`) override `VCPKG_TARGET_TRIPLET` explicitly; `VCPKG_HOST_TRIPLET` stays the host's (e.g. `arm64-osx` when building iOS on Apple Silicon). ### Preset history The preset set was cleaned up on 2026-06-18 (see `PROGRESS.md`). The old names map as follows: | Old name | New name | Notes | |----------|----------|-------| | `dev` | `skeleton` | Renamed to reflect its actual purpose (no-deps stub smoke check). | | `m1-dev` | `dev` | Renamed — the project is past M5, so milestone-named presets were misleading. This is now the default development preset. | | `m2-dev` | *(dropped)* | Was cache-identical to `m1-dev` (same flags, same triplet, only the binary dir differed). Removed. | | `server-release` | `server-release` | Unchanged name; now stripped (`-s`) and auto-triplet. | | *(new)* | `release` | New: optimized build with tests on, symbols kept. | | `windows-client` | `windows-client` | Unchanged name; triplet now auto-resolved. | | *(new)* | `apple-dev`, `apple-ios`, `apple-ios-sim` | New: Apple platform scaffolding. | If you see `m1-dev` or `m2-dev` in old scripts, commits, or `PROGRESS.md` history entries, use `dev` instead. Historical `PROGRESS.md` entries are left intact as a true record of what was run. ## 2. One-time setup for the real-deps presets `dev`, `release`, `server-release`, `windows-client`, and `apple-*` all need `VCPKG_ROOT` pointing at a bootstrapped vcpkg checkout: ```bash # once: git clone https://github.com/microsoft/vcpkg ./vcpkg/bootstrap-vcpkg.sh # .bat on Windows # every shell session (PowerShell): $env:VCPKG_ROOT = "D:\path\to\vcpkg" # or on Linux/macOS: export VCPKG_ROOT=/path/to/vcpkg ``` `vcpkg.json` (manifest mode) pins every dependency (protobuf, mbedTLS, libsodium, asio, sqlite3, spdlog, opus, miniaudio) — `cmake --preset dev` resolves and builds them automatically on first configure. That first configure is slow (vcpkg building from source); subsequent ones are cached. ## 3. Build + test (the loop you'll run constantly) ```bash cmake --preset dev cmake --build --preset dev ctest --preset dev ``` Binaries land in `build/dev/bin/` (`.exe` suffix on Windows): - `build/dev/bin/voicecat-server` - `build/dev/bin/vccli` - `build/dev/bin/voicecat-admin` To run the same suite against optimized code: ```bash cmake --preset release cmake --build --preset release ctest --preset release ``` ## 4. Manual testing: server + two clients ### Start the server ```bash ./build/dev/bin/voicecat-server --name "Test Server" --data-dir ./voicecat-data ``` First run generates an Ed25519 identity + self-signed cert under `--data-dir`, creates the SQLite store, and creates a default "Lobby" channel. Other server flags: ``` --port control+media port (default 8384) --data-dir data directory (default ./voicecat-data) --name server name --no-guests disable guest access (then provision accounts via voicecat-admin) --print-config print effective config and exit --version print version and exit ``` ### Drive it with `vccli` `vccli` is the headless client used to exercise the protocol by hand. Full flag list: ``` vccli [--host H] [--port P] [--nick NAME] [--channel ID] [--voice] [--mute] [--text MSG] [--list-devices] [--input-device ID] [--input-mode vad|ptt] [--share-screen-audio] --host H server host (default 127.0.0.1) --port P server TCP port (default 8384) --nick NAME guest nickname (default vccli-test) --channel ID channel to join after auth (default 1, Lobby) --voice start a MIC stream and stay connected until Ctrl+C --mute start with the mic muted (only meaningful with --voice) --text MSG send MSG to the channel, then exit --list-devices print input/output devices (vc_list_devices) and exit --input-device ID use device ID (from --list-devices) for the MIC stream --input-mode vad|ptt send-side input gate mode (default vad) --share-screen-audio also start a SCREEN_AUDIO stream (WASAPI loopback on Windows) ``` While `--voice` is running, stdin accepts `ptt on`, `ptt off`, `mode vad`, `mode ptt` to toggle the input gate live. **Smoke test — two clients talking:** ```bash # terminal A ./build/dev/bin/vccli --nick Alice --text "hello from Alice" # terminal B (separate window, after A confirms it sent) ./build/dev/bin/vccli --nick Bob --text "hello from Bob" ``` **Real voice between two clients (needs working mic/speakers, two terminals):** ```bash # terminal A ./build/dev/bin/vccli --nick Alice --voice # terminal B ./build/dev/bin/vccli --nick Bob --voice ``` Speak into the mic on one side; you should hear it on the other. Ctrl+C to disconnect. **Enumerate audio devices before picking one:** ```bash ./build/dev/bin/vccli --list-devices ./build/dev/bin/vccli --nick Alice --voice --input-device --input-mode ptt ``` **Provisioning a non-guest account** (if the server was started with `--no-guests`): ```bash ./build/dev/bin/voicecat-admin --data-dir ./voicecat-data account add alice --password secret ./build/dev/bin/voicecat-admin --data-dir ./voicecat-data account list ``` ## 5. `server-release` (production-shaped build) Same dependency story, but `Release` build type, stripped binaries, and no tests — this is the closest local analogue to what [docs/deployment.md](deployment.md)'s "from source" path produces: ```bash cmake --preset server-release cmake --build --preset server-release ./build/server-release/bin/voicecat-server ``` Use this to sanity-check release-mode behavior (e.g. perf, optimized codepaths) — not for day-to-day development, since it has no test target wired up. The `-s` linker flag strips symbol tables from the binaries, producing smaller executables suitable for distribution. ## 6. Apple platform builds (scaffolding) The `apple-dev`, `apple-ios`, and `apple-ios-sim` presets produce static `libvoicecat.a` slices for the Swift Package / XCFramework. The `apple-dev` preset (macOS slice) is **validated** — it builds green on macOS 26.5 / Apple Silicon and produces a valid arm64 `.a` + XCFramework. The iOS cross-compile presets (`apple-ios`, `apple-ios-sim`) are still **scaffolding** — not yet CI-validated. Build on macOS: ```bash # macOS slice (arm64-osx on Apple Silicon, x64-osx on Intel) cmake --preset apple-dev cmake --build --preset apple-dev # → build/apple-dev/lib/libvoicecat.a # iOS device slice cmake --preset apple-ios cmake --build --preset apple-ios # → build/apple-ios/lib/libvoicecat.a # iOS simulator slice cmake --preset apple-ios-sim cmake --build --preset apple-ios-sim # → build/apple-ios-sim/lib/libvoicecat.a ``` The three `.a` files are then stitched into an XCFramework via `xcodebuild -create-xcframework` (see [clients/apple/README.md](../clients/apple/README.md) for the planned workflow). Actual XCFramework stitching, `AVAudioSession` integration, and iOS UI work are tracked as follow-up tasks — the presets exist so the build entry point is ready when that work starts. ## 7. Windows client (C# / WinForms) The Windows client is a .NET 10 WinForms app that loads `voicecat.dll` (the MinGW-built shared library from the `windows-client` preset) via P/Invoke. Full details in [`clients/windows/README.md`](../clients/windows/README.md). **Prerequisites:** .NET SDK 10, MinGW-w64 / MSYS2 UCRT64 (GCC 13+), vcpkg. ### Build the DLL ```powershell cmake --preset windows-client cmake --build --preset windows-client # → build/windows-client/bin/voicecat.dll ``` ### Build the C# solution ```powershell cd clients/windows dotnet build VoiceCat.slnx ``` `Directory.Build.props` copies `voicecat.dll` into the output directory automatically. ### Run the app ```powershell # Terminal 1 — start the server (built with the dev preset) ./build/dev/bin/voicecat-server.exe --name "My Server" # Terminal 2 — launch the client dotnet run --project clients/windows/VoiceCat.App/VoiceCat.App.csproj ``` ### Run the C# interop tests ```powershell dotnet test clients/windows/VoiceCat.slnx ``` ## 8. macOS client (AppKit) The macOS client is an Xcode project (AppKit / Swift) that links `libvoicecat` via the `VoiceCatCore` Swift Package, which consumes a binary XCFramework target. Full details in [`clients/apple/README.md`](../clients/apple/README.md). **Prerequisites:** Xcode, vcpkg (`VCPKG_ROOT` set), macOS 14+ (deployment target). ### Build the XCFramework The XCFramework is a local build artifact (gitignored, like the Windows DLL). It bundles `libvoicecat.a` + all vcpkg static deps into a single fat `.a` per slice, plus staged headers with a module map so Swift gets `import VoiceCatC`. ```bash # macOS slice only (default, validated) clients/apple/scripts/build-xcframework.sh # → clients/apple/VoiceCatCore.xcframework/ # All 3 slices (macOS + iOS device + iOS sim — iOS still scaffolding) clients/apple/scripts/build-xcframework.sh --all ``` The script runs `cmake --preset apple-dev` + `cmake --build --preset apple-dev` internally, then merges vcpkg's static deps with `libtool -static` and stitches the XCFramework with `xcodebuild -create-xcframework`. ### Build the Swift core (SPM) ```bash cd clients/apple swift build # builds VoiceCatCore library swift test # 6 smoke tests against a real voicecat-server ``` `swift test` requires the `dev` CMake preset to be built (`build/dev/bin/voicecat-server` + `voicecat-admin`). ### Build the macOS app (Xcode) ```bash xcodebuild -project clients/apple/macOS/VoiceCatMac.xcodeproj \ -scheme VoiceCatMac -configuration Debug build # → ~/Library/Developer/Xcode/DerivedData/VoiceCatMac-*/Build/Products/Debug/VoiceCatMac.app ``` Or open the project in Xcode and build from the UI: ```bash open clients/apple/macOS/VoiceCatMac.xcodeproj ``` ### Run the app ```bash # Terminal 1 — start the server (built with the dev preset) ./build/dev/bin/voicecat-server --name "My Server" # Terminal 2 — launch the client open ~/Library/Developer/Xcode/DerivedData/VoiceCatMac-*/Build/Products/Debug/VoiceCatMac.app ```