rust-lang / cc-rs

Rust library for build scripts to compile C/C++ code into a Rust library
https://docs.rs/cc
Apache License 2.0
1.88k stars 458 forks source link

Unable to link stdc++ for example when building rust library #1206

Open mjforan opened 2 months ago

mjforan commented 2 months ago

Problem: cc is unable to find stdc++ when building an example for a library crate.

✅ works: binary crate, call c++ from example


lib_x/
  src/
    main.rs
  examples/
    test.cpp            #include <iostream>
    lib_x_example.rs    call function in test.cpp

✅ works: library crate, call c++ from library


lib_x/
  src/
    test.cpp            #include <iostream>
    lib.rs              call function in test.cpp
  examples/
    lib_x_example.rs    call function in lib.rs

❌ does not work: library crate, call c++ from example


lib_x/
  src/
    lib.rs
  examples/
    test.cpp            #include <iostream>
    lib_x_example.rs    call function in test.cpp

build.rs:

  println!("cargo:rerun-if-changed=examples/test.cpp");
  cc::Build::new()
  .cpp(true)
  .file("examples/test.cpp")
  .cpp_link_stdlib("stdc++")
  .compile("test_cpp");

Error output:

error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/mjforan/.cargo/bin:/home/mjforan/.local/bin:/usr/local/cuda-11.6/bin:/home/mjforan/.local/bin:/usr/local/cuda-11.6/bin:/home/mjforan/.local/bin:/home/mjforan/.local/bin:/usr/local/cuda-11.6/bin:/home/mjforan/anaconda3/bin:/home/mjforan/anaconda3/condabin:/home/mjforan/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin" VSLANG="1033" "cc" "-m64" "/tmp/rustcUr3FJl/symbols.o" "/home/mjforan/lib_x/target/release/examples/lib_x_example-06693f701c91b976.lib_x_example.a5b620fa4eee2b6-cgu.0.rcgu.o" "/home/mjforan/lib_x/target/release/examples/lib_x_example-06693f701c91b976.e6xnw6b1lbeu1tod6egd8g1j6.rcgu.o" "-Wl,--as-needed" "-L" "/home/mjforan/lib_x/target/release/deps" "-L" "/home/mjforan/lib_x/target/release/build/lib_x-e4aaea58acf14dea/out" "-L" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bdynamic" "-ltest_cpp" "-Wl,-Bstatic" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-1c4b19562077c20d.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-85a631ebc91746e0.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libobject-fdace1a0b4cda412.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libmemchr-e5c28d21823e9a85.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libaddr2line-1e0edbcd516a8cce.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libgimli-77a1dc5e8fb357d6.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-8c9d2edb6dff139f.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_detect-ecadd85ae8bacc0c.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libhashbrown-67895a0c8dd8130b.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-5b4263e767961458.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libminiz_oxide-4f03d5a171522141.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libadler-9e4e8543de06315e.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-7fc51dfce9c057eb.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcfg_if-7ec98a9b1cc1792f.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-2f9b4333f6d32438.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-b6fe0262c36c500a.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-2a862c0b1c86f483.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-500f37ee5bcf0ffe.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-06dfbf1de02fde3b.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/home/mjforan/lib_x/target/release/examples/lib_x_example-06693f701c91b976" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-Wl,--strip-debug" "-nodefaultlibs"
  = note: /usr/bin/ld: /home/mjforan/lib_x/target/release/build/lib_x-e4aaea58acf14dea/out/libtest_cpp.a(b688d06bea1307b6-test.o): in function `test_cpp':
          test.cpp:(.text.test_cpp+0x19): undefined reference to `std::cout'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x21): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x47): undefined reference to `std::ostream::put(char)'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x64): undefined reference to `std::ctype<char>::_M_widen_init() const'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x89): undefined reference to `std::__throw_bad_cast()'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x55): undefined reference to `std::ostream::flush()'
          collect2: error: ld returned 1 exit status

  = note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib)

error: could not compile `lib_x` (example "lib_x_example") due to 1 previous error

Environment: Raspberry Pi OS (aarch64) and Ubuntu 24.04 (amd64) with fresh install of Rust: cargo 1.81.0, cc="1.1.18"

I have tried setting various options in build.rs such as manually specifying the path to /lib/aarch64-linux-gnu/libstdc++.so.6

NobodyXu commented 2 months ago

Sorry I don't understand how you are calling std::cout, can you provide the code please?

Just including the header file won't work, and linking with stdc++ also won't work.

C++ ABI is different from rust with its own name demangles so that it can support function overloading.

mjforan commented 2 months ago

test.cpp

#include <iostream>
extern "C" void test_cpp() {
    std::cout<<"hello"<<std::endl;
}

lib_x_example.rs

#[link(name = "test_cpp")]
extern { fn test_cpp(); }

pub fn main() {
    unsafe {test_cpp();}
}
NobodyXu commented 2 months ago

Thanks.

And can you also show me the build.rs ?

Because you would need it to be put in the workspace, not in src or examples, but in the workspace .

Only when it's in there, it would be automatically picked up by cargo, and run by cargo to compile and link the c++ file.

mjforan commented 2 months ago

Yes, build.rs is in the workspace, as well as Cargo.toml, Cargo.lock, and the target directory. I left them out of the initial issue for simplicity. The relevant section of build.rs is listed above.

NobodyXu commented 2 months ago

Thanks.

I think you would have to define

#[link(name = "test_cpp")]
pub extern { fn test_cpp(); }

in the lib.rs, when you compile and link with an external library, you usually define the API of the external library in the library itself.

mjforan commented 2 months ago

That does not work. The example still fails to compile.

NobodyXu commented 2 months ago

Can you remove

.cpp_link_stdlib("stdc++")

I think using the default libstdc++ might work.

mjforan commented 2 months ago

No, that value is correct for my platform. Removing it does not help. https://docs.rs/cc/latest/cc/struct.Build.html#method.cpp_link_stdlib https://docs.rs/cc/latest/cc/#c-support

Would you mind reproducing on your end? It seems you are just taking guesses here and I have already tried these things.

Another clue: when I wrap the c++ function in my lib.rs and call that method from my example, then I am also able to use the c++ function directly in my example.

NobodyXu commented 2 months ago

Would you mind reproducing on your end? It seems you are just taking guesses here and I have already tried these things.

Sorry I don't have access to my computer for now.

Another clue: when I wrap the c++ function in my lib.rs and call that method from my example, then I am also able to use the c++ function directly in my example.

Yes, that's pretty much how most crates use cc and external crates.

Creates bindings for it in rust library crate, then call the library crate.

I think the error you experience might be due to how rust links crates.

AFAIK, cargo does a linking per crate to produce rlib, so if the external library function isn't used in the library crate, it might strip out the unused linkage to stdc++.

Since rlib isn't stablised and mostly internal to cargo, there isn't much documentation on it.

For that particular behavior, it might be faster to ask on rust's official zulip in t-cargo channel