cargo-rake
A configuration-driven build tool that runs named targets declared in a
Rakefile.toml, in dependency order. It ships as both a cargo subcommand
(cargo rake) and a standalone rake binary.
Installation
The two binaries are distributed through different channels:
cargo-rake(thecargo rakesubcommand) — through cargo.rake(the standalone binary) — through system package managers.
cargo (cargo-rake)
Arch Linux (rake)
Available from the AUR in four mutually-exclusive flavors — pre-built or
built-from-source, each in a stable and a nightly unstable-feature variant:
Debian/Ubuntu (rake)
From the apt repository (recommended)
The signed apt repository at https://rustyhorde.github.io/cargo-rake-packages/
tracks every release, so apt upgrade keeps rake current. Packages are
available for amd64 and arm64:
# Add the repository signing key
|
# Add the apt source
|
# Install
The same repository also carries the rake-unstable build (the unstable
feature is a functional no-op — it only toggles nightly-only lint gates).
rake-unstable conflicts with rake, so only one can be installed at a time.
From a downloaded .deb
Pre-built .deb packages are also attached to each
GitHub release if you prefer
not to add the repository. dpkg -i runs as root and works from any location:
Fedora/RHEL (rake)
From the dnf repository (recommended)
Pre-built .rpm packages for x86_64 and aarch64 are served from the signed
dnf repository at https://rustyhorde.github.io/cargo-rake-packages/, so
dnf upgrade keeps rake current:
# Add the repository (imports the signing key on first install)
# Install
On older releases the subcommand is
sudo dnf config-manager addrepo --from-repofile=…, and on dnf 4 you may needsudo dnf install dnf-plugins-corefirst.
The rake-unstable build is available from the same repository by name; it
conflicts with rake, so only one can be installed at a time.
From a downloaded .rpm
.rpm files are also attached to each
GitHub release for direct
installation:
macOS (rake)
Pre-compiled binaries for Apple Silicon (aarch64) are published to the
rustyhorde/rake Homebrew tap.
Intel Macs are not currently supported.
Rakefile.toml
A Rakefile.toml declares named targets. Each target owns an ordered array
of named commands, an optional depends_on list, and an optional tools
list. Tools are defined once in a top-level [tool] table. A complete example:
# toolchain = "stable" # optional; pins both binaries to a specific Rust channel via rustup
# Cargo-installable tools (cargo subcommands, etc.)
[]
= "cargo-nextest" # crates.io name (required for update checks)
= ["cargo", "nextest", "--version"] # probe: zero exit = installed
= ["cargo", "install", "cargo-nextest", "--force", "--locked"]
= true # re-install when a newer crates.io version exists
# OS-level tools that rake cannot install (docker, pkg-config, …)
[]
= ["docker", "--version"]
= "install Docker: https://docs.docker.com/get-docker/" # shown when absent and no install
# ── Targets ──────────────────────────────────────────────────────────────────
[[]]
= "compile" # label shown in `list` output and error messages (required)
= ["cargo", "build"] # program + args, spawned directly — no shell involved
[]
= ["build"] # run these targets first, in order
= ["nextest"] # ensure these tools before the target's commands run
[[]]
= "run"
= ["cargo", "nextest", "run"]
[[]]
= "fmt"
= ["cargo", "fmt", "--check"]
= true # tolerate a non-zero exit and keep going
[]
= ["docker"]
[[]]
= "archive"
= ["linux", "macos"] # skipped silently on other platforms
= "tar czf dist.tgz \"$(pwd)\"/target/release/*"
= "tar czf dist.tgz (pwd)/target/release/*"
[[]]
= "zip"
= ["windows"]
= "Compress-Archive target/release/* dist.zip"
[]
= ["build", "test", "check", "package"] # depends-only aggregator
[]
= ["test"]
- A command sets one kind of body: either
cmd, or one or more shell variants (sh/fish/ps).cmdis mutually exclusive with the shell variants; the shell variants may coexist.cmdis a program followed by its arguments, spawned directly — no shell is involved — so it behaves the same on every platform.sh/fish/psare each a single command line run through that shell, so shell features ($(...)substitution,~/$VARexpansion, globs, pipes) apply:sh -c,fish -c, and PowerShell-Command(pwshif onPATH, elsepowershell) respectively. rake resolves the current shell in priority order: (1) a PowerShell environment variable (POWERSHELL_DISTRIBUTION_CHANNELorPSModulePathon non-Windows) — checked first because PowerShell does not set$SHELL; (2)$SHELL's basename; (3) the platform default (pson Windows, Posix otherwise). Selection is strict: if the detected shell has no matching variant, the run aborts with an error — so define a variant for every shell a command must run under. Aplatform/archmismatch, by contrast, is a silent skip, not an error.
[[target.<t>.command]]is a TOML array of tables. Each entry needs aname(a label used inlistoutput and error messages) and a body (cmdor one or more ofsh/fish/ps). Commands run in array (declaration) order. (TOML table headers are absolute, so thetarget.<t>.commandprefix is required on each entry.)skip_on_error(per command, defaultfalse): whentrue, a non-zero exit from that command is tolerated and the target continues with its remaining commands instead of aborting.depends_onlists other targets that must run, in order, before this one.toolslists external tools (by name) the target needs; see Tools.platform(optional list) names OS or family tokens (linux/macos/windows/unix/…);archnames architecture tokens (x86_64/aarch64/…). A command runs only when every declared dimension matches (AND across dimensions, OR within each list). A non-matching command is silently skipped — aSkippedstatus line is printed and execution continues. Both lists are validated at parse time; an empty list or an unknown token is a hard error.- Skipping targets: prefix a target name with
^to exclude it from the run (e.g.rake all ^clean). The skipped target and any dependency reachable only through it are pruned. A skip is not allowed if another non-root target that still runsdepends_onthe skipped target — the run fails fast. With only skip tokens (e.g.rake ^clean), thedefaulttarget runs.
Tools
Tools are defined in a top-level [tool] table, split into two categories:
[tool.cargo.<name>] for cargo-installable tools (cargo subcommands and
the like) and [tool.os.<name>] for OS-level dependencies (docker,
pkg-config, …) that rake cannot cargo install. A target references either
kind by name from its tools list (the two categories share one flat reference
namespace, so a name must be unique across them).
Tools are ensured lazily: a tool's check only runs when a target that
references it is actually run (never at parse time or for unrelated targets), and
at most once per run. Before a target's commands run, each tool it references is
ensured:
- The
checkcommand probes whether the tool is already installed: it must exit 0 when the tool is present. For a cargo subcommand this means["cargo", "<sub>", "--version"]— the barecargo-<sub>binary rejects--versionand exits non-zero, which would make the tool look perpetually absent.
Cargo tools ([tool.cargo.<name>]):
- When absent,
rakeprints anInstallingnotice and runsinstall(bothcheckandinstallare required). update(defaultfalse): whentrue, the installed version (parsed fromcheck's output) is compared against the latest reported by the tool'ssemver_checkmode and re-installed if a newer one exists. The only mode today is"crates-io"(the default), which queries the crates.io API and so needs acratename; a failed version check — or acheckwhose output has no parseable version — is non-fatal and keeps the installed version. Withupdate = false, the already-installed version is used as-is.- A failed
installaborts the run (unlike a tolerated command failure).
OS tools ([tool.os.<name>]): only check is required; there is no update
support.
- When absent and an
installis declared,rakeruns it (like a cargo tool). When absent and noinstallis declared, the run aborts with a message stating the requirement, plus anyhint—rakedoes not try to install OS dependencies itself.
Execution
A target runs after its transitive dependencies. Within a target, commands run
in array order and execution stops at the first command that exits non-zero,
aborting the dependency chain — unless that command sets skip_on_error = true,
in which case the failure is tolerated and execution continues. The process
exits with the code of the command that stopped the run (or the last command if
all succeeded).
When multiple roots are given (e.g. rake build test), each root's dependency
graph runs in full in the order given. A target shared by two roots runs once per
root (no cross-root deduplication). Tools, however, are ensured at most once for
the whole run regardless of how many roots reference them.
Usage
list and syntax are reserved subcommand names: a target sharing one of
those names cannot be run by name (run it via a parent target instead).
When no target is named, the default target runs.