Cargo idea: fixing `rerun-if-changed` modularity

There's a subtle footgun to emitting cargo:rerun-if-changed=PATH from buildscript libraries — if no rerun-if-changed is emitted, the buildscript is rerun for any change within the package directory, whereas once the first directive is emitted, only requested paths trigger reruns. Buildscript libraries are in a bit of a catch-22: if they don't emit the directive, they might not get rerun when required, and if they do, they might break reruns for functionality (e.g. in their downstream) which doesn't emit the directive and relies on default rerun behavior.

The status quo mostly seems to be to just use the directive (for anything outside the package directory it's required if you want to get rerun for changes) and accept that anything relying on the sans-directive default is fragile and will get its rerun detection broken.

The idea: make emitting cargo::rerun-if-changed=PATH (new syntax in 1.77 uses ::) not disable the automatic package directory scan, and require using specifically cargo::rerun-if-changed=build.rs (or whatever path package.build is set to, if it's not default) to do so. If we made this change to current nightly 1.79, very few buildscripts would regress to rerunning more often than currently — only those written against msrv 1.77 or 1.78.

Then we could also make edition=2024 buildscripts not get the default rerun scan, asking them to use cargo::rerun-if-changed=. if they really do want that behavior. With this the detective becomes purely additive and buildscripts no longer need to remember to start with a println!("cargo:rerun-if-changed=build.rs") to opt out of the fragile default. Even without the change to directive behavior this seems desirable to me, but changing cargo::rerun-if-changed to leave the default in place makes it actually properly compose across libraries written with different editions.

2 Likes

Relevant/related issues:

3 Likes