Skip to content
Molto

The manifest

Every key Project.toml reads, what it emits on the command line, and the limits it refuses to exceed.

One Project.toml sits at the root of a project. Every command searches the current directory and its ancestors for it, so molto build works from any subdirectory.

The rule the whole file rests on:

Molto discovers your sources; it does not discover your build settings.

Every .c, .cpp and .cc file under src/ and tests/ is compiled automatically — you never list files. Include paths, defines, link libraries and test layout are only what this file says. Nothing is inferred from the directory layout, and there is no fallback to a Makefile.

Relative paths in include and test.sources resolve against the project root, never the directory you ran the command from. flags is passed verbatim by contract and is never rewritten.

What molto new writes

[package]
name = "my_app"
version = "0.1.0"

[target]
std = "c17"
include = ["include"]

[profile.debug]
opt_level = 0
debug_info = true

[profile.release]
opt_level = 3
debug_info = false

That is a complete project. There is no second file and no generator step.

[package]

Key Type Notes
name string Required. snake_case, must start with a lowercase letter
version string Free-form; defaults to 0.0.0
artifact string A hard error. Every project links an executable today

name and version reach every translation unit as MOLTO_PKG_NAME and MOLTO_PKG_VERSION, so a program can report its own version without a header to keep in step.

artifact is refused rather than accepted and ignored: static and shared would need ar, -shared and -fPIC, none of which exists yet.

[target]

Key Type Emits
compiler string Nothing directly — gcc, g++, clang, llvm or msvc. Absent means autodetect
std string -std= for C sources
cpp_std string -std= for C++ sources
requires array Nothing — capabilities that must prove they compile
include array -I, relative to the project root
defines array -D, in the right form per compiler
link array -l, names without the prefix
flags array Verbatim, to both the compiler and the linker

compiler is a vendor preference, not a binary. Naming a vendor rather than a path is what keeps a manifest portable: gcc is version 9 on one machine and 14 on another. Anything outside that list is an error.

std selects the mode; requires states what has to actually work. Accepting -std=c2x is not the same as implementing it — a compiler may take the flag and still reject [[nodiscard]]. Molto asks pickup which local toolchain proves each capability by compiling a program that uses it, and caches the answer in .bin/wsdb. --refresh-toolchain asks again.

Omitting std inherits the compiler’s default, which differs by toolchain and version. Declare it.

For a linker flag that is not a library — -Wl,..., or -pthread at compile time — use flags, not link.

Molto picks the C driver for .c and the C++ driver for .cpp and .cc, both from the same resolved toolchain, so a mixed project never mixes compilers. A C++ project whose toolchain has no C++ driver is reported up front, not discovered at link time.

[test]

Key Type Notes
mode string per_file (default) or single
sources array Extra sources compiled for tests only; directories are walked
defines, include, flags array Applied only when compiling tests/

per_file builds one executable per file under tests/, each linked against the project’s objects minus src/main.c, and each supplying its own main(). Output lands in build/<profile>/tests/<name>.

single links everything into one binary at build/<profile>/tests/<package>_tests. That is what a framework which registers its cases and owns main() needs.

A framework living outside src/ is not compiled at all unless sources names it. This is the most common reason a test build fails on the framework’s own symbols.

[profile.*]

Four profiles exist: debug (the default), release, bench and custom. Select one with molto build --profile release; output goes to build/<profile>/.

Key Type Notes
opt_level integer -O
debug_info bool -g
defines, include, flags array Added on top of [target], never replacing it

Defaults when the table is absent:

Profile opt_level debug_info
debug 0 true
release 3 false
bench 3 false
custom 2 true

Changing any compile setting triggers a recompile: Molto records the exact command per object and rebuilds when it differs.

[env]

[env]
MY_APP_LOG = "debug"

Keys are variable names and values must be strings. They are exported into the compiler and linker invocations, and into the program under molto run and molto test. The variables are set in the child process after forking, so Molto’s own environment is never modified and one project’s [env] cannot leak into another.

Limits

Exceeding one is a manifest error, never a silent truncation — dropping a flag would produce a green build using options you did not ask for.

Limit
Entries in defines / include / flags / requires / test.sources 16
Length of one such entry 95 characters
Entries in link, and the length of one 32 / 63 characters
Entries in [env] 32
[env] name / value length 63 / 255 characters

Unknown keys are dropped without warning

A typo in a key name simply disappears. If a setting seems to have no effect, check its spelling first.

The manifest tolerates unknown keys because RFC-0003 specifies tables this binary does not implement yet, and refusing them would reject manifests that are valid by design.

The exception is [deps] and [dev-deps], which fail closed: an unknown key, two sources or a version range is an error naming the dependency. So do format.json and linter.json. A dependency read wrong is a dependency that silently is not there.

Specified but not implemented

Table or key What happens today
package.artifact Hard error
dep.recipe, artifact, optional, features Refused, not ignored
[features], [build-deps], [workspace] Not read
target.triple, per-OS tables Not read
profile.lto, strip, sanitizers, warnings_as_errors Not read

Coming from a Makefile

Read CFLAGS and LDFLAGS, and put each piece under the key that owns it. Against a Makefile passing -Iinclude -D_DEFAULT_SOURCE -Wall -Wextra -Wpedantic and building its tests as one binary:

[target]
std      = "c2x"
requires = ["attr_nodiscard"]
defines  = ["_DEFAULT_SOURCE"]
include  = ["include"]
flags    = ["-Wall", "-Wextra", "-Wpedantic"]

[test]
mode    = "single"
sources = ["modules/moltest/src"]
include = ["modules/moltest/include"]

That is Molto’s own manifest, minus the profiles: the repository builds itself with its own tool.

Troubleshooting

Symptom Cause Fix
fatal error: 'pkg/foo.h' file not found No include path declared include = ["include"]
Tests fail to link, missing or duplicate main The framework owns main() mode = "single"
Tests fail on the framework’s own headers It lives outside src/ test.sources and test.include
Expected warnings missing flags not declared flags = ["-Wall", "-Wextra"]
An #ifdef never fires defines not declared Add it to [target].defines
undefined reference to sqrt, pthread_create Library not linked link = ["m", "pthread"]
unknown compiler '…' compiler names a binary Use a vendor, or drop the key
A key seems to do nothing Typo, or not implemented Check it against the tables above
not inside a molto workspace No manifest here or above molto init