An open API service indexing awesome lists of open source software.

https://github.com/calsign/gazelle_rust

Gazelle language plugin for Rust.
https://github.com/calsign/gazelle_rust

bazel gazelle rust

Last synced: about 1 month ago
JSON representation

Gazelle language plugin for Rust.

Awesome Lists containing this project

README

          

## gazelle\_rust

A Gazelle language plugin for Rust; automatic dependency management for Rust projects built with
Bazel.

References:
- [Gazelle](https://github.com/bazelbuild/bazel-gazelle)
- [rules\_rust](https://github.com/bazelbuild/rules_rust)
- [Bazel](https://bazel.build/)

## Setup

[`example/MODULE.bazel`](./example/MODULE.bazel) shows how to load rules\_rust and gazelle\_rust
together. In a real project, you would need to use `archive_override` instead of
`local_path_override`, for example:

```py
GAZELLE_RUST_COMMIT = ""
GAZELLE_RUST_SHA256 = ""

bazel_dep(name = "gazelle_rust", version = "0.1.0")

archive_override(
module_name = "gazelle_rust",
sha256 = GAZELLE_RUST_SHA256,
strip_prefix = "gazelle_rust-{}".format(GAZELLE_RUST_COMMIT),
url = "https://github.com/Calsign/gazelle_rust/archive/{}.zip".format(GAZELLE_RUST_COMMIT),
)
```

gazelle\_rust doesn't have any releases yet, so please just pick the latest commit on `main`. To
determine the sha256, first set the value to `None`, then fill in the sha256 that bazel tells you.

gazelle\_rust requires rules_rust 0.40.0 or later. Previous versions required a patch to
rules\_rust.

The dependencies for gazelle\_rust itself are loaded through two repository rule macros, shown in
[`example/MODULE.bazel`](./example/MODULE.bazel). This includes setting up gazelle, but you may use
a different gazelle version by loading the gazelle repo before calling `gazelle_rust_dependencies*`.

gazelle\_rust includes a patch to gazelle which allows for reporting unused crate\_universe
dependencies. If you do not include the patch, everything else will still work fine but unused
crate\_universe dependencies will not be reported.

## Running gazelle

[`example/BUILD.bazel`](./example/BUILD.bazel) shows
how to create the gazelle target. With the gazelle target defined, you can run it:

```sh
bazel run //:gazelle
```

This will modify your build files in-place to create and update rust targets for all of the rust
files in your project.

gazelle\_rust provides a premade gazelle binary, but you can also create your own `gazelle_binary`
target and add `@gazelle_rust//rust_language` to languages.

## Generated targets

[`example/src/BUILD.bazel`](./example/src/BUILD.bazel)
shows sample targets generated by gazelle. The following rules are supported:

* `rust_library`
* `rust_binary`
* `rust_test`
* `rust_proc_macro`
* `rust_shared_library`
* `rust_static_library`

When generating targets for new sources (those not already listed in `srcs` for an existing target),
gazelle\_rust will infer the rule kind based on information like whether the file has a `main` and
the name of the directory. The full logic is in `inferRuleKind` in
[`rust_language/generate.go`](./rust_language/generate.go). If you change the rule kind afterward,
gazelle\_rust will respect the existing rule kind.

By default gazelle\_rust will generate one target per source file. You may change the grouping by
adding a file to `srcs` for an existing target, and gazelle will respect that existing grouping.

gazelle\_rust does not currently support sources in subdirectories, and will always place targets
into build files adjacent to the sources that they correspond to.

## Generation from Cargo.toml files

It is possible to instruct gazelle\_rust to generate `BUILD.bazel` files next to existing
`Cargo.toml` files, which may be helpful if you would like to keep `Cargo.toml` files around and
structure your Bazel definitions around them. This mode of operation is enabled by adding the
following directive:

```py
# gazelle:rust_mode generate_from_cargo
```

In this mode of operation it is also possible to specify the default Rust edition, like so:

```py
# gazelle:rust_default_edition 2021
```

The value this directive is set to should normally match the
[`default_edition`](https://bazelbuild.github.io/rules_rust/rust_toolchains.html#rust_toolchain-default_edition)
attribute on the Rust toolchain. Whenever the edition is directly specified in the `Cargo.toml` file
(i.e. it is not inherited from the workspace file) and it is different from the default edition, the
corresponding `"edition"` attribute will be added to the generated targets. If
`gazelle:rust_default_edition` is not set, the `"edition"` attribute will always be added when it is
specified.

## Crate features

gazelle\_rust reads the set of features in the `crate_features` attribute and skips adding
dependencies which are not needed for the enabled features.

For `generate_from_cargo` mode, the initial `crate_features` list is generated from the default
features list in `Cargo.toml`. This behavior for newly-generated targets can be further customized
with the following directives which apply to the package in which they are defined and subpackages:

```py
# gazelle:rust_default_features
# gazelle:rust_feature
```

`gazelle:rust_default_features false` disables use of the default features from `Cargo.toml`,
analogous to `default_features = false` in cargo.

`gazelle:rust_feature my_feature ` adds an override for a specific feature name to add
or remove that feature from the set of features added to newly-generated targets. The feature will
only be added to a rust target if that feature is present in `Cargo.toml` for that package.

## Assigning dependencies

gazelle\_rust parses each source file and identifies any path that looks like an external crate
dependency. For example `some_lib::Foobar` implies a new dependency on `some_lib` unless `some_lib`
is already in scope.

This approach is fairly robust. Please see [`rust_parser/parser.rs`](./rust_parser/parser.rs) for
implementation details and the [parser
tests](https://github.com/Calsign/gazelle_rust/tree/main/rust_parser/test_data) for the range of
cases covered.

For each dependency, gazelle\_rust identifies the crate in the project (or crate universe
dependency) providing that crate name. gazelle\_rust raises an error if the crate could not be found
or more than one crate with that name was found.

This means there is a global namespace of crates within the project. If this poses an issue for you,
you can use the gazelle [`resolve`
directive](https://github.com/bazelbuild/bazel-gazelle#directives) to configure which target is
selected on a per-directory basis.

## Crate universe

The example shows how to handle crate universe dependencies with gazelle\_rust.
[`example/MODULE.bazel`](./example/MODULE.bazel) shows how to load crate universe dependencies,
please refer to the [rules\_rust
documentation](http://bazelbuild.github.io/rules_rust/crate_universe.html) for more information. The
example shows the repository rule approach, but gazelle\_rust also supports the vendored approach.

[`example/BUILD.bazel`](./example/BUILD.bazel) shows
how to configure gazelle\_rust to resolve crate universe dependencies.

Different configurations of crate universe use either a cargo lockfile (`Cargo.lock`) or a custom
lockfile (`Cargo.Bazel.lock`), and gazelle\_rust supports both. Use the directive
`gazelle:rust_cargo_lockfile` to indicate a cargo lockfile and `gazelle:rust_lockfile` to indicate a
custom lockfile. These options are mutually exclusive.

Additionally, you must tell rules\_rust the prefix for all crate universe labels using the
`gazelle:rust_crates_prefix` directive, e.g. `@crates//:` for a repository rule approach or
`//3rdparty/crates:` for a vendored approach.

## Ignoring dependencies

Some situations are too complex for gazelle\_rust to handle, such as platform-conditional
dependencies. It is possible that fancy support could be added in the future, but for now you must
handle this manually by ignoring the dependency in the source file and potentially adding [`# keep`
comments](https://github.com/bazelbuild/bazel-gazelle#keep-comments) in the build file.

To tell gazelle\_rust to ignore a dependency, you can add the `#[gazelle::ignore]` attribute macro
to a use item. For example:

```rust
// the tokio runtime is not supported in wasm
#[cfg(not(target_arch = "wasm32"))]
#[gazelle::ignore]
use tokio::runtime::Runtime;
```

Then in the build file:

```py
rust_library(
name = "maybe_tokio",
deps = select({
"@platforms//cpu:wasm32": [],
"//conditions:default": [
"//3rdparty/crates:tokio",
],
}),
)

rust_library(
name = "some_cool_cross_platform_thing",
deps = [
":maybe_tokio", # keep
],
...
)
```

See the [macro crate](./macro) for more information about the ignore macro.