wrapc
A zero-fuss parser for rustc arguments, designed for RUSTC_WRAPPER tools.
Provides comprehensive and type-safe coverage of rustc's CLI surface area.
Usage Guide: Building Bulletproof RUSTC_WRAPPER Tools
1. The Core Concept
When Cargo invokes a RUSTC_WRAPPER, it doesn't just pass rustc arguments.
It uses a specific protocol: <wrapper_binary> - <actual_rustc_path> <rustc_args...>
wrapc automatically detects this protocol, extracts the real rustc path, and
parses the remaining arguments into a strongly-typed Info struct.
Experimental: If invoked directly (without Cargo's
-separator), it gracefully falls back to standard parsing.
2. Basic Setup & Execution
Add wrapc to your wrapper tool's dependencies:
cargo add wrapc
Here is the canonical skeleton for a RUSTC_WRAPPER executable:
use Command;
use fetch;
3. Inspecting Parsed Data
wrapc categorizes rustc flags into logical, type-safe collections.
Configurations and Crate Metadata
// --cfg and --check-cfg
for cfg in &info.configs
// --crate-type (e.g., bin, rlib, dylib)
if info.crate_types.contains
Complex Linking (-l and -L)
rustc's linking flags are highly structured. wrapc parses modifiers, kinds, and renames automatically.
use ;
// Inspecting -L (Library Search Paths)
for lib_path in &info.libpaths
// Inspecting -l (Link Libraries)
// Example parsed: `-lstatic:+bundle,+whole-archive=mylib:renamed_lib`
for link in &info.links
4. Mutating Arguments (The Wrapper Superpower)
The most common use case for a wrapper is to inject or modify flags before passing
them to rustc. Because [Info] is just a standard Rust struct, you can mutate it freely.
[Info::to_args()] will flawlessly reconstruct the CLI string.
let mut info = fetch.unwrap;
// Inject a custom linker argument
info.codegen_opts.push;
// Force a specific optimization level for a specific crate
if info.crate_name.as_deref == Some
// Add a custom configuration flag
info.configs.push;
// Pass to rustc
let args = info.to_args;
5. Handling Edge Cases & "Unknown" Flags
rustc has hundreds of flags, including unstable nightly flags (-Z) and internal
compiler queries. wrapc captures everything it explicitly knows, and safely
buckets the rest into info.unknown.
The -- Separator
In rustc, anything after -- is usually ignored or passed to specific internal tools.
wrapc stops parsing at -- and dumps the rest into [Info::unknown] to prevent
accidental misinterpretation.
// If rustc is called with: `rustc --crate-name foo -- --some-internal-flag`
assert_eq!;
assert_eq!;
Unrecognized / Nightly Flags
If a user passes a brand new nightly flag that wrapc hasn't explicitly modeled
yet, it won't crash. It goes to [Info::unknown] and is perfectly
preserved by [Info::to_args()].
for unknown_arg in &info.unknown
The stdin Edge Case (-)
Tools like cargo or IDEs sometimes pass - as the input file
to tell rustc to read source code from stdin.
Because Cargo's wrapper protocol also uses - as a separator
(<wrapper> - <rustc>), naive parsers break here.
wrapc handles this contextually:
// Invocation: `my_wrapper - rustc -`
let info = fetch.unwrap;
assert_eq!;
assert_eq!; // Correctly identified as input!
6. Testing Your Wrapper
To test your wrapper locally without publishing it, use the RUSTC_WRAPPER environment variable:
# Build your wrapper
# Point Cargo to your wrapper
# Run a standard cargo command
Your wrapper will intercept every rustc invocation, allowing you to log,
mutate, or cache the compilation process using the type-safe
data provided by wrapc.