Rust-Xargo: The sysroot manager that lets you build and customize `std`

PSA: Xargo is in maintenance mode


The sysroot manager that lets you build and customize std

Cross compiling `std` for i686-unknown-linux-gnu
Cross compiling `std` for i686-unknown-linux-gnu

Xargo 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.


  • The rust-src component, which you can install with rustup component add rust-src.

  • Rust and Cargo.


$ cargo install xargo



xargo has the exact same CLI as cargo.

# This Just Works
$ xargo 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

xargo will cache the sysroot, in this case the core crate, so the next build command will be (very) fast.

$ xargo build --target thumbv6m-none-eabi
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs

By default, xargo will only compile the core crate for the target. If you need a bigger subset of the standard crates, specify the dependencies in a Xargo.toml at the root of your Cargo project (right next to Cargo.toml).

$ cat Xargo.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
collections = {}

$ xargo 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


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
$ cat Xargo.toml
features = ["jemalloc"]

# Needed to compile `std` with `-C panic=abort`
$ tail -n2 Cargo.toml
panic = "abort"

$ xargo run --target i686-unknown-linux-gnu --release
    Updating registry ``
   Compiling libc v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/rustc/libc_shim)
   Compiling core v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcore)
   Compiling build_helper v0.1.0 (file://$SYSROOT/lib/rustlib/src/rust/src/build_helper)
   Compiling gcc v0.3.41
   Compiling unwind v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libunwind)
   Compiling std v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libstd)
   Compiling compiler_builtins v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcompiler_builtins)
   Compiling alloc_jemalloc v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/liballoc_jemalloc)
   Compiling alloc v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/liballoc)
   Compiling rand v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/librand)
   Compiling std_unicode v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libstd_unicode)
   Compiling alloc_system v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/liballoc_system)
   Compiling panic_abort v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libpanic_abort)
   Compiling collections v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcollections)
    Finished release [optimized] target(s) in 33.49 secs
   Compiling hello v0.1.0 (file://$PWD)
    Finished release [optimized] target(s) in 0.28 secs
     Running `target/i686-unknown-linux-gnu/release/hello`
Hello, world!

If you'd like to know what xargo is doing under the hood, pass the verbose, -v, flag to it.

$ xargo build --target thumbv6m-none-eabi -v
+ "rustc" "--print" "target-list"
+ "rustc" "--print" "sysroot"
+ "cargo" "build" "--release" "--manifest-path" "/tmp/xargo.lTBXKnaUGicV/Cargo.toml" "--target" "thumbv6m-none-eabi" "-v" "-p" "core"
   Compiling core v0.0.0 (file://$SYSROOT/lib/rustlib/src/rust/src/libcore)
     Running `rustc --crate-name core $SYSROOT/lib/rustlib/src/rust/src/libcore/ --crate-type lib -C opt-level=3 -C metadata=a5c596f87f7d486b -C extra-filename=-a5c596f87f7d486b --out-dir /tmp/xargo.lTBXKnaUGicV/target/thumbv6m-none-eabi/release/deps --emit=dep-info,link --target thumbv6m-none-eabi -L dependency=/tmp/xargo.lTBXKnaUGicV/target/thumbv6m-none-eabi/release/deps -L dependency=/tmp/xargo.lTBXKnaUGicV/target/release/deps`
    Finished release [optimized] target(s) in 11.50 secs
+ "cargo" "build" "--target" "thumbv6m-none-eabi" "-v"
   Compiling lib v0.1.0 (file://$PWD)
     Running `rustc --crate-name lib src/ --crate-type lib -g -C metadata=461fd0b398821543 -C extra-filename=-461fd0b398821543 --out-dir $PWD/target/thumbv6m-none-eabi/debug/deps --emit=dep-info,link --target thumbv6m-none-eabi -L dependency=$PWD/target/thumbv6m-none-eabi/debug/deps -L dependency=$PWD/lib/target/debug/deps --sysroot $HOME/.xargo`
    Finished debug [unoptimized + debuginfo] target(s) in 0.5 secs

Dev channel

Oh, and if you want to use xargo to compile std using a "dev" rustc, a rust compiled from source, you can use the XARGO_RUST_SRC environment variable to tell xargo where the Rust source is.

# The source of the `core` crate must be in `$XARGO_RUST_SRC/libcore`
$ export XARGO_RUST_SRC=/path/to/rust/src

$ xargo 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

Xargo 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' xargo build --target x86_64-unknown-linux-gnu

# Alternatively
$ edit .cargo/config && cat $_
rustflags = ["-g"]

# Then you can omit RUSTFLAGS
$ xargo build --target x86_64-unknown-linux-gnu

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. Xargo'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. Luckily you don't need to write a specification file from scratch; you can start from an existing one.

For example, let's say that you want to cross compile a program for a PowerPC Linux systems that uses uclibc instead of glibc. There's a similarly looking target in the list of targets supported by the compiler -- see rustc --print target-list -- and that is powerpc-unknown-linux-gnu. So you can start by dumping the specification of that target into a file:

$ rustc -Z unstable-options --print target-spec-json --target powerpc-unknown-linux-gnu | tee powerpc-unknown-linux-uclibc.json
  "arch": "powerpc",
  "data-layout": "E-m:e-p:32:32-i64:64-n32",
  "dynamic-linking": true,
  "env": "gnu",
  "executables": true,
  "has-elf-tls": true,
  "has-rpath": true,
  "is-builtin": true,
  "linker-flavor": "gcc",
  "linker-is-gnu": true,
  "llvm-target": "powerpc-unknown-linux-gnu",
  "max-atomic-width": 32,
  "os": "linux",
  "position-independent-executables": true,
  "pre-link-args": {
    "gcc": [
  "target-endian": "big",
  "target-family": "unix",
  "target-pointer-width": "32",
  "vendor": "unknown"

One of the things you'll definitively want to do is drop the is-builtin field as that's reserved for targets that are defined in the compiler itself. Apart from that the only modification you would have to in this case is change the env field from gnu (glibc) to uclibc.

   "arch": "powerpc",
   "data-layout": "E-m:e-p:32:32-i64:64-n32",
   "dynamic-linking": true,
-  "env": "gnu",
+  "env": "uclibc",
   "executables": true,
   "has-elf-tls": true,
   "has-rpath": true,
-  "is-builtin": true,
   "linker-flavor": "gcc",
   "linker-is-gnu": true,
   "llvm-target": "powerpc-unknown-linux-gnu",

Once you have your target specification file you only have to call Xargo with the right target triple; make sure that the specification file is the same folder from where you invoke Xargo because that's where rustc expects it to be.

$ ls powerpc-unknown-linux-uclibc.json

$ xargo build --target powerpc-unknown-linux-uclibc

Your build may fail because if rustc doesn't support your target then it's likely that the standard library doesn't support it either. In that case you will have to modify the source of the standard library. Xargo helps with that too because you can make a copy of the original source -- see rustc --print sysroot, modify it and then point Xargo to it using the XARGO_RUST_SRC env variable.

Multi-stage builds

Some standard crates have implicit dependencies between them. For example, the test crate implicitly depends on the std. Implicit here means that the test crate Cargo.toml doesn't list std as its dependency. To compile a sysroot that contains such crates you can perform the build in stages by specifying which crates belong to each stage in the Xargo.toml file:

stage = 0

stage = 1

This will compile an intermediate sysroot, the stage 0 sysroot, containing the std crate, and then it will compile the test crate against that intermediate sysroot. The final sysroot, the stage 1 sysroot, will contain both the std and test crates, and their dependencies.

Creating a sysroot with custom crates

Xargo lets you create a sysroot with custom crates. You can virtually put any crate in the sysroot. However, this feature is mainly used to create [alternative std facades][rust-3ds], and to replace the test crate with one that supports no_std targets. To specify the contents of the sysroot simply list the dependencies in the Xargo.toml file as you would do with Cargo.toml:

# First build some standard crates.

# Then build our custom facade. It (implicitly) requires the crates above to
# already be in the sysroot, so we need to set the `stage`.
git = ""
stage = 1

Patching sysroot crates

Xargo also supports the patch feature from Cargo. This allows you to force the use of a custom crate throughout your sysroot's dependency tree. This can be especially useful to force the use of a custom libc or compiler_builtins without having to do intrusive changes to every transitive dependency.

path = "path/to/custom/libc"

Notice that you should not list patched crates as [dependencies]! [dependencies] determines which crates are built in the first place; [patch] lets you replace some of their (transitive) dependencies with your own choice. Having a crate listed in both will likely lead to crate duplication.

Check-only sysroot build

Xargo supports performing a 'check build' of the syroot via the xargo-check command. This command is invoked exactly like xargo, but will invoke cargo check instead of cargo build when building the sysroot.

This is only useful for very specialized applicationsm like Miri. The resulting libstd will not be useable in a normal build, since codegen will not be performed. You should almost always run xargo check (note the space), which will perform a normal sysroot build, followed by a 'check' build of your application

Caveats / gotchas

  • Xargo won't build a sysroot when used with stable or beta Rust. This is because std and other standard crates depend on unstable features so it's not possible to build the sysroot with stable or beta.

  • std is built as rlib and dylib. The dylib needs a panic library and an allocator. If you do not specify the panic-unwind feature, you have to set panic = "abort" in Cargo.toml.

  • To build without the jemalloc feature include the following in Xargo.toml:

    features = ["force_alloc_system"]

    What this flag means is that every program compiled with this libstd can only use the system allocator. If your program tries to set its own allocator, compilation will fail because now two allocators are set (one by libstd, one by your program). For some further information on this issue, see rust-lang/rust#43637.

  • It's recommended that the --target option is always used for xargo. This is because it must be provided even when compiling for the host platform due to the way cargo handles compiler plugins (e.g. serde_derive) and build scripts ( This also applies to how all of the dependant crates get compiled that use compiler plugins or build scripts. You can determine your host's target triple with rustc -vV. On *nix, the following rune will extract the triple: rustc -vV | egrep '^host: ' | sed 's/^host: //'.

  • Remember that core and std will get implicitly linked to your crate but all the other sysroot crates will not. This means that if your Xargo.toml contains a crate like alloc then you will have to add a extern crate alloc somewhere in your dependency graph (either in your current crate or in some of its dependencies).

  • Remember that rustc will always implicitly link compiler_builtins into your final binary, but won't make it available for use the same way core and std are. So if you need to manually call a compiler_builtins function, you will still need to manually add an extern crate compielr_builtins within your crate.

  • Care must be taken not to end up with any "top-level" crates (core, std, compiler-builtins) twice in the sysroot. Doing so will cause cargo to error on build with a message like multiple matching crates for core. Duplicate crates in the sysroot generally occur when the same crate is built twice with different features as part of a multi-stage build.


Licensed under either of

at your option.


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.


  • When a .cargo/config target-dir is set, xargo may fail
    When a .cargo/config target-dir is set, xargo may fail

    Apr 1, 2020

    When a cargo target-dir is set, such as:

     > cat ~/.cargo/config
    target-dir = "/Volumes/ramdisk/rs"

    Xargo can fail, as the target directory is not where it expects, which can cause the cp_r utilities to fail unexpectedly. This was found in:

  • Description of cross build process
    Description of cross build process

    Apr 12, 2020

    Is there a description somewhere, in the rust docs, rfc, something..? that explains what exactly is necessary to do a cross build, or do I have to reverse engineer

  • LTO builds on recent nightlies fail
    LTO builds on recent nightlies fail "to get bitcode from object file"

    May 12, 2020

    We recently got a bug report for the cargo-xbuild project that builds with LTO fail on recent nightlies: The error message is "error: failed to get bitcode from object file for LTO (Bitcode section not found in object file)". This also seems to affect xargo.

    @toku-sa-n created a minimal example to reproduce this issue at Just clone the project and run xargo build --target cargo_settings with a recent nightly to see the error.

    We are pretty sure that this is caused by (bisect shows rustc commit, but don't know what's the best way to fix this. The only thing that worked so far is to set RUSTFLAGS to -Clinker-plugin-lto when compiling the sysroot, but maybe there's a better way.

  • Couldn't copy error during build
    Couldn't copy error during build

    May 14, 2020

    I am following this guide:

    I ran the following commands:

    $ rustup toolchain install nightly
    $ rustup override set nightly
    $ rustup component add rust-src
    $ cargo install xargo

    When I run this command, I get an error.

    xargo build --target x86_64-pc-windows-gnu --release
    error: couldn't copy /home/drdinosaur/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/rsbegin.o to /home/drdinosaur/.xargo/lib/rustlib/x86_64-pc-windows-gnu/lib/rsbegin.o
    caused by: No such file or directory (os error 2)
    note: run with `RUST_BACKTRACE=1` for a backtrace

    I am attempting to compile to Windows from Linux. I'm not sure what the issue is from that. I'm still, new to Rust, I'm just trying to reduce my binary size through this method. Thanks.

  • cargo-features = [
    cargo-features = ["strip"] cause compile error

    Jun 1, 2020

    I'm trying to use xargo to reduce space of binary file. min-sized-rust mentions it here. But cargo-features = ["strip"] cause error. This thing was recently added (see

    $ RUST_BACKTRACE=1 xargo build --target x86_64-unknown-linux-gnu --release --verbose
    # output:
    + "rustc" "--print" "sysroot"
    + RUSTFLAGS="--sysroot /home/alex/.xargo/HOST -Z force-unstable-if-unmarked"
    + "cargo" "build" "--release" "--manifest-path" "/tmp/xargo.Dax5TY8K3VlJ/Cargo.toml" "--target" "x86_64-unknown-linux-gnu" "-v" "-p" "std"
    error: failed to parse manifest at `/tmp/xargo.Dax5TY8K3VlJ/Cargo.toml`
    Caused by:
      feature `strip` is required
    consider adding `cargo-features = ["strip"]` to the manifest
    error: `"cargo" "build" "--release" "--manifest-path" "/tmp/xargo.Dax5TY8K3VlJ/Cargo.toml" "--target" "x86_64-unknown-linux-gnu" "-v" "-p" "std"` failed with exit code: Some(101)
       0: error_chain::make_backtrace
       1: <error_chain::State as core::default::Default>::default
       2: <std::process::Command as xargo::extensions::CommandExt>::run
       3: xargo::sysroot::build
       4: xargo::sysroot::update
       5: xargo::main_inner
       6: std::rt::lang_start::{{closure}}
       7: std::rt::lang_start_internal::{{closure}}
                 at /rustc/a74d1862d4d87a56244958416fd05976c58ca1a8/src/libstd/
                 at /rustc/a74d1862d4d87a56244958416fd05976c58ca1a8/src/libstd/
                 at /rustc/a74d1862d4d87a56244958416fd05976c58ca1a8/src/libstd/
                 at /rustc/a74d1862d4d87a56244958416fd05976c58ca1a8/src/libstd/
                 at /rustc/a74d1862d4d87a56244958416fd05976c58ca1a8/src/libstd/
       8: main
       9: __libc_start_main
      10: _start

    Example Cargo.toml

    cargo-features = ["strip"]
    name = "failed_case"
    version = "0.1.0"
    edition = "2018"
    # See more keys and their definitions at
    strip = "symbols"

    and Xargo.toml

    default-features = false
    features = ["panic_unwind"]
    $ xargo -V
    xargo 0.3.20
    cargo 1.45.0-nightly (9fcb8c1d2 2020-05-25)
    $ rustc -Vv
    rustc 1.45.0-nightly (5fd2f06e9 2020-05-31)
    binary: rustc
    commit-hash: 5fd2f06e99a985dd896684cb2c9f8c7090eca1ab
    commit-date: 2020-05-31
    host: x86_64-unknown-linux-gnu
    release: 1.45.0-nightly
    LLVM version: 10.0
  • xargo build error
    xargo build error

    Jun 8, 2020

    I am trying out nightly xargo for further customizing target riscv64gc-unknown-linux-musl for cross building inspired by this issue but I failed at the very beginning :)

    I want a statically linked executable so targetting x86_64-unknown-linux-musl with

    xargo build --release --target x86_64-unknown-linux-musl

    then got

    xargo build --release --target x86_64-unknown-linux-musl
       Compiling hello v0.1.0 (/home/chao/rust_playground/hello)
    error[E0463]: can't find crate for `std`
      = note: the `x86_64-unknown-linux-musl` target may not be installed
    error: aborting due to previous error
    For more information about this error, try `rustc --explain E0463`.
    error: could not compile `hello`.
    To learn more, run the command again with --verbose.
    Makefile:3: recipe for target 'x86-musl' failed
    make: *** [x86-musl] Error 101

    my rustup show

    [email protected]:~/rust_playground/hello$ rustup show
    Default host: x86_64-unknown-linux-gnu
    rustup home:  /home/chao/.rustup
    installed toolchains
    nightly-x86_64-unknown-linux-gnu (default)
    installed targets for active toolchain
    active toolchain
    nightly-x86_64-unknown-linux-gnu (default)
    rustc 1.44.0-nightly (f509b26a7 2020-03-18)

    Things work fine without nightly but with getting WARNING: the sysroot can't be built for the Stable channel. Switch to nightly. in customizing sysroot and target

  • Force sysroot to have different hashes
    Force sysroot to have different hashes

    Apr 25, 2018

    A terrible workaround a problem I found: If the sysroot and project share a dependency, it might cause conflicts, leading to confusing cargo errors.

  • Xargo.toml, support other built-in targets, refactor
    Xargo.toml, support other built-in targets, refactor

    Dec 28, 2016

    closes #77 closes #98

  •  Search for Xargo.toml in parent directories, including test
    Search for Xargo.toml in parent directories, including test

    Sep 9, 2019

    So far, xargo expected the Xargo.toml in the directory of the Cargo.toml. This patch searches for the Xargo.toml by walking up the directory hierarchy. This is useful if a workspace is used, leading, for example, to one Cargo.toml with the workspace and multiple Cargo.toml files for the members of the workspace. With this patch, it is no longer necessary to have one Xargo.toml for each Cargo.toml, but only one next to the workspace, for example.

  • Xargo can not find component `rust-src`
    Xargo can not find component `rust-src`

    Nov 3, 2016

    Following along with the intermezzOs, I installed xargo. After installing Xargo, I ran the following command:

    $ xargo build --release --target ./x86_64-unknown-metalomai-gnu.json
    error: `rust-src` component not found. Run `rustup component add rust-src`.

    Following the instructions in the error message, I installed the rust-src component.

    $ rustup component add rust-src
    info: downloading component 'rust-src'
     26.4 MiB /  26.4 MiB (100 %) 377.6 KiB/s ETA:   0 s
    info: installing component 'rust-src'
    warning: could not delete temp directory: /home/geemili/.multirust/tmp/zgpxf8_tm0kk8a72_dir

    Then I tried running xargo again.

    $ xargo build --release --target ./x86_64-unknown-metalomai-gnu.json
    error: `rust-src` component not found. Run `rustup component add rust-src`.
    $ # :(

    Xargo finds the current sysroot using [this code][find-sysroot]. In bash, that looks like:

    $ rustc --print sysroot

    Then it walks the directories to [find a file named "version", in a directory named rust.][find-version]. Translating to bash again:

    $ find /home/geemili/.multirust/toolchains/nightly-x86_64-unknown-linux-gnu/ -name "version"

    The "version" file is not in the "rust" folder, so the program fails.

    I don't know why where version is was changed, but depending on this seems like a hack. Perhaps a better way could be found to find the location of rust-src? Maybe someone from who works on rustup could comment.

  • Fix for latest nightly: need to add a patch section
    Fix for latest nightly: need to add a patch section

    Dec 13, 2018


  • Add `xargo-check` command
    Add `xargo-check` command

    Nov 14, 2019

    This allows configuring Xargo to run cargo check instead of cargo build

    Needed to support