diff options
Diffstat (limited to 'tools/build_rules/rust/README.md')
-rw-r--r-- | tools/build_rules/rust/README.md | 719 |
1 files changed, 602 insertions, 117 deletions
diff --git a/tools/build_rules/rust/README.md b/tools/build_rules/rust/README.md index 536c10f837..a040ad306f 100644 --- a/tools/build_rules/rust/README.md +++ b/tools/build_rules/rust/README.md @@ -1,18 +1,16 @@ # Rust Rules for Bazel +* [`rust_library`](#rust_library) +* [`rust_binary`](#rust_binary) +* [`rust_test`](#rust_test) +* [`rust_bench_test`](#rust_bench_test) +* [`rust_doc`](#rust_doc) +* [`rust_doc_test`](#rust_doc_test) + ## Overview These build rules are used for building [Rust][rust] projects with Bazel. -* [Setup](#setup) -* [Basic Example](#basic-example) -* [Build Rule Reference](#reference) - * [`rust_library`](#reference-rust_library) - * [`rust_binary`](#reference-rust_binary) - * [`rust_test`](#reference-rust_test) - * [`rust_docs`](#reference-rust_docs) -* [Roadmap](#roadmap) - [rust]: http://www.rust-lang.org/ <a name="setup"></a> @@ -21,8 +19,121 @@ These build rules are used for building [Rust][rust] projects with Bazel. To use the Rust rules, simply copy the contents of `rust.WORKSPACE` to your `WORKSPACE` file. -<a name="basic-example"></a> -## Basic Example +<a name="roadmap"></a> +## Roadmap + +* Add `rust_toolchain` rule to make it easy to use a custom Rust toolchain. +* Add tool for taking `Cargo.toml` and generating a `WORKSPACE` file with + workspace rules for pulling external dependencies. +* Improve expressiveness of features and support for [Cargo's feature + groups](http://doc.crates.io/manifest.html#the-[features]-section). +* Add `cargo_crate` workspace rule for pulling crates from + [Cargo](https://crates.io/). + +<a name="rust_library"></a> +## rust_library + +```python +rust_library(name, srcs, deps, data, crate_features, rustc_flags) +``` + +<table> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>name</code></td> + <td> + <code>Name, required</code> + <p>A unique name for this rule.</p> + <p> + This name will also be used as the name of the library crate built by + this rule. + </p> + </td> + </tr> + <tr> + <td><code>srcs</code></td> + <td> + <code>List of labels, required</code> + <p>List of Rust <code>.rs</code> source files used to build the + library.</p> + <p> + If <code>srcs</code> contains more than one file, then there must be + a file either named <code>lib.rs</code>. Otherwise, + <code>crate_root</code> must be set to the source file that is the + root of the crate to be passed to <code>rustc</code> to build this + crate. + </p> + </td> + </tr> + <tr> + <td><code>crate_root</code></td> + <td> + <code>Label, optional</code> + <p> + The file that will be passed to <code>rustc</code> to be used for + building this crate. + </p> + <p> + If <code>crate_root</code> is not set, then this rule will look for + a <code>lib.rs</code> file or the single file in <code>srcs</code> + if <code>srcs</code> contains only one file. + </p> + </td> + </td> + <tr> + <td><code>deps</code></td> + <td> + <code>List of labels, optional</code> + <p>List of other libraries to be linked to this library target.</p> + <p> + These can be either other <code>rust_library</code> targets or + <code>cc_library</code> targets if linking a native library. + </p> + </td> + </tr> + <tr> + <td><code>data</code></td> + <td> + <code>List of labels, optional</code> + <p>List of files used by this rule at runtime.</p> + <p> + This attribute can be used to specify any data files that are embedded + into the library, such as via the + <a href="https://doc.rust-lang.org/std/macro.include_str!.html target="_blank"><code>include_str!</code></a> + macro. + </p> + </td> + </tr> + <tr> + <td><code>crate_features</code></td> + <td> + <code>List of strings, optional</code> + <p>List of features to enable for this crate.</p> + <p> + Features are defined in the code using the + <code>#[cfg(feature = "foo")]</code> configuration option. The + features listed here will be passed to <code>rustc</code> with + <code>--cfg feature="${feature_name}"</code> flags. + </p> + </td> + </tr> + <tr> + <td><code>rustc_flags</code></td> + <td> + <code>List of strings, optional</code> + <p>List of compiler flags passed to <code>rustc</code>.</p> + </td> + </tr> + </tbody> +</table> + +### Example Suppose you have the following directory structure for a simple Rust library crate: @@ -88,69 +199,13 @@ Target //examples/rust/hello_lib:hello_lib up-to-date: INFO: Elapsed time: 1.245s, Critical Path: 1.01s ``` -Now, let's add a binary crate that uses the `hello_lib` library. The directory -structure now looks like the following: +<a name="rust_binary"></a> +## rust_binary ``` -[workspace]/ - WORKSPACE - hello_lib/ - BUILD - src/ - greeter.rs - lib.rs - hello_world/ - BUILD - src/ - main.rs +rust_binary(name, srcs, deps, data, crate_features, rustc_flags) ``` -`hello_world/src/main.rs`: - -```rust -extern crate hello_lib; - -use hello_lib::greeter; - -fn main() { - let hello = greeter::Greeter::new("Hello"); - hello.greet("world"); -} -``` - -`hello_world/BUILD`: - -```python -load("/tools/build_rules/rust/rust", "rust_binary") - -rust_binary( - name = "hello_world", - srcs = ["src/main.rs"], - deps = ["//hello_lib"], -) -``` - -Build and run `hello_world`: - -``` -$ bazel run //hello_world -INFO: Found 1 target... -Target //examples/rust/hello_world:hello_world up-to-date: - bazel-bin/examples/rust/hello_world/hello_world -INFO: Elapsed time: 1.308s, Critical Path: 1.22s - -INFO: Running command line: bazel-bin/examples/rust/hello_world/hello_world -Hello world -``` - -<a name="reference"></a> -## Build Rule Reference - -<a name="reference-rust_library"></a> -### `rust_library` - -`rust_library(name, srcs, deps, data, crate_features, rustc_flags)` - <table> <thead> <tr> @@ -165,7 +220,7 @@ Hello world <code>Name, required</code> <p>A unique name for this rule.</p> <p> - This name will also be used as the name of the library crate built by + This name will also be used as the name of the binary crate built by this rule. </p> </td> @@ -175,24 +230,38 @@ Hello world <td> <code>List of labels, required</code> <p>List of Rust <code>.rs</code> source files used to build the - library.</p> + binary.</p> <p> - There must be a file either named <code>lib.rs</code> or with a name - matching the name of this crate. For example, if the name of a given - rule is <code>foo</code>, then there must be a file named - <code>lib.rs</code> or <code>foo.rs</code> in <code>srcs</code>. - This file will be passed to <code>rustc</code> as the crate root. + If <code>srcs</code> contains more than one file, then there must be + a file either named <code>main.rs</code>. Otherwise, + <code>crate_root</code> must be set to the source file that is the + root of the crate to be passed to <code>rustc</code> to build this + crate. </p> </td> </tr> <tr> + <td><code>crate_root</code></td> + <td> + <code>Label, optional</code> + <p> + The file that will be passed to <code>rustc</code> to be used for + building this crate. + </p> + <p> + If <code>crate_root</code> is not set, then this rule will look for + a <code>main.rs</code> file or the single file in <code>srcs</code> + if <code>srcs</code> contains only one file. + </p> + </td> + </td> + <tr> <td><code>deps</code></td> <td> <code>List of labels, optional</code> <p>List of other libraries to be linked to this library target.</p> <p> - These can be either other <code>rust_library</code> targets or - <code>cc_library</code> targets if linking a native library. + These must be <code>rust_library</code> targets. </p> </td> </tr> @@ -232,10 +301,98 @@ Hello world </tbody> </table> -<a name="reference-rust_binary"></a> -### `rust_binary` +### Example + +Suppose you have the following directory structure for a Rust project with a +library crate, `hello_lib`, and a binary crate, `hello_world` that uses the +`hello_lib` library: + +``` +[workspace]/ + WORKSPACE + hello_lib/ + BUILD + src/ + lib.rs + hello_world/ + BUILD + src/ + main.rs +``` + +`hello_lib/src/lib.rs`: + +```rust +pub struct Greeter { + greeting: String, +} + +impl Greeter { + pub fn new(greeting: &str) -> Greeter { + Greeter { greeting: greeting.to_string(), } + } + + pub fn greet(&self, thing: &str) { + println!("{} {}", &self.greeting, thing); + } +} +``` + +`hello_lib/BUILD`: + +```python +package(default_visibility = ["//visibility:public"]) + +load("/tools/build_rules/rust/rust", "rust_library") + +rust_library( + name = "hello_lib", + srcs = ["src/lib.rs"], +) +``` + +`hello_world/src/main.rs`: + +```rust +extern crate hello_lib; + +fn main() { + let hello = hello_lib::Greeter::new("Hello"); + hello.greet("world"); +} +``` + +`hello_world/BUILD`: + +```python +load("/tools/build_rules/rust/rust", "rust_binary") + +rust_binary( + name = "hello_world", + srcs = ["src/main.rs"], + deps = ["//hello_lib"], +) +``` + +Build and run `hello_world`: -`rust_binary(name, srcs, deps, data, crate_features, rustc_flags)` +``` +$ bazel run //hello_world +INFO: Found 1 target... +Target //examples/rust/hello_world:hello_world up-to-date: + bazel-bin/examples/rust/hello_world/hello_world +INFO: Elapsed time: 1.308s, Critical Path: 1.22s + +INFO: Running command line: bazel-bin/examples/rust/hello_world/hello_world +Hello world +``` + +<a name="rust_test"></a> +## rust_test + +```python +rust_test(name, srcs, deps, data, crate_features, rustc_flags) +``` <table> <thead> @@ -251,8 +408,8 @@ Hello world <code>Name, required</code> <p>A unique name for this rule.</p> <p> - This name will also be used as the name of the binary crate built by - this rule. + This name will also be used as the name of the binary test crate + built by this rule. </p> </td> </tr> @@ -261,22 +418,36 @@ Hello world <td> <code>List of labels, required</code> <p>List of Rust <code>.rs</code> source files used to build the - binary.</p> + library.</p> <p> - There must be a file either named <code>main.rs</code> or with a name - matching the name of this crate that contains the <code>main</code> - function. For example, if the name of a given - rule is <code>foo</code>, then there must be a file named - <code>main.rs</code> or <code>foo.rs</code> in <code>srcs</code>. - This file will be passed to <code>rustc</code> as the crate root. + If <code>srcs</code> contains more than one file, then there must be + a file either named <code>lib.rs</code>. Otherwise, + <code>crate_root</code> must be set to the source file that is the + root of the crate to be passed to <code>rustc</code> to build this + crate. </p> </td> </tr> <tr> + <td><code>crate_root</code></td> + <td> + <code>Label, optional</code> + <p> + The file that will be passed to <code>rustc</code> to be used for + building this crate. + </p> + <p> + If <code>crate_root</code> is not set, then this rule will look for + a <code>lib.rs</code> file or the single file in <code>srcs</code> + if <code>srcs</code> contains only one file. + </p> + </td> + </td> + <tr> <td><code>deps</code></td> <td> <code>List of labels, optional</code> - <p>List of other libraries to be linked to this library target.</p> + <p>List of other libraries to be linked to this test target.</p> <p> These must be <code>rust_library</code> targets. </p> @@ -318,13 +489,146 @@ Hello world </tbody> </table> -<a name="reference-rust_test"></a> -### `rust_test` +### Example + +Suppose you have the following directory structure for a Rust library crate +with unit test code in the library sources: + +``` +[workspace]/ + WORKSPACE + hello_lib/ + BUILD + src/ + lib.rs +``` + +`hello_lib/src/lib.rs`: + +```rust +pub struct Greeter { + greeting: String, +} + +impl Greeter { + pub fn new(greeting: &str) -> Greeter { + Greeter { greeting: greeting.to_string(), } + } + + pub fn greet(&self, thing: &str) { + println!("{} {}", &self.greeting, thing); + } +} + +#[cfg(test)] +mod test { + use super::Greeter; + + #[test] + fn test_greeting() { + let hello = Greeter::new("Hi"); + assert_eq!("Hi Rust", hello.greeting("Rust")); + } +} +``` + +To build and run the tests, simply add a `rust_test` rule with no `srcs` and +only depends on the `hello_lib` `rust_library` target: + +`hello_lib/BUILD`: ```python -rust_test(name, srcs, deps, data, crate_features, rustc_flags) +package(default_visibility = ["//visibility:public"]) + +load("/tools/build_rules/rust/rust", "rust_library", "rust_test") + +rust_library( + name = "hello_lib", + srcs = ["src/lib.rs"], +) + +rust_test( + name = "hello_lib_test", + deps = [":hello_lib"], +) +``` + +Run the test with `bazel build //hello_lib:hello_lib_test`. + +### Example: `test` directory + +Integration tests that live in the [`tests` directory][int-tests], they are +essentially built as separate crates. Suppose you have the following directory +structure where `greeting.rs` is an integration test for the `hello_lib` +library crate: + +[int-tests]: http://doc.rust-lang.org/book/testing.html#the-tests-directory + +``` +[workspace]/ + WORKSPACE + hello_lib/ + BUILD + src/ + lib.rs + tests/ + greeting.rs +``` + +`hello_lib/tests/greeting.rs`: + +```rust +extern crate hello_lib; + +use hello_lib; + +#[test] +fn test_greeting() { + let hello = greeter::Greeter::new("Hello"); + assert_eq!("Hello world", hello.greeting("world")); +} +``` + +To build the `greeting.rs` integration test, simply add a `rust_test` target +with `greeting.rs` in `srcs` and a dependency on the `hello_lib` target: + +`hello_lib/BUILD`: + +```python +package(default_visibility = ["//visibility:public"]) + +load("/tools/build_rules/rust/rust", "rust_library", "rust_test") + +rust_library( + name = "hello_lib", + srcs = ["src/lib.rs"], +) + +rust_test( + name = "greeting_test", + srcs = ["tests/greeting.rs"], + deps = [":hello_lib"], +) +``` + +Run the test with `bazel build //hello_lib:hello_lib_test`. + +<a name="rust_bench_test"></a> +## rust_bench_test + +```python +rust_bench_test(name, srcs, deps, data, crate_features, rustc_flags) ``` +**Warning**: This rule is currently experimental. [Rust Benchmark +tests][rust-bench] require the `Bencher` interface in the unstable `libtest` +crate, which is behind the `test` unstable feature gate. As a result, using +this rule would require using a nightly binary release of Rust. A +`rust_toolchain` rule will be added in the [near future](#roadmap) to make it +easy to use a custom Rust toolchain, such as a nightly release. + +[rust-bench]: https://doc.rust-lang.org/book/benchmark-tests.html + <table> <thead> <tr> @@ -351,15 +655,30 @@ rust_test(name, srcs, deps, data, crate_features, rustc_flags) <p>List of Rust <code>.rs</code> source files used to build the library.</p> <p> - There must be a file either with a name matching the name of this - test. For example, if the name of a <code>rust_test</code> rule is - <code>foo</code>, then there must be a file named <code>foo.rs</code> - in <code>srcs</code>. This file will be passed to <code>rustc</code> - as the crate root. + If <code>srcs</code> contains more than one file, then there must be + a file either named <code>lib.rs</code>. Otherwise, + <code>crate_root</code> must be set to the source file that is the + root of the crate to be passed to <code>rustc</code> to build this + crate. </p> </td> </tr> <tr> + <td><code>crate_root</code></td> + <td> + <code>Label, optional</code> + <p> + The file that will be passed to <code>rustc</code> to be used for + building this crate. + </p> + <p> + If <code>crate_root</code> is not set, then this rule will look for + a <code>lib.rs</code> file or the single file in <code>srcs</code> + if <code>srcs</code> contains only one file. + </p> + </td> + </td> + <tr> <td><code>deps</code></td> <td> <code>List of labels, optional</code> @@ -405,11 +724,84 @@ rust_test(name, srcs, deps, data, crate_features, rustc_flags) </tbody> </table> -<a name="reference-rust_docs"></a> -### `rust_docs` +### Example + +Suppose you have the following directory structure for a Rust project with a +library crate, `fibonacci` with benchmarks under the `benches/` directory: + +``` +[workspace]/ + WORKSPACE + fibonacci/ + BUILD + src/ + lib.rs + benches/ + fibonacci_bench.rs +``` + +`fibonacci/src/lib.rs`: + +```rust +pub fn fibonacci(n: u64) -> u64 { + if n < 2 { + return n; + } + let mut n1: u64 = 0; + let mut n2: u64 = 1; + for _ in 1..n { + let sum = n1 + n2; + n1 = n2; + n2 = sum; + } + n2 +} +``` + +`fibonacci/benches/fibonacci_bench.rs`: + +```rust +#![feature(test)] + +extern crate test; +extern crate fibonacci; + +use test::Bencher; + +#[bench] +fn bench_fibonacci(b: &mut Bencher) { + b.iter(|| fibonacci::fibonacci(40)); +} +``` + +To build the benchmark test, simply add a `rust_bench_test` target: + +`fibonacci/BUILD`: + +```python +package(default_visibility = ["//visibility:public"]) + +load("/tools/build_rules/rust/rust", "rust_library", "rust_bench_test") + +rust_library( + name = "fibonacci", + srcs = ["src/lib.rs"], +) + +rust_bench_test( + name = "fibonacci_bench", + srcs = ["benches/fibonacci_bench.rs"], + deps = [":fibonacci"], +) +``` + +Run the benchmark test using: `bazel build //fibonacci:fibonacci_bench`. + +<a name="rust_doc"></a> +## rust_doc ```python -rust_docs(name, dep, markdown_css, html_in_header, html_before_content, html_after_content) +rust_doc(name, dep, markdown_css, html_in_header, html_before_content, html_after_content) ``` <table> @@ -433,7 +825,7 @@ rust_docs(name, dep, markdown_css, html_in_header, html_before_content, html_aft <code>Label, required</code> <p>The label of the target to generate code documentation for.</p> <p> - <code>rust_docs</code> can generate HTML code documentation for the + <code>rust_doc</code> can generate HTML code documentation for the source files of <code>rust_library</code> or <code>rust_binary</code> targets. </p> @@ -473,20 +865,113 @@ rust_docs(name, dep, markdown_css, html_in_header, html_before_content, html_aft </tbody> </table> -<a name="#roadmap"></a> -## Roadmap +### Example + +Suppose you have the following directory structure for a Rust library crate: + +``` +[workspace]/ + WORKSPACE + hello_lib/ + BUILD + src/ + lib.rs +``` -### Near-term roadmap +To build [`rustdoc`][rustdoc] documentation for the `hello_lib` crate, define +a `rust_doc` rule that depends on the the `hello_lib` `rust_library` target: -* Enable `rust_test` to depend solely on a `rust_library` since many projects - intermix `#[test]` methods in implementation source. -* Improve documentation with more detailed examples. +[rustdoc]: https://doc.rust-lang.org/book/documentation.html -### Longer-term roadmap +```python +package(default_visibility = ["//visibility:public"]) -* Add tool for taking `Cargo.toml` and generating a `WORKSPACE` file with - workspace rules for pulling external dependencies. -* Improve expressiveness of features and support for [Cargo's feature - groups](http://doc.crates.io/manifest.html#the-[features]-section). -* Add `cargo_crate` workspace rule for pulling crates from - [Cargo](https://crates.io/). +load("/tools/build_rules/rust/rust", "rust_library", "rust_doc") + +rust_library( + name = "hello_lib", + srcs = ["src/lib.rs"], +) + +rust_doc( + name = "hello_lib_doc", + dep = ":hello_lib", +) +``` + +Running `bazel build //hello_lib:hello_lib_doc` will build a zip file containing +the documentation for the `hello_lib` library crate generated by `rustdoc`. + +<a name="rust_doc_test"></a> +### `rust_doc_test` + +```python +rust_doc_test(name, dep) +``` + +<table> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>name</code></td> + <td> + <code>Name, required</code> + <p>A unique name for this rule.</p> + </td> + </tr> + <tr> + <td><code>dep</code></td> + <td> + <code>Label, required</code> + <p>The label of the target to run documentation tests for.</p> + <p> + <code>rust_doc_test</code> can run documentation tests for the + source files of <code>rust_library</code> or <code>rust_binary</code> + targets. + </p> + </td> + </tr> + </tbody> +</table> + +### Example + +Suppose you have the following directory structure for a Rust library crate: + +``` +[workspace]/ + WORKSPACE + hello_lib/ + BUILD + src/ + lib.rs +``` + +To run [documentation tests][doc-test] for the `hello_lib` crate, define a +`rust_doc_test` target that depends on the `hello_lib` `rust_library` target: + +[doc-test]: https://doc.rust-lang.org/book/documentation.html#documentation-as-tests + +```python +package(default_visibility = ["//visibility:public"]) + +load("/tools/build_rules/rust/rust", "rust_library", "rust_doc_test") + +rust_library( + name = "hello_lib", + srcs = ["src/lib.rs"], +) + +rust_doc_test( + name = "hello_lib_doc_test", + dep = ":hello_lib", +) +``` + +Running `bazel test //hello_lib:hello_lib_doc_test` will run all documentation +tests for the `hello_lib` library crate. |