More granual `build.rs` companion: `links.rs`
I agree this is a problem, but adding more build-time executables adds even more problems.
It's not even a big improvement. It gives Cargo only one bit of information and one coarse lever to pull at the cost of making a more cumbersome API for crates and more expensive builds.
Build and link don't split that cleanly
"-sys" build scripts can often be configured to either find a pre-built library or build from a vendored source (as a fallback). Then they may need to run bindgen on whatever version that has been found or built. Even when bundling pre-generated bindings, the script may need to know which version of the library to target.
So you can't have the code generation part live in complete isolation without the find-or-build part. With separate scripts at best you could duplicate the find-vs-vendored logic, and hope it matches across both script runs, and doesn't get half-cached, or otherwise you may get a crashy ABI mismatch.
Build scripts themselves are expensive.
These are extra targets to build and link. Debug builds generate lots of code bloat to link.
On Windows and macOS you may have an antivirus/corporate spyware, and/or code signing checks that delay the first run of all new executables. Having more build-time executables multiplies that cost.
Cargo has an annoying non-composable interpretation of rerun-if-changed, which forces build-time libraries to have bad defaults, which makes accurate caching of build scripts even harder. And aligning the cache directives across two scripts that must agree is even harder. There could be some config-syncing protocol between the build and link scripts, but that depends on build script authors knowing about it and using it.
And if the build script doesn't tell Cargo that it's cacheable, then Cargo is forced to re-run it and then rebuild everything that depends on it.
Build scripts have a crude API for bash, not Rust
Cargo's protocol for communication with build scripts is a combination of env vars and a custom line-based microsyntax. It has no type safety. No efficient way to communicate back and forth with Cargo. It makes scripts mostly opaque. It all relies on every author implementing the custom text protocol from scratch correctly.
I've tried to make build scripts support a totally basic thing: error messages with more than one line of text. But because of the custom in-band DIY protocol, it was a long bikseshed endeavour that ultimately failed. Passing of a string with \n is a multi-year effort with no acceptable solution! I don't want more features built on this foundation!
This should have been a native Rust API. It isn't a Rust API only because in Cargo's early history build scripts were actually in bash, not Rust. And now Rust programs suffer a bash API with all the problems that bash APIs have and none of the benefits that Rust APIs have.
So I'd rather see the build scripts in their current form scrapped entirely, rather than this cumbersome API proliferate[1]
- some turing-complete pile of hacks is still necessary to deal with all the snowflake C libraries to replicate the work done by Linux distros for all the platforms that aren't Linux and libraries that aren't in a distro, but I'd rather use a Rust API for it ↩︎
Discussion in the ATmosphere