Environment variables
This page lists every runtime CABIN_* environment variable
Cabin reads, the build-time CABIN_* metadata variables that
affect cabin version -v, the small fixed set Cabin sets on a
child process (cabin run / cabin test), and the precedence
chain for each.
The single source of truth for runtime and child-process names
lives in the
cabin-env
crate as pub const ... : &str = ... constants. Build-time
version metadata is owned by cabin/build.rs because those
values are embedded while compiling the CLI, not read from the
user's environment when cabin runs.
Read-side variables
| Name | Default | Meaning |
|---|---|---|
CABIN_CONFIG |
unset | Path to one explicit config file. Disables the normal config-discovery walk. |
CABIN_CONFIG_HOME |
xdg-resolved user config home with cabin prefix |
Override for the per-user config home. Used verbatim (no extra cabin segment). When unset, Cabin resolves the user config home via the xdg crate (typically $XDG_CONFIG_HOME/cabin or $HOME/.config/cabin). |
CABIN_NO_CONFIG |
unset | When truthy, no config files load at all |
CABIN_BUILD_DIR |
build |
Build output directory |
CABIN_CACHE_DIR |
unset | Artifact cache directory for this invocation. Wins over CABIN_CACHE_HOME and the XDG fallback. |
CABIN_CACHE_HOME |
xdg-resolved user cache home with cabin prefix |
Per-user cache home (the directory the global cache lives under). Used verbatim (no extra cabin segment). When unset, Cabin resolves the user cache home via the xdg crate (typically $XDG_CACHE_HOME/cabin or $HOME/.cache/cabin). |
CABIN_NET_OFFLINE |
unset | Forbid network access this invocation |
CABIN_COMPILER_WRAPPER |
unset | ccache / sccache / unset |
CABIN_TERM_COLOR |
unset | Terminal-color choice (auto / always / never) |
CABIN_TERM_VERBOSE |
unset | Enable verbose Cabin-owned status output when truthy |
CABIN_TERM_QUIET |
unset | Suppress Cabin-owned status output when truthy |
CABIN_FMT |
unset | Override for the clang-format executable cabin fmt spawns |
CABIN_TIDY |
unset | Override for the run-clang-tidy executable cabin tidy spawns |
CABIN_PKG_CONFIG |
unset | Override for the pkg-config executable Cabin spawns when probing `system = true deps` |
CABIN_BUILD_JOBS |
unset | Number of parallel jobs the build backend should use |
CPPFLAGS |
unset | Conventional preprocessor flags appended to both C and C++ compile commands |
CFLAGS |
unset | Conventional flags appended only to C compile commands |
CXXFLAGS |
unset | Conventional flags appended only to C++ compile commands |
LDFLAGS |
unset | Conventional flags appended only to link commands |
Build-time version metadata
These variables are consumed while compiling cabin and
are embedded into the binary's verbose version output. They are
not runtime controls: setting them in the shell when invoking
cabin version -v has no effect on an already-built binary.
| Name | Meaning |
|---|---|
CABIN_BUILD_COMMIT |
Full git commit hash to embed in verbose version output |
CABIN_BUILD_COMMIT_DATE |
Commit date to embed in verbose version output |
CABIN_BUILD_HOST |
Host triple to embed in verbose version output |
Precedence
Read-side settings use a consistent shape when they have more than one source:
- CLI flag (e.g.
--build-dir,--offline) - Environment variable (e.g.
CABIN_BUILD_DIR=…) - Config file (e.g.
[paths] build-dir = …) - Built-in default (e.g.
build)
Not every environment variable has all four layers. For example,
CPPFLAGS / CFLAGS / CXXFLAGS / LDFLAGS have no Cabin CLI
or config equivalent, and tool executable overrides such as
CABIN_FMT are environment-only. Where a setting does have a
config or CLI counterpart, precedence labels are surfaced through
cabin metadata so users can audit which layer supplied each
effective value.
Truthy / falsy spellings
For boolean env vars (CABIN_NET_OFFLINE, CABIN_NO_CONFIG,
CABIN_TERM_VERBOSE, CABIN_TERM_QUIET, …) Cabin recognizes:
- truthy:
1,true,yes,on(case-insensitive) - falsy: empty string,
0,false,no,off
Anything else is an error.
Terminal color (CABIN_TERM_COLOR and --color)
Cabin emits ANSI color in human-readable diagnostic output. The choice follows the same precedence chain as every other read-side variable:
--color <when>on the command line.CABIN_TERM_COLOR=<when>environment variable.[term] color = "<when>"in a config file.- Default:
auto.
<when> is one of:
| Value | Meaning |
|---|---|
auto |
Emit color only when stderr is a terminal and the environment does not opt out (e.g. NO_COLOR). |
always |
Always emit color, even when output is redirected. |
never |
Never emit color, regardless of terminal detection. |
Invalid values produce a clear error. Example:
$ CABIN_TERM_COLOR=sometimes cabin build
error: invalid CABIN_TERM_COLOR value 'sometimes'; expected one of: auto, always, never
Machine-readable output (cabin metadata --format json,
cabin tree --format json, etc.) is always emitted without
color regardless of the selected mode, so piping into jq
stays deterministic.
Terminal verbosity (CABIN_TERM_VERBOSE, CABIN_TERM_QUIET, -v, and -q)
Cabin-owned status output follows this precedence chain:
-q/--quietor-v/--verboseon the command line.CABIN_TERM_QUIET=<bool>orCABIN_TERM_VERBOSE=<bool>.[term] quiet = <bool>or[term] verbose = <bool>in a config file.- Default: normal status output.
The two environment variables use the truthy / falsy spellings
listed above. If both are truthy in the same invocation, Cabin
rejects the conflict instead of guessing which level the user
intended. CLI flags still take precedence, so cabin -q build
does not inspect a conflicting CABIN_TERM_VERBOSE.
Build jobs (CABIN_BUILD_JOBS and --jobs)
Cabin lets the user cap the number of parallel jobs the build backend runs. The choice follows the standard precedence chain:
-j/--jobs <N>on the command line (supported bycabin build,cabin run, andcabin tidy).CABIN_BUILD_JOBS=<N>environment variable.[build] jobs = <N>in a config file.- Default — the build backend's own default (Ninja picks a value derived from the host's CPU count).
<N> must be a positive integer. Cabin rejects 0,
negatives, and non-numeric values with a clear error before
spawning anything:
$ cabin build --jobs 0
error: invalid value '0' for '--jobs <N>': expected a positive integer, got 0
$ CABIN_BUILD_JOBS=many cabin build
error: invalid CABIN_BUILD_JOBS value "many": invalid jobs value "many"; expected a positive integer
Cabin passes the resolved value to Ninja as -jN and does not
otherwise modify the parallelism story for compilers or
linkers. cabin test does not expose --jobs: the test
runner is sequential, and CABIN_BUILD_JOBS is ignored when
cabin test invokes Ninja for the build phase.
cabin tidy honors the same precedence chain and forwards the
resolved value to run-clang-tidy as -j N. In --fix mode
the effective parallelism is clamped to 1 so concurrent
clang-tidy instances cannot race while applying overlapping
fixes; verbose mode reports the override when a higher count
was requested.
CPPFLAGS / CFLAGS / CXXFLAGS / LDFLAGS
Cabin honors the conventional C / C++ build-flag environment
variables alongside its typed manifest [profile] fields. Each
variable is read at command start, parsed once, and merged
into the per-package compile / link command-lines.
| Variable | Routes to | Reaches |
|---|---|---|
CPPFLAGS |
language-neutral compile bucket | every C and every C++ compile command |
CFLAGS |
C-only compile bucket | every C compile command |
CXXFLAGS |
C++-only compile bucket | every C++ compile command |
LDFLAGS |
link bucket | every link command |
Cabin keeps the four buckets strictly separated. A CFLAGS
token never reaches a C++ compile line, a CXXFLAGS token
never reaches a C compile line, and neither reaches the link
command. CPPFLAGS is preprocessor-shaped so it lands on both
compile languages, but is never treated as a linker flag.
Parsing
Each variable's value is split into argv tokens using POSIX
shell-style word splitting via the [shlex] crate. No shell
process is invoked. Quoted runs ('…' and "…"), backslash
escapes (\<char>), and whitespace separation all behave as a
POSIX shell would when reading a single command line.
Two adjustments make the parser fit the flag-env-var role rather than a shell command line:
- an unquoted
#is preserved as a literal character (it does not start a comment), so a value likeCFLAGS="-DFOO=1 #r1 -O2"reaches the compiler with every token intact; \routside quotes is treated as a token separator, so CRLF-contaminated values from Windows-formatted tooling do not carry a stray\rinto an argument.
Empty and whitespace-only variables are no-ops. Malformed shell words — for example, an unterminated quote or a trailing backslash — are rejected with a clear error that names the offending variable:
$ CXXFLAGS="'oops" cabin build
error: invalid CXXFLAGS: could not parse shell words
$ LDFLAGS='-L/lib\' cabin build
error: invalid LDFLAGS: could not parse shell words
Layer order
Environment flags merge with Cabin's existing build-flag sources in this documented order (later layers append to earlier ones; nothing replaces or erases an earlier layer):
- Built-in backend defaults (
-std=c11/-std=c++17, the profile's-O…/-g, …). - The package's own
[profile]table. - The package's matching
[target.'cfg(...)'.profile]blocks. - The selected profile's
[profile.<name>]block. pkg-configcontributions from`system = truedeps`.CPPFLAGS,CFLAGS,CXXFLAGS,LDFLAGS— the environment layer described above.
Environment flags only contribute to primary packages —
the workspace members the user owns. Registry and path
dependencies still observe their own [profile] declarations
but are not augmented by the user's environment, so a
-Werror in CXXFLAGS cannot break a third-party dep.
Commands and reproducibility
cabin build, cabin run, cabin test, and cabin tidy read
the four variables and pass the parsed tokens through to the
generated build.ninja and compile_commands.json. The build
configuration fingerprint folds in the per-bucket values, so
changing a relevant variable moves the fingerprint exactly the
way changing the matching [profile] field would; Ninja
rebuilds are driven by the changed command lines.
cabin fmt, cabin clean, cabin new, and cabin init do
not participate. These commands either do not invoke the
C / C++ build at all (fmt, new, init) or only touch the
on-disk build directory (clean), so the four environment
variables have no effect on them.
Output policy
- Quiet and normal modes do not print the env flags.
- Verbose mode prints one line per active variable on stderr
with arg counts only (e.g.
cabin: applying CPPFLAGS (2 args)). - Very-verbose mode prints the parsed argv tokens on stderr. Be careful: environment flag values can contain local include paths or tokens — treat the very-verbose log as command-line output.
- Machine-readable stdout (
cabin metadata --format json,cabin tree --format json, …) stays clean: all chatter routes to stderr. The metadata JSON view reflects the applied flags through the per-packagetoolchain.build_flags_per_packageblock.
LD is not honored
The conventional LD environment variable selects a linker
binary. Cabin does not honor LD — linker selection would
require a linker-command abstraction the C++ backend does not
expose. Use LDFLAGS to extend the link command line
(via the C / C++ driver Cabin already picked); the driver is
what controls whether the C++ runtime is pulled in.
Variables Cabin sets for cabin run and cabin test
cabin run spawns the selected executable, and cabin
test spawns each test executable, with the user's
environment plus a small, deterministic, identical
CABIN_* package-execution overlay:
| Name | Meaning |
|---|---|
CABIN_MANIFEST_DIR |
Absolute path to the owning package's manifest directory |
CABIN_MANIFEST_PATH |
Absolute path to the owning package's cabin.toml |
CABIN_PACKAGE_NAME |
Package name as the manifest declares it |
CABIN_PACKAGE_VERSION |
Resolved package version |
CABIN_PROFILE |
Active profile (dev, release, …) |
CABIN_BUILD_DIR |
Resolved build directory |
This is the entire injected contract: the overlay is the same
for cabin run and cabin test and does not depend on the
target's name or kind. The user's PATH, LANG, etc. are
inherited unchanged. cabin run's working directory is the
user's invoking cwd (matching cargo run); cabin test runs
each executable in its owning package's manifest directory so
tests can reach repository-relative fixture data
deterministically.
Why Cabin does not auto-inject -DCABIN_PACKAGE_* macros
Cargo's CARGO_PKG_* env vars are compile-time constants in
Rust because env!() reads them during compilation. C/C++
has no equivalent: turning package metadata into compiler
-D flags would change every translation unit's preprocessor
state, leak into public headers if surfaced via
usage-requirements, and churn every affected object file
whenever the version bumps.
The default is therefore: run/test executables receive the
metadata as env vars; compile commands do not receive
-DCABIN_PACKAGE_* macros. Users who genuinely want the
macro form can add explicit defines to their target in the
manifest.
See also
cargo-inspired-interface.md— full Cabin-vs-Cargo audit.config.md— config-file precedence and[paths] build-dirsyntax.toolchains.md— toolchain inputs that participate in the build-configuration fingerprint.