Cargo->Bazel
cargo-bazel
is a Bazel repository rule for generating Rust targets using Cargo.
Setup
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# To get an accurate and up to date repository definition, See the releases page
# https://github.com/abrisco/cargo-bazel/releases
http_archive(
name = "cargo_bazel",
sha256 = "{sha256}",
urls = ["https://github.com/abrisco/cargo-bazel/releases/download/{version}/cargo_bazel.tar.gz"],
)
load("@cargo_bazel//:deps.bzl", "cargo_bazel_deps")
cargo_bazel_deps()
# It's important to set a constant for desired Rust version so it
# can easily be passed to each `crates_repository` definition.
RUST_VERSION = "1.54.0"
load("@rules_rust//rust:repositories.bzl", "rust_repositories")
rust_repositories(version = RUST_VERSION)
crates_repository
Workflows
The crates_repository
rule (the primary repository rule of cargo-bazel
) supports a number of different ways users
can express and organize their dependencies. The most common are listed below though there are more to be found in
the ./examples directory.
Cargo Workspaces
One of the simpler ways to wire up dependencies would be to first structure your project into a Cargo workspace.
The crates_repository
rule can ingest a the root Cargo.toml
file and generate dependencies from there.
load("@cargo_bazel//:defs.bzl", "crate", "crates_repository")
crates_repository(
name = "crate_index",
lockfile = "//:Cargo.Bazel.lock",
manifests = ["//:Cargo.toml"],
)
load("@crate_index//:defs.bzl", "crate_repositories")
crate_repositories()
The generated crates_repository
contains helper macros which make collecting dependencies for Bazel targets simpler.
Notably, the all_crate_deps
and aliases
macros commonly allow the Cargo.toml
files to be the single source of
truth for dependencies. Since these macros come from the generated repository, the dependencies and alias definitions
they return will automatically update BUILD targets.
load("@crate_index//:defs.bzl", "aliases", "all_crate_deps")
load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
rust_library(
name = "lib",
aliases = aliases(),
deps = all_crate_deps(
normal = True,
),
proc_macro_deps = all_crate_deps(
proc_macro = True,
),
)
rust_test(
name = "unit_test",
crate = ":lib",
aliases = aliases(
normal_dev = True,
proc_macro_dev = True,
),
deps = all_crate_deps(
normal_dev = True,
),
proc_macro_deps = all_crate_deps(
proc_macro_dev = True,
),
)
Direct Packages
In cases where Rust targets have heavy interractions with other Bazel targests (Cc, Proto, etc.),
maintaining Cargo.toml
files may have deminishing returns as things like rust-analyzer begin to be confused
about missing targets or environment variables defined only in Bazel. In workspaces like this, it may be desirable
to have a "Cargo free" setup. crates_repository
supports this through the packages
attribute.
load("@cargo_bazel//:defs.bzl", "crate", "crates_repository", "render_config")
crates_repository(
name = "crate_index",
lockfile = "//:Cargo.Bazel.lock",
packages = {
"async-trait": crate.spec(
version = "0.1.51",
),
"mockall": crate.spec(
version = "0.10.2",
),
"tokio": crate.spec(
version = "1.12.0",
),
},
# Setting the default package name to `""` forces the use of the macros defined in this repository
# to always use the root package when looking for dependencies or aliases. This should be considered
# optional as the repository also exposes alises for easy access to all dependencies.
render_config = render_config(
default_package_name = ""
),
)
load("@crate_index//:defs.bzl", "crate_repositories")
crate_repositories()
Consuming dependencies may be more ergonomic in this case through the aliases defined in the new repository.
load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
rust_library(
name = "lib",
deps = [
"@crate_index//:tokio",
],
proc_macro_deps = [
"@crate_index//:async-trait",
],
)
rust_test(
name = "unit_test",
crate = ":lib",
deps = [
"@crate_index//:mockall",
],
)