Cargo-Each
cargo-each: run a command over a cargo-style selection of workspace
members.
cargo-each resolves a package selection expressed with the same
selectors as cargo build, optionally narrows it with package predicates,
and runs a command over the result — once per member, once per matching
Cargo target, or exactly once for the whole set. It replaces hand-rolled
shell loops with one cargo-native, cross-platform command.
cargo-each ships as an executable only; it is a cargo subcommand, not a
library dependency.
Installation
cargo install cargo-each
Usage
cargo each [SELECTION] [FILTERS] [EXECUTION] -- <COMMAND> [ARG...]
Everything after -- is the command template; cargo-each spawns it
directly (argv, not a shell string) after substituting placeholders.
Selection (mirrors cargo build)
-p/--package <SPEC>— select a member. Repeatable.SPECis a package name, aname@versionspec, or a Unix glob (tokio-*).--workspace/--all— select every workspace member.--exclude <SPEC>— drop a member (with--workspace). Repeatable.--none— explicitly select zero members (a no-op that exits 0).
When nothing is named the default is cargo default-members, exactly
like cargo build; pass --workspace for every member. A selector that
matches no member is an error, so typos fail loudly. A computed selection
(for example a CI affected-packages set) is fed in as ordinary flags via
shell expansion — cargo-each has no file or environment-variable source
of its own.
Filters
--filter accepts Boolean expressions using not, and, or, and
parentheses, with conventional precedence. Repeated --filter expressions
are AND-combined. Repeated --exclude-filter expressions are OR-combined,
and exclusion wins. Metadata values containing whitespace or Boolean syntax
can be double-quoted. Expression atoms:
lib/bin/target-kind:<kind>— target-kind membership.publishable— Cargo permits publishing the package.feature:<name>— the package declares the feature.dep:<name>— the member declares<name>as a dependency.metadata:<dotted.key>—package.metadata.<dotted.key>is present.metadata:<dotted.key>=<value>— that key equals<value>(numeric compare when both sides parse as a number, else string compare).
Execution modes
- per-package (default): run the command once per selected member, in name order, substituting the per-package placeholders below.
--once: run the command exactly once when the set is non-empty (skip when empty), using the{packages}placeholder to inject the selection.--each-target <KIND>: run once per matching Cargo target, using{target}plus the package placeholders. Repeated kinds are OR-combined;--target-required-featurefurther narrows targets.
--keep-going runs every invocation and exits non-zero if any failed
(default is fail-fast); --chdir runs each per-package or per-target
command from that member crate root; --dry-run prints commands without
running them.
Placeholders
Substituted inside each command argument:
{name}— bare package name (per-package and per-target).{spec}—name@version(per-package and per-target).{version}— package version (per-package and per-target).{manifest}— absolute memberCargo.tomlpath (per-package and per-target).{target}— Cargo target name (per-target).{packages}— the cargo selection flags for the resolved set (--workspacefor the whole workspace, else--package name@version …); valid only in--oncemode and only as a standalone argument.
Using a placeholder in the wrong mode is a usage error. Only the tokens
above are interpreted; any other {…} sequence (a typo, or a literal brace
an argument needs) passes through verbatim to the spawned command — there is
no brace-escape, so this passthrough is part of the contract.
Behavior
An empty resolved selection (via --none, or a filter that removes every
member) is a successful no-op: cargo-each prints a one-line note and
exits 0. This is what lets callers drop bespoke nothing-to-do guards.
Child commands inherit PATH explicitly. On Windows this makes relative
program lookup honor the inherited PATH order instead of preferring an
unrelated executable beside cargo-each.
Otherwise the exit code is the first failing command code (fail-fast),
1 under --keep-going if any command failed, or 2 for a cargo-each
usage error (unknown selector, bad filter expression, misused placeholder).
Examples
Run a per-manifest tool over every library crate:
cargo each --workspace --filter lib -- \
cargo check-external-types --manifest-path {manifest}
Run one clippy invocation over a computed subset, skipping when it is empty:
cargo each -p crate-a -p crate-b --once -- \
cargo clippy {packages} --all-targets -- -D warnings
Run every Cargo test target that requires the loom feature:
cargo each --workspace --each-target test --target-required-feature loom -- \
cargo test -p {name} --test {target} --features loom