Tooling: cpc
cpc is the single command for building, checking, formatting, testing, and querying C+ code.
cpc runs on macOS (Apple Silicon), Linux x86-64, and Windows x86-64, and cross-compiles to iOS (ios-arm64 and the simulator), Android (android-arm64), and the ESP32 (esp32-xtensa) with --target — see Targets & cross-compilation. GUI bindings ship for macOS (appkit) and iOS (uikit); Linux GTK+ and Windows Win32 are planned.
cpc build # multi-file project (reads Cplus.toml)
cpc FILE.cplus -o BIN # single-file build
cpc check FILE # parse + sema only, fast feedback (single file)
cpc check # whole-project front-end check (reads Cplus.toml,
# enforces [profile.realtime]); no codegen, the CI gate
cpc --realtime-report[=json] # whole-project real-time contract digest
cpc fmt FILE # canonical format in place
cpc fmt --check DIR # CI mode, exits 1 on drift
cpc test # run #[test] functions + doctests
cpc doc # generate Markdown documentation from public items
cpc lsp # language server: diagnostics, formatting, code-actions, navigation
# (hover, type-at, value-refs, goto/refs, symbols reflect
# unsaved buffer edits before save)
cpc build --target NAME # cross-compile: host (default), ios-arm64,
# ios-arm64-simulator, android-arm64, esp32-xtensa
cpc --emit-ll FILE # pre-optimisation LLVM IR
cpc --emit-ll-opt FILE # post-optimisation LLVM IR
cpc --emit-asm FILE # native assembly
cpc --emit-obj FILE # native object file
cpc --diagnostics=json # machine-readable diagnostics
cpc --release # -O2 (default is debug -O0 with overflow traps)
cpc -V # print version
Build versus check
cpc build compiles a project (reading Cplus.toml) or, in the FILE.cplus -o BIN form, a single file. cpc check runs only the front-end (lex, parse, sema, borrowck) and stops before codegen, which makes it the fast feedback loop and the CI gate. With no file argument it checks the whole project and enforces any [profile.realtime] contracts.
The default build is debug (-O0) with overflow traps; pass --release for -O2.
Formatting and tests
cpc fmt gives one canonical layout, and cpc fmt --check exits non-zero on drift so CI can reject style noise without arguing about it. cpc test runs #[test] functions and doctests. Every feature is expected to ship three test shapes: a positive case that compiles and runs, a negative case that rejects with a specific Exxxx code, and an end-to-end case that drives cpc build.
A vendor package runs its own tests with cd vendor/<pkg> && cpc test; the driver auto-discovers the entry, propagates the package's link flags, and resolves sibling vendor dependencies one directory up.
The code graph
cpc keeps the resolved, typed information the compiler already computes and exposes it as a queryable code graph, so navigation is by symbol and type rather than by text:
cpc query def SYMBOL # resolved definition site(s)
cpc query refs SYMBOL # every use site
cpc query callers FN # who calls FN
cpc query callees FN # what FN calls
cpc query type-at FILE:LINE:COL # type at a position (incl. inferred expressions)
cpc query value-refs FILE:LINE:COL # a binding's value-flow: def + classified uses
cpc query members TYPE # fields + methods
cpc query context FN # edit pack: signature, callers, callees, types
cpc mcp # resident MCP server over the graph (for agents)
cpc graph # whole-project graph as JSON
type-at now answers inferred positions, not just annotated ones: a call result, a field or index read, an arithmetic expression, and match / if values, all rendered with concrete names like Result[Value, ParseError] or Vec[i32].
value-refs returns a binding's definition plus every use, each classified as read, call, construct (re-wrap), return, match, or assign — the value-flow of one variable rather than a flat list of textual references. Its scoping is precise: a use resolves to the innermost in-scope definition, so shadowing is handled correctly; match-arm payload bindings and for loop variables are first-class definitions; and when a binding is returned from a function, the result records the caller-side bindings its value flows into.
Every result is JSON with file:line:col locations. See C+ for LLMs for why this matters to an agent loop.
Linking against Apple frameworks
cpc build does not yet know framework search paths or the Objective-C runtime. For a program needing -framework X or -lobjc, emit IR and hand it to clang:
cpc --emit-ll src/main.cplus > out.ll
clang out.ll \
-framework Cocoa \
-lobjc \
-Wno-override-module \
-o my_binary