Files
voice-cat/docs/building.md
Talon 56a6e4fab5 docs: add Windows + macOS client build commands to building.md
Add §7 (Windows client: dotnet build) and §8 (macOS client: XCFramework
+ xcodebuild) with the full build/run commands for each client platform.
Also add a quick-navigation table at the top of the doc.
2026-06-18 17:36:47 +02:00

15 KiB

Building & Manual Testing

This doc explains what each CMake preset in 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; for ctest targets see 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 cmake --preset dev && cmake --build --preset dev && ctest --preset dev
Production server (stripped, no tests) §5 cmake --preset server-release && cmake --build --preset server-release
Windows client (C# / WinForms) §7 dotnet build clients/windows/VoiceCat.slnx
macOS client (AppKit) §8 xcodebuild -project clients/apple/macOS/VoiceCatMac.xcodeproj -scheme VoiceCatMac build
Swift core + tests §8 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) 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.
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.
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.
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:

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:

# 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)

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:

cmake --preset release
cmake --build --preset release
ctest --preset release

4. Manual testing: server + two clients

Start the server

./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 <n>          control+media port (default 8384)
--data-dir <path>   data directory (default ./voicecat-data)
--name <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:

# 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):

# 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:

./build/dev/bin/vccli --list-devices
./build/dev/bin/vccli --nick Alice --voice --input-device <ID> --input-mode ptt

Provisioning a non-guest account (if the server was started with --no-guests):

./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's "from source" path produces:

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:

# 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 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.

Prerequisites: .NET SDK 10, MinGW-w64 / MSYS2 UCRT64 (GCC 13+), vcpkg.

Build the DLL

cmake --preset windows-client
cmake --build --preset windows-client
# → build/windows-client/bin/voicecat.dll

Build the C# solution

cd clients/windows
dotnet build VoiceCat.slnx

Directory.Build.props copies voicecat.dll into the output directory automatically.

Run the app

# 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

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.

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.

# 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)

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)

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:

open clients/apple/macOS/VoiceCatMac.xcodeproj

Run the app

# 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