Squashed 'third_party/rules_rust/' content from commit bf59038ca
git-subtree-dir: third_party/rules_rust
git-subtree-split: bf59038cac11798cbaef9f3bf965bad8182b97fa
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I5a20e403203d670df467ea97dde9a4ac40339a8d
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
new file mode 100644
index 0000000..58cc9c4
--- /dev/null
+++ b/rust/private/rust.bzl
@@ -0,0 +1,1151 @@
+# Copyright 2015 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# buildifier: disable=module-docstring
+load("//rust/private:common.bzl", "rust_common")
+load("//rust/private:rustc.bzl", "rustc_compile_action")
+load(
+ "//rust/private:utils.bzl",
+ "compute_crate_name",
+ "dedent",
+ "determine_output_hash",
+ "expand_dict_value_locations",
+ "find_toolchain",
+ "get_import_macro_deps",
+ "transform_deps",
+)
+
+# TODO(marco): Separate each rule into its own file.
+
+def _assert_no_deprecated_attributes(_ctx):
+ """Forces a failure if any deprecated attributes were specified
+
+ Args:
+ _ctx (ctx): The current rule's context object
+ """
+ pass
+
+def _assert_correct_dep_mapping(ctx):
+ """Forces a failure if proc_macro_deps and deps are mixed inappropriately
+
+ Args:
+ ctx (ctx): The current rule's context object
+ """
+ for dep in ctx.attr.deps:
+ if rust_common.crate_info in dep:
+ if dep[rust_common.crate_info].type == "proc-macro":
+ fail(
+ "{} listed {} in its deps, but it is a proc-macro. It should instead be in the bazel property proc_macro_deps.".format(
+ ctx.label,
+ dep.label,
+ ),
+ )
+ for dep in ctx.attr.proc_macro_deps:
+ type = dep[rust_common.crate_info].type
+ if type != "proc-macro":
+ fail(
+ "{} listed {} in its proc_macro_deps, but it is not proc-macro, it is a {}. It should probably instead be listed in deps.".format(
+ ctx.label,
+ dep.label,
+ type,
+ ),
+ )
+
+def _determine_lib_name(name, crate_type, toolchain, lib_hash = None):
+ """See https://github.com/bazelbuild/rules_rust/issues/405
+
+ Args:
+ name (str): The name of the current target
+ crate_type (str): The `crate_type`
+ toolchain (rust_toolchain): The current `rust_toolchain`
+ lib_hash (str, optional): The hashed crate root path
+
+ Returns:
+ str: A unique library name
+ """
+ extension = None
+ prefix = ""
+ if crate_type in ("dylib", "cdylib", "proc-macro"):
+ extension = toolchain.dylib_ext
+ elif crate_type == "staticlib":
+ extension = toolchain.staticlib_ext
+ elif crate_type in ("lib", "rlib"):
+ # All platforms produce 'rlib' here
+ extension = ".rlib"
+ prefix = "lib"
+ elif crate_type == "bin":
+ fail("crate_type of 'bin' was detected in a rust_library. Please compile " +
+ "this crate as a rust_binary instead.")
+
+ if not extension:
+ fail(("Unknown crate_type: {}. If this is a cargo-supported crate type, " +
+ "please file an issue!").format(crate_type))
+
+ prefix = "lib"
+ if (toolchain.target_triple.find("windows") != -1) and crate_type not in ("lib", "rlib"):
+ prefix = ""
+ if toolchain.target_arch == "wasm32" and crate_type == "cdylib":
+ prefix = ""
+
+ return "{prefix}{name}{lib_hash}{extension}".format(
+ prefix = prefix,
+ name = name,
+ lib_hash = "-" + lib_hash if lib_hash else "",
+ extension = extension,
+ )
+
+def get_edition(attr, toolchain):
+ """Returns the Rust edition from either the current rule's attirbutes or the current `rust_toolchain`
+
+ Args:
+ attr (struct): The current rule's attributes
+ toolchain (rust_toolchain): The `rust_toolchain` for the current target
+
+ Returns:
+ str: The target Rust edition
+ """
+ if getattr(attr, "edition"):
+ return attr.edition
+ else:
+ return toolchain.default_edition
+
+def crate_root_src(attr, srcs, crate_type):
+ """Finds the source file for the crate root.
+
+ Args:
+ attr (struct): The attributes of the current target
+ srcs (list): A list of all sources for the target Crate.
+ crate_type (str): The type of this crate ("bin", "lib", "rlib", "cdylib", etc).
+
+ Returns:
+ File: The root File object for a given crate. See the following links for more details:
+ - https://doc.rust-lang.org/cargo/reference/cargo-targets.html#library
+ - https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries
+ """
+ default_crate_root_filename = "main.rs" if crate_type == "bin" else "lib.rs"
+
+ crate_root = None
+ if hasattr(attr, "crate_root"):
+ if attr.crate_root:
+ crate_root = attr.crate_root.files.to_list()[0]
+
+ if not crate_root:
+ crate_root = (
+ (srcs[0] if len(srcs) == 1 else None) or
+ _shortest_src_with_basename(srcs, default_crate_root_filename) or
+ _shortest_src_with_basename(srcs, attr.name + ".rs")
+ )
+ if not crate_root:
+ file_names = [default_crate_root_filename, attr.name + ".rs"]
+ fail("No {} source file found.".format(" or ".join(file_names)), "srcs")
+ return crate_root
+
+def _shortest_src_with_basename(srcs, basename):
+ """Finds the shortest among the paths in srcs that match the desired basename.
+
+ Args:
+ srcs (list): A list of File objects
+ basename (str): The target basename to match against.
+
+ Returns:
+ File: The File object with the shortest path that matches `basename`
+ """
+ shortest = None
+ for f in srcs:
+ if f.basename == basename:
+ if not shortest or len(f.dirname) < len(shortest.dirname):
+ shortest = f
+ return shortest
+
+def _rust_library_impl(ctx):
+ """The implementation of the `rust_library` rule.
+
+ This rule provides CcInfo, so it can be used everywhere Bazel
+ expects rules_cc, but care must be taken to have the correct
+ dependencies on an allocator and std implemetation as needed.
+
+ Args:
+ ctx (ctx): The rule's context object
+
+ Returns:
+ list: A list of providers.
+ """
+ return _rust_library_common(ctx, "rlib")
+
+def _rust_static_library_impl(ctx):
+ """The implementation of the `rust_static_library` rule.
+
+ This rule provides CcInfo, so it can be used everywhere Bazel
+ expects rules_cc.
+
+ Args:
+ ctx (ctx): The rule's context object
+
+ Returns:
+ list: A list of providers.
+ """
+ return _rust_library_common(ctx, "staticlib")
+
+def _rust_shared_library_impl(ctx):
+ """The implementation of the `rust_shared_library` rule.
+
+ This rule provides CcInfo, so it can be used everywhere Bazel
+ expects rules_cc.
+
+ On Windows, a PDB file containing debugging information is available under
+ the key `pdb_file` in `OutputGroupInfo`. Similarly on macOS, a dSYM folder
+ is available under the key `dsym_folder` in `OutputGroupInfo`.
+
+ Args:
+ ctx (ctx): The rule's context object
+
+ Returns:
+ list: A list of providers.
+ """
+ return _rust_library_common(ctx, "cdylib")
+
+def _rust_proc_macro_impl(ctx):
+ """The implementation of the `rust_proc_macro` rule.
+
+ Args:
+ ctx (ctx): The rule's context object
+
+ Returns:
+ list: A list of providers.
+ """
+ return _rust_library_common(ctx, "proc-macro")
+
+def _rust_library_common(ctx, crate_type):
+ """The common implementation of the library-like rules.
+
+ Args:
+ ctx (ctx): The rule's context object
+ crate_type (String): one of lib|rlib|dylib|staticlib|cdylib|proc-macro
+
+ Returns:
+ list: A list of providers. See `rustc_compile_action`
+ """
+
+ # Find lib.rs
+ crate_root = crate_root_src(ctx.attr, ctx.files.srcs, "lib")
+ _assert_no_deprecated_attributes(ctx)
+ _assert_correct_dep_mapping(ctx)
+
+ toolchain = find_toolchain(ctx)
+
+ # Determine unique hash for this rlib.
+ # Note that we don't include a hash for `cdylib` since they are meant to be consumed externally and having a
+ # deterministic name is important since it ends up embedded in the executable. This is problematic when one needs
+ # to include the library with a specific filename into a larger application.
+ # (see https://github.com/bazelbuild/rules_rust/issues/405#issuecomment-993089889 for more details)
+ if crate_type != "cdylib":
+ output_hash = determine_output_hash(crate_root, ctx.label)
+ else:
+ output_hash = None
+
+ crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
+ rust_lib_name = _determine_lib_name(
+ crate_name,
+ crate_type,
+ toolchain,
+ output_hash,
+ )
+ rust_lib = ctx.actions.declare_file(rust_lib_name)
+
+ deps = transform_deps(ctx.attr.deps)
+ proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
+
+ return rustc_compile_action(
+ ctx = ctx,
+ attr = ctx.attr,
+ toolchain = toolchain,
+ crate_info = rust_common.create_crate_info(
+ name = crate_name,
+ type = crate_type,
+ root = crate_root,
+ srcs = depset(ctx.files.srcs),
+ deps = depset(deps),
+ proc_macro_deps = depset(proc_macro_deps),
+ aliases = ctx.attr.aliases,
+ output = rust_lib,
+ edition = get_edition(ctx.attr, toolchain),
+ rustc_env = ctx.attr.rustc_env,
+ is_test = False,
+ compile_data = depset(ctx.files.compile_data),
+ owner = ctx.label,
+ ),
+ output_hash = output_hash,
+ )
+
+def _rust_binary_impl(ctx):
+ """The implementation of the `rust_binary` rule
+
+ Args:
+ ctx (ctx): The rule's context object
+
+ Returns:
+ list: A list of providers. See `rustc_compile_action`
+ """
+ toolchain = find_toolchain(ctx)
+ crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
+ _assert_correct_dep_mapping(ctx)
+
+ output = ctx.actions.declare_file(ctx.label.name + toolchain.binary_ext)
+
+ deps = transform_deps(ctx.attr.deps)
+ proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
+
+ return rustc_compile_action(
+ ctx = ctx,
+ attr = ctx.attr,
+ toolchain = toolchain,
+ crate_info = rust_common.create_crate_info(
+ name = crate_name,
+ type = ctx.attr.crate_type,
+ root = crate_root_src(ctx.attr, ctx.files.srcs, ctx.attr.crate_type),
+ srcs = depset(ctx.files.srcs),
+ deps = depset(deps),
+ proc_macro_deps = depset(proc_macro_deps),
+ aliases = ctx.attr.aliases,
+ output = output,
+ edition = get_edition(ctx.attr, toolchain),
+ rustc_env = ctx.attr.rustc_env,
+ is_test = False,
+ compile_data = depset(ctx.files.compile_data),
+ owner = ctx.label,
+ ),
+ )
+
+def _rust_test_common(ctx, toolchain, output):
+ """Builds a Rust test binary.
+
+ Args:
+ ctx (ctx): The ctx object for the current target.
+ toolchain (rust_toolchain): The current `rust_toolchain`
+ output (File): The output File that will be produced, depends on crate type.
+
+ Returns:
+ list: The list of providers. See `rustc_compile_action`
+ """
+ _assert_no_deprecated_attributes(ctx)
+ _assert_correct_dep_mapping(ctx)
+
+ crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
+ crate_type = "bin"
+
+ deps = transform_deps(ctx.attr.deps)
+ proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
+
+ if ctx.attr.crate:
+ # Target is building the crate in `test` config
+ crate = ctx.attr.crate[rust_common.crate_info]
+
+ # Optionally join compile data
+ if crate.compile_data:
+ compile_data = depset(ctx.files.compile_data, transitive = [crate.compile_data])
+ else:
+ compile_data = depset(ctx.files.compile_data)
+
+ # Build the test binary using the dependency's srcs.
+ crate_info = rust_common.create_crate_info(
+ name = crate_name,
+ type = crate_type,
+ root = crate.root,
+ srcs = depset(ctx.files.srcs, transitive = [crate.srcs]),
+ deps = depset(deps, transitive = [crate.deps]),
+ proc_macro_deps = depset(proc_macro_deps, transitive = [crate.proc_macro_deps]),
+ aliases = ctx.attr.aliases,
+ output = output,
+ edition = crate.edition,
+ rustc_env = ctx.attr.rustc_env,
+ is_test = True,
+ compile_data = compile_data,
+ wrapped_crate_type = crate.type,
+ owner = ctx.label,
+ )
+ else:
+ # Target is a standalone crate. Build the test binary as its own crate.
+ crate_info = rust_common.create_crate_info(
+ name = crate_name,
+ type = crate_type,
+ root = crate_root_src(ctx.attr, ctx.files.srcs, "lib"),
+ srcs = depset(ctx.files.srcs),
+ deps = depset(deps),
+ proc_macro_deps = depset(proc_macro_deps),
+ aliases = ctx.attr.aliases,
+ output = output,
+ edition = get_edition(ctx.attr, toolchain),
+ rustc_env = ctx.attr.rustc_env,
+ is_test = True,
+ compile_data = depset(ctx.files.compile_data),
+ owner = ctx.label,
+ )
+
+ providers = rustc_compile_action(
+ ctx = ctx,
+ attr = ctx.attr,
+ toolchain = toolchain,
+ crate_info = crate_info,
+ rust_flags = ["--test"] if ctx.attr.use_libtest_harness else ["--cfg", "test"],
+ )
+ data = getattr(ctx.attr, "data", [])
+
+ env = expand_dict_value_locations(
+ ctx,
+ getattr(ctx.attr, "env", {}),
+ data,
+ )
+ providers.append(testing.TestEnvironment(env))
+
+ return providers
+
+def _rust_test_impl(ctx):
+ """The implementation of the `rust_test` rule
+
+ Args:
+ ctx (ctx): The rule's context object
+
+ Returns:
+ list: A list of providers. See `_rust_test_common`
+ """
+ toolchain = find_toolchain(ctx)
+
+ output = ctx.actions.declare_file(
+ ctx.label.name + toolchain.binary_ext,
+ )
+
+ return _rust_test_common(ctx, toolchain, output)
+
+_common_attrs = {
+ "aliases": attr.label_keyed_string_dict(
+ doc = dedent("""\
+ Remap crates to a new name or moniker for linkage to this target
+
+ These are other `rust_library` targets and will be presented as the new name given.
+ """),
+ ),
+ "compile_data": attr.label_list(
+ doc = dedent("""\
+ List of files used by this rule at compile time.
+
+ 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.
+ """),
+ allow_files = True,
+ ),
+ "crate_features": attr.string_list(
+ doc = dedent("""\
+ 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_name": attr.string(
+ doc = dedent("""\
+ Crate name to use for this target.
+
+ This must be a valid Rust identifier, i.e. it may contain only alphanumeric characters and underscores.
+ Defaults to the target name, with any hyphens replaced by underscores.
+ """),
+ ),
+ "crate_root": attr.label(
+ doc = dedent("""\
+ 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 `main.rs` for rust_binary)
+ or the single file in `srcs` if `srcs` contains only one file.
+ """),
+ allow_single_file = [".rs"],
+ ),
+ "data": attr.label_list(
+ doc = dedent("""\
+ List of files used by this rule at compile time and runtime.
+
+ If including data at compile time with include_str!() and similar,
+ prefer `compile_data` over `data`, to prevent the data also being included
+ in the runfiles.
+ """),
+ allow_files = True,
+ ),
+ "deps": attr.label_list(
+ doc = dedent("""\
+ 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.
+ """),
+ ),
+ "edition": attr.string(
+ doc = "The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain.",
+ ),
+ # Previously `proc_macro_deps` were a part of `deps`, and then proc_macro_host_transition was
+ # used into cfg="host" using `@local_config_platform//:host`.
+ # This fails for remote execution, which needs cfg="exec", and there isn't anything like
+ # `@local_config_platform//:exec` exposed.
+ "proc_macro_deps": attr.label_list(
+ doc = dedent("""\
+ List of `rust_library` targets with kind `proc-macro` used to help build this library target.
+ """),
+ cfg = "exec",
+ providers = [rust_common.crate_info],
+ ),
+ "rustc_env": attr.string_dict(
+ doc = dedent("""\
+ Dictionary of additional `"key": "value"` environment variables to set for rustc.
+
+ rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the
+ location of a generated file or external tool. Cargo build scripts that wish to
+ expand locations should use cargo_build_script()'s build_script_env argument instead,
+ as build scripts are run in a different environment - see cargo_build_script()'s
+ documentation for more.
+ """),
+ ),
+ "rustc_env_files": attr.label_list(
+ doc = dedent("""\
+ Files containing additional environment variables to set for rustc.
+
+ These files should contain a single variable per line, of format
+ `NAME=value`, and newlines may be included in a value by ending a
+ line with a trailing back-slash (`\\\\`).
+
+ The order that these files will be processed is unspecified, so
+ multiple definitions of a particular variable are discouraged.
+
+ Note that the variables here are subject to
+ [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status)
+ stamping should the `stamp` attribute be enabled. Stamp variables
+ should be wrapped in brackets in order to be resolved. E.g.
+ `NAME={WORKSPACE_STATUS_VARIABLE}`.
+ """),
+ allow_files = True,
+ ),
+ "rustc_flags": attr.string_list(
+ doc = dedent("""\
+ List of compiler flags passed to `rustc`.
+
+ These strings are subject to Make variable expansion for predefined
+ source/output path variables like `$location`, `$execpath`, and
+ `$rootpath`. This expansion is useful if you wish to pass a generated
+ file of arguments to rustc: `@$(location //package:target)`.
+ """),
+ ),
+ # TODO(stardoc): How do we provide additional documentation to an inherited attribute?
+ # "name": attr.string(
+ # doc = "This name will also be used as the name of the crate built by this rule.",
+ # `),
+ "srcs": attr.label_list(
+ doc = dedent("""\
+ 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.
+ """),
+ allow_files = [".rs"],
+ ),
+ "stamp": attr.int(
+ doc = dedent("""\
+ Whether to encode build information into the `Rustc` action. Possible values:
+
+ - `stamp = 1`: Always stamp the build information into the `Rustc` action, even in \
+ [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \
+ This setting should be avoided, since it potentially kills remote caching for the target and \
+ any downstream actions that depend on it.
+
+ - `stamp = 0`: Always replace build information by constant values. This gives good build result caching.
+
+ - `stamp = -1`: Embedding of build information is controlled by the \
+ [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.
+
+ Stamped targets are not rebuilt unless their dependencies change.
+
+ For example if a `rust_library` is stamped, and a `rust_binary` depends on that library, the stamped
+ library won't be rebuilt when we change sources of the `rust_binary`. This is different from how
+ [`cc_library.linkstamps`](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp)
+ behaves.
+ """),
+ default = -1,
+ values = [1, 0, -1],
+ ),
+ "version": attr.string(
+ doc = "A version to inject in the cargo environment variable.",
+ default = "0.0.0",
+ ),
+ "_cc_toolchain": attr.label(
+ default = "@bazel_tools//tools/cpp:current_cc_toolchain",
+ ),
+ "_error_format": attr.label(default = "//:error_format"),
+ "_extra_exec_rustc_flags": attr.label(default = "//:extra_exec_rustc_flags"),
+ "_extra_rustc_flags": attr.label(default = "//:extra_rustc_flags"),
+ "_import_macro_dep": attr.label(
+ default = "@rules_rust//util/import",
+ ),
+ "_process_wrapper": attr.label(
+ default = Label("//util/process_wrapper"),
+ executable = True,
+ allow_single_file = True,
+ cfg = "exec",
+ ),
+ "_stamp_flag": attr.label(
+ doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
+ default = Label("//rust/private:stamp"),
+ ),
+}
+
+_rust_test_attrs = {
+ "crate": attr.label(
+ mandatory = False,
+ doc = dedent("""\
+ Target inline tests declared in the given crate
+
+ These tests are typically those that would be held out under
+ `#[cfg(test)]` declarations.
+ """),
+ ),
+ "env": attr.string_dict(
+ mandatory = False,
+ doc = dedent("""\
+ Specifies additional environment variables to set when the test is executed by bazel test.
+ Values are subject to `$(rootpath)`, `$(execpath)`, location, and
+ ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution.
+
+ Execpath returns absolute path, and in order to be able to construct the absolute path we
+ need to wrap the test binary in a launcher. Using a launcher comes with complications, such as
+ more complicated debugger attachment.
+ """),
+ ),
+ "use_libtest_harness": attr.bool(
+ mandatory = False,
+ default = True,
+ doc = dedent("""\
+ Whether to use `libtest`. For targets using this flag, individual tests can be run by using the
+ [--test_arg](https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--test_arg) flag.
+ E.g. `bazel test //src:rust_test --test_arg=foo::test::test_fn`.
+ """),
+ ),
+ "_grep_includes": attr.label(
+ allow_single_file = True,
+ cfg = "exec",
+ default = Label("@bazel_tools//tools/cpp:grep-includes"),
+ executable = True,
+ ),
+}
+
+_common_providers = [
+ rust_common.crate_info,
+ rust_common.dep_info,
+ DefaultInfo,
+]
+
+rust_library = rule(
+ implementation = _rust_library_impl,
+ provides = _common_providers,
+ attrs = dict(_common_attrs.items()),
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ toolchains = [
+ str(Label("//rust:toolchain")),
+ "@bazel_tools//tools/cpp:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
+ doc = dedent("""\
+ Builds a Rust library crate.
+
+ Example:
+
+ Suppose you have the following directory structure for a simple Rust library crate:
+
+ ```output
+ [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("@rules_rust//rust:defs.bzl", "rust_library")
+
+ rust_library(
+ name = "hello_lib",
+ srcs = [
+ "src/greeter.rs",
+ "src/lib.rs",
+ ],
+ )
+ ```
+
+ Build the library:
+ ```output
+ $ 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_static_library = rule(
+ implementation = _rust_static_library_impl,
+ provides = _common_providers,
+ attrs = dict(_common_attrs.items()),
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ toolchains = [
+ str(Label("//rust:toolchain")),
+ "@bazel_tools//tools/cpp:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
+ doc = dedent("""\
+ Builds a Rust static library.
+
+ This static library will contain all transitively reachable crates and native objects.
+ It is meant to be used when producing an artifact that is then consumed by some other build system
+ (for example to produce an archive that Python program links against).
+
+ This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`.
+
+ When building the whole binary in Bazel, use `rust_library` instead.
+ """),
+)
+
+rust_shared_library = rule(
+ implementation = _rust_shared_library_impl,
+ provides = _common_providers,
+ attrs = dict(_common_attrs.items()),
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ toolchains = [
+ str(Label("//rust:toolchain")),
+ "@bazel_tools//tools/cpp:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
+ doc = dedent("""\
+ Builds a Rust shared library.
+
+ This shared library will contain all transitively reachable crates and native objects.
+ It is meant to be used when producing an artifact that is then consumed by some other build system
+ (for example to produce a shared library that Python program links against).
+
+ This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`.
+
+ When building the whole binary in Bazel, use `rust_library` instead.
+ """),
+)
+
+rust_proc_macro = rule(
+ implementation = _rust_proc_macro_impl,
+ provides = _common_providers,
+ attrs = dict(_common_attrs.items()),
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ toolchains = [
+ str(Label("//rust:toolchain")),
+ "@bazel_tools//tools/cpp:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
+ doc = dedent("""\
+ Builds a Rust proc-macro crate.
+ """),
+)
+
+_rust_binary_attrs = {
+ "crate_type": attr.string(
+ doc = dedent("""\
+ Crate type that will be passed to `rustc` to be used for building this crate.
+
+ This option is a temporary workaround and should be used only when building
+ for WebAssembly targets (//rust/platform:wasi and //rust/platform:wasm).
+ """),
+ default = "bin",
+ ),
+ "linker_script": attr.label(
+ doc = dedent("""\
+ Link script to forward into linker via rustc options.
+ """),
+ cfg = "exec",
+ allow_single_file = True,
+ ),
+ "out_binary": attr.bool(
+ doc = (
+ "Force a target, regardless of it's `crate_type`, to always mark the " +
+ "file as executable. This attribute is only used to support wasm targets but is " +
+ "expected to be removed following a resolution to https://github.com/bazelbuild/rules_rust/issues/771."
+ ),
+ default = False,
+ ),
+ "_grep_includes": attr.label(
+ allow_single_file = True,
+ cfg = "exec",
+ default = Label("@bazel_tools//tools/cpp:grep-includes"),
+ executable = True,
+ ),
+}
+
+rust_binary = rule(
+ implementation = _rust_binary_impl,
+ provides = _common_providers,
+ attrs = dict(_common_attrs.items() + _rust_binary_attrs.items()),
+ executable = True,
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ toolchains = [
+ str(Label("//rust:toolchain")),
+ "@bazel_tools//tools/cpp:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
+ doc = dedent("""\
+ Builds a Rust binary crate.
+
+ 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:
+
+ ```output
+ [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("@rules_rust//rust:defs.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("@rules_rust//rust:defs.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
+ ```
+
+ On Windows, a PDB file containing debugging information is available under
+ the key `pdb_file` in `OutputGroupInfo`. Similarly on macOS, a dSYM folder
+ is available under the key `dsym_folder` in `OutputGroupInfo`.
+"""),
+)
+
+rust_test = rule(
+ implementation = _rust_test_impl,
+ provides = _common_providers,
+ attrs = dict(_common_attrs.items() +
+ _rust_test_attrs.items()),
+ executable = True,
+ fragments = ["cpp"],
+ host_fragments = ["cpp"],
+ test = True,
+ toolchains = [
+ str(Label("//rust:toolchain")),
+ "@bazel_tools//tools/cpp:toolchain_type",
+ ],
+ incompatible_use_toolchain_transition = True,
+ doc = dedent("""\
+ Builds a Rust test crate.
+
+ Examples:
+
+ Suppose you have the following directory structure for a Rust library crate \
+ with unit test code in the library sources:
+
+ ```output
+ [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) -> String {
+ format!("{} {}", &self.greeting, thing)
+ }
+ }
+
+ #[cfg(test)]
+ mod test {
+ use super::Greeter;
+
+ #[test]
+ fn test_greeting() {
+ let hello = Greeter::new("Hi");
+ assert_eq!("Hi Rust", hello.greet("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
+ load("@rules_rust//rust:defs.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`.
+
+ To run a crate or lib with the `#[cfg(test)]` configuration, handling inline \
+ tests, you should specify the crate directly like so.
+
+ ```python
+ rust_test(
+ name = "hello_lib_test",
+ crate = ":hello_lib",
+ # You may add other deps that are specific to the test configuration
+ deps = ["//some/dev/dep"],
+ )
+ ```
+
+ ### 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
+
+ ```output
+ [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
+ load("@rules_rust//rust:defs.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`.
+"""),
+)
+
+def rust_test_suite(name, srcs, **kwargs):
+ """A rule for creating a test suite for a set of `rust_test` targets.
+
+ This rule can be used for setting up typical rust [integration tests][it]. Given the following
+ directory structure:
+
+ ```text
+ [crate]/
+ BUILD.bazel
+ src/
+ lib.rs
+ main.rs
+ tests/
+ integrated_test_a.rs
+ integrated_test_b.rs
+ integrated_test_c.rs
+ patterns/
+ fibonacci_test.rs
+ ```
+
+ The rule can be used to generate [rust_test](#rust_test) targets for each source file under `tests`
+ and a [test_suite][ts] which encapsulates all tests.
+
+ ```python
+ load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test_suite")
+
+ rust_library(
+ name = "math_lib",
+ srcs = ["src/lib.rs"],
+ )
+
+ rust_binary(
+ name = "math_bin",
+ srcs = ["src/main.rs"],
+ )
+
+ rust_test_suite(
+ name = "integrated_tests_suite",
+ srcs = glob(["tests/**"]),
+ deps = [":math_lib"],
+ )
+ ```
+
+ [it]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html
+ [ts]: https://docs.bazel.build/versions/master/be/general.html#test_suite
+
+ Args:
+ name (str): The name of the `test_suite`.
+ srcs (list): All test sources, typically `glob(["tests/**/*.rs"])`.
+ **kwargs (dict): Additional keyword arguments for the underyling [rust_test](#rust_test) targets. The
+ `tags` argument is also passed to the generated `test_suite` target.
+ """
+ tests = []
+
+ for src in srcs:
+ if not src.endswith(".rs"):
+ fail("srcs should have `.rs` extensions")
+
+ # Prefixed with `name` to allow parameterization with macros
+ # The test name should not end with `.rs`
+ test_name = name + "_" + src[:-3]
+ rust_test(
+ name = test_name,
+ crate_name = test_name.replace("/", "_"),
+ srcs = [src],
+ **kwargs
+ )
+ tests.append(test_name)
+
+ native.test_suite(
+ name = name,
+ tests = tests,
+ tags = kwargs.get("tags", None),
+ )