NAME
Build::Rust - build Rust crates at install time and locate their binaries at run time
SYNOPSIS
In your distribution's Build.rakumod:
use Build::Rust;
class Build does Build::Rust {
method crates {
crate 'psshaw', :on-missing<fetch>,
}
}
In META6.json, list the binary as a resource and add the build dependency:
"build-depends": ["Build::Rust"],
"depends": ["Build::Rust", ...],
"resources": ["bin/psshaw", ...]
At run time, wherever you need the binary:
use Resource::Wrangler;
use Build::Rust::Binary;
my $psshaw = Build::Rust::Binary.new:
:name<psshaw>,
:resources(Resource::Wrangler[-> { %?RESOURCES }].new),
:env<PANG_PSSHAW>;
run $psshaw.path, '--help';
DESCRIPTION
Build::Rust lets a Raku distribution ship a binary built from Rust source that lives inside the distribution, typically as a git submodule. It has two halves.
The Build::Rust role is composed into the Build class that zef runs at install time. Each crate you declare is built with cargo and its binaries are copied to resources/bin/, so they install with the distribution. Cargo is the incremental authority: it is always invoked and no-ops when nothing changed.
Build::Rust::Binary finds that binary again at run time. From a checkout the resource is the file in resources/bin/ itself and is used in place. Once installed, a resource lives in the CompUnit store with no execute bit, so it is staged out first to a private per-user directory — XDG_RUNTIME_DIR or $*TMPDIR when that is a directory we own that nobody else can write to, else $*HOME/.cache/build-rust — keyed by the resource's size and mtime so a reinstall never reuses a stale copy.
DECLARING CRATES
crate $path, *%opts returns a Build::Rust::Crate. $path is the crate's directory relative to the distribution root; Cargo.toml must be there.
:bins — names of the [[bin]] targets to ship. Defaults to the basename of $path. Nothing is read from Cargo.toml; you say what you mean.
:profile — release (default), dev, or a custom profile name.
:features — list of cargo features.
:on-missing — what to do when the crate's source is absent: die (default) stops with instructions; fetch runs git submodule update --init --recursive for that path, which needs a git checkout with a .gitmodules; skip notes the absence and carries on, so the installed distribution has no binary and Build::Rust::Binary.available is False.
To declare several crates, parenthesise each call:
method crates {
crate('psshaw', :on-missing<fetch>),
crate('tools', :bins<fmt lint>),
}
cargo is found on PATH, or via $CARGO if that is set. If the built binary is not listed under resources in META6.json, the build warns that it will not ship.
LOCATING BINARIES
Build::Rust::Binary.new(:name, :resources, :env)
:name — the binary's name, without any .exe.
:resources — an object with AT-KEY and load-resource-to-path, such as Resource::Wrangler[-> { %?RESOURCES }].new. The closure is what carries your distribution's %?RESOURCES across the module boundary.
:env — optional. If this environment variable is set, it must name an executable file, which is used instead of the shipped binary.
.path returns the absolute path of a runnable copy, resolved once per instance (the module-scoped singleton in the SYNOPSIS makes that once per process in practice); it dies with instructions when there is nothing to run. .available answers whether .path would succeed, without staging anything.
AUTHOR
ab5tract ab5tract@raku.org
COPYRIGHT AND LICENSE
Copyright 2026 ab5tract
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.