Linking to a static library (.a) built with C++ on macOS

I'd like to use a static library that I have built with C++ in a Rust project (more specifically, a Tauri based GUI app). I'm having trouble when trying to link to the library, and to compile the project with Rust.

It's a very simple C++ function:

#include <iostream>
#include "greeting.h"

void say_greeting() {
    std::cout << "Hello from C++" << std::endl;
}

Full source code can be found here:

There are currently three directories within that repo, one for the library code, one for a test C++ program that includes the library and runs the function, and then another for rust to do the same thing.

It seems to work ok on Windows, as I can see the expected output:

Hello, world! from Rust
Hello from C++

But on macOS, when compiling with Rust, I see this error:

   Compiling rust-static-lib v0.1.0 (/Users/davidwinter/Developer/rust-static-lib/rust-app)
error: linking with `cc` failed: exit status: 1
  |
  = note: env -u IPHONEOS_DEPLOYMENT_TARGET -u TVOS_DEPLOYMENT_TARGET LC_ALL="C" PATH="/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/bin:/Users/davidwinter/.proto/shims:/Users/davidwinter/.proto/bin:/Users/davidwinter/.cargo/bin:/Users/davidwinter/.local/bin:/opt/homebrew/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Library/Apple/usr/bin:/usr/local/MacGPG2/bin:/Users/davidwinter/.proto/shims:/Users/davidwinter/.proto/bin" VSLANG="1033" ZERO_AR_DATE="1" "cc" "-arch" "arm64" "/var/folders/80/8c17b3vd2wzfyvvw5m9ld66w0000gn/T/rustcLPq4Vd/symbols.o" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps/rust_static_lib-7ef688efb1707c23.182iz7qytmuq8lr6.rcgu.o" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps/rust_static_lib-7ef688efb1707c23.19y32txsnixsxvsq.rcgu.o" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps/rust_static_lib-7ef688efb1707c23.1fs5yvq7m4jez12.rcgu.o" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps/rust_static_lib-7ef688efb1707c23.1hy0aus4vb643lnx.rcgu.o" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps/rust_static_lib-7ef688efb1707c23.6rmoruu7miuuyua.rcgu.o" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps/rust_static_lib-7ef688efb1707c23.hrd86soi3yojoi1.rcgu.o" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps/rust_static_lib-7ef688efb1707c23.57okrvvevp50vgdn.rcgu.o" "-L" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps" "-L" "../cpp-lib/build/lib" "-L" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib" "-lgreeting" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libstd-d7097f83793f285d.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libpanic_unwind-50e7fd4712e1104c.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libobject-9bf4c2305270bb3d.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libmemchr-b9180b0bd18086ab.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libaddr2line-09f75b2a7a30a183.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libgimli-72b430ce2d1ca406.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/librustc_demangle-8016ac6fb72599e3.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libstd_detect-13855c7195db552b.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libhashbrown-c4874185cc82a43a.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/librustc_std_workspace_alloc-6ef0176aaa60ff0c.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libminiz_oxide-1e9f0e423eed4f7c.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libadler-263f3ba6f4d2645b.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libunwind-85e43ed53a81d633.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libcfg_if-279824e18f4fd20b.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/liblibc-b7ead8c5aa11dde6.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/liballoc-37d126161ada8ba6.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/librustc_std_workspace_core-c7113231a51981ef.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libcore-0e8873809402687b.rlib" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libcompiler_builtins-c3f3955ff7203236.rlib" "-lSystem" "-lc" "-lm" "-L" "/Users/davidwinter/.rustup/toolchains/1.78.0-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib" "-o" "/Users/davidwinter/Developer/rust-static-lib/rust-app/target/debug/deps/rust_static_lib-7ef688efb1707c23" "-Wl,-dead_strip" "-nodefaultlibs"
  = note: Undefined symbols for architecture arm64:
            "std::__1::locale::use_facet(std::__1::locale::id&) const", referenced from:
                std::__1::ctype<char> const& std::__1::use_facet[abi:ue170006]<std::__1::ctype<char>>(std::__1::locale const&) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::ios_base::getloc() const", referenced from:
                std::__1::basic_ios<char, std::__1::char_traits<char>>::widen[abi:ue170006](char) const in libgreeting.a[2](greeting.cpp.o)
            "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__init(unsigned long, char)", referenced from:
                std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::basic_string[abi:ue170006](unsigned long, char) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::~basic_string()", referenced from:
                std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>> std::__1::__pad_and_output[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>>, char const*, char const*, char const*, std::__1::ios_base&, char) in libgreeting.a[2](greeting.cpp.o)
                std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>> std::__1::__pad_and_output[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>>, char const*, char const*, char const*, std::__1::ios_base&, char) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::basic_ostream<char, std::__1::char_traits<char>>::put(char)", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::endl[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::basic_ostream<char, std::__1::char_traits<char>>::flush()", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::endl[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::basic_ostream<char, std::__1::char_traits<char>>::sentry::sentry(std::__1::basic_ostream<char, std::__1::char_traits<char>>&)", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::basic_ostream<char, std::__1::char_traits<char>>::sentry::~sentry()", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in libgreeting.a[2](greeting.cpp.o)
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::cout", referenced from:
                _say_greeting in libgreeting.a[2](greeting.cpp.o)
            "std::__1::ctype<char>::id", referenced from:
                std::__1::ctype<char> const& std::__1::use_facet[abi:ue170006]<std::__1::ctype<char>>(std::__1::locale const&) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::locale::~locale()", referenced from:
                std::__1::basic_ios<char, std::__1::char_traits<char>>::widen[abi:ue170006](char) const in libgreeting.a[2](greeting.cpp.o)
                std::__1::basic_ios<char, std::__1::char_traits<char>>::widen[abi:ue170006](char) const in libgreeting.a[2](greeting.cpp.o)
            "std::__1::ios_base::__set_badbit_and_consider_rethrow()", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in libgreeting.a[2](greeting.cpp.o)
            "std::__1::ios_base::clear(unsigned int)", referenced from:
                std::__1::ios_base::setstate[abi:ue170006](unsigned int) in libgreeting.a[2](greeting.cpp.o)
            "std::terminate()", referenced from:
                ___clang_call_terminate in libgreeting.a[2](greeting.cpp.o)
            "___cxa_begin_catch", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in libgreeting.a[2](greeting.cpp.o)
                ___clang_call_terminate in libgreeting.a[2](greeting.cpp.o)
            "___cxa_end_catch", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in libgreeting.a[2](greeting.cpp.o)
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in libgreeting.a[2](greeting.cpp.o)
            "___gxx_personality_v0", referenced from:
                ../cpp-lib/build/lib/libgreeting.a[2](greeting.cpp.o)
          ld: symbol(s) not found for architecture arm64
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
          

error: could not compile `rust-static-lib` (bin "rust-static-lib") due to 1 previous error
make: *** [build.rust] Error 101

I'm at a loss of what to try next, as this is such a simple C++ example that I'm trying to get to work. I'd like for the static library to be included within the Rust executable so that the end-user would not need a dynamic library. Ideally also, I'd like to continue to be able to use cmake as the build tool for the library in future. Any help, advice or guidance would be much appreciated.

Maybe adding this in the build script works for you?

println!("cargo:rustc-flags=-l dylib=c++");

If you're building for iOS, use cargo-xcode to configure the project. Otherwise you may be accidentally using a macOS compiler or macOS dependencies for iOS libraries.

@cili thank you so much!! This worked perfectly for macOS, and I just had to make a change for Linux and Windows didn't need anything extra. In the end it looked like:

if cfg!(target_os = "macos") {
    println!("cargo:rustc-flags=-l dylib=c++");
} else if cfg!(target_os = "linux") {
    println!("cargo:rustc-flags=-l dylib=stdc++");
}

Works perfectly :ok_hand: :bowing_man:

1 Like

@cili I wonder if you know - will this have a dependency on users having the C++ library on their computer? I wonder how widespread that is? Quite likely I would assume? But I wonder if it is possible to bundle it with the app too, statically link it if possible? Are you aware of that?

Yes

Technically yes, however having more than one copy of the C++ standard library linked in can cause crashes due to symbol conflicts and certain system libraries like mesa (userspace half of the gpu driver on linux) already link to the C++ standard library.

If you're building the code with the cc crate, then call build.cpp(true). This will pick the right C++ library automatically.

Thanks - sounds best to not try and do that then. I see with Windows, you can install the C++ by downloading the runtime if not already installed on a system. I'll have to check on maOS and Ubuntu whether something similar is already installed by default.

@kornel I'm using CMake to build the C++ code outside of the Rust project (not the example repo I shared) and would prefer to keep that in place as the library will be developed independently. I'm hoping the CMake conditinal I've now added should be sufficient.

On macOS libc++ (the C++ standard library used by default for clang) is part of the OS. On Ubuntu I believe libstdc++ (the C++ standard library used by default for GCC) is preinstalled.

Only if you have MacOS X 10.7 or newer. On MacOS X 10.0 to 10.6 standard library was libstdc++.

That's why by default LLVM uses libstdc++, if you don't specify anything else.

Of course in these 12 years after release of MacOS X things have changed and libstdc++ is no longer shipped by default (and I don't think you may even build it for ARM-based macOS).

Rustc only supports macOS 10.12 and later.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.