|
1 |
| -"""# Crate Universe |
| 1 | +""" |
| 2 | +# Crate Universe |
2 | 3 |
|
3 | 4 | Crate Universe is a set of Bazel rule for generating Rust targets using Cargo.
|
4 | 5 |
|
5 | 6 | This doc describes using crate_universe with bzlmod.
|
6 | 7 |
|
7 | 8 | If you're using a WORKSPACE file, please see [the WORKSPACE equivalent of this doc](crate_universe.html).
|
8 | 9 |
|
9 |
| -There are some examples of using crate_universe with bzlmod: |
| 10 | +There are some examples of using crate_universe with bzlmod in the [example folder](../examples/bzlmod). |
| 11 | +
|
| 12 | +# Table of Contents |
| 13 | +
|
| 14 | +1. [Setup](#Setup) |
| 15 | +2. [Dependencies](#dependencies) |
| 16 | + * [Cargo Workspace](#cargo-workspaces) |
| 17 | + * [Direct Packages](#direct-dependencies) |
| 18 | + * [Vendored Dependencies](#vendored-dependencies) |
| 19 | +3. [Crate reference](#crate) |
| 20 | + * [from_cargo](#from_cargo) |
| 21 | + * [from_specs](#from_specs) |
| 22 | +
|
| 23 | +
|
| 24 | +## Setup |
| 25 | +
|
| 26 | +To use rules_rust in a project using bzlmod, add the following to your MODULE.bazel file: |
| 27 | +
|
| 28 | +```starlark |
| 29 | +bazel_dep(name = "rules_rust", version = "0.49.3") |
| 30 | +``` |
| 31 | +
|
| 32 | +You find the latest version on the [release page](https://github.com/bazelbuild/rules_rust/releases). |
| 33 | +
|
| 34 | +
|
| 35 | +After adding `rules_rust` in your MODULE.bazel, set the following to begin using `crate_universe`: |
| 36 | +
|
| 37 | +```starlark |
| 38 | +crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate") |
| 39 | +// # ... Dependencies |
| 40 | +use_repo(crate, "crates") |
| 41 | +``` |
| 42 | +
|
| 43 | +## Dependencies |
| 44 | +
|
| 45 | +There are three different ways to declare dependencies in your MODULE. |
| 46 | +
|
| 47 | +1) Cargo workspace |
| 48 | +2) Direct Dependencies |
| 49 | +3) Vendored Dependencies |
| 50 | +
|
| 51 | +### Cargo Workspaces |
| 52 | +
|
| 53 | +One of the simpler ways to wire up dependencies would be to first structure your project into a Cargo workspace. |
| 54 | +The crates_repository rule can ingest a root Cargo.toml file and generate Bazel dependencies from there. |
| 55 | +You find a complete example in the in the [example folder](../examples/bzlmod/all_crate_deps). |
| 56 | +
|
| 57 | +```starlark |
| 58 | +crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate") |
| 59 | +
|
| 60 | +crate.from_cargo( |
| 61 | + name = "crates", |
| 62 | + cargo_lockfile = "//:Cargo.lock", |
| 63 | + manifests = ["//:Cargo.toml"], |
| 64 | +) |
| 65 | +use_repo(crate, "crates") |
| 66 | +``` |
| 67 | +
|
| 68 | +The generated crates_repository contains helper macros which make collecting dependencies for Bazel targets simpler. |
| 69 | +Notably, the all_crate_deps and aliases macros ( |
| 70 | +see [Dependencies API](https://bazelbuild.github.io/rules_rust/crate_universe.html#dependencies-api)) commonly allow the |
| 71 | +Cargo.toml files to be the single source of truth for dependencies. |
| 72 | +Since these macros come from the generated repository, the dependencies and alias definitions |
| 73 | +they return will automatically update BUILD targets. In your BUILD files, |
| 74 | +you use these macros for a Rust library as shown below: |
| 75 | +
|
| 76 | +```starlark |
| 77 | +load("@crate_index//:defs.bzl", "aliases", "all_crate_deps") |
| 78 | +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") |
| 79 | +
|
| 80 | +rust_library( |
| 81 | + name = "lib", |
| 82 | + aliases = aliases(), |
| 83 | + deps = all_crate_deps( |
| 84 | + normal = True, |
| 85 | + ), |
| 86 | + proc_macro_deps = all_crate_deps( |
| 87 | + proc_macro = True, |
| 88 | + ), |
| 89 | +) |
| 90 | +
|
| 91 | +rust_test( |
| 92 | + name = "unit_test", |
| 93 | + crate = ":lib", |
| 94 | + aliases = aliases( |
| 95 | + normal_dev = True, |
| 96 | + proc_macro_dev = True, |
| 97 | + ), |
| 98 | + deps = all_crate_deps( |
| 99 | + normal_dev = True, |
| 100 | + ), |
| 101 | + proc_macro_deps = all_crate_deps( |
| 102 | + proc_macro_dev = True, |
| 103 | + ), |
| 104 | +) |
| 105 | +``` |
| 106 | +
|
| 107 | +For a Rust binary that does not depend on any macro, use the following configuration |
| 108 | +in your build file: |
| 109 | +
|
| 110 | +```starlark |
| 111 | +rust_binary( |
| 112 | + name = "bin", |
| 113 | + srcs = ["src/main.rs"], |
| 114 | + deps = all_crate_deps(normal = True), |
| 115 | +) |
| 116 | +``` |
| 117 | +
|
| 118 | +You have to repin before your first build to ensure all Bazel targets for the macros |
| 119 | +are generated. |
| 120 | +
|
| 121 | +Dependency syncing and updating is done in the repository rule which means it's done during the |
| 122 | +analysis phase of builds. As mentioned in the environments variable table above, the `CARGO_BAZEL_REPIN` |
| 123 | +(or `REPIN`) environment variables can be used to force the rule to update dependencies and potentially |
| 124 | +render a new lockfile. Given an instance of this repository rule named `crates`, the easiest way to |
| 125 | +repin dependencies is to run: |
| 126 | +
|
| 127 | +```shell |
| 128 | +CARGO_BAZEL_REPIN=1 bazel sync --only=crates |
| 129 | +``` |
| 130 | +
|
| 131 | +This will result in all dependencies being updated for a project. The `CARGO_BAZEL_REPIN` |
| 132 | +environment variable can also be used to customize how dependencies are updated. |
| 133 | +For more details about repin, [please refer to the documentation](https://bazelbuild.github.io/rules_rust/crate_universe.html#crates_vendor). |
| 134 | +
|
| 135 | +### Direct Dependencies |
| 136 | +
|
| 137 | +In cases where Rust targets have heavy interactions with other Bazel targets ([Cc](https://docs.bazel.build/versions/main/be/c-cpp.html), [Proto](https://rules-proto-grpc.com/en/4.5.0/lang/rust.html), |
| 138 | +etc.), maintaining Cargo.toml files may have diminishing returns as things like rust-analyzer |
| 139 | +begin to be confused about missing targets or environment variables defined only in Bazel. |
| 140 | +In situations like this, it may be desirable to have a “Cargo free” setup. You find an example in the in the [example folder](../examples/bzlmod/hello_world_no_cargo). |
| 141 | +
|
| 142 | +crates_repository supports this through the packages attribute, |
| 143 | +as shown below. |
| 144 | +
|
| 145 | +```starlark |
| 146 | +crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate") |
| 147 | +
|
| 148 | +crate.spec(package = "serde", features = ["derive"], version = "1.0") |
| 149 | +crate.spec(package = "serde_json", version = "1.0") |
| 150 | +crate.spec(package = "tokio", default_features=False, features = ["macros", "net", "rt-multi-thread"], version = "1.38") |
| 151 | +
|
| 152 | +crate.from_specs() |
| 153 | +use_repo(crate, "crates") |
| 154 | +``` |
| 155 | +
|
| 156 | +Consuming dependencies may be more ergonomic in this case through the aliases defined in the new repository. |
| 157 | +In your BUILD files, you use direct dependencies as shown below: |
| 158 | +
|
| 159 | +```starlark |
| 160 | +rust_binary( |
| 161 | + name = "bin", |
| 162 | + crate_root = "src/main.rs", |
| 163 | + srcs = glob([ |
| 164 | + "src/*.rs", |
| 165 | + ]), |
| 166 | + deps = [ |
| 167 | + # External crates |
| 168 | + "@crates//:serde", |
| 169 | + "@crates//:serde_json", |
| 170 | + "@crates//:tokio", |
| 171 | + ], |
| 172 | + visibility = ["//visibility:public"], |
| 173 | +) |
| 174 | +``` |
| 175 | +
|
| 176 | +Notice, direct dependencies do not need repining. |
| 177 | +Only a cargo workspace needs updating whenever the underlying Cargo.toml file changed. |
| 178 | +
|
| 179 | +### Vendored Dependencies |
| 180 | +
|
| 181 | +In some cases, it is require that all external dependencies are vendored, meaning downloaded |
| 182 | +and stored in the workspace. This helps, for example, to conduct licence scans, apply custom patches, |
| 183 | +or to ensure full build reproducibility since no download error could possibly occur. |
| 184 | +You find a complete example in the in the [example folder](../examples/bzlmod/all_deps_vendor). |
| 185 | +
|
| 186 | +For the setup, you need to add the skylib in addition to the rust rules to your MODUE.bazel. |
| 187 | +
|
| 188 | +```starlark |
| 189 | +module( |
| 190 | + name = "deps_vendored", |
| 191 | + version = "0.0.0" |
| 192 | +) |
| 193 | +############################################################################### |
| 194 | +# B A Z E L C E N T R A L R E G I S T R Y # https://registry.bazel.build/ |
| 195 | +############################################################################### |
| 196 | +# https://github.com/bazelbuild/bazel-skylib/releases/ |
| 197 | +bazel_dep(name = "bazel_skylib", version = "1.7.1") |
| 198 | +
|
| 199 | +# https://github.com/bazelbuild/rules_rust/releases |
| 200 | +bazel_dep(name = "rules_rust", version = "0.49.3") |
| 201 | +
|
| 202 | +############################################################################### |
| 203 | +# T O O L C H A I N S |
| 204 | +############################################################################### |
| 205 | +
|
| 206 | +# Rust toolchain |
| 207 | +RUST_EDITION = "2021" |
| 208 | +RUST_VERSION = "1.80.1" |
| 209 | +
|
| 210 | +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") |
| 211 | +rust.toolchain( |
| 212 | + edition = RUST_EDITION, |
| 213 | + versions = [RUST_VERSION], |
| 214 | +) |
| 215 | +use_repo(rust, "rust_toolchains") |
| 216 | +register_toolchains("@rust_toolchains//:all") |
| 217 | +
|
| 218 | +############################################################################### |
| 219 | +# R U S T C R A T E S |
| 220 | +############################################################################### |
| 221 | +crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate") |
| 222 | +``` |
| 223 | +
|
| 224 | +Note, it is important to load the crate_universe rules otherwise you will get an error |
| 225 | +as the rule set is needed in the vendored target. |
| 226 | +
|
| 227 | +Assuming you have a package called `basic` in which you want to vendor dependencies, |
| 228 | +then you create a folder `basic/3rdparty`. The folder name can be arbitrary, |
| 229 | +but by convention, its either thirdparty or 3rdparty to indicate vendored dependencies. |
| 230 | +In the 3rdparty folder, you add a target crates_vendor to declare your dependencies to vendor. |
| 231 | +In the example, we vendor a specific version of bzip2. |
| 232 | +
|
| 233 | +```starlark |
| 234 | +load("@rules_rust//crate_universe:defs.bzl", "crate", "crates_vendor") |
| 235 | +
|
| 236 | +crates_vendor( |
| 237 | + name = "crates_vendor", |
| 238 | + annotations = { |
| 239 | + "bzip2-sys": [crate.annotation( |
| 240 | + gen_build_script = True, |
| 241 | + )], |
| 242 | + }, |
| 243 | + cargo_lockfile = "Cargo.Bazel.lock", |
| 244 | + generate_build_scripts = False, |
| 245 | + mode = "remote", |
| 246 | + packages = { |
| 247 | + "bzip2": crate.spec( |
| 248 | + version = "=0.3.3", |
| 249 | + ), |
| 250 | + }, |
| 251 | + repository_name = "basic", |
| 252 | + tags = ["manual"], |
| 253 | +) |
| 254 | +``` |
| 255 | +
|
| 256 | +Next, you have to run `Cargo build` to generate a Cargo.lock file with all resolved dependencies. |
| 257 | +Then, you rename Cargo.lock to Cargo.Bazel.lock and place it inside the `basic/3rdparty` folder. |
| 258 | +
|
| 259 | +At this point, you have the following folder and files: |
| 260 | +
|
| 261 | +``` |
| 262 | +basic |
| 263 | + ├── 3rdparty |
| 264 | + │ ├── BUILD.bazel |
| 265 | + │ ├── Cargo.Bazel.lock |
| 266 | +``` |
| 267 | +
|
| 268 | +Now you can run the `crates_vendor` target: |
| 269 | +
|
| 270 | +`bazel run //basic/3rdparty:crates_vendor` |
| 271 | +
|
| 272 | +This generates a crate folders with all configurations for the vendored dependencies. |
| 273 | +
|
| 274 | +``` |
| 275 | +basic |
| 276 | + ├── 3rdparty |
| 277 | + │ ├── cratea |
| 278 | + │ ├── BUILD.bazel |
| 279 | + │ ├── Cargo.Bazel.lock |
| 280 | +``` |
| 281 | +
|
| 282 | +Suppose you have an application in `basic/src` that is defined in `basic/BUILD.bazel` and |
| 283 | +that depends on a vendored dependency. You find a list of all available vendored dependencies |
| 284 | +in the BUILD file of the generated folder: `basic/3rdparty/crates/BUILD.bazel` |
| 285 | +You declare a vendored dependency in you target as following: |
| 286 | +
|
| 287 | +```starlark |
| 288 | +load("@rules_rust//rust:defs.bzl", "rust_binary") |
| 289 | +
|
| 290 | +rust_binary( |
| 291 | + name = "hello_sys", |
| 292 | + srcs = ["src/main.rs"], |
| 293 | + deps = ["//basic/3rdparty/crates:bzip2"], |
| 294 | + visibility = ["//visibility:public"], |
| 295 | +) |
| 296 | +``` |
| 297 | +Note, the vendored dependency is not yet accessible because you have to define first |
| 298 | +how to load the vendored dependencies. For that, you first create a file `sys_deps.bzl` |
| 299 | +and add the following content: |
| 300 | +
|
| 301 | +```starlark |
| 302 | +# rename the default name "crate_repositories" in case you import multiple vendored folders. |
| 303 | +load("//basic/3rdparty/crates:defs.bzl", basic_crate_repositories = "crate_repositories") |
| 304 | +
|
| 305 | +def sys_deps(): |
| 306 | + # Load the vendored dependencies |
| 307 | + basic_crate_repositories() |
| 308 | +``` |
| 309 | +
|
| 310 | +This is straightforward, you import the generated crate_repositories from the crates folder, |
| 311 | +rename it to avoid name clashes in case you import from multiple vendored folders, and then |
| 312 | +just load the vendored dependencies. |
| 313 | +
|
| 314 | +In a WORKSPACE configuration, you would just load and call sys_deps(), but in a MODULE configuration, you cannot do that. |
| 315 | +Instead, you create a new file `WORKSPACE.bzlmod` and add the following content. |
| 316 | +
|
| 317 | +```starlark |
| 318 | +load("//:sys_deps.bzl", "sys_deps") |
| 319 | +sys_deps() |
| 320 | +``` |
| 321 | +
|
| 322 | +Now, you can build the project as usual. |
| 323 | +
|
| 324 | +There are some more examples of using crate_universe with bzlmod in the [example folder](https://github.com/bazelbuild/rules_rust/blob/main/examples/bzlmod/). |
10 | 325 |
|
11 |
| -* https://github.com/bazelbuild/rules_rust/blob/main/examples/bzlmod/hello_world/MODULE.bazel |
12 |
| -* https://github.com/bazelbuild/rules_rust/blob/main/examples/bzlmod/override_target/MODULE.bazel |
13 |
| -* https://github.com/bazelbuild/rules_rust/blob/main/examples/bzlmod/all_crate_deps/MODULE.bazel |
14 | 326 | """
|
15 | 327 |
|
16 | 328 | load(
|
|
0 commit comments