Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bazel

Crubit is a Bazel module named rules_crubit. It provides both directions of bindings:

  • cpp_api_from_rust generates a C++ library from a rust_library. Load it from @rules_crubit//bazel:cpp_api_from_rust.bzl.
  • rust_api_from_cpp generates a Rust library from a cc_library. Load it from @rules_crubit//bazel:rust_api_from_cpp.bzl.

The examples/build_systems/bazel project is the reference setup. You can see Crubit in action by running:

git clone https://github.com/google/crubit.git
cd crubit/examples/build_systems/bazel
bazelisk run //:main

Set up your module

Add rules_crubit to your MODULE.bazel, together with the Rust and LLVM toolchains. The example project’s MODULE.bazel shows the exact versions that Crubit is tested with:

module(
    name = "demo_project",
    version = "0.0.1",
)

bazel_dep(name = "rules_crubit", version = "0.0.1")

local_path_override(
    module_name = "rules_crubit",
    path = "../../..",
)

bazel_dep(name = "rules_rust", version = "0.71.3")
bazel_dep(name = "rules_cc", version = "0.2.25")
bazel_dep(name = "toolchains_llvm", version = "1.9.0")

crate = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")
crate.from_cargo(
    name = "crates",
    manifests = [
      "//:rust_lib/Cargo.toml",
    ],
)
use_repo(crate, "crates")

crubit_toolchains = use_extension("@rules_crubit//bazel:extensions.bzl", "crubit_toolchains")
crubit_toolchains.configure(
  llvm_version = "23.1.0",
  rust_version = "nightly/2026-09-29",
)
use_repo(crubit_toolchains, "llvm_toolchain", "rust_toolchains")

register_toolchains(
  "@rust_toolchains//:all",
  "@llvm_toolchain//:all",
)

The example uses local_path_override because it lives inside the Crubit repository. In your own project, use git_override instead, and pin a commit (for example, the one the latest tag points to):

git_override(
    module_name = "rules_crubit",
    remote = "https://github.com/google/crubit.git",
    commit = "<commit SHA>",
)

crubit_toolchains.configure configures the Rust and LLVM toolchains for the build. See Toolchain requirements for the versions that work.

Also copy the example’s .bazelrc. It sets the Rust channel and the C++20 and Clang flags that Crubit expects.

C++ bindings for a Rust library

Add a cpp_api_from_rust target for your rust_library, and depend on that target from C++:

load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
load(
    "@rules_crubit//bazel:cpp_api_from_rust.bzl",
    "cpp_api_from_rust",
)
load("@rules_rust//rust:rust_library.bzl", "rust_library")

cc_binary(
    name = "main",
    srcs = ["main.cpp"],
    deps = [
        ":lib_cc_api",
    ],
)

rust_library(
    name = "lib",
    srcs = ["rust_lib/src/lib.rs"],
    edition = "2024",
    deps = [
        "@crates//base64",
    ],
)

cpp_api_from_rust(
    name = "lib_cc_api",
    crate = ":lib",
)

The header name is the Rust target’s name with .h appended, and the namespace is the target name:

#include <iostream>
#include <string_view>

#include "lib.h"
#include "support/rs_std/rs_std.h"

int main() {
  // Test we can successfully use a type from a Rust dependency.
  base64::engine::GeneralPurpose engine = lib::make_engine();
  std::cout << "Successfully linked crubit generated C++ api: "
            << lib::add(1, 2) << std::endl;

  // Test we can use bindings generated from the Rust standard library.
  rs::std::path::PathBuf temp_dir = rs::std::env::temp_dir();
  lib::Gymnastics g = lib::Gymnastics::new_(temp_dir);
  std::string_view temp_dir_str = g.as_str();
  std::cout << "Successfully used Rust std::env::temp_dir(): " << temp_dir_str
            << std::endl;
  return 0;
}

See C++ bindings for Rust libraries for what the generated API looks like, and how to customize it.

Rust bindings for a C++ library

Define a rust_api_from_cpp target next to the cc_library, and add its automatically generated <name>.hint to the cc_library’s aspect_hints:

load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_crubit//bazel:rust_api_from_cpp.bzl", "rust_api_from_cpp")
load("@rules_rust//rust:rust_binary.bzl", "rust_binary")

cc_library(
    name = "foo",
    hdrs = ["foo.h"],
    srcs = ["foo.cc"],
    aspect_hints = [":foo_rust.hint"],
)

rust_api_from_cpp(
    name = "foo_rust",
    cpp_target = ":foo",
)

rust_binary(
    name = "main",
    srcs = ["main.rs"],  # use foo_rust::...;
    deps = [":foo_rust"],
)

See Rust bindings for C++ libraries for what the generated API looks like, and how to customize it.