Magnet.toml reference
Magnet.toml is the package manifest, like Cargo.toml. The root package is
every .mag file next to it and in src/. Example:
[package]name = "vehicle"
[dependencies]motor = { path = "../motor" } # a directory: every *.mag inside it or its src/util = { path = "../shared/util.mag" } # a single filepid = { git = "https://git.example.com/org/pid.git", tag = "v1.2" }
[deploy] # optional: what `magnet flash` puts on a boardmodel = "controller::Controller"
[simulate] # optional: the run, when no flag says otherwisedt = 0.0001end = 1.0
[codegen] # optional: the generated crate's module filesmodule-files = "mod-rs"Each table has its own section below. When any command loads the project, each of these is refused by name:
- an unknown table
- an unknown key
- a value of the wrong type
[package]
Section titled “[package]”[package]name = "vehicle"name is the root package’s name, and the generated crate’s. A name starts
with a lowercase letter or _, followed by lowercase letters, digits, _ or
-. These names are refused:
core, the embedded stdlibmagnetite,magnetite-commonandserde, which the generated crate depends onmainandlib, which Rust reserves for a crate root- Rust and magscript keywords
A hyphenated name is written raw in the manifest and on disk but imports with
underscores (use control_lib for a package control-lib). Names that fold to
the same identifier conflict: foo-bar and foo_bar can’t coexist, and a
dependency can’t fold to the root package’s name.
magnet-version is optional and sets the minimum magnet version:
[package]name = "rig"magnet-version = "0.1.0-alpha.21"A pre-release sorts before its release: 0.1.0 satisfies 0.1.0-alpha.11,
not the other way round. An older magnet refuses the package in every command
and in the editor; run magnet self-update.
Without the key, any version works. magnet new writes the current version.
[dependencies]
Section titled “[dependencies]”[dependencies]motor = { path = "../motor" }util = { path = "../shared/util.mag" }pid = { git = "https://git.example.com/org/pid.git", tag = "v1.2" }obs = { git = "git@git.example.com:org/obs.git", rev = "a1b2c3d" }plant = { git = "ssh://git@host/org/plant", branch = "main" }aux = { git = "https://git.example.com/org/aux.git" } # the remote's default branchEach key is a dependency, imported under that name, with - folded to _.
The key is authoritative: any manifest in the dependency’s own directory is not
consulted for its name. A dependency has exactly one source, a path or a
git key, never both and never neither. The package’s lowercase-name rules
apply, and core is reserved.
A path dependency is a directory (every .mag in it and its src/) or a
single .mag file, resolved relative to the manifest:
motor = { path = "../motor" }util = { path = "../shared/util.mag" }A git dependency specifies a repository and at most one revision
selector: tag, rev, or branch. Naming two is refused. With none, the
remote’s default branch is used.
pinned = { git = "https://git.example.com/org/pid.git", tag = "v1.2" }frozen = { git = "git@git.example.com:org/obs.git", rev = "a1b2c3d" }moving = { git = "ssh://git@host/org/plant", branch = "main" }The URL is handed to the system git verbatim, and magnet never
second-guesses its syntax. These forms all work:
- scp-style
ssh://https://file://
Fetching, freezing and re-resolving git sources are covered under
magnet add and
magnet update. The pinning rules are in
Packages and libraries.
[deploy]
Section titled “[deploy]”[deploy] specifies magnet flash behavior. The table is optional, and
a package without it builds, simulates, and syncs exactly as before. Its four
keys are optional, and each of the first three has a
magnet flash flag that stands in for it:
[deploy]model = "controller::Controller" # the model to flash, as a `use` pathchip = "…" # the target as `probe-rs chip list` prints itfirmware = "firmware" # the crate whose binary runs on the boardruntime = "embassy" # who steps the model on the boardruntime = "embassy" has a flash write the model’s embassy glue to
target/codegen/embassy/<package>/ before it builds the firmware, so the
firmware depends on that one crate and steps the model through it. See
A firmware.
firmware is relative to the manifest, and it is your crate: it depends on the
generated one by path, reads its own inputs and calls step. magnet builds it
and flashes what it builds, emitting no runtime of its own for it to run under.
Any other key is refused by name.
flash refuses everything it can before cargo starts, so a flash with the
cable out fails at once rather than after a release build:
- no
chiporfirmwarefrom either place - a firmware directory with no crate in it
- a
modelthe package does not declare - no
probe-rson thePATH - no probe plugged in
A firmware walks through the whole thing.
[simulate]
Section titled “[simulate]”[simulate] specifies the run when nothing else does. Both keys are
seconds, both optional, and both refused unless positive:
[simulate]dt = 0.0001 # the sample timeend = 1.0 # the end time; the tick count is end / dtmagnet simulate takes dt when --dt is
not passed, and end over the sample time when --ticks is not. The editor’s
Run uses the same two numbers, so a run’s length is written once and committed
with the models it runs. The editor’s simulation settings write this table.
A rated entry, a step with @every, fixes its own sample
time and ignores dt; end still counts its ticks, at the rate it declares.
[codegen]
Section titled “[codegen]”[codegen] is optional and specifies the generated crate layout.
[codegen]module-files = "mod-rs" # or "self-named"It decides where a module with child modules writes its own file:
mod-rs, the default, puts it inside the directory of the same name (src/sensors/mod.rs), so the directory is self-contained.self-namedputs it beside the directory assrc/sensors.rs.
A module without children is <name>.rs either way. Module paths are identical
under both, so code that depends on the generated crate is unaffected by the
choice.