Rust projects
Checks, tests and binaries with Cargo, and crates shared between members.
Cargo builds Rust crates. Constructing one declares the targets, so a member
gets check, test, clippy and doc without asking.
import { Cargo, CargoToolchain } from "bldr/rust-tools";
import rust from "rust";
const c = new Cargo({
scope,
toolchain: new CargoToolchain({ baseImage: rust }),
source: member.rootDirectory,
binary: { binaryName: "my-tool", profile: "release" },
});
export const binary = c.binary().dir;bldr build my-member/check # does it compile
bldr test my-member:test # per-case results
bldr build my-member/binary # the executableCrates are downloaded by one step, Cargo > fetch, which runs cargo fetch
over your Cargo.toml and Cargo.lock files alone: its cache key is your
manifests, and it survives every source edit. It is the only cargo step with
internet access. Every other command compiles against what it fetched, offline
— so a test that calls out to the internet fails, and a build.rs that
downloads has to get its input some other way (a mount, a file in the toolchain
image).
The toolchain image
A CargoToolchain is the image cargo runs in. Add what your crate's build
needs:
const toolchain = new CargoToolchain({ baseImage: rust })
.aptInstall(["clang", "libclang-dev"]);A build step has no network unless it is approved for one. .aptInstall()
approves itself, as do the rustup steps behind channel, components and
targets; any other step that downloads says so:
.run("cargo install cargo-nextest --locked", { internet: true }).
You can also bake files in with .copy(), which is how a crate whose
build.rs reads a .proto finds it without a path relative to the repository.
Depending on another member's crate
Two members, one crate, compiled from one copy. The consumer depends on it by version, not by path:
my-shared-crate = "0"import { lib as shared } from "my/shared-crate";
new Cargo({ scope, toolchain, source: member.rootDirectory, dependencies: [shared] });The version dependency keeps the manifest ordinary, so the crate still builds
outside bldr, and dependencies patches it to the member's source at build
time.
Cross-compiling
const toolchain = new CargoToolchain({
baseImage: rust,
targets: ["x86_64-unknown-linux-musl"],
env: { RUSTFLAGS: "-C target-feature=+crt-static" },
}).aptInstall("musl-tools");
new Cargo({
scope,
toolchain,
source: member.rootDirectory,
defaults: { target: "x86_64-unknown-linux-musl" },
binary: { binaryName: "my-tool", profile: "release" },
});Set the target on defaults rather than per command. A toolchain that sets
+crt-static without a target applies it to host artifacts too, and
proc-macros are host artifacts that cannot link statically.
Bounding parallelism
Cargo defaults to one job per core, and peak memory scales with it. A large workspace on a many-core machine can exceed the memory a build is allowed and be killed with no diagnostic:
check: { jobs: 12 },
test: [{ id: "unit", jobs: 12, cargoArgs: ["--lib", "--bins", "--no-fail-fast"] }],--no-fail-fast matters for a test target. Without it, cargo test stops at
the first crate that fails, and the report looks complete while missing every
later crate.