Proposal: Add "cargo:rustc-compile-crate-without-waiting-for-build-rs" for build.rs

I have observed that a lot of crates can actually have their build.rs be run in parallel to compilation of the crate.

For example, zstd (without enabling feature "bindgen"), xz2 and libz-sys, they search for the corresponding library or build the library and link with it.

Since they do not generate any binding using bindgen in build.rs, their build.rs can be run in parallel with the compilation of the crate itself and other crates dependent on it, without blocking them until build.rs is done.

Thus, I propose that we could add a new build script instruction "cargo:rustc-compile-crate-without-waiting-for-build-rs" so that rustc/cargo can continue to build without waiting for the build.rs to complete, and only wait for it before linking.

4 Likes

In this case building the crate in parallel with the build script is unlikely to save anything. Especially if the build script itself signals that cargo can start invoking rustc as by the time cargo has processed this, the build script likely already exited. In addition cargo has to wait for the full list of libraries to link from the build script to determine which -l arguments it needs to pass to rustc.

If it is statically linked the library needs to be available in it's entirety before rustc can be invoked as the library is merged into the rlib produced by rustc. If it isn't available you will get an error like

error: could not find native static library `foo`, perhaps an -L flag is missing?

when trying to compile an rlib.

Perhaps this should be Cargo.toml key instead?

build = { path = "build.rs", crate-dependency = false }
3 Likes

Well, I didn't know that, so build.rs will block the linking stage of the current crate, but at the very least, it would unblock any crate that is dependent on this one, which means that they can be compiled in parallel instead of having to wait for build.rs to exit.

What about having "cargo:rustc-build-rs-does-not-affect-crate-api" to signal that rustc can proceed to generate the APIs of the crate so that other dependents can be built in parallel?

That could work even better, though for zstd, it has a feature "bindgen" which will generate the bindings at build time if enabled, not sure how to work with that.

Edit:

Well, perhaps we can have build-affect-api.rs and build-affect-linking.rs?

There is no way to notify rustc that all dependencies for linking are satisfied. As such they must be satisfied before rustc starts.

I remembered reading somewhere that rustc can start once all their dependencies' APIs are generated. When I looked at the timings generated by cargo b --timings, I noticed that is indeed correct.

This is true for rust dependencies where it starts once an earlier rustc invocation generated the .rmeta file and notified cargo of this. In those cases the build only depends on the .rmeta file and not the full .rlib file. For native static libraries however rustc depends on the entire static library, not just a subset, as it embeds the entire static library in the .rlib file. As such the entire static library needs to be available before rustc starts.

I have the feeling that build scripts do two different things: a) Compile and link libraries. b) Generate Rust code

While build scripts serve the secound application quite well, they have many shortcomings for the first one in particular:

i) Build scripts are compiled and executed, even in check mode.

ii) Build scripts poorly interact with each other or with external build systems of any kind.

iii) They often requiere options that need to be bubbled up through their users. (E.g. dynamic vs static linkage).

Rather them just introducing this flag, it would probably be usefull to introduce a more general way to indicate that something is used to provide some external dependency. My personal suggestion would be a different cargo-packge type (possibly embeddable into an other package) declaring what dependency and version it does provide and what linkage options (static, dynamic, etc), are supported. External dependencies could then be listed (usefull for something like cargo-deb), build in parallel to other code without ever becoming part of any .rlib and being linked into the final executable/dylib using explicit link flags set by cargo, configured globally, being replaced by a version build by an external build system, etc.

Edit: See also:

2 Likes

I think you are right, build scripts really isn't suitable for compiling and linking with external libraries.