diff --git a/.gitignore b/.gitignore index a6ef824c1f..5a105330db 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +*.swp /bazel-* diff --git a/WORKSPACE b/WORKSPACE index 3f1957f104..a762f946bd 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,4 +1,26 @@ workspace(name = "io_bazel_rules_rust") load("//rust:rust.bzl", "rust_repositories") + rust_repositories() + +# Used for documenting Rust rules. +git_repository( + name = "io_bazel_rules_sass", + remote = "https://github.com/bazelbuild/rules_sass.git", + tag = "0.0.2", +) + +load("@io_bazel_rules_sass//sass:sass.bzl", "sass_repositories") + +sass_repositories() + +git_repository( + name = "io_bazel_skydoc", + remote = "https://github.com/bazelbuild/skydoc.git", + tag = "0.1.2", +) + +load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories") + +skydoc_repositories() diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000000..0526883908 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,201 @@ + + + + + + + + + + + Overview + + + + + + + + +
+
+
+ Overview +
+
+
+ Bazel + +
+ +
+
+

Overview

+ + + + +

Rust Rules

+ +

Rules

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + rust_bench_test + + +

Builds a Rust benchmark test.

+ +
+ + rust_binary + + +

Builds a Rust binary crate.

+ +
+ + rust_doc + + +

Generates code documentation.

+ +
+ + rust_doc_test + + +

Runs Rust documentation tests.

+ +
+ + rust_library + + +

Builds a Rust library crate.

+ +
+ + rust_test + + +

Builds a Rust test crate.

+ +
+

Macros

+ + + + + + + + + + + +
+ + rust_repositories + + +

Adds the external dependencies needed for the Rust rules.

+ +
+ + +
+ + +
+
+ + diff --git a/docs/main.css b/docs/main.css new file mode 100755 index 0000000000..f506e12273 --- /dev/null +++ b/docs/main.css @@ -0,0 +1,3 @@ +body{background-color:#fafafa}pre,code{font-family:'Liberation Mono', Consolas, Monaco, 'Andale Mono', monospace}pre{background-color:#eee;padding:20px;overflow-x:auto;word-wrap:normal}pre code{overflow-wrap:normal;white-space:pre}code{display:inline-block;font-size:90%;white-space:pre-wrap}.mdl-layout__drawer{background-color:#fff}.mdl-layout__drawer .mdl-layout-title{border-bottom:1px solid #e0e0e0;padding-left:24px}.drawer-nav ul{list-style:none;padding-left:0}.drawer-nav ul li{display:block;padding:0}.drawer-nav ul li ul li a{padding-left:44px;font-weight:400}.drawer-nav ul li a{display:block;flex-shrink:0;padding:15px 0 15px 22px;margin:0;font-weight:600;color:#757575;line-height:1em;text-decoration:none;cursor:pointer}.drawer-nav ul li a:active,.drawer-nav ul li a:hover{background-color:#f0f0f0}.drawer-nav ul li.active a{color:#4caf50;font-weight:500}h1.page-title{font-size:34px;font-weight:400;line-height:40px;margin-bottom:30px;color:#4caf50}p.lead{font-size:20px;line-height:32px}table{border-collapse:collapse;border-spacing:0;background-color:#fff;table-layout:auto}table thead th{background-color:#fafafa;border:1px solid #eee;color:#757575;padding:12px 12px 12px 24px;vertical-align:top}table tbody td{border:1px solid #eee;padding:12px 12px 12px 24px;vertical-align:top}table.params-table{width:100%}table.params-table col.col-param{width:25%}table.params-table col.col-description{width:75%}table.overview-table{width:100%}table.overview-table col.col-name{width:25%}table.overview-table col.col-description{width:75%}table.overview-table td p{margin:0}hr{margin-top:80px;margin-bottom:80px}nav.toc{border-left:5px solid #4caf50;padding-left:20px;margin-bottom:48px}nav.toc h1,nav.toc h2{font-size:15px;line-height:16px;padding-bottom:12px;margin-bottom:0;font-weight:400;color:#757575}nav.toc ul{list-style:none;margin-top:0;padding-left:0}nav.toc ul li{font-size:20px;line-height:40px}nav.toc ul li a{color:#4caf50}.page-content{margin-left:auto;margin-right:auto;padding-top:60px;padding-bottom:60px;width:760px}.page-content a{text-decoration:none}.page-content h1{font-size:34px;font-weight:400;line-height:40px;margin-bottom:30px;color:#4caf50}.page-content h2{font-size:24px;font-weight:400;line-height:32px;margin-bottom:30px;color:#4caf50}.page-content h3{font-size:20px;font-weight:400;line-height:32px;margin-bottom:30px;color:#4caf50}@media (max-width: 768px){.page-content{width:360px}}@media (min-width: 768px){.page-content{width:760px}}@media (min-width: 1476px){.page-content{width:1160px}}.mdl-mini-footer{padding-left:40px} + +/*# sourceMappingURL=main.css.map */ \ No newline at end of file diff --git a/docs/rust/rust.html b/docs/rust/rust.html new file mode 100644 index 0000000000..ebd10d17e4 --- /dev/null +++ b/docs/rust/rust.html @@ -0,0 +1,923 @@ + + + + + + + + + + + Rust Rules + + + + + + + + +
+
+
+ Rust Rules +
+
+
+ Bazel + +
+ +
+
+

Rust Rules

+ + +
+

Overview

+

These build rules are used for building Rust projects with Bazel.

+

Setup

+

To use the Rust rules, add the following to your WORKSPACE file to add the +external repositories for the Rust toolchain:

+
git_repository(
+    name = "io_bazel_rules_rust",
+    remote = "https://github.com/bazelbuild/rules_rust.git",
+    tag = "0.0.1",
+)
+load("@io_bazel_rules_rust//rust:rust.bzl", "rust_repositories")
+
+rust_repositories()
+
+

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.
  • +
  • Add cargo_crate workspace rule for pulling crates from +Cargo.
  • +
+ +
+ +

rust_repositories

+ +
rust_repositories()
+ +

Adds the external dependencies needed for the Rust rules.

+ + +
+ +

rust_bench_test

+ +
rust_bench_test(name, deps, data, srcs, crate_features, crate_root, rustc_flags)
+ +

Builds a Rust benchmark test.

+

Warning: This rule is currently experimental. Rust Benchmark +tests 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 to make it +easy to use a custom Rust toolchain, such as a nightly release.

+ + +

Attributes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name +

Name; Required

+

This name will also be used as the name of the binary crate built by +this rule.

+
deps +

List of labels; Optional; Default is []

+

List of other libraries to be linked to this library target.

+

These must be rust_library targets.

+
data +

List of labels; Optional; Default is []

+

List of files used by this rule at runtime.

+

This attribute can be used to specify any data files that are embedded into +the library, such as via the +include_str! +macro.

+
srcs +

List of labels; Optional; Default is []

+

List of Rust .rs source files used to build the test.

+

If srcs contains more than one file, then there must be a file either +named lib.rs. Otherwise, crate_root must be set to the source file that +is the root of the crate to be passed to rustc to build this crate.

+
crate_features +

List of strings; Optional; Default is []

+

List of features to enable for this crate.

+

Features are defined in the code using the #[cfg(feature = "foo")] +configuration option. The features listed here will be passed to rustc +with --cfg feature="${feature_name}" flags.

+
crate_root +

Label; Optional

+

The file that will be passed to rustc to be used for building +this crate.

+

If crate_root is not set, then this rule will look for a lib.rs file or +the single file in srcs if srcs contains only one file.

+
rustc_flags +

List of strings; Optional; Default is []

+

List of compiler flags passed to rustc.

+
+

Examples

+

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:

+
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:

+
#![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:

+
package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_rust//rust:rust.bzl", "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.

+ +
+ +

rust_binary

+ +
rust_binary(name, deps, data, srcs, crate_features, crate_root, rustc_flags)
+ +

Builds a Rust binary crate.

+ + +

Attributes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name +

Name; Required

+

This name will also be used as the name of the binary crate built by +this rule.

+
deps +

List of labels; Optional; Default is []

+

List of other libraries to be linked to this library target.

+

These must be rust_library targets.

+
data +

List of labels; Optional; Default is []

+

List of files used by this rule at runtime.

+

This attribute can be used to specify any data files that are embedded into +the library, such as via the +include_str! +macro.

+
srcs +

List of labels; Optional; Default is []

+

List of Rust .rs source files used to build the test.

+

If srcs contains more than one file, then there must be a file either +named lib.rs. Otherwise, crate_root must be set to the source file that +is the root of the crate to be passed to rustc to build this crate.

+
crate_features +

List of strings; Optional; Default is []

+

List of features to enable for this crate.

+

Features are defined in the code using the #[cfg(feature = "foo")] +configuration option. The features listed here will be passed to rustc +with --cfg feature="${feature_name}" flags.

+
crate_root +

Label; Optional

+

The file that will be passed to rustc to be used for building +this crate.

+

If crate_root is not set, then this rule will look for a lib.rs file or +the single file in srcs if srcs contains only one file.

+
rustc_flags +

List of strings; Optional; Default is []

+

List of compiler flags passed to rustc.

+
+

Examples

+

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:

+
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:

+
package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_rust//rust:rust.bzl", "rust_library")
+
+rust_library(
+    name = "hello_lib",
+    srcs = ["src/lib.rs"],
+)
+
+

hello_world/src/main.rs:

+
extern crate hello_lib;
+
+fn main() {
+    let hello = hello_lib::Greeter::new("Hello");
+    hello.greet("world");
+}
+
+

hello_world/BUILD:

+
load("@io_bazel_rules_rust//rust:rust.bzl", "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
+
+ +
+ +

rust_doc

+ +
rust_doc(name, dep, html_after_content, html_before_content, html_in_header, markdown_css)
+ +

Generates code documentation.

+ + +

Attributes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name +

Name; Required

+

A unique name for this rule.

+
dep +

Label; Required

+

The label of the target to run documentation tests for.

+

rust_doc_test can run documentation tests for the source files of +rust_library or rust_binary targets.

+
html_after_content +

Label; Optional

+

File to add in &lt;body&gt;, after content.

+
html_before_content +

Label; Optional

+

File to add in &lt;body&gt;, before content.

+
html_in_header +

Label; Optional

+

File to add to &lt;head&gt;.

+
markdown_css +

List of labels; Optional; Default is []

+

CSS files to include via &lt;link&gt; in a rendered +Markdown file.

+
+

Examples

+

Suppose you have the following directory structure for a Rust library crate:

+
[workspace]/
+    WORKSPACE
+    hello_lib/
+        BUILD
+        src/
+            lib.rs
+
+

To build rustdoc documentation for the hello_lib crate, define +a rust_doc rule that depends on the the hello_lib rust_library target:

+
package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_rust//rust:rust.bzl", "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.

+ +
+ +

rust_doc_test

+ +
rust_doc_test(name, dep)
+ +

Runs Rust documentation tests.

+ + +

Attributes

+ + + + + + + + + + + + + + + + +
name +

Name; Required

+

A unique name for this rule.

+
dep +

Label; Required

+

The label of the target to run documentation tests for.

+

rust_doc_test can run documentation tests for the source files of +rust_library or rust_binary targets.

+
+

Examples

+

Suppose you have the following directory structure for a Rust library crate:

+
[workspace]/
+    WORKSPACE
+    hello_lib/
+        BUILD
+        src/
+            lib.rs
+
+

To run documentation tests for the hello_lib crate, define a +rust_doc_test target that depends on the hello_lib rust_library target:

+
package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_rust//rust:rust.bzl", "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.

+ +
+ +

rust_library

+ +
rust_library(name, deps, data, srcs, crate_features, crate_root, crate_type, rustc_flags)
+ +

Builds a Rust library crate.

+ + +

Attributes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name +

Name; Required

+

This name will also be used as the name of the library crate built by +this rule.

+
deps +

List of labels; Optional; Default is []

+

List of other libraries to be linked to this library target.

+

These must be rust_library targets.

+
data +

List of labels; Optional; Default is []

+

List of files used by this rule at runtime.

+

This attribute can be used to specify any data files that are embedded into +the library, such as via the +include_str! +macro.

+
srcs +

List of labels; Optional; Default is []

+

List of Rust .rs source files used to build the test.

+

If srcs contains more than one file, then there must be a file either +named lib.rs. Otherwise, crate_root must be set to the source file that +is the root of the crate to be passed to rustc to build this crate.

+
crate_features +

List of strings; Optional; Default is []

+

List of features to enable for this crate.

+

Features are defined in the code using the #[cfg(feature = "foo")] +configuration option. The features listed here will be passed to rustc +with --cfg feature="${feature_name}" flags.

+
crate_root +

Label; Optional

+

The file that will be passed to rustc to be used for building +this crate.

+

If crate_root is not set, then this rule will look for a lib.rs file or +the single file in srcs if srcs contains only one file.

+
crate_type +

String; Optional; Default is ''

+ +
rustc_flags +

List of strings; Optional; Default is []

+

List of compiler flags passed to rustc.

+
+

Examples

+

Suppose you have the following directory structure for a simple Rust library +crate:

+
[workspace]/
+    WORKSPACE
+    hello_lib/
+        BUILD
+        src/
+            greeter.rs
+            lib.rs
+
+

hello_lib/src/greeter.rs:

+
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/src/lib.rs:

+
pub mod greeter;
+
+

hello_lib/BUILD:

+
package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_rust//rust:rust.bzl", "rust_library")
+
+rust_library(
+    name = "hello_lib",
+    srcs = [
+        "src/greeter.rs",
+        "src/lib.rs",
+    ],
+)
+
+

Build the library:

+
$ bazel build //hello_lib
+INFO: Found 1 target...
+Target //examples/rust/hello_lib:hello_lib up-to-date:
+  bazel-bin/examples/rust/hello_lib/libhello_lib.rlib
+INFO: Elapsed time: 1.245s, Critical Path: 1.01s
+
+ +
+ +

rust_test

+ +
rust_test(name, deps, data, srcs, crate_features, crate_root, rustc_flags)
+ +

Builds a Rust test crate.

+ + +

Attributes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name +

Name; Required

+

This name will also be used as the name of the binary crate built by +this rule.

+
deps +

List of labels; Optional; Default is []

+

List of other libraries to be linked to this library target.

+

These must be rust_library targets.

+
data +

List of labels; Optional; Default is []

+

List of files used by this rule at runtime.

+

This attribute can be used to specify any data files that are embedded into +the library, such as via the +include_str! +macro.

+
srcs +

List of labels; Optional; Default is []

+

List of Rust .rs source files used to build the test.

+

If srcs contains more than one file, then there must be a file either +named lib.rs. Otherwise, crate_root must be set to the source file that +is the root of the crate to be passed to rustc to build this crate.

+
crate_features +

List of strings; Optional; Default is []

+

List of features to enable for this crate.

+

Features are defined in the code using the #[cfg(feature = "foo")] +configuration option. The features listed here will be passed to rustc +with --cfg feature="${feature_name}" flags.

+
crate_root +

Label; Optional

+

The file that will be passed to rustc to be used for building +this crate.

+

If crate_root is not set, then this rule will look for a lib.rs file or +the single file in srcs if srcs contains only one file.

+
rustc_flags +

List of strings; Optional; Default is []

+

List of compiler flags passed to rustc.

+
+

Examples

+

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:

+
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:

+
package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_rust//rust:rust.bzl", "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, 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:

+
[workspace]/
+    WORKSPACE
+    hello_lib/
+        BUILD
+        src/
+            lib.rs
+        tests/
+            greeting.rs
+
+

hello_lib/tests/greeting.rs:

+
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:

+
package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_rust//rust:rust.bzl", "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.

+ + + +
+ + +
+
+ + diff --git a/rust/BUILD b/rust/BUILD index 3abf631571..21e9bb8664 100644 --- a/rust/BUILD +++ b/rust/BUILD @@ -1,9 +1,11 @@ package(default_visibility = ["//visibility:public"]) -filegroup( - name = "srcs", - srcs = glob(["**"]), - visibility = ["//tools:__pkg__"], +load("@io_bazel_skydoc//skylark:skylark.bzl", "skylark_doc") + +skylark_doc( + name = "rust-docs", + srcs = ["rust.bzl"], + format = "html", ) config_setting( diff --git a/rust/rust.bzl b/rust/rust.bzl index 6d904d2963..5546e2d1ec 100644 --- a/rust/rust.bzl +++ b/rust/rust.bzl @@ -12,7 +12,38 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Rust rules for Bazel""" +"""Rust Rules + +These build rules are used for building [Rust][rust] projects with Bazel. + +[rust]: http://www.rust-lang.org/ + +### Setup + +To use the Rust rules, add the following to your `WORKSPACE` file to add the +external repositories for the Rust toolchain: + +```python +git_repository( + name = "io_bazel_rules_rust", + remote = "https://github.com/bazelbuild/rules_rust.git", + tag = "0.0.1", +) +load("@io_bazel_rules_rust//rust:rust.bzl", "rust_repositories") + +rust_repositories() +``` + +### 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/). +""" RUST_FILETYPE = FileType([".rs"]) @@ -167,16 +198,16 @@ def _get_features_flags(features): features_flags += ["--cfg feature=\\\"%s\\\"" % feature] return features_flags -def get_dirname(short_path): +def _get_dirname(short_path): return short_path[0:short_path.rfind('/')] def _rust_toolchain(ctx): return struct( rustc_path = ctx.file._rustc.path, rustc_lib_path = ctx.files._rustc_lib[0].dirname, - rustc_lib_short_path = get_dirname(ctx.files._rustc_lib[0].short_path), + rustc_lib_short_path = _get_dirname(ctx.files._rustc_lib[0].short_path), rustlib_path = ctx.files._rustlib[0].dirname, - rustlib_short_path = get_dirname(ctx.files._rustlib[0].short_path), + rustlib_short_path = _get_dirname(ctx.files._rustlib[0].short_path), rustdoc_path = ctx.file._rustdoc.path, rustdoc_short_path = ctx.file._rustdoc.short_path) @@ -627,47 +658,528 @@ _rust_toolchain_attrs = { ), } -_rust_library_attrs = _rust_common_attrs + { +_rust_library_attrs = { "crate_type": attr.string(), } rust_library = rule( _rust_library_impl, - attrs = _rust_library_attrs + _rust_toolchain_attrs, + attrs = dict(_rust_common_attrs.items() + + _rust_library_attrs.items() + + _rust_toolchain_attrs.items()), fragments = ["cpp"], outputs = { "rust_lib": "lib%{name}.rlib", }, ) +"""Builds a Rust library crate. + +Args: + name: This name will also be used as the name of the library crate built by + this rule. + srcs: List of Rust `.rs` source files used to build the library. + + If `srcs` contains more than one file, then there must be a file either + named `lib.rs`. Otherwise, `crate_root` must be set to the source file that + is the root of the crate to be passed to rustc to build this crate. + crate_root: The file that will be passed to `rustc` to be used for building + this crate. + + If `crate_root` is not set, then this rule will look for a `lib.rs` file or + the single file in `srcs` if `srcs` contains only one file. + deps: List of other libraries to be linked to this library target. + + These can be either other `rust_library` targets or `cc_library` targets if + linking a native library. + data: List of files used by this rule at runtime. + + This attribute can be used to specify any data files that are embedded into + the library, such as via the + [`include_str!`](https://doc.rust-lang.org/std/macro.include_str!.html) + macro. + crate_features: List of features to enable for this crate. + + Features are defined in the code using the `#[cfg(feature = "foo")]` + configuration option. The features listed here will be passed to `rustc` + with `--cfg feature="${feature_name}"` flags. + rustc_flags: List of compiler flags passed to `rustc`. + +Example: + Suppose you have the following directory structure for a simple Rust library + crate: + + ``` + [workspace]/ + WORKSPACE + hello_lib/ + BUILD + src/ + greeter.rs + lib.rs + ``` + + `hello_lib/src/greeter.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/src/lib.rs`: + + + ```rust + pub mod greeter; + ``` + + `hello_lib/BUILD`: + + ```python + package(default_visibility = ["//visibility:public"]) + + load("@io_bazel_rules_rust//rust:rust.bzl", "rust_library") + + rust_library( + name = "hello_lib", + srcs = [ + "src/greeter.rs", + "src/lib.rs", + ], + ) + ``` + + Build the library: + + ``` + $ bazel build //hello_lib + INFO: Found 1 target... + Target //examples/rust/hello_lib:hello_lib up-to-date: + bazel-bin/examples/rust/hello_lib/libhello_lib.rlib + INFO: Elapsed time: 1.245s, Critical Path: 1.01s + ``` +""" rust_binary = rule( _rust_binary_impl, - attrs = _rust_common_attrs + _rust_toolchain_attrs, + attrs = dict(_rust_common_attrs.items() + _rust_toolchain_attrs.items()), executable = True, fragments = ["cpp"], ) +"""Builds a Rust binary crate. + +Args: + name: This name will also be used as the name of the binary crate built by + this rule. + srcs: List of Rust `.rs` source files used to build the library. + + If `srcs` contains more than one file, then there must be a file either + named `main.rs`. Otherwise, `crate_root` must be set to the source file that + is the root of the crate to be passed to rustc to build this crate. + crate_root: The file that will be passed to `rustc` to be used for building + this crate. + + If `crate_root` is not set, then this rule will look for a `bin.rs` file or + the single file in `srcs` if `srcs` contains only one file. + deps: List of other libraries to be linked to this library target. + + These must be `rust_library` targets. + data: List of files used by this rule at runtime. + + This attribute can be used to specify any data files that are embedded into + the library, such as via the + [`include_str!`](https://doc.rust-lang.org/std/macro.include_str!.html) + macro. + crate_features: List of features to enable for this crate. + + Features are defined in the code using the `#[cfg(feature = "foo")]` + configuration option. The features listed here will be passed to `rustc` + with `--cfg feature="${feature_name}"` flags. + rustc_flags: List of compiler flags passed to `rustc`. + +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("@io_bazel_rules_rust//rust:rust.bzl", "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("@io_bazel_rules_rust//rust:rust.bzl", "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 + ``` +""" rust_test = rule( _rust_test_impl, - attrs = _rust_common_attrs + _rust_toolchain_attrs, + attrs = dict(_rust_common_attrs.items() + _rust_toolchain_attrs.items()), executable = True, fragments = ["cpp"], test = True, ) +"""Builds a Rust test crate. + +Args: + name: This name will also be used as the name of the binary crate built by + this rule. + srcs: List of Rust `.rs` source files used to build the test. + + If `srcs` contains more than one file, then there must be a file either + named `lib.rs`. Otherwise, `crate_root` must be set to the source file that + is the root of the crate to be passed to rustc to build this crate. + crate_root: The file that will be passed to `rustc` to be used for building + this crate. + + If `crate_root` is not set, then this rule will look for a `lib.rs` file or + the single file in `srcs` if `srcs` contains only one file. + deps: List of other libraries to be linked to this library target. + + These must be `rust_library` targets. + data: List of files used by this rule at runtime. + + This attribute can be used to specify any data files that are embedded into + the library, such as via the + [`include_str!`](https://doc.rust-lang.org/std/macro.include_str!.html) + macro. + crate_features: List of features to enable for this crate. + + Features are defined in the code using the `#[cfg(feature = "foo")]` + configuration option. The features listed here will be passed to `rustc` + with `--cfg feature="${feature_name}"` flags. + rustc_flags: List of compiler flags passed to `rustc`. + +Examples: + 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 + package(default_visibility = ["//visibility:public"]) + + load("@io_bazel_rules_rust//rust:rust.bzl", "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("@io_bazel_rules_rust//rust:rust.bzl", "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`. +""" rust_bench_test = rule( _rust_bench_test_impl, - attrs = _rust_common_attrs + _rust_toolchain_attrs, + attrs = dict(_rust_common_attrs.items() + _rust_toolchain_attrs.items()), executable = True, fragments = ["cpp"], test = True, ) +"""Builds a Rust benchmark test. + +**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 + +Args: + name: This name will also be used as the name of the binary crate built by + this rule. + srcs: List of Rust `.rs` source files used to build the test. + + If `srcs` contains more than one file, then there must be a file either + named `lib.rs`. Otherwise, `crate_root` must be set to the source file that + is the root of the crate to be passed to rustc to build this crate. + crate_root: The file that will be passed to `rustc` to be used for building + this crate. + + If `crate_root` is not set, then this rule will look for a `lib.rs` file or + the single file in `srcs` if `srcs` contains only one file. + deps: List of other libraries to be linked to this library target. + + These must be `rust_library` targets. + data: List of files used by this rule at runtime. + + This attribute can be used to specify any data files that are embedded into + the library, such as via the + [`include_str!`](https://doc.rust-lang.org/std/macro.include_str!.html) + macro. + crate_features: List of features to enable for this crate. + + Features are defined in the code using the `#[cfg(feature = "foo")]` + configuration option. The features listed here will be passed to `rustc` + with `--cfg feature="${feature_name}"` flags. + rustc_flags: List of compiler flags passed to `rustc`. + +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("@io_bazel_rules_rust//rust:rust.bzl", "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`. +""" _rust_doc_common_attrs = { "dep": attr.label(mandatory = True), } -_rust_doc_attrs = _rust_doc_common_attrs + { +_rust_doc_attrs = { "markdown_css": attr.label_list(allow_files = CSS_FILETYPE), "html_in_header": attr.label(allow_files = HTML_MD_FILETYPE), "html_before_content": attr.label(allow_files = HTML_MD_FILETYPE), @@ -676,18 +1188,116 @@ _rust_doc_attrs = _rust_doc_common_attrs + { rust_doc = rule( _rust_doc_impl, - attrs = _rust_doc_attrs + _rust_toolchain_attrs, + attrs = dict(_rust_doc_common_attrs.items() + + _rust_doc_attrs.items() + + _rust_toolchain_attrs.items()), outputs = { "rust_doc_zip": "%{name}-docs.zip", }, ) +"""Generates code documentation. + +Args: + name: A unique name for this rule. + dep: The label of the target to generate code documentation for. + + `rust_doc` can generate HTML code documentation for the source files of + `rust_library` or `rust_binary` targets. + markdown_css: CSS files to include via `` in a rendered + Markdown file. + html_in_header: File to add to ``. + html_before_content: File to add in ``, before content. + html_after_content: File to add in ``, after content. + +Example: + Suppose you have the following directory structure for a Rust library crate: + + ``` + [workspace]/ + WORKSPACE + hello_lib/ + BUILD + src/ + lib.rs + ``` + + 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: + + [rustdoc]: https://doc.rust-lang.org/book/documentation.html + + ```python + package(default_visibility = ["//visibility:public"]) + + load("@io_bazel_rules_rust//rust:rust.bzl", "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`. +""" rust_doc_test = rule( _rust_doc_test_impl, - attrs = _rust_doc_common_attrs + _rust_toolchain_attrs, + attrs = dict(_rust_doc_common_attrs.items() + + _rust_toolchain_attrs.items()), executable = True, test = True, ) +"""Runs Rust documentation tests. + +Args: + name: A unique name for this rule. + dep: The label of the target to run documentation tests for. + + `rust_doc_test` can run documentation tests for the source files of + `rust_library` or `rust_binary` targets. + +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("@io_bazel_rules_rust//rust:rust.bzl", "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. +""" RUST_BUILD_FILE = """ config_setting( @@ -752,6 +1362,7 @@ filegroup( """ def rust_repositories(): + """Adds the external dependencies needed for the Rust rules.""" native.new_http_archive( name = "rust_linux_x86_64", url = "http://bazel-mirror.storage.googleapis.com/static.rust-lang.org/dist/rust-1.11.0-x86_64-unknown-linux-gnu.tar.gz",