Currently build script tell Cargo what to do by printing cargo:foo=
directives for things like library linking, relevant env vars, warnings, and custom key/value variables. And they get inputs via env vars.
It gets the job done, but it's a "stringly-typed" API and it misses out on things that Rust APIs can provide.
- rerun-if-changed has a side effect that makes it not composable
- support for arbitrary key=value syntax is an extensibility gotcha
- line based API doesn't allow multi-line warnings
- panics from build scripts are noisy, and aren't as nicely formatted as other compilation errors
- everyone uses
println!
to specify things like search paths, but technically that's lossy forPath
s. - none of this is type checked, there's no rust-analyzer support.
Here's an alternative idea: abstract this away behind a Rust crate. Let build scripts use a native Rust API to set these things.
Details of naming and API structure are up to bikeshedding, that's just to give a general idea:
extern crate buildscript; // some build-in crate or prelude
fn main() {
let mut build = buildscript::new();
build.link("foolibrary").search_path(path); // real Path
build.warning("multiple\nlines\nhey!");
build.set_metadata("key", "value");
// helper to automatically mark as rerun-if-env-changed=FOO
if let Some(var) = build.env_var("FOO") {
}
let out = build.out_dir();
// future ideas
if build.rust_newer_than("1.75.1") {
}
}
I know this currently can be provided by a crate (and the API should be prototyped and ironed out as a crate), But having this as a first-party crate would allow keeping the stringly-printed protocol as an implementation detail between Cargo and its Rust API, thus allowing it be extensible. Also authors of build scripts may not want to pull in a 3rd party crates-io crate for a task that isn't strictly necessary, but when that is a built-in officially blessed API, there's no excuse.