Squashed 'third_party/rules_rust/' changes from 078c6908f..bf9ddeb7c

bf9ddeb7c Release 0.25.1 (#2049)
db5b2fd65 Update tinyjson (#2050)
6a7872ae3 Fix prost proto packages not sanitizing to valid module names (#2044)
c080d7bfa Moved legacy protobuf rules to `proto/protobuf` (#2043)
1281cc051 Remove debug code. (#2048)
cd126be1f Fix build failure finding crate_roots when mixed with generated sources (#2041)
7f751cddd Consolidate rust_prost_library and fix extension-only proto generation. (#2047)
6118c81f2 Release 0.25.0 (#2042)
a6f29fd07 Add Prost and Tonic rules. (#2033)
9442aed8c fix: `crate_type` more accurately corresponds to CC linking actions (#1975)
4f4e2b17b Re-enable zig example on CI (#2030)
2ded0c2f5 Fix flaky coverage test in CI (#2028)
36f8251f9 Exclude .tmp_git_root from globs (#1948)
ca750fa83 Eliminate Rustfmt action in Bindgen rules. Bindgen can run rustfmt (#2025)
c55ec0cfb Allow sysroots from cc_toolchains to be added to bindgen actions (#2024)
9314b1b0c Release 0.24.1 (#2023)
92ea74ade Making rust_std attr in rust_toolchain mandatory (#1984)
a54b8e14b Update `rust_library_group` to use `DepVariantInfo` (#2022)
47644346b Release v0.24.0 (#2020)
a6b0a7f39 Rust library group (#1848)
bc43f4841 Fix crate_universe's `all_crate_deps` and `aliases` functions failing in case the crate's Cargo.toml has condtional dependencies (#2018)
8f27ec7c5 fix: load cargo manifest without resolving abs path of deps (#2017)
23f99bb63 feature: `target_compatible_with` added to `CommonAttrs` (#1976)
11f8c9875 Make `rust_doc_test` inherit it's crate aliases attribute (#2007)
8e848414d Regenerated crate_universe outputs for all packages (#2011)
1b6365131 Don't use startup:windows (#2012)
e80582e75 Fix thumbv* platform resolution (#2010)
367f90ef0 Update bindgen version to 0.65.1 (#2008)
e6ed5bf90 Release 0.23.0 (#2003)
93b230bb8 Fix code coverage collection. (#2001)
0a14bfbb0 Minor CI and test cleanup (#2004)
3e2ee941a Update bindgen rules to build clang from source. (#1998)
5a1a7577d Split up cargo_build_script tests (#2002)
eb6413e83 Update various bash scripts to pipe errors to stderr (#1999)
affe947ac Update stardoc version (#1997)
7073146f8 Add support for armv8-m (#1993)
73a06f130 Added Rust 1.70.0 (#1991)
23c20a93f Fixes crates_vendor workspace name detection when using bzlmod (#1990)
f5813fa08 Set windows flags in platform-specific bazelrc (#1988)
c1632b5b5 Fix up anchor link (#1987)
56e760487 Fix typo in crate_universe-generated defs.bzl comment (#1981)
94cbe4c2c Symlink in the exec-root so that relative paths will work, unchanged. (#1781)
af8ef62eb Release 0.22.0 (#1974)
4aaa6de30 Allow specifying exec and target compatibility constraints (#1971)
f1b19c394 Update rules_apple in tests (#1972)
937e63399 Add T2 support for x86_64-unknown-none (#1967)
66b1bf165 fix: lld-link (MSVC) fix flags including `-l` prefix (#1958)
285dcbbb9 feature: expose `extra_rustc_flags` and `extra_exec_rustc_flags` at `rust_register_toolchains` (#1959)
0f25cb462 Removed `rust_toolchain.os` in favor of `rust_toolchain.exec_triple`. (#1960)
a2a1109dc Add T2 support for thumbv7em-none-eabi (#1957)
80f0eb488 Support for `no_std` mode (#1934)
99aaf0830 Rename crates_vendor_manifests to cvm to shorten windows path lengths (#1944)
0a57da049 Added tests for build script dependencies to crate_universe (#1943)
caffb0a08 Release 0.21.1 (#1936)
c869a17c7 Fix regression in building zlib (#1935)
24b9dea4f Release 0.21.0 (#1933)
7677c617e Add support for rustc flags to `rust_proto_library` (#1932)
fa304ae48 Updated zlib BUILD file to support darwin-arm64 (#1931)
a86313282 Added Rust 1.69.0 (#1930)
f0e12c707 Make BuildInfo provider public (#1920)
c6ad23aba Respect `#[global_allocator]` in `cc_common.link` builds (#1926)
d78752504 Exclude target directory from release tars (#1922)
0339cd18a [wasm-bindgen] Update to v0.2.84 (#1919)
07af5678e Handle corner case for windows architecture detection (#1915)
c56e7660d Fix optional deps by platform (#1911)
4663ff6a3 cc_common_link: also respect --custom_malloc if set (#1912)
dab425760 Add Rust 1.68.2 (#1908)
e4bd39f95 Add empty rustfmt.toml (#1907)
eaf513865 Support bzlmod (#1528)
1074ecbab Release v0.20.0 (#1900)
44aec0a79 ci: fix test config in cc_common_link_ubuntu2004 (#1904)
6571cde64 Adds per_crate_rustc_flag build setting. (#1827)
7a47449df Added Rust 1.68.1 (#1898)
e3bcdbad1 Fixed rustdoc warnings in crate_universe (#1897)
529f45900 Added `rustdoc_flags` attribute to rust_doc rule (#1867)
9e3499405 Have rustdoc return its output directory instead of zip default. (#1868)
9d6741f40 Implement support for optional crates enabled with dep: features (#1885)
fd10963ea Skip adding -lstatic to libtest and libstd on Darwin (#1620)
b3314b45e Release 0.19.1 (#1895)
c1a9fd86f Accumulate all features from cargo tree output (#1884)
206f71c95 Disable zig example (#1893)
1a5d07cd2 Add runfiles support to rust_stdlib_filegroup (#1890)
6996cd550 Deleted unused targets and cleanup docs (#1889)
a85e24e20 Fix triple constraints for iOS and watchOS (#1888)
e13fd3bad Release rules_rust and cargo-bazel (#1882)
9e9853d63 Add support for thumbv7em with hard float (#1871)
b3cd5962e Added Rust 1.68.0 (#1866)
f1b7aedf5 Support sparse indexes (#1857)
7f2dd433a Make fetch_shas work with mktemp from coreutils 8.32 (#1870)
a9cc01230 Update crate_universe dependencies (#1872)
c038e94ae Pipe stderr from cargo tree processes (#1879)
222d60322 Parallelize cargo tree calls (#1874)
cdbbf7131 Add Fuchsia platform support (#1833)
17e5b04c2 Use `_make_link_flags_darwin` when target os is `ios`. (#1843)
d9ecc8df4 crate_universe: Support fetching crates with git branch, tag or rev (#1846)
1c694cd60 Forward `toolchains` to `cargo_build_script` targets (#1862)
9affcbfa7 Skip detecting abi for empty values (#1830)
6193fe823 Re-enable crate_universe MacOS tests (#1861)
c25db95ae Updated Rust to 1.67.1 (#1864)
7b8fd06be Support `[patch]` in crate_universe when using multiple `Cargo.toml`s (#1856)
c645fe399 Silence windows build failure (#1863)
75bba7b50 Make rust_clippy providers match rustfmt_test (#1806)
f09d72755 Fix test assertion for arm64 macs (#1845)
f4113bfe1 Fix tests for new Apple toolchain (#1844)
20ce44e30 fix: use target_triple struct instead of string (#1835)
bdbded181 Fix code example in doc (#1838)
4f4014052 Fix typo: plced -> placed (#1834)
baeb0664d Remove ios/android/wasm support for gen_rust_project deps (#1684)
02557a47a Add `render_config` attribute to `crates_vendor`. (#1832)
4357fb154 Updated rules_rust to version 0.18.0 (#1829)
9adfdca9b Various cleanups (#1828)
4fa412385 Added update known shas to include T1-T2 triples (#1824)
905731ad9 Instructions on how to perform `rustfmt` check (#1822) (#1823)
108b1a187 Encapsulate running cargo into a struct (#1815)
57a099b63 Fixes resolver issue with root packages and another dependency format (#1819)
78ca9ba0a Use env method recently added to cargo_metadata (#1813)
92834930f Updated `rust_toolchain.target_json` to take encoded json strings (#1810)
84f1d0657 support `resolver = "2"` target-specific features (#1710)
a5853fd37 Use correct dynamic link args fro proc-macro crates (#1803)
b656e2553 Added tests for the `triple` constructor (#1811)
ea4a79ad9 Disable job in CI to avoid infrastructure failure. (#1816)
2fc02f030 Delete `rust_toolchain.rusrc_srcs` (#1807)
804d5fc1f Convert `rust_toolchain` attrs `exec_triple` and `target_triple` to structs (#1808)
499a2ca38 Updated platform triple values from strings to structs ("triple") (#1804)
aae1dbdcb Unify functions for computing constraint values for platform triple abi (#1805)
0d6d2b1eb Updated rules_rust version to `0.17.0` (#1800)
88e83f2df Added Rust 1.67.0 (#1799)
6922b5012 rustdoc_test: fix and test OUT_DIR (#1779)
ad01d1b0e [crate_universe] add an annotation to disable pipelining (#1733)
f651cd18f Add `CARGO_BAZEL_REPIN_ONLY` repinning allowlist (#1798)
d7f0debb0 Revert "Disable broken clang and ldd CI jobs (#1785)" (#1796)
96f82aaad Fix `cc_common.link` file output name (#1795)
5079b64d5 Fix use of `rustfmt_toolchain` when `rustc` is not provided (#1794)
23c650f35 Have `--experimental_use_cc_common_link` cover `rust_shared_library` (#1792)
ba0fb5956 Added support for `--nolegacy_external_runfiles` to `rust_doc_test` (#1790)
112242bb7 Prevent crates_vendor from restarting bazel. (#1791)
52231ef9f Added compatibility flags to `.bazelrc` to prevent regressions (#1789)
91cd399a0 Add "crate-name={}" tag to Crate Universe targets (#1787)
1b1dae196 Added Rust 1.66.1 (#1767)
fe17e8b8e Add file:// prefix to env var in docs (#1788)
0fe742bff Updated `rust_bindgen` to use `rustfmt_toolchain` (#1770)
042fd6c1c Update docs on setting Rust versions (#1786)
dddd8a0d4 Updated crate_universe dependencies (#1775)
a1330a71f Download `rustc` in `rustfmt_toolchain_repository` (#1769)
e96aad9aa Updated the ios_build example to use `crates_vendor` (#1778)
e315007df Disable broken clang and ldd CI jobs (#1785)
4e89d52a9 rustdoc_test: substitute the root of the current crate (#1777)
a52041fb5 Support `target_settings` in `rust_repository_set` and `rust_toolchain_repository` (#1758)
49906eb29 Update clippy and rustfmt aspects to require CrateInfo providers (#1772)
85564208e Updated rules_rust version to `0.16.1` (#1761)
614499a5b Fixed inability to deserialize crate_universe lockfiles (#1760)
9803d3034 Fix data and compile_data for rust_doc (#1741)
927a364cb Update Release github pipeline to trigger automatically (#1757)
7d03e05f8 Fix release pipeline (#1756)
cf7ca5dfd Updated rules_rust to version `0.16.0` (#1750)
203fe4b9a Remove unnecessary binary file (#1755)
941c7cca9 Don't propagate `compatible_with` to the underlying `cargo_build_script` `rust_binary` target (#1754)
a31490d9a Make loads from @rules_rust//rust:defs.bzl come out on one line (#1753)
7ebad4d50 Generate only the needed subset of binaries for bindgen and proto (#1751)
4ef3d4aaa Repin examples/crate_universe_unnamed (#1752)
d6e300359 Regenerate BUILD files using serde_starlark renderer (#1746)
e7c8a97d1 Convert BUILD.$name-$version.bazel to serde_starlark (#1743)
c09818d3b Exclude generated files from language stats and collapse in code review (#1747)
26a24f030 Added CI for single toolchain channel workspaces (#1712)
caed7d814 Report context on error failing to get version (#1744)
36b57af7b Add gen_binaries annotation to control which bins to make target for (#1718)
d916a6f52 crate_universe re-pinning now defaults to "workspace" (#1723)
f34661ee1 Propagate `compatible_with` attribute to the underlying `_build_script_run` target (#1745)
92977d1bf Re-pinned all dependencies managed by crate_universe (#1735)
d5289ad1c Added `rustfmt_toolchain` and refactored toolchain repository rules (#1719)
532e60ff0 Collect targets in a deterministic order (#1736)
52e02c25b Eliminate all use of hash-based collections from crate_universe (#1737)
31073ff8e Replace tera template with serde_starlark (#1734)
d4e5586d0 Support the RUNFILES_DIR environment variable. (#1732)
1357b85b1 Addressed clippy warnings from `clippy 0.1.67 (ec56537c 2022-12-15)` (#1717)
8bc9f788d Support dsym_folder output group in tests (#1703)
90c5b6eb7 Added CI for minimum supported Rust version (#1720)
be82ff8bd Match prerelease versions with annotation wildcard (#1716)
36c7f285b Arm Thumb Embedded Targets. (#1721)
5ef52e465 Update current_toolchain_files tests to use a dedicated test rule (#1714)
c75ea6f9e Add `Runfiles::current_repository` to runfiles library (#1713)
2f0511782 Updated rules_rust to version `0.15.0` (#1706)
019f87178 Added Rust 1.66.0 (#1705)
1469cd7cb Fix labels to work with canonical label literals. (#1700)
5826a500a Add riscv32imc and riscv64gc to the known sha targets (#1698)
40dee95ce Fixed typos: normla -> normal (#1699)
8f08e77ac load_arbitrary_tool uses tool_suburl to look up sha256 (#1695)
8faec3060 Fix typos in crate_universe rendered comments (#1691)
bd64711ff Silence flaky test (#1693)
46b7ea5af Added a build setting for toolchain channels (#1671)
70b272aad Updated rules_rust to version `0.14.0` (#1669)
91e597dd1 Updated all crates_vendor outputs (#1687)
9a047b0b9 Updated crate_universe dependencies (#1686)
3a91d2f5b Add RV64GC target (#1683)
d9e752ab4 Add per-toolchain `rustc_flags` (#1635)
56237415e stardoc: Use backtick not `<code>` for attr default values  (#1682)
d4b31a494 Allow passing a bazel path to vendor explicitly (#1661)
d51bf9ce0 Updated crate_universe to work with `--nolegacy_external_runfiles` (#1680)
7f40636d1 crate_universe/private/crates_vendor.bzl typo fix (#1678)
025bf7db8 Merge cc toolchain flags into build script env (#1675)
b7c36c051 Fix confusing/misleading crate_universe docs (#1677)
29233e354 Revert #1564 (#1663)
ed32b6de2 Common glob excludes (#1673)
61b99cdd1 fix: add space to crate data exclude list (#1665)
8bb25b8b7 Support Windows ARM64 (aarch64-pc-windows-msvc) (#1664)
ddf2a4c23 Re-render crate BUILD files after #1647 (#1655)
44c7e1588 Group deps and aliases by platform triple rather than by cfg string when generating BUILD files. This avoid bazel errors due to duplicate keys/deps. (#1647)
de18d8bb6 Allow `buildifier` attribute to be a file (#1660)
aa0815dc9 Fix naming of ambiguous libs (#1625)
ff314d4ab Also pass -c opt to tests in opt mode CI (#1626)
ff4e90515 Reenable windows job (#1658)
c45b8e91f Updated rules_rust to version `0.13.0` (#1644)
87d6b6c37 Update `//util/label` to support `+` in packages (#1654)
ab6959db5 fix: Fix issue with wasi-0.11.0+wasi-snapshot-preview1 (#1632)
28c090ed0 Replaced custom platform constraint values with aliases to `@platforms` (#1652)
dfbea4f52 Deprecated `rust_toolchain.rustc_srcs` (#1653)
fd1db4391 Remove deprecated attributes from rust_toolchain and cargo_bootstrap (#1651)
c8ab970c4 Generated rust-project.json files now include sysroot paths (#1641)
0a3e04cf9 Fix vendoring when not in a package (#1646)
aece1e37d Deduplicate expand_location targets in rust-project.json crate creation to avoid a bazel crash (#1639)
03a0b2483 [docs] Fixing typos in CargoConfig doc strings (#1638)
bd4fd2ac5 Upgraded cfg-expr dependency to 0.12.0. (#1636)
330554a13 Disable failing job in CI (#1640)
849f106e6 Consider compilation mode when choosing `pic`/`nopic` object files to link (#1624)
53491d719 Updated rules_rust to version `0.12.0` (#1630)
8e8843724 Remove empty glob (#1628)
1f621a944 Added Rust 1.65.0 (#1627)
c6af4d025 Add `-c opt` mode to CI (#1621)
95320cc8b process_wrapper: print line on error to parse message as json (#1566)
81eaccf39 Fixed CI breakage (#1619)
478fc3a57 Fix ambiguous native dependencies in `proc_macro`s and `staticlib`s (#1611)
9e3d8415e Build deps of _build_script_run in 'exec' mode (#1561)
ea031082b Fixed outdated docs (#1614)
a8c540e49 Restore support for old cargo_build_script load statements (#1613)
295b5ccc7 Renamed `_build_script_run` rule to `cargo_build_script` (#1612)
3778069ec Remove references to Google mirror in docs (#1607)
aad54ba29 Updated crate_universe dependencies (#1606)
c349df2a6 Remove Google mirror from Starlark snippet in release notes (#1604)
0493b998d Avoid rendering a mock root package when possible (#1596)
b04aa053c process_wrapper: Apply substitutions to param files (#1565)
b209b3e15 Updated rules_rust to version `0.11.0`. (#1587)
b1079453b Typo correction on doc (#1593)
ca5678266 Updated crate_universe dependencies (#1591)
a364d448f Fixes crates_vendor labels in remote mode when used from the root workspace (#1575)
1cc37c268 Expose the output directory from cargo_build_script (#1588)
7ffe0a555 Ignore non-utf8 text in build script output (#1583)
c5b38fe00 Merge runfiles from transitive dependencies of rust_test crate attr (#1487)
da3d522d5 Fix build scripts targeting the wrong architecture (#1564)
d288ed634 Add `out_dir` support in `cargo_dep_env` (#1571)
78d6c1b46 fix: incorrect rustfmt edition query (#1582)
48927127e Set CARGO_MANIFEST_DIR at runtime for tests (#1559)
76bd69033 Add an output group for the .rmeta (#1585)
352bfeb05 Cleanup deprecated code (#1577)
86dc561f9 Move crate_root_src to utils (#1570)
beb554eb9 update to wasm-bindgen v0.2.83 (#1567)
73fd1616b Export AbsoluteLabel functionality (#1568)
c57e7a399 Remap $PWD to empty string instead of "." (#1563)
f0cdcedc2 Added Rust 1.64.0 (#1562)
1d326554a Update docs to show release policies and support (#1560)
78c793a0a Fix markdown typo in rust_analyzer.md (#1553)
c13980fb6 Add iOS examples (#1546)
8a5e07e9f Update apple_support (#1549)
6dacd9803 Strip leading '@'s for labels in the splicing manifest (#1547)
f73d1d6fb use crate_info.deps in establish_cc_info (#1543)
4845af6c0 Add android example (#1545)
9570b7aa7 Remove -lgcc from Android builds (#1541)
cb9ca1b81 Fix crate_universe/private/srcs.bzl to work with repo mappings (#1540)
d1fc9accc Minor cleanup of CI pipelines (#1534)
2bb077b3b Updated rules_rust to version 0.10.0 (#1533)
b8751b860 add cc config info to dummy wasm32 cc toolchain (#1532)
f5ed797ee Updates rust_test to use main.rs as the root when use_libtest_harness is false (#1518)
cfcaf21d5 Preserve directory structure of source files when some are generated (#1526)
51c065841 migrating to rbe_preconfig and remove bazel_toolchains (#1524)
055abd402 Fix typo in an example of crates_repository rule (#1520)
8bfed1cd2 Added Rust 1.63.0 (#1512)
3a69ce09b Update wasm_bindgen to 0.2.82 (#1513)

git-subtree-dir: third_party/rules_rust
git-subtree-split: bf9ddeb7c83a9fe8a7d87c76134cdd8e16131b62
Signed-off-by: Adam Snaider <adsnaider@gmail.com>
Change-Id: Id9490c68d6221da66953a915a25042ef8b848505
diff --git a/crate_universe/src/rendering.rs b/crate_universe/src/rendering.rs
index c4cc249..1e9818d 100644
--- a/crate_universe/src/rendering.rs
+++ b/crate_universe/src/rendering.rs
@@ -2,35 +2,59 @@
 
 mod template_engine;
 
-use std::collections::BTreeMap;
+use std::collections::{BTreeMap, BTreeSet};
 use std::fs;
+use std::iter::FromIterator;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
 use anyhow::{bail, Context as AnyhowContext, Result};
+use indoc::formatdoc;
 
-use crate::config::RenderConfig;
-use crate::context::Context;
+use crate::config::{RenderConfig, VendorMode};
+use crate::context::crate_context::{CrateContext, CrateDependency, Rule};
+use crate::context::{Context, TargetAttributes};
 use crate::rendering::template_engine::TemplateEngine;
 use crate::splicing::default_splicing_package_crate_id;
-use crate::utils::starlark::Label;
+use crate::utils::starlark::{
+    self, Alias, CargoBuildScript, CommonAttrs, Data, ExportsFiles, Filegroup, Glob, Label, Load,
+    Package, RustBinary, RustLibrary, RustProcMacro, Select, SelectDict, SelectList, SelectMap,
+    Starlark, TargetCompatibleWith,
+};
+use crate::utils::{self, sanitize_repository_name};
+
+// Configuration remapper used to convert from cfg expressions like "cfg(unix)"
+// to platform labels like "@rules_rust//rust/platform:x86_64-unknown-linux-gnu".
+pub(crate) type Platforms = BTreeMap<String, BTreeSet<String>>;
 
 pub struct Renderer {
     config: RenderConfig,
+    supported_platform_triples: BTreeSet<String>,
+    generate_target_compatible_with: bool,
     engine: TemplateEngine,
 }
 
 impl Renderer {
-    pub fn new(config: RenderConfig) -> Self {
+    pub fn new(
+        config: RenderConfig,
+        supported_platform_triples: BTreeSet<String>,
+        generate_target_compatible_with: bool,
+    ) -> Self {
         let engine = TemplateEngine::new(&config);
-        Self { config, engine }
+        Self {
+            config,
+            supported_platform_triples,
+            generate_target_compatible_with,
+            engine,
+        }
     }
 
     pub fn render(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>> {
         let mut output = BTreeMap::new();
 
-        output.extend(self.render_build_files(context)?);
-        output.extend(self.render_crates_module(context)?);
+        let platforms = self.render_platform_labels(context);
+        output.extend(self.render_build_files(context, &platforms)?);
+        output.extend(self.render_crates_module(context, &platforms)?);
 
         if let Some(vendor_mode) = &self.config.vendor_mode {
             match vendor_mode {
@@ -46,7 +70,32 @@
         Ok(output)
     }
 
-    fn render_crates_module(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>> {
+    fn render_platform_labels(&self, context: &Context) -> BTreeMap<String, BTreeSet<String>> {
+        context
+            .conditions
+            .iter()
+            .map(|(cfg, triples)| {
+                (
+                    cfg.clone(),
+                    triples
+                        .iter()
+                        .map(|triple| {
+                            render_platform_constraint_label(
+                                &self.config.platforms_template,
+                                triple,
+                            )
+                        })
+                        .collect(),
+                )
+            })
+            .collect()
+    }
+
+    fn render_crates_module(
+        &self,
+        context: &Context,
+        platforms: &Platforms,
+    ) -> Result<BTreeMap<PathBuf, String>> {
         let module_label = render_module_label(&self.config.crates_module_template, "defs.bzl")
             .context("Failed to resolve string to module file label")?;
         let module_build_label =
@@ -56,43 +105,510 @@
         let mut map = BTreeMap::new();
         map.insert(
             Renderer::label_to_path(&module_label),
-            self.engine.render_module_bzl(context)?,
+            self.engine.render_module_bzl(context, platforms)?,
         );
         map.insert(
             Renderer::label_to_path(&module_build_label),
-            self.engine.render_module_build_file(context)?,
+            self.render_module_build_file(context)?,
         );
 
         Ok(map)
     }
 
-    fn render_build_files(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>> {
+    fn render_module_build_file(&self, context: &Context) -> Result<String> {
+        let mut starlark = Vec::new();
+
+        // Banner comment for top of the file.
+        let header = self.engine.render_header()?;
+        starlark.push(Starlark::Verbatim(header));
+
+        // Package visibility, exported bzl files.
+        let package = Package::default_visibility_public();
+        starlark.push(Starlark::Package(package));
+
+        let mut exports_files = ExportsFiles {
+            paths: BTreeSet::from(["cargo-bazel.json".to_owned(), "defs.bzl".to_owned()]),
+            globs: Glob {
+                include: BTreeSet::from(["*.bazel".to_owned()]),
+                exclude: BTreeSet::new(),
+            },
+        };
+        if let Some(VendorMode::Remote) = self.config.vendor_mode {
+            exports_files.paths.insert("crates.bzl".to_owned());
+        }
+        starlark.push(Starlark::ExportsFiles(exports_files));
+
+        let filegroup = Filegroup {
+            name: "srcs".to_owned(),
+            srcs: Glob {
+                include: BTreeSet::from(["*.bazel".to_owned(), "*.bzl".to_owned()]),
+                exclude: BTreeSet::new(),
+            },
+        };
+        starlark.push(Starlark::Filegroup(filegroup));
+
+        // An `alias` for each direct dependency of a workspace member crate.
+        let mut dependencies = Vec::new();
+        for dep in context.workspace_member_deps() {
+            let krate = &context.crates[&dep.id];
+            if let Some(library_target_name) = &krate.library_target_name {
+                let rename = dep.alias.as_ref().unwrap_or(&krate.name);
+                dependencies.push(Alias {
+                    // If duplicates exist, include version to disambiguate them.
+                    name: if context.has_duplicate_workspace_member_dep(dep) {
+                        format!("{}-{}", rename, krate.version)
+                    } else {
+                        rename.clone()
+                    },
+                    actual: self.crate_label(&krate.name, &krate.version, library_target_name),
+                    tags: BTreeSet::from(["manual".to_owned()]),
+                });
+            }
+        }
+        if !dependencies.is_empty() {
+            let comment = "# Workspace Member Dependencies".to_owned();
+            starlark.push(Starlark::Verbatim(comment));
+            starlark.extend(dependencies.into_iter().map(Starlark::Alias));
+        }
+
+        // An `alias` for each binary dependency.
+        let mut binaries = Vec::new();
+        for crate_id in &context.binary_crates {
+            let krate = &context.crates[crate_id];
+            for rule in &krate.targets {
+                if let Rule::Binary(bin) = rule {
+                    binaries.push(Alias {
+                        // If duplicates exist, include version to disambiguate them.
+                        name: if context.has_duplicate_binary_crate(crate_id) {
+                            format!("{}-{}__{}", krate.name, krate.version, bin.crate_name)
+                        } else {
+                            format!("{}__{}", krate.name, bin.crate_name)
+                        },
+                        actual: self.crate_label(
+                            &krate.name,
+                            &krate.version,
+                            &format!("{}__bin", bin.crate_name),
+                        ),
+                        tags: BTreeSet::from(["manual".to_owned()]),
+                    });
+                }
+            }
+        }
+        if !binaries.is_empty() {
+            let comment = "# Binaries".to_owned();
+            starlark.push(Starlark::Verbatim(comment));
+            starlark.extend(binaries.into_iter().map(Starlark::Alias));
+        }
+
+        let starlark = starlark::serialize(&starlark)?;
+        Ok(starlark)
+    }
+
+    fn render_build_files(
+        &self,
+        context: &Context,
+        platforms: &Platforms,
+    ) -> Result<BTreeMap<PathBuf, String>> {
         let default_splicing_package_id = default_splicing_package_crate_id();
-        self.engine
-            .render_crate_build_files(context)?
-            .into_iter()
+        context
+            .crates
+            .keys()
             // Do not render the default splicing package
-            .filter(|(id, _)| *id != &default_splicing_package_id)
+            .filter(|id| *id != &default_splicing_package_id)
             // Do not render local packages
-            .filter(|(id, _)| !context.workspace_members.contains_key(id))
-            .map(|(id, content)| {
-                let ctx = &context.crates[id];
+            .filter(|id| !context.workspace_members.contains_key(id))
+            .map(|id| {
                 let label = match render_build_file_template(
                     &self.config.build_file_template,
-                    &ctx.name,
-                    &ctx.version,
+                    &id.name,
+                    &id.version,
                 ) {
                     Ok(label) => label,
                     Err(e) => bail!(e),
                 };
 
                 let filename = Renderer::label_to_path(&label);
-
+                let content = self.render_one_build_file(platforms, &context.crates[id])?;
                 Ok((filename, content))
             })
             .collect()
     }
 
+    fn render_one_build_file(&self, platforms: &Platforms, krate: &CrateContext) -> Result<String> {
+        let mut starlark = Vec::new();
+
+        // Banner comment for top of the file.
+        let header = self.engine.render_header()?;
+        starlark.push(Starlark::Verbatim(header));
+
+        // Loads: map of bzl file to set of items imported from that file. These
+        // get inserted into `starlark` at the bottom of this function.
+        let mut loads: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
+        let mut load = |bzl: &str, item: &str| {
+            loads
+                .entry(bzl.to_owned())
+                .or_default()
+                .insert(item.to_owned())
+        };
+
+        let disable_visibility = "# buildifier: disable=bzl-visibility".to_owned();
+        starlark.push(Starlark::Verbatim(disable_visibility));
+        starlark.push(Starlark::Load(Load {
+            bzl: "@rules_rust//crate_universe/private:selects.bzl".to_owned(),
+            items: BTreeSet::from(["selects".to_owned()]),
+        }));
+
+        // Package visibility.
+        let package = Package::default_visibility_public();
+        starlark.push(Starlark::Package(package));
+
+        if let Some(license) = &krate.license {
+            starlark.push(Starlark::Verbatim(formatdoc! {r#"
+                # licenses([
+                #     "TODO",  # {license}
+                # ])
+            "#}));
+        }
+
+        for rule in &krate.targets {
+            match rule {
+                Rule::BuildScript(target) => {
+                    load("@rules_rust//cargo:defs.bzl", "cargo_build_script");
+                    let cargo_build_script =
+                        self.make_cargo_build_script(platforms, krate, target)?;
+                    starlark.push(Starlark::CargoBuildScript(cargo_build_script));
+                    starlark.push(Starlark::Alias(Alias {
+                        name: target.crate_name.clone(),
+                        actual: format!("{}_build_script", krate.name),
+                        tags: BTreeSet::from(["manual".to_owned()]),
+                    }));
+                }
+                Rule::ProcMacro(target) => {
+                    load("@rules_rust//rust:defs.bzl", "rust_proc_macro");
+                    let rust_proc_macro = self.make_rust_proc_macro(platforms, krate, target)?;
+                    starlark.push(Starlark::RustProcMacro(rust_proc_macro));
+                }
+                Rule::Library(target) => {
+                    load("@rules_rust//rust:defs.bzl", "rust_library");
+                    let rust_library = self.make_rust_library(platforms, krate, target)?;
+                    starlark.push(Starlark::RustLibrary(rust_library));
+                }
+                Rule::Binary(target) => {
+                    load("@rules_rust//rust:defs.bzl", "rust_binary");
+                    let rust_binary = self.make_rust_binary(platforms, krate, target)?;
+                    starlark.push(Starlark::RustBinary(rust_binary));
+                }
+            }
+        }
+
+        if let Some(additive_build_file_content) = &krate.additive_build_file_content {
+            let comment = "# Additive BUILD file content".to_owned();
+            starlark.push(Starlark::Verbatim(comment));
+            starlark.push(Starlark::Verbatim(additive_build_file_content.clone()));
+        }
+
+        // Insert all the loads immediately after the header banner comment.
+        let loads = loads
+            .into_iter()
+            .map(|(bzl, items)| Starlark::Load(Load { bzl, items }));
+        starlark.splice(1..1, loads);
+
+        let starlark = starlark::serialize(&starlark)?;
+        Ok(starlark)
+    }
+
+    fn make_cargo_build_script(
+        &self,
+        platforms: &Platforms,
+        krate: &CrateContext,
+        target: &TargetAttributes,
+    ) -> Result<CargoBuildScript> {
+        let empty_set = BTreeSet::<String>::new();
+        let empty_list = SelectList::<String>::default();
+        let empty_deps = SelectList::<CrateDependency>::default();
+        let attrs = krate.build_script_attrs.as_ref();
+
+        Ok(CargoBuildScript {
+            // Because `cargo_build_script` does some invisible target name
+            // mutating to determine the package and crate name for a build
+            // script, the Bazel target name of any build script cannot be the
+            // Cargo canonical name of "cargo_build_script" without losing out
+            // on having certain Cargo environment variables set.
+            //
+            // Do not change this name to "cargo_build_script".
+            name: format!("{}_build_script", krate.name),
+            aliases: self
+                .make_aliases(krate, true, false)
+                .remap_configurations(platforms),
+            build_script_env: attrs
+                .map_or_else(SelectDict::default, |attrs| attrs.build_script_env.clone())
+                .remap_configurations(platforms),
+            compile_data: make_data(
+                platforms,
+                &empty_set,
+                attrs.map_or(&empty_list, |attrs| &attrs.compile_data),
+            ),
+            crate_features: SelectList::from(&krate.common_attrs.crate_features)
+                .map_configuration_names(|triple| {
+                    render_platform_constraint_label(&self.config.platforms_template, &triple)
+                }),
+            crate_name: utils::sanitize_module_name(&target.crate_name),
+            crate_root: target.crate_root.clone(),
+            data: make_data(
+                platforms,
+                attrs.map_or(&empty_set, |attrs| &attrs.data_glob),
+                attrs.map_or(&empty_list, |attrs| &attrs.data),
+            ),
+            deps: self
+                .make_deps(
+                    attrs.map_or(&empty_deps, |attrs| &attrs.deps),
+                    attrs.map_or(&empty_set, |attrs| &attrs.extra_deps),
+                )
+                .remap_configurations(platforms),
+            edition: krate.common_attrs.edition.clone(),
+            linker_script: krate.common_attrs.linker_script.clone(),
+            links: attrs.and_then(|attrs| attrs.links.clone()),
+            proc_macro_deps: self
+                .make_deps(
+                    attrs.map_or(&empty_deps, |attrs| &attrs.proc_macro_deps),
+                    attrs.map_or(&empty_set, |attrs| &attrs.extra_proc_macro_deps),
+                )
+                .remap_configurations(platforms),
+            rustc_env: attrs
+                .map_or_else(SelectDict::default, |attrs| attrs.rustc_env.clone())
+                .remap_configurations(platforms),
+            rustc_env_files: attrs
+                .map_or_else(SelectList::default, |attrs| attrs.rustc_env_files.clone())
+                .remap_configurations(platforms),
+            rustc_flags: {
+                let mut rustc_flags =
+                    attrs.map_or_else(SelectList::default, |attrs| attrs.rustc_flags.clone());
+                // In most cases, warnings in 3rd party crates are not
+                // interesting as they're out of the control of consumers. The
+                // flag here silences warnings. For more details see:
+                // https://doc.rust-lang.org/rustc/lints/levels.html
+                rustc_flags.insert("--cap-lints=allow".to_owned(), None);
+                rustc_flags.remap_configurations(platforms)
+            },
+            srcs: target.srcs.clone(),
+            tags: {
+                let mut tags = BTreeSet::from_iter(krate.common_attrs.tags.iter().cloned());
+                tags.insert("cargo-bazel".to_owned());
+                tags.insert("manual".to_owned());
+                tags.insert("noclippy".to_owned());
+                tags.insert("norustfmt".to_owned());
+                tags.insert(format!("crate-name={}", krate.name));
+                tags
+            },
+            tools: attrs
+                .map_or_else(SelectList::default, |attrs| attrs.tools.clone())
+                .remap_configurations(platforms),
+            toolchains: attrs.map_or_else(BTreeSet::new, |attrs| attrs.toolchains.clone()),
+            version: krate.common_attrs.version.clone(),
+            visibility: BTreeSet::from(["//visibility:private".to_owned()]),
+        })
+    }
+
+    fn make_rust_proc_macro(
+        &self,
+        platforms: &Platforms,
+        krate: &CrateContext,
+        target: &TargetAttributes,
+    ) -> Result<RustProcMacro> {
+        Ok(RustProcMacro {
+            name: target.crate_name.clone(),
+            deps: self
+                .make_deps(&krate.common_attrs.deps, &krate.common_attrs.extra_deps)
+                .remap_configurations(platforms),
+            proc_macro_deps: self
+                .make_deps(
+                    &krate.common_attrs.proc_macro_deps,
+                    &krate.common_attrs.extra_proc_macro_deps,
+                )
+                .remap_configurations(platforms),
+            aliases: self
+                .make_aliases(krate, false, false)
+                .remap_configurations(platforms),
+            common: self.make_common_attrs(platforms, krate, target)?,
+        })
+    }
+
+    fn make_rust_library(
+        &self,
+        platforms: &Platforms,
+        krate: &CrateContext,
+        target: &TargetAttributes,
+    ) -> Result<RustLibrary> {
+        Ok(RustLibrary {
+            name: target.crate_name.clone(),
+            deps: self
+                .make_deps(&krate.common_attrs.deps, &krate.common_attrs.extra_deps)
+                .remap_configurations(platforms),
+            proc_macro_deps: self
+                .make_deps(
+                    &krate.common_attrs.proc_macro_deps,
+                    &krate.common_attrs.extra_proc_macro_deps,
+                )
+                .remap_configurations(platforms),
+            aliases: self
+                .make_aliases(krate, false, false)
+                .remap_configurations(platforms),
+            common: self.make_common_attrs(platforms, krate, target)?,
+            disable_pipelining: krate.disable_pipelining,
+        })
+    }
+
+    fn make_rust_binary(
+        &self,
+        platforms: &Platforms,
+        krate: &CrateContext,
+        target: &TargetAttributes,
+    ) -> Result<RustBinary> {
+        Ok(RustBinary {
+            name: format!("{}__bin", target.crate_name),
+            deps: {
+                let mut deps =
+                    self.make_deps(&krate.common_attrs.deps, &krate.common_attrs.extra_deps);
+                if let Some(library_target_name) = &krate.library_target_name {
+                    deps.insert(format!(":{library_target_name}"), None);
+                }
+                deps.remap_configurations(platforms)
+            },
+            proc_macro_deps: self
+                .make_deps(
+                    &krate.common_attrs.proc_macro_deps,
+                    &krate.common_attrs.extra_proc_macro_deps,
+                )
+                .remap_configurations(platforms),
+            aliases: self
+                .make_aliases(krate, false, false)
+                .remap_configurations(platforms),
+            common: self.make_common_attrs(platforms, krate, target)?,
+        })
+    }
+
+    fn make_common_attrs(
+        &self,
+        platforms: &Platforms,
+        krate: &CrateContext,
+        target: &TargetAttributes,
+    ) -> Result<CommonAttrs> {
+        Ok(CommonAttrs {
+            compile_data: make_data(
+                platforms,
+                &krate.common_attrs.compile_data_glob,
+                &krate.common_attrs.compile_data,
+            ),
+            crate_features: SelectList::from(&krate.common_attrs.crate_features)
+                .map_configuration_names(|triple| {
+                    render_platform_constraint_label(&self.config.platforms_template, &triple)
+                }),
+            crate_root: target.crate_root.clone(),
+            data: make_data(
+                platforms,
+                &krate.common_attrs.data_glob,
+                &krate.common_attrs.data,
+            ),
+            edition: krate.common_attrs.edition.clone(),
+            linker_script: krate.common_attrs.linker_script.clone(),
+            rustc_env: krate
+                .common_attrs
+                .rustc_env
+                .clone()
+                .remap_configurations(platforms),
+            rustc_env_files: krate
+                .common_attrs
+                .rustc_env_files
+                .clone()
+                .remap_configurations(platforms),
+            rustc_flags: {
+                let mut rustc_flags = krate.common_attrs.rustc_flags.clone();
+                // In most cases, warnings in 3rd party crates are not
+                // interesting as they're out of the control of consumers. The
+                // flag here silences warnings. For more details see:
+                // https://doc.rust-lang.org/rustc/lints/levels.html
+                rustc_flags.insert(0, "--cap-lints=allow".to_owned());
+                rustc_flags
+            },
+            srcs: target.srcs.clone(),
+            tags: {
+                let mut tags = BTreeSet::from_iter(krate.common_attrs.tags.iter().cloned());
+                tags.insert("cargo-bazel".to_owned());
+                tags.insert("manual".to_owned());
+                tags.insert("noclippy".to_owned());
+                tags.insert("norustfmt".to_owned());
+                tags.insert(format!("crate-name={}", krate.name));
+                tags
+            },
+            target_compatible_with: self.generate_target_compatible_with.then(|| {
+                TargetCompatibleWith::new(
+                    self.supported_platform_triples
+                        .iter()
+                        .map(|triple| {
+                            render_platform_constraint_label(
+                                &self.config.platforms_template,
+                                triple,
+                            )
+                        })
+                        .collect(),
+                )
+            }),
+            version: krate.common_attrs.version.clone(),
+        })
+    }
+
+    /// Filter a crate's dependencies to only ones with aliases
+    fn make_aliases(
+        &self,
+        krate: &CrateContext,
+        build: bool,
+        include_dev: bool,
+    ) -> SelectDict<String> {
+        let mut dep_lists = Vec::new();
+        if build {
+            if let Some(build_script_attrs) = &krate.build_script_attrs {
+                dep_lists.push(&build_script_attrs.deps);
+                dep_lists.push(&build_script_attrs.proc_macro_deps);
+            }
+        } else {
+            dep_lists.push(&krate.common_attrs.deps);
+            dep_lists.push(&krate.common_attrs.proc_macro_deps);
+            if include_dev {
+                dep_lists.push(&krate.common_attrs.deps_dev);
+                dep_lists.push(&krate.common_attrs.proc_macro_deps_dev);
+            }
+        }
+
+        let mut aliases = SelectDict::default();
+        for (dep, conf) in dep_lists.into_iter().flat_map(|deps| {
+            deps.configurations().into_iter().flat_map(move |conf| {
+                deps.get_iter(conf)
+                    .expect("Iterating over known keys should never panic")
+                    .map(move |dep| (dep, conf))
+            })
+        }) {
+            if let Some(alias) = &dep.alias {
+                let label = self.crate_label(&dep.id.name, &dep.id.version, &dep.target);
+                aliases.insert(label, alias.clone(), conf.cloned());
+            }
+        }
+        aliases
+    }
+
+    fn make_deps(
+        &self,
+        deps: &SelectList<CrateDependency>,
+        extra_deps: &BTreeSet<String>,
+    ) -> SelectList<String> {
+        let mut deps = deps
+            .clone()
+            .map(|dep| self.crate_label(&dep.id.name, &dep.id.version, &dep.target));
+        for extra_dep in extra_deps {
+            deps.insert(extra_dep.clone(), None);
+        }
+        deps
+    }
+
     fn render_vendor_support_files(&self, context: &Context) -> Result<BTreeMap<PathBuf, String>> {
         let module_label = render_module_label(&self.config.crates_module_template, "crates.bzl")
             .context("Failed to resolve string to module file label")?;
@@ -112,9 +628,19 @@
             None => PathBuf::from(&label.target),
         }
     }
+
+    fn crate_label(&self, name: &str, version: &str, target: &str) -> String {
+        sanitize_repository_name(&render_crate_bazel_label(
+            &self.config.crate_label_template,
+            &self.config.repository_name,
+            name,
+            version,
+            target,
+        ))
+    }
 }
 
-/// Write a set of [CrateContext][crate::context::CrateContext] to disk.
+/// Write a set of [crate::context::crate_context::CrateContext] to disk.
 pub fn write_outputs(
     outputs: BTreeMap<PathBuf, String>,
     out_dir: &Path,
@@ -134,7 +660,7 @@
             println!(
                 "==============================================================================="
             );
-            println!("{}\n", content);
+            println!("{content}\n");
         }
     } else {
         for (path, content) in outputs {
@@ -193,7 +719,7 @@
 }
 
 /// Render the Bazel label of a platform triple
-pub fn render_platform_constraint_label(template: &str, triple: &str) -> String {
+fn render_platform_constraint_label(template: &str, triple: &str) -> String {
     template.replace("{triple}", triple)
 }
 
@@ -205,23 +731,43 @@
     )
 }
 
+fn make_data(platforms: &Platforms, glob: &BTreeSet<String>, select: &SelectList<String>) -> Data {
+    const COMMON_GLOB_EXCLUDES: &[&str] = &[
+        "**/* *",
+        "BUILD.bazel",
+        "BUILD",
+        "WORKSPACE.bazel",
+        "WORKSPACE",
+        ".tmp_git_root/**/*",
+    ];
+
+    Data {
+        glob: Glob {
+            include: glob.clone(),
+            exclude: COMMON_GLOB_EXCLUDES
+                .iter()
+                .map(|&glob| glob.to_owned())
+                .collect(),
+        },
+        select: select.clone().remap_configurations(platforms),
+    }
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
 
+    use indoc::indoc;
+    use std::collections::BTreeSet;
+
     use crate::config::{Config, CrateId, VendorMode};
     use crate::context::crate_context::{CrateContext, Rule};
-    use crate::context::{BuildScriptAttributes, CommonAttributes, Context, TargetAttributes};
+    use crate::context::{
+        BuildScriptAttributes, CommonAttributes, Context, CrateFeatures, TargetAttributes,
+    };
     use crate::metadata::Annotations;
     use crate::test;
-
-    fn mock_render_config() -> RenderConfig {
-        serde_json::from_value(serde_json::json!({
-            "repository_name": "test_rendering",
-            "regen_command": "cargo_bazel_regen_command",
-        }))
-        .unwrap()
-    }
+    use crate::utils::starlark::SelectList;
 
     fn mock_target_attributes() -> TargetAttributes {
         TargetAttributes {
@@ -231,6 +777,42 @@
         }
     }
 
+    fn mock_render_config(vendor_mode: Option<VendorMode>) -> RenderConfig {
+        RenderConfig {
+            repository_name: "test_rendering".to_owned(),
+            regen_command: "cargo_bazel_regen_command".to_owned(),
+            vendor_mode,
+            ..RenderConfig::default()
+        }
+    }
+
+    fn mock_supported_platform_triples() -> BTreeSet<String> {
+        BTreeSet::from([
+            "aarch64-apple-darwin".to_owned(),
+            "aarch64-apple-ios".to_owned(),
+            "aarch64-linux-android".to_owned(),
+            "aarch64-pc-windows-msvc".to_owned(),
+            "aarch64-unknown-linux-gnu".to_owned(),
+            "arm-unknown-linux-gnueabi".to_owned(),
+            "armv7-unknown-linux-gnueabi".to_owned(),
+            "i686-apple-darwin".to_owned(),
+            "i686-linux-android".to_owned(),
+            "i686-pc-windows-msvc".to_owned(),
+            "i686-unknown-freebsd".to_owned(),
+            "i686-unknown-linux-gnu".to_owned(),
+            "powerpc-unknown-linux-gnu".to_owned(),
+            "s390x-unknown-linux-gnu".to_owned(),
+            "wasm32-unknown-unknown".to_owned(),
+            "wasm32-wasi".to_owned(),
+            "x86_64-apple-darwin".to_owned(),
+            "x86_64-apple-ios".to_owned(),
+            "x86_64-linux-android".to_owned(),
+            "x86_64-pc-windows-msvc".to_owned(),
+            "x86_64-unknown-freebsd".to_owned(),
+            "x86_64-unknown-linux-gnu".to_owned(),
+        ])
+    }
+
     #[test]
     fn render_rust_library() {
         let mut context = Context::default();
@@ -240,12 +822,16 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::Library(mock_target_attributes())],
+                targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
                 ..CrateContext::default()
             },
         );
 
-        let renderer = Renderer::new(mock_render_config());
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let build_file_content = output
@@ -254,6 +840,36 @@
 
         assert!(build_file_content.contains("rust_library("));
         assert!(build_file_content.contains("name = \"mock_crate\""));
+        assert!(build_file_content.contains("\"crate-name=mock_crate\""));
+    }
+
+    #[test]
+    fn test_disable_pipelining() {
+        let mut context = Context::default();
+        let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
+        context.crates.insert(
+            crate_id.clone(),
+            CrateContext {
+                name: crate_id.name,
+                version: crate_id.version,
+                targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
+                disable_pipelining: true,
+                ..CrateContext::default()
+            },
+        );
+
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
+        let output = renderer.render(&context).unwrap();
+
+        let build_file_content = output
+            .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
+            .unwrap();
+
+        assert!(build_file_content.contains("disable_pipelining = True"));
     }
 
     #[test]
@@ -265,18 +881,22 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::BuildScript(TargetAttributes {
+                targets: BTreeSet::from([Rule::BuildScript(TargetAttributes {
                     crate_name: "build_script_build".to_owned(),
                     crate_root: Some("build.rs".to_owned()),
                     ..TargetAttributes::default()
-                })],
+                })]),
                 // Build script attributes are required.
                 build_script_attrs: Some(BuildScriptAttributes::default()),
                 ..CrateContext::default()
             },
         );
 
-        let renderer = Renderer::new(mock_render_config());
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let build_file_content = output
@@ -285,6 +905,7 @@
 
         assert!(build_file_content.contains("cargo_build_script("));
         assert!(build_file_content.contains("name = \"build_script_build\""));
+        assert!(build_file_content.contains("\"crate-name=mock_crate\""));
 
         // Ensure `cargo_build_script` requirements are met
         assert!(build_file_content.contains("name = \"mock_crate_build_script\""));
@@ -299,12 +920,16 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::ProcMacro(mock_target_attributes())],
+                targets: BTreeSet::from([Rule::ProcMacro(mock_target_attributes())]),
                 ..CrateContext::default()
             },
         );
 
-        let renderer = Renderer::new(mock_render_config());
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let build_file_content = output
@@ -313,6 +938,7 @@
 
         assert!(build_file_content.contains("rust_proc_macro("));
         assert!(build_file_content.contains("name = \"mock_crate\""));
+        assert!(build_file_content.contains("\"crate-name=mock_crate\""));
     }
 
     #[test]
@@ -324,12 +950,16 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::Binary(mock_target_attributes())],
+                targets: BTreeSet::from([Rule::Binary(mock_target_attributes())]),
                 ..CrateContext::default()
             },
         );
 
-        let renderer = Renderer::new(mock_render_config());
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let build_file_content = output
@@ -338,6 +968,7 @@
 
         assert!(build_file_content.contains("rust_binary("));
         assert!(build_file_content.contains("name = \"mock_crate__bin\""));
+        assert!(build_file_content.contains("\"crate-name=mock_crate\""));
     }
 
     #[test]
@@ -349,7 +980,7 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::Binary(mock_target_attributes())],
+                targets: BTreeSet::from([Rule::Binary(mock_target_attributes())]),
                 additive_build_file_content: Some(
                     "# Hello World from additive section!".to_owned(),
                 ),
@@ -357,7 +988,11 @@
             },
         );
 
-        let renderer = Renderer::new(mock_render_config());
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let build_file_content = output
@@ -369,15 +1004,19 @@
 
     #[test]
     fn render_aliases() {
-        let annotations = Annotations::new(
-            test::metadata::alias(),
-            test::lockfile::alias(),
-            Config::default(),
-        )
-        .unwrap();
+        let config = Config {
+            generate_binaries: true,
+            ..Config::default()
+        };
+        let annotations =
+            Annotations::new(test::metadata::alias(), test::lockfile::alias(), config).unwrap();
         let context = Context::new(annotations).unwrap();
 
-        let renderer = Renderer::new(mock_render_config());
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let build_file_content = output.get(&PathBuf::from("BUILD.bazel")).unwrap();
@@ -395,12 +1034,16 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::Library(mock_target_attributes())],
+                targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
                 ..CrateContext::default()
             },
         );
 
-        let renderer = Renderer::new(mock_render_config());
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
@@ -417,18 +1060,17 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::Library(mock_target_attributes())],
+                targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
                 ..CrateContext::default()
             },
         );
 
         // Enable remote vendor mode
-        let config = RenderConfig {
-            vendor_mode: Some(VendorMode::Remote),
-            ..mock_render_config()
-        };
-
-        let renderer = Renderer::new(config);
+        let renderer = Renderer::new(
+            mock_render_config(Some(VendorMode::Remote)),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let defs_module = output.get(&PathBuf::from("defs.bzl")).unwrap();
@@ -447,18 +1089,17 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::Library(mock_target_attributes())],
+                targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
                 ..CrateContext::default()
             },
         );
 
         // Enable local vendor mode
-        let config = RenderConfig {
-            vendor_mode: Some(VendorMode::Local),
-            ..mock_render_config()
-        };
-
-        let renderer = Renderer::new(config);
+        let renderer = Renderer::new(
+            mock_render_config(Some(VendorMode::Local)),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         // Local vendoring does not produce a `crate_repositories` macro
@@ -486,7 +1127,7 @@
             CrateContext {
                 name: crate_id.name,
                 version: crate_id.version,
-                targets: vec![Rule::Library(mock_target_attributes())],
+                targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
                 common_attrs: CommonAttributes {
                     rustc_flags: rustc_flags.clone(),
                     ..CommonAttributes::default()
@@ -496,12 +1137,11 @@
         );
 
         // Enable local vendor mode
-        let config = RenderConfig {
-            vendor_mode: Some(VendorMode::Local),
-            ..mock_render_config()
-        };
-
-        let renderer = Renderer::new(config);
+        let renderer = Renderer::new(
+            mock_render_config(Some(VendorMode::Local)),
+            mock_supported_platform_triples(),
+            true,
+        );
         let output = renderer.render(&context).unwrap();
 
         let build_file_content = output
@@ -513,9 +1153,143 @@
         assert!(build_file_content.replace(' ', "").contains(
             &rustc_flags
                 .iter()
-                .map(|s| format!("\"{}\",", s))
+                .map(|s| format!("\"{s}\","))
                 .collect::<Vec<String>>()
                 .join("\n")
         ));
     }
+
+    #[test]
+    fn test_render_build_file_deps() {
+        let config: Config = serde_json::from_value(serde_json::json!({
+            "generate_binaries": false,
+            "generate_build_scripts": false,
+            "rendering": {
+                "repository_name": "multi_cfg_dep",
+                "regen_command": "bazel test //crate_universe:unit_test",
+            },
+            "supported_platform_triples": [
+                "x86_64-apple-darwin",
+                "x86_64-unknown-linux-gnu",
+                "aarch64-apple-darwin",
+                "aarch64-unknown-linux-gnu",
+            ],
+        }))
+        .unwrap();
+        let metadata = test::metadata::multi_cfg_dep();
+        let lockfile = test::lockfile::multi_cfg_dep();
+
+        let annotations = Annotations::new(metadata, lockfile, config.clone()).unwrap();
+        let context = Context::new(annotations).unwrap();
+
+        let renderer = Renderer::new(config.rendering, config.supported_platform_triples, true);
+        let output = renderer.render(&context).unwrap();
+
+        let build_file_content = output
+            .get(&PathBuf::from("BUILD.cpufeatures-0.2.7.bazel"))
+            .unwrap();
+
+        // This is unfortunately somewhat brittle. Alas. Ultimately we wish to demonstrate that the
+        // original cfg(...) strings are preserved in the `deps` list for ease of debugging.
+        let expected = indoc! {r#"
+            deps = select({
+                "@rules_rust//rust/platform:aarch64-apple-darwin": [
+                    "@multi_cfg_dep__libc-0.2.117//:libc",  # cfg(all(target_arch = "aarch64", target_vendor = "apple"))
+                ],
+                "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [
+                    "@multi_cfg_dep__libc-0.2.117//:libc",  # cfg(all(target_arch = "aarch64", target_os = "linux"))
+                ],
+                "//conditions:default": [],
+            }),
+        "#};
+
+        assert!(
+            build_file_content.contains(&expected.replace('\n', "\n    ")),
+            "{}",
+            build_file_content,
+        );
+    }
+
+    #[test]
+    fn legacy_crate_features() {
+        let mut context = Context::default();
+        let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
+        context.crates.insert(
+            crate_id.clone(),
+            CrateContext {
+                name: crate_id.name,
+                version: crate_id.version,
+                targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
+                common_attrs: CommonAttributes {
+                    crate_features: CrateFeatures::LegacySet(BTreeSet::from([
+                        "foo".to_owned(),
+                        "bar".to_owned(),
+                    ])),
+                    ..CommonAttributes::default()
+                },
+                ..CrateContext::default()
+            },
+        );
+
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
+        let output = renderer.render(&context).unwrap();
+
+        let build_file_content = output
+            .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
+            .unwrap();
+        assert!(build_file_content.replace(' ', "").contains(
+            &r#"crate_features = [
+    "bar",
+    "foo",
+],"#
+            .replace(' ', "")
+        ));
+    }
+    #[test]
+    fn crate_features_by_target() {
+        let mut context = Context::default();
+        let crate_id = CrateId::new("mock_crate".to_owned(), "0.1.0".to_owned());
+        let mut features = SelectList::default();
+        features.insert("foo".to_owned(), Some("aarch64-apple-darwin".to_owned()));
+        features.insert("bar".to_owned(), None);
+        context.crates.insert(
+            crate_id.clone(),
+            CrateContext {
+                name: crate_id.name,
+                version: crate_id.version,
+                targets: BTreeSet::from([Rule::Library(mock_target_attributes())]),
+                common_attrs: CommonAttributes {
+                    crate_features: CrateFeatures::SelectList(features),
+                    ..CommonAttributes::default()
+                },
+                ..CrateContext::default()
+            },
+        );
+
+        let renderer = Renderer::new(
+            mock_render_config(None),
+            mock_supported_platform_triples(),
+            true,
+        );
+        let output = renderer.render(&context).unwrap();
+
+        let build_file_content = output
+            .get(&PathBuf::from("BUILD.mock_crate-0.1.0.bazel"))
+            .unwrap();
+        assert!(build_file_content.replace(' ', "").contains(
+            &r#"crate_features = [
+        "bar",
+    ] + select({
+    "@rules_rust//rust/platform:aarch64-apple-darwin": [
+        "foo",
+    ],
+    "//conditions:default": [],
+}),"#
+                .replace(' ', "")
+        ));
+    }
 }