2 releases
| new 0.1.4 | Aug 5, 2026 |
|---|---|
| 0.1.3 | Aug 5, 2026 |
| 0.1.2 |
|
| 0.1.1 |
|
| 0.1.0 |
|
#1093 in Embedded development
93KB
2K
SLoC
Note: Zargo is a hard fork of xargo.
In most cases you should prefer Cargo's
build-stdfeature over this. I mainly maintain this so I can build, test, and benchmark Rust standard libraries in a time-optimized and controlled environment for my contributions. For example, you can useZARGO_RUST_SRC=YOUR_PATH/rust/library zargo rustc -- --emit asm -C "llvm-args=-x86-asm-syntax=intel"to emit assembly for your version of the Rust library, or benchmark with your ownrust/libraryinstead.
Zargo builds and manages "sysroots" (cf. rustc --print sysroot). Making it
easy to cross compile Rust crates for targets that don't have binary
releases of the standard crates, like the thumbv*m-none-eabi* targets. And
it also lets you build a customized std crate, e.g. compiled with -C panic=abort, for your target.
๐ Dependencies
-
The
rust-srccomponent, which you can install withrustup component add rust-src. -
Rust and Cargo.
๐ฆ Installation
cargo install zargo
๐ Usage
๐ฅ no_std
zargo has the exact same CLI as cargo.
# This Just Works
zargo build --target thumbv6m-none-eabi
Compiling core v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcore)
Finished release [optimized] target(s) in 11.61 secs
Compiling lib v0.1.0 (file://$PWD)
Finished debug [unoptimized + debuginfo] target(s) in 0.5 secs
zargo will cache the sysroot, in this case the core crate, so the next
build command will be (very) fast.
$ zargo build --target thumbv6m-none-eabi
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
By default, zargo will only compile the core crate for the target. If you
need a bigger subset of the standard crates, specify the dependencies in a
Zargo.toml at the root of your Cargo project (right next to Cargo.toml).
# Alternatively you can use [build.dependencies]
# the syntax is the same as Cargo.toml's; you don't need to specify path or git
[target.thumbv6m-none-eabi.dependencies]
collections = {}
$ zargo build --target thumbv6m-none-eabi
Compiling core v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcore)
Compiling alloc v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/liballoc)
Compiling std_unicode v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libstd_unicode)
Compiling collections v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcollections)
Finished release [optimized] target(s) in 15.26 secs
Compiling lib v0.1.0 (file://$PWD)
Finished debug [unoptimized + debuginfo] target(s) in 0.5 secs
๐ std
You can compile a customized std crate as well, just specify which Cargo
features to enable.
# Build `std` with `-C panic=abort` (default) and with jemalloc as the default
# allocator
[target.i686-unknown-linux-gnu.dependencies.std]
features = ["jemalloc"]
# Needed to compile `std` with `-C panic=abort`
[profile.release]
panic = "abort"
zargo run --target i686-unknown-linux-gnu --release
๐งช test and bench
Cargo compiles the library and binary targets of a test or benchmark run with
--test whatever harness the tests themselves use, and rustc then injects an
extern crate test into them. zargo test and zargo bench therefore need
the test crate in the sysroot, and add it themselves to a sysroot built from
a rust-src std. The crate list does not have to name it.
test is added to the sysroot that is already there rather than built into
every one of them, under a record of its own, so a zargo build neither builds
it nor is invalidated by it. The first zargo test or zargo bench after a
change to the sysroot rebuilds the stage std belongs to, and the ones after
that find test in place and build nothing.
Naming [dependencies.test] yourself still works and takes precedence; a std
of your own, given by path or git, gets no test because the one in
rust-src would pull the rust-src std in beside it.
If you'd like to know what zargo is doing under the hood, pass the verbose,
-v, flag to it.
zargo build --target thumbv6m-none-eabi -v
๐งช Dev channel
To use zargo with a development version of rustc (compiled from source), set the ZARGO_RUST_SRC environment variable to specify the location of your Rust source code.
# `$ZARGO_RUST_SRC` must point to the `library` subfolder of a Rust checkout.
export ZARGO_RUST_SRC=/path/to/rust/library
zargo build --target msp430-none-elf
โ ๏ธ NOTE This also works with the nightly channel but it's not recommended as the Rust source may diverge from what your compiler is able to compile as it may make use of newer features that your compiler doesn't understand.
โ๏ธ Compiling the sysroot with custom rustc flags
Zargo uses the same custom rustc flags that apply to the target Cargo project.
So you can use either the RUSTFLAGS env variable or a .cargo/config
configuration file to specify custom rustc flags.
# build the sysroot with debug information
RUSTFLAGS='-g' zargo build --target x86_64-unknown-linux-gnu
# Alternatively
[build]
rustflags = ["-g"]
๐ฏ Compiling the sysroot for a custom target
At some point you may want to develop a program for a target that's not officially supported by rustc. Zargo's got your back! It supports custom targets via target specifications files, which are not really documented anywhere other than in the compiler source code.
For example, let's say that you want to cross compile a program for a PowerPC Linux systems that uses uclibc instead of glibc. You can start by dumping the specification of a similar target into a file:
rustc -Z unstable-options --print target-spec-json --target powerpc-unknown-linux-gnu | tee powerpc-unknown-linux-uclibc.json
Once you have your target specification file you only have to call Zargo with the right target triple:
zargo build --target powerpc-unknown-linux-uclibc
๐ Multi-stage builds
Some standard crates have implicit dependencies between them. To compile a sysroot that contains such crates you can perform the build in stages:
[dependencies.std]
stage = 0
[dependencies.test]
stage = 1
๐ฆ Creating a sysroot with custom crates
Zargo lets you create a sysroot with custom crates:
# First build some standard crates.
[dependencies.alloc]
[dependencies.panic_abort]
[dependencies.panic_unwind]
# Then build our custom facade.
[dependencies.std]
git = "https://github.com/rust3ds/ctru-rs"
stage = 1
๐ง Patching sysroot crates
Zargo supports the patch feature from Cargo:
[patch.crates-io.libc]
path = "path/to/custom/libc"
โ Check-only sysroot build
Zargo supports performing a 'check build' of the sysroot via the zargo-check command:
zargo-check build --target thumbv6m-none-eabi
โ ๏ธ Caveats / gotchas
-
Zargo won't build a sysroot when used with stable or beta Rust. This is because
stdand other standard crates depend on unstable features so it's not possible to build the sysroot with stable or beta. -
stdis built as rlib and dylib. The dylib needs a panic library and an allocator. If you do not specify thepanic-unwindfeature, you have to setpanic = "abort"inCargo.toml. -
To build without the
jemallocfeature include the following inZargo.toml:[dependencies.std] features = ["force_alloc_system"] -
It's recommended that the
--targetoption is always used forzargo. -
Remember that
coreandstdwill get implicitly linked to your crate but all the other sysroot crates will not. -
Remember that rustc will always implicitly link
compiler_builtinsinto your final binary. -
Care must be taken not to end up with any "top-level" crates (
core,std,compiler-builtins) twice in the sysroot.
๐ License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE
- MIT license LICENSE-MIT
at your option.
๐ค Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Dependencies
~2.1โ5MB
~84K SLoC