machdocs
Home GitHub

CLI

The mach binary is the compiler driver. It dispatches on the first argument to a small set of commands - build, run, test, and more - that compile, execute, and manage a project rooted at a mach.toml.

Invocation

mach <command> [options]

The compiler dispatches on argv[1]. With no command, or an unknown one, it prints usage and exits non-zero. The project commands - build, run, test, and doc - take the project root as a required positional and walk up from it until a mach.toml is found. A bare invocation with no path is a user error.

Flags are matched exactly: --flag value (the value follows in the next argument) or a bare --flag toggle.

Restriction

The combined --flag=value form and bundled short flags are not recognized. Each flag and its value are separate arguments.

Commands

CommandSummary
buildcompile the project to objects and (for a [bin.*]) a linked binary
runbuild, then execute the produced binary
testbuild the test binary and run the collected tests
depmanage git-backed dependencies (clone, lock, vendor)
initscaffold a new project
docgenerate Markdown reference docs from source doc-comments
infoprint compiler version, build host, and registered target capabilities
helpprint usage; mach help <command> for detail

Global flags

Read by build, run, test, and doc, which share one config parser. Passing --verbose and --quiet together is a parse error.

FlagValueEffect
--verbose, -v-write extra detail (per-stage compile progress) to stderr
--quiet, -q-suppress non-error output
--color <mode>auto, always, nevercolor preference (default auto); an unknown mode is a parse error
--target <name>target nameselect a declared target; absent, defers to [project].target
--profile <name>profile nameselect a [profile.<name>] variant; absent, the first declared
--bin <name>artifact namenarrow the build to one [bin.<name>] artifact
--lib <name>artifact namenarrow to one [lib.<name>] artifact (mutually exclusive with --bin)
-o <path>pathoverride the artifact path, rooted at the project root
--all-targets-build every declared [target.*], not just the default
--emit-asm-emit per-module assembly text (.s); forces the profile's emit_asm on
--emit-ir-emit per-module SSA IR text (.ir); forces the profile's emit_ir on
--no-emit-asm-force assembly emission off, overriding the profile
--no-emit-ir-force IR emission off, overriding the profile
--verify-ir-run the IR verifier after each optimisation pass
Note

mach dep and mach init do not use the shared config parser; they read only their own flags.

build

mach build <path> [options]

Compiles the project rooted at <path> (for example mach build .). With no --bin or --lib, it builds every declared artifact for the default target and profile. Every reachable module is driven through sema, lower, optimise, and codegen to one relocatable object under the manifest's obj template. For a [bin.*] the objects are linked into the resolved out path; for a [lib.*] (or with --emit obj) the objects are the deliverable and nothing is linked.

FlagValueEffect
--release-select the release optimisation pipeline
-O0-force the debug pipeline (overrides --release)
-O1, -O2-select the release pipeline (both select the same one today)
--emit <kind>obj, exeobj stops at the objects; exe (default) links a binary
-L <dir>diradd a search directory for -l-resolved inputs; repeatable
-l <name>namelink a named object, archive, or shared library; repeatable

Plus the global flags. The -O<n> flags override --release when both are present; absent any optimisation flag, the build uses the debug pipeline (the bootstrap-stable default).

ext fun declarations are forward references resolved at link time by external precompiled code - a loose .o object, a static .a archive, or a shared .so library. Inputs come from the command line and from the manifest's merged libs overlay; both sets are linked. An input that resolves to no existing file is a hard error, so a typo never silently drops a dependency.

How an input resolves decides static vs dynamic linking. A loose .o or static .a is a static input merged into the executable (an archive contributes every member object). A shared .so is a dynamic dependency: its DT_SONAME is recorded and undefined ext symbols are bound at load time through an emitted PLT. A static definition always wins over a dynamic import of the same name. -l prefers a static candidate, so the .so fallback applies only when none exists (the common case for system libraries like libc). Manifest libs resolve before CLI inputs, giving a deterministic link order.

Note

Dynamic linking is implemented for the ELF (Linux) and PE (Windows) targets. The Mach-O (Darwin) import path is not yet implemented.

run

mach run <path> [options] [-- args...]

Builds the project at <path> exactly like mach build, then executes the produced binary. Arguments after a -- separator are forwarded to the child as its argv; the child's exit code becomes this command's exit code. --emit is rejected - running a relocatable object is not meaningful. All other build and global flags apply.

FlagValueEffect
--runner <cmd>commandexecute the binary as <cmd> <binary> <args...> instead of directly

--runner names a host-side launcher for binaries the host cannot exec directly, for example mach run . --target windows --runner wine. <cmd> is a single command name or path (no shell-style word splitting); a bare name is resolved on PATH. Without the flag the binary is exec'd directly, and a launch failure reports exit 127 when execve rejects the binary, with no auto-detection.

test

mach test <path> [options]

Builds one standalone executable per test declaration (the test plus the project's transitive code, with a synthesized main calling just that test), then spawns each as a separate process and reports a per-test name file:line PASS|FAIL line. A crashing test reports its signal and the run continues. A test build always links executables, even for a library target.

FlagValueEffect
--filter <pattern>patternrun only tests whose name contains <pattern>
--list-list the collected tests and exit
--runner <cmd>commandlaunch every test as <cmd> <exe> instead of exec'ing it directly

Plus the build and global flags. --runner has the same semantics as on run. Without it, a test the host cannot launch reports a per-test failure - FAIL(exit 127) when execve rejects the binary, FAIL(spawn) when the spawn itself fails.

dep

mach dep <action> [args]

Manages the project's dependency tree. A dependency has exactly one source form: a git URL plus a ref, acquired into the dep directory with plain git operations, or a path to another project tree, materialised as a relative symlink so the build resolves it by the same vendor layout.

ActionArgsEffect
pull-realise the manifest: clone missing git deps (transitively), link path deps, re-resolve a changed ref, repair drift, write mach.lock. Idempotent.
update<name> or --allthe only lock-advancer: re-resolve branch refs to current remote tips. Tag/commit refs are a no-op. Never edits the manifest.
add<name> --git <url> [--ref <ref>] or --path <dir>append a [deps.<name>] stanza, then pull.
remove<name> [--purge]drop the entry from mach.toml and mach.lock; --purge also deletes the vendored checkout.
list-print each entry with its source form, ref, locked commit, and state (synced, missing, drifted, path).

mach build never requires git or the network: a project whose dep tree is present builds on a bare machine. Only the network-shaped commands (pull, update, add) use git, discovered on PATH and invoked with an allowlisted environment. Git's absence is a clean error naming the operation that needed it. The same name required from two different sources or refs is a hard error naming both requirers; there is no version resolution.

Lockfile

The manifest is intent; mach.lock is the record of resolving it. After a pull, mach dep writes a TOML file recording each git dep's url, ref, and resolved commit (path deps have no lock entry).

# generated by `mach dep pull`; do not edit by hand.
version = 1

[deps.mach-std]
url = "https://github.com/briar-systems/mach-std"
ref = "branch/dev"
commit = "6b78ae1e8c3c9cc45e4ab4b916fd191d61e76aff"

pull honours the lock except where the manifest ref was edited, where it re-resolves loudly. A checked-out commit that differs from the lock is drift, repaired and reported, never silent. The writer is idempotent: an up-to-date lock is left untouched. Commit mach.lock to pin builds.

init

mach init [dir] [options]

Scaffolds a new project in [dir] (default: the current directory). Writes a complete mach.toml with a [project] block, [target.*] platforms for linux/windows/darwin, one artifact, debug and release profiles, a [deps.mach-std] dependency, a starter source file, and a cloned mach-std. Every collision is checked before any file is written, so a refused init leaves nothing behind.

FlagValueEffect
--name <name>nameproject id (default: the directory base name)
--force-scaffold even when mach.toml, src/main.mach, or src/lib.mach already exists
--lib-library layout: write src/lib.mach and scaffold a [lib.<id>] artifact (kind = "static") instead of [bin.<id>]

The first non-flag argument is the target directory. A default binary scaffold links and runs from mach build . without further manifest edits.

doc, info, and help

mach doc <path> loads the module graph and generates Markdown reference docs from source doc-comments - one page per module plus an index. Each pub declaration is paired with the run of # comment lines immediately preceding it. --out <dir> sets the output directory (default doc/api); --target <name> selects a target for module discovery. The hand-written language material is never touched.

mach info prints an at-a-glance identity of the binary: its version, the host it was built for, and the registered capability surface. It needs no project. The output is line-oriented and stable for scripts.

mach 
host: linux/x86_64
isa: x86_64 aarch64 riscv64
os: linux darwin windows freestanding
abi: sysv64 win64 aapcs64 lp64
object: elf coff macho raw

The capability lines are read from the binary's target registries, so they report exactly what this build can target. mach info --version prints the version string alone on one line, for tooling.

mach help [command] prints the top-level usage summary, or - with a known command - that command's detail page. An unknown command prints usage and exits non-zero.

Exit codes

The commands share a stable convention for scripting:

CodeMeaning
0success (for test, all tests passed)
1user error: missing project path, no mach.toml, unknown target, compile errors, an unresolvable link input (for test, any test failed)
2internal error

mach run instead returns the child's exit code on a successful build, falling back to 1 on a build/user error and 2 internally.

See also

  • Manifest - the mach.toml reference for targets, profiles, and libs
  • Dependencies - git and path deps managed by mach dep
  • Testing - the test declaration behind mach test
  • Project layout - the directory shape mach init scaffolds