machdocs
Home GitHub

Manifest

Every mach project has a mach.toml at its root: the complete, readable statement of what the project builds - its identity, target platforms, artifacts, build variants, and dependencies. The compiler finds it by walking up from the working directory, so any subcommand run inside a project tree resolves the same manifest.

The schema

The manifest separates the three orthogonal axes of a build: what is built (the [bin.*] / [lib.*] artifacts), where it runs (the [target.*] platforms), and where outputs land (the path templates). Repetition between axes is eliminated by the build engine's cartesian product, never restated in the file. Nothing is inferred: every value resolves from a stanza present in the file.

[project]
id      = "demo"            # required: root of every module path the project exposes
version = "0.1.0"           # required
src     = "src"             # source dir (default "src")
dep     = "dep"             # vendored-dependency dir (default "dep")
target  = "native"          # default target selector (default "native")
out     = "out/{target}/{profile}/bin/{name}{ext}"  # artifact path template
obj     = "out/{target}/{profile}/obj"              # object dir template
ir      = "out/{target}/{profile}/ir"               # IR-dump dir template
asm     = "out/{target}/{profile}/asm"              # ASM-dump dir template
tests   = "out/{target}/{profile}/test/{name}"      # per-test executable template
# optional metadata: name, description, license, authors

[target.linux]             # a platform: a fully-spelled tuple, nothing inferred
isa = "x86_64"
os  = "linux"
abi = "sysv64"

[target.windows]
isa     = "x86_64"
os      = "windows"
abi     = "win64"
ext     = ".exe"           # artifact extension (default "")
libs    = ["kernel32.dll"] # platform link overlay, inherited by every artifact
defines = []               # per-target comptime defines

[os.windows]               # os-component link overlay (any windows target)
libs = ["kernel32.dll"]    # inherited by every windows build, yours and consumers'

[bin.hello]                # an executable artifact, named by its table key
entry = "hello.mach"       # entry source, relative to the src dir (required)

[lib.core]                 # a library artifact
entry = "lib.mach"
kind  = "static"           # "static" (default) | "shared"

[bin.hello.target.windows] # a per-cell exception refining one artifact-target pair
entry = "hello_win.mach"   # overrides the artifact's entry for this target only
libs  = ["user32.dll"]     # appended to the merged link overlay

[profile.debug]            # a build variant
opt = 0                    # 0 (none) | 1 (standard) | 2 (aggressive)

[profile.release]
opt      = 2
emit_ir  = true            # emit per-module IR for this profile (default false)
emit_asm = false           # emit per-module assembly for this profile

[deps.mach-std]            # a dependency: exactly one of git|path, plus ref for git
git = "https://github.com/briar-systems/mach-std"
ref = "v0.4.0"

[project]

Project identity and the path roots everything else resolves against.

KeyReqDefaultMeaning
idyes-Root segment of every module path the project exposes. A file at <src>/foo/bar.mach is reachable as <id>.foo.bar.
versionyes-Project version. Read by the $project.version comptime root.
moduleno-Src-relative path a bare use <id> / fwd <id> resolves to (see Bare project-id imports). Never inferred.
srcno"src"Source root, relative to the project root. Module paths resolve under it.
depno"dep"Vendored-dependency root. Each dep lives at <dep>/<alias>/.
targetno"native"Default target when --target is not passed. native matches the host.
outnoout/{target}/{profile}/bin/{name}{ext}Artifact path template (see Path templates).
objnoout/{target}/{profile}/objPer-module object tree template.
irnoout/{target}/{profile}/irPer-module IR-dump template (used when emission is on).
asmnoout/{target}/{profile}/asmPer-module assembly-dump template.
testsnoout/{target}/{profile}/test/{name}Per-test executable template for mach test.

Optional metadata read by the $project.* comptime roots but not the build: name, description, license, authors.

Targets

Each [target.<name>] declares a selector you pass to --target <name> (or set as [project].target). At least one target must be declared. A target is a fully-spelled platform tuple; nothing is inferred from another key.

KeyReqDefaultMeaning
isayes-Instruction-set architecture. Read by $project.target.arch.
osyes-Operating system. Read by $project.target.os.
abiyes-Application binary interface (e.g. sysv64, win64). Read by $project.target.abi.
ofno(os default)Object format, selected by name (e.g. "raw"). Omit to derive it from the os default (see the recognized values below).
extno""Artifact filename extension expanded by {ext} (e.g. ".exe").
libsno[]Platform link overlay inherited by every artifact built for this target (see Link inputs).
definesno[]Per-target comptime defines. Each is NAME (a true flag) or NAME=VALUE, readable as $mach.build.NAME.
Note

native is a reserved name: declaring [target.native] is an error. It is a selector that resolves to a declared target, not a target itself.

Recognized isa / os / object-format values

ValueAxisStatus
x86_64isaSupported - the only fully working ISA today.
aarch64isaRecognized; an ISA vtable exists, but codegen is not yet validated end-to-end.
riscv64isaRecognized; full RV64 codegen (selection, encoder, relocations) byte-verified against llvm and run under qemu. Cross-compile only - no mach-std riscv64 runtime yet, so no hosted toolchain.
linuxosSupported - the primary host and target.
windowsosSupported as a cross-compilation target (PE/COFF, Win64 ABI).
darwinosRecognized; vtables exist, but the toolchain is not yet validated end-to-end.
freestandingosBare-metal: no syscalls, no OS runtime, a custom _start, and image base 0. Defaults to the raw object format.
elf / coff / machoofContainer object formats for linux / windows / darwin respectively; selected automatically unless of overrides.
rawofFlat-image writer: each segment is laid at its virtual address and emitted with no container, entered at the image base. The default for freestanding.

Artifacts

Every artifact is declared explicitly and named by its table key. A [bin.<name>] links an executable; a [lib.<name>] produces a library. The name drives {name} in the output templates and is read by $bin.name.

KeyAppliesReqDefaultMeaning
entrybin, libyes-Entry source, relative to the project src dir. The entry module's FQN is <id>.<entry without .mach>, with / turned into ..
kindlibno"static""static" leaves the per-module objects as the deliverable; "shared" produces a shared library.
outbin, libnoproject outPer-artifact override of the path template.
libsbin, libno[]Per-artifact link inputs, merged into the link overlay.
definesbin, libno[]Per-artifact comptime defines.

Per-cell exceptions

A [bin.<name>.target.<t>] table refines one artifact for one target. It may override entry and out, and append libs / defines at the highest precedence level. An unset key inherits the artifact's value.

Profiles

A [profile.<name>] is a build variant. The optimization level and the debug-emission toggles live here because they are variant concerns.

KeyDefaultMeaning
opt(debug)Optimization level: 0 (none beyond the always-on pipeline), 1 (standard), or 2 (aggressive). Any other value is a manifest error.
emit_irfalseWrite per-module IR dumps under the ir template for this profile.
emit_asmfalseWrite per-module assembly dumps under the asm template for this profile.

A CLI -O0 / -O1 / -O2 / --release flag overrides the selected profile's opt per invocation, and --emit-ir / --emit-asm / --no-emit-ir / --no-emit-asm override the emission toggles. Absent --profile / --release, the first declared profile is used; with no profile declared, a debug default (no optimization, no emission) applies.

Note

1 and 2 currently select the same pass set; 2 is where future loop and vectorization work lands.

Dependencies

Each [deps.<alias>] names a dependency materialised under <dep>/<alias>/. The build resolves a dependency purely by vendor layout: it reads that directory's own mach.toml for its [project].id and [project].src. A module path whose head matches a dep's id resolves into that dep's tree. A stanza declares exactly one source key.

KeyMeaning
gitGit URL to clone into <dep>/<alias>/.
pathLocal path to another project tree, resolved relative to this manifest; never fetched. mach dep pull materialises it at the vendor location as a relative symlink.
refGit ref to check out (with git): tag/<name>, branch/<name>, a bare tag or branch, or a commit SHA. An absent ref means the remote default branch.

A git dep is pinned to a resolved commit in mach.lock; a path dep has no pinned content, so it carries no lock entry. mach dep performs only plain git operations, so a checkout you also commit as a submodule composes naturally; mach never invokes git submodule. mach dep pull is idempotent: a stale link is replaced, a correct one is left untouched, and a source already at the vendor location is a no-op.

Restriction

A registry-style version = key is reserved and rejected. Cloning, lockfile handling, and transitive resolution are covered in Dependencies.

A libs entry - at the target, artifact, or per-cell level - is either an explicit path (a .o object, a .a archive, or a .so shared library, project-root-relative or absolute) or a bare -l-style name resolved at link time to lib<name>.o / <name>.o / lib<name>.a / <name>.a, then a shared lib<name>.so.

Loose .o objects and static .a archives link statically (an .a contributes every member object); a shared .so is recorded as a dynamic dependency. The overlays merge target < artifact < per-cell, deduplicated by name, and join every mach build / run / test link alongside any CLI -L / -l / object arguments.

OS overlays and cascading

An [os.<name>] overlay scopes a link requirement to a single tuple component - the operating system - rather than a full (isa, os, abi) tuple. A build matches iff its target's os equals <name>.

[os.windows]
libs = ["kernel32.dll"]   # linked into every windows build, this project's and any consumer's

A dependency declares its own link requirements as [target.*].libs (full-tuple) or [os.<name>].libs (os-component) overlays, and a consumer inherits every such lib that matches the build it is producing. So a platform link requirement lives once, in the providing dependency's manifest, and out of every consumer. Matching is by tuple/component equality; target names are local to each manifest.

Restriction

The single-component [isa.<name>] and [abi.<name>] overlays are reserved: declaring either is an error until a real case demands them.

Bare project-id imports

A one-segment use / fwd path equal to a resolvable project id - a dependency's [project].id, or the current project's own id - resolves to that project's declared [project].module. So a library that sets module = "glfw.mach" is imported as use glfw; instead of by the full path to its surface file. Longer paths are unaffected, so this is purely additive.

# in glfw's manifest:
[project]
id     = "glfw"
module = "glfw.mach"   # the surface a bare use glfw; binds
# in a consumer:
use glfw;              # binds the glfw module (glfw's [project].module)

A bare import of a project that declares no module is a resolution error naming the fix. A declared module that names no file is a manifest error at build start, whether or not anything imports it.

Path templates

Output paths come only from the declared templates, expanded over four variables:

Manifest paths are always /-separated and normalized to the host separator at the filesystem boundary, so the same manifest is portable; a literal \ is a hard error. Two artifacts that resolve to the same out path collide and fail at build start.

The tests template expands the same way, except {name} is the per-test name. mach test substitutes it per test as <index>-<sanitized label>: the zero-based collection index keeps the name unique and stable, and every label byte outside [A-Za-z0-9._-] becomes _. A test labeled "parses empty input" collected third lands at out/linux/debug/test/2-parses_empty_input.

The build matrix

A build cell is one artifact times one target times one profile.

native target resolution

native resolves the host's (isa, os) tuple against the declared targets only - never a synthesized tuple. Exactly one host match is chosen; several matching tuples is an ambiguity error naming the candidates; no match warns and falls back to the first declared target, so cross-only projects still build on a foreign host.

Annotated example

The compiler's own manifest builds one binary for two targets, keeping its output paths flat (no {profile} segment) so released paths stay stable.

[project]
id      = "mach"          # module-path root: this project's code is mach.*
name    = "Mach Compiler" # metadata
version = "1.3.0"
src     = "src"           # sources under ./src
dep     = "dep"           # vendored deps under ./dep
target  = "native"        # default target: the host-matching tuple below
out     = "out/{target}/bin/{name}{ext}"  # e.g. out/linux/bin/mach
obj     = "out/{target}/obj"
tests   = "out/{target}/test/{name}"

[target.linux]
isa = "x86_64"
os  = "linux"
abi = "sysv64"

[target.windows]
isa  = "x86_64"
os   = "windows"
abi  = "win64"
ext  = ".exe"            # the windows artifact is out/windows/bin/mach.exe
libs = ["kernel32.dll"]  # the windows platform link requirement

[bin.mach]
entry = "main.mach"      # entry module mach.main, at src/main.mach

[deps.mach-std]
git = "https://github.com/briar-systems/mach-std"
ref = "branch/dev"

mach build . (no --target) selects linux because [project].target = "native" resolves to the host-matching tuple, compiles src/main.mach and its transitive imports - including modules from mach-std vendored at dep/mach-std/ - into objects under out/linux/obj/, and links out/linux/bin/mach. The dependency is realized by mach dep pull from the manifest pin, and the build then resolves it purely by that vendor path: no git at build time.

See also

  • Dependencies - vendoring, mach.lock, and the dep resolver
  • CLI - the mach command-line reference
  • Modules - how files map to module paths
  • Project layout - src, lib.mach, and main.mach