Command-line reference
The axle binary is the whole toolchain. This page is the surface: one
section per subcommand, then the flags that cut across them.
axle new <NAME> [--lib | --workspace]
axle build [SOURCE] [-O LEVEL] [-o PATH] [--emit KIND]
axle run [SOURCE] [-O LEVEL] [-- ARGS]
axle check [SOURCE] [--target TRIPLE]
axle ports [SOURCE] [--target TRIPLE]
axle fmt [FILES...] [--check]
axle bench <SOURCE> [-O LEVEL]
axle profile [SOURCE] [-O LEVEL] [-- ARGS] SOURCE is a .axle file or a directory holding an axle.toml. Omit it
and build, run, check and ports use the current directory — Projects and dependencies covers what a
project is.
new
axle new hello # hello/axle.toml + hello/src/main.axle
axle new mathx --lib # hello/src/lib.axle instead — a library crate
axle new studio --workspace # a root manifest, app/ (bin) and core/ (lib) --lib and --workspace are mutually exclusive. --workspace writes a
root axle.toml listing the members, an app binary and a core library that app depends on — a working cross-crate build out of the
box. Build it from app/, which is where the binary crate lives.
build
Compiles and links. A project build writes target/<name> (<name>.exe on Windows), where <name> is [package] name; a single-file build
writes the artefact beside you, named after the file’s stem. -o overrides
either, and --emit swaps the extension — target/<name>.ll, <stem>.o,
and so on.
axle build # the project in the current directory
axle build hello # … or at a path
axle build hello.axle # a single file
axle build -O 3 -o hello # optimised, named output
axle build --emit llvm # stop after the LLVM pass pipeline, print the IR
axle build --incremental # skip codegen + link when nothing moved | Flag | Effect |
|---|---|
-o, --output PATH | where the artefact goes |
-O LEVEL | optimisation level (0…3, below) |
--optimize | legacy bool shortcut for -O 2; an explicit -O beside it still wins |
--emit KIND | stop at an intermediate stage (below) |
--incremental | skip codegen and the link on a cache hit |
--link-lib LIB | link a native library |
--lib-path DIR | add a directory to the native-library search path |
--target TRIPLE | cross-compile |
--target-cpu CPU | tune for a named CPU (znver3, skylake, …) |
--target-features F | +avx2,+fma |
--pie | force a position-independent executable |
--codegen-units N | how many object files to build from, concurrently; the default follows the program’s size and the machine’s cores, and N is a ceiling (lowered to 8, and to the number of function bodies there are to split) |
--alloc-stats | print the program’s allocation tally to its stderr |
--timings | per-phase timings on stderr |
--features NAMES | turn features on in the root crate |
-v, --verbose | per-phase detail |
--codegen-units 1 is the single-module reference. A non-binary --emit uses one unit regardless, because an intermediate artefact is a
whole-program artefact by definition.
--alloc-stats instruments the program rather than the compiler: each
allocation costs a call and two atomics, so measure time without it.
run
axle run # build the project in the current directory, then run it
axle run hello.axle -- a b # run one file, passing a and b to the program A project directory builds a native binary and executes it. A single .axle file runs through the LLVM JIT instead, which means lli must be on your PATH and must be the LLVM your compiler was built
against — the IR handed to it is that version’s. When in doubt, axle build hello.axle && ./hello needs no JIT.
run takes -O, --link-lib, --lib-path, --pie, --features and
the -- ARGS tail. It never uses the build cache: a binary that is about
to be executed is always built fresh.
check
axle check # analyse the project, generate nothing
axle check --target x86_64-unknown-linux-gnu Lexes, parses and runs sema — no codegen, no link. --target selects
which port is active, so this is how the half of your program written for
another platform is found broken without that platform’s toolchain. It
takes --target and --features.
ports
axle ports # which ports the project declares, and what each owes Prints one row per module path that crosses a port boundary, one column per port, and the file to write for each hole. It is the same reading the build refuses on, so a listing that shows a port complete is a tree the build agrees about. See code that differs by operating system.
fmt
axle fmt src/ # format in place
axle fmt --check src/ # list what would change, exit non-zero, write nothing Layout comes from the [fmt] table of axle.toml when there is one —
see the manifest reference — and from the formatter’s
defaults otherwise.
bench
axle bench tests/bench/x.axle
axle bench x.axle --compare cpp,rust --format json --output bench.json Runs the program repeatedly and reports timings.
| Flag | Default | Effect |
|---|---|---|
-O LEVEL | sweeps 0…3 | a single level instead of the sweep |
--runs N | 10 | timed iterations |
--warmup N | 3 | untimed iterations first; their average calibrates --min-seconds |
--min-seconds S | — | size the run count from the warmup timings so the measured total stays near this budget, --runs becoming the floor |
--compare cpp,rust,… | — | build and run other-language baselines from <stem>.cpp / <stem>.rs next to the input |
--format text\|json | text | report format |
--output PATH | — | write the report to a file (--format json; stdout otherwise) |
bench is the one subcommand whose absent -O means something other than
a default: it sweeps 0, 1, 2 and 3 in a single invocation.
profile
axle profile game.axle --top 20
axle profile game.axle -- --snap Runs a program and reports where its time and memory went, as two
flamegraphs — <name>.cpu.svg and <name>.ram.svg — plus the heaviest
functions of each with their file and line.
The default instruments the build: the compiler emits entry and exit
hooks, so every frame is named, including the ones the optimiser inlined.
That is also why the result moves with -O: a function that no longer
costs a call has its share attributed to where the work ended up. --sample asks the OS instead (perf on Linux, an ETW session on Windows) — no
instrumentation overhead, but only frames that still have a symbol.
| Flag | Default | Effect |
|---|---|---|
-O LEVEL | 3 | optimisation level to build at — a profile of unoptimised code tells you about the compiler, not the program |
-o, --output PATH | the program’s own name | base path for the flamegraphs: -o report.svg writes report.cpu.svg and report.ram.svg |
--timeout SECS | 30 | how long the program may run before it is asked to stop |
--top N | 15 | how many functions the hot list carries |
--frequency HZ | 999 | sampling frequency, with --sample only |
--sample | — | ask the OS to sample instead of instrumenting the build |
--features NAMES | — | features to enable |
-- ARGS | — | passed to the program after -- |
A program with a main loop is ended by --timeout or by Ctrl-C, and the
report is written either way — that is how a game is profiled, not a
failure of the run.
-O LEVEL
| Level | What runs |
|---|---|
0 | nothing — neither the HIR transforms nor the LLVM pipeline |
1 | every HIR transform, no LLVM pipeline |
2 | … plus LLVM’s own pipeline |
3 | … plus LLVM’s more aggressive passes |
axle build -O 0 # is an optimisation responsible for this? 0 is the level to build at when the question is whether an optimisation
changed behaviour: it turns off both halves, so a difference between -O 0 and -O 3 is a difference the optimiser made. The default is 1 for build and run, and 3 for profile.
--emit KIND
Stops the build at an intermediate stage and writes that instead of a binary.
| Kind | What you get |
|---|---|
ast | the parsed syntax tree |
hir | Axle’s typed IR, after the sema transforms |
llvm | textual LLVM IR after the LLVM pass pipeline |
llvm-raw | the IR exactly as codegen emitted it, before any LLVM pass |
bitcode | LLVM bitcode |
asm | target assembly |
object | a relocatable object file |
header | a C header declaring the program’s extern "C" exports |
binary | the default — a linked executable |
llvm-raw against llvm is the pair to reach for when deciding whether
an oddity is Axle’s or LLVM’s.
Exit codes
0 on success. 1 on any user error, with the diagnostics rendered
against the source; an internal failure prints internal compiler error: … rather than a stack trace.
See also
- Projects and dependencies —
axle.toml, local dependencies, ports - The manifest — every table and key of
axle.toml - Install the compiler — putting
axleand LLVM onPATH - Reading compiler errors — what a diagnostic says