Crate Universe
Crate Universe is a set of Bazel rule for generating Rust targets using Cargo.
This doc describes using crate_universe with bzlmod.
If you're using a WORKSPACE file, please see the WORKSPACE equivalent of this doc.
There are some examples of using crate_universe with bzlmod in the example folder.
Table of Contents
Setup
To use rules_rust in a project using bzlmod, add the following to your MODULE.bazel file:
bazel_dep(name = "rules_rust", version = "0.49.3")
You find the latest version on the release page.
After adding rules_rust
in your MODULE.bazel, set the following to begin using crate_universe
:
crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate")
// # ... Dependencies
use_repo(crate, "crates")
Dependencies
There are three different ways to declare dependencies in your MODULE.
- Cargo workspace
- Direct Dependencies
- Vendored Dependencies
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 root Cargo.toml file and generate Bazel dependencies from there. You find a complete example in the in the example folder.
crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate")
crate.from_cargo(
name = "crates",
cargo_lockfile = "//:Cargo.lock",
manifests = ["//:Cargo.toml"],
)
use_repo(crate, "crates")
The generated crates_repository contains helper macros which make collecting dependencies for Bazel targets simpler. Notably, the all_crate_deps and aliases macros ( see Dependencies API) 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. In your BUILD files, you use these macros for a Rust library as shown below:
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,
),
)
For a Rust binary that does not depend on any macro, use the following configuration in your build file:
rust_binary(
name = "bin",
srcs = ["src/main.rs"],
deps = all_crate_deps(normal = True),
)
You have to repin before your first build to ensure all Bazel targets for the macros are generated.
Dependency syncing and updating is done in the repository rule which means it's done during the
analysis phase of builds. As mentioned in the environments variable table above, the CARGO_BAZEL_REPIN
(or REPIN
) environment variables can be used to force the rule to update dependencies and potentially
render a new lockfile. Given an instance of this repository rule named crates
, the easiest way to
repin dependencies is to run:
CARGO_BAZEL_REPIN=1 bazel sync --only=crates
This will result in all dependencies being updated for a project. The CARGO_BAZEL_REPIN
environment variable can also be used to customize how dependencies are updated.
For more details about repin, please refer to the documentation.
Direct Dependencies
In cases where Rust targets have heavy interactions with other Bazel targets (Cc, Proto, etc.), maintaining Cargo.toml files may have diminishing returns as things like rust-analyzer begin to be confused about missing targets or environment variables defined only in Bazel. In situations like this, it may be desirable to have a âCargo freeâ setup. You find an example in the in the example folder.
crates_repository supports this through the packages attribute, as shown below.
crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate")
crate.spec(package = "serde", features = ["derive"], version = "1.0")
crate.spec(package = "serde_json", version = "1.0")
crate.spec(package = "tokio", default_features=False, features = ["macros", "net", "rt-multi-thread"], version = "1.38")
crate.from_specs()
use_repo(crate, "crates")
Consuming dependencies may be more ergonomic in this case through the aliases defined in the new repository. In your BUILD files, you use direct dependencies as shown below:
rust_binary(
name = "bin",
crate_root = "src/main.rs",
srcs = glob([
"src/*.rs",
]),
deps = [
# External crates
"@crates//:serde",
"@crates//:serde_json",
"@crates//:tokio",
],
visibility = ["//visibility:public"],
)
Notice, direct dependencies do not need repining. Only a cargo workspace needs updating whenever the underlying Cargo.toml file changed.
Vendored Dependencies
In some cases, it is require that all external dependencies are vendored, meaning downloaded and stored in the workspace. This helps, for example, to conduct licence scans, apply custom patches, or to ensure full build reproducibility since no download error could possibly occur. You find a complete example in the in the example folder.
For the setup, you need to add the skylib in addition to the rust rules to your MODUE.bazel.
module(
name = "deps_vendored",
version = "0.0.0"
)
###############################################################################
# B A Z E L C E N T R A L R E G I S T R Y # https://registry.bazel.build/
###############################################################################
# https://github.com/bazelbuild/bazel-skylib/releases/
bazel_dep(name = "bazel_skylib", version = "1.7.1")
# https://github.com/bazelbuild/rules_rust/releases
bazel_dep(name = "rules_rust", version = "0.49.3")
###############################################################################
# T O O L C H A I N S
###############################################################################
# Rust toolchain
RUST_EDITION = "2021"
RUST_VERSION = "1.80.1"
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
edition = RUST_EDITION,
versions = [RUST_VERSION],
)
use_repo(rust, "rust_toolchains")
register_toolchains("@rust_toolchains//:all")
###############################################################################
# R U S T C R A T E S
###############################################################################
crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate")
Note, it is important to load the crate_universe rules otherwise you will get an error as the rule set is needed in the vendored target.
Assuming you have a package called basic
in which you want to vendor dependencies,
then you create a folder basic/3rdparty
. The folder name can be arbitrary,
but by convention, its either thirdparty or 3rdparty to indicate vendored dependencies.
In the 3rdparty folder, you add a target crates_vendor to declare your dependencies to vendor.
In the example, we vendor a specific version of bzip2.
load("@rules_rust//crate_universe:defs.bzl", "crate", "crates_vendor")
crates_vendor(
name = "crates_vendor",
annotations = {
"bzip2-sys": [crate.annotation(
gen_build_script = True,
)],
},
cargo_lockfile = "Cargo.Bazel.lock",
generate_build_scripts = False,
mode = "remote",
packages = {
"bzip2": crate.spec(
version = "=0.3.3",
),
},
repository_name = "basic",
tags = ["manual"],
)
Next, you have to run Cargo build
to generate a Cargo.lock file with all resolved dependencies.
Then, you rename Cargo.lock to Cargo.Bazel.lock and place it inside the basic/3rdparty
folder.
At this point, you have the following folder and files:
basic
âââ 3rdparty
â âââ BUILD.bazel
â âââ Cargo.Bazel.lock
Now you can run the crates_vendor
target:
bazel run //basic/3rdparty:crates_vendor
This generates a crate folders with all configurations for the vendored dependencies.
basic
âââ 3rdparty
â âââ cratea
â âââ BUILD.bazel
â âââ Cargo.Bazel.lock
Suppose you have an application in basic/src
that is defined in basic/BUILD.bazel
and
that depends on a vendored dependency. You find a list of all available vendored dependencies
in the BUILD file of the generated folder: basic/3rdparty/crates/BUILD.bazel
You declare a vendored dependency in you target as following:
load("@rules_rust//rust:defs.bzl", "rust_binary")
rust_binary(
name = "hello_sys",
srcs = ["src/main.rs"],
deps = ["//basic/3rdparty/crates:bzip2"],
visibility = ["//visibility:public"],
)
Note, the vendored dependency is not yet accessible because you have to define first
how to load the vendored dependencies. For that, you first create a file sys_deps.bzl
and add the following content:
# rename the default name "crate_repositories" in case you import multiple vendored folders.
load("//basic/3rdparty/crates:defs.bzl", basic_crate_repositories = "crate_repositories")
def sys_deps():
# Load the vendored dependencies
basic_crate_repositories()
This is straightforward, you import the generated crate_repositories from the crates folder, rename it to avoid name clashes in case you import from multiple vendored folders, and then just load the vendored dependencies.
In a WORKSPACE configuration, you would just load and call sys_deps(), but in a MODULE configuration, you cannot do that.
Instead, you create a new file WORKSPACE.bzlmod
and add the following content.
load("//:sys_deps.bzl", "sys_deps")
sys_deps()
Now, you can build the project as usual.
There are some more examples of using crate_universe with bzlmod in the example folder.
crate
crate = use_extension("@rules_rust//crate_universe:docs_bzlmod.bzl", "crate") crate.from_cargo(name, cargo_config, cargo_lockfile, generate_binaries, generate_build_scripts, manifests, splicing_config, supported_platform_triples) crate.annotation(deps, data, additive_build_file, additive_build_file_content, alias_rule, build_script_data, build_script_data_glob, build_script_deps, build_script_env, build_script_proc_macro_deps, build_script_rundir, build_script_rustc_env, build_script_toolchains, build_script_tools, compile_data, compile_data_glob, crate, crate_features, data_glob, disable_pipelining, extra_aliased_targets, gen_all_binaries, gen_binaries, gen_build_script, override_target_bin, override_target_build_script, override_target_lib, override_target_proc_macro, patch_args, patch_tool, patches, proc_macro_deps, repositories, rustc_env, rustc_env_files, rustc_flags, shallow_since, version) crate.from_specs(name, cargo_config, generate_binaries, generate_build_scripts, splicing_config, supported_platform_triples) crate.spec(artifact, branch, default_features, features, git, lib, package, rev, tag, version)
TAG CLASSES
from_cargo
Generates a repo @crates from a Cargo.toml / Cargo.lock pair
Attributes
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
name | The name of the repo to generate | Name | optional | "crates" |
cargo_config | A Cargo configuration file. | Label | optional | None |
cargo_lockfile | The path to an existing Cargo.lock file | Label | optional | None |
generate_binaries | Whether to generate rust_binary targets for all the binary crates in every package. By default only the rust_library targets are generated. | Boolean | optional | False |
generate_build_scripts | Whether or not to generate cargo build scripts by default. | Boolean | optional | True |
manifests | A list of Cargo manifests (Cargo.toml files). | List of labels | optional | [] |
splicing_config | The configuration flags to use for splicing Cargo maniests. Use //crate_universe:defs.bzl\%rsplicing_config to generate the value for this field. If unset, the defaults defined there will be used. | String | optional | "" |
supported_platform_triples | A set of all platform triples to consider when generating dependencies. | List of strings | optional | ["aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", "aarch64-linux-android", "aarch64-pc-windows-msvc", "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", "arm-unknown-linux-gnueabi", "armv7-linux-androideabi", "armv7-unknown-linux-gnueabi", "i686-apple-darwin", "i686-linux-android", "i686-pc-windows-msvc", "i686-unknown-freebsd", "i686-unknown-linux-gnu", "powerpc-unknown-linux-gnu", "riscv32imc-unknown-none-elf", "riscv64gc-unknown-none-elf", "s390x-unknown-linux-gnu", "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", "wasm32-wasi", "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none"] |
annotation
Attributes
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
deps | A list of labels to add to a crate's rust_library::deps attribute. | List of strings | optional | [] |
data | A list of labels to add to a crate's rust_library::data attribute. | List of strings | optional | [] |
additive_build_file | A file containing extra contents to write to the bottom of generated BUILD files. | Label | optional | None |
additive_build_file_content | Extra contents to write to the bottom of generated BUILD files. | String | optional | "" |
alias_rule | Alias rule to use instead of native.alias() . Overrides render_config's 'default_alias_rule'. | String | optional | "" |
build_script_data | A list of labels to add to a crate's cargo_build_script::data attribute. | List of strings | optional | [] |
build_script_data_glob | A list of glob patterns to add to a crate's cargo_build_script::data attribute | List of strings | optional | [] |
build_script_deps | A list of labels to add to a crate's cargo_build_script::deps attribute. | List of strings | optional | [] |
build_script_env | Additional environment variables to set on a crate's cargo_build_script::env attribute. | Dictionary: String -> String | optional | {} |
build_script_proc_macro_deps | A list of labels to add to a crate's cargo_build_script::proc_macro_deps attribute. | List of strings | optional | [] |
build_script_rundir | An override for the build script's rundir attribute. | String | optional | "" |
build_script_rustc_env | Additional environment variables to set on a crate's cargo_build_script::env attribute. | Dictionary: String -> String | optional | {} |
build_script_toolchains | A list of labels to set on a crates's cargo_build_script::toolchains attribute. | List of labels | optional | [] |
build_script_tools | A list of labels to add to a crate's cargo_build_script::tools attribute. | List of strings | optional | [] |
compile_data | A list of labels to add to a crate's rust_library::compile_data attribute. | List of strings | optional | [] |
compile_data_glob | A list of glob patterns to add to a crate's rust_library::compile_data attribute. | List of strings | optional | [] |
crate | The name of the crate the annotation is applied to | String | required | |
crate_features | A list of strings to add to a crate's rust_library::crate_features attribute. | List of strings | optional | [] |
data_glob | A list of glob patterns to add to a crate's rust_library::data attribute. | List of strings | optional | [] |
disable_pipelining | If True, disables pipelining for library targets for this crate. | Boolean | optional | False |
extra_aliased_targets | A list of targets to add to the generated aliases in the root crate_universe repository. | Dictionary: String -> String | optional | {} |
gen_all_binaries | If true, generates rust_binary targets for all of the crates bins | Boolean | optional | False |
gen_binaries | As a list, the subset of the crate's bins that should get rust_binary targets produced. | List of strings | optional | [] |
gen_build_script | An authorative flag to determine whether or not to produce cargo_build_script targets for the current crate. Supported values are 'on', 'off', and 'auto'. | String | optional | "auto" |
override_target_bin | An optional alternate taget to use when something depends on this crate to allow the parent repo to provide its own version of this dependency. | Label | optional | None |
override_target_build_script | An optional alternate taget to use when something depends on this crate to allow the parent repo to provide its own version of this dependency. | Label | optional | None |
override_target_lib | An optional alternate taget to use when something depends on this crate to allow the parent repo to provide its own version of this dependency. | Label | optional | None |
override_target_proc_macro | An optional alternate taget to use when something depends on this crate to allow the parent repo to provide its own version of this dependency. | Label | optional | None |
patch_args | The patch_args attribute of a Bazel repository rule. See http_archive.patch_args | List of strings | optional | [] |
patch_tool | The patch_tool attribute of a Bazel repository rule. See http_archive.patch_tool | String | optional | "" |
patches | The patches attribute of a Bazel repository rule. See http_archive.patches | List of labels | optional | [] |
proc_macro_deps | A list of labels to add to a crate's rust_library::proc_macro_deps attribute. | List of strings | optional | [] |
repositories | A list of repository names specified from crate.from_cargo(name=...) that this annotation is applied to. Defaults to all repositories. | List of strings | optional | [] |
rustc_env | Additional variables to set on a crate's rust_library::rustc_env attribute. | Dictionary: String -> String | optional | {} |
rustc_env_files | A list of labels to set on a crate's rust_library::rustc_env_files attribute. | List of strings | optional | [] |
rustc_flags | A list of strings to set on a crate's rust_library::rustc_flags attribute. | List of strings | optional | [] |
shallow_since | An optional timestamp used for crates originating from a git repository instead of a crate registry. This flag optimizes fetching the source code. | String | optional | "" |
version | The versions of the crate the annotation is applied to. Defaults to all versions. | String | optional | "*" |
from_specs
Generates a repo @crates from the defined spec
tags
Attributes
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
name | The name of the repo to generate | Name | optional | "crates" |
cargo_config | A Cargo configuration file. | Label | optional | None |
generate_binaries | Whether to generate rust_binary targets for all the binary crates in every package. By default only the rust_library targets are generated. | Boolean | optional | False |
generate_build_scripts | Whether or not to generate cargo build scripts by default. | Boolean | optional | True |
splicing_config | The configuration flags to use for splicing Cargo maniests. Use //crate_universe:defs.bzl\%rsplicing_config to generate the value for this field. If unset, the defaults defined there will be used. | String | optional | "" |
supported_platform_triples | A set of all platform triples to consider when generating dependencies. | List of strings | optional | ["aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", "aarch64-linux-android", "aarch64-pc-windows-msvc", "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", "arm-unknown-linux-gnueabi", "armv7-linux-androideabi", "armv7-unknown-linux-gnueabi", "i686-apple-darwin", "i686-linux-android", "i686-pc-windows-msvc", "i686-unknown-freebsd", "i686-unknown-linux-gnu", "powerpc-unknown-linux-gnu", "riscv32imc-unknown-none-elf", "riscv64gc-unknown-none-elf", "s390x-unknown-linux-gnu", "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", "wasm32-wasi", "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none"] |
spec
Attributes