In general, I think moving away from the model of “run build script once and then use its side-effects” is a good idea.
For example, as an intermediate step from where we are now, I’d like to see a build script have a bit more of an API like something using the test harness:
-
build-script -l - list the things this script can do
-
build-script <thing> - perform one of those things, which could emit all the inputs to that thing
-
build-script -q <thing> - cheaply check whether <thing> is out of date or not
Also I’m thinking something like a lib would need to explicitly declare a dep on a build script with something like:
[lib]
buildscript = thing1 thing2 # requires things from a build script
This would also allow you to have multiple build scripts, so long as they have distinct things to disambiguate them.
One of the problems right now is that Cargo.toml doesn’t explicitly list the input sources to a crate - instead it works out the top-level source then asks rustc to generate the full list of sources. But this will fail if the sources don’t exist yet because they haven’t been generated. I assume that currently cargo always unconditionally runs the build script before trying to compile anything else, but it would be nice to defer build script work until as late as possible. (It also has pretty ad-hoc ways of determining whether it needs to recompile/rerun the build script.)
I’m being deliberately vague about what thing actually is, but definitely falls along the lines @Manishearth mentions.
To expand on this a bit: if thing represents - say - a dependency on an external library (“I want to bind with openssl”) then it means we can abstract it away. In a pure Cargo build it could be a build script which either builds it from source or uses pkgconfig to find the system openssl; but if we’re embedded in - say - a Buck build environment then it maps to “I want to take a dependency on the normal openssl target” and let Buck sort out the details.
In other words, we end up where there’s a cargo front-end generates a build plan for consumption by a build execution engine backend which can be implemented in multiple ways. In this model a build script is an implementation detail of the cargo build execution engine.
There’s also a secondary annoyance here, which is that code which expects a build script to generate a source file ends up having an explicit dependency on Cargo’s directory layout. It would be nice if we could have the build script emit “I wrote this here”, and then have the source be able to reference that logically (include!("<build script thing3>") rather than explicitly (include!(concat!(env!("OUT_DIR"), "/thing3.rs)).
EDIT: I guess we could hack this in with an env-var convention: include!(env!("BUILD_SCRIPT_thing3")), though we’d definitely want to use better terminology than “build script” here.
(cc @Xanewok)