| # Crubit: C++/Rust Bidirectional Interop Tool |
| |
| [](https://buildkite.com/bazel/crubit) |
| |
| NOTE: Crubit currently expects deep integration with the build system, and is |
| difficult to deploy to environments dissimilar to Google's monorepo. External |
| contributions are accepted, but may in some cases be difficult to integrate |
| for tooling reasons. See [CONTRIBUTING](CONTRIBUTING). Both of these are being worked on, see |
| https://github.com/google/crubit/blob/main/docs/overview/status.md#usage-outside-of-google |
| |
| Crubit is a bidirectional bindings generator for C++ and Rust, with the goal of |
| integrating the C++ and Rust ecosystems. |
| |
| ## Status |
| |
| See the [status](http://<internal link>/overview/status) page for an overview of the |
| current supported features. |
| |
| ## Example |
| |
| <section class="tabs"> |
| |
| ### C++ {.new-tab} |
| |
| Consider the following C++ function: |
| |
| ```c++ |
| bool IsGreater(int lhs, int rhs); |
| ``` |
| |
| This function, if present in a header file which is processed by Crubit, becomes |
| callable from Rust as if it were defined as: |
| |
| ```rs |
| pub fn IsGreater(lhs: ffi::c_int, rhs: ffi::c_int) -> bool {...} |
| ``` |
| |
| Note: There are some temporary restrictions on the API shape. For example, |
| functions that accept a type like `std::vector` can't be called from Rust |
| directly via Crubit. These restrictions will be relaxed over time. |
| |
| ### Rust {.new-tab} |
| |
| Consider the following Rust function: |
| |
| ```rust |
| pub fn is_greater(lhs: i32, rhs: i32) -> bool { ... } |
| ``` |
| |
| This function becomes callable from C++ as if it were defined as: |
| |
| ```c++ |
| bool is_greater(int32_t lhs, int32_t rhs); |
| ``` |
| |
| Note: There are some temporary restrictions on the API shape. For example, |
| functions that accept two mutable references can't be called from C++ |
| directly via Crubit. These restrictions will be relaxed over time. |
| |
| </section> |
| |
| ## Getting Started |
| |
| We have detailed walkthroughs on how to use C++ from Rust, or Rust from C++, |
| using Crubit, as well as copy-pastable example code. The example code also |
| includes spanshots of what the generated bindings look like. |
| |
| * Walkthrough: |
| [Rust Bindings for C++ Libraries](https://github.com/google/crubit/tree/main/docs/cpp/) |
| * Examples: |
| [`examples/cpp/`](http://examples/cpp) |
| * Walkthrough: |
| [C++ Bindings for Rust Libraries](https://github.com/google/crubit/tree/main/docs/rust/) |
| * Examples: |
| [`examples/rust/`](http://examples/rust) |
| |
| ## Building Crubit |
| |
| ### Cargo |
| |
| Prerequisites: |
| * Requires LLVM and Clang libraries to be built and installed. |
| * They must be built with support for compression (zlib), which is the default |
| build config. |
| * Requires Abseil libraries to be built and installed. |
| * Requires zlib (e.g. libz.so) to be available in the system include and lib |
| paths. |
| * An up-to-date stable Rust toolchain. |
| |
| Linux-specific setup: |
| ```sh |
| # Choice of compiler is optional. |
| export CC=/path/to/clang |
| export CXX=/path/to/clang++ |
| |
| # We must use `lld` linker via clang. It must be in the PATH. |
| export PATH="$PATH:/dir/containing/lld" |
| export RUSTFLAGS="$RUSTFLAGS -Clinker=/path/to/clang" |
| export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-fuse-ld=lld" |
| |
| # If you want to use a sysroot. |
| # SYSROOT_FLAG=--sysroot=$SYSROOT |
| # export CXXFLAGS="$CXXFLAGS $SYSROOT_FLAG" |
| # export RUSTFLAGS="$RUSTFLAGS -Clink-arg=$SYSROOT_FLAG" |
| ``` |
| |
| MacOS-specific setup: |
| ```sh |
| export CC=clang |
| export CXX=clang++ |
| export RUSTFLAGS="$RUSTFLAGS -Clinker=clang" |
| export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-fuse-ld=lld" |
| |
| # Point to the Xcode sysroot. |
| export CXXFLAGS="$CXXFLAGS -isysroot $(xcrun --show-sdk-path)" |
| export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-isysroot -Clink-arg=$(xcrun --show-sdk-path)" |
| ``` |
| |
| Windows-specific setup: |
| * **Windows is currently unsupported**, and the APIs generated by Crubit may |
| not compile and will change over time. |
| * All commands must be run from a development shell, where MSVC environment |
| variables are set up. |
| ```sh |
| # We use clang compiler (clang-cl); MSVC may work too but is unsupported. |
| export CC=clang-cl |
| export CXX=clang-cl |
| # We must use lld to link, which is spelt lld-link. So user-specified linker |
| # flags must be in MSVC format. |
| export RUSTFLAGS="$RUSTFLAGS -Clinker=/path/to/lld-link" |
| |
| # LLVM was built with Zlib support. Point Crubit to the same library. |
| export CXXFLAGS="$CXXFLAGS /I/path/to/zlib" |
| export RUSTFLAGS="$RUSTFLAGS -Clink-arg=/LIBPATH:/path/to/zlib" |
| |
| # Avoid deprecation warnings. |
| export CXXFLAGS="$CXXFLAGS /D_CRT_SECURE_NO_DEPRECATE" |
| |
| # If LLVM (-DCMAKE_MSVC_RUNTIME_LIBRARY) and Abseil (-DABSL_MSVC_STATIC_RUNTIME) |
| # are built against static CRT, then Rust needs to match, or vice-versa. |
| # export RUSTFLAGS="$RUSTFLAGS -Ctarget-feature=+crt-static" |
| ``` |
| |
| Run the build step via cargo: |
| ```sh |
| # Paths for Crubit's cargo to use. |
| ## This path contains clang/ and llvm/ dirs with their respective headers. |
| export CLANG_INCLUDE_PATH=/path/to/llvm/and/clang/headers |
| ## This path contains libLLVM*.a and libclang*.a. |
| export CLANG_LIB_STATIC_PATH=/path/to/llvm/and/clang/libs |
| ## This path contains absl/ dir with all the includes. |
| export ABSL_INCLUDE_PATH=/path/to/absl/include/dir |
| ## This path contains libabsl_* |
| export ABSL_LIB_STATIC_PATH=/path/to/absl/libs |
| |
| cargo build --bin rs_bindings_from_cc |
| ``` |
| |
| ### Bazel |
| |
| ```sh |
| apt install clang lld bazel |
| git clone git@github.com:google/crubit.git |
| cd crubit |
| bazel build --linkopt=-fuse-ld=/usr/bin/ld.lld //rs_bindings_from_cc:rs_bindings_from_cc_impl |
| ``` |
| |
| #### Using a prebuilt LLVM tree |
| |
| ```sh |
| git clone https://github.com/llvm/llvm-project |
| cd llvm-project |
| CC=clang CXX=clang++ cmake -S llvm -B build -DLLVM_ENABLE_PROJECTS='clang' -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=install |
| cmake --build build -j |
| # wait... |
| cmake --install build |
| cd ../crubit |
| LLVM_INSTALL_PATH=../llvm-project/install bazel build //rs_bindings_from_cc:rs_bindings_from_cc_impl |
| ``` |