dowel.toml referenceEvery table and key the implementation reads, and what happens when it is missing or wrong.
A top-level table that dowel.toml does not read is unknown-table. When
its name belongs to dowel.build’s vocabulary the diagnostic says so —
[runner.<triple>] sits one table away from [toolchain.<triple>] and is
easy to write into the wrong file, and the resulting missing-runner would
otherwise insist a declaration is absent while the reader is looking at it
(issue #74). [policy] stays accepted: it is reserved and documented as not
yet acted on. Unknown keys inside [package] are still ignored (edition
and [toolchain] sysroot from the design examples are reserved the same
way); [toolchain] is the exception, where a misspelled key would silently
fall back to a default.
dowel.toml must stay strict TOML: function calls, match, postfix when,
and configuration references are rejected in value position with
expression-in-strict-toml. Anything that needs an expression belongs in
dowel.build.
[package][package]
name = "libfoo"
version = "0.3.1"
| Key | Type | Required | Behavior |
|---|---|---|---|
name |
string | yes | the package name, used in target references (<package>:<target>) and diagnostics. Missing: missing-field. Default while erroring: the directory name |
version |
string | no | recorded; not yet used for resolution. Default 0.0.0 |
targets |
list of strings | no | the target triples this package is for. When declared, any other triple — the host included — is refused with unsupported-target before building. Undeclared (the default) means the package builds for any triple. This is deliberately separate from [toolchain.<triple>]: a package that builds for the host but swaps tools when cross-compiling declares toolchains without narrowing its targets |
A missing [package] table is missing-table. A dowel.toml whose
directory has no dowel.build defines no targets but can still be depended
on for its metadata (in practice every package has both).
[toolchain] and [toolchain.<triple>][toolchain] # applies to host builds
c = "clang-19"
cxx = "clang++-19"
[toolchain.aarch64-unknown-linux-gnu] # applies to --target=aarch64-unknown-linux-gnu
c = "aarch64-linux-gnu-gcc"
cxx = "aarch64-linux-gnu-g++"
| Key | Type | Behavior |
|---|---|---|
c |
string | the C compiler command, default cc for host builds. It must be on PATH at plan time (a value containing a path separator is probed as a path) — toolchain fetching is not implemented. Missing from PATH: missing-toolchain. Required in [toolchain.<triple>]: missing there is missing-field |
cxx |
string | the C++ compiler command, default c++ for host builds. Required — and probed — only when the build contains C++ sources. Missing from PATH: missing-toolchain |
ar |
string | the archiver command, default ar. Required — and probed — only when the build produces a static library. Cross builds should declare it alongside c / cxx so archives are not created by the host’s tool. Missing from PATH: missing-toolchain |
objcopy |
string | the object copier, default objcopy. Used by [<kind>.<name>.artifacts] to derive files from an artifact (12-build-reference.md); probed only when such a declaration exists. Missing from PATH: missing-toolchain |
size nm objdump readelf |
string | reporting tools, each defaulting to its own name. Used by [<kind>.<name>.inspect] (12-build-reference.md). An inspection is not part of the build graph, so these are not probed at plan time; dowel inspect reports a tool it cannot start |
Any other key is unknown-property, with a suggestion — a misspelled tool
would otherwise silently fall back to its default, which for a cross
archiver means the host’s ar quietly builds the archives.
The toolchain is selected by the target triple, the same way
[runner.<triple>] is (issue #42). The plain [toolchain] table is the
declaration for host builds; it never applies to another triple. Passing
--target=<triple> for a triple with no [toolchain.<triple>] declaration
is refused before building with missing-toolchain — building with the
host compiler would silently place host artifacts under that triple’s name,
and the mistake would only surface later (a runner’s
Invalid ELF image for this architecture, or a debugger showing the wrong
architecture). Likewise, a cross build whose sources contain C++ requires
cxx in the triple’s table; falling back to the host c++ is refused.
If a dependency package declares a toolchain different from the one the
build uses, planning warns with toolchain-mismatch — ABI checking assumes
a single pinned toolchain per build. Only declarations that apply to the
current target triple participate in this comparison.
[[dependencies]][[dependencies]]
name = "libgreet"
path = "../libgreet"
[[dependencies]]
name = "bar"
git = "https://github.com/example/bar"
rev = "9f3c0a1e2b7d4856c0f1a93e5d2b8c4770ae6135"
[[dependencies]]
name = "zlib"
version = "1.3" # resolved via the system pkg-config
optional = true
Each [[dependencies]] entry declares one package this package may use.
Declaring it here creates no edge by itself — a target must also reference
it with dep("name") in dowel.build (12-build-reference.md).
| Key | Type | Behavior |
|---|---|---|
name |
string | required. Missing: missing-field. The name used by dep("...") and, for optional dependencies, by the feature flag that activates them |
path |
string | a directory containing another dowel package, relative to this dowel.toml. The path must exist and contain a manifest (missing-manifest otherwise) |
git |
string | a git URL (anything git itself accepts, including local paths). Requires rev. Fetched once into .dowel/deps/<name>-<rev12>/; later runs never touch the network. A failing fetch is unfetchable-dependency |
rev |
string | required with git: a full 40-digit commit sha. Branches, tags, and abbreviated shas are refused with unpinned-dependency — a name-only reference does not count as pinned. Because the rev pins the content exactly, git dependencies need no lock file |
version |
string | a system package, resolved through pkg-config (ADR-0015). name is the pkg-config module name; the version is a minimum (--atleast-version). --cflags / --libs become the dependency’s public flags and link flags. Absent module, too-low version, or missing pkg-config: unsatisfied-dependency. Resolutions are recorded in dowel.lock (below) |
optional |
bool | default false. An optional dependency participates only when a feature flag with the same name is enabled. When inactive, neither the edge nor the node exists — the package is not even loaded |
when |
inline table | reserved for conditional dependencies (when = { os = "windows" }). Parsed, but not yet honored — the dependency is treated as unconditional |
A dependency has exactly one source. An entry with none of path /
git / version is incomplete-dependency; an entry with two or more is
conflicting-dependency-source, naming each one (issue #79). Accepting two
would mean one of the declarations is never read, and the manifest would not
say which — the source of a library is switched during development (path
while it is being edited, git or version once it is published), and
leaving the old key behind still builds for whoever has the tree.
[features][features]
default = ["zlib"]
zlib = []
png = ["zlib", "libpng/simd"] # also enables `simd` in the dependency
Each key declares a feature flag; its value is the list of other features it
enables (transitively closed, cycle-safe). Values must be arrays of strings
(type-mismatch otherwise).
A feature belongs to the package that declares it
(ADR-0017). Two packages may use the same
feature name for unrelated things, and enabling one never enables the other.
A value of the form dep/feat forwards: it enables feat in the
dependency dep rather than becoming a feature of this package.
dep must be declared in [[dependencies]] (undeclared-dependency
otherwise) and feat must be declared in that dependency’s [features]
(unknown-feature, reported at the forwarding site — an unforwarded typo
would just evaluate to false in the dependency, indistinguishable from a
feature deliberately left off).
default is special: it is included unless --no-default-features is
passed. default itself is never a feature name--features on the command line or from a feature.<name> reference
in dowel.buildoptional dependency activates that
dependencydowel.build, features
are read as feature.<name> in when conditions
(12-build-reference.md)dowel.lockpath dependencies are local content and git dependencies are pinned by
their rev, so neither needs locking. version dependencies resolve against
whatever the system has, so each resolution is recorded in dowel.lock at
the workspace root (ADR-0015):
[[package]]
name = "zlib"
version = "1.3.1"
source = "pkg-config"
lockfile-drift and the
file is never rewritten silently — delete the entry (or the file) to
accept the new resolutionThe lock detects drift; it does not restore anything. A system package cannot be fetched, so the promise is “you will notice a changed environment”, not “you will get the same bits”.
dowel.buildversion constraint is a lower bound only;
comparison is delegated to pkg-config itself