| """# Crate Universe |
| |
| Crate Universe is a set of Bazel rule for generating Rust targets using Cargo. |
| |
| ## Setup |
| |
| After loading `rules_rust` in your workspace, set the following to begin using `crate_universe`: |
| |
| ```python |
| load("@rules_rust//crate_universe:repositories.bzl", "crate_universe_dependencies") |
| |
| crate_universe_dependencies() |
| ``` |
| |
| Note that if the current version of `rules_rust` is not a release artifact, you may need to set additional |
| flags such as [`bootstrap = True`](#crate_universe_dependencies-bootstrap) on the `crate_universe_dependencies` |
| call above or [crates_repository::generator_urls](#crates_repository-generator_urls) in uses of `crates_repository`. |
| |
| ## Rules |
| |
| - [crates_repository](#crates_repository) |
| - [crates_vendor](#crates_vendor) |
| |
| ## Utility Macros |
| |
| - [crate_universe_dependencies](#crate_universe_dependencies) |
| - [crate.annotation](#crateannotation) |
| - [crate.select](#crateselect) |
| - [crate.spec](#cratespec) |
| - [crate.workspace_member](#crateworkspace_member) |
| - [render_config](#render_config) |
| - [splicing_config](#splicing_config) |
| |
| ## Workflows |
| |
| The [`crates_repository`](#crates_repository) rule (the primary repository rule of `rules_rust`'s cargo support) 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/crate_universe](https://github.com/bazelbuild/rules_rust/tree/main/examples/crate_universe) directory. |
| |
| ### Cargo Workspaces |
| |
| One of the simpler ways to wire up dependencies would be to first structure your project into a [Cargo workspace][cw]. |
| The `crates_repository` rule can ingest a root `Cargo.toml` file and generate dependencies from there. |
| |
| ```python |
| load("@rules_rust//crate_universe:defs.bzl", "crates_repository") |
| |
| crates_repository( |
| name = "crate_index", |
| cargo_lockfile = "//:Cargo.lock", |
| 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 (see [Dependencies API](#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. |
| |
| ```python |
| 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][cc], [Proto][proto], etc.), |
| maintaining `Cargo.toml` files may have deminishing returns as things like [rust-analyzer][ra] 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. |
| |
| ```python |
| load("@rules_rust//crate_universe:defs.bzl", "crate", "crates_repository", "render_config") |
| |
| crates_repository( |
| name = "crate_index", |
| cargo_lockfile = "//:Cargo.lock", |
| 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. |
| |
| ```python |
| 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", |
| ], |
| ) |
| ``` |
| |
| ### Binary dependencies |
| |
| Neither of the above approaches supports depending on binary-only packages. |
| |
| In order to depend on a Cargo package that contains binaries and no library, you |
| will need to do one of the following: |
| |
| - Fork the package to add an empty lib.rs, which makes the package visible to |
| Cargo metadata and compatible with the above approaches; |
| |
| - Or handwrite your own build target for the binary, use `http_archive` to |
| import its source code, and use `crates_repository` to make build targets for |
| its dependencies. This is demonstrated below using the `rustfilt` crate as an |
| example. |
| |
| ```python |
| # in WORKSPACE.bazel |
| |
| load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| |
| http_archive( |
| name = "rustfilt", |
| build_file = "//rustfilt:BUILD.rustfilt.bazel", |
| sha256 = "c8d748b182c8f95224336d20dcc5609598af612581ce60cfb29da4dc8d0091f2", |
| strip_prefix = "rustfilt-0.2.1", |
| type = "tar.gz", |
| urls = ["https://crates.io/api/v1/crates/rustfilt/0.2.1/download"], |
| ) |
| |
| load("@rules_rust//crate_universe:defs.bzl", "crates_repository") |
| |
| crates_repository( |
| name = "rustfilt_deps", |
| cargo_lockfile = "//rustfilt:Cargo.lock", |
| manifests = ["@rustfilt//:Cargo.toml"], |
| ) |
| |
| load("@rustfilt_deps//:defs.bzl", rustfilt_deps = "crate_repositories") |
| |
| rustfilt_deps() |
| ``` |
| |
| ```python |
| # in rustfilt/BUILD.rustfilt.bazel |
| |
| load("@rules_rust//rust:defs.bzl", "rust_binary") |
| |
| rust_binary( |
| name = "rustfilt", |
| srcs = glob(["src/**/*.rs"]), |
| edition = "2018", |
| deps = [ |
| "@rustfilt_deps//:clap", |
| "@rustfilt_deps//:lazy_static", |
| "@rustfilt_deps//:regex", |
| "@rustfilt_deps//:rustc-demangle", |
| ], |
| ) |
| ``` |
| |
| If you use either `crates_repository` or `crates_vendor` to depend on a Cargo |
| package that contains _both_ a library crate _and_ binaries, by default only the |
| library gets made available to Bazel. To generate Bazel targets for the binary |
| crates as well, you must opt in to it with an annotation on the package: |
| |
| ```python |
| load("@rules_rust//crate_universe:defs.bzl", "crates_repository", "crate") |
| |
| crates_repository( |
| name = "crate_index", |
| annotations = { |
| "thepackage": [crate.annotation( |
| gen_binaries = True, |
| # Or, to expose just a subset of the package's binaries by name: |
| gen_binaries = ["rustfilt"], |
| )], |
| }, |
| # Or, to expose every binary of every package: |
| generate_binaries = True, |
| ... |
| ) |
| ``` |
| |
| ## Dependencies API |
| |
| After rendering dependencies, convenience macros may also be generated to provide |
| convenient accessors to larger sections of the dependency graph. |
| |
| - [aliases](#aliases) |
| - [crate_deps](#crate_deps) |
| - [all_crate_deps](#all_crate_deps) |
| - [crate_repositories](#crate_repositories) |
| |
| ## Building crates with complicated dependencies |
| |
| Some crates have build.rs scripts which are complicated to run. Typically these build C++ (or other languages), or attempt to find pre-installed libraries on the build machine. |
| |
| There are a few approaches to making sure these run: |
| |
| ### Some things work without intervention |
| |
| Some build scripts will happily run without any support needed. |
| |
| rules_rust already supplies a configured C++ toolchain as input to build script execution, and sets variables like `CC`, `CXX`, `LD`, `LDFLAGS`, etc as needed. Many crates which invoke a compiler with the default environment, or forward these env vars, will Just Work (e.g. if using [`cc-rs`][cc-rs]). |
| |
| rules_rust is open to PRs which make build scripts more likely to work by default with intervention assuming they're broadly applicable (e.g. setting extra widely-known env vars is probably fine, wiring up additional toolchains like `cmake` that aren't present by default for most Bazel users probably isn't). |
| |
| ### Supplying extra tools to build |
| |
| Some build scripts can be made to work by pulling in some extra files and making them available to the build script. |
| |
| Commonly this is done by passing the file to the `build_script_data` annotation for the crate, and using `build_script_env` to tell the build script where the file is. That env var may often use `$(execroot)` to get the path to the label, or `$${pwd}/` as a prefix if the path given is relative to the execroot (as will frequently happen when using a toolchain).A |
| |
| There is an example of this in the "complicated dependencies" section of https://github.com/bazelbuild/rules_rust/blob/main/examples/crate_universe/WORKSPACE.bazel which builds libz-ng-sys. |
| |
| ### Building with Bazel and supplying via an override |
| |
| Some build scripts have hooks to allow replacing parts that are complicated to build with output prepared by Bazel. |
| |
| We can use those hooks by specifying paths (generally using the `build_script_data` and `build_script_env` annotations) and pointing them at labels which Bazel will then build. These env vars may often use `$(execroot)` to get the path to the label, or `$${pwd}/` as a prefix if the path given is relative to the execroot (as will frequently happen when using a toolchain). |
| |
| There is an example of this in the "complicated dependencies" section of https://github.com/bazelbuild/rules_rust/blob/main/examples/crate_universe/WORKSPACE.bazel which builds boring-sys. |
| |
| --- |
| |
| --- |
| |
| [cw]: https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html |
| [cc]: https://docs.bazel.build/versions/main/be/c-cpp.html |
| [proto]: https://rules-proto-grpc.com/en/latest/lang/rust.html |
| [ra]: https://rust-analyzer.github.io/ |
| [cc-rs]: https://github.com/rust-lang/cc-rs |
| """ |
| |
| load( |
| "//crate_universe:defs.bzl", |
| _crate = "crate", |
| _crates_repository = "crates_repository", |
| _crates_vendor = "crates_vendor", |
| _render_config = "render_config", |
| _splicing_config = "splicing_config", |
| ) |
| load( |
| "//crate_universe:repositories.bzl", |
| _crate_universe_dependencies = "crate_universe_dependencies", |
| ) |
| load( |
| "//crate_universe/3rdparty/crates:defs.bzl", |
| _aliases = "aliases", |
| _all_crate_deps = "all_crate_deps", |
| _crate_deps = "crate_deps", |
| _crate_repositories = "crate_repositories", |
| ) |
| |
| # Rules |
| crates_repository = _crates_repository |
| crates_vendor = _crates_vendor |
| |
| # Utility Macros |
| crate_universe_dependencies = _crate_universe_dependencies |
| crate = _crate |
| render_config = _render_config |
| splicing_config = _splicing_config |
| |
| # Dependencies API |
| aliases = _aliases |
| all_crate_deps = _all_crate_deps |
| crate_deps = _crate_deps |
| crate_repositories = _crate_repositories |