rust-lang / cargo

The Rust package manager
https://doc.rust-lang.org/cargo
Apache License 2.0
12.66k stars 2.41k forks source link

slow compilation with multiple [[bin]] and build.rs #3724

Closed philippkeller closed 7 years ago

philippkeller commented 7 years ago

With this Cargo.toml:

[package]
name = "buildrs"
version = "0.1.0"
authors = ["Philipp Keller"]
build = "my_build.rs"

[dependencies]

[[bin]]
name="foo"
path = "src/bin/foo.rs"
[[bin]]
name="bin01"
path = "src/bin/hello.rs"
[[bin]]
name="bin02"
path = "src/bin/hello.rs"
...

and an empty my_build.rs (fn main() {})

After an initial cargo build, when I change something in foo.rs (e.g. enter a newline) then it takes around 4 seconds to build:

$ cargo build
warning: file found to be present in multiple build targets: /Users/philipp/oss/playground/buildrs/src/bin/hello.rs
   Compiling buildrs v0.1.0 (file:///Users/philipp/oss/playground/buildrs)
    Finished dev [unoptimized + debuginfo] target(s) in 4.2 secs

Whereas when I remove build = "my_build.rs" from Cargo.toml and then again change something in foo.rs the build is much faster:

$ cargo build
warning: file found to be present in multiple build targets: /Users/philipp/oss/playground/buildrs/src/bin/hello.rs
   Compiling buildrs v0.1.0 (file:///Users/philipp/oss/playground/buildrs)
    Finished dev [unoptimized + debuginfo] target(s) in 0.65 secs

This happens both on stable as on stable and on nightly. Current setup:

$ rustup show
Default host: x86_64-apple-darwin

installed toolchains
--------------------

stable-x86_64-apple-darwin
nightly-x86_64-apple-darwin (default)

active toolchain
----------------

nightly-x86_64-apple-darwin (default)
rustc 1.17.0-nightly (62eb6056d 2017-02-15)

Why is that? Can I fix this somehow? I tried moving the build = ... directive to [[bin]] but that is not supported it seems.

Here is a sample project to reproduce: buildrs.tar.gz

alexcrichton commented 7 years ago

Ah yes this is an unfortunate consequence of build scripts today. Cargo doesn't understand what the inputs to build scripts are so it conservatively assumes everything is an input to the build script. This means that when a file changes the build script is rerun, which forces everything to be recompiled. (if you run cargo build -v you'll see the compilations).

You can change this, though, with the rerun-if-changed build script key which will tell Cargo specifically what the inputs are, only rerunning the build script when those inputs change.

Does that fix the problem locally for you?

philippkeller commented 7 years ago

Yes, rerun-if-changed fixes this issue for me. I have put this into my_build.rs:

extern crate gcc;

fn main() {
    gcc::compile_library("libthread-cleanup.a", &["src/bin/11-threads/thread-cleanup.c", "src/bin/11-threads/hello.c"]);
    println!("cargo:rerun-if-changed=src/bin/11-threads/thread-cleanup.c");
    println!("cargo:rerun-if-changed=src/bin/11-threads/hello.c");
}

So apparently you can "watch" multiple c files this way and can be very specific about when to rebuild the whole project. This solves my case as it now only rebuilds the whole project if I do any change in my_build.rs or in one of the two .c files.

@alexcrichton thanks for the quick reply and for the nice gcc crate!