rust-lang / cmake-rs

Rust build dependency for running cmake
https://docs.rs/cmake
Apache License 2.0
301 stars 121 forks source link

Parallel builds break when combined with cc-rs using the "parallel" feature #172

Open Notgnoshi opened 1 year ago

Notgnoshi commented 1 year ago

TL;DR - cmake-rs 0.1.49 breaks parallel builds when the build script also performs a cc-rs build with the "parallel" feature enabled.

I don't think this is a cmake-rs bug, but it's the cmake-rs behavior that isn't right, so I thought this was the right place to file the issue.


At work, we have a project that uses https://github.com/dtolnay/cxx to wrap a quite large CMake C++ library with Rust bindings. I can't share the project, but the build script looks something like

fn main() {
    let cxxbridge_source_files = vec![ ... ];
    cxx_build::bridges(&cxx_bridge_source_files)
        .include("src/")
        .flag("-std=c++11")
        .compile("cxxbridge-foo");

    let install_dir = cmake::Config::new("submodules/foo")
        .cxxflag("-w")
        .build();

    println!("cargo:rustc-link-search=native={}/build/lib/", install_dir.display());
    println!("cargo:rustc-link-lib=static=foo");

    println!("cargo:rerun-if-changed=submodules/foo/src/");
    println!("cargo:rerun-if-changed=submodules/foo/include/");
    println!("cargo:rerun-if-changed=src/cpp/");
    for source_file in cxxbridge_source_files {
        println!("cargo:rerun-if-changed={}", source_file);
    }
}

I was able to reproduce the issue with a much smaller example project here: https://github.com/Notgnoshi/cmake-jobserver-bug. I tried to use the commit history to help narrow in on the issue.


My attempt at explaining the example project

$ export MAKEFLAGS=-j16
$ cargo build -vv
...
[cmake-jobserver-bug 0.1.0] -- Build files have been written to: /home/nots/workspace/cmake-jobserver-bug/target/debug/build/cmake-jobserver-bug-00c8459a6b87808e/out/build
[cmake-jobserver-bug 0.1.0] running: "cmake" "--build" "." "--target" "install" "--config" "Debug"
[cmake-jobserver-bug 0.1.0] gmake: warning: jobserver unavailable: using -j1.  Add '+' to parent make rule.
...

I see the same behavior whether I export MAKEFLAGS=-j16 or NUM_JOBS=16.

When I update the cmake-rs submodule to revert the commits

Submodule cmake-rs 07cbf8f..cfe11fc (rewind):
  < Merge pull request #166 from atouchet/http
  < Merge pull request #165 from thomcc/bump-version
  < Use SPDX-compatible license format
  < Remove support for publishing to gh-pages (docs.rs exists now)
  < Update links in Cargo.toml
  < Disable some targets where zlib seems to no longer compile
  < use jobserver if available

the behavior changes:

$ export MAKEFLAGS=-j16
$ cargo build -vv
...
[cmake-jobserver-bug 0.1.0] -- Build files have been written to: /home/nots/workspace/cmake-jobserver-bug/target/debug/build/cmake-jobserver-bug-5d863cf04a2d52eb/out/build
[cmake-jobserver-bug 0.1.0] running: "cmake" "--build" "." "--target" "install" "--config" "Debug" "--parallel" "16"
[cmake-jobserver-bug 0.1.0] gmake: warning: -j16 forced in submake: resetting jobserver mode.
...

but I still don't think this is right - it should share the same jobserver as cc-rs and cargo instead if starting a new one for just the spdlog build.


What I think the cause is

Notice that in the Cargo.toml, there's something suspicious:

# cc is a dependency of cxx-build. The "parallel" feature is not turned on by default, but it
# significantly decreases the compile time, so we want to enable it.
# cxx-build pins the cc version, so to enable the "parallel"
# feature, we do NOT pin the version, so that we get whatever version cxx-build pinned.
cc = { version = "*", features = ["parallel"] }

We do this in our work project, because the cxx_bridge build is slow (it's a quite large project, and there's lots of files). Building in parallel made things magically better <3

If we instead do

cc = { version = "1.0", features = [] }

the spdlog parallel CMake build works as expected (I visually verified that the build was parallel by watching htop in a separate window)

$ export MAKEFLAGS=-j16
$ cargo build -vv
...
[cmake-jobserver-bug 0.1.0] -- Build files have been written to: /home/nots/workspace/cmake-jobserver-bug/target/debug/build/cmake-jobserver-bug-e634a5cc4fbc3085/out/build
[cmake-jobserver-bug 0.1.0] running: "cmake" "--build" "." "--target" "install" "--config" "Debug"
...

but the cc-rs build (and the cxx_bridge build in our work project) is no longer parallel :(


I think this points the finger at an awkward jobserver interplay between cmake-rs and cc-rs when the cc-rs "parallel" feature is enabled.


version details:

Notgnoshi commented 1 year ago

I'm now convinced this is a cc-rs issue. Using binary search, I was able to narrow it down to the 1.0.42 release of cc-rs, which is the release that first included jobserver support. So I don't think this is a regression, just unexpected behavior. I'll go file a cc-rs issue shortly.