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/BUILD.bazel b/rust/private/BUILD.bazel
new file mode 100644
index 0000000..929a39e
--- /dev/null
+++ b/rust/private/BUILD.bazel
@@ -0,0 +1,24 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("//rust/private:rust_analyzer.bzl", "rust_analyzer_detect_sysroot")
+load("//rust/private:stamp.bzl", "stamp_build_setting")
+
+bzl_library(
+    name = "bzl_lib",
+    srcs = glob(["**/*.bzl"]),
+    visibility = ["//rust:__subpackages__"],
+    deps = ["//rust/platform:bzl_lib"],
+)
+
+alias(
+    name = "rules",
+    actual = ":bzl_lib",
+    deprecation = "Please use the `@rules_rust//private:bzl_lib` target instead",
+    visibility = ["//rust:__subpackages__"],
+)
+
+stamp_build_setting(name = "stamp")
+
+rust_analyzer_detect_sysroot(
+    name = "rust_analyzer_detect_sysroot",
+    visibility = ["//visibility:public"],
+)
diff --git a/rust/private/README.md b/rust/private/README.md
new file mode 100644
index 0000000..a7a45c6
--- /dev/null
+++ b/rust/private/README.md
@@ -0,0 +1 @@
+Implementation details of the rules that are not publicly exposed.
\ No newline at end of file
diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl
new file mode 100644
index 0000000..d1464e9
--- /dev/null
+++ b/rust/private/clippy.bzl
@@ -0,0 +1,304 @@
+# Copyright 2020 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.
+
+"""A module defining clippy rules"""
+
+load("//rust/private:common.bzl", "rust_common")
+load("//rust/private:providers.bzl", "CaptureClippyOutputInfo", "ClippyInfo")
+load(
+    "//rust/private:rustc.bzl",
+    "collect_deps",
+    "collect_inputs",
+    "construct_arguments",
+)
+load(
+    "//rust/private:utils.bzl",
+    "determine_output_hash",
+    "find_cc_toolchain",
+    "find_toolchain",
+)
+
+def _get_clippy_ready_crate_info(target, aspect_ctx):
+    """Check that a target is suitable for clippy and extract the `CrateInfo` provider from it.
+
+    Args:
+        target (Target): The target the aspect is running on.
+        aspect_ctx (ctx, optional): The aspect's context object.
+
+    Returns:
+        CrateInfo, optional: A `CrateInfo` provider if clippy should be run or `None`.
+    """
+
+    # Ignore external targets
+    if target.label.workspace_root.startswith("external"):
+        return None
+
+    # Targets annotated with `noclippy` will not be formatted
+    if aspect_ctx and "noclippy" in aspect_ctx.rule.attr.tags:
+        return None
+
+    # Obviously ignore any targets that don't contain `CrateInfo`
+    if rust_common.crate_info not in target:
+        return None
+
+    return target[rust_common.crate_info]
+
+def _clippy_aspect_impl(target, ctx):
+    crate_info = _get_clippy_ready_crate_info(target, ctx)
+    if not crate_info:
+        return [ClippyInfo(output = depset([]))]
+
+    toolchain = find_toolchain(ctx)
+    cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
+
+    dep_info, build_info, linkstamps = collect_deps(
+        deps = crate_info.deps,
+        proc_macro_deps = crate_info.proc_macro_deps,
+        aliases = crate_info.aliases,
+        # Clippy doesn't need to invoke transitive linking, therefore doesn't need linkstamps.
+        are_linkstamps_supported = False,
+    )
+
+    compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs(
+        ctx,
+        ctx.rule.file,
+        ctx.rule.files,
+        linkstamps,
+        toolchain,
+        cc_toolchain,
+        feature_configuration,
+        crate_info,
+        dep_info,
+        build_info,
+    )
+
+    args, env = construct_arguments(
+        ctx = ctx,
+        attr = ctx.rule.attr,
+        file = ctx.file,
+        toolchain = toolchain,
+        tool_path = toolchain.clippy_driver.path,
+        cc_toolchain = cc_toolchain,
+        feature_configuration = feature_configuration,
+        crate_info = crate_info,
+        dep_info = dep_info,
+        linkstamp_outs = linkstamp_outs,
+        ambiguous_libs = ambiguous_libs,
+        output_hash = determine_output_hash(crate_info.root, ctx.label),
+        rust_flags = [],
+        out_dir = out_dir,
+        build_env_files = build_env_files,
+        build_flags_files = build_flags_files,
+        emit = ["dep-info", "metadata"],
+    )
+
+    if crate_info.is_test:
+        args.rustc_flags.add("--test")
+
+    # For remote execution purposes, the clippy_out file must be a sibling of crate_info.output
+    # or rustc may fail to create intermediate output files because the directory does not exist.
+    if ctx.attr._capture_output[CaptureClippyOutputInfo].capture_output:
+        clippy_out = ctx.actions.declare_file(ctx.label.name + ".clippy.out", sibling = crate_info.output)
+        args.process_wrapper_flags.add("--stderr-file", clippy_out.path)
+
+        # If we are capturing the output, we want the build system to be able to keep going
+        # and consume the output. Some clippy lints are denials, so we treat them as warnings.
+        args.rustc_flags.add("-Wclippy::all")
+    else:
+        # A marker file indicating clippy has executed successfully.
+        # This file is necessary because "ctx.actions.run" mandates an output.
+        clippy_out = ctx.actions.declare_file(ctx.label.name + ".clippy.ok", sibling = crate_info.output)
+        args.process_wrapper_flags.add("--touch-file", clippy_out.path)
+
+        # Turn any warnings from clippy or rustc into an error, as otherwise
+        # Bazel will consider the execution result of the aspect to be "success",
+        # and Clippy won't be re-triggered unless the source file is modified.
+        if "__bindgen" in ctx.rule.attr.tags:
+            # bindgen-generated content is likely to trigger warnings, so
+            # only fail on clippy warnings
+            args.rustc_flags.add("-Dclippy::style")
+            args.rustc_flags.add("-Dclippy::correctness")
+            args.rustc_flags.add("-Dclippy::complexity")
+            args.rustc_flags.add("-Dclippy::perf")
+        else:
+            # fail on any warning
+            args.rustc_flags.add("-Dwarnings")
+
+    # Upstream clippy requires one of these two filenames or it silently uses
+    # the default config. Enforce the naming so users are not confused.
+    valid_config_file_names = [".clippy.toml", "clippy.toml"]
+    if ctx.file._config.basename not in valid_config_file_names:
+        fail("The clippy config file must be named one of: {}".format(valid_config_file_names))
+    env["CLIPPY_CONF_DIR"] = "${{pwd}}/{}".format(ctx.file._config.dirname)
+    compile_inputs = depset([ctx.file._config], transitive = [compile_inputs])
+
+    ctx.actions.run(
+        executable = ctx.executable._process_wrapper,
+        inputs = compile_inputs,
+        outputs = [clippy_out],
+        env = env,
+        tools = [toolchain.clippy_driver],
+        arguments = args.all,
+        mnemonic = "Clippy",
+    )
+
+    return [
+        OutputGroupInfo(clippy_checks = depset([clippy_out])),
+        ClippyInfo(output = depset([clippy_out])),
+    ]
+
+# Example: Run the clippy checker on all targets in the codebase.
+#   bazel build --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect \
+#               --output_groups=clippy_checks \
+#               //...
+rust_clippy_aspect = aspect(
+    fragments = ["cpp"],
+    host_fragments = ["cpp"],
+    attrs = {
+        "_capture_output": attr.label(
+            doc = "Value of the `capture_clippy_output` build setting",
+            default = Label("//:capture_clippy_output"),
+        ),
+        "_cc_toolchain": attr.label(
+            doc = (
+                "Required attribute to access the cc_toolchain. See [Accessing the C++ toolchain]" +
+                "(https://docs.bazel.build/versions/master/integrating-with-rules-cc.html#accessing-the-c-toolchain)"
+            ),
+            default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
+        ),
+        "_config": attr.label(
+            doc = "The `clippy.toml` file used for configuration",
+            allow_single_file = True,
+            default = Label("//:clippy.toml"),
+        ),
+        "_error_format": attr.label(
+            doc = "The desired `--error-format` flags for clippy",
+            default = "//:error_format",
+        ),
+        "_extra_rustc_flags": attr.label(default = "//:extra_rustc_flags"),
+        "_process_wrapper": attr.label(
+            doc = "A process wrapper for running clippy on all platforms",
+            default = Label("//util/process_wrapper"),
+            executable = True,
+            cfg = "exec",
+        ),
+    },
+    provides = [ClippyInfo],
+    toolchains = [
+        str(Label("//rust:toolchain")),
+        "@bazel_tools//tools/cpp:toolchain_type",
+    ],
+    incompatible_use_toolchain_transition = True,
+    implementation = _clippy_aspect_impl,
+    doc = """\
+Executes the clippy checker on specified targets.
+
+This aspect applies to existing rust_library, rust_test, and rust_binary rules.
+
+As an example, if the following is defined in `examples/hello_lib/BUILD.bazel`:
+
+```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"],
+)
+```
+
+Then the targets can be analyzed with clippy using the following command:
+
+```output
+$ bazel build --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect \
+              --output_groups=clippy_checks //hello_lib:all
+```
+""",
+)
+
+def _rust_clippy_rule_impl(ctx):
+    clippy_ready_targets = [dep for dep in ctx.attr.deps if "clippy_checks" in dir(dep[OutputGroupInfo])]
+    files = depset([], transitive = [dep[OutputGroupInfo].clippy_checks for dep in clippy_ready_targets])
+    return [DefaultInfo(files = files)]
+
+rust_clippy = rule(
+    implementation = _rust_clippy_rule_impl,
+    attrs = {
+        "deps": attr.label_list(
+            doc = "Rust targets to run clippy on.",
+            providers = [rust_common.crate_info],
+            aspects = [rust_clippy_aspect],
+        ),
+    },
+    doc = """\
+Executes the clippy checker on a specific target.
+
+Similar to `rust_clippy_aspect`, but allows specifying a list of dependencies \
+within the build system.
+
+For example, given the following example targets:
+
+```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"],
+)
+```
+
+Rust clippy can be set as a build target with the following:
+
+```python
+load("@rules_rust//rust:defs.bzl", "rust_clippy")
+
+rust_clippy(
+    name = "hello_library_clippy",
+    testonly = True,
+    deps = [
+        ":hello_lib",
+        ":greeting_test",
+    ],
+)
+```
+""",
+)
+
+def _capture_clippy_output_impl(ctx):
+    """Implementation of the `capture_clippy_output` rule
+
+    Args:
+        ctx (ctx): The rule's context object
+
+    Returns:
+        list: A list containing the CaptureClippyOutputInfo provider
+    """
+    return [CaptureClippyOutputInfo(capture_output = ctx.build_setting_value)]
+
+capture_clippy_output = rule(
+    doc = "Control whether to print clippy output or store it to a file, using the configured error_format.",
+    implementation = _capture_clippy_output_impl,
+    build_setting = config.bool(flag = True),
+)
diff --git a/rust/private/common.bzl b/rust/private/common.bzl
new file mode 100644
index 0000000..78a3011
--- /dev/null
+++ b/rust/private/common.bzl
@@ -0,0 +1,60 @@
+# Copyright 2021 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.
+
+"""A resilient API layer wrapping compilation and other logic for Rust rules.
+
+This module is meant to be used by custom rules that need to compile Rust code
+and cannot simply rely on writing a macro that wraps `rust_library`. This module
+provides the lower-level interface to Rust providers, actions, and functions.
+Do not load this file directly; instead, load the top-level `defs.bzl` file,
+which exports the `rust_common` struct.
+
+In the Bazel lingo, `rust_common` gives the access to the Rust Sandwich API.
+"""
+
+load(":providers.bzl", "CrateInfo", "DepInfo", "StdLibInfo")
+
+# These constants only represent the default value for attributes and macros
+# defiend in `rules_rust`. Like any attribute public attribute, they can be
+# overwritten by the user on the rules they're defiend on.
+#
+# Note: Code in `.github/workflows/crate_universe.yaml` looks for this line, if
+# you remove it or change its format, you will also need to update that code.
+DEFAULT_RUST_VERSION = "1.59.0"
+DEFAULT_RUST_EDITION = "2018"
+
+def _create_crate_info(**kwargs):
+    """A constructor for a `CrateInfo` provider
+
+    This function should be used in place of directly creating a `CrateInfo`
+    provider to improve API stability.
+
+    Args:
+        **kwargs: An inital set of keyword arguments.
+
+    Returns:
+        CrateInfo: A provider
+    """
+    if not "wrapped_crate_type" in kwargs:
+        kwargs.update({"wrapped_crate_type": None})
+    return CrateInfo(**kwargs)
+
+rust_common = struct(
+    create_crate_info = _create_crate_info,
+    crate_info = CrateInfo,
+    dep_info = DepInfo,
+    stdlib_info = StdLibInfo,
+    default_edition = DEFAULT_RUST_EDITION,
+    default_version = DEFAULT_RUST_VERSION,
+)
diff --git a/rust/private/dummy_cc_toolchain/BUILD.bazel b/rust/private/dummy_cc_toolchain/BUILD.bazel
new file mode 100644
index 0000000..004d233
--- /dev/null
+++ b/rust/private/dummy_cc_toolchain/BUILD.bazel
@@ -0,0 +1,13 @@
+load(":dummy_cc_toolchain.bzl", "dummy_cc_toolchain")
+
+dummy_cc_toolchain(name = "dummy_cc_wasm32")
+
+# When compiling Rust code for wasm32, we avoid linking to cpp code so we introduce a dummy cc
+# toolchain since we know we'll never look it up.
+# TODO(jedmonds@spotify.com): Need to support linking C code to rust code when compiling for wasm32.
+toolchain(
+    name = "dummy_cc_wasm32_toolchain",
+    target_compatible_with = ["//rust/platform/cpu:wasm32"],
+    toolchain = ":dummy_cc_wasm32",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
diff --git a/rust/private/dummy_cc_toolchain/dummy_cc_toolchain.bzl b/rust/private/dummy_cc_toolchain/dummy_cc_toolchain.bzl
new file mode 100644
index 0000000..8ca8c8f
--- /dev/null
+++ b/rust/private/dummy_cc_toolchain/dummy_cc_toolchain.bzl
@@ -0,0 +1,9 @@
+# buildifier: disable=module-docstring
+def _dummy_cc_toolchain_impl(_ctx):
+    # The `all_files` attribute is referenced by rustc_compile_action().
+    return [platform_common.ToolchainInfo(all_files = depset([]))]
+
+dummy_cc_toolchain = rule(
+    implementation = _dummy_cc_toolchain_impl,
+    attrs = {},
+)
diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl
new file mode 100644
index 0000000..b471ac0
--- /dev/null
+++ b/rust/private/providers.bzl
@@ -0,0 +1,109 @@
+# Copyright 2021 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.
+
+"""Module containing definitions of all Rust providers."""
+
+CrateInfo = provider(
+    doc = "A provider containing general Crate information.",
+    fields = {
+        "aliases": "Dict[Label, String]: Renamed and aliased crates",
+        "compile_data": "depset[File]: Compile data required by this crate.",
+        "deps": "depset[DepVariantInfo]: This crate's (rust or cc) dependencies' providers.",
+        "edition": "str: The edition of this crate.",
+        "is_test": "bool: If the crate is being compiled in a test context",
+        "name": "str: The name of this crate.",
+        "output": "File: The output File that will be produced, depends on crate type.",
+        "owner": "Label: The label of the target that produced this CrateInfo",
+        "proc_macro_deps": "depset[DepVariantInfo]: This crate's rust proc_macro dependencies' providers.",
+        "root": "File: The source File entrypoint to this crate, eg. lib.rs",
+        "rustc_env": "Dict[String, String]: Additional `\"key\": \"value\"` environment variables to set for rustc.",
+        "srcs": "depset[File]: All source Files that are part of the crate.",
+        "type": (
+            "str: The type of this crate " +
+            "(see [rustc --crate-type](https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit))."
+        ),
+        "wrapped_crate_type": (
+            "str, optional: The original crate type for targets generated using a previously defined " +
+            "crate (typically tests using the `rust_test::crate` attribute)"
+        ),
+    },
+)
+
+DepInfo = provider(
+    doc = "A provider containing information about a Crate's dependencies.",
+    fields = {
+        "dep_env": "File: File with environment variables direct dependencies build scripts rely upon.",
+        "direct_crates": "depset[AliasableDepInfo]",
+        "link_search_path_files": "depset[File]: All transitive files containing search paths to pass to the linker",
+        "transitive_build_infos": "depset[BuildInfo]",
+        "transitive_crate_outputs": "depset[File]: All transitive crate outputs.",
+        "transitive_crates": "depset[CrateInfo]",
+        "transitive_noncrates": "depset[LinkerInput]: All transitive dependencies that aren't crates.",
+    },
+)
+
+BuildInfo = provider(
+    doc = "A provider containing `rustc` build settings for a given Crate.",
+    fields = {
+        "dep_env": "File: extra build script environment varibles to be set to direct dependencies.",
+        "flags": "File: file containing additional flags to pass to rustc",
+        "link_flags": "File: file containing flags to pass to the linker",
+        "link_search_paths": "File: file containing search paths to pass to the linker",
+        "out_dir": "File: directory containing the result of a build script",
+        "rustc_env": "File: file containing additional environment variables to set for rustc.",
+    },
+)
+
+DepVariantInfo = provider(
+    doc = "A wrapper provider for a dependency of a crate. The dependency can be a Rust " +
+          "dependency, in which case the `crate_info` and `dep_info` fields will be populated, " +
+          "a Rust build script dependency, in which case `build_info` will be populated, or a " +
+          "C/C++ dependency, in which case `cc_info` will be populated.",
+    fields = {
+        "build_info": "BuildInfo: The BuildInfo of a Rust dependency",
+        "cc_info": "CcInfo: The CcInfo of a C/C++ dependency",
+        "crate_info": "CrateInfo: The CrateInfo of a Rust dependency",
+        "dep_info": "DepInfo: The DepInfo of a Rust dependency",
+    },
+)
+
+StdLibInfo = provider(
+    doc = (
+        "A collection of files either found within the `rust-stdlib` artifact or " +
+        "generated based on existing files."
+    ),
+    fields = {
+        "alloc_files": "List[File]: `.a` files related to the `alloc` module.",
+        "between_alloc_and_core_files": "List[File]: `.a` files related to the `compiler_builtins` module.",
+        "between_core_and_std_files": "List[File]: `.a` files related to all modules except `adler`, `alloc`, `compiler_builtins`, `core`, and `std`.",
+        "core_files": "List[File]: `.a` files related to the `core` and `adler` modules",
+        "dot_a_files": "Depset[File]: Generated `.a` files",
+        "self_contained_files": "List[File]: All `.o` files from the `self-contained` directory.",
+        "srcs": "List[Target]: All targets from the original `srcs` attribute.",
+        "std_files": "Depset[File]: `.a` files associated with the `std` module.",
+        "std_rlibs": "List[File]: All `.rlib` files",
+    },
+)
+
+CaptureClippyOutputInfo = provider(
+    doc = "Value of the `capture_clippy_output` build setting",
+    fields = {"capture_output": "Value of the `capture_clippy_output` build setting"},
+)
+
+ClippyInfo = provider(
+    doc = "Provides information on a clippy run.",
+    fields = {
+        "output": "File with the clippy output.",
+    },
+)
diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl
new file mode 100644
index 0000000..b6c83aa
--- /dev/null
+++ b/rust/private/repository_utils.bzl
@@ -0,0 +1,587 @@
+"""Utility macros for use in rules_rust repository rules"""
+
+load("//rust:known_shas.bzl", "FILE_KEY_TO_SHA")
+load(
+    "//rust/platform:triple_mappings.bzl",
+    "system_to_binary_ext",
+    "system_to_dylib_ext",
+    "system_to_staticlib_ext",
+    "system_to_stdlib_linkflags",
+    "triple_to_constraint_set",
+    "triple_to_system",
+)
+
+DEFAULT_TOOLCHAIN_NAME_PREFIX = "toolchain_for"
+DEFAULT_STATIC_RUST_URL_TEMPLATES = ["https://static.rust-lang.org/dist/{}.tar.gz"]
+
+_build_file_for_compiler_template = """\
+load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
+
+filegroup(
+    name = "rustc",
+    srcs = ["bin/rustc{binary_ext}"],
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "rustc_lib",
+    srcs = glob(
+        [
+            "bin/*{dylib_ext}",
+            "lib/*{dylib_ext}",
+            "lib/rustlib/{target_triple}/codegen-backends/*{dylib_ext}",
+            "lib/rustlib/{target_triple}/bin/rust-lld{binary_ext}",
+            "lib/rustlib/{target_triple}/lib/*{dylib_ext}",
+        ],
+        allow_empty = True,
+    ),
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "rustdoc",
+    srcs = ["bin/rustdoc{binary_ext}"],
+    visibility = ["//visibility:public"],
+)
+"""
+
+def BUILD_for_compiler(target_triple):
+    """Emits a BUILD file the compiler `.tar.gz`.
+
+    Args:
+        target_triple (str): The triple of the target platform
+
+    Returns:
+        str: The contents of a BUILD file
+    """
+    system = triple_to_system(target_triple)
+    return _build_file_for_compiler_template.format(
+        binary_ext = system_to_binary_ext(system),
+        staticlib_ext = system_to_staticlib_ext(system),
+        dylib_ext = system_to_dylib_ext(system),
+        target_triple = target_triple,
+    )
+
+_build_file_for_cargo_template = """\
+load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
+
+filegroup(
+    name = "cargo",
+    srcs = ["bin/cargo{binary_ext}"],
+    visibility = ["//visibility:public"],
+)"""
+
+def BUILD_for_cargo(target_triple):
+    """Emits a BUILD file the cargo `.tar.gz`.
+
+    Args:
+        target_triple (str): The triple of the target platform
+
+    Returns:
+        str: The contents of a BUILD file
+    """
+    system = triple_to_system(target_triple)
+    return _build_file_for_cargo_template.format(
+        binary_ext = system_to_binary_ext(system),
+    )
+
+_build_file_for_rustfmt_template = """\
+load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
+
+filegroup(
+    name = "rustfmt_bin",
+    srcs = ["bin/rustfmt{binary_ext}"],
+    visibility = ["//visibility:public"],
+)
+
+sh_binary(
+    name = "rustfmt",
+    srcs = [":rustfmt_bin"],
+    visibility = ["//visibility:public"],
+)
+"""
+
+def BUILD_for_rustfmt(target_triple):
+    """Emits a BUILD file the rustfmt `.tar.gz`.
+
+    Args:
+        target_triple (str): The triple of the target platform
+
+    Returns:
+        str: The contents of a BUILD file
+    """
+    system = triple_to_system(target_triple)
+    return _build_file_for_rustfmt_template.format(
+        binary_ext = system_to_binary_ext(system),
+    )
+
+_build_file_for_clippy_template = """\
+load("@rules_rust//rust:toolchain.bzl", "rust_toolchain")
+
+filegroup(
+    name = "clippy_driver_bin",
+    srcs = ["bin/clippy-driver{binary_ext}"],
+    visibility = ["//visibility:public"],
+)
+"""
+
+def BUILD_for_clippy(target_triple):
+    """Emits a BUILD file the clippy `.tar.gz`.
+
+    Args:
+        target_triple (str): The triple of the target platform
+
+    Returns:
+        str: The contents of a BUILD file
+    """
+    system = triple_to_system(target_triple)
+    return _build_file_for_clippy_template.format(binary_ext = system_to_binary_ext(system))
+
+_build_file_for_stdlib_template = """\
+load("@rules_rust//rust:toolchain.bzl", "rust_stdlib_filegroup")
+
+rust_stdlib_filegroup(
+    name = "rust_std-{target_triple}",
+    srcs = glob(
+        [
+            "lib/rustlib/{target_triple}/lib/*.rlib",
+            "lib/rustlib/{target_triple}/lib/*{dylib_ext}",
+            "lib/rustlib/{target_triple}/lib/*{staticlib_ext}",
+            "lib/rustlib/{target_triple}/lib/self-contained/**",
+        ],
+        # Some patterns (e.g. `lib/*.a`) don't match anything, see https://github.com/bazelbuild/rules_rust/pull/245
+        allow_empty = True,
+    ),
+    visibility = ["//visibility:public"],
+)
+
+# For legacy support
+alias(
+    name = "rust_lib-{target_triple}",
+    actual = "rust_std-{target_triple}",
+    visibility = ["//visibility:public"],
+)
+"""
+
+def BUILD_for_stdlib(target_triple):
+    """Emits a BUILD file the stdlib `.tar.gz`.
+
+    Args:
+        target_triple (str): The triple of the target platform
+
+    Returns:
+        str: The contents of a BUILD file
+    """
+    system = triple_to_system(target_triple)
+    return _build_file_for_stdlib_template.format(
+        binary_ext = system_to_binary_ext(system),
+        staticlib_ext = system_to_staticlib_ext(system),
+        dylib_ext = system_to_dylib_ext(system),
+        target_triple = target_triple,
+    )
+
+_build_file_for_rust_toolchain_template = """\
+rust_toolchain(
+    name = "{toolchain_name}_impl",
+    rust_doc = "@{workspace_name}//:rustdoc",
+    rust_std = "@{workspace_name}//:rust_std-{target_triple}",
+    rustc = "@{workspace_name}//:rustc",
+    rustfmt = {rustfmt_label},
+    cargo = "@{workspace_name}//:cargo",
+    clippy_driver = "@{workspace_name}//:clippy_driver_bin",
+    rustc_lib = "@{workspace_name}//:rustc_lib",
+    rustc_srcs = {rustc_srcs},
+    binary_ext = "{binary_ext}",
+    staticlib_ext = "{staticlib_ext}",
+    dylib_ext = "{dylib_ext}",
+    stdlib_linkflags = [{stdlib_linkflags}],
+    os = "{system}",
+    default_edition = "{default_edition}",
+    exec_triple = "{exec_triple}",
+    target_triple = "{target_triple}",
+    visibility = ["//visibility:public"],
+)
+"""
+
+def BUILD_for_rust_toolchain(
+        workspace_name,
+        name,
+        exec_triple,
+        target_triple,
+        include_rustc_srcs,
+        default_edition,
+        include_rustfmt,
+        stdlib_linkflags = None):
+    """Emits a toolchain declaration to match an existing compiler and stdlib.
+
+    Args:
+        workspace_name (str): The name of the workspace that this toolchain resides in
+        name (str): The name of the toolchain declaration
+        exec_triple (str): The rust-style target that this compiler runs on
+        target_triple (str): The rust-style target triple of the tool
+        include_rustc_srcs (bool, optional): Whether to download rustc's src code. This is required in order to use rust-analyzer support. Defaults to False.
+        default_edition (str): Default Rust edition.
+        include_rustfmt (bool): Whether rustfmt is present in the toolchain.
+        stdlib_linkflags (list, optional): Overriden flags needed for linking to rust
+                                           stdlib, akin to BAZEL_LINKLIBS. Defaults to
+                                           None.
+
+
+    Returns:
+        str: A rendered template of a `rust_toolchain` declaration
+    """
+    system = triple_to_system(target_triple)
+    if stdlib_linkflags == None:
+        stdlib_linkflags = ", ".join(['"%s"' % x for x in system_to_stdlib_linkflags(system)])
+
+    rustc_srcs = "None"
+    if include_rustc_srcs:
+        rustc_srcs = "\"@{workspace_name}//lib/rustlib/src:rustc_srcs\"".format(workspace_name = workspace_name)
+    rustfmt_label = "None"
+    if include_rustfmt:
+        rustfmt_label = "\"@{workspace_name}//:rustfmt_bin\"".format(workspace_name = workspace_name)
+
+    return _build_file_for_rust_toolchain_template.format(
+        toolchain_name = name,
+        workspace_name = workspace_name,
+        binary_ext = system_to_binary_ext(system),
+        staticlib_ext = system_to_staticlib_ext(system),
+        dylib_ext = system_to_dylib_ext(system),
+        rustc_srcs = rustc_srcs,
+        stdlib_linkflags = stdlib_linkflags,
+        system = system,
+        default_edition = default_edition,
+        exec_triple = exec_triple,
+        target_triple = target_triple,
+        rustfmt_label = rustfmt_label,
+    )
+
+_build_file_for_toolchain_template = """\
+toolchain(
+    name = "{name}",
+    exec_compatible_with = {exec_constraint_sets_serialized},
+    target_compatible_with = {target_constraint_sets_serialized},
+    toolchain = "@{parent_workspace_name}//:{name}_impl",
+    toolchain_type = "@rules_rust//rust:toolchain",
+)
+"""
+
+def BUILD_for_toolchain(name, parent_workspace_name, exec_triple, target_triple):
+    return _build_file_for_toolchain_template.format(
+        name = name,
+        exec_constraint_sets_serialized = serialized_constraint_set_from_triple(exec_triple),
+        target_constraint_sets_serialized = serialized_constraint_set_from_triple(target_triple),
+        parent_workspace_name = parent_workspace_name,
+    )
+
+def load_rustfmt(ctx):
+    """Loads a rustfmt binary and yields corresponding BUILD for it
+
+    Args:
+        ctx (repository_ctx): The repository rule's context object
+
+    Returns:
+        str: The BUILD file contents for this rustfmt binary
+    """
+    target_triple = ctx.attr.exec_triple
+
+    load_arbitrary_tool(
+        ctx,
+        iso_date = ctx.attr.iso_date,
+        target_triple = target_triple,
+        tool_name = "rustfmt",
+        tool_subdirectories = ["rustfmt-preview"],
+        version = ctx.attr.rustfmt_version,
+    )
+
+    return BUILD_for_rustfmt(target_triple)
+
+def load_rust_compiler(ctx):
+    """Loads a rust compiler and yields corresponding BUILD for it
+
+    Args:
+        ctx (repository_ctx): A repository_ctx.
+
+    Returns:
+        str: The BUILD file contents for this compiler and compiler library
+    """
+
+    target_triple = ctx.attr.exec_triple
+    load_arbitrary_tool(
+        ctx,
+        iso_date = ctx.attr.iso_date,
+        target_triple = target_triple,
+        tool_name = "rust",
+        tool_subdirectories = ["rustc", "clippy-preview", "cargo"],
+        version = ctx.attr.version,
+    )
+
+    compiler_build_file = BUILD_for_compiler(target_triple) + BUILD_for_clippy(target_triple) + BUILD_for_cargo(target_triple)
+
+    return compiler_build_file
+
+def should_include_rustc_srcs(repository_ctx):
+    """Determing whether or not to include rustc sources in the toolchain.
+
+    Args:
+        repository_ctx (repository_ctx): The repository rule's context object
+
+    Returns:
+        bool: Whether or not to include rustc source files in a `rustc_toolchain`
+    """
+
+    # The environment variable will always take precedence over the attribute.
+    include_rustc_srcs_env = repository_ctx.os.environ.get("RULES_RUST_TOOLCHAIN_INCLUDE_RUSTC_SRCS")
+    if include_rustc_srcs_env != None:
+        return include_rustc_srcs_env.lower() in ["true", "1"]
+
+    return getattr(repository_ctx.attr, "include_rustc_srcs", False)
+
+def load_rust_src(ctx):
+    """Loads the rust source code. Used by the rust-analyzer rust-project.json generator.
+
+    Args:
+        ctx (ctx): A repository_ctx.
+    """
+    tool_suburl = produce_tool_suburl("rust-src", None, ctx.attr.version, ctx.attr.iso_date)
+    static_rust = ctx.os.environ.get("STATIC_RUST_URL", "https://static.rust-lang.org")
+    url = "{}/dist/{}.tar.gz".format(static_rust, tool_suburl)
+
+    tool_path = produce_tool_path("rust-src", None, ctx.attr.version)
+    archive_path = tool_path + ".tar.gz"
+    ctx.download(
+        url,
+        output = archive_path,
+        sha256 = ctx.attr.sha256s.get(tool_suburl) or FILE_KEY_TO_SHA.get(tool_suburl) or "",
+        auth = _make_auth_dict(ctx, [url]),
+    )
+    ctx.extract(
+        archive_path,
+        output = "lib/rustlib/src",
+        stripPrefix = "{}/rust-src/lib/rustlib/src/rust".format(tool_path),
+    )
+    ctx.file(
+        "lib/rustlib/src/BUILD.bazel",
+        """\
+filegroup(
+    name = "rustc_srcs",
+    srcs = glob(["**/*"]),
+    visibility = ["//visibility:public"],
+)""",
+    )
+
+def load_rust_stdlib(ctx, target_triple):
+    """Loads a rust standard library and yields corresponding BUILD for it
+
+    Args:
+        ctx (repository_ctx): A repository_ctx.
+        target_triple (str): The rust-style target triple of the tool
+
+    Returns:
+        str: The BUILD file contents for this stdlib, and a toolchain decl to match
+    """
+
+    load_arbitrary_tool(
+        ctx,
+        iso_date = ctx.attr.iso_date,
+        target_triple = target_triple,
+        tool_name = "rust-std",
+        tool_subdirectories = ["rust-std-{}".format(target_triple)],
+        version = ctx.attr.version,
+    )
+
+    toolchain_prefix = ctx.attr.toolchain_name_prefix or DEFAULT_TOOLCHAIN_NAME_PREFIX
+    stdlib_build_file = BUILD_for_stdlib(target_triple)
+
+    stdlib_linkflags = None
+    if "BAZEL_RUST_STDLIB_LINKFLAGS" in ctx.os.environ:
+        stdlib_linkflags = ctx.os.environ["BAZEL_RUST_STDLIB_LINKFLAGS"].split(":")
+
+    toolchain_build_file = BUILD_for_rust_toolchain(
+        name = "{toolchain_prefix}_{target_triple}".format(
+            toolchain_prefix = toolchain_prefix,
+            target_triple = target_triple,
+        ),
+        exec_triple = ctx.attr.exec_triple,
+        include_rustc_srcs = should_include_rustc_srcs(ctx),
+        target_triple = target_triple,
+        stdlib_linkflags = stdlib_linkflags,
+        workspace_name = ctx.attr.name,
+        default_edition = ctx.attr.edition,
+        include_rustfmt = not (not ctx.attr.rustfmt_version),
+    )
+
+    return stdlib_build_file + toolchain_build_file
+
+def load_rustc_dev_nightly(ctx, target_triple):
+    """Loads the nightly rustc dev component
+
+    Args:
+        ctx: A repository_ctx.
+        target_triple: The rust-style target triple of the tool
+    """
+
+    subdir_name = "rustc-dev"
+    if ctx.attr.iso_date < "2020-12-24":
+        subdir_name = "rustc-dev-{}".format(target_triple)
+
+    load_arbitrary_tool(
+        ctx,
+        iso_date = ctx.attr.iso_date,
+        target_triple = target_triple,
+        tool_name = "rustc-dev",
+        tool_subdirectories = [subdir_name],
+        version = ctx.attr.version,
+    )
+
+def load_llvm_tools(ctx, target_triple):
+    """Loads the llvm tools
+
+    Args:
+        ctx: A repository_ctx.
+        target_triple: The rust-style target triple of the tool
+    """
+    load_arbitrary_tool(
+        ctx,
+        iso_date = ctx.attr.iso_date,
+        target_triple = target_triple,
+        tool_name = "llvm-tools",
+        tool_subdirectories = ["llvm-tools-preview"],
+        version = ctx.attr.version,
+    )
+
+def check_version_valid(version, iso_date, param_prefix = ""):
+    """Verifies that the provided rust version and iso_date make sense.
+
+    Args:
+        version (str): The rustc version
+        iso_date (str): The rustc nightly version's iso date
+        param_prefix (str, optional): The name of the tool who's version is being checked.
+    """
+
+    if not version and iso_date:
+        fail("{param_prefix}iso_date must be paired with a {param_prefix}version".format(param_prefix = param_prefix))
+
+    if version in ("beta", "nightly") and not iso_date:
+        fail("{param_prefix}iso_date must be specified if version is 'beta' or 'nightly'".format(param_prefix = param_prefix))
+
+def serialized_constraint_set_from_triple(target_triple):
+    """Returns a string representing a set of constraints
+
+    Args:
+        target_triple (str): The target triple of the constraint set
+
+    Returns:
+        str: Formatted string representing the serialized constraint
+    """
+    constraint_set = triple_to_constraint_set(target_triple)
+    constraint_set_strs = []
+    for constraint in constraint_set:
+        constraint_set_strs.append("\"{}\"".format(constraint))
+    return "[{}]".format(", ".join(constraint_set_strs))
+
+def produce_tool_suburl(tool_name, target_triple, version, iso_date = None):
+    """Produces a fully qualified Rust tool name for URL
+
+    Args:
+        tool_name: The name of the tool per static.rust-lang.org
+        target_triple: The rust-style target triple of the tool
+        version: The version of the tool among "nightly", "beta', or an exact version.
+        iso_date: The date of the tool (or None, if the version is a specific version).
+
+    Returns:
+        str: The fully qualified url path for the specified tool.
+    """
+    path = produce_tool_path(tool_name, target_triple, version)
+    return iso_date + "/" + path if (iso_date and version in ("beta", "nightly")) else path
+
+def produce_tool_path(tool_name, target_triple, version):
+    """Produces a qualified Rust tool name
+
+    Args:
+        tool_name: The name of the tool per static.rust-lang.org
+        target_triple: The rust-style target triple of the tool
+        version: The version of the tool among "nightly", "beta', or an exact version.
+
+    Returns:
+        str: The qualified path for the specified tool.
+    """
+    if not tool_name:
+        fail("No tool name was provided")
+    if not version:
+        fail("No tool version was provided")
+    return "-".join([e for e in [tool_name, version, target_triple] if e])
+
+def load_arbitrary_tool(ctx, tool_name, tool_subdirectories, version, iso_date, target_triple, sha256 = ""):
+    """Loads a Rust tool, downloads, and extracts into the common workspace.
+
+    This function sources the tool from the Rust-lang static file server. The index is available at:
+    - https://static.rust-lang.org/dist/channel-rust-stable.toml
+    - https://static.rust-lang.org/dist/channel-rust-beta.toml
+    - https://static.rust-lang.org/dist/channel-rust-nightly.toml
+
+    The environment variable `STATIC_RUST_URL` can be used to replace the schema and hostname of
+    the URLs used for fetching assets. `https://static.rust-lang.org/dist/channel-rust-stable.toml`
+    becomes `${STATIC_RUST_URL}/dist/channel-rust-stable.toml`
+
+    Args:
+        ctx (repository_ctx): A repository_ctx (no attrs required).
+        tool_name (str): The name of the given tool per the archive naming.
+        tool_subdirectories (str): The subdirectories of the tool files (at a level below the root directory of
+            the archive). The root directory of the archive is expected to match
+            $TOOL_NAME-$VERSION-$TARGET_TRIPLE.
+            Example:
+            tool_name
+            |    version
+            |    |      target_triple
+            v    v      v
+            rust-1.39.0-x86_64-unknown-linux-gnu/clippy-preview
+                                             .../rustc
+                                             .../etc
+            tool_subdirectories = ["clippy-preview", "rustc"]
+        version (str): The version of the tool among "nightly", "beta', or an exact version.
+        iso_date (str): The date of the tool (ignored if the version is a specific version).
+        target_triple (str): The rust-style target triple of the tool
+        sha256 (str, optional): The expected hash of hash of the Rust tool. Defaults to "".
+    """
+    check_version_valid(version, iso_date, param_prefix = tool_name + "_")
+
+    # View the indices mentioned in the docstring to find the tool_suburl for a given
+    # tool.
+    tool_suburl = produce_tool_suburl(tool_name, target_triple, version, iso_date)
+    urls = []
+
+    static_rust_url_from_env = ctx.os.environ.get("STATIC_RUST_URL")
+    if static_rust_url_from_env:
+        urls.append("{}/dist/{}.tar.gz".format(static_rust_url_from_env, tool_suburl))
+
+    for url in getattr(ctx.attr, "urls", DEFAULT_STATIC_RUST_URL_TEMPLATES):
+        new_url = url.format(tool_suburl)
+        if new_url not in urls:
+            urls.append(new_url)
+
+    tool_path = produce_tool_path(tool_name, target_triple, version)
+    archive_path = "{}.tar.gz".format(tool_path)
+    ctx.download(
+        urls,
+        output = archive_path,
+        sha256 = getattr(ctx.attr, "sha256s", dict()).get(tool_suburl) or
+                 FILE_KEY_TO_SHA.get(tool_suburl) or
+                 sha256,
+        auth = _make_auth_dict(ctx, urls),
+    )
+    for subdirectory in tool_subdirectories:
+        ctx.extract(
+            archive_path,
+            output = "",
+            stripPrefix = "{}/{}".format(tool_path, subdirectory),
+        )
+
+def _make_auth_dict(ctx, urls):
+    auth = getattr(ctx.attr, "auth", {})
+    if not auth:
+        return {}
+    ret = {}
+    for url in urls:
+        ret[url] = auth
+    return ret
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),
+    )
diff --git a/rust/private/rust_analyzer.bzl b/rust/private/rust_analyzer.bzl
new file mode 100644
index 0000000..e28ca30
--- /dev/null
+++ b/rust/private/rust_analyzer.bzl
@@ -0,0 +1,240 @@
+# Copyright 2020 Google
+#
+# 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.
+
+"""
+Rust Analyzer Bazel rules.
+
+rust_analyzer will generate a rust-project.json file for the
+given targets. This file can be consumed by rust-analyzer as an alternative
+to Cargo.toml files.
+"""
+
+load("//rust/platform:triple_mappings.bzl", "system_to_dylib_ext", "triple_to_system")
+load("//rust/private:common.bzl", "rust_common")
+load("//rust/private:rustc.bzl", "BuildInfo")
+load("//rust/private:utils.bzl", "dedent", "find_toolchain")
+
+RustAnalyzerInfo = provider(
+    doc = "RustAnalyzerInfo holds rust crate metadata for targets",
+    fields = {
+        "build_info": "BuildInfo: build info for this crate if present",
+        "cfgs": "List[String]: features or other compilation --cfg settings",
+        "crate": "rust_common.crate_info",
+        "crate_specs": "Depset[File]: transitive closure of OutputGroupInfo files",
+        "deps": "List[RustAnalyzerInfo]: direct dependencies",
+        "env": "Dict{String: String}: Environment variables, used for the `env!` macro",
+        "proc_macro_dylib_path": "File: compiled shared library output of proc-macro rule",
+    },
+)
+
+def _rust_analyzer_aspect_impl(target, ctx):
+    if rust_common.crate_info not in target:
+        return []
+
+    toolchain = find_toolchain(ctx)
+
+    # Always add `test` & `debug_assertions`. See rust-analyzer source code:
+    # https://github.com/rust-analyzer/rust-analyzer/blob/2021-11-15/crates/project_model/src/workspace.rs#L529-L531
+    cfgs = ["test", "debug_assertions"]
+    if hasattr(ctx.rule.attr, "crate_features"):
+        cfgs += ['feature="{}"'.format(f) for f in ctx.rule.attr.crate_features]
+    if hasattr(ctx.rule.attr, "rustc_flags"):
+        cfgs += [f[6:] for f in ctx.rule.attr.rustc_flags if f.startswith("--cfg ") or f.startswith("--cfg=")]
+
+    # Save BuildInfo if we find any (for build script output)
+    build_info = None
+    for dep in ctx.rule.attr.deps:
+        if BuildInfo in dep:
+            build_info = dep[BuildInfo]
+
+    dep_infos = [dep[RustAnalyzerInfo] for dep in ctx.rule.attr.deps if RustAnalyzerInfo in dep]
+    if hasattr(ctx.rule.attr, "proc_macro_deps"):
+        dep_infos += [dep[RustAnalyzerInfo] for dep in ctx.rule.attr.proc_macro_deps if RustAnalyzerInfo in dep]
+    if hasattr(ctx.rule.attr, "crate") and ctx.rule.attr.crate != None:
+        dep_infos.append(ctx.rule.attr.crate[RustAnalyzerInfo])
+
+    crate_spec = ctx.actions.declare_file(ctx.label.name + ".rust_analyzer_crate_spec")
+
+    crate_info = target[rust_common.crate_info]
+
+    rust_analyzer_info = RustAnalyzerInfo(
+        crate = crate_info,
+        cfgs = cfgs,
+        env = getattr(ctx.rule.attr, "rustc_env", {}),
+        deps = dep_infos,
+        crate_specs = depset(direct = [crate_spec], transitive = [dep.crate_specs for dep in dep_infos]),
+        proc_macro_dylib_path = find_proc_macro_dylib_path(toolchain, target),
+        build_info = build_info,
+    )
+
+    ctx.actions.write(
+        output = crate_spec,
+        content = json.encode(_create_single_crate(ctx, rust_analyzer_info)),
+    )
+
+    return [
+        rust_analyzer_info,
+        OutputGroupInfo(rust_analyzer_crate_spec = rust_analyzer_info.crate_specs),
+    ]
+
+def find_proc_macro_dylib_path(toolchain, target):
+    """Find the proc_macro_dylib_path of target. Returns None if target crate is not type proc-macro.
+
+    Args:
+        toolchain: The current rust toolchain.
+        target: The current target.
+    Returns:
+        (path): The path to the proc macro dylib, or None if this crate is not a proc-macro.
+    """
+    if target[rust_common.crate_info].type != "proc-macro":
+        return None
+
+    dylib_ext = system_to_dylib_ext(triple_to_system(toolchain.target_triple))
+    for action in target.actions:
+        for output in action.outputs.to_list():
+            if output.extension == dylib_ext[1:]:
+                return output.path
+
+    # Failed to find the dylib path inside a proc-macro crate.
+    # TODO: Should this be an error?
+    return None
+
+rust_analyzer_aspect = aspect(
+    attr_aspects = ["deps", "proc_macro_deps", "crate"],
+    implementation = _rust_analyzer_aspect_impl,
+    toolchains = [str(Label("//rust:toolchain"))],
+    incompatible_use_toolchain_transition = True,
+    doc = "Annotates rust rules with RustAnalyzerInfo later used to build a rust-project.json",
+)
+
+_exec_root_tmpl = "__EXEC_ROOT__/"
+
+def _crate_id(crate_info):
+    """Returns a unique stable identifier for a crate
+
+    Returns:
+        (string): This crate's unique stable id.
+    """
+    return "ID-" + crate_info.root.path
+
+def _create_single_crate(ctx, info):
+    """Creates a crate in the rust-project.json format.
+
+    Args:
+        ctx (ctx): The rule context
+        info (RustAnalyzerInfo): RustAnalyzerInfo for the current crate
+
+    Returns:
+        (dict) The crate rust-project.json representation
+    """
+    crate_name = info.crate.name
+    crate = dict()
+    crate_id = _crate_id(info.crate)
+    crate["crate_id"] = crate_id
+    crate["display_name"] = crate_name
+    crate["edition"] = info.crate.edition
+    crate["env"] = {}
+    crate["crate_type"] = info.crate.type
+
+    # Switch on external/ to determine if crates are in the workspace or remote.
+    # TODO: Some folks may want to override this for vendored dependencies.
+    root_path = info.crate.root.path
+    root_dirname = info.crate.root.dirname
+    if root_path.startswith("external/"):
+        crate["is_workspace_member"] = False
+        crate["root_module"] = _exec_root_tmpl + root_path
+        crate_root = _exec_root_tmpl + root_dirname
+    else:
+        crate["is_workspace_member"] = True
+        crate["root_module"] = root_path
+        crate_root = root_dirname
+
+    if info.build_info != None:
+        out_dir_path = info.build_info.out_dir.path
+        crate["env"].update({"OUT_DIR": _exec_root_tmpl + out_dir_path})
+        crate["source"] = {
+            # We have to tell rust-analyzer about our out_dir since it's not under the crate root.
+            "exclude_dirs": [],
+            "include_dirs": [crate_root, _exec_root_tmpl + out_dir_path],
+        }
+
+    # TODO: The only imagined use case is an env var holding a filename in the workspace passed to a
+    # macro like include_bytes!. Other use cases might exist that require more complex logic.
+    expand_targets = getattr(ctx.rule.attr, "data", []) + getattr(ctx.rule.attr, "compile_data", [])
+    crate["env"].update({k: ctx.expand_location(v, expand_targets) for k, v in info.env.items()})
+
+    # Omit when a crate appears to depend on itself (e.g. foo_test crates).
+    # It can happen a single source file is present in multiple crates - there can
+    # be a `rust_library` with a `lib.rs` file, and a `rust_test` for the `test`
+    # module in that file. Tests can declare more dependencies than what library
+    # had. Therefore we had to collect all RustAnalyzerInfos for a given crate
+    # and take deps from all of them.
+
+    # There's one exception - if the dependency is the same crate name as the
+    # the crate being processed, we don't add it as a dependency to itself. This is
+    # common and expected - `rust_test.crate` pointing to the `rust_library`.
+    crate["deps"] = [_crate_id(dep.crate) for dep in info.deps if _crate_id(dep.crate) != crate_id]
+    crate["cfg"] = info.cfgs
+    crate["target"] = find_toolchain(ctx).target_triple
+    if info.proc_macro_dylib_path != None:
+        crate["proc_macro_dylib_path"] = _exec_root_tmpl + info.proc_macro_dylib_path
+    return crate
+
+def _rust_analyzer_detect_sysroot_impl(ctx):
+    rust_toolchain = find_toolchain(ctx)
+
+    if not rust_toolchain.rustc_srcs:
+        fail(
+            "Current Rust toolchain doesn't contain rustc sources in `rustc_srcs` attribute.",
+            "These are needed by rust analyzer.",
+            "If you are using the default Rust toolchain, add `rust_repositories(include_rustc_srcs = True, ...).` to your WORKSPACE file.",
+        )
+    sysroot_src = rust_toolchain.rustc_srcs.label.package + "/library"
+    if rust_toolchain.rustc_srcs.label.workspace_root:
+        sysroot_src = _exec_root_tmpl + rust_toolchain.rustc_srcs.label.workspace_root + "/" + sysroot_src
+
+    sysroot_src_file = ctx.actions.declare_file(ctx.label.name + ".rust_analyzer_sysroot_src")
+    ctx.actions.write(
+        output = sysroot_src_file,
+        content = sysroot_src,
+    )
+
+    return [DefaultInfo(files = depset([sysroot_src_file]))]
+
+rust_analyzer_detect_sysroot = rule(
+    implementation = _rust_analyzer_detect_sysroot_impl,
+    toolchains = ["@rules_rust//rust:toolchain"],
+    incompatible_use_toolchain_transition = True,
+    doc = dedent("""\
+        Detect the sysroot and store in a file for use by the gen_rust_project tool.
+    """),
+)
+
+def _rust_analyzer_impl(_ctx):
+    pass
+
+rust_analyzer = rule(
+    attrs = {
+        "targets": attr.label_list(
+            aspects = [rust_analyzer_aspect],
+            doc = "List of all targets to be included in the index",
+        ),
+    },
+    implementation = _rust_analyzer_impl,
+    toolchains = [str(Label("//rust:toolchain"))],
+    incompatible_use_toolchain_transition = True,
+    doc = dedent("""\
+        Deprecated: gen_rust_project can now create a rust-project.json without a rust_analyzer rule.
+    """),
+)
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
new file mode 100644
index 0000000..596c10d
--- /dev/null
+++ b/rust/private/rustc.bzl
@@ -0,0 +1,1436 @@
+# Copyright 2018 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.
+
+"""Functionality for constructing actions that invoke the Rust compiler"""
+
+load(
+    "@bazel_tools//tools/build_defs/cc:action_names.bzl",
+    "CPP_LINK_EXECUTABLE_ACTION_NAME",
+)
+load("//rust/private:common.bzl", "rust_common")
+load("//rust/private:providers.bzl", _BuildInfo = "BuildInfo")
+load("//rust/private:stamp.bzl", "is_stamping_enabled")
+load(
+    "//rust/private:utils.bzl",
+    "abs",
+    "expand_dict_value_locations",
+    "expand_list_element_locations",
+    "find_cc_toolchain",
+    "get_lib_name",
+    "get_preferred_artifact",
+    "is_exec_configuration",
+    "make_static_lib_symlink",
+    "relativize",
+)
+
+BuildInfo = _BuildInfo
+
+AliasableDepInfo = provider(
+    doc = "A provider mapping an alias name to a Crate's information.",
+    fields = {
+        "dep": "CrateInfo",
+        "name": "str",
+    },
+)
+
+_error_format_values = ["human", "json", "short"]
+
+ErrorFormatInfo = provider(
+    doc = "Set the --error-format flag for all rustc invocations",
+    fields = {"error_format": "(string) [" + ", ".join(_error_format_values) + "]"},
+)
+
+ExtraRustcFlagsInfo = provider(
+    doc = "Pass each value as an additional flag to non-exec rustc invocations",
+    fields = {"extra_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"},
+)
+
+ExtraExecRustcFlagsInfo = provider(
+    doc = "Pass each value as an additional flag to exec rustc invocations",
+    fields = {"extra_exec_rustc_flags": "List[string] Extra flags to pass to rustc in exec configuration"},
+)
+
+def _get_rustc_env(attr, toolchain, crate_name):
+    """Gathers rustc environment variables
+
+    Args:
+        attr (struct): The current target's attributes
+        toolchain (rust_toolchain): The current target's rust toolchain context
+        crate_name (str): The name of the crate to be compiled
+
+    Returns:
+        dict: Rustc environment variables
+    """
+    version = attr.version if hasattr(attr, "version") else "0.0.0"
+    major, minor, patch = version.split(".", 2)
+    if "-" in patch:
+        patch, pre = patch.split("-", 1)
+    else:
+        pre = ""
+    return {
+        "CARGO_CFG_TARGET_ARCH": toolchain.target_arch,
+        "CARGO_CFG_TARGET_OS": toolchain.os,
+        "CARGO_CRATE_NAME": crate_name,
+        "CARGO_PKG_AUTHORS": "",
+        "CARGO_PKG_DESCRIPTION": "",
+        "CARGO_PKG_HOMEPAGE": "",
+        "CARGO_PKG_NAME": attr.name,
+        "CARGO_PKG_VERSION": version,
+        "CARGO_PKG_VERSION_MAJOR": major,
+        "CARGO_PKG_VERSION_MINOR": minor,
+        "CARGO_PKG_VERSION_PATCH": patch,
+        "CARGO_PKG_VERSION_PRE": pre,
+    }
+
+def get_compilation_mode_opts(ctx, toolchain):
+    """Gathers rustc flags for the current compilation mode (opt/debug)
+
+    Args:
+        ctx (ctx): The current rule's context object
+        toolchain (rust_toolchain): The current rule's `rust_toolchain`
+
+    Returns:
+        struct: See `_rust_toolchain_impl` for more details
+    """
+    comp_mode = ctx.var["COMPILATION_MODE"]
+    if not comp_mode in toolchain.compilation_mode_opts:
+        fail("Unrecognized compilation mode {} for toolchain.".format(comp_mode))
+
+    return toolchain.compilation_mode_opts[comp_mode]
+
+def _are_linkstamps_supported(feature_configuration, has_grep_includes):
+    # Are linkstamps supported by the C++ toolchain?
+    return (cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "linkstamps") and
+            # Is Bazel recent enough to support Starlark linkstamps?
+            hasattr(cc_common, "register_linkstamp_compile_action") and
+            # The current rule doesn't define _grep_includes attribute; this
+            # attribute is required for compiling linkstamps.
+            has_grep_includes)
+
+def _should_use_pic(cc_toolchain, feature_configuration, crate_type):
+    if crate_type in ("cdylib" or "dylib"):
+        return cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration)
+    return False
+
+def collect_deps(
+        deps,
+        proc_macro_deps,
+        aliases,
+        are_linkstamps_supported = False):
+    """Walks through dependencies and collects the transitive dependencies.
+
+    Args:
+        deps (list): The deps from ctx.attr.deps.
+        proc_macro_deps (list): The proc_macro deps from ctx.attr.proc_macro_deps.
+        aliases (dict): A dict mapping aliased targets to their actual Crate information.
+        are_linkstamps_supported (bool): Whether the current rule and the toolchain support building linkstamps..
+
+    Returns:
+        tuple: Returns a tuple of:
+            DepInfo,
+            BuildInfo,
+            linkstamps (depset[CcLinkstamp]): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries.
+
+    """
+    direct_crates = []
+    transitive_crates = []
+    transitive_noncrates = []
+    transitive_build_infos = []
+    transitive_link_search_paths = []
+    build_info = None
+    linkstamps = []
+    transitive_crate_outputs = []
+
+    aliases = {k.label: v for k, v in aliases.items()}
+    for dep in depset(transitive = [deps, proc_macro_deps]).to_list():
+        (crate_info, dep_info) = _get_crate_and_dep_info(dep)
+        cc_info = _get_cc_info(dep)
+        dep_build_info = _get_build_info(dep)
+
+        if cc_info and are_linkstamps_supported:
+            linkstamps.append(cc_info.linking_context.linkstamps())
+
+        if crate_info:
+            # This dependency is a rust_library
+
+            # When crate_info.owner is set, we use it. When the dep type is Target we get the
+            # label from dep.label
+            owner = getattr(crate_info, "owner", dep.label if type(dep) == "Target" else None)
+
+            direct_crates.append(AliasableDepInfo(
+                name = aliases.get(owner, crate_info.name),
+                dep = crate_info,
+            ))
+
+            transitive_crates.append(depset([crate_info], transitive = [dep_info.transitive_crates]))
+            transitive_crate_outputs.append(
+                depset(
+                    [crate_info.output],
+                    transitive = [dep_info.transitive_crate_outputs],
+                ),
+            )
+            transitive_noncrates.append(dep_info.transitive_noncrates)
+            transitive_build_infos.append(dep_info.transitive_build_infos)
+            transitive_link_search_paths.append(dep_info.link_search_path_files)
+
+        elif cc_info:
+            # This dependency is a cc_library
+            transitive_noncrates.append(cc_info.linking_context.linker_inputs)
+        elif dep_build_info:
+            if build_info:
+                fail("Several deps are providing build information, " +
+                     "only one is allowed in the dependencies")
+            build_info = dep_build_info
+            transitive_build_infos.append(depset([build_info]))
+            transitive_link_search_paths.append(depset([build_info.link_search_paths]))
+        else:
+            fail("rust targets can only depend on rust_library, rust_*_library or cc_library " +
+                 "targets.")
+
+    transitive_crates_depset = depset(transitive = transitive_crates)
+
+    return (
+        rust_common.dep_info(
+            direct_crates = depset(direct_crates),
+            transitive_crates = transitive_crates_depset,
+            transitive_noncrates = depset(
+                transitive = transitive_noncrates,
+                order = "topological",  # dylib link flag ordering matters.
+            ),
+            transitive_crate_outputs = depset(transitive = transitive_crate_outputs),
+            transitive_build_infos = depset(transitive = transitive_build_infos),
+            link_search_path_files = depset(transitive = transitive_link_search_paths),
+            dep_env = build_info.dep_env if build_info else None,
+        ),
+        build_info,
+        depset(transitive = linkstamps),
+    )
+
+def _collect_libs_from_linker_inputs(linker_inputs, use_pic):
+    # TODO: We could let the user choose how to link, instead of always preferring to link static libraries.
+    return [
+        get_preferred_artifact(lib, use_pic)
+        for li in linker_inputs
+        for lib in li.libraries
+    ]
+
+def _get_crate_and_dep_info(dep):
+    if type(dep) == "Target" and rust_common.crate_info in dep:
+        return (dep[rust_common.crate_info], dep[rust_common.dep_info])
+    elif type(dep) == "struct" and hasattr(dep, "crate_info"):
+        return (dep.crate_info, dep.dep_info)
+    return (None, None)
+
+def _get_cc_info(dep):
+    if type(dep) == "Target" and CcInfo in dep:
+        return dep[CcInfo]
+    elif type(dep) == "struct" and hasattr(dep, "cc_info"):
+        return dep.cc_info
+    return None
+
+def _get_build_info(dep):
+    if type(dep) == "Target" and BuildInfo in dep:
+        return dep[BuildInfo]
+    elif type(dep) == "struct" and hasattr(dep, "build_info"):
+        return dep.build_info
+    return None
+
+def get_cc_user_link_flags(ctx):
+    """Get the current target's linkopt flags
+
+    Args:
+        ctx (ctx): The current rule's context object
+
+    Returns:
+        depset: The flags passed to Bazel by --linkopt option.
+    """
+    return ctx.fragments.cpp.linkopts
+
+def get_linker_and_args(ctx, attr, cc_toolchain, feature_configuration, rpaths):
+    """Gathers cc_common linker information
+
+    Args:
+        ctx (ctx): The current target's context object
+        attr (struct): Attributes to use in gathering linker args
+        cc_toolchain (CcToolchain): cc_toolchain for which we are creating build variables.
+        feature_configuration (FeatureConfiguration): Feature configuration to be queried.
+        rpaths (depset): Depset of directories where loader will look for libraries at runtime.
+
+
+    Returns:
+        tuple: A tuple of the following items:
+            - (str): The tool path for given action.
+            - (sequence): A flattened command line flags for given action.
+            - (dict): Environment variables to be set for given action.
+    """
+    user_link_flags = get_cc_user_link_flags(ctx)
+
+    # Add linkopt's from dependencies. This includes linkopts from transitive
+    # dependencies since they get merged up.
+    for dep in getattr(attr, "deps", []):
+        if CcInfo in dep and dep[CcInfo].linking_context:
+            for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list():
+                for flag in linker_input.user_link_flags:
+                    user_link_flags.append(flag)
+    link_variables = cc_common.create_link_variables(
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        is_linking_dynamic_library = False,
+        runtime_library_search_directories = rpaths,
+        user_link_flags = user_link_flags,
+    )
+    link_args = cc_common.get_memory_inefficient_command_line(
+        feature_configuration = feature_configuration,
+        action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
+        variables = link_variables,
+    )
+    link_env = cc_common.get_environment_variables(
+        feature_configuration = feature_configuration,
+        action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
+        variables = link_variables,
+    )
+    ld = cc_common.get_tool_for_action(
+        feature_configuration = feature_configuration,
+        action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
+    )
+
+    return ld, link_args, link_env
+
+def _process_build_scripts(
+        build_info,
+        dep_info,
+        compile_inputs):
+    """Gathers the outputs from a target's `cargo_build_script` action.
+
+    Args:
+        build_info (BuildInfo): The target Build's dependency info.
+        dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs.
+        compile_inputs (depset): A set of all files that will participate in the build.
+
+    Returns:
+        tuple: A tuple: A tuple of the following items:
+            - (depset[File]): A list of all build info `OUT_DIR` File objects
+            - (str): The `OUT_DIR` of the current build info
+            - (File): An optional path to a generated environment file from a `cargo_build_script` target
+            - (depset[File]): All direct and transitive build flags from the current build info.
+    """
+    extra_inputs, out_dir, build_env_file, build_flags_files = _create_extra_input_args(build_info, dep_info)
+    compile_inputs = depset(transitive = [extra_inputs, compile_inputs])
+    return compile_inputs, out_dir, build_env_file, build_flags_files
+
+def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib):
+    """Constructs a disambiguating symlink for a library dependency.
+
+    Args:
+      actions (Actions): The rule's context actions object.
+      toolchain: The Rust toolchain object.
+      crate_info (CrateInfo): The target crate's info.
+      lib (File): The library to symlink to.
+
+    Returns:
+      (File): The disambiguating symlink for the library.
+    """
+    # FIXME: Once the relative order part of the native-link-modifiers rustc
+    # feature is stable, we should be able to eliminate the need to construct
+    # symlinks by passing the full paths to the libraries.
+    # https://github.com/rust-lang/rust/issues/81490.
+
+    # Take the absolute value of hash() since it could be negative.
+    path_hash = abs(hash(lib.path))
+    lib_name = get_lib_name(lib)
+
+    prefix = "lib"
+    extension = ".a"
+    if toolchain.os.startswith("windows"):
+        prefix = ""
+        extension = ".lib"
+
+    # Ensure the symlink follows the lib<name>.a pattern on Unix-like platforms
+    # or <name>.lib on Windows.
+    # Add a hash of the original library path to disambiguate libraries with the same basename.
+    symlink_name = "{}{}-{}{}".format(prefix, lib_name, path_hash, extension)
+
+    # Add the symlink to a target crate-specific _ambiguous_libs/ subfolder,
+    # to avoid possible collisions with sibling crates that may depend on the
+    # same ambiguous libraries.
+    symlink = actions.declare_file("_ambiguous_libs/" + crate_info.output.basename + "/" + symlink_name)
+    actions.symlink(
+        output = symlink,
+        target_file = lib,
+        progress_message = "Creating symlink to ambiguous lib: {}".format(lib.path),
+    )
+    return symlink
+
+def _disambiguate_libs(actions, toolchain, crate_info, dep_info, use_pic):
+    """Constructs disambiguating symlinks for ambiguous library dependencies.
+
+    The symlinks are all created in a _ambiguous_libs/ subfolder specific to
+    the target crate to avoid possible collisions with sibling crates that may
+    depend on the same ambiguous libraries.
+
+    Args:
+      actions (Actions): The rule's context actions object.
+      toolchain: The Rust toolchain object.
+      crate_info (CrateInfo): The target crate's info.
+      dep_info: (DepInfo): The target crate's dependency info.
+      use_pic: (boolean): Whether the build should use PIC.
+
+    Returns:
+      dict[String, File]: A mapping from ambiguous library paths to their
+        disambiguating symlink.
+    """
+    # FIXME: Once the relative order part of the native-link-modifiers rustc
+    # feature is stable, we should be able to eliminate the need to construct
+    # symlinks by passing the full paths to the libraries.
+    # https://github.com/rust-lang/rust/issues/81490.
+
+    # A dictionary from file paths of ambiguous libraries to the corresponding
+    # symlink.
+    ambiguous_libs = {}
+
+    # A dictionary maintaining a mapping from preferred library name to the
+    # last visited artifact with that name.
+    visited_libs = {}
+    for link_input in dep_info.transitive_noncrates.to_list():
+        for lib in link_input.libraries:
+            # FIXME: Dynamic libs are not disambiguated right now, there are
+            # cases where those have a non-standard name with version (e.g.,
+            # //test/unit/versioned_libs). We hope that the link modifiers
+            # stabilization will come before we need to make this work.
+            if _is_dylib(lib):
+                continue
+            artifact = get_preferred_artifact(lib, use_pic)
+            name = get_lib_name(artifact)
+
+            # On Linux-like platforms, normally library base names start with
+            # `lib`, following the pattern `lib[name].(a|lo)` and we pass
+            # -lstatic=name.
+            # On Windows, the base name looks like `name.lib` and we pass
+            # -lstatic=name.
+            # FIXME: Under the native-link-modifiers unstable rustc feature,
+            # we could use -lstatic:+verbatim instead.
+            needs_symlink_to_standardize_name = (
+                (toolchain.os.startswith("linux") or toolchain.os.startswith("mac") or toolchain.os.startswith("darwin")) and
+                artifact.basename.endswith(".a") and not artifact.basename.startswith("lib")
+            ) or (
+                toolchain.os.startswith("windows") and not artifact.basename.endswith(".lib")
+            )
+
+            # Detect cases where we need to disambiguate library dependencies
+            # by constructing symlinks.
+            if (
+                needs_symlink_to_standardize_name or
+                # We have multiple libraries with the same name.
+                (name in visited_libs and visited_libs[name].path != artifact.path)
+            ):
+                # Disambiguate the previously visited library (if we just detected
+                # that it is ambiguous) and the current library.
+                if name in visited_libs:
+                    old_path = visited_libs[name].path
+                    if old_path not in ambiguous_libs:
+                        ambiguous_libs[old_path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, visited_libs[name])
+                ambiguous_libs[artifact.path] = _symlink_for_ambiguous_lib(actions, toolchain, crate_info, artifact)
+
+            visited_libs[name] = artifact
+    return ambiguous_libs
+
+def collect_inputs(
+        ctx,
+        file,
+        files,
+        linkstamps,
+        toolchain,
+        cc_toolchain,
+        feature_configuration,
+        crate_info,
+        dep_info,
+        build_info,
+        stamp = False):
+    """Gather's the inputs and required input information for a rustc action
+
+    Args:
+        ctx (ctx): The rule's context object.
+        file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`.
+        files (list): A list of all inputs (`ctx.files`).
+        linkstamps (depset): A depset of CcLinkstamps that need to be compiled and linked into all linked binaries.
+        toolchain (rust_toolchain): The current `rust_toolchain`.
+        cc_toolchain (CcToolchainInfo): The current `cc_toolchain`.
+        feature_configuration (FeatureConfiguration): Feature configuration to be queried.
+        crate_info (CrateInfo): The Crate information of the crate to process build scripts for.
+        dep_info (DepInfo): The target Crate's dependency information.
+        build_info (BuildInfo): The target Crate's build settings.
+        stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
+            https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
+
+    Returns:
+        tuple: A tuple: A tuple of the following items:
+            - (list): A list of all build info `OUT_DIR` File objects
+            - (str): The `OUT_DIR` of the current build info
+            - (File): An optional path to a generated environment file from a `cargo_build_script` target
+            - (depset[File]): All direct and transitive build flag files from the current build info
+            - (list[File]): Linkstamp outputs
+            - (dict[String, File]): Ambiguous libs, see `_disambiguate_libs`.
+    """
+    linker_script = getattr(file, "linker_script") if hasattr(file, "linker_script") else None
+
+    linker_depset = cc_toolchain.all_files
+
+    use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type)
+
+    # Pass linker inputs only for linking-like actions, not for example where
+    # the output is rlib. This avoids quadratic behavior where transitive noncrates are
+    # flattened on each transitive rust_library dependency.
+    additional_transitive_inputs = []
+    ambiguous_libs = {}
+    if crate_info.type in ("staticlib", "proc-macro"):
+        additional_transitive_inputs = _collect_libs_from_linker_inputs(
+            dep_info.transitive_noncrates.to_list(),
+            use_pic,
+        )
+    elif crate_info.type in ("bin", "dylib", "cdylib"):
+        linker_inputs = dep_info.transitive_noncrates.to_list()
+        ambiguous_libs = _disambiguate_libs(ctx.actions, toolchain, crate_info, dep_info, use_pic)
+        additional_transitive_inputs = _collect_libs_from_linker_inputs(linker_inputs, use_pic) + [
+            additional_input
+            for linker_input in linker_inputs
+            for additional_input in linker_input.additional_inputs
+        ] + ambiguous_libs.values()
+
+    # Compute linkstamps. Use the inputs of the binary as inputs to the
+    # linkstamp action to ensure linkstamps are rebuilt whenever binary inputs
+    # change.
+    linkstamp_outs = []
+
+    nolinkstamp_compile_inputs = depset(
+        getattr(files, "data", []) +
+        ([build_info.rustc_env, build_info.flags] if build_info else []) +
+        ([toolchain.target_json] if toolchain.target_json else []) +
+        ([] if linker_script == None else [linker_script]),
+        transitive = [
+            linker_depset,
+            crate_info.srcs,
+            dep_info.transitive_crate_outputs,
+            depset(additional_transitive_inputs),
+            crate_info.compile_data,
+            toolchain.all_files,
+        ],
+    )
+
+    if crate_info.type in ("bin", "cdylib"):
+        # There is no other way to register an action for each member of a depset than
+        # flattening the depset as of 2021-10-12. Luckily, usually there is only one linkstamp
+        # in a build, and we only flatten the list on binary targets that perform transitive linking,
+        # so it's extremely unlikely that this call to `to_list()` will ever be a performance
+        # problem.
+        for linkstamp in linkstamps.to_list():
+            # The linkstamp output path is based on the binary crate
+            # name and the input linkstamp path. This is to disambiguate
+            # the linkstamp outputs produced by multiple binary crates
+            # that depend on the same linkstamp. We use the same pattern
+            # for the output name as the one used by native cc rules.
+            out_name = "_objs/" + crate_info.output.basename + "/" + linkstamp.file().path[:-len(linkstamp.file().extension)] + "o"
+            linkstamp_out = ctx.actions.declare_file(out_name)
+            linkstamp_outs.append(linkstamp_out)
+            cc_common.register_linkstamp_compile_action(
+                actions = ctx.actions,
+                cc_toolchain = cc_toolchain,
+                feature_configuration = feature_configuration,
+                grep_includes = ctx.file._grep_includes,
+                source_file = linkstamp.file(),
+                output_file = linkstamp_out,
+                compilation_inputs = linkstamp.hdrs(),
+                inputs_for_validation = nolinkstamp_compile_inputs,
+                label_replacement = str(ctx.label),
+                output_replacement = crate_info.output.path,
+            )
+
+    # If stamping is enabled include the volatile status info file
+    stamp_info = [ctx.version_file] if stamp else []
+
+    compile_inputs = depset(
+        linkstamp_outs + stamp_info,
+        transitive = [
+            nolinkstamp_compile_inputs,
+        ],
+    )
+
+    build_env_files = getattr(files, "rustc_env_files", [])
+    compile_inputs, out_dir, build_env_file, build_flags_files = _process_build_scripts(build_info, dep_info, compile_inputs)
+    if build_env_file:
+        build_env_files = [f for f in build_env_files] + [build_env_file]
+    compile_inputs = depset(build_env_files, transitive = [compile_inputs])
+
+    return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs
+
+def construct_arguments(
+        ctx,
+        attr,
+        file,
+        toolchain,
+        tool_path,
+        cc_toolchain,
+        feature_configuration,
+        crate_info,
+        dep_info,
+        linkstamp_outs,
+        ambiguous_libs,
+        output_hash,
+        rust_flags,
+        out_dir,
+        build_env_files,
+        build_flags_files,
+        emit = ["dep-info", "link"],
+        force_all_deps_direct = False,
+        force_link = False,
+        stamp = False,
+        remap_path_prefix = "."):
+    """Builds an Args object containing common rustc flags
+
+    Args:
+        ctx (ctx): The rule's context object
+        attr (struct): The attributes for the target. These may be different from ctx.attr in an aspect context.
+        file (struct): A struct containing files defined in label type attributes marked as `allow_single_file`.
+        toolchain (rust_toolchain): The current target's `rust_toolchain`
+        tool_path (str): Path to rustc
+        cc_toolchain (CcToolchain): The CcToolchain for the current target.
+        feature_configuration (FeatureConfiguration): Class used to construct command lines from CROSSTOOL features.
+        crate_info (CrateInfo): The CrateInfo provider of the target crate
+        dep_info (DepInfo): The DepInfo provider of the target crate
+        linkstamp_outs (list): Linkstamp outputs of native dependencies
+        ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs`
+        output_hash (str): The hashed path of the crate root
+        rust_flags (list): Additional flags to pass to rustc
+        out_dir (str): The path to the output directory for the target Crate.
+        build_env_files (list): Files containing rustc environment variables, for instance from `cargo_build_script` actions.
+        build_flags_files (depset): The output files of a `cargo_build_script` actions containing rustc build flags
+        emit (list): Values for the --emit flag to rustc.
+        force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
+            to the commandline as opposed to -L.
+        force_link (bool, optional): Whether to add link flags to the command regardless of `emit`.
+        stamp (bool, optional): Whether or not workspace status stamping is enabled. For more details see
+            https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
+        remap_path_prefix (str, optional): A value used to remap `${pwd}` to. If set to a falsey value, no prefix will be set.
+
+    Returns:
+        tuple: A tuple of the following items
+            - (struct): A struct of arguments used to run the `Rustc` action
+                - process_wrapper_flags (Args): Arguments for the process wrapper
+                - rustc_path (Args): Arguments for invoking rustc via the process wrapper
+                - rustc_flags (Args): Rust flags for the Rust compiler
+                - all (list): A list of all `Args` objects in the order listed above.
+                    This is to be passed to the `arguments` parameter of actions
+            - (dict): Common rustc environment variables
+    """
+    output_dir = getattr(crate_info.output, "dirname", None)
+    linker_script = getattr(file, "linker_script", None)
+
+    env = _get_rustc_env(attr, toolchain, crate_info.name)
+
+    # Wrapper args first
+    process_wrapper_flags = ctx.actions.args()
+
+    for build_env_file in build_env_files:
+        process_wrapper_flags.add("--env-file", build_env_file)
+
+    process_wrapper_flags.add_all(build_flags_files, before_each = "--arg-file")
+
+    # Certain rust build processes expect to find files from the environment
+    # variable `$CARGO_MANIFEST_DIR`. Examples of this include pest, tera,
+    # asakuma.
+    #
+    # The compiler and by extension proc-macros see the current working
+    # directory as the Bazel exec root. This is what `$CARGO_MANIFEST_DIR`
+    # would default to but is often the wrong value (e.g. if the source is in a
+    # sub-package or if we are building something in an external repository).
+    # Hence, we need to set `CARGO_MANIFEST_DIR` explicitly.
+    #
+    # Since we cannot get the `exec_root` from starlark, we cheat a little and
+    # use `${pwd}` which resolves the `exec_root` at action execution time.
+    process_wrapper_flags.add("--subst", "pwd=${pwd}")
+
+    # If stamping is enabled, enable the functionality in the process wrapper
+    if stamp:
+        process_wrapper_flags.add("--volatile-status-file", ctx.version_file)
+
+    # Both ctx.label.workspace_root and ctx.label.package are relative paths
+    # and either can be empty strings. Avoid trailing/double slashes in the path.
+    components = "${{pwd}}/{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
+    env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])
+
+    if out_dir != None:
+        env["OUT_DIR"] = "${pwd}/" + out_dir
+
+    # Handle that the binary name and crate name may be different.
+    #
+    # If a target name contains a - then cargo (and rules_rust) will generate a
+    # crate name with _ instead.  Accordingly, rustc will generate a output
+    # file (executable, or rlib, or whatever) with _ not -.  But when cargo
+    # puts a binary in the target/${config} directory, and sets environment
+    # variables like `CARGO_BIN_EXE_${binary_name}` it will use the - version
+    # not the _ version.  So we rename the rustc-generated file (with _s) to
+    # have -s if needed.
+    emit_with_paths = emit
+    if crate_info.type == "bin" and crate_info.output != None:
+        generated_file = crate_info.name + toolchain.binary_ext
+        src = "/".join([crate_info.output.dirname, generated_file])
+        dst = crate_info.output.path
+        if src != dst:
+            emit_with_paths = [("link=" + dst if val == "link" else val) for val in emit]
+
+    # Arguments for launching rustc from the process wrapper
+    rustc_path = ctx.actions.args()
+    rustc_path.add("--")
+    rustc_path.add(tool_path)
+
+    # Rustc arguments
+    rustc_flags = ctx.actions.args()
+    rustc_flags.set_param_file_format("multiline")
+    rustc_flags.use_param_file("@%s", use_always = False)
+    rustc_flags.add(crate_info.root)
+    rustc_flags.add("--crate-name=" + crate_info.name)
+    rustc_flags.add("--crate-type=" + crate_info.type)
+    if hasattr(attr, "_error_format"):
+        rustc_flags.add("--error-format=" + attr._error_format[ErrorFormatInfo].error_format)
+
+    # Mangle symbols to disambiguate crates with the same name
+    extra_filename = "-" + output_hash if output_hash else ""
+    rustc_flags.add("--codegen=metadata=" + extra_filename)
+    if output_dir:
+        rustc_flags.add("--out-dir=" + output_dir)
+    rustc_flags.add("--codegen=extra-filename=" + extra_filename)
+
+    compilation_mode = get_compilation_mode_opts(ctx, toolchain)
+    rustc_flags.add("--codegen=opt-level=" + compilation_mode.opt_level)
+    rustc_flags.add("--codegen=debuginfo=" + compilation_mode.debug_info)
+
+    # For determinism to help with build distribution and such
+    if remap_path_prefix:
+        rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix))
+
+    if emit:
+        rustc_flags.add("--emit=" + ",".join(emit_with_paths))
+    rustc_flags.add("--color=always")
+    rustc_flags.add("--target=" + toolchain.target_flag_value)
+    if hasattr(attr, "crate_features"):
+        rustc_flags.add_all(getattr(attr, "crate_features"), before_each = "--cfg", format_each = 'feature="%s"')
+    if linker_script:
+        rustc_flags.add(linker_script.path, format = "--codegen=link-arg=-T%s")
+
+    # Gets the paths to the folders containing the standard library (or libcore)
+    rust_std_paths = toolchain.rust_std_paths.to_list()
+
+    # Tell Rustc where to find the standard library
+    rustc_flags.add_all(rust_std_paths, before_each = "-L", format_each = "%s")
+    rustc_flags.add_all(rust_flags)
+
+    # Deduplicate data paths due to https://github.com/bazelbuild/bazel/issues/14681
+    data_paths = depset(direct = getattr(attr, "data", []) + getattr(attr, "compile_data", [])).to_list()
+
+    rustc_flags.add_all(
+        expand_list_element_locations(
+            ctx,
+            getattr(attr, "rustc_flags", []),
+            data_paths,
+        ),
+    )
+    add_edition_flags(rustc_flags, crate_info)
+
+    # Link!
+    if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or force_link:
+        # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc
+        # linker since it won't understand.
+        if toolchain.target_arch != "wasm32":
+            if output_dir:
+                use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_info.type)
+                rpaths = _compute_rpaths(toolchain, output_dir, dep_info, use_pic)
+            else:
+                rpaths = depset([])
+            ld, link_args, link_env = get_linker_and_args(ctx, attr, cc_toolchain, feature_configuration, rpaths)
+            env.update(link_env)
+            rustc_flags.add("--codegen=linker=" + ld)
+            rustc_flags.add_joined("--codegen", link_args, join_with = " ", format_joined = "link-args=%s")
+
+        _add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, ambiguous_libs, crate_info.type, toolchain, cc_toolchain, feature_configuration)
+
+    # These always need to be added, even if not linking this crate.
+    add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct)
+
+    needs_extern_proc_macro_flag = "proc-macro" in [crate_info.type, crate_info.wrapped_crate_type] and \
+                                   crate_info.edition != "2015"
+    if needs_extern_proc_macro_flag:
+        rustc_flags.add("--extern")
+        rustc_flags.add("proc_macro")
+
+    # Make bin crate data deps available to tests.
+    for data in getattr(attr, "data", []):
+        if rust_common.crate_info in data:
+            dep_crate_info = data[rust_common.crate_info]
+            if dep_crate_info.type == "bin":
+                # Trying to make CARGO_BIN_EXE_{} canonical across platform by strip out extension if exists
+                env_basename = dep_crate_info.output.basename[:-(1 + len(dep_crate_info.output.extension))] if len(dep_crate_info.output.extension) > 0 else dep_crate_info.output.basename
+                env["CARGO_BIN_EXE_" + env_basename] = dep_crate_info.output.short_path
+
+    # Update environment with user provided variables.
+    env.update(expand_dict_value_locations(
+        ctx,
+        crate_info.rustc_env,
+        data_paths,
+    ))
+
+    # Ensure the sysroot is set for the target platform
+    env["SYSROOT"] = toolchain.sysroot
+
+    if toolchain._rename_first_party_crates:
+        env["RULES_RUST_THIRD_PARTY_DIR"] = toolchain._third_party_dir
+
+    # extra_rustc_flags apply to the target configuration, not the exec configuration.
+    if hasattr(ctx.attr, "_extra_rustc_flags") and not is_exec_configuration(ctx):
+        rustc_flags.add_all(ctx.attr._extra_rustc_flags[ExtraRustcFlagsInfo].extra_rustc_flags)
+
+    if hasattr(ctx.attr, "_extra_exec_rustc_flags") and is_exec_configuration(ctx):
+        rustc_flags.add_all(ctx.attr._extra_exec_rustc_flags[ExtraExecRustcFlagsInfo].extra_exec_rustc_flags)
+
+    # Create a struct which keeps the arguments separate so each may be tuned or
+    # replaced where necessary
+    args = struct(
+        process_wrapper_flags = process_wrapper_flags,
+        rustc_path = rustc_path,
+        rustc_flags = rustc_flags,
+        all = [process_wrapper_flags, rustc_path, rustc_flags],
+    )
+
+    return args, env
+
+def rustc_compile_action(
+        ctx,
+        attr,
+        toolchain,
+        crate_info,
+        output_hash = None,
+        rust_flags = [],
+        force_all_deps_direct = False):
+    """Create and run a rustc compile action based on the current rule's attributes
+
+    Args:
+        ctx (ctx): The rule's context object
+        attr (struct): Attributes to use for the rust compile action
+        toolchain (rust_toolchain): The current `rust_toolchain`
+        crate_info (CrateInfo): The CrateInfo provider for the current target.
+        output_hash (str, optional): The hashed path of the crate root. Defaults to None.
+        rust_flags (list, optional): Additional flags to pass to rustc. Defaults to [].
+        force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
+            to the commandline as opposed to -L.
+
+    Returns:
+        list: A list of the following providers:
+            - (CrateInfo): info for the crate we just built; same as `crate_info` parameter.
+            - (DepInfo): The transitive dependencies of this crate.
+            - (DefaultInfo): The output file for this crate, and its runfiles.
+    """
+    cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
+
+    dep_info, build_info, linkstamps = collect_deps(
+        deps = crate_info.deps,
+        proc_macro_deps = crate_info.proc_macro_deps,
+        aliases = crate_info.aliases,
+        are_linkstamps_supported = _are_linkstamps_supported(
+            feature_configuration = feature_configuration,
+            has_grep_includes = hasattr(ctx.attr, "_grep_includes"),
+        ),
+    )
+
+    # Determine if the build is currently running with --stamp
+    stamp = is_stamping_enabled(attr)
+
+    compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs(
+        ctx = ctx,
+        file = ctx.file,
+        files = ctx.files,
+        linkstamps = linkstamps,
+        toolchain = toolchain,
+        cc_toolchain = cc_toolchain,
+        feature_configuration = feature_configuration,
+        crate_info = crate_info,
+        dep_info = dep_info,
+        build_info = build_info,
+        stamp = stamp,
+    )
+
+    args, env_from_args = construct_arguments(
+        ctx = ctx,
+        attr = attr,
+        file = ctx.file,
+        toolchain = toolchain,
+        tool_path = toolchain.rustc.path,
+        cc_toolchain = cc_toolchain,
+        feature_configuration = feature_configuration,
+        crate_info = crate_info,
+        dep_info = dep_info,
+        linkstamp_outs = linkstamp_outs,
+        ambiguous_libs = ambiguous_libs,
+        output_hash = output_hash,
+        rust_flags = rust_flags,
+        out_dir = out_dir,
+        build_env_files = build_env_files,
+        build_flags_files = build_flags_files,
+        force_all_deps_direct = force_all_deps_direct,
+        stamp = stamp,
+    )
+
+    env = dict(ctx.configuration.default_shell_env)
+    env.update(env_from_args)
+
+    if hasattr(attr, "version") and attr.version != "0.0.0":
+        formatted_version = " v{}".format(attr.version)
+    else:
+        formatted_version = ""
+
+    outputs = [crate_info.output]
+
+    # For a cdylib that might be added as a dependency to a cc_* target on Windows, it is important to include the
+    # interface library that rustc generates in the output files.
+    interface_library = None
+    if toolchain.os == "windows" and crate_info.type == "cdylib":
+        # Rustc generates the import library with a `.dll.lib` extension rather than the usual `.lib` one that msvc
+        # expects (see https://github.com/rust-lang/rust/pull/29520 for more context).
+        interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib")
+        outputs.append(interface_library)
+
+    # The action might generate extra output that we don't want to include in the `DefaultInfo` files.
+    action_outputs = list(outputs)
+
+    # Rustc generates a pdb file (on Windows) or a dsym folder (on macos) so provide it in an output group for crate
+    # types that benefit from having debug information in a separate file.
+    pdb_file = None
+    dsym_folder = None
+    if crate_info.type in ("cdylib", "bin") and not crate_info.is_test:
+        if toolchain.os == "windows":
+            pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb")
+            action_outputs.append(pdb_file)
+        elif toolchain.os == "darwin":
+            dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM")
+            action_outputs.append(dsym_folder)
+
+    # This uses startswith as on windows the basename will be process_wrapper_fake.exe.
+    if not ctx.executable._process_wrapper.basename.startswith("process_wrapper_fake"):
+        # Run as normal
+        ctx.actions.run(
+            executable = ctx.executable._process_wrapper,
+            inputs = compile_inputs,
+            outputs = action_outputs,
+            env = env,
+            arguments = args.all,
+            mnemonic = "Rustc",
+            progress_message = "Compiling Rust {} {}{} ({} files)".format(
+                crate_info.type,
+                ctx.label.name,
+                formatted_version,
+                len(crate_info.srcs.to_list()),
+            ),
+        )
+    else:
+        # Run without process_wrapper
+        if build_env_files or build_flags_files or stamp:
+            fail("build_env_files, build_flags_files, stamp are not supported if use_process_wrapper is False")
+        ctx.actions.run(
+            executable = toolchain.rustc,
+            inputs = compile_inputs,
+            outputs = action_outputs,
+            env = env,
+            arguments = [args.rustc_flags],
+            mnemonic = "Rustc",
+            progress_message = "Compiling Rust (without process_wrapper) {} {}{} ({} files)".format(
+                crate_info.type,
+                ctx.label.name,
+                formatted_version,
+                len(crate_info.srcs.to_list()),
+            ),
+        )
+
+    runfiles = ctx.runfiles(
+        files = getattr(ctx.files, "data", []),
+        collect_data = True,
+    )
+
+    # TODO: Remove after some resolution to
+    # https://github.com/bazelbuild/rules_rust/issues/771
+    out_binary = getattr(attr, "out_binary", False)
+
+    providers = [
+        crate_info,
+        dep_info,
+        DefaultInfo(
+            # nb. This field is required for cc_library to depend on our output.
+            files = depset(outputs),
+            runfiles = runfiles,
+            executable = crate_info.output if crate_info.type == "bin" or crate_info.is_test or out_binary else None,
+        ),
+    ]
+    if toolchain.target_arch != "wasm32":
+        providers += establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library)
+    if pdb_file:
+        providers.append(OutputGroupInfo(pdb_file = depset([pdb_file])))
+    if dsym_folder:
+        providers.append(OutputGroupInfo(dsym_folder = depset([dsym_folder])))
+
+    return providers
+
+def _is_dylib(dep):
+    return not bool(dep.static_library or dep.pic_static_library)
+
+def establish_cc_info(ctx, attr, crate_info, toolchain, cc_toolchain, feature_configuration, interface_library):
+    """If the produced crate is suitable yield a CcInfo to allow for interop with cc rules
+
+    Args:
+        ctx (ctx): The rule's context object
+        attr (struct): Attributes to use in gathering CcInfo
+        crate_info (CrateInfo): The CrateInfo provider of the target crate
+        toolchain (rust_toolchain): The current `rust_toolchain`
+        cc_toolchain (CcToolchainInfo): The current `CcToolchainInfo`
+        feature_configuration (FeatureConfiguration): Feature configuration to be queried.
+        interface_library (File): Optional interface library for cdylib crates on Windows.
+
+    Returns:
+        list: A list containing the CcInfo provider
+    """
+
+    # A test will not need to produce CcInfo as nothing can depend on test targets
+    if crate_info.is_test:
+        return []
+
+    # Only generate CcInfo for particular crate types
+    if crate_info.type not in ("staticlib", "cdylib", "rlib", "lib"):
+        return []
+
+    # TODO: Remove after some resolution to
+    # https://github.com/bazelbuild/rules_rust/issues/771
+    if getattr(attr, "out_binary", False):
+        return []
+
+    if crate_info.type == "staticlib":
+        library_to_link = cc_common.create_library_to_link(
+            actions = ctx.actions,
+            feature_configuration = feature_configuration,
+            cc_toolchain = cc_toolchain,
+            static_library = crate_info.output,
+            # TODO(hlopko): handle PIC/NOPIC correctly
+            pic_static_library = crate_info.output,
+        )
+    elif crate_info.type in ("rlib", "lib"):
+        # bazel hard-codes a check for endswith((".a", ".pic.a",
+        # ".lib")) in create_library_to_link, so we work around that
+        # by creating a symlink to the .rlib with a .a extension.
+        dot_a = make_static_lib_symlink(ctx.actions, crate_info.output)
+
+        # TODO(hlopko): handle PIC/NOPIC correctly
+        library_to_link = cc_common.create_library_to_link(
+            actions = ctx.actions,
+            feature_configuration = feature_configuration,
+            cc_toolchain = cc_toolchain,
+            static_library = dot_a,
+            # TODO(hlopko): handle PIC/NOPIC correctly
+            pic_static_library = dot_a,
+        )
+    elif crate_info.type == "cdylib":
+        library_to_link = cc_common.create_library_to_link(
+            actions = ctx.actions,
+            feature_configuration = feature_configuration,
+            cc_toolchain = cc_toolchain,
+            dynamic_library = crate_info.output,
+            interface_library = interface_library,
+        )
+    else:
+        fail("Unexpected case")
+
+    link_input = cc_common.create_linker_input(
+        owner = ctx.label,
+        libraries = depset([library_to_link]),
+    )
+
+    linking_context = cc_common.create_linking_context(
+        # TODO - What to do for no_std?
+        linker_inputs = depset([link_input]),
+    )
+
+    cc_infos = [
+        CcInfo(linking_context = linking_context),
+        toolchain.stdlib_linkflags,
+    ]
+    for dep in getattr(attr, "deps", []):
+        if CcInfo in dep:
+            cc_infos.append(dep[CcInfo])
+
+    if crate_info.type in ("rlib", "lib") and toolchain.libstd_and_allocator_ccinfo:
+        # TODO: if we already have an rlib in our deps, we could skip this
+        cc_infos.append(toolchain.libstd_and_allocator_ccinfo)
+
+    return [cc_common.merge_cc_infos(cc_infos = cc_infos)]
+
+def add_edition_flags(args, crate):
+    """Adds the Rust edition flag to an arguments object reference
+
+    Args:
+        args (Args): A reference to an Args object
+        crate (CrateInfo): A CrateInfo provider
+    """
+    if crate.edition != "2015":
+        args.add("--edition={}".format(crate.edition))
+
+def _create_extra_input_args(build_info, dep_info):
+    """Gather additional input arguments from transitive dependencies
+
+    Args:
+        build_info (BuildInfo): The BuildInfo provider from the target Crate's set of inputs.
+        dep_info (DepInfo): The Depinfo provider form the target Crate's set of inputs.
+
+    Returns:
+        tuple: A tuple of the following items:
+            - (depset[File]): A list of all build info `OUT_DIR` File objects
+            - (str): The `OUT_DIR` of the current build info
+            - (File): An optional generated environment file from a `cargo_build_script` target
+            - (depset[File]): All direct and transitive build flag files from the current build info.
+    """
+    input_files = []
+
+    # Arguments to the commandline line wrapper that are going to be used
+    # to create the final command line
+    out_dir = None
+    build_env_file = None
+    build_flags_files = []
+
+    if build_info:
+        out_dir = build_info.out_dir.path
+        build_env_file = build_info.rustc_env
+        build_flags_files.append(build_info.flags)
+        build_flags_files.append(build_info.link_flags)
+        input_files.append(build_info.out_dir)
+        input_files.append(build_info.link_flags)
+
+    return (
+        depset(input_files, transitive = [dep_info.link_search_path_files]),
+        out_dir,
+        build_env_file,
+        depset(build_flags_files, transitive = [dep_info.link_search_path_files]),
+    )
+
+def _compute_rpaths(toolchain, output_dir, dep_info, use_pic):
+    """Determine the artifact's rpaths relative to the bazel root for runtime linking of shared libraries.
+
+    Args:
+        toolchain (rust_toolchain): The current `rust_toolchain`
+        output_dir (str): The output directory of the current target
+        dep_info (DepInfo): The current target's dependency info
+        use_pic: If set, prefers pic_static_library over static_library.
+
+    Returns:
+        depset: A set of relative paths from the output directory to each dependency
+    """
+
+    # Windows has no rpath equivalent, so always return an empty depset.
+    if toolchain.os == "windows":
+        return depset([])
+
+    dylibs = [
+        get_preferred_artifact(lib, use_pic)
+        for linker_input in dep_info.transitive_noncrates.to_list()
+        for lib in linker_input.libraries
+        if _is_dylib(lib)
+    ]
+    if not dylibs:
+        return depset([])
+
+    # For darwin, dylibs compiled by Bazel will fail to be resolved at runtime
+    # without a version of Bazel that includes
+    # https://github.com/bazelbuild/bazel/pull/13427. This is known to not be
+    # included in Bazel 4.1 and below.
+    if toolchain.os != "linux" and toolchain.os != "darwin":
+        fail("Runtime linking is not supported on {}, but found {}".format(
+            toolchain.os,
+            dep_info.transitive_noncrates,
+        ))
+
+    # Multiple dylibs can be present in the same directory, so deduplicate them.
+    return depset([
+        relativize(lib_dir, output_dir)
+        for lib_dir in _get_dir_names(dylibs)
+    ])
+
+def _get_dir_names(files):
+    """Returns a list of directory names from the given list of File objects
+
+    Args:
+        files (list): A list of File objects
+
+    Returns:
+        list: A list of directory names for all files
+    """
+    dirs = {}
+    for f in files:
+        dirs[f.dirname] = None
+    return dirs.keys()
+
+def add_crate_link_flags(args, dep_info, force_all_deps_direct = False):
+    """Adds link flags to an Args object reference
+
+    Args:
+        args (Args): An arguments object reference
+        dep_info (DepInfo): The current target's dependency info
+        force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
+            to the commandline as opposed to -L.
+    """
+
+    if force_all_deps_direct:
+        args.add_all(
+            depset(
+                transitive = [
+                    dep_info.direct_crates,
+                    dep_info.transitive_crates,
+                ],
+            ),
+            uniquify = True,
+            map_each = _crate_to_link_flag,
+        )
+    else:
+        # nb. Direct crates are linked via --extern regardless of their crate_type
+        args.add_all(dep_info.direct_crates, map_each = _crate_to_link_flag)
+    args.add_all(
+        dep_info.transitive_crates,
+        map_each = _get_crate_dirname,
+        uniquify = True,
+        format_each = "-Ldependency=%s",
+    )
+
+def _crate_to_link_flag(crate):
+    """A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
+
+    Args:
+        crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider
+
+    Returns:
+        list: Link flags for the given provider
+    """
+
+    # This is AliasableDepInfo, we should use the alias as a crate name
+    if hasattr(crate, "dep"):
+        name = crate.name
+        crate_info = crate.dep
+    else:
+        name = crate.name
+        crate_info = crate
+    return ["--extern={}={}".format(name, crate_info.output.path)]
+
+def _get_crate_dirname(crate):
+    """A helper macro used by `add_crate_link_flags` for getting the directory name of the current crate's output path
+
+    Args:
+        crate (CrateInfo): A CrateInfo provider from the current rule
+
+    Returns:
+        str: The directory name of the the output File that will be produced.
+    """
+    return crate.output.dirname
+
+def _portable_link_flags(lib, use_pic, ambiguous_libs):
+    artifact = get_preferred_artifact(lib, use_pic)
+    if ambiguous_libs and artifact.path in ambiguous_libs:
+        artifact = ambiguous_libs[artifact.path]
+    if lib.static_library or lib.pic_static_library:
+        return [
+            "-lstatic=%s" % get_lib_name(artifact),
+        ]
+    elif _is_dylib(lib):
+        return [
+            "-ldylib=%s" % get_lib_name(artifact),
+        ]
+
+    return []
+
+def _make_link_flags_windows(linker_input_and_use_pic_and_ambiguous_libs):
+    linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
+    ret = []
+    for lib in linker_input.libraries:
+        if lib.alwayslink:
+            ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path])
+        else:
+            ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
+    return ret
+
+def _make_link_flags_darwin(linker_input_and_use_pic_and_ambiguous_libs):
+    linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
+    ret = []
+    for lib in linker_input.libraries:
+        if lib.alwayslink:
+            ret.extend([
+                "-C",
+                ("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path),
+            ])
+        else:
+            ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
+    return ret
+
+def _make_link_flags_default(linker_input_and_use_pic_and_ambiguous_libs):
+    linker_input, use_pic, ambiguous_libs = linker_input_and_use_pic_and_ambiguous_libs
+    ret = []
+    for lib in linker_input.libraries:
+        if lib.alwayslink:
+            ret.extend([
+                "-C",
+                "link-arg=-Wl,--whole-archive",
+                "-C",
+                ("link-arg=%s" % get_preferred_artifact(lib, use_pic).path),
+                "-C",
+                "link-arg=-Wl,--no-whole-archive",
+            ])
+        else:
+            ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
+    return ret
+
+def _libraries_dirnames(linker_input_and_use_pic_and_ambiguous_libs):
+    link_input, use_pic, _ = linker_input_and_use_pic_and_ambiguous_libs
+
+    # De-duplicate names.
+    return depset([get_preferred_artifact(lib, use_pic).dirname for lib in link_input.libraries]).to_list()
+
+def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate_type, toolchain, cc_toolchain, feature_configuration):
+    """Adds linker flags for all dependencies of the current target.
+
+    Args:
+        args (Args): The Args struct for a ctx.action
+        dep_info (DepInfo): Dependency Info provider
+        linkstamp_outs (list): Linkstamp outputs of native dependencies
+        ambiguous_libs (dict): Ambiguous libs, see `_disambiguate_libs`
+        crate_type: Crate type of the current target
+        toolchain (rust_toolchain): The current `rust_toolchain`
+        cc_toolchain (CcToolchainInfo): The current `cc_toolchain`
+        feature_configuration (FeatureConfiguration): feature configuration to use with cc_toolchain
+
+    """
+    if crate_type in ["lib", "rlib"]:
+        return
+
+    use_pic = _should_use_pic(cc_toolchain, feature_configuration, crate_type)
+
+    if toolchain.os == "windows":
+        make_link_flags = _make_link_flags_windows
+    elif toolchain.os.startswith("mac") or toolchain.os.startswith("darwin"):
+        make_link_flags = _make_link_flags_darwin
+    else:
+        make_link_flags = _make_link_flags_default
+
+    # TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0
+    args_and_pic_and_ambiguous_libs = [(arg, use_pic, ambiguous_libs) for arg in dep_info.transitive_noncrates.to_list()]
+    args.add_all(args_and_pic_and_ambiguous_libs, map_each = _libraries_dirnames, uniquify = True, format_each = "-Lnative=%s")
+    if ambiguous_libs:
+        # If there are ambiguous libs, the disambiguation symlinks to them are
+        # all created in the same directory. Add it to the library search path.
+        ambiguous_libs_dirname = ambiguous_libs.values()[0].dirname
+        args.add("-Lnative={}".format(ambiguous_libs_dirname))
+
+    args.add_all(args_and_pic_and_ambiguous_libs, map_each = make_link_flags)
+
+    for linkstamp_out in linkstamp_outs:
+        args.add_all(["-C", "link-arg=%s" % linkstamp_out.path])
+
+    if crate_type in ["dylib", "cdylib"]:
+        # For shared libraries we want to link C++ runtime library dynamically
+        # (for example libstdc++.so or libc++.so).
+        args.add_all(
+            cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
+            map_each = _get_dirname,
+            format_each = "-Lnative=%s",
+        )
+        args.add_all(
+            cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
+            map_each = get_lib_name,
+            format_each = "-ldylib=%s",
+        )
+    else:
+        # For all other crate types we want to link C++ runtime library statically
+        # (for example libstdc++.a or libc++.a).
+        args.add_all(
+            cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
+            map_each = _get_dirname,
+            format_each = "-Lnative=%s",
+        )
+        args.add_all(
+            cc_toolchain.static_runtime_lib(feature_configuration = feature_configuration),
+            map_each = get_lib_name,
+            format_each = "-lstatic=%s",
+        )
+
+def _get_dirname(file):
+    """A helper function for `_add_native_link_flags`.
+
+    Args:
+        file (File): The target file
+
+    Returns:
+        str: Directory name of `file`
+    """
+    return file.dirname
+
+def _error_format_impl(ctx):
+    """Implementation of the `error_format` rule
+
+    Args:
+        ctx (ctx): The rule's context object
+
+    Returns:
+        list: A list containing the ErrorFormatInfo provider
+    """
+    raw = ctx.build_setting_value
+    if raw not in _error_format_values:
+        fail("{} expected a value in `{}` but got `{}`".format(
+            ctx.label,
+            _error_format_values,
+            raw,
+        ))
+    return [ErrorFormatInfo(error_format = raw)]
+
+error_format = rule(
+    doc = (
+        "Change the [--error-format](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-error-format) " +
+        "flag from the command line with `--@rules_rust//:error_format`. See rustc documentation for valid values."
+    ),
+    implementation = _error_format_impl,
+    build_setting = config.string(flag = True),
+)
+
+def _extra_rustc_flags_impl(ctx):
+    return ExtraRustcFlagsInfo(extra_rustc_flags = ctx.build_setting_value)
+
+extra_rustc_flags = rule(
+    doc = (
+        "Add additional rustc_flags from the command line with `--@rules_rust//:extra_rustc_flags`. " +
+        "This flag should only be used for flags that need to be applied across the entire build. For options that " +
+        "apply to individual crates, use the rustc_flags attribute on the individual crate's rule instead. NOTE: " +
+        "These flags not applied to the exec configuration (proc-macros, cargo_build_script, etc); " +
+        "use `--@rules_rust//:extra_exec_rustc_flags` to apply flags to the exec configuration."
+    ),
+    implementation = _extra_rustc_flags_impl,
+    build_setting = config.string_list(flag = True),
+)
+
+def _extra_exec_rustc_flags_impl(ctx):
+    return ExtraExecRustcFlagsInfo(extra_exec_rustc_flags = ctx.build_setting_value)
+
+extra_exec_rustc_flags = rule(
+    doc = (
+        "Add additional rustc_flags in the exec configuration from the command line with `--@rules_rust//:extra_exec_rustc_flags`. " +
+        "This flag should only be used for flags that need to be applied across the entire build. " +
+        "These flags only apply to the exec configuration (proc-macros, cargo_build_script, etc)."
+    ),
+    implementation = _extra_exec_rustc_flags_impl,
+    build_setting = config.string_list(flag = True),
+)
diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl
new file mode 100644
index 0000000..86c2acd
--- /dev/null
+++ b/rust/private/rustdoc.bzl
@@ -0,0 +1,318 @@
+# Copyright 2018 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.
+
+"""Rules for generating documentation with `rustdoc` for Bazel built crates"""
+
+load("//rust/private:common.bzl", "rust_common")
+load("//rust/private:rustc.bzl", "collect_deps", "collect_inputs", "construct_arguments")
+load("//rust/private:utils.bzl", "dedent", "find_cc_toolchain", "find_toolchain")
+
+def _strip_crate_info_output(crate_info):
+    """Set the CrateInfo.output to None for a given CrateInfo provider.
+
+    Args:
+        crate_info (CrateInfo): A provider
+
+    Returns:
+        CrateInfo: A modified CrateInfo provider
+    """
+    return rust_common.create_crate_info(
+        name = crate_info.name,
+        type = crate_info.type,
+        root = crate_info.root,
+        srcs = crate_info.srcs,
+        deps = crate_info.deps,
+        proc_macro_deps = crate_info.proc_macro_deps,
+        aliases = crate_info.aliases,
+        # This crate info should have no output
+        output = None,
+        edition = crate_info.edition,
+        rustc_env = crate_info.rustc_env,
+        is_test = crate_info.is_test,
+        compile_data = crate_info.compile_data,
+    )
+
+def rustdoc_compile_action(
+        ctx,
+        toolchain,
+        crate_info,
+        output = None,
+        rustdoc_flags = [],
+        is_test = False):
+    """Create a struct of information needed for a `rustdoc` compile action based on crate passed to the rustdoc rule.
+
+    Args:
+        ctx (ctx): The rule's context object.
+        toolchain (rust_toolchain): The currently configured `rust_toolchain`.
+        crate_info (CrateInfo): The provider of the crate passed to a rustdoc rule.
+        output (File, optional): An optional output a `rustdoc` action is intended to produce.
+        rustdoc_flags (list, optional): A list of `rustdoc` specific flags.
+        is_test (bool, optional): If True, the action will be configured for `rust_doc_test` targets
+
+    Returns:
+        struct: A struct of some `ctx.actions.run` arguments.
+    """
+
+    # If an output was provided, ensure it's used in rustdoc arguments
+    if output:
+        rustdoc_flags = [
+            "--output",
+            output.path,
+        ] + rustdoc_flags
+
+    cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
+
+    dep_info, build_info, linkstamps = collect_deps(
+        deps = crate_info.deps,
+        proc_macro_deps = crate_info.proc_macro_deps,
+        aliases = crate_info.aliases,
+    )
+
+    compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs(
+        ctx = ctx,
+        file = ctx.file,
+        files = ctx.files,
+        linkstamps = linkstamps,
+        toolchain = toolchain,
+        cc_toolchain = cc_toolchain,
+        feature_configuration = feature_configuration,
+        crate_info = crate_info,
+        dep_info = dep_info,
+        build_info = build_info,
+    )
+
+    # Since this crate is not actually producing the output described by the
+    # given CrateInfo, this attribute needs to be stripped to allow the rest
+    # of the rustc functionality in `construct_arguments` to avoid generating
+    # arguments expecting to do so.
+    rustdoc_crate_info = _strip_crate_info_output(crate_info)
+
+    args, env = construct_arguments(
+        ctx = ctx,
+        attr = ctx.attr,
+        file = ctx.file,
+        toolchain = toolchain,
+        tool_path = toolchain.rust_doc.short_path if is_test else toolchain.rust_doc.path,
+        cc_toolchain = cc_toolchain,
+        feature_configuration = feature_configuration,
+        crate_info = rustdoc_crate_info,
+        dep_info = dep_info,
+        linkstamp_outs = linkstamp_outs,
+        ambiguous_libs = ambiguous_libs,
+        output_hash = None,
+        rust_flags = rustdoc_flags,
+        out_dir = out_dir,
+        build_env_files = build_env_files,
+        build_flags_files = build_flags_files,
+        emit = [],
+        remap_path_prefix = None,
+        force_link = True,
+    )
+
+    # Because rustdoc tests compile tests outside of the sandbox, the sysroot
+    # must be updated to the `short_path` equivilant as it will now be
+    # a part of runfiles.
+    if is_test:
+        if "SYSROOT" in env:
+            env.update({"SYSROOT": "${{pwd}}/{}".format(toolchain.sysroot_short_path)})
+
+        # `rustdoc` does not support the SYSROOT environment variable. To account
+        # for this, the flag must be explicitly passed to the `rustdoc` binary.
+        args.rustc_flags.add("--sysroot=${{pwd}}/{}".format(toolchain.sysroot_short_path))
+
+    return struct(
+        executable = ctx.executable._process_wrapper,
+        inputs = depset([crate_info.output], transitive = [compile_inputs]),
+        env = env,
+        arguments = args.all,
+        tools = [toolchain.rust_doc],
+    )
+
+def _zip_action(ctx, input_dir, output_zip, crate_label):
+    """Creates an archive of the generated documentation from `rustdoc`
+
+    Args:
+        ctx (ctx): The `rust_doc` rule's context object
+        input_dir (File): A directory containing the outputs from rustdoc
+        output_zip (File): The location of the output archive containing generated documentation
+        crate_label (Label): The label of the crate docs are being generated for.
+    """
+    args = ctx.actions.args()
+    args.add(ctx.executable._zipper)
+    args.add(output_zip)
+    args.add(ctx.bin_dir.path)
+    args.add_all([input_dir], expand_directories = True)
+    ctx.actions.run(
+        executable = ctx.executable._dir_zipper,
+        inputs = [input_dir],
+        outputs = [output_zip],
+        arguments = [args],
+        mnemonic = "RustdocZip",
+        progress_message = "Creating RustdocZip for {}".format(crate_label),
+        tools = [ctx.executable._zipper],
+    )
+
+def _rust_doc_impl(ctx):
+    """The implementation of the `rust_doc` rule
+
+    Args:
+        ctx (ctx): The rule's context object
+    """
+
+    crate = ctx.attr.crate
+    crate_info = crate[rust_common.crate_info]
+
+    output_dir = ctx.actions.declare_directory("{}.rustdoc".format(ctx.label.name))
+
+    # Add the current crate as an extern for the compile action
+    rustdoc_flags = [
+        "--extern",
+        "{}={}".format(crate_info.name, crate_info.output.path),
+    ]
+
+    action = rustdoc_compile_action(
+        ctx = ctx,
+        toolchain = find_toolchain(ctx),
+        crate_info = crate_info,
+        output = output_dir,
+        rustdoc_flags = rustdoc_flags,
+    )
+
+    ctx.actions.run(
+        mnemonic = "Rustdoc",
+        progress_message = "Generating Rustdoc for {}".format(crate.label),
+        outputs = [output_dir],
+        executable = action.executable,
+        inputs = action.inputs,
+        env = action.env,
+        arguments = action.arguments,
+        tools = action.tools,
+    )
+
+    # This rule does nothing without a single-file output, though the directory should've sufficed.
+    _zip_action(ctx, output_dir, ctx.outputs.rust_doc_zip, crate.label)
+
+    return [
+        DefaultInfo(
+            files = depset([ctx.outputs.rust_doc_zip]),
+        ),
+        OutputGroupInfo(
+            rustdoc_dir = depset([output_dir]),
+            rustdoc_zip = depset([ctx.outputs.rust_doc_zip]),
+        ),
+    ]
+
+rust_doc = rule(
+    doc = dedent("""\
+    Generates code documentation.
+
+    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("@rules_rust//rust:defs.bzl", "rust_library", "rust_doc")
+
+    rust_library(
+        name = "hello_lib",
+        srcs = ["src/lib.rs"],
+    )
+
+    rust_doc(
+        name = "hello_lib_doc",
+        crate = ":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`.
+    """),
+    implementation = _rust_doc_impl,
+    attrs = {
+        "crate": attr.label(
+            doc = (
+                "The label of the target to generate code documentation for.\n" +
+                "\n" +
+                "`rust_doc` can generate HTML code documentation for the source files of " +
+                "`rust_library` or `rust_binary` targets."
+            ),
+            providers = [rust_common.crate_info],
+            mandatory = True,
+        ),
+        "html_after_content": attr.label(
+            doc = "File to add in `<body>`, after content.",
+            allow_single_file = [".html", ".md"],
+        ),
+        "html_before_content": attr.label(
+            doc = "File to add in `<body>`, before content.",
+            allow_single_file = [".html", ".md"],
+        ),
+        "html_in_header": attr.label(
+            doc = "File to add to `<head>`.",
+            allow_single_file = [".html", ".md"],
+        ),
+        "markdown_css": attr.label_list(
+            doc = "CSS files to include via `<link>` in a rendered Markdown file.",
+            allow_files = [".css"],
+        ),
+        "_cc_toolchain": attr.label(
+            doc = "In order to use find_cpp_toolchain, you must define the '_cc_toolchain' attribute on your rule or aspect.",
+            default = "@bazel_tools//tools/cpp:current_cc_toolchain",
+        ),
+        "_dir_zipper": attr.label(
+            doc = "A tool that orchestrates the creation of zip archives for rustdoc outputs.",
+            default = Label("//util/dir_zipper"),
+            cfg = "exec",
+            executable = True,
+        ),
+        "_process_wrapper": attr.label(
+            doc = "A process wrapper for running rustdoc on all platforms",
+            default = Label("@rules_rust//util/process_wrapper"),
+            executable = True,
+            allow_single_file = True,
+            cfg = "exec",
+        ),
+        "_zipper": attr.label(
+            doc = "A Bazel provided tool for creating archives",
+            default = Label("@bazel_tools//tools/zip:zipper"),
+            cfg = "exec",
+            executable = True,
+        ),
+    },
+    fragments = ["cpp"],
+    host_fragments = ["cpp"],
+    outputs = {
+        "rust_doc_zip": "%{name}.zip",
+    },
+    toolchains = [
+        str(Label("//rust:toolchain")),
+        "@bazel_tools//tools/cpp:toolchain_type",
+    ],
+    incompatible_use_toolchain_transition = True,
+)
diff --git a/rust/private/rustdoc_test.bzl b/rust/private/rustdoc_test.bzl
new file mode 100644
index 0000000..663c65f
--- /dev/null
+++ b/rust/private/rustdoc_test.bzl
@@ -0,0 +1,225 @@
+# Copyright 2018 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.
+
+"""Rules for performing `rustdoc --test` on Bazel built crates"""
+
+load("//rust/private:common.bzl", "rust_common")
+load("//rust/private:rustdoc.bzl", "rustdoc_compile_action")
+load("//rust/private:utils.bzl", "dedent", "find_toolchain")
+
+def _construct_writer_arguments(ctx, test_runner, action, crate_info):
+    """Construct arguments and environment variables specific to `rustdoc_test_writer`.
+
+    This is largely solving for the fact that tests run from a runfiles directory
+    where actions run in an execroot. But it also tracks what environment variables
+    were explicitly added to the action.
+
+    Args:
+        ctx (ctx): The rule's context object.
+        test_runner (File): The test_runner output file declared by `rustdoc_test`.
+        action (struct): Action arguments generated by `rustdoc_compile_action`.
+        crate_info (CrateInfo): The provider of the crate who's docs are being tested.
+
+    Returns:
+        tuple: A tuple of `rustdoc_test_writer` specific inputs
+            - Args: Arguments for the test writer
+            - dict: Required environment variables
+    """
+
+    writer_args = ctx.actions.args()
+
+    # Track the output path where the test writer should write the test
+    writer_args.add("--output={}".format(test_runner.path))
+
+    # Track what environment variables should be written to the test runner
+    writer_args.add("--action_env=DEVELOPER_DIR")
+    writer_args.add("--action_env=PATHEXT")
+    writer_args.add("--action_env=SDKROOT")
+    writer_args.add("--action_env=SYSROOT")
+    for var in action.env.keys():
+        writer_args.add("--action_env={}".format(var))
+
+    # Since the test runner will be running from a runfiles directory, the
+    # paths originally generated for the build action will not map to any
+    # files. To ensure rustdoc can find the appropriate dependencies, the
+    # file roots are identified and tracked for each dependency so it can be
+    # stripped from the test runner.
+    for dep in crate_info.deps.to_list():
+        dep_crate_info = getattr(dep, "crate_info", None)
+        dep_dep_info = getattr(dep, "dep_info", None)
+        if dep_crate_info:
+            root = dep_crate_info.output.root.path
+            writer_args.add("--strip_substring={}/".format(root))
+        if dep_dep_info:
+            for direct_dep in dep_dep_info.direct_crates.to_list():
+                root = direct_dep.dep.output.root.path
+                writer_args.add("--strip_substring={}/".format(root))
+            for transitive_dep in dep_dep_info.transitive_crates.to_list():
+                root = transitive_dep.output.root.path
+                writer_args.add("--strip_substring={}/".format(root))
+
+    # Indicate that the rustdoc_test args are over.
+    writer_args.add("--")
+
+    # Prepare for the process runner to ingest the rest of the arguments
+    # to match the expectations of `rustc_compile_action`.
+    writer_args.add(ctx.executable._process_wrapper.short_path)
+
+    return (writer_args, action.env)
+
+def _rust_doc_test_impl(ctx):
+    """The implementation for the `rust_doc_test` rule
+
+    Args:
+        ctx (ctx): The rule's context object
+
+    Returns:
+        list: A list containing a DefaultInfo provider
+    """
+
+    toolchain = find_toolchain(ctx)
+
+    crate = ctx.attr.crate
+    crate_info = crate[rust_common.crate_info]
+
+    if toolchain.os == "windows":
+        test_runner = ctx.actions.declare_file(ctx.label.name + ".rustdoc_test.bat")
+    else:
+        test_runner = ctx.actions.declare_file(ctx.label.name + ".rustdoc_test.sh")
+
+    # Add the current crate as an extern for the compile action
+    rustdoc_flags = [
+        "--extern",
+        "{}={}".format(crate_info.name, crate_info.output.short_path),
+        "--test",
+    ]
+
+    action = rustdoc_compile_action(
+        ctx = ctx,
+        toolchain = toolchain,
+        crate_info = crate_info,
+        rustdoc_flags = rustdoc_flags,
+        is_test = True,
+    )
+
+    tools = action.tools + [ctx.executable._process_wrapper]
+
+    writer_args, env = _construct_writer_arguments(
+        ctx = ctx,
+        test_runner = test_runner,
+        action = action,
+        crate_info = crate_info,
+    )
+
+    # Allow writer environment variables to override those from the action.
+    action.env.update(env)
+
+    ctx.actions.run(
+        mnemonic = "RustdocTestWriter",
+        progress_message = "Generating Rustdoc test runner for {}".format(crate.label),
+        executable = ctx.executable._test_writer,
+        inputs = action.inputs,
+        tools = tools,
+        arguments = [writer_args] + action.arguments,
+        env = action.env,
+        outputs = [test_runner],
+    )
+
+    return [DefaultInfo(
+        files = depset([test_runner]),
+        runfiles = ctx.runfiles(files = tools, transitive_files = action.inputs),
+        executable = test_runner,
+    )]
+
+rust_doc_test = rule(
+    implementation = _rust_doc_test_impl,
+    attrs = {
+        "crate": attr.label(
+            doc = (
+                "The label of the target to generate code documentation for. " +
+                "`rust_doc_test` can generate HTML code documentation for the " +
+                "source files of `rust_library` or `rust_binary` targets."
+            ),
+            providers = [rust_common.crate_info],
+            mandatory = True,
+        ),
+        "_cc_toolchain": attr.label(
+            doc = (
+                "In order to use find_cc_toolchain, your rule has to depend " +
+                "on C++ toolchain. See @rules_cc//cc:find_cc_toolchain.bzl " +
+                "docs for details."
+            ),
+            default = "@bazel_tools//tools/cpp:current_cc_toolchain",
+        ),
+        "_process_wrapper": attr.label(
+            doc = "A process wrapper for running rustdoc on all platforms",
+            cfg = "exec",
+            default = Label("//util/process_wrapper"),
+            executable = True,
+        ),
+        "_test_writer": attr.label(
+            doc = "A binary used for writing script for use as the test executable.",
+            cfg = "exec",
+            default = Label("//tools/rustdoc:rustdoc_test_writer"),
+            executable = True,
+        ),
+    },
+    test = True,
+    fragments = ["cpp"],
+    host_fragments = ["cpp"],
+    toolchains = [
+        str(Label("//rust:toolchain")),
+        "@bazel_tools//tools/cpp:toolchain_type",
+    ],
+    incompatible_use_toolchain_transition = True,
+    doc = dedent("""\
+        Runs Rust documentation tests.
+
+        Example:
+
+        Suppose you have the following directory structure for a Rust library crate:
+
+        ```output
+        [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("@rules_rust//rust:defs.bzl", "rust_library", "rust_doc_test")
+
+        rust_library(
+            name = "hello_lib",
+            srcs = ["src/lib.rs"],
+        )
+
+        rust_doc_test(
+            name = "hello_lib_doc_test",
+            crate = ":hello_lib",
+        )
+        ```
+
+        Running `bazel test //hello_lib:hello_lib_doc_test` will run all documentation tests for the `hello_lib` library crate.
+    """),
+)
diff --git a/rust/private/rustfmt.bzl b/rust/private/rustfmt.bzl
new file mode 100644
index 0000000..9c59f48
--- /dev/null
+++ b/rust/private/rustfmt.bzl
@@ -0,0 +1,186 @@
+"""A module defining rustfmt rules"""
+
+load(":common.bzl", "rust_common")
+load(":utils.bzl", "find_toolchain")
+
+def _find_rustfmtable_srcs(target, aspect_ctx = None):
+    """Parse a target for rustfmt formattable sources.
+
+    Args:
+        target (Target): The target the aspect is running on.
+        aspect_ctx (ctx, optional): The aspect's context object.
+
+    Returns:
+        list: A list of formattable sources (`File`).
+    """
+    if rust_common.crate_info not in target:
+        return []
+
+    # Ignore external targets
+    if target.label.workspace_root.startswith("external"):
+        return []
+
+    # Targets annotated with `norustfmt` will not be formatted
+    if aspect_ctx and "norustfmt" in aspect_ctx.rule.attr.tags:
+        return []
+
+    crate_info = target[rust_common.crate_info]
+
+    # Filter out any generated files
+    srcs = [src for src in crate_info.srcs.to_list() if src.is_source]
+
+    return srcs
+
+def _generate_manifest(edition, srcs, ctx):
+    # Gather the source paths to non-generated files
+    src_paths = [src.path for src in srcs]
+
+    # Write the rustfmt manifest
+    manifest = ctx.actions.declare_file(ctx.label.name + ".rustfmt")
+    ctx.actions.write(
+        output = manifest,
+        content = "\n".join(src_paths + [
+            edition,
+        ]),
+    )
+
+    return manifest
+
+def _perform_check(edition, srcs, ctx):
+    toolchain = find_toolchain(ctx)
+    config = ctx.file._config
+    marker = ctx.actions.declare_file(ctx.label.name + ".rustfmt.ok")
+
+    args = ctx.actions.args()
+    args.add("--touch-file")
+    args.add(marker)
+    args.add("--")
+    args.add(toolchain.rustfmt)
+    args.add("--config-path")
+    args.add(config)
+    args.add("--edition")
+    args.add(edition)
+    args.add("--check")
+    args.add_all(srcs)
+
+    ctx.actions.run(
+        executable = ctx.executable._process_wrapper,
+        inputs = srcs + [config],
+        outputs = [marker],
+        tools = [toolchain.rustfmt],
+        arguments = [args],
+        mnemonic = "Rustfmt",
+    )
+
+    return marker
+
+def _rustfmt_aspect_impl(target, ctx):
+    srcs = _find_rustfmtable_srcs(target, ctx)
+
+    # If there are no formattable sources, do nothing.
+    if not srcs:
+        return []
+
+    # Parse the edition to use for formatting from the target
+    edition = target[rust_common.crate_info].edition
+
+    manifest = _generate_manifest(edition, srcs, ctx)
+    marker = _perform_check(edition, srcs, ctx)
+
+    return [
+        OutputGroupInfo(
+            rustfmt_manifest = depset([manifest]),
+            rustfmt_checks = depset([marker]),
+        ),
+    ]
+
+rustfmt_aspect = aspect(
+    implementation = _rustfmt_aspect_impl,
+    doc = """\
+This aspect is used to gather information about a crate for use in rustfmt and perform rustfmt checks
+
+Output Groups:
+
+- `rustfmt_manifest`: A manifest used by rustfmt binaries to provide crate specific settings.
+- `rustfmt_checks`: Executes `rustfmt --check` on the specified target.
+
+The build setting `@rules_rust//:rustfmt.toml` is used to control the Rustfmt [configuration settings][cs]
+used at runtime.
+
+[cs]: https://rust-lang.github.io/rustfmt/
+
+This aspect is executed on any target which provides the `CrateInfo` provider. However
+users may tag a target with `norustfmt` to have it skipped. Additionally, generated
+source files are also ignored by this aspect.
+""",
+    attrs = {
+        "_config": attr.label(
+            doc = "The `rustfmt.toml` file used for formatting",
+            allow_single_file = True,
+            default = Label("//:rustfmt.toml"),
+        ),
+        "_process_wrapper": attr.label(
+            doc = "A process wrapper for running rustfmt on all platforms",
+            cfg = "exec",
+            executable = True,
+            default = Label("//util/process_wrapper"),
+        ),
+    },
+    incompatible_use_toolchain_transition = True,
+    fragments = ["cpp"],
+    host_fragments = ["cpp"],
+    toolchains = [
+        str(Label("//rust:toolchain")),
+    ],
+)
+
+def _rustfmt_test_impl(ctx):
+    # The executable of a test target must be the output of an action in
+    # the rule implementation. This file is simply a symlink to the real
+    # rustfmt test runner.
+    runner = ctx.actions.declare_file("{}{}".format(
+        ctx.label.name,
+        ctx.executable._runner.extension,
+    ))
+
+    ctx.actions.symlink(
+        output = runner,
+        target_file = ctx.executable._runner,
+        is_executable = True,
+    )
+
+    manifests = [target[OutputGroupInfo].rustfmt_manifest for target in ctx.attr.targets]
+    srcs = [depset(_find_rustfmtable_srcs(target)) for target in ctx.attr.targets]
+
+    runfiles = ctx.runfiles(
+        transitive_files = depset(transitive = manifests + srcs),
+    )
+
+    runfiles = runfiles.merge(
+        ctx.attr._runner[DefaultInfo].default_runfiles,
+    )
+
+    return [DefaultInfo(
+        files = depset([runner]),
+        runfiles = runfiles,
+        executable = runner,
+    )]
+
+rustfmt_test = rule(
+    implementation = _rustfmt_test_impl,
+    doc = "A test rule for performing `rustfmt --check` on a set of targets",
+    attrs = {
+        "targets": attr.label_list(
+            doc = "Rust targets to run `rustfmt --check` on.",
+            providers = [rust_common.crate_info],
+            aspects = [rustfmt_aspect],
+        ),
+        "_runner": attr.label(
+            doc = "The rustfmt test runner",
+            cfg = "exec",
+            executable = True,
+            default = Label("//tools/rustfmt:rustfmt_test"),
+        ),
+    },
+    test = True,
+)
diff --git a/rust/private/stamp.bzl b/rust/private/stamp.bzl
new file mode 100644
index 0000000..1a7cab6
--- /dev/null
+++ b/rust/private/stamp.bzl
@@ -0,0 +1,75 @@
+"""A small utility module dedicated to detecting whether or not the `--stamp` flag is enabled
+
+This module can be removed likely after the following PRs ar addressed:
+- https://github.com/bazelbuild/bazel/issues/11164
+"""
+
+load("//rust/private:utils.bzl", "dedent")
+
+StampSettingInfo = provider(
+    doc = "Information about the `--stamp` command line flag",
+    fields = {
+        "value": "bool: Whether or not the `--stamp` flag was enabled",
+    },
+)
+
+def _stamp_build_setting_impl(ctx):
+    return StampSettingInfo(value = ctx.attr.value)
+
+_stamp_build_setting = rule(
+    doc = dedent("""\
+        Whether to encode build information into the binary. Possible values:
+
+        - stamp = 1: Always stamp the build information into the binary, even in [--nostamp][stamp] builds. \
+        This setting should be avoided, since it potentially kills remote caching for the binary 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][stamp] flag.
+
+        Stamped binaries are not rebuilt unless their dependencies change.
+        [stamp]: https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
+    """),
+    implementation = _stamp_build_setting_impl,
+    attrs = {
+        "value": attr.bool(
+            doc = "The default value of the stamp build flag",
+            mandatory = True,
+        ),
+    },
+)
+
+def stamp_build_setting(name, visibility = ["//visibility:public"]):
+    native.config_setting(
+        name = "stamp_detect",
+        values = {"stamp": "1"},
+        visibility = visibility,
+    )
+
+    _stamp_build_setting(
+        name = name,
+        value = select({
+            ":stamp_detect": True,
+            "//conditions:default": False,
+        }),
+        visibility = visibility,
+    )
+
+def is_stamping_enabled(attr):
+    """Determine whether or not build stamping is enabled
+
+    Args:
+        attr (struct): A rule's struct of attributes (`ctx.attr`)
+
+    Returns:
+        bool: The stamp value
+    """
+    stamp_num = getattr(attr, "stamp", -1)
+    if stamp_num == 1:
+        return True
+    elif stamp_num == 0:
+        return False
+    elif stamp_num == -1:
+        stamp_flag = getattr(attr, "_stamp_flag", None)
+        return stamp_flag[StampSettingInfo].value if stamp_flag else False
+    else:
+        fail("Unexpected `stamp` value: {}".format(stamp_num))
diff --git a/rust/private/toolchain_utils.bzl b/rust/private/toolchain_utils.bzl
new file mode 100644
index 0000000..fcd4fb7
--- /dev/null
+++ b/rust/private/toolchain_utils.bzl
@@ -0,0 +1,82 @@
+"""A module defining toolchain utilities"""
+
+def _toolchain_files_impl(ctx):
+    toolchain = ctx.toolchains[str(Label("//rust:toolchain"))]
+
+    runfiles = None
+    if ctx.attr.tool == "cargo":
+        files = depset([toolchain.cargo])
+        runfiles = ctx.runfiles(
+            files = [
+                toolchain.cargo,
+                toolchain.rustc,
+            ],
+            transitive_files = toolchain.rustc_lib,
+        )
+    elif ctx.attr.tool == "clippy":
+        files = depset([toolchain.clippy_driver])
+        runfiles = ctx.runfiles(
+            files = [
+                toolchain.clippy_driver,
+                toolchain.rustc,
+            ],
+            transitive_files = toolchain.rustc_lib,
+        )
+    elif ctx.attr.tool == "rustc":
+        files = depset([toolchain.rustc])
+        runfiles = ctx.runfiles(
+            files = [toolchain.rustc],
+            transitive_files = toolchain.rustc_lib,
+        )
+    elif ctx.attr.tool == "rustdoc":
+        files = depset([toolchain.rust_doc])
+        runfiles = ctx.runfiles(
+            files = [toolchain.rust_doc],
+            transitive_files = toolchain.rustc_lib,
+        )
+    elif ctx.attr.tool == "rustfmt":
+        files = depset([toolchain.rustfmt])
+        runfiles = ctx.runfiles(
+            files = [toolchain.rustfmt],
+            transitive_files = toolchain.rustc_lib,
+        )
+    elif ctx.attr.tool == "rustc_lib":
+        files = toolchain.rustc_lib
+    elif ctx.attr.tool == "rustc_srcs":
+        files = toolchain.rustc_srcs.files
+    elif ctx.attr.tool == "rust_std" or ctx.attr.tool == "rust_stdlib" or ctx.attr.tool == "rust_lib":
+        files = toolchain.rust_std
+    else:
+        fail("Unsupported tool: ", ctx.attr.tool)
+
+    return [DefaultInfo(
+        files = files,
+        runfiles = runfiles,
+    )]
+
+toolchain_files = rule(
+    doc = "A rule for fetching files from a rust toolchain.",
+    implementation = _toolchain_files_impl,
+    attrs = {
+        "tool": attr.string(
+            doc = "The desired tool to get form the current rust_toolchain",
+            values = [
+                "cargo",
+                "clippy",
+                "rust_lib",
+                "rust_std",
+                "rust_stdlib",
+                "rustc_lib",
+                "rustc_srcs",
+                "rustc",
+                "rustdoc",
+                "rustfmt",
+            ],
+            mandatory = True,
+        ),
+    },
+    toolchains = [
+        str(Label("//rust:toolchain")),
+    ],
+    incompatible_use_toolchain_transition = True,
+)
diff --git a/rust/private/transitions.bzl b/rust/private/transitions.bzl
new file mode 100644
index 0000000..cd28efd
--- /dev/null
+++ b/rust/private/transitions.bzl
@@ -0,0 +1,111 @@
+# buildifier: disable=module-docstring
+load("//rust:defs.bzl", "rust_common")
+
+def _wasm_bindgen_transition(_settings, _attr):
+    """The implementation of the `wasm_bindgen_transition` transition
+
+    Args:
+        _settings (dict): A dict {String:Object} of all settings declared
+            in the inputs parameter to `transition()`
+        _attr (dict): A dict of attributes and values of the rule to which
+            the transition is attached
+
+    Returns:
+        dict: A dict of new build settings values to apply
+    """
+    return {"//command_line_option:platforms": str(Label("//rust/platform:wasm"))}
+
+wasm_bindgen_transition = transition(
+    implementation = _wasm_bindgen_transition,
+    inputs = [],
+    outputs = ["//command_line_option:platforms"],
+)
+
+def _import_macro_dep_bootstrap_transition(_settings, _attr):
+    """The implementation of the `import_macro_dep_bootstrap_transition` transition.
+
+    This transition modifies the config to start using the fake macro
+    implementation, so that the macro itself can be bootstrapped without
+    creating a dependency cycle, even while every Rust target has an implicit
+    dependency on the "import" macro (either real or fake).
+
+    Args:
+        _settings (dict): a dict {String:Object} of all settings declared in the
+            inputs parameter to `transition()`.
+        _attr (dict): A dict of attributes and values of the rule to which the
+            transition is attached.
+
+    Returns:
+        dict: A dict of new build settings values to apply.
+    """
+    return {"@rules_rust//rust/settings:use_real_import_macro": False}
+
+import_macro_dep_bootstrap_transition = transition(
+    implementation = _import_macro_dep_bootstrap_transition,
+    inputs = [],
+    outputs = ["@rules_rust//rust/settings:use_real_import_macro"],
+)
+
+def _with_import_macro_bootstrapping_mode_impl(ctx):
+    target = ctx.attr.target[0]
+    return [target[rust_common.crate_info], target[rust_common.dep_info]]
+
+with_import_macro_bootstrapping_mode = rule(
+    implementation = _with_import_macro_bootstrapping_mode_impl,
+    attrs = {
+        "target": attr.label(
+            cfg = import_macro_dep_bootstrap_transition,
+            allow_single_file = True,
+            mandatory = True,
+            executable = False,
+        ),
+        "_allowlist_function_transition": attr.label(
+            default = Label("//tools/allowlists/function_transition_allowlist"),
+        ),
+    },
+)
+
+def _without_process_wrapper_transition_impl(_settings, _attr):
+    """This transition allows rust_* rules to invoke rustc without process_wrapper."""
+    return {
+        "//rust/settings:use_process_wrapper": False,
+    }
+
+without_process_wrapper_transition = transition(
+    implementation = _without_process_wrapper_transition_impl,
+    inputs = [],
+    outputs = ["//rust/settings:use_process_wrapper"],
+)
+
+def _without_process_wrapper_impl(ctx):
+    executable = ctx.executable.target
+    link_name = ctx.label.name
+
+    # Append .exe if on windows
+    if executable.extension:
+        link_name = link_name + "." + executable.extension
+    link = ctx.actions.declare_file(link_name)
+    ctx.actions.symlink(
+        output = link,
+        target_file = executable,
+    )
+    return [
+        DefaultInfo(
+            executable = link,
+        ),
+    ]
+
+without_process_wrapper = rule(
+    implementation = _without_process_wrapper_impl,
+    attrs = {
+        "target": attr.label(
+            cfg = without_process_wrapper_transition,
+            allow_single_file = True,
+            mandatory = True,
+            executable = True,
+        ),
+        "_allowlist_function_transition": attr.label(
+            default = Label("//tools/allowlists/function_transition_allowlist"),
+        ),
+    },
+)
diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl
new file mode 100644
index 0000000..fa7318b
--- /dev/null
+++ b/rust/private/utils.bzl
@@ -0,0 +1,576 @@
+# 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.
+
+"""Utility functions not specific to the rust toolchain."""
+
+load("@bazel_tools//tools/cpp:toolchain_utils.bzl", find_rules_cc_toolchain = "find_cpp_toolchain")
+load(":providers.bzl", "BuildInfo", "CrateInfo", "DepInfo", "DepVariantInfo")
+
+def find_toolchain(ctx):
+    """Finds the first rust toolchain that is configured.
+
+    Args:
+        ctx (ctx): The ctx object for the current target.
+
+    Returns:
+        rust_toolchain: A Rust toolchain context.
+    """
+    return ctx.toolchains[Label("//rust:toolchain")]
+
+def find_cc_toolchain(ctx):
+    """Extracts a CcToolchain from the current target's context
+
+    Args:
+        ctx (ctx): The current target's rule context object
+
+    Returns:
+        tuple: A tuple of (CcToolchain, FeatureConfiguration)
+    """
+    cc_toolchain = find_rules_cc_toolchain(ctx)
+
+    feature_configuration = cc_common.configure_features(
+        ctx = ctx,
+        cc_toolchain = cc_toolchain,
+        requested_features = ctx.features,
+        unsupported_features = ctx.disabled_features,
+    )
+    return cc_toolchain, feature_configuration
+
+# TODO: Replace with bazel-skylib's `path.dirname`. This requires addressing some
+# dependency issues or generating docs will break.
+def relativize(path, start):
+    """Returns the relative path from start to path.
+
+    Args:
+        path (str): The path to relativize.
+        start (str): The ancestor path against which to relativize.
+
+    Returns:
+        str: The portion of `path` that is relative to `start`.
+    """
+    src_parts = _path_parts(start)
+    dest_parts = _path_parts(path)
+    n = 0
+    for src_part, dest_part in zip(src_parts, dest_parts):
+        if src_part != dest_part:
+            break
+        n += 1
+
+    relative_path = ""
+    for _ in range(n, len(src_parts)):
+        relative_path += "../"
+    relative_path += "/".join(dest_parts[n:])
+
+    return relative_path
+
+def _path_parts(path):
+    """Takes a path and returns a list of its parts with all "." elements removed.
+
+    The main use case of this function is if one of the inputs to relativize()
+    is a relative path, such as "./foo".
+
+    Args:
+      path (str): A string representing a unix path
+
+    Returns:
+      list: A list containing the path parts with all "." elements removed.
+    """
+    path_parts = path.split("/")
+    return [part for part in path_parts if part != "."]
+
+def get_lib_name(lib):
+    """Returns the name of a library artifact, eg. libabc.a -> abc
+
+    Args:
+        lib (File): A library file
+
+    Returns:
+        str: The name of the library
+    """
+    # On macos and windows, dynamic/static libraries always end with the
+    # extension and potential versions will be before the extension, and should
+    # be part of the library name.
+    # On linux, the version usually comes after the extension.
+    # So regardless of the platform we want to find the extension and make
+    # everything left to it the library name.
+
+    # Search for the extension - starting from the right - by removing any
+    # trailing digit.
+    comps = lib.basename.split(".")
+    for comp in reversed(comps):
+        if comp.isdigit():
+            comps.pop()
+        else:
+            break
+
+    # The library name is now everything minus the extension.
+    libname = ".".join(comps[:-1])
+
+    if libname.startswith("lib"):
+        return libname[3:]
+    else:
+        return libname
+
+def abs(value):
+    """Returns the absolute value of a number.
+
+    Args:
+      value (int): A number.
+
+    Returns:
+      int: The absolute value of the number.
+    """
+    if value < 0:
+        return -value
+    return value
+
+def determine_output_hash(crate_root, label):
+    """Generates a hash of the crate root file's path.
+
+    Args:
+        crate_root (File): The crate's root file (typically `lib.rs`).
+        label (Label): The label of the target.
+
+    Returns:
+        str: A string representation of the hash.
+    """
+
+    # Take the absolute value of hash() since it could be negative.
+    h = abs(hash(crate_root.path) + hash(repr(label)))
+    return repr(h)
+
+def get_preferred_artifact(library_to_link, use_pic):
+    """Get the first available library to link from a LibraryToLink object.
+
+    Args:
+        library_to_link (LibraryToLink): See the followg links for additional details:
+            https://docs.bazel.build/versions/master/skylark/lib/LibraryToLink.html
+        use_pic: If set, prefers pic_static_library over static_library.
+
+    Returns:
+        File: Returns the first valid library type (only one is expected)
+    """
+    if use_pic:
+        return (
+            library_to_link.pic_static_library or
+            library_to_link.interface_library or
+            library_to_link.dynamic_library
+        )
+    else:
+        return (
+            library_to_link.static_library or
+            library_to_link.pic_static_library or
+            library_to_link.interface_library or
+            library_to_link.dynamic_library
+        )
+
+def _expand_location(ctx, env, data):
+    """A trivial helper for `_expand_locations`
+
+    Args:
+        ctx (ctx): The rule's context object
+        env (str): The value possibly containing location macros to expand.
+        data (sequence of Targets): see `_expand_locations`
+
+    Returns:
+        string: The location-macro expanded version of the string.
+    """
+    for directive in ("$(execpath ", "$(location "):
+        if directive in env:
+            # build script runner will expand pwd to execroot for us
+            env = env.replace(directive, "${pwd}/" + directive)
+    return ctx.expand_location(env, data)
+
+def expand_dict_value_locations(ctx, env, data):
+    """Performs location-macro expansion on string values.
+
+    $(execroot ...) and $(location ...) are prefixed with ${pwd},
+    which process_wrapper and build_script_runner will expand at run time
+    to the absolute path. This is necessary because include_str!() is relative
+    to the currently compiled file, and build scripts run relative to the
+    manifest dir, so we can not use execroot-relative paths.
+
+    $(rootpath ...) is unmodified, and is useful for passing in paths via
+    rustc_env that are encoded in the binary with env!(), but utilized at
+    runtime, such as in tests. The absolute paths are not usable in this case,
+    as compilation happens in a separate sandbox folder, so when it comes time
+    to read the file at runtime, the path is no longer valid.
+
+    See [`expand_location`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#expand_location) for detailed documentation.
+
+    Args:
+        ctx (ctx): The rule's context object
+        env (dict): A dict whose values we iterate over
+        data (sequence of Targets): The targets which may be referenced by
+            location macros. This is expected to be the `data` attribute of
+            the target, though may have other targets or attributes mixed in.
+
+    Returns:
+        dict: A dict of environment variables with expanded location macros
+    """
+    return dict([(k, _expand_location(ctx, v, data)) for (k, v) in env.items()])
+
+def expand_list_element_locations(ctx, args, data):
+    """Performs location-macro expansion on a list of string values.
+
+    $(execroot ...) and $(location ...) are prefixed with ${pwd},
+    which process_wrapper and build_script_runner will expand at run time
+    to the absolute path.
+
+    See [`expand_location`](https://docs.bazel.build/versions/main/skylark/lib/ctx.html#expand_location) for detailed documentation.
+
+    Args:
+        ctx (ctx): The rule's context object
+        args (list): A list we iterate over
+        data (sequence of Targets): The targets which may be referenced by
+            location macros. This is expected to be the `data` attribute of
+            the target, though may have other targets or attributes mixed in.
+
+    Returns:
+        list: A list of arguments with expanded location macros
+    """
+    return [_expand_location(ctx, arg, data) for arg in args]
+
+def name_to_crate_name(name):
+    """Converts a build target's name into the name of its associated crate.
+
+    Crate names cannot contain certain characters, such as -, which are allowed
+    in build target names. All illegal characters will be converted to
+    underscores.
+
+    This is a similar conversion as that which cargo does, taking a
+    `Cargo.toml`'s `package.name` and canonicalizing it
+
+    Note that targets can specify the `crate_name` attribute to customize their
+    crate name; in situations where this is important, use the
+    compute_crate_name() function instead.
+
+    Args:
+        name (str): The name of the target.
+
+    Returns:
+        str: The name of the crate for this target.
+    """
+    return name.replace("-", "_")
+
+def _invalid_chars_in_crate_name(name):
+    """Returns any invalid chars in the given crate name.
+
+    Args:
+        name (str): Name to test.
+
+    Returns:
+        list: List of invalid characters in the crate name.
+    """
+
+    return dict([(c, ()) for c in name.elems() if not (c.isalnum() or c == "_")]).keys()
+
+def compute_crate_name(workspace_name, label, toolchain, name_override = None):
+    """Returns the crate name to use for the current target.
+
+    Args:
+        workspace_name (string): The current workspace name.
+        label (struct): The label of the current target.
+        toolchain (struct): The toolchain in use for the target.
+        name_override (String): An optional name to use (as an override of label.name).
+
+    Returns:
+        str: The crate name to use for this target.
+    """
+    if name_override:
+        invalid_chars = _invalid_chars_in_crate_name(name_override)
+        if invalid_chars:
+            fail("Crate name '{}' contains invalid character(s): {}".format(
+                name_override,
+                " ".join(invalid_chars),
+            ))
+        return name_override
+
+    if (toolchain and label and toolchain._rename_first_party_crates and
+        should_encode_label_in_crate_name(workspace_name, label, toolchain._third_party_dir)):
+        crate_name = encode_label_as_crate_name(label.package, label.name)
+    else:
+        crate_name = name_to_crate_name(label.name)
+
+    invalid_chars = _invalid_chars_in_crate_name(crate_name)
+    if invalid_chars:
+        fail(
+            "Crate name '{}' ".format(crate_name) +
+            "derived from Bazel target name '{}' ".format(label.name) +
+            "contains invalid character(s): {}\n".format(" ".join(invalid_chars)) +
+            "Consider adding a crate_name attribute to set a valid crate name",
+        )
+    return crate_name
+
+def dedent(doc_string):
+    """Remove any common leading whitespace from every line in text.
+
+    This functionality is similar to python's `textwrap.dedent` functionality
+    https://docs.python.org/3/library/textwrap.html#textwrap.dedent
+
+    Args:
+        doc_string (str): A docstring style string
+
+    Returns:
+        str: A string optimized for stardoc rendering
+    """
+    lines = doc_string.splitlines()
+    if not lines:
+        return doc_string
+
+    # If the first line is empty, use the second line
+    first_line = lines[0]
+    if not first_line:
+        first_line = lines[1]
+
+    # Detect how much space prepends the first line and subtract that from all lines
+    space_count = len(first_line) - len(first_line.lstrip())
+
+    # If there are no leading spaces, do not alter the docstring
+    if space_count == 0:
+        return doc_string
+    else:
+        # Remove the leading block of spaces from the current line
+        block = " " * space_count
+        return "\n".join([line.replace(block, "", 1).rstrip() for line in lines])
+
+def make_static_lib_symlink(actions, rlib_file):
+    """Add a .a symlink to an .rlib file.
+
+    The name of the symlink is derived from the <name> of the <name>.rlib file as follows:
+    * `<name>.a`, if <name> starts with `lib`
+    * `lib<name>.a`, otherwise.
+
+    For example, the name of the symlink for
+    * `libcratea.rlib` is `libcratea.a`
+    * `crateb.rlib` is `libcrateb.a`.
+
+    Args:
+        actions (actions): The rule's context actions object.
+        rlib_file (File): The file to symlink, which must end in .rlib.
+
+    Returns:
+        The symlink's File.
+    """
+    if not rlib_file.basename.endswith(".rlib"):
+        fail("file is not an .rlib: ", rlib_file.basename)
+    basename = rlib_file.basename[:-5]
+    if not basename.startswith("lib"):
+        basename = "lib" + basename
+    dot_a = actions.declare_file(basename + ".a", sibling = rlib_file)
+    actions.symlink(output = dot_a, target_file = rlib_file)
+    return dot_a
+
+def is_exec_configuration(ctx):
+    """Determine if a context is building for the exec configuration.
+
+    This is helpful when processing command line flags that should apply
+    to the target configuration but not the exec configuration.
+
+    Args:
+        ctx (ctx): The ctx object for the current target.
+
+    Returns:
+        True if the exec configuration is detected, False otherwise.
+    """
+
+    # TODO(djmarcin): Is there any better way to determine cfg=exec?
+    return ctx.genfiles_dir.path.find("-exec-") != -1
+
+def transform_deps(deps):
+    """Transforms a [Target] into [DepVariantInfo].
+
+    This helper function is used to transform ctx.attr.deps and ctx.attr.proc_macro_deps into
+    [DepVariantInfo].
+
+    Args:
+        deps (list of Targets): Dependencies coming from ctx.attr.deps or ctx.attr.proc_macro_deps
+
+    Returns:
+        list of DepVariantInfos.
+    """
+    return [DepVariantInfo(
+        crate_info = dep[CrateInfo] if CrateInfo in dep else None,
+        dep_info = dep[DepInfo] if DepInfo in dep else None,
+        build_info = dep[BuildInfo] if BuildInfo in dep else None,
+        cc_info = dep[CcInfo] if CcInfo in dep else None,
+    ) for dep in deps]
+
+def get_import_macro_deps(ctx):
+    """Returns a list of targets to be added to proc_macro_deps.
+
+    Args:
+        ctx (struct): the ctx of the current target.
+
+    Returns:
+        list of Targets. Either empty (if the fake import macro implementation
+        is being used), or a singleton list with the real implementation.
+    """
+    if ctx.attr._import_macro_dep.label.name == "fake_import_macro_impl":
+        return []
+
+    return [ctx.attr._import_macro_dep]
+
+def should_encode_label_in_crate_name(workspace_name, label, third_party_dir):
+    """Determines if the crate's name should include the Bazel label, encoded.
+
+    Crate names may only encode the label if the target is in the current repo,
+    the target is not in the third_party_dir, and the current repo is not
+    rules_rust.
+
+    Args:
+        workspace_name (string): The name of the current workspace.
+        label (Label): The package in question.
+        third_party_dir (string): The directory in which third-party packages are kept.
+
+    Returns:
+        True if the crate name should encode the label, False otherwise.
+    """
+
+    # TODO(hlopko): This code assumes a monorepo; make it work with external
+    # repositories as well.
+    return (
+        workspace_name != "rules_rust" and
+        not label.workspace_root and
+        not ("//" + label.package + "/").startswith(third_party_dir + "/")
+    )
+
+# This is a list of pairs, where the first element of the pair is a character
+# that is allowed in Bazel package or target names but not in crate names; and
+# the second element is an encoding of that char suitable for use in a crate
+# name.
+_encodings = (
+    (":", "colon"),
+    ("!", "bang"),
+    ("%", "percent"),
+    ("@", "at"),
+    ("^", "caret"),
+    ("`", "backtick"),
+    (" ", "space"),
+    ("\"", "quote"),
+    ("#", "hash"),
+    ("$", "dollar"),
+    ("&", "ampersand"),
+    ("'", "backslash"),
+    ("(", "lparen"),
+    (")", "rparen"),
+    ("*", "star"),
+    ("-", "dash"),
+    ("+", "plus"),
+    (",", "comma"),
+    (";", "semicolon"),
+    ("<", "langle"),
+    ("=", "equal"),
+    (">", "rangle"),
+    ("?", "question"),
+    ("[", "lbracket"),
+    ("]", "rbracket"),
+    ("{", "lbrace"),
+    ("|", "pipe"),
+    ("}", "rbrace"),
+    ("~", "tilde"),
+    ("/", "slash"),
+    (".", "dot"),
+)
+
+# For each of the above encodings, we generate two substitution rules: one that
+# ensures any occurrences of the encodings themselves in the package/target
+# aren't clobbered by this translation, and one that does the encoding itself.
+# We also include a rule that protects the clobbering-protection rules from
+# getting clobbered.
+_substitutions = [("_quote", "_quotequote_")] + [
+    subst
+    for (pattern, replacement) in _encodings
+    for subst in (
+        ("_{}_".format(replacement), "_quote{}_".format(replacement)),
+        (pattern, "_{}_".format(replacement)),
+    )
+]
+
+def encode_label_as_crate_name(package, name):
+    """Encodes the package and target names in a format suitable for a crate name.
+
+    Args:
+        package (string): The package of the target in question.
+        name (string): The name of the target in question.
+
+    Returns:
+        A string that encodes the package and target name, to be used as the crate's name.
+    """
+    full_name = package + ":" + name
+    return _replace_all(full_name, _substitutions)
+
+def decode_crate_name_as_label_for_testing(crate_name):
+    """Decodes a crate_name that was encoded by encode_label_as_crate_name.
+
+    This is used to check that the encoding is bijective; it is expected to only
+    be used in tests.
+
+    Args:
+        crate_name (string): The name of the crate.
+
+    Returns:
+        A string representing the Bazel label (package and target).
+    """
+    return _replace_all(crate_name, [(t[1], t[0]) for t in _substitutions])
+
+def _replace_all(string, substitutions):
+    """Replaces occurrences of the given patterns in `string`.
+
+    There are a few reasons this looks complicated:
+    * The substitutions are performed with some priority, i.e. patterns that are
+      listed first in `substitutions` are higher priority than patterns that are
+      listed later.
+    * We also take pains to avoid doing replacements that overlap with each
+      other, since overlaps invalidate pattern matches.
+    * To avoid hairy offset invalidation, we apply the substitutions
+      right-to-left.
+    * To avoid the "_quote" -> "_quotequote_" rule introducing new pattern
+      matches later in the string during decoding, we take the leftmost
+      replacement, in cases of overlap.  (Note that no rule can induce new
+      pattern matches *earlier* in the string.) (E.g. "_quotedot_" encodes to
+      "_quotequote_dot_". Note that "_quotequote_" and "_dot_" both occur in
+      this string, and overlap.).
+
+    Args:
+        string (string): the string in which the replacements should be performed.
+        substitutions: the list of patterns and replacements to apply.
+
+    Returns:
+        A string with the appropriate substitutions performed.
+    """
+
+    # Find the highest-priority pattern matches for each string index, going
+    # left-to-right and skipping indices that are already involved in a
+    # pattern match.
+    plan = {}
+    matched_indices_set = {}
+    for pattern_start in range(len(string)):
+        if pattern_start in matched_indices_set:
+            continue
+        for (pattern, replacement) in substitutions:
+            if not string.startswith(pattern, pattern_start):
+                continue
+            length = len(pattern)
+            plan[pattern_start] = (length, replacement)
+            matched_indices_set.update([(pattern_start + i, True) for i in range(length)])
+            break
+
+    # Execute the replacement plan, working from right to left.
+    for pattern_start in sorted(plan.keys(), reverse = True):
+        length, replacement = plan[pattern_start]
+        after_pattern = pattern_start + length
+        string = string[:pattern_start] + replacement + string[after_pattern:]
+
+    return string