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/cli.rs b/crate_universe/src/cli.rs
index fdff844..ade755c 100644
--- a/crate_universe/src/cli.rs
+++ b/crate_universe/src/cli.rs
@@ -7,10 +7,10 @@
 
 use clap::Parser;
 
-use self::generate::GenerateOptions;
-use self::query::QueryOptions;
-use self::splice::SpliceOptions;
-use self::vendor::VendorOptions;
+pub use self::generate::GenerateOptions;
+pub use self::query::QueryOptions;
+pub use self::splice::SpliceOptions;
+pub use self::vendor::VendorOptions;
 
 // Entrypoints
 pub use generate::generate;
diff --git a/crate_universe/src/cli/generate.rs b/crate_universe/src/cli/generate.rs
index 3fdc97e..925aecd 100644
--- a/crate_universe/src/cli/generate.rs
+++ b/crate_universe/src/cli/generate.rs
@@ -9,8 +9,7 @@
 use crate::config::Config;
 use crate::context::Context;
 use crate::lockfile::{lock_context, write_lockfile};
-use crate::metadata::load_metadata;
-use crate::metadata::Annotations;
+use crate::metadata::{load_metadata, Annotations, Cargo};
 use crate::rendering::{write_outputs, Renderer};
 use crate::splicing::SplicingManifest;
 
@@ -74,7 +73,12 @@
             let context = Context::try_from_path(lockfile)?;
 
             // Render build files
-            let outputs = Renderer::new(config.rendering).render(&context)?;
+            let outputs = Renderer::new(
+                config.rendering,
+                config.supported_platform_triples,
+                config.generate_target_compatible_with,
+            )
+            .render(&context)?;
 
             // Write the outputs to disk
             write_outputs(outputs, &opt.repository_dir, opt.dry_run)?;
@@ -84,10 +88,10 @@
     }
 
     // Ensure Cargo and Rustc are available for use during generation.
-    let cargo_bin = match &opt.cargo {
+    let cargo_bin = Cargo::new(match opt.cargo {
         Some(bin) => bin,
         None => bail!("The `--cargo` argument is required when generating unpinned content"),
-    };
+    });
     let rustc_bin = match &opt.rustc {
         Some(bin) => bin,
         None => bail!("The `--rustc` argument is required when generating unpinned content"),
@@ -102,27 +106,29 @@
     // Load Metadata and Lockfile
     let (cargo_metadata, cargo_lockfile) = load_metadata(metadata_path)?;
 
-    // Copy the rendering config for later use
-    let render_config = config.rendering.clone();
-
     // Annotate metadata
     let annotations = Annotations::new(cargo_metadata, cargo_lockfile.clone(), config.clone())?;
 
-    // Generate renderable contexts for earch package
+    // Generate renderable contexts for each package
     let context = Context::new(annotations)?;
 
     // Render build files
-    let outputs = Renderer::new(render_config).render(&context)?;
+    let outputs = Renderer::new(
+        config.rendering.clone(),
+        config.supported_platform_triples.clone(),
+        config.generate_target_compatible_with,
+    )
+    .render(&context)?;
 
     // Write outputs
     write_outputs(outputs, &opt.repository_dir, opt.dry_run)?;
 
-    // Ensure Bazel lockfiles are written to disk so future generations can be short-circuted.
+    // Ensure Bazel lockfiles are written to disk so future generations can be short-circuited.
     if let Some(lockfile) = opt.lockfile {
         let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)?;
 
         let lock_content =
-            lock_context(context, &config, &splicing_manifest, cargo_bin, rustc_bin)?;
+            lock_context(context, &config, &splicing_manifest, &cargo_bin, rustc_bin)?;
 
         write_lockfile(lock_content, &lockfile, opt.dry_run)?;
     }
diff --git a/crate_universe/src/cli/query.rs b/crate_universe/src/cli/query.rs
index 19087ab..51bacaa 100644
--- a/crate_universe/src/cli/query.rs
+++ b/crate_universe/src/cli/query.rs
@@ -9,6 +9,7 @@
 use crate::config::Config;
 use crate::context::Context;
 use crate::lockfile::Digest;
+use crate::metadata::Cargo;
 use crate::splicing::SplicingManifest;
 
 /// Command line options for the `query` subcommand
@@ -66,14 +67,11 @@
         &lockfile,
         &config,
         &splicing_manifest,
-        &opt.cargo,
+        &Cargo::new(opt.cargo),
         &opt.rustc,
     )?;
     if digest != expected {
-        return announce_repin(&format!(
-            "Digests do not match: {:?} != {:?}",
-            digest, expected
-        ));
+        return announce_repin(&format!("Digests do not match: {digest:?} != {expected:?}",));
     }
 
     // There is no need to repin
@@ -81,7 +79,7 @@
 }
 
 fn announce_repin(reason: &str) -> Result<()> {
-    eprintln!("{}", reason);
+    eprintln!("{reason}");
     println!("repin");
     Ok(())
 }
diff --git a/crate_universe/src/cli/splice.rs b/crate_universe/src/cli/splice.rs
index 213ee34..207c33e 100644
--- a/crate_universe/src/cli/splice.rs
+++ b/crate_universe/src/cli/splice.rs
@@ -6,7 +6,10 @@
 use clap::Parser;
 
 use crate::cli::Result;
-use crate::metadata::{write_metadata, CargoUpdateRequest, Generator, MetadataGenerator};
+use crate::config::Config;
+use crate::metadata::{
+    write_metadata, Cargo, CargoUpdateRequest, FeatureGenerator, Generator, MetadataGenerator,
+};
 use crate::splicing::{generate_lockfile, Splicer, SplicingManifest, WorkspaceMetadata};
 
 /// Command line options for the `splice` subcommand
@@ -22,7 +25,7 @@
     pub cargo_lockfile: Option<PathBuf>,
 
     /// The desired update/repin behavior
-    #[clap(long, env = "CARGO_BAZEL_REPIN", default_missing_value = "true")]
+    #[clap(long, env = "CARGO_BAZEL_REPIN", num_args=0..=1, default_missing_value = "true")]
     pub repin: Option<CargoUpdateRequest>,
 
     /// The directory in which to build the workspace. If this argument is not
@@ -42,6 +45,10 @@
     #[clap(long)]
     pub cargo_config: Option<PathBuf>,
 
+    /// The path to the config file (containing `cargo_bazel::config::Config`.)
+    #[clap(long)]
+    pub config: PathBuf,
+
     /// The path to a Cargo binary to use for gathering metadata
     #[clap(long, env = "CARGO")]
     pub cargo: PathBuf,
@@ -72,25 +79,39 @@
     // Splice together the manifest
     let manifest_path = splicer.splice_workspace(&opt.cargo)?;
 
+    let cargo = Cargo::new(opt.cargo);
+
     // Generate a lockfile
     let cargo_lockfile = generate_lockfile(
         &manifest_path,
         &opt.cargo_lockfile,
-        &opt.cargo,
+        cargo.clone(),
         &opt.rustc,
         &opt.repin,
     )?;
 
+    let config = Config::try_from_path(&opt.config)?;
+
+    let feature_map = FeatureGenerator::new(cargo.clone(), opt.rustc.clone()).generate(
+        manifest_path.as_path_buf(),
+        &config.supported_platform_triples,
+    )?;
     // Write the registry url info to the manifest now that a lockfile has been generated
-    WorkspaceMetadata::write_registry_urls(&cargo_lockfile, &manifest_path)?;
+    WorkspaceMetadata::write_registry_urls_and_feature_map(
+        &cargo,
+        &cargo_lockfile,
+        feature_map,
+        manifest_path.as_path_buf(),
+        manifest_path.as_path_buf(),
+    )?;
 
     let output_dir = opt.output_dir.clone();
 
     // Write metadata to the workspace for future reuse
     let (cargo_metadata, _) = Generator::new()
-        .with_cargo(opt.cargo)
+        .with_cargo(cargo)
         .with_rustc(opt.rustc)
-        .generate(&manifest_path.as_path_buf())?;
+        .generate(manifest_path.as_path_buf())?;
 
     let cargo_lockfile_path = manifest_path
         .as_path_buf()
diff --git a/crate_universe/src/cli/vendor.rs b/crate_universe/src/cli/vendor.rs
index 0b90541..5d99bc2 100644
--- a/crate_universe/src/cli/vendor.rs
+++ b/crate_universe/src/cli/vendor.rs
@@ -12,8 +12,8 @@
 use crate::config::{Config, VendorMode};
 use crate::context::Context;
 use crate::metadata::CargoUpdateRequest;
-use crate::metadata::{Annotations, VendorGenerator};
-use crate::metadata::{Generator, MetadataGenerator};
+use crate::metadata::FeatureGenerator;
+use crate::metadata::{Annotations, Cargo, Generator, MetadataGenerator, VendorGenerator};
 use crate::rendering::{render_module_label, write_outputs, Renderer};
 use crate::splicing::{generate_lockfile, Splicer, SplicingManifest, WorkspaceMetadata};
 
@@ -52,8 +52,8 @@
 
     /// The desired update/repin behavior. The arguments passed here are forward to
     /// [cargo update](https://doc.rust-lang.org/cargo/commands/cargo-update.html). See
-    /// [metadata::CargoUpdateRequest] for details on the values to pass here.
-    #[clap(long, env = "CARGO_BAZEL_REPIN", default_missing_value = "true")]
+    /// [crate::metadata::CargoUpdateRequest] for details on the values to pass here.
+    #[clap(long, env = "CARGO_BAZEL_REPIN", num_args=0..=1, default_missing_value = "true")]
     pub repin: Option<CargoUpdateRequest>,
 
     /// The path to a Cargo metadata `json` file.
@@ -130,26 +130,39 @@
         .splice_workspace(&opt.cargo)
         .context("Failed to splice workspace")?;
 
+    let cargo = Cargo::new(opt.cargo);
+
     // Gather a cargo lockfile
     let cargo_lockfile = generate_lockfile(
         &manifest_path,
         &opt.cargo_lockfile,
-        &opt.cargo,
+        cargo.clone(),
         &opt.rustc,
         &opt.repin,
     )?;
 
+    // Load the config from disk
+    let config = Config::try_from_path(&opt.config)?;
+
+    let feature_map = FeatureGenerator::new(cargo.clone(), opt.rustc.clone()).generate(
+        manifest_path.as_path_buf(),
+        &config.supported_platform_triples,
+    )?;
+
     // Write the registry url info to the manifest now that a lockfile has been generated
-    WorkspaceMetadata::write_registry_urls(&cargo_lockfile, &manifest_path)?;
+    WorkspaceMetadata::write_registry_urls_and_feature_map(
+        &cargo,
+        &cargo_lockfile,
+        feature_map,
+        manifest_path.as_path_buf(),
+        manifest_path.as_path_buf(),
+    )?;
 
     // Write metadata to the workspace for future reuse
     let (cargo_metadata, cargo_lockfile) = Generator::new()
-        .with_cargo(opt.cargo.clone())
+        .with_cargo(cargo.clone())
         .with_rustc(opt.rustc.clone())
-        .generate(&manifest_path.as_path_buf())?;
-
-    // Load the config from disk
-    let config = Config::try_from_path(&opt.config)?;
+        .generate(manifest_path.as_path_buf())?;
 
     // Annotate metadata
     let annotations = Annotations::new(cargo_metadata, cargo_lockfile.clone(), config.clone())?;
@@ -158,7 +171,12 @@
     let context = Context::new(annotations)?;
 
     // Render build files
-    let outputs = Renderer::new(config.rendering.clone()).render(&context)?;
+    let outputs = Renderer::new(
+        config.rendering.clone(),
+        config.supported_platform_triples.clone(),
+        config.generate_target_compatible_with,
+    )
+    .render(&context)?;
 
     // Cache the file names for potential use with buildifier
     let file_names: BTreeSet<PathBuf> = outputs.keys().cloned().collect();
@@ -181,7 +199,7 @@
 
     // Vendor the crates from the spliced workspace
     if matches!(config.rendering.vendor_mode, Some(VendorMode::Local)) {
-        VendorGenerator::new(opt.cargo.clone(), opt.rustc.clone())
+        VendorGenerator::new(cargo, opt.rustc.clone())
             .generate(manifest_path.as_path_buf(), &vendor_dir)
             .context("Failed to vendor dependencies")?;
     }
diff --git a/crate_universe/src/config.rs b/crate_universe/src/config.rs
index 0560829..6f1b8ce 100644
--- a/crate_universe/src/config.rs
+++ b/crate_universe/src/config.rs
@@ -11,11 +11,12 @@
 use cargo_lock::package::GitReference;
 use cargo_metadata::Package;
 use semver::VersionReq;
-use serde::de::Visitor;
+use serde::de::value::SeqAccessDeserializer;
+use serde::de::{Deserializer, SeqAccess, Visitor};
 use serde::{Deserialize, Serialize, Serializer};
 
 /// Representations of different kinds of crate vendoring into workspaces.
-#[derive(Debug, Serialize, Deserialize, Hash, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
 #[serde(rename_all = "lowercase")]
 pub enum VendorMode {
     /// Crates having full source being vendored into a workspace
@@ -37,7 +38,7 @@
     }
 }
 
-#[derive(Debug, Default, Hash, Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
 #[serde(deny_unknown_fields)]
 pub struct RenderConfig {
     /// The name of the repository being rendered
@@ -76,10 +77,29 @@
     /// The command to use for regenerating generated files.
     pub regen_command: String,
 
-    /// An optional configuration for rendirng content to be rendered into repositories.
+    /// An optional configuration for rendering content to be rendered into repositories.
     pub vendor_mode: Option<VendorMode>,
 }
 
+// Default is manually implemented so that the default values match the default
+// values when deserializing, which involves calling the vairous `default_x()`
+// functions specified in `#[serde(default = "default_x")]`.
+impl Default for RenderConfig {
+    fn default() -> Self {
+        RenderConfig {
+            repository_name: String::default(),
+            build_file_template: default_build_file_template(),
+            crate_label_template: default_crate_label_template(),
+            crates_module_template: default_crates_module_template(),
+            crate_repository_template: default_crate_repository_template(),
+            default_package_name: Option::default(),
+            platforms_template: default_platforms_template(),
+            regen_command: String::default(),
+            vendor_mode: Option::default(),
+        }
+    }
+}
+
 fn default_build_file_template() -> String {
     "//:BUILD.{name}-{version}.bazel".to_owned()
 }
@@ -141,8 +161,11 @@
     },
 }
 
-#[derive(Debug, Default, Hash, Deserialize, Serialize, Clone)]
+#[derive(Debug, Default, Deserialize, Serialize, Clone)]
 pub struct CrateAnnotations {
+    /// Which subset of the crate's bins should get produced as `rust_binary` targets.
+    pub gen_binaries: Option<GenBinaries>,
+
     /// Determins whether or not Cargo build scripts should be generated for the current package
     pub gen_build_script: Option<bool>,
 
@@ -174,6 +197,9 @@
     /// [compile_data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-compile_data) attribute.
     pub compile_data_glob: Option<BTreeSet<String>>,
 
+    /// If true, disables pipelining for library targets generated for this crate.
+    pub disable_pipelining: bool,
+
     /// Additional data to pass to  the target's
     /// [rustc_env](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_env) attribute.
     pub rustc_env: Option<BTreeMap<String, String>>,
@@ -261,42 +287,20 @@
     type Output = CrateAnnotations;
 
     fn add(self, rhs: Self) -> Self::Output {
-        let shallow_since = if self.shallow_since.is_some() {
-            self.shallow_since
-        } else if rhs.shallow_since.is_some() {
-            rhs.shallow_since
-        } else {
-            None
-        };
-
-        let patch_tool = if self.patch_tool.is_some() {
-            self.patch_tool
-        } else if rhs.patch_tool.is_some() {
-            rhs.patch_tool
-        } else {
-            None
-        };
-
-        let gen_build_script = if self.gen_build_script.is_some() {
-            self.gen_build_script
-        } else if rhs.gen_build_script.is_some() {
-            rhs.gen_build_script
-        } else {
-            None
-        };
-
         let concat_string = |lhs: &mut String, rhs: String| {
-            *lhs = format!("{}{}", lhs, rhs);
+            *lhs = format!("{lhs}{rhs}");
         };
 
         #[rustfmt::skip]
         let output = CrateAnnotations {
-            gen_build_script,
+            gen_binaries: self.gen_binaries.or(rhs.gen_binaries),
+            gen_build_script: self.gen_build_script.or(rhs.gen_build_script),
             deps: joined_extra_member!(self.deps, rhs.deps, BTreeSet::new, BTreeSet::extend),
             proc_macro_deps: joined_extra_member!(self.proc_macro_deps, rhs.proc_macro_deps, BTreeSet::new, BTreeSet::extend),
             crate_features: joined_extra_member!(self.crate_features, rhs.crate_features, BTreeSet::new, BTreeSet::extend),
             data: joined_extra_member!(self.data, rhs.data, BTreeSet::new, BTreeSet::extend),
             data_glob: joined_extra_member!(self.data_glob, rhs.data_glob, BTreeSet::new, BTreeSet::extend),
+            disable_pipelining: self.disable_pipelining || rhs.disable_pipelining,
             compile_data: joined_extra_member!(self.compile_data, rhs.compile_data, BTreeSet::new, BTreeSet::extend),
             compile_data_glob: joined_extra_member!(self.compile_data_glob, rhs.compile_data_glob, BTreeSet::new, BTreeSet::extend),
             rustc_env: joined_extra_member!(self.rustc_env, rhs.rustc_env, BTreeMap::new, BTreeMap::extend),
@@ -311,9 +315,9 @@
             build_script_rustc_env: joined_extra_member!(self.build_script_rustc_env, rhs.build_script_rustc_env, BTreeMap::new, BTreeMap::extend),
             build_script_toolchains: joined_extra_member!(self.build_script_toolchains, rhs.build_script_toolchains, BTreeSet::new, BTreeSet::extend),
             additive_build_file_content: joined_extra_member!(self.additive_build_file_content, rhs.additive_build_file_content, String::new, concat_string),
-            shallow_since,
+            shallow_since: self.shallow_since.or(rhs.shallow_since),
             patch_args: joined_extra_member!(self.patch_args, rhs.patch_args, Vec::new, Vec::extend),
-            patch_tool,
+            patch_tool: self.patch_tool.or(rhs.patch_tool),
             patches: joined_extra_member!(self.patches, rhs.patches, BTreeSet::new, BTreeSet::extend),
         };
 
@@ -328,7 +332,7 @@
 }
 
 /// A unique identifier for Crates
-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
 pub struct CrateId {
     /// The name of the crate
     pub name: String,
@@ -356,6 +360,16 @@
             return true;
         }
 
+        // If the version provided is the wildcard "*", it matches. Do not
+        // delegate to the semver crate in this case because semver does not
+        // consider "*" to match prerelease packages. That's expected behavior
+        // in the context of declaring package dependencies, but not in the
+        // context of declaring which versions of preselected packages an
+        // annotation applies to.
+        if self.version == "*" {
+            return true;
+        }
+
         // Next, check to see if the version provided is a semver req and
         // check if the package matches the condition
         if let Ok(semver) = VersionReq::parse(&self.version) {
@@ -405,8 +419,7 @@
             })
             .ok_or_else(|| {
                 E::custom(format!(
-                    "Expected string value of `{{name}} {{version}}`. Got '{}'",
-                    v
+                    "Expected string value of `{{name}} {{version}}`. Got '{v}'"
                 ))
             })
     }
@@ -427,13 +440,81 @@
     }
 }
 
+#[derive(Debug, Hash, Clone)]
+pub enum GenBinaries {
+    All,
+    Some(BTreeSet<String>),
+}
+
+impl Default for GenBinaries {
+    fn default() -> Self {
+        GenBinaries::Some(BTreeSet::new())
+    }
+}
+
+impl Serialize for GenBinaries {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        match self {
+            GenBinaries::All => serializer.serialize_bool(true),
+            GenBinaries::Some(set) if set.is_empty() => serializer.serialize_bool(false),
+            GenBinaries::Some(set) => serializer.collect_seq(set),
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for GenBinaries {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        deserializer.deserialize_any(GenBinariesVisitor)
+    }
+}
+
+struct GenBinariesVisitor;
+impl<'de> Visitor<'de> for GenBinariesVisitor {
+    type Value = GenBinaries;
+
+    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str("boolean, or array of bin names")
+    }
+
+    fn visit_bool<E>(self, gen_binaries: bool) -> Result<Self::Value, E> {
+        if gen_binaries {
+            Ok(GenBinaries::All)
+        } else {
+            Ok(GenBinaries::Some(BTreeSet::new()))
+        }
+    }
+
+    fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
+    where
+        A: SeqAccess<'de>,
+    {
+        BTreeSet::deserialize(SeqAccessDeserializer::new(seq)).map(GenBinaries::Some)
+    }
+}
+
 /// Workspace specific settings to control how targets are generated
 #[derive(Debug, Default, Serialize, Deserialize, Clone)]
 #[serde(deny_unknown_fields)]
 pub struct Config {
+    /// Whether to generate `rust_binary` targets for all bins by default
+    pub generate_binaries: bool,
+
     /// Whether or not to generate Cargo build scripts by default
     pub generate_build_scripts: bool,
 
+    /// A set of platform triples to use in generated select statements
+    #[serde(
+        default = "default_generate_target_compatible_with",
+        skip_serializing_if = "skip_generate_target_compatible_with"
+    )]
+    pub generate_target_compatible_with: bool,
+
     /// Additional settings to apply to generated crates
     #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
     pub annotations: BTreeMap<CrateId, CrateAnnotations>,
@@ -456,6 +537,13 @@
     }
 }
 
+fn default_generate_target_compatible_with() -> bool {
+    true
+}
+fn skip_generate_target_compatible_with(value: &bool) -> bool {
+    *value
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
@@ -481,22 +569,26 @@
         let mut package = mock_cargo_metadata_package();
         let id = CrateId::new("mock-pkg".to_owned(), "0.1.0".to_owned());
 
-        package.version = cargo_metadata::Version::new(0, 1, 0);
+        package.version = cargo_metadata::semver::Version::new(0, 1, 0);
         assert!(id.matches(&package));
 
-        package.version = cargo_metadata::Version::new(1, 0, 0);
+        package.version = cargo_metadata::semver::Version::new(1, 0, 0);
         assert!(!id.matches(&package));
     }
 
     #[test]
     fn test_crate_id_semver_matches() {
         let mut package = mock_cargo_metadata_package();
-        package.version = cargo_metadata::Version::new(1, 0, 0);
+        package.version = cargo_metadata::semver::Version::new(1, 0, 0);
         let mut id = CrateId::new("mock-pkg".to_owned(), "0.1.0".to_owned());
 
         id.version = "*".to_owned();
         assert!(id.matches(&package));
 
+        let mut prerelease = mock_cargo_metadata_package();
+        prerelease.version = cargo_metadata::semver::Version::parse("1.0.0-pre.0").unwrap();
+        assert!(id.matches(&prerelease));
+
         id.version = "<1".to_owned();
         assert!(!id.matches(&package));
     }
@@ -523,6 +615,7 @@
 
         // Global settings
         assert!(config.cargo_config.is_none());
+        assert!(!config.generate_binaries);
         assert!(!config.generate_build_scripts);
 
         // Render Config
diff --git a/crate_universe/src/context.rs b/crate_universe/src/context.rs
index 912cd80..41145c5 100644
--- a/crate_universe/src/context.rs
+++ b/crate_universe/src/context.rs
@@ -15,7 +15,7 @@
 use crate::context::platforms::resolve_cfg_platforms;
 use crate::lockfile::Digest;
 use crate::metadata::Annotations;
-use crate::utils::starlark::{Select, SelectList};
+use crate::utils::starlark::Select;
 
 pub use self::crate_context::*;
 
@@ -51,14 +51,15 @@
         let crates: BTreeMap<CrateId, CrateContext> = annotations
             .metadata
             .crates
-            .iter()
-            // Convert the crate annotations into more renderable contexts
-            .map(|(_, annotation)| {
+            .values()
+            .map(|annotation| {
                 let context = CrateContext::new(
                     annotation,
                     &annotations.metadata.packages,
                     &annotations.lockfile.crates,
                     &annotations.pairred_extras,
+                    &annotations.features,
+                    annotations.config.generate_binaries,
                     annotations.config.generate_build_scripts,
                 );
                 let id = CrateId::new(context.name.clone(), context.version.clone());
@@ -159,261 +160,41 @@
         Ok(package_path_id)
     }
 
-    /// Filter a crate's dependencies to only ones with aliases
-    pub fn crate_aliases(
-        &self,
-        crate_id: &CrateId,
-        build: bool,
-        include_dev: bool,
-    ) -> SelectList<&CrateDependency> {
-        let ctx = &self.crates[crate_id];
-        let mut set = SelectList::default();
-
-        // Return a set of aliases for build dependencies
-        // vs normal dependencies when requested.
-        if build {
-            // Note that there may not be build dependencies so no dependencies
-            // will be gathered in this case
-            if let Some(attrs) = &ctx.build_script_attrs {
-                let collection: Vec<(Option<String>, &CrateDependency)> = attrs
-                    .deps
-                    .configurations()
-                    .into_iter()
-                    .flat_map(move |conf| {
-                        attrs
-                            .deps
-                            .get_iter(conf)
-                            .expect("Iterating over known keys should never panic")
-                            .filter(|dep| dep.alias.is_some())
-                            .map(move |dep| (conf.cloned(), dep))
-                    })
-                    .chain(attrs.proc_macro_deps.configurations().into_iter().flat_map(
-                        move |conf| {
-                            attrs
-                                .proc_macro_deps
-                                .get_iter(conf)
-                                .expect("Iterating over known keys should never panic")
-                                .filter(|dep| dep.alias.is_some())
-                                .map(move |dep| (conf.cloned(), dep))
-                        },
-                    ))
-                    .collect();
-
-                for (config, dep) in collection {
-                    set.insert(dep, config);
-                }
-            }
-        } else {
-            let attrs = &ctx.common_attrs;
-            let mut collection: Vec<(Option<String>, &CrateDependency)> =
-                attrs
-                    .deps
-                    .configurations()
-                    .into_iter()
-                    .flat_map(move |conf| {
-                        attrs
-                            .deps
-                            .get_iter(conf)
-                            .expect("Iterating over known keys should never panic")
-                            .filter(|dep| dep.alias.is_some())
-                            .map(move |dep| (conf.cloned(), dep))
-                    })
-                    .chain(attrs.proc_macro_deps.configurations().into_iter().flat_map(
-                        move |conf| {
-                            attrs
-                                .proc_macro_deps
-                                .get_iter(conf)
-                                .expect("Iterating over known keys should never panic")
-                                .filter(|dep| dep.alias.is_some())
-                                .map(move |dep| (conf.cloned(), dep))
-                        },
-                    ))
-                    .collect();
-
-            // Optionally include dev dependencies
-            if include_dev {
-                collection = collection
-                    .into_iter()
-                    .chain(
-                        attrs
-                            .deps_dev
-                            .configurations()
-                            .into_iter()
-                            .flat_map(move |conf| {
-                                attrs
-                                    .deps_dev
-                                    .get_iter(conf)
-                                    .expect("Iterating over known keys should never panic")
-                                    .filter(|dep| dep.alias.is_some())
-                                    .map(move |dep| (conf.cloned(), dep))
-                            }),
-                    )
-                    .chain(
-                        attrs
-                            .proc_macro_deps_dev
-                            .configurations()
-                            .into_iter()
-                            .flat_map(move |conf| {
-                                attrs
-                                    .proc_macro_deps_dev
-                                    .get_iter(conf)
-                                    .expect("Iterating over known keys should never panic")
-                                    .filter(|dep| dep.alias.is_some())
-                                    .map(move |dep| (conf.cloned(), dep))
-                            }),
-                    )
-                    .collect();
-            }
-
-            for (config, dep) in collection {
-                set.insert(dep, config);
-            }
-        }
-
-        set
-    }
-
-    /// Create a set of all direct dependencies of workspace member crates and map them to
-    /// optional alternative names that allow them to be uniquely identified. This typically
-    /// results in a mapping of ([CrateId], [None]) where [None] defaults to using the crate
-    /// name. The next most common would be using ([CrateId], `Some(alias)`) as some projects
-    /// may use aliases in Cargo as a way to differentiate different versions of the same dep.
-    pub fn flat_workspace_member_deps(&self) -> BTreeMap<CrateId, Option<String>> {
-        let workspace_member_dependencies: BTreeSet<CrateDependency> = self
-            .workspace_members
-            .iter()
-            .map(|(id, _)| &self.crates[id])
+    /// Create a set of all direct dependencies of workspace member crates.
+    pub fn workspace_member_deps(&self) -> BTreeSet<&CrateDependency> {
+        self.workspace_members
+            .keys()
+            .map(move |id| &self.crates[id])
             .flat_map(|ctx| {
-                // Build an interator of all dependency CrateIds.
-                // TODO: This expansion is horribly verbose and should be refactored but closures
-                // were not playing nice when I tried it.
-                ctx.common_attrs
-                    .deps
-                    .configurations()
-                    .into_iter()
-                    .flat_map(move |conf| {
-                        ctx.common_attrs
-                            .deps
-                            .get_iter(conf)
-                            .expect("Lookup should be guaranteed")
+                IntoIterator::into_iter([
+                    &ctx.common_attrs.deps,
+                    &ctx.common_attrs.deps_dev,
+                    &ctx.common_attrs.proc_macro_deps,
+                    &ctx.common_attrs.proc_macro_deps_dev,
+                ])
+                .flat_map(|deps| {
+                    deps.configurations().into_iter().flat_map(move |conf| {
+                        deps.get_iter(conf).expect("Lookup should be guaranteed")
                     })
-                    .chain(
-                        ctx.common_attrs
-                            .deps_dev
-                            .configurations()
-                            .into_iter()
-                            .flat_map(move |conf| {
-                                ctx.common_attrs
-                                    .deps_dev
-                                    .get_iter(conf)
-                                    .expect("Lookup should be guaranteed")
-                            }),
-                    )
-                    .chain(
-                        ctx.common_attrs
-                            .proc_macro_deps
-                            .configurations()
-                            .into_iter()
-                            .flat_map(move |conf| {
-                                ctx.common_attrs
-                                    .proc_macro_deps
-                                    .get_iter(conf)
-                                    .expect("Lookup should be guaranteed")
-                            }),
-                    )
-                    .chain(
-                        ctx.common_attrs
-                            .proc_macro_deps_dev
-                            .configurations()
-                            .into_iter()
-                            .flat_map(move |conf| {
-                                ctx.common_attrs
-                                    .proc_macro_deps_dev
-                                    .get_iter(conf)
-                                    .expect("Lookup should be guaranteed")
-                            }),
-                    )
-            })
-            .cloned()
-            .collect();
-
-        // Search for any duplicate workspace member definitions
-        let duplicate_deps: Vec<CrateDependency> = workspace_member_dependencies
-            .iter()
-            .filter(|dep| {
-                workspace_member_dependencies
-                    .iter()
-                    .filter(|check| dep.id.name == check.id.name)
-                    .count()
-                    > 1
-            })
-            .cloned()
-            .collect();
-
-        workspace_member_dependencies
-            .into_iter()
-            .map(|dep| {
-                if duplicate_deps.contains(&dep) {
-                    if let Some(alias) = &dep.alias {
-                        // Check for any duplicate aliases
-                        let aliases = duplicate_deps
-                            .iter()
-                            .filter(|dupe| dupe.id.name == dep.id.name)
-                            .filter(|dupe| dupe.alias.is_some())
-                            .filter(|dupe| dupe.alias == dep.alias);
-
-                        // If there are multiple aliased crates with the same name, the name is updated to
-                        // be `{alias}-{version}` to differentiate them.
-                        if aliases.count() >= 2 {
-                            let rename = format!("{}-{}", &alias, &dep.id.version);
-                            (dep.id, Some(rename))
-                        } else {
-                            (dep.id, Some(alias.clone()))
-                        }
-                    } else {
-                        // Check for all duplicates that match the current dependency and have no alias
-                        let unaliased = duplicate_deps
-                            .iter()
-                            .filter(|dupe| dupe.id.name == dep.id.name)
-                            .filter(|dupe| dupe.alias.is_none());
-
-                        // If there are multiple unaliased crates with the same name, the name is updated to
-                        // be `{name}-{version}` to differentiate them.
-                        if unaliased.count() >= 2 {
-                            let rename = format!("{}-{}", &dep.id.name, &dep.id.version);
-                            (dep.id, Some(rename))
-                        } else {
-                            (dep.id, None)
-                        }
-                    }
-                } else {
-                    (dep.id, dep.alias)
-                }
+                })
             })
             .collect()
     }
 
-    /// Produce a list of binary dependencies with optional aliases which prevent duplicate
-    /// targets from being generated.
-    pub fn flat_binary_deps(&self) -> BTreeMap<CrateId, Option<String>> {
-        // Check for any duplicate binary crate names. If one exists provide an alias to differentiate them
-        self.binary_crates
+    pub fn has_duplicate_workspace_member_dep(&self, dep: &CrateDependency) -> bool {
+        1 < self
+            .workspace_member_deps()
+            .into_iter()
+            .filter(|check| check.id.name == dep.id.name && check.alias == dep.alias)
+            .count()
+    }
+
+    pub fn has_duplicate_binary_crate(&self, bin: &CrateId) -> bool {
+        1 < self
+            .binary_crates
             .iter()
-            .map(|crate_id| {
-                let dupe_count = self
-                    .binary_crates
-                    .iter()
-                    .filter(|id| crate_id.name == id.name)
-                    .count();
-                // For targets that appear twice (which can happen if one crate aliases a binary dependency)
-                if dupe_count >= 2 {
-                    let rename = format!("{}-{}", crate_id.name, crate_id.version);
-                    (crate_id.clone(), Some(rename))
-                } else {
-                    (crate_id.clone(), None)
-                }
-            })
-            .collect()
+            .filter(|check| check.name == bin.name)
+            .count()
     }
 }
 
@@ -446,66 +227,51 @@
     }
 
     #[test]
-    fn flat_workspace_member_deps() {
+    fn workspace_member_deps() {
         let context = mock_context_common();
-        let workspace_member_deps = context.flat_workspace_member_deps();
+        let workspace_member_deps = context.workspace_member_deps();
 
-        assert_eq!(
-            workspace_member_deps,
-            BTreeMap::from([
-                (
-                    CrateId::new("bitflags".to_owned(), "1.3.2".to_owned()),
-                    None
-                ),
-                (CrateId::new("cfg-if".to_owned(), "1.0.0".to_owned()), None),
-            ])
-        );
+        assert_eq! {
+            workspace_member_deps
+                .iter()
+                .map(|dep| (&dep.id, context.has_duplicate_workspace_member_dep(dep)))
+                .collect::<Vec<_>>(),
+            [
+                (&CrateId::new("bitflags".to_owned(), "1.3.2".to_owned()), false),
+                (&CrateId::new("cfg-if".to_owned(), "1.0.0".to_owned()), false),
+            ],
+        }
     }
 
     #[test]
-    fn flat_workspace_member_deps_with_alises() {
+    fn workspace_member_deps_with_aliases() {
         let context = mock_context_aliases();
-        let workspace_member_deps = context.flat_workspace_member_deps();
+        let workspace_member_deps = context.workspace_member_deps();
 
-        assert_eq!(
-            workspace_member_deps,
-            BTreeMap::from([
-                (
-                    CrateId {
-                        name: "log".to_owned(),
-                        version: "0.3.9".to_owned(),
-                    },
-                    Some("pinned_log".to_owned())
-                ),
-                (
-                    CrateId {
-                        name: "log".to_owned(),
-                        version: "0.4.14".to_owned(),
-                    },
-                    None
-                ),
-                (
-                    CrateId {
-                        name: "names".to_owned(),
-                        version: "0.12.1-dev".to_owned(),
-                    },
-                    Some("pinned_names".to_owned())
-                ),
-                (
-                    CrateId {
-                        name: "names".to_owned(),
-                        version: "0.13.0".to_owned(),
-                    },
-                    None
-                ),
-                (
-                    CrateId {
-                        name: "value-bag".to_owned(),
-                        version: "1.0.0-alpha.7".to_owned(),
-                    },
-                    None
-                ),
-            ])
-        );
+        assert_eq! {
+            workspace_member_deps
+                .iter()
+                .map(|dep| (&dep.id, context.has_duplicate_workspace_member_dep(dep)))
+                .collect::<Vec<_>>(),
+            [
+                (&CrateId::new("log".to_owned(), "0.3.9".to_owned()), false),
+                (&CrateId::new("log".to_owned(), "0.4.14".to_owned()), false),
+                (&CrateId::new("names".to_owned(), "0.12.1-dev".to_owned()), false),
+                (&CrateId::new("names".to_owned(), "0.13.0".to_owned()), false),
+                (&CrateId::new("value-bag".to_owned(), "1.0.0-alpha.7".to_owned()), false),
+            ],
+        }
+    }
+
+    #[test]
+    fn seralization() {
+        let context = mock_context_aliases();
+
+        // Seralize and deseralize the context object
+        let json_text = serde_json::to_string(&context).unwrap();
+        let deserialized_context: Context = serde_json::from_str(&json_text).unwrap();
+
+        // The data should be identical
+        assert_eq!(context, deserialized_context);
     }
 }
diff --git a/crate_universe/src/context/crate_context.rs b/crate_universe/src/context/crate_context.rs
index 923fa1e..16f5c23 100644
--- a/crate_universe/src/context/crate_context.rs
+++ b/crate_universe/src/context/crate_context.rs
@@ -5,7 +5,7 @@
 use cargo_metadata::{Node, Package, PackageId};
 use serde::{Deserialize, Serialize};
 
-use crate::config::CrateId;
+use crate::config::{CrateId, GenBinaries};
 use crate::metadata::{CrateAnnotation, Dependency, PairredExtras, SourceAnnotation};
 use crate::utils::sanitize_module_name;
 use crate::utils::starlark::{Glob, SelectList, SelectMap, SelectStringDict, SelectStringList};
@@ -28,6 +28,12 @@
 #[serde(default)]
 pub struct TargetAttributes {
     /// The module name of the crate (notably, not the package name).
+    //
+    // This must be the first field of `TargetAttributes` to make it the
+    // lexicographically first thing the derived `Ord` implementation will sort
+    // by. The `Ord` impl controls the order of multiple rules of the same type
+    // in the same BUILD file. In particular, this makes packages with multiple
+    // bin crates generate those `rust_binary` targets in alphanumeric order.
     pub crate_name: String,
 
     /// The path to the crate's root source file, relative to the manifest.
@@ -39,17 +45,58 @@
 
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
 pub enum Rule {
-    /// `cargo_build_script`
-    BuildScript(TargetAttributes),
+    /// `rust_library`
+    Library(TargetAttributes),
 
     /// `rust_proc_macro`
     ProcMacro(TargetAttributes),
 
-    /// `rust_library`
-    Library(TargetAttributes),
-
     /// `rust_binary`
     Binary(TargetAttributes),
+
+    /// `cargo_build_script`
+    BuildScript(TargetAttributes),
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum CrateFeatures {
+    // Not populated going forward. This just exists for backward compatiblity
+    // with old lock files.
+    LegacySet(BTreeSet<String>),
+
+    /// Map from target triplet to set of features.
+    SelectList(SelectList<String>),
+}
+
+impl Default for CrateFeatures {
+    fn default() -> Self {
+        CrateFeatures::SelectList(Default::default())
+    }
+}
+
+impl From<&CrateFeatures> for SelectList<String> {
+    fn from(value: &CrateFeatures) -> Self {
+        match value {
+            CrateFeatures::LegacySet(s) => {
+                let mut sl = SelectList::default();
+                for v in s {
+                    sl.insert(v.clone(), None);
+                }
+                sl
+            }
+            CrateFeatures::SelectList(sl) => sl.clone(),
+        }
+    }
+}
+
+impl CrateFeatures {
+    pub fn is_empty(&self) -> bool {
+        match self {
+            CrateFeatures::LegacySet(s) => s.is_empty(),
+            CrateFeatures::SelectList(sl) => sl.is_empty(),
+        }
+    }
 }
 
 /// A set of attributes common to most `rust_library`, `rust_proc_macro`, and other
@@ -57,28 +104,28 @@
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
 #[serde(default)]
 pub struct CommonAttributes {
-    #[serde(skip_serializing_if = "SelectStringList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringList::is_empty")]
     pub compile_data: SelectStringList,
 
     #[serde(skip_serializing_if = "BTreeSet::is_empty")]
     pub compile_data_glob: BTreeSet<String>,
 
-    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
-    pub crate_features: BTreeSet<String>,
+    #[serde(skip_serializing_if = "CrateFeatures::is_empty")]
+    pub crate_features: CrateFeatures,
 
-    #[serde(skip_serializing_if = "SelectStringList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringList::is_empty")]
     pub data: SelectStringList,
 
     #[serde(skip_serializing_if = "BTreeSet::is_empty")]
     pub data_glob: BTreeSet<String>,
 
-    #[serde(skip_serializing_if = "SelectList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectList::is_empty")]
     pub deps: SelectList<CrateDependency>,
 
     #[serde(skip_serializing_if = "BTreeSet::is_empty")]
     pub extra_deps: BTreeSet<String>,
 
-    #[serde(skip_serializing_if = "SelectList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectList::is_empty")]
     pub deps_dev: SelectList<CrateDependency>,
 
     pub edition: String,
@@ -86,19 +133,19 @@
     #[serde(skip_serializing_if = "Option::is_none")]
     pub linker_script: Option<String>,
 
-    #[serde(skip_serializing_if = "SelectList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectList::is_empty")]
     pub proc_macro_deps: SelectList<CrateDependency>,
 
     #[serde(skip_serializing_if = "BTreeSet::is_empty")]
     pub extra_proc_macro_deps: BTreeSet<String>,
 
-    #[serde(skip_serializing_if = "SelectList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectList::is_empty")]
     pub proc_macro_deps_dev: SelectList<CrateDependency>,
 
-    #[serde(skip_serializing_if = "SelectStringDict::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringDict::is_empty")]
     pub rustc_env: SelectStringDict,
 
-    #[serde(skip_serializing_if = "SelectStringList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringList::is_empty")]
     pub rustc_env_files: SelectStringList,
 
     #[serde(skip_serializing_if = "Vec::is_empty")]
@@ -141,40 +188,40 @@
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
 #[serde(default)]
 pub struct BuildScriptAttributes {
-    #[serde(skip_serializing_if = "SelectStringList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringList::is_empty")]
     pub compile_data: SelectStringList,
 
-    #[serde(skip_serializing_if = "SelectStringList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringList::is_empty")]
     pub data: SelectStringList,
 
     #[serde(skip_serializing_if = "BTreeSet::is_empty")]
     pub data_glob: BTreeSet<String>,
 
-    #[serde(skip_serializing_if = "SelectList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectList::is_empty")]
     pub deps: SelectList<CrateDependency>,
 
     #[serde(skip_serializing_if = "BTreeSet::is_empty")]
     pub extra_deps: BTreeSet<String>,
 
-    #[serde(skip_serializing_if = "SelectStringDict::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringDict::is_empty")]
     pub build_script_env: SelectStringDict,
 
     #[serde(skip_serializing_if = "BTreeSet::is_empty")]
     pub extra_proc_macro_deps: BTreeSet<String>,
 
-    #[serde(skip_serializing_if = "SelectList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectList::is_empty")]
     pub proc_macro_deps: SelectList<CrateDependency>,
 
-    #[serde(skip_serializing_if = "SelectStringDict::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringDict::is_empty")]
     pub rustc_env: SelectStringDict,
 
-    #[serde(skip_serializing_if = "SelectStringList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringList::is_empty")]
     pub rustc_flags: SelectStringList,
 
-    #[serde(skip_serializing_if = "SelectStringList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringList::is_empty")]
     pub rustc_env_files: SelectStringList,
 
-    #[serde(skip_serializing_if = "SelectStringList::should_skip_serializing")]
+    #[serde(skip_serializing_if = "SelectStringList::is_empty")]
     pub tools: SelectStringList,
 
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -221,7 +268,7 @@
     pub repository: Option<SourceAnnotation>,
 
     /// A list of all targets (lib, proc-macro, bin) associated with this package
-    pub targets: Vec<Rule>,
+    pub targets: BTreeSet<Rule>,
 
     /// The name of the crate's root library target. This is the target that a dependent
     /// would get if they were to depend on `{crate_name}`.
@@ -241,6 +288,10 @@
     /// Additional text to add to the generated BUILD file.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub additive_build_file_content: Option<String>,
+
+    /// If true, disables pipelining for library targets generated for this crate
+    #[serde(skip_serializing_if = "std::ops::Not::not")]
+    pub disable_pipelining: bool,
 }
 
 impl CrateContext {
@@ -249,6 +300,8 @@
         packages: &BTreeMap<PackageId, Package>,
         source_annotations: &BTreeMap<PackageId, SourceAnnotation>,
         extras: &BTreeMap<CrateId, PairredExtras>,
+        features: &BTreeMap<CrateId, SelectList<String>>,
+        include_binaries: bool,
         include_build_scripts: bool,
     ) -> Self {
         let package: &Package = &packages[&annotation.node.id];
@@ -281,21 +334,44 @@
 
         // Gather all "common" attributes
         let mut common_attrs = CommonAttributes {
-            crate_features: annotation.node.features.iter().cloned().collect(),
+            crate_features: CrateFeatures::SelectList(
+                features
+                    .get(&current_crate_id)
+                    .map_or_else(SelectList::default, |f| f.clone()),
+            ),
             deps,
             deps_dev,
-            edition: package.edition.clone(),
+            edition: package.edition.as_str().to_string(),
             proc_macro_deps,
             proc_macro_deps_dev,
             version: package.version.to_string(),
             ..Default::default()
         };
 
+        // Locate extra settings for the current package.
+        let package_extra = extras
+            .iter()
+            .find(|(_, settings)| settings.package_id == package.id);
+
         let include_build_scripts =
-            Self::crate_includes_build_script(package, extras, include_build_scripts);
+            Self::crate_includes_build_script(package_extra, include_build_scripts);
+
+        let gen_none = GenBinaries::Some(BTreeSet::new());
+        let gen_binaries = package_extra
+            .and_then(|(_, settings)| settings.crate_extra.gen_binaries.as_ref())
+            .unwrap_or(if include_binaries {
+                &GenBinaries::All
+            } else {
+                &gen_none
+            });
 
         // Iterate over each target and produce a Bazel target for all supported "kinds"
-        let targets = Self::collect_targets(&annotation.node, packages, include_build_scripts);
+        let targets = Self::collect_targets(
+            &annotation.node,
+            packages,
+            gen_binaries,
+            include_build_scripts,
+        );
 
         // Parse the library crate name from the set of included targets
         let library_target_name = {
@@ -368,6 +444,7 @@
             build_script_attrs,
             license,
             additive_build_file_content: None,
+            disable_pipelining: false,
         }
         .with_overrides(extras)
     }
@@ -403,8 +480,13 @@
 
             // Crate features
             if let Some(extra) = &crate_extra.crate_features {
-                for data in extra.iter() {
-                    self.common_attrs.crate_features.insert(data.clone());
+                match &mut self.common_attrs.crate_features {
+                    CrateFeatures::LegacySet(s) => s.append(&mut extra.clone()),
+                    CrateFeatures::SelectList(sl) => {
+                        for data in extra.iter() {
+                            sl.insert(data.clone(), None);
+                        }
+                    }
                 }
             }
 
@@ -420,6 +502,11 @@
                 self.common_attrs.data_glob.extend(extra.clone());
             }
 
+            // Disable pipelining
+            if crate_extra.disable_pipelining {
+                self.disable_pipelining = true;
+            }
+
             // Rustc flags
             if let Some(extra) = &crate_extra.rustc_flags {
                 self.common_attrs.rustc_flags.append(&mut extra.clone());
@@ -427,7 +514,7 @@
 
             // Rustc env
             if let Some(extra) = &crate_extra.rustc_env {
-                self.common_attrs.rustc_env.insert(extra.clone(), None);
+                self.common_attrs.rustc_env.extend(extra.clone(), None);
             }
 
             // Rustc env files
@@ -477,12 +564,12 @@
 
                 // Rustc env
                 if let Some(extra) = &crate_extra.build_script_rustc_env {
-                    attrs.rustc_env.insert(extra.clone(), None);
+                    attrs.rustc_env.extend(extra.clone(), None);
                 }
 
                 // Build script env
                 if let Some(extra) = &crate_extra.build_script_env {
-                    attrs.build_script_env.insert(extra.clone(), None);
+                    attrs.build_script_env.extend(extra.clone(), None);
                 }
             }
 
@@ -533,18 +620,12 @@
     /// Determine whether or not a crate __should__ include a build script
     /// (build.rs) if it happens to have one.
     fn crate_includes_build_script(
-        package: &Package,
-        overrides: &BTreeMap<CrateId, PairredExtras>,
+        package_extra: Option<(&CrateId, &PairredExtras)>,
         default_generate_build_script: bool,
     ) -> bool {
-        // Locate extra settings for the current package.
-        let settings = overrides
-            .iter()
-            .find(|(_, settings)| settings.package_id == package.id);
-
         // If the crate has extra settings, which explicitly set `gen_build_script`, always use
         // this value, otherwise, fallback to the provided default.
-        settings
+        package_extra
             .and_then(|(_, settings)| settings.crate_extra.gen_build_script)
             .unwrap_or(default_generate_build_script)
     }
@@ -553,8 +634,9 @@
     fn collect_targets(
         node: &Node,
         packages: &BTreeMap<PackageId, Package>,
+        gen_binaries: &GenBinaries,
         include_build_scripts: bool,
-    ) -> Vec<Rule> {
+    ) -> BTreeSet<Rule> {
         let package = &packages[&node.id];
 
         let package_root = package
@@ -567,60 +649,61 @@
             .targets
             .iter()
             .flat_map(|target| {
-                target
-                    .kind
-                    .iter()
-                    .filter_map(|kind| {
-                        // Unfortunately, The package graph and resolve graph of cargo metadata have different representations
-                        // for the crate names (resolve graph sanitizes names to match module names) so to get the rest of this
-                        // content to align when rendering, the package target names are always sanitized.
-                        let crate_name = sanitize_module_name(&target.name);
+                target.kind.iter().filter_map(move |kind| {
+                    // Unfortunately, The package graph and resolve graph of cargo metadata have different representations
+                    // for the crate names (resolve graph sanitizes names to match module names) so to get the rest of this
+                    // content to align when rendering, the package target names are always sanitized.
+                    let crate_name = sanitize_module_name(&target.name);
 
-                        // Locate the crate's root source file relative to the package root normalized for unix
-                        let crate_root = pathdiff::diff_paths(&target.src_path, package_root).map(
-                            // Normalize the path so that it always renders the same regardless of platform
-                            |root| root.to_string_lossy().replace('\\', "/"),
-                        );
+                    // Locate the crate's root source file relative to the package root normalized for unix
+                    let crate_root = pathdiff::diff_paths(&target.src_path, package_root).map(
+                        // Normalize the path so that it always renders the same regardless of platform
+                        |root| root.to_string_lossy().replace('\\', "/"),
+                    );
 
-                        // Conditionally check to see if the dependencies is a build-script target
-                        if include_build_scripts && kind == "custom-build" {
-                            return Some(Rule::BuildScript(TargetAttributes {
-                                crate_name,
-                                crate_root,
-                                srcs: Glob::new_rust_srcs(),
-                            }));
+                    // Conditionally check to see if the dependencies is a build-script target
+                    if include_build_scripts && kind == "custom-build" {
+                        return Some(Rule::BuildScript(TargetAttributes {
+                            crate_name,
+                            crate_root,
+                            srcs: Glob::new_rust_srcs(),
+                        }));
+                    }
+
+                    // Check to see if the dependencies is a proc-macro target
+                    if kind == "proc-macro" {
+                        return Some(Rule::ProcMacro(TargetAttributes {
+                            crate_name,
+                            crate_root,
+                            srcs: Glob::new_rust_srcs(),
+                        }));
+                    }
+
+                    // Check to see if the dependencies is a library target
+                    if ["lib", "rlib"].contains(&kind.as_str()) {
+                        return Some(Rule::Library(TargetAttributes {
+                            crate_name,
+                            crate_root,
+                            srcs: Glob::new_rust_srcs(),
+                        }));
+                    }
+
+                    // Check if the target kind is binary and is one of the ones included in gen_binaries
+                    if kind == "bin"
+                        && match gen_binaries {
+                            GenBinaries::All => true,
+                            GenBinaries::Some(set) => set.contains(&target.name),
                         }
+                    {
+                        return Some(Rule::Binary(TargetAttributes {
+                            crate_name: target.name.clone(),
+                            crate_root,
+                            srcs: Glob::new_rust_srcs(),
+                        }));
+                    }
 
-                        // Check to see if the dependencies is a proc-macro target
-                        if kind == "proc-macro" {
-                            return Some(Rule::ProcMacro(TargetAttributes {
-                                crate_name,
-                                crate_root,
-                                srcs: Glob::new_rust_srcs(),
-                            }));
-                        }
-
-                        // Check to see if the dependencies is a library target
-                        if ["lib", "rlib"].contains(&kind.as_str()) {
-                            return Some(Rule::Library(TargetAttributes {
-                                crate_name,
-                                crate_root,
-                                srcs: Glob::new_rust_srcs(),
-                            }));
-                        }
-
-                        // Check to see if the dependencies is a library target
-                        if kind == "bin" {
-                            return Some(Rule::Binary(TargetAttributes {
-                                crate_name: target.name.clone(),
-                                crate_root,
-                                srcs: Glob::new_rust_srcs(),
-                            }));
-                        }
-
-                        None
-                    })
-                    .collect::<Vec<Rule>>()
+                    None
+                })
             })
             .collect()
     }
@@ -650,29 +733,26 @@
             repr: "common 0.1.0 (path+file://{TEMP_DIR}/common)".to_owned(),
         }];
 
+        let include_binaries = false;
+        let include_build_scripts = false;
         let context = CrateContext::new(
             crate_annotation,
             &annotations.metadata.packages,
             &annotations.lockfile.crates,
             &annotations.pairred_extras,
-            false,
+            &annotations.features,
+            include_binaries,
+            include_build_scripts,
         );
 
         assert_eq!(context.name, "common");
         assert_eq!(
             context.targets,
-            vec![
-                Rule::Library(TargetAttributes {
-                    crate_name: "common".to_owned(),
-                    crate_root: Some("lib.rs".to_owned()),
-                    srcs: Glob::new_rust_srcs(),
-                }),
-                Rule::Binary(TargetAttributes {
-                    crate_name: "common-bin".to_owned(),
-                    crate_root: Some("main.rs".to_owned()),
-                    srcs: Glob::new_rust_srcs(),
-                }),
-            ]
+            BTreeSet::from([Rule::Library(TargetAttributes {
+                crate_name: "common".to_owned(),
+                crate_root: Some("lib.rs".to_owned()),
+                srcs: Glob::new_rust_srcs(),
+            })]),
         );
     }
 
@@ -692,24 +772,29 @@
             PairredExtras {
                 package_id,
                 crate_extra: CrateAnnotations {
+                    gen_binaries: Some(GenBinaries::All),
                     data_glob: Some(BTreeSet::from(["**/data_glob/**".to_owned()])),
                     ..CrateAnnotations::default()
                 },
             },
         );
 
+        let include_binaries = false;
+        let include_build_scripts = false;
         let context = CrateContext::new(
             crate_annotation,
             &annotations.metadata.packages,
             &annotations.lockfile.crates,
             &pairred_extras,
-            false,
+            &annotations.features,
+            include_binaries,
+            include_build_scripts,
         );
 
         assert_eq!(context.name, "common");
         assert_eq!(
             context.targets,
-            vec![
+            BTreeSet::from([
                 Rule::Library(TargetAttributes {
                     crate_name: "common".to_owned(),
                     crate_root: Some("lib.rs".to_owned()),
@@ -720,7 +805,7 @@
                     crate_root: Some("main.rs".to_owned()),
                     srcs: Glob::new_rust_srcs(),
                 }),
-            ]
+            ]),
         );
         assert_eq!(
             context.common_attrs.data_glob,
@@ -751,25 +836,29 @@
         let annotations = build_script_annotations();
 
         let package_id = PackageId {
-            repr: "openssl-sys 0.9.72 (registry+https://github.com/rust-lang/crates.io-index)"
+            repr: "openssl-sys 0.9.87 (registry+https://github.com/rust-lang/crates.io-index)"
                 .to_owned(),
         };
 
         let crate_annotation = &annotations.metadata.crates[&package_id];
 
+        let include_binaries = false;
+        let include_build_scripts = true;
         let context = CrateContext::new(
             crate_annotation,
             &annotations.metadata.packages,
             &annotations.lockfile.crates,
             &annotations.pairred_extras,
-            true,
+            &annotations.features,
+            include_binaries,
+            include_build_scripts,
         );
 
         assert_eq!(context.name, "openssl-sys");
         assert!(context.build_script_attrs.is_some());
         assert_eq!(
             context.targets,
-            vec![
+            BTreeSet::from([
                 Rule::Library(TargetAttributes {
                     crate_name: "openssl_sys".to_owned(),
                     crate_root: Some("src/lib.rs".to_owned()),
@@ -780,7 +869,7 @@
                     crate_root: Some("build/main.rs".to_owned()),
                     srcs: Glob::new_rust_srcs(),
                 })
-            ]
+            ]),
         );
 
         // Cargo build scripts should include all sources
@@ -792,29 +881,33 @@
         let annotations = build_script_annotations();
 
         let package_id = PackageId {
-            repr: "openssl-sys 0.9.72 (registry+https://github.com/rust-lang/crates.io-index)"
+            repr: "openssl-sys 0.9.87 (registry+https://github.com/rust-lang/crates.io-index)"
                 .to_owned(),
         };
 
         let crate_annotation = &annotations.metadata.crates[&package_id];
 
+        let include_binaries = false;
+        let include_build_scripts = false;
         let context = CrateContext::new(
             crate_annotation,
             &annotations.metadata.packages,
             &annotations.lockfile.crates,
             &annotations.pairred_extras,
-            false,
+            &annotations.features,
+            include_binaries,
+            include_build_scripts,
         );
 
         assert_eq!(context.name, "openssl-sys");
         assert!(context.build_script_attrs.is_none());
         assert_eq!(
             context.targets,
-            vec![Rule::Library(TargetAttributes {
+            BTreeSet::from([Rule::Library(TargetAttributes {
                 crate_name: "openssl_sys".to_owned(),
                 crate_root: Some("src/lib.rs".to_owned()),
                 srcs: Glob::new_rust_srcs(),
-            })],
+            })]),
         );
     }
 
@@ -829,23 +922,27 @@
 
         let crate_annotation = &annotations.metadata.crates[&package_id];
 
+        let include_binaries = false;
+        let include_build_scripts = false;
         let context = CrateContext::new(
             crate_annotation,
             &annotations.metadata.packages,
             &annotations.lockfile.crates,
             &annotations.pairred_extras,
-            false,
+            &annotations.features,
+            include_binaries,
+            include_build_scripts,
         );
 
         assert_eq!(context.name, "sysinfo");
         assert!(context.build_script_attrs.is_none());
         assert_eq!(
             context.targets,
-            vec![Rule::Library(TargetAttributes {
+            BTreeSet::from([Rule::Library(TargetAttributes {
                 crate_name: "sysinfo".to_owned(),
                 crate_root: Some("src/lib.rs".to_owned()),
                 srcs: Glob::new_rust_srcs(),
-            })],
+            })]),
         );
     }
 }
diff --git a/crate_universe/src/context/platforms.rs b/crate_universe/src/context/platforms.rs
index 724dbd0..fc4e789 100644
--- a/crate_universe/src/context/platforms.rs
+++ b/crate_universe/src/context/platforms.rs
@@ -1,4 +1,4 @@
-use std::collections::{BTreeMap, BTreeSet, HashMap};
+use std::collections::{BTreeMap, BTreeSet};
 
 use anyhow::{anyhow, Context, Result};
 use cfg_expr::targets::{get_builtin_target_by_triple, TargetInfo};
@@ -56,8 +56,8 @@
     // (`x86_64-unknown-linux-gun` vs `cfg(target = "x86_64-unkonwn-linux-gnu")`). So
     // in order to parse configurations, the text is renamed for the check but the
     // original is retained for comaptibility with the manifest.
-    let rename = |cfg: &str| -> String { format!("cfg(target = \"{}\")", cfg) };
-    let original_cfgs: HashMap<String, String> = configurations
+    let rename = |cfg: &str| -> String { format!("cfg(target = \"{cfg}\")") };
+    let original_cfgs: BTreeMap<String, String> = configurations
         .iter()
         .filter(|cfg| !cfg.starts_with("cfg("))
         .map(|cfg| (rename(cfg), cfg.clone()))
@@ -73,8 +73,8 @@
         })
         // Check the current configuration with against each supported triple
         .map(|cfg| {
-            let expression = Expression::parse(&cfg)
-                .context(format!("Failed to parse expression: '{}'", cfg))?;
+            let expression =
+                Expression::parse(&cfg).context(format!("Failed to parse expression: '{cfg}'"))?;
 
             let triples = target_infos
                 .iter()
@@ -116,6 +116,7 @@
             "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(),
@@ -165,9 +166,7 @@
         assert_eq!(configurations, BTreeMap::new(),)
     }
 
-    #[test]
-    fn resolve_targeted() {
-        let configuration = r#"cfg(target = "x86_64-unknown-linux-gnu")"#.to_owned();
+    fn mock_resolve_context(configuration: String) -> CrateContext {
         let mut deps = SelectList::default();
         deps.insert(
             CrateDependency {
@@ -175,10 +174,10 @@
                 target: "mock_crate_b".to_owned(),
                 alias: None,
             },
-            Some(configuration.clone()),
+            Some(configuration),
         );
 
-        let context = CrateContext {
+        CrateContext {
             name: "mock_crate_a".to_owned(),
             version: "0.1.0".to_owned(),
             common_attrs: CommonAttributes {
@@ -186,18 +185,39 @@
                 ..CommonAttributes::default()
             },
             ..CrateContext::default()
-        };
+        }
+    }
 
-        let configurations =
-            resolve_cfg_platforms(vec![&context], &supported_platform_triples()).unwrap();
+    #[test]
+    fn resolve_targeted() {
+        let data = BTreeMap::from([
+            (
+                r#"cfg(target = "x86_64-unknown-linux-gnu")"#.to_owned(),
+                BTreeSet::from(["x86_64-unknown-linux-gnu".to_owned()]),
+            ),
+            (
+                r#"cfg(any(target_os = "macos", target_os = "ios"))"#.to_owned(),
+                BTreeSet::from([
+                    "aarch64-apple-darwin".to_owned(),
+                    "aarch64-apple-ios".to_owned(),
+                    "i686-apple-darwin".to_owned(),
+                    "x86_64-apple-darwin".to_owned(),
+                    "x86_64-apple-ios".to_owned(),
+                ]),
+            ),
+        ]);
 
-        assert_eq!(
-            configurations,
-            BTreeMap::from([(
-                configuration,
-                BTreeSet::from(["x86_64-unknown-linux-gnu".to_owned()])
-            )])
-        );
+        data.into_iter().for_each(|(configuration, expectation)| {
+            let context = mock_resolve_context(configuration.clone());
+
+            let configurations =
+                resolve_cfg_platforms(vec![&context], &supported_platform_triples()).unwrap();
+
+            assert_eq!(
+                configurations,
+                BTreeMap::from([(configuration, expectation,)])
+            );
+        })
     }
 
     #[test]
diff --git a/crate_universe/src/lib.rs b/crate_universe/src/lib.rs
index ae85717..e6fbd6f 100644
--- a/crate_universe/src/lib.rs
+++ b/crate_universe/src/lib.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::large_enum_variant)]
+
 pub mod cli;
 
 mod config;
diff --git a/crate_universe/src/lockfile.rs b/crate_universe/src/lockfile.rs
index 65738a6..7eccea3 100644
--- a/crate_universe/src/lockfile.rs
+++ b/crate_universe/src/lockfile.rs
@@ -1,6 +1,6 @@
 //! Utility module for interracting with different kinds of lock files
 
-use std::collections::HashMap;
+use std::collections::BTreeMap;
 use std::convert::TryFrom;
 use std::ffi::OsStr;
 use std::fs;
@@ -14,13 +14,14 @@
 
 use crate::config::Config;
 use crate::context::Context;
+use crate::metadata::Cargo;
 use crate::splicing::{SplicingManifest, SplicingMetadata};
 
 pub fn lock_context(
     mut context: Context,
     config: &Config,
     splicing_manifest: &SplicingManifest,
-    cargo_bin: &Path,
+    cargo_bin: &Cargo,
     rustc_bin: &Path,
 ) -> Result<Context> {
     // Ensure there is no existing checksum which could impact the lockfile results
@@ -35,12 +36,12 @@
     })
 }
 
-/// Write a [crate::planning::PlannedContext] to disk
+/// Write a [crate::context::Context] to disk
 pub fn write_lockfile(lockfile: Context, path: &Path, dry_run: bool) -> Result<()> {
     let content = serde_json::to_string_pretty(&lockfile)?;
 
     if dry_run {
-        println!("{:#?}", content);
+        println!("{content:#?}");
     } else {
         // Ensure the parent directory exists
         if let Some(parent) = path.parent() {
@@ -61,11 +62,11 @@
         context: &Context,
         config: &Config,
         splicing_manifest: &SplicingManifest,
-        cargo_bin: &Path,
+        cargo_bin: &Cargo,
         rustc_bin: &Path,
     ) -> Result<Self> {
         let splicing_metadata = SplicingMetadata::try_from((*splicing_manifest).clone())?;
-        let cargo_version = Self::bin_version(cargo_bin)?;
+        let cargo_version = cargo_bin.full_version()?;
         let rustc_version = Self::bin_version(rustc_bin)?;
         let cargo_bazel_version = env!("CARGO_PKG_VERSION");
 
@@ -129,7 +130,7 @@
         Self(hasher.finalize().encode_hex::<String>())
     }
 
-    fn bin_version(binary: &Path) -> Result<String> {
+    pub fn bin_version(binary: &Path) -> Result<String> {
         let safe_vars = [OsStr::new("HOMEDRIVE"), OsStr::new("PATHEXT")];
         let env = std::env::vars_os().filter(|(var, _)| safe_vars.contains(&var.as_os_str()));
 
@@ -137,7 +138,8 @@
             .arg("--version")
             .env_clear()
             .envs(env)
-            .output()?;
+            .output()
+            .with_context(|| format!("Failed to run {} to get its version", binary.display()))?;
 
         if !output.status.success() {
             bail!("Failed to query cargo version")
@@ -152,7 +154,7 @@
         // computed consistently. If a new binary is released then this
         // condition should be removed
         // https://github.com/rust-lang/cargo/issues/10547
-        let corrections = HashMap::from([
+        let corrections = BTreeMap::from([
             (
                 "cargo 1.60.0 (d1fd9fe 2022-03-01)",
                 "cargo 1.60.0 (d1fd9fe2c 2022-03-01)",
@@ -209,7 +211,7 @@
 
         assert_eq!(
             digest,
-            Digest("9711073103bd532b7d9c2e32e805280d29fc8591c3e76f9fe489fc372e2866db".to_owned())
+            Digest("fcca6635448d70091bffb6409f5edb153a46fcf7e889e39a33a9b9ff6e345ca0".to_owned())
         );
     }
 
@@ -217,6 +219,7 @@
     fn digest_with_config() {
         let context = Context::default();
         let config = Config {
+            generate_binaries: false,
             generate_build_scripts: false,
             annotations: BTreeMap::from([(
                 CrateId::new("rustonomicon".to_owned(), "1.0.0".to_owned()),
@@ -229,6 +232,7 @@
             supported_platform_triples: BTreeSet::from([
                 "aarch64-apple-darwin".to_owned(),
                 "aarch64-unknown-linux-gnu".to_owned(),
+                "aarch64-pc-windows-msvc".to_owned(),
                 "wasm32-unknown-unknown".to_owned(),
                 "wasm32-wasi".to_owned(),
                 "x86_64-apple-darwin".to_owned(),
@@ -252,7 +256,7 @@
 
         assert_eq!(
             digest,
-            Digest("756a613410573552bb8a85d6fcafd24a9df3000b8d943bf74c38bda9c306ef0e".to_owned())
+            Digest("c90e7e5a98e49884c9962f99aea5cf20d5a32df243dbb549001c50badf0a02d3".to_owned())
         );
     }
 
@@ -283,7 +287,7 @@
 
         assert_eq!(
             digest,
-            Digest("851b789765d8ee248fd3d55840ffd702ba2f8b0ca6aed2faa45ea63d1b011a99".to_owned())
+            Digest("e199dd859bd5b75d6b152f364f8cc6ad6c3a2a68ae777dfb8b250c2d90e35f28".to_owned())
         );
     }
 
@@ -332,7 +336,7 @@
 
         assert_eq!(
             digest,
-            Digest("a9f7ea66f1b04331f8e09c64cd0b972e4c2a136907d7ef90e81ae2654e3c002c".to_owned())
+            Digest("0222be160f1031346cc209a8732c678bf32acb08f891fdfa0e9965d0ad22a33a".to_owned())
         );
     }
 }
diff --git a/crate_universe/src/metadata.rs b/crate_universe/src/metadata.rs
index 57d90e7..4e8cf1a 100644
--- a/crate_universe/src/metadata.rs
+++ b/crate_universe/src/metadata.rs
@@ -3,15 +3,23 @@
 mod dependency;
 mod metadata_annotation;
 
+use std::collections::{BTreeMap, BTreeSet};
 use std::env;
 use std::fs;
+use std::io::BufRead;
 use std::path::{Path, PathBuf};
 use std::process::Command;
 use std::str::FromStr;
+use std::sync::{Arc, Mutex};
 
-use anyhow::{bail, Context, Result};
+use crate::lockfile::Digest;
+use anyhow::{anyhow, bail, Context, Result};
 use cargo_lock::Lockfile as CargoLockfile;
 use cargo_metadata::{Metadata as CargoMetadata, MetadataCommand};
+use semver::Version;
+
+use crate::config::CrateId;
+use crate::utils::starlark::SelectList;
 
 pub use self::dependency::*;
 pub use self::metadata_annotation::*;
@@ -25,7 +33,7 @@
 /// Generates Cargo metadata and a lockfile from a provided manifest.
 pub struct Generator {
     /// The path to a `cargo` binary
-    cargo_bin: PathBuf,
+    cargo_bin: Cargo,
 
     /// The path to a `rustc` binary
     rustc_bin: PathBuf,
@@ -34,12 +42,14 @@
 impl Generator {
     pub fn new() -> Self {
         Generator {
-            cargo_bin: PathBuf::from(env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())),
+            cargo_bin: Cargo::new(PathBuf::from(
+                env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()),
+            )),
             rustc_bin: PathBuf::from(env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string())),
         }
     }
 
-    pub fn with_cargo(mut self, cargo_bin: PathBuf) -> Self {
+    pub fn with_cargo(mut self, cargo_bin: Cargo) -> Self {
         self.cargo_bin = cargo_bin;
         self
     }
@@ -64,8 +74,9 @@
             cargo_lock::Lockfile::load(lock_path)?
         };
 
-        let metadata = MetadataCommand::new()
-            .cargo_path(&self.cargo_bin)
+        let metadata = self
+            .cargo_bin
+            .metadata_command()?
             .current_dir(manifest_dir)
             .manifest_path(manifest_path.as_ref())
             .other_options(["--locked".to_owned()])
@@ -75,8 +86,76 @@
     }
 }
 
+/// Cargo encapsulates a path to a `cargo` binary.
+/// Any invocations of `cargo` (either as a `std::process::Command` or via `cargo_metadata`) should
+/// go via this wrapper to ensure that any environment variables needed are set appropriately.
+#[derive(Clone)]
+pub struct Cargo {
+    path: PathBuf,
+    full_version: Arc<Mutex<Option<String>>>,
+}
+
+impl Cargo {
+    pub fn new(path: PathBuf) -> Cargo {
+        Cargo {
+            path,
+            full_version: Arc::new(Mutex::new(None)),
+        }
+    }
+
+    /// Returns a new `Command` for running this cargo.
+    pub fn command(&self) -> Result<Command> {
+        let mut command = Command::new(&self.path);
+        command.envs(self.env()?);
+        Ok(command)
+    }
+
+    /// Returns a new `MetadataCommand` using this cargo.
+    pub fn metadata_command(&self) -> Result<MetadataCommand> {
+        let mut command = MetadataCommand::new();
+        command.cargo_path(&self.path);
+        for (k, v) in self.env()? {
+            command.env(k, v);
+        }
+        Ok(command)
+    }
+
+    /// Returns the output of running `cargo version`, trimming any leading or trailing whitespace.
+    /// This function performs normalisation to work around `<https://github.com/rust-lang/cargo/issues/10547>`
+    pub fn full_version(&self) -> Result<String> {
+        let mut full_version = self.full_version.lock().unwrap();
+        if full_version.is_none() {
+            let observed_version = Digest::bin_version(&self.path)?;
+            *full_version = Some(observed_version);
+        }
+        Ok(full_version.clone().unwrap())
+    }
+
+    pub fn use_sparse_registries_for_crates_io(&self) -> Result<bool> {
+        let full_version = self.full_version()?;
+        let version_str = full_version.split(' ').nth(1);
+        if let Some(version_str) = version_str {
+            let version = Version::parse(version_str).context("Failed to parse cargo version")?;
+            return Ok(version.major >= 1 && version.minor >= 68);
+        }
+        bail!("Couldn't parse cargo version");
+    }
+
+    fn env(&self) -> Result<BTreeMap<String, String>> {
+        let mut map = BTreeMap::new();
+
+        if self.use_sparse_registries_for_crates_io()? {
+            map.insert(
+                "CARGO_REGISTRIES_CRATES_IO_PROTOCOL".into(),
+                "sparse".into(),
+            );
+        }
+        Ok(map)
+    }
+}
+
 /// A configuration desrcibing how to invoke [cargo update](https://doc.rust-lang.org/cargo/commands/cargo-update.html).
-#[derive(Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum CargoUpdateRequest {
     /// Translates to an unrestricted `cargo update` command
     Eager,
@@ -100,11 +179,11 @@
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let lower = s.to_lowercase();
 
-        if ["1", "yes", "true", "on"].contains(&lower.as_str()) {
+        if ["eager", "full", "all"].contains(&lower.as_str()) {
             return Ok(Self::Eager);
         }
 
-        if ["workspace", "minimal"].contains(&lower.as_str()) {
+        if ["1", "yes", "true", "on", "workspace", "minimal"].contains(&lower.as_str()) {
             return Ok(Self::Workspace);
         }
 
@@ -136,11 +215,12 @@
     }
 
     /// Calls `cargo update` with arguments specific to the state of the current variant.
-    pub fn update(&self, manifest: &Path, cargo_bin: &Path, rustc_bin: &Path) -> Result<()> {
+    pub fn update(&self, manifest: &Path, cargo_bin: &Cargo, rustc_bin: &Path) -> Result<()> {
         let manifest_dir = manifest.parent().unwrap();
 
         // Simply invoke `cargo update`
-        let output = Command::new(cargo_bin)
+        let output = cargo_bin
+            .command()?
             // Cargo detects config files based on `pwd` when running so
             // to ensure user provided Cargo config files are used, it's
             // critical to set the working directory to the manifest dir.
@@ -149,7 +229,7 @@
             .arg("--manifest-path")
             .arg(manifest)
             .args(self.get_update_args())
-            .env("RUSTC", &rustc_bin)
+            .env("RUSTC", rustc_bin)
             .output()
             .with_context(|| {
                 format!(
@@ -170,14 +250,14 @@
 
 pub struct LockGenerator {
     /// The path to a `cargo` binary
-    cargo_bin: PathBuf,
+    cargo_bin: Cargo,
 
     /// The path to a `rustc` binary
     rustc_bin: PathBuf,
 }
 
 impl LockGenerator {
-    pub fn new(cargo_bin: PathBuf, rustc_bin: PathBuf) -> Self {
+    pub fn new(cargo_bin: Cargo, rustc_bin: PathBuf) -> Self {
         Self {
             cargo_bin,
             rustc_bin,
@@ -205,7 +285,7 @@
             if generated_lockfile_path.exists() {
                 fs::remove_file(&generated_lockfile_path)?;
             }
-            fs::copy(&lock, &generated_lockfile_path)?;
+            fs::copy(lock, &generated_lockfile_path)?;
 
             if let Some(request) = update_request {
                 request.update(manifest_path, &self.cargo_bin, &self.rustc_bin)?;
@@ -213,7 +293,9 @@
 
             // Ensure the Cargo cache is up to date to simulate the behavior
             // of having just generated a new one
-            let output = Command::new(&self.cargo_bin)
+            let output = self
+                .cargo_bin
+                .command()?
                 // Cargo detects config files based on `pwd` when running so
                 // to ensure user provided Cargo config files are used, it's
                 // critical to set the working directory to the manifest dir.
@@ -239,7 +321,9 @@
             }
         } else {
             // Simply invoke `cargo generate-lockfile`
-            let output = Command::new(&self.cargo_bin)
+            let output = self
+                .cargo_bin
+                .command()?
                 // Cargo detects config files based on `pwd` when running so
                 // to ensure user provided Cargo config files are used, it's
                 // critical to set the working directory to the manifest dir.
@@ -271,14 +355,14 @@
 /// A generator which runs `cargo vendor` on a given manifest
 pub struct VendorGenerator {
     /// The path to a `cargo` binary
-    cargo_bin: PathBuf,
+    cargo_bin: Cargo,
 
     /// The path to a `rustc` binary
     rustc_bin: PathBuf,
 }
 
 impl VendorGenerator {
-    pub fn new(cargo_bin: PathBuf, rustc_bin: PathBuf) -> Self {
+    pub fn new(cargo_bin: Cargo, rustc_bin: PathBuf) -> Self {
         Self {
             cargo_bin,
             rustc_bin,
@@ -289,7 +373,9 @@
         let manifest_dir = manifest_path.parent().unwrap();
 
         // Simply invoke `cargo generate-lockfile`
-        let output = Command::new(&self.cargo_bin)
+        let output = self
+            .cargo_bin
+            .command()?
             // Cargo detects config files based on `pwd` when running so
             // to ensure user provided Cargo config files are used, it's
             // critical to set the working directory to the manifest dir.
@@ -319,6 +405,177 @@
     }
 }
 
+/// A generate which computes per-platform feature sets.
+pub struct FeatureGenerator {
+    /// The path to a `cargo` binary
+    cargo_bin: Cargo,
+
+    /// The path to a `rustc` binary
+    rustc_bin: PathBuf,
+}
+
+impl FeatureGenerator {
+    pub fn new(cargo_bin: Cargo, rustc_bin: PathBuf) -> Self {
+        Self {
+            cargo_bin,
+            rustc_bin,
+        }
+    }
+
+    /// Computes the set of enabled features for each target triplet for each crate.
+    pub fn generate(
+        &self,
+        manifest_path: &Path,
+        platform_triples: &BTreeSet<String>,
+    ) -> Result<BTreeMap<CrateId, SelectList<String>>> {
+        let manifest_dir = manifest_path.parent().unwrap();
+        let mut target_to_child = BTreeMap::new();
+        for target in platform_triples {
+            // We use `cargo tree` here because `cargo metadata` doesn't report
+            // back target-specific features (enabled with `resolver = "2"`).
+            // This is unfortunately a bit of a hack. See:
+            // - https://github.com/rust-lang/cargo/issues/9863
+            // - https://github.com/bazelbuild/rules_rust/issues/1662
+            let output = self
+                .cargo_bin
+                .command()?
+                .current_dir(manifest_dir)
+                .arg("tree")
+                .arg("--locked")
+                .arg("--manifest-path")
+                .arg(manifest_path)
+                .arg("--prefix=none")
+                // https://doc.rust-lang.org/cargo/commands/cargo-tree.html#tree-formatting-options
+                .arg("--format=|{p}|{f}|")
+                .arg("--color=never")
+                .arg("--workspace")
+                .arg("--target")
+                .arg(target)
+                .env("RUSTC", &self.rustc_bin)
+                .stdout(std::process::Stdio::piped())
+                .stderr(std::process::Stdio::piped())
+                .spawn()
+                .with_context(|| {
+                    format!(
+                        "Error spawning cargo in child process to compute features for target '{}', manifest path '{}'",
+                        target,
+                        manifest_path.display()
+                    )
+                })?;
+            target_to_child.insert(target, output);
+        }
+        let mut crate_features = BTreeMap::<CrateId, BTreeMap<String, BTreeSet<String>>>::new();
+        for (target, child) in target_to_child.into_iter() {
+            let output = child
+                .wait_with_output()
+                .with_context(|| {
+                    format!(
+                        "Error running cargo in child process to compute features for target '{}', manifest path '{}'",
+                        target,
+                        manifest_path.display()
+                    )
+                })?;
+            if !output.status.success() {
+                eprintln!("{}", String::from_utf8_lossy(&output.stdout));
+                eprintln!("{}", String::from_utf8_lossy(&output.stderr));
+                bail!(format!("Failed to run cargo tree: {}", output.status))
+            }
+            for (crate_id, features) in
+                parse_features_from_cargo_tree_output(output.stdout.lines())?
+            {
+                crate_features
+                    .entry(crate_id)
+                    .or_default()
+                    .insert(target.to_owned(), features);
+            }
+        }
+        let mut result = BTreeMap::<CrateId, SelectList<String>>::new();
+        for (crate_id, features) in crate_features.into_iter() {
+            let common = features
+                .iter()
+                .fold(
+                    None,
+                    |common: Option<BTreeSet<String>>, (_, features)| match common {
+                        Some(common) => Some(common.intersection(features).cloned().collect()),
+                        None => Some(features.clone()),
+                    },
+                )
+                .unwrap_or_default();
+            let mut select_list = SelectList::default();
+            for (target, fs) in features {
+                if fs != common {
+                    for f in fs {
+                        select_list.insert(f, Some(target.clone()));
+                    }
+                }
+            }
+            for f in common {
+                select_list.insert(f, None);
+            }
+            result.insert(crate_id, select_list);
+        }
+        Ok(result)
+    }
+}
+
+/// Parses the output of `cargo tree --format=|{p}|{f}|`. Other flags may be
+/// passed to `cargo tree` as well, but this format is critical.
+fn parse_features_from_cargo_tree_output<I, S, E>(
+    lines: I,
+) -> Result<BTreeMap<CrateId, BTreeSet<String>>>
+where
+    I: Iterator<Item = std::result::Result<S, E>>,
+    S: AsRef<str>,
+    E: std::error::Error + Sync + Send + 'static,
+{
+    let mut crate_features = BTreeMap::<CrateId, BTreeSet<String>>::new();
+    for line in lines {
+        let line = line?;
+        let line = line.as_ref();
+        if line.is_empty() {
+            continue;
+        }
+        let parts = line.split('|').collect::<Vec<_>>();
+        if parts.len() != 4 {
+            bail!("Unexpected line '{}'", line);
+        }
+        // We expect the crate id (parts[1]) to be either
+        // "<crate name> v<crate version>" or
+        // "<crate name> v<crate version> (<path>)"
+        // "<crate name> v<crate version> (proc-macro) (<path>)"
+        // https://github.com/rust-lang/cargo/blob/19f952f160d4f750d1e12fad2bf45e995719673d/src/cargo/ops/tree/mod.rs#L281
+        let crate_id_parts = parts[1].split(' ').collect::<Vec<_>>();
+        if crate_id_parts.len() < 2 && crate_id_parts.len() > 4 {
+            bail!(
+                "Unexpected crate id format '{}' when parsing 'cargo tree' output.",
+                parts[1]
+            );
+        }
+        let crate_id = CrateId::new(
+            crate_id_parts[0].to_owned(),
+            crate_id_parts[1]
+                .strip_prefix('v')
+                .ok_or_else(|| {
+                    anyhow!(
+                        "Unexpected crate version '{}' when parsing 'cargo tree' output.",
+                        crate_id_parts[1]
+                    )
+                })?
+                .to_owned(),
+        );
+        let mut features = if parts[2].is_empty() {
+            BTreeSet::new()
+        } else {
+            parts[2].split(',').map(str::to_owned).collect()
+        };
+        crate_features
+            .entry(crate_id)
+            .or_default()
+            .append(&mut features);
+    }
+    Ok(crate_features)
+}
+
 /// A helper function for writing Cargo metadata to a file.
 pub fn write_metadata(path: &Path, metadata: &cargo_metadata::Metadata) -> Result<()> {
     let content =
@@ -361,7 +618,7 @@
 
     #[test]
     fn deserialize_cargo_update_request_for_eager() {
-        for value in ["1", "yes", "true", "on"] {
+        for value in ["all", "full", "eager"] {
             let request = CargoUpdateRequest::from_str(value).unwrap();
 
             assert_eq!(request, CargoUpdateRequest::Eager);
@@ -370,7 +627,7 @@
 
     #[test]
     fn deserialize_cargo_update_request_for_workspace() {
-        for value in ["workspace", "minimal"] {
+        for value in ["1", "true", "yes", "on", "workspace", "minimal"] {
             let request = CargoUpdateRequest::from_str(value).unwrap();
 
             assert_eq!(request, CargoUpdateRequest::Workspace);
@@ -402,4 +659,60 @@
             }
         );
     }
+
+    #[test]
+    fn parse_features_from_cargo_tree_output_prefix_none() {
+        assert_eq!(
+            parse_features_from_cargo_tree_output(
+                vec![
+                    Ok::<&str, std::io::Error>(""), // Blank lines are ignored.
+                    Ok("|multi_cfg_dep v0.1.0 (/private/tmp/ct)||"),
+                    Ok("|chrono v0.4.24|default,std|"),
+                    Ok("|cpufeatures v0.2.1||"),
+                    Ok("|libc v0.2.117|default,std|"),
+                    Ok("|serde_derive v1.0.152 (proc-macro) (*)||"),
+                    Ok("|chrono v0.4.24|default,std,serde|"),
+                ]
+                .into_iter()
+            )
+            .unwrap(),
+            BTreeMap::from([
+                (
+                    CrateId {
+                        name: "multi_cfg_dep".to_owned(),
+                        version: "0.1.0".to_owned()
+                    },
+                    BTreeSet::from([])
+                ),
+                (
+                    CrateId {
+                        name: "cpufeatures".to_owned(),
+                        version: "0.2.1".to_owned()
+                    },
+                    BTreeSet::from([])
+                ),
+                (
+                    CrateId {
+                        name: "libc".to_owned(),
+                        version: "0.2.117".to_owned()
+                    },
+                    BTreeSet::from(["default".to_owned(), "std".to_owned()])
+                ),
+                (
+                    CrateId {
+                        name: "serde_derive".to_owned(),
+                        version: "1.0.152".to_owned()
+                    },
+                    BTreeSet::from([])
+                ),
+                (
+                    CrateId {
+                        name: "chrono".to_owned(),
+                        version: "0.4.24".to_owned()
+                    },
+                    BTreeSet::from(["default".to_owned(), "std".to_owned(), "serde".to_owned()])
+                ),
+            ])
+        );
+    }
 }
diff --git a/crate_universe/src/metadata/dependency.rs b/crate_universe/src/metadata/dependency.rs
index 7a98ae2..ceb4931 100644
--- a/crate_universe/src/metadata/dependency.rs
+++ b/crate_universe/src/metadata/dependency.rs
@@ -1,6 +1,8 @@
-///! Gathering dependencies is the largest part of annotating.
+//! Gathering dependencies is the largest part of annotating.
+
 use anyhow::{bail, Result};
 use cargo_metadata::{Metadata as CargoMetadata, Node, NodeDep, Package, PackageId};
+use cargo_platform::Platform;
 use serde::{Deserialize, Serialize};
 
 use crate::utils::sanitize_module_name;
@@ -44,8 +46,8 @@
                 .partition(|dep| is_dev_dependency(dep));
 
             (
-                collect_deps_selectable(dev, metadata),
-                collect_deps_selectable(normal, metadata),
+                collect_deps_selectable(node, dev, metadata),
+                collect_deps_selectable(node, normal, metadata),
             )
         };
 
@@ -60,11 +62,13 @@
                 .partition(|dep| is_dev_dependency(dep));
 
             (
-                collect_deps_selectable(dev, metadata),
-                collect_deps_selectable(normal, metadata),
+                collect_deps_selectable(node, dev, metadata),
+                collect_deps_selectable(node, normal, metadata),
             )
         };
 
+        // For rules on build script dependencies see:
+        //  https://doc.rust-lang.org/cargo/reference/build-scripts.html#build-dependencies
         let (build_proc_macro_deps, mut build_deps) = {
             let (proc_macro, normal) = node
                 .deps
@@ -76,8 +80,8 @@
                 .partition(|dep| is_proc_macro_package(&metadata[&dep.pkg]));
 
             (
-                collect_deps_selectable(proc_macro, metadata),
-                collect_deps_selectable(normal, metadata),
+                collect_deps_selectable(node, proc_macro, metadata),
+                collect_deps_selectable(node, normal, metadata),
             )
         };
 
@@ -87,6 +91,7 @@
         // on a `*-sys` crate for itself, so would it's build script. Hopefully this is correct.
         // https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
         // https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages
+        // https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate
         let sys_name = format!("{}-sys", &metadata[&node.id].name);
         normal_deps.configurations().into_iter().for_each(|config| {
             normal_deps
@@ -113,7 +118,39 @@
     }
 }
 
+/// For details on optional dependencies see [the Rust docs](https://doc.rust-lang.org/cargo/reference/features.html#optional-dependencies).
+fn is_optional_crate_enabled(
+    parent: &Node,
+    dep: &NodeDep,
+    target: Option<&Platform>,
+    metadata: &CargoMetadata,
+) -> bool {
+    let pkg = &metadata[&parent.id];
+
+    let mut enabled_deps = pkg
+        .features
+        .iter()
+        .filter(|(pkg_feature, _)| parent.features.contains(pkg_feature))
+        .flat_map(|(_, features)| features)
+        .filter_map(|f| f.strip_prefix("dep:"));
+
+    // if the crate is marked as optional dependency, we check whether
+    // a feature prefixed with dep: is enabled
+    if let Some(toml_dep) = pkg
+        .dependencies
+        .iter()
+        .filter(|&d| d.target.as_ref() == target)
+        .filter(|&d| d.optional)
+        .find(|&d| sanitize_module_name(&d.name) == dep.name)
+    {
+        enabled_deps.any(|d| d == toml_dep.name.as_str())
+    } else {
+        true
+    }
+}
+
 fn collect_deps_selectable(
+    node: &Node,
     deps: Vec<&NodeDep>,
     metadata: &cargo_metadata::Metadata,
 ) -> SelectList<Dependency> {
@@ -126,17 +163,19 @@
         let alias = get_target_alias(&dep.name, dep_pkg);
 
         for kind_info in &dep.dep_kinds {
-            selectable.insert(
-                Dependency {
-                    package_id: dep.pkg.clone(),
-                    target_name: target_name.clone(),
-                    alias: alias.clone(),
-                },
-                kind_info
-                    .target
-                    .as_ref()
-                    .map(|platform| platform.to_string()),
-            );
+            if is_optional_crate_enabled(node, dep, kind_info.target.as_ref(), metadata) {
+                selectable.insert(
+                    Dependency {
+                        package_id: dep.pkg.clone(),
+                        target_name: target_name.clone(),
+                        alias: alias.clone(),
+                    },
+                    kind_info
+                        .target
+                        .as_ref()
+                        .map(|platform| platform.to_string()),
+                );
+            }
         }
     }
 
@@ -227,7 +266,7 @@
 /// for targets where packages (packages[#].targets[#].name) uses crate names. In order to
 /// determine whether or not a dependency is aliased, we compare it with all available targets
 /// on it's package. Note that target names are not guaranteed to be module names where Node
-/// dependnecies are, so we need to do a conversion to check for this
+/// dependencies are, so we need to do a conversion to check for this
 fn get_target_alias(target_name: &str, package: &Package) -> Option<String> {
     match package
         .targets
@@ -406,6 +445,69 @@
     }
 
     #[test]
+    fn sys_crate_with_build_script() {
+        let metadata = metadata::build_scripts();
+
+        let libssh2 = find_metadata_node("libssh2-sys", &metadata);
+        let libssh2_depset = DependencySet::new_for_node(libssh2, &metadata);
+
+        // Collect build dependencies into a set
+        let build_deps: BTreeSet<String> = libssh2_depset
+            .build_deps
+            .configurations()
+            .into_iter()
+            .flat_map(|conf| {
+                libssh2_depset
+                    .build_deps
+                    .get_iter(conf)
+                    .unwrap()
+                    .map(|dep| dep.package_id.repr.clone())
+                    .collect::<Vec<String>>()
+            })
+            .collect();
+
+        assert_eq!(
+            BTreeSet::from([
+                "cc 1.0.72 (registry+https://github.com/rust-lang/crates.io-index)".to_owned(),
+                "pkg-config 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)"
+                    .to_owned(),
+                "vcpkg 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)".to_owned()
+            ]),
+            build_deps,
+        );
+
+        // Collect normal dependencies into a set
+        let normal_deps: BTreeSet<String> = libssh2_depset
+            .normal_deps
+            .configurations()
+            .into_iter()
+            .flat_map(|conf| {
+                libssh2_depset
+                    .normal_deps
+                    .get_iter(conf)
+                    .unwrap()
+                    .map(|dep| dep.package_id.to_string())
+                    .collect::<Vec<String>>()
+            })
+            .collect();
+
+        assert_eq!(
+            BTreeSet::from([
+                "libc 0.2.112 (registry+https://github.com/rust-lang/crates.io-index)".to_owned(),
+                "libz-sys 1.1.8 (registry+https://github.com/rust-lang/crates.io-index)".to_owned(),
+                "openssl-sys 0.9.87 (registry+https://github.com/rust-lang/crates.io-index)"
+                    .to_owned(),
+            ]),
+            normal_deps,
+        );
+
+        assert!(libssh2_depset.proc_macro_deps.is_empty());
+        assert!(libssh2_depset.normal_dev_deps.is_empty());
+        assert!(libssh2_depset.proc_macro_dev_deps.is_empty());
+        assert!(libssh2_depset.build_proc_macro_deps.is_empty());
+    }
+
+    #[test]
     fn tracked_aliases() {
         let metadata = metadata::alias();
 
@@ -465,7 +567,7 @@
         let node = find_metadata_node("cpufeatures", &metadata);
         let dependencies = DependencySet::new_for_node(node, &metadata);
 
-        let libc_cfgs: Vec<Option<String>> = dependencies
+        let libc_cfgs: BTreeSet<String> = dependencies
             .normal_deps
             .configurations()
             .into_iter()
@@ -477,17 +579,73 @@
                     .filter(|dep| dep.target_name == "libc")
                     .map(move |_| conf.cloned())
             })
+            .flatten()
             .collect();
 
-        assert_eq!(libc_cfgs.len(), 2);
-
-        let cfg_strs: BTreeSet<String> = libc_cfgs.into_iter().flatten().collect();
         assert_eq!(
-            cfg_strs,
             BTreeSet::from([
-                "aarch64-apple-darwin".to_owned(),
+                "aarch64-linux-android".to_owned(),
                 "cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))".to_owned(),
-            ])
+                "cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))".to_owned(),
+            ]),
+            libc_cfgs,
         );
     }
+
+    #[test]
+    fn optional_deps_disabled() {
+        let metadata = metadata::optional_deps_disabled();
+
+        let node = find_metadata_node("clap", &metadata);
+        let dependencies = DependencySet::new_for_node(node, &metadata);
+
+        assert!(!dependencies
+            .normal_deps
+            .get_iter(None)
+            .expect("Iterating over known keys should never panic")
+            .any(|dep| { dep.target_name == "is-terminal" || dep.target_name == "termcolor" }));
+    }
+
+    #[test]
+    fn optional_deps_enabled() {
+        let metadata = metadata::optional_deps_enabled();
+
+        let clap = find_metadata_node("clap", &metadata);
+        let clap_depset = DependencySet::new_for_node(clap, &metadata);
+        assert_eq!(
+            clap_depset
+                .normal_deps
+                .get_iter(None)
+                .expect("Iterating over known keys should never panic")
+                .filter(|dep| {
+                    dep.target_name == "is-terminal" || dep.target_name == "termcolor"
+                })
+                .count(),
+            2
+        );
+
+        let notify = find_metadata_node("notify", &metadata);
+        let notify_depset = DependencySet::new_for_node(notify, &metadata);
+
+        // mio is not present in the common list of dependencies
+        assert!(!notify_depset
+            .normal_deps
+            .get_iter(None)
+            .expect("Iterating over known keys should never panic")
+            .any(|dep| { dep.target_name == "mio" }));
+
+        // mio is a dependency on linux
+        assert!(notify_depset
+            .normal_deps
+            .get_iter(Some(&"cfg(target_os = \"linux\")".to_string()))
+            .expect("Iterating over known keys should never panic")
+            .any(|dep| { dep.target_name == "mio" }));
+
+        // mio is marked optional=true on macos
+        assert!(!notify_depset
+            .normal_deps
+            .get_iter(Some(&"cfg(target_os = \"macos\")".to_string()))
+            .expect("Iterating over known keys should never panic")
+            .any(|dep| { dep.target_name == "mio" }));
+    }
 }
diff --git a/crate_universe/src/metadata/metadata_annotation.rs b/crate_universe/src/metadata/metadata_annotation.rs
index c045519..b19d696 100644
--- a/crate_universe/src/metadata/metadata_annotation.rs
+++ b/crate_universe/src/metadata/metadata_annotation.rs
@@ -12,6 +12,7 @@
 use crate::config::{Commitish, Config, CrateAnnotations, CrateId};
 use crate::metadata::dependency::DependencySet;
 use crate::splicing::{SourceInfo, WorkspaceMetadata};
+use crate::utils::starlark::SelectList;
 
 pub type CargoMetadata = cargo_metadata::Metadata;
 pub type CargoLockfile = cargo_lock::Lockfile;
@@ -359,6 +360,9 @@
 
     /// Pairred crate annotations
     pub pairred_extras: BTreeMap<CrateId, PairredExtras>,
+
+    /// Feature set for each target triplet and crate.
+    pub features: BTreeMap<CrateId, SelectList<String>>,
 }
 
 impl Annotations {
@@ -415,12 +419,15 @@
             );
         }
 
+        let features = metadata_annotation.workspace_metadata.features.clone();
+
         // Annotate metadata
         Ok(Annotations {
             metadata: metadata_annotation,
             lockfile: lockfile_annotation,
             config,
             pairred_extras,
+            features,
         })
     }
 }
@@ -549,7 +556,7 @@
         let result = Annotations::new(test::metadata::no_deps(), test::lockfile::no_deps(), config);
         assert!(result.is_err());
 
-        let result_str = format!("{:?}", result);
+        let result_str = format!("{result:?}");
         assert!(result_str.contains("Unused annotations were provided. Please remove them"));
         assert!(result_str.contains("mock-crate"));
     }
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(' ', "")
+        ));
+    }
 }
diff --git a/crate_universe/src/rendering/template_engine.rs b/crate_universe/src/rendering/template_engine.rs
index 3785b34..28fff72 100644
--- a/crate_universe/src/rendering/template_engine.rs
+++ b/crate_universe/src/rendering/template_engine.rs
@@ -6,15 +6,14 @@
 use serde_json::{from_value, to_value, Value};
 use tera::{self, Tera};
 
-use crate::config::{CrateId, RenderConfig};
+use crate::config::RenderConfig;
 use crate::context::Context;
 use crate::rendering::{
     render_crate_bazel_label, render_crate_bazel_repository, render_crate_build_file,
-    render_module_label, render_platform_constraint_label,
+    render_module_label, Platforms,
 };
-use crate::utils::sanitize_module_name;
 use crate::utils::sanitize_repository_name;
-use crate::utils::starlark::{SelectStringDict, SelectStringList};
+use crate::utils::starlark::SelectStringList;
 
 pub struct TemplateEngine {
     engine: Tera,
@@ -26,55 +25,6 @@
         let mut tera = Tera::default();
         tera.add_raw_templates(vec![
             (
-                "partials/crate/aliases.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/crate/aliases.j2"
-                )),
-            ),
-            (
-                "partials/crate/binary.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/crate/binary.j2"
-                )),
-            ),
-            (
-                "partials/crate/build_script.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/crate/build_script.j2"
-                )),
-            ),
-            (
-                "partials/crate/common_attrs.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/crate/common_attrs.j2"
-                )),
-            ),
-            (
-                "partials/crate/deps.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/crate/deps.j2"
-                )),
-            ),
-            (
-                "partials/crate/library.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/crate/library.j2"
-                )),
-            ),
-            (
-                "partials/crate/proc_macro.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/crate/proc_macro.j2"
-                )),
-            ),
-            (
                 "partials/module/aliases_map.j2",
                 include_str!(concat!(
                     env!("CARGO_MANIFEST_DIR"),
@@ -103,27 +53,6 @@
                 )),
             ),
             (
-                "partials/starlark/glob.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/starlark/glob.j2"
-                )),
-            ),
-            (
-                "partials/starlark/selectable_dict.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/starlark/selectable_dict.j2"
-                )),
-            ),
-            (
-                "partials/starlark/selectable_list.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/partials/starlark/selectable_list.j2"
-                )),
-            ),
-            (
                 "partials/header.j2",
                 include_str!(concat!(
                     env!("CARGO_MANIFEST_DIR"),
@@ -131,20 +60,6 @@
                 )),
             ),
             (
-                "crate_build_file.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/crate_build_file.j2"
-                )),
-            ),
-            (
-                "module_build_file.j2",
-                include_str!(concat!(
-                    env!("CARGO_MANIFEST_DIR"),
-                    "/src/rendering/templates/module_build_file.j2"
-                )),
-            ),
-            (
                 "module_bzl.j2",
                 include_str!(concat!(
                     env!("CARGO_MANIFEST_DIR"),
@@ -180,18 +95,12 @@
             ),
         );
         tera.register_function(
-            "platform_label",
-            platform_label_fn_generator(render_config.platforms_template.clone()),
-        );
-        tera.register_function("sanitize_module_name", sanitize_module_name_fn);
-        tera.register_function(
             "crates_module_label",
             module_label_fn_generator(render_config.crates_module_template.clone()),
         );
 
         let mut context = tera::Context::new();
         context.insert("default_select_list", &SelectStringList::default());
-        context.insert("default_select_dict", &SelectStringDict::default());
         context.insert("repository_name", &render_config.repository_name);
         context.insert("vendor_mode", &render_config.vendor_mode);
         context.insert("regen_command", &render_config.regen_command);
@@ -199,7 +108,7 @@
         context.insert(
             "default_package_name",
             &match render_config.default_package_name.as_ref() {
-                Some(pkg_name) => format!("\"{}\"", pkg_name),
+                Some(pkg_name) => format!("\"{pkg_name}\""),
                 None => "None".to_owned(),
             },
         );
@@ -214,53 +123,20 @@
         self.context.clone()
     }
 
-    pub fn render_crate_build_files<'a>(
-        &self,
-        ctx: &'a Context,
-    ) -> Result<HashMap<&'a CrateId, String>> {
-        // Create the render context with the global planned context to be
-        // reused when rendering crates.
-        let mut context = self.new_tera_ctx();
-        context.insert("context", ctx);
-
-        ctx.crates
-            .iter()
-            .map(|(id, _)| {
-                let aliases = ctx.crate_aliases(id, false, false);
-                let build_aliases = ctx.crate_aliases(id, true, false);
-
-                context.insert("crate_id", &id);
-                context.insert("common_aliases", &aliases);
-                context.insert("build_aliases", &build_aliases);
-
-                let content = self
-                    .engine
-                    .render("crate_build_file.j2", &context)
-                    .context("Failed to render BUILD file")?;
-
-                Ok((id, content))
-            })
-            .collect()
+    pub fn render_header(&self) -> Result<String> {
+        let context = self.new_tera_ctx();
+        let mut header = self
+            .engine
+            .render("partials/header.j2", &context)
+            .context("Failed to render header comment")?;
+        header.push('\n');
+        Ok(header)
     }
 
-    pub fn render_module_build_file(&self, data: &Context) -> Result<String> {
+    pub fn render_module_bzl(&self, data: &Context, platforms: &Platforms) -> Result<String> {
         let mut context = self.new_tera_ctx();
         context.insert("context", data);
-
-        let workspace_member_deps = data.flat_workspace_member_deps();
-        context.insert("workspace_member_dependencies", &workspace_member_deps);
-
-        let binary_crates_map = data.flat_binary_deps();
-        context.insert("binary_crates_map", &binary_crates_map);
-
-        self.engine
-            .render("module_build_file.j2", &context)
-            .context("Failed to render crates module")
-    }
-
-    pub fn render_module_bzl(&self, data: &Context) -> Result<String> {
-        let mut context = self.new_tera_ctx();
-        context.insert("context", data);
+        context.insert("platforms", platforms);
 
         self.engine
             .render("module_bzl.j2", &context)
@@ -301,29 +177,6 @@
 }
 
 /// Convert a crate name into a module name by applying transforms to invalid characters.
-fn sanitize_module_name_fn(args: &HashMap<String, Value>) -> tera::Result<Value> {
-    let crate_name = parse_tera_param!("crate_name", String, args);
-
-    match to_value(sanitize_module_name(&crate_name)) {
-        Ok(v) => Ok(v),
-        Err(_) => Err(tera::Error::msg("Failed to generate resulting module name")),
-    }
-}
-
-/// Convert a crate name into a module name by applying transforms to invalid characters.
-fn platform_label_fn_generator(template: String) -> impl tera::Function {
-    Box::new(
-        move |args: &HashMap<String, Value>| -> tera::Result<Value> {
-            let triple = parse_tera_param!("triple", String, args);
-            match to_value(render_platform_constraint_label(&template, &triple)) {
-                Ok(v) => Ok(v),
-                Err(_) => Err(tera::Error::msg("Failed to generate resulting module name")),
-            }
-        },
-    )
-}
-
-/// Convert a crate name into a module name by applying transforms to invalid characters.
 fn crate_build_file_fn_generator(template: String) -> impl tera::Function {
     Box::new(
         move |args: &HashMap<String, Value>| -> tera::Result<Value> {
diff --git a/crate_universe/src/rendering/templates/crate_build_file.j2 b/crate_universe/src/rendering/templates/crate_build_file.j2
deleted file mode 100644
index ff9d4ad..0000000
--- a/crate_universe/src/rendering/templates/crate_build_file.j2
+++ /dev/null
@@ -1,44 +0,0 @@
-{%- set crate = context.crates | get(key=crate_id) %}
-{%- include "partials/header.j2" %}
-
-load(
-    "@bazel_skylib//lib:selects.bzl", 
-    "selects",
-)
-load(
-    "@rules_rust//cargo:defs.bzl",
-    "cargo_build_script",
-)
-load(
-    "@rules_rust//rust:defs.bzl",
-    "rust_binary",
-    "rust_library",
-    "rust_proc_macro",
-)
-
-# buildifier: disable=bzl-visibility
-load("@rules_rust//crate_universe/private:selects.bzl", "select_with_or")
-
-package(default_visibility = ["//visibility:public"])
-
-# licenses([
-#     "TODO",  # {{ crate.license }}
-# ])
-
-{% for rule in crate.targets -%}
-{%- for rule_type, target in rule %}
-{%- if rule_type in ["BuildScript"] %}
-{% include "partials/crate/build_script.j2" %}
-{%- elif rule_type in ["ProcMacro"] %}
-{% include "partials/crate/proc_macro.j2" %}
-{%- elif rule_type in ["Library"] %}
-{% include "partials/crate/library.j2" %}
-{%- elif rule_type in ["Binary"] %}
-{% include "partials/crate/binary.j2" %}
-{%- endif %}
-{%- endfor %}
-{%- endfor %}
-{%- if crate.additive_build_file_content %}
-# Additive BUILD file content
-{{ crate.additive_build_file_content }}
-{%- endif %}
diff --git a/crate_universe/src/rendering/templates/module_build_file.j2 b/crate_universe/src/rendering/templates/module_build_file.j2
deleted file mode 100644
index 63124dc..0000000
--- a/crate_universe/src/rendering/templates/module_build_file.j2
+++ /dev/null
@@ -1,49 +0,0 @@
-{%- include "partials/header.j2" %}
-
-package(default_visibility = ["//visibility:public"])
-
-exports_files(
-    [
-        "cargo-bazel.json",
-        "defs.bzl",
-        {%- set current_vendor_mode = vendor_mode | default(value="") %}{%- if current_vendor_mode == "remote" %}"crates.bzl",{%- endif %}
-    ] + glob([
-        "*.bazel",
-    ]),
-)
-
-filegroup(
-    name = "srcs",
-    srcs = glob([
-        "*.bazel",
-        "*.bzl",
-    ]),
-)
-
-# Workspace Member Dependencies
-{%- for dep, rename in workspace_member_dependencies %}
-{%- set crate = context.crates | get(key=dep) %}
-{%- if crate | get(key="library_target_name", default=Null) %}
-alias(
-    name = "{{ rename | default(value=crate.name) }}",
-    actual = "{{ crate_label(name = crate.name, version = crate.version, target = crate.library_target_name) }}",
-    tags = ["manual"],
-)
-{%- endif %}
-{%- endfor %}
-
-# Binaries
-{%- for id, rename in binary_crates_map %}
-{%- set crate = context.crates | get(key=id) %}
-{%- for rule in crate.targets %}
-{%- for rule_type, target in rule %}
-{%- if rule_type in ["Binary"] %}
-alias(
-    name = "{{ rename | default(value=crate.name) }}__{{ target.crate_name }}",
-    actual = "{{ crate_label(name = crate.name, version = crate.version, target = target.crate_name ~ '__bin') }}",
-    tags = ["manual"],
-)
-{%- endif %}
-{%- endfor %}
-{%- endfor %}
-{%- endfor %}
diff --git a/crate_universe/src/rendering/templates/module_bzl.j2 b/crate_universe/src/rendering/templates/module_bzl.j2
index c3a1e6d..63d027c 100644
--- a/crate_universe/src/rendering/templates/module_bzl.j2
+++ b/crate_universe/src/rendering/templates/module_bzl.j2
@@ -1,6 +1,6 @@
 {#
 To keep line numbers consistent with the rendered version, empty space is
-intentionally plced here which should match the line length of `partials/header.j2`.
+intentionally placed here which should match the line length of `partials/header.j2`.
 
 Expected length = 6 lines
 #}{%- include "partials/header.j2" %}
@@ -37,7 +37,7 @@
         # name of the workspace this file is defined in.
         "workspace_member_package": {
 
-            # Not all dependnecies are supported for all platforms.
+            # Not all dependencies are supported for all platforms.
             # the condition key is the condition required to be true
             # on the host platform.
             "condition": {
@@ -153,7 +153,7 @@
     Args:
         normal (bool, optional): If True, normal dependencies are included in the
             output list.
-        normal_dev (bool, optional): If True, normla dev dependencies will be
+        normal_dev (bool, optional): If True, normal dev dependencies will be
             included in the output list..
         proc_macro (bool, optional): If True, proc_macro dependencies are included
             in the output list.
@@ -202,7 +202,10 @@
 
     crate_deps = list(dependencies.pop(_COMMON_CONDITION, {}).values())
     for condition, deps in dependencies.items():
-        crate_deps += selects.with_or({_CONDITIONS[condition]: deps.values()})
+        crate_deps += selects.with_or({
+            tuple(_CONDITIONS[condition]): deps.values(),
+            "//conditions:default": [],
+        })
 
     return crate_deps
 
@@ -222,7 +225,7 @@
     Args:
         normal (bool, optional): If True, normal dependencies are included in the
             output list.
-        normal_dev (bool, optional): If True, normla dev dependencies will be
+        normal_dev (bool, optional): If True, normal dev dependencies will be
             included in the output list..
         proc_macro (bool, optional): If True, proc_macro dependencies are included
             in the output list.
@@ -274,15 +277,16 @@
 
     # Build a single select statement where each conditional has accounted for the
     # common set of aliases.
-    crate_aliases = {"//conditions:default": common_items}
+    crate_aliases = {"//conditions:default": dict(common_items)}
     for condition, deps in aliases.items():
         condition_triples = _CONDITIONS[condition]
-        if condition_triples in crate_aliases:
-            crate_aliases[condition_triples].update(deps)
-        else:
-            crate_aliases.update({_CONDITIONS[condition]: dict(deps.items() + common_items)})
+        for triple in condition_triples:
+            if triple in crate_aliases:
+                crate_aliases[triple].update(deps)
+            else:
+                crate_aliases.update({triple: dict(deps.items() + common_items)})
 
-    return selects.with_or(crate_aliases)
+    return select(crate_aliases)
 
 ###############################################################################
 # WORKSPACE MEMBER DEPS AND ALIASES
@@ -313,7 +317,7 @@
 _BUILD_PROC_MACRO_ALIASES = {% set deps_type = "build-proc-macro" %}{% include "partials/module/aliases_map.j2" %}
 
 _CONDITIONS = {
-{%- for condition, triples in context.conditions %}
+{%- for condition, triples in platforms %}
     "{{ condition | addslashes }}": {{ triples | sort | json_encode | safe }},
 {%- endfor %}
 }
diff --git a/crate_universe/src/rendering/templates/partials/crate/aliases.j2 b/crate_universe/src/rendering/templates/partials/crate/aliases.j2
deleted file mode 100644
index 5c4b67e..0000000
--- a/crate_universe/src/rendering/templates/partials/crate/aliases.j2
+++ /dev/null
@@ -1,32 +0,0 @@
-selects.with_or({
-    {%- for cfg, values in selectable.selects %}
-    {%- if cfg in context.conditions and context.conditions[cfg] | length %}
-        # {{ cfg }}
-        (
-            {%- for triple in context.conditions[cfg] %}
-            "{{ platform_label(triple = triple) }}",
-            {%- endfor %}
-        ): {
-            {%- for dep in values %}
-            {%- set dep_crate = context.crates | get(key=dep.id) %}
-            "{{ crate_label(name = dep_crate.name, version = dep_crate.version, target = dep.target) }}": "{{ dep.alias }}",
-            {%- endfor %}
-            {%- for dep in selectable.common %}
-            {%- set dep_crate = context.crates | get(key=dep.id) %}
-            "{{ crate_label(name = dep_crate.name, version = dep_crate.version, target = dep.target) }}": "{{ dep.alias }}",
-            {%- endfor %}
-        },
-    {%- else %}
-        # {
-        # No supported platform triples for cfg: '{{ cfg }}'
-        # Skipped dependencies: {{ values | json_encode | safe }}
-        # }
-    {%- endif %}
-    {%- endfor %}
-        "//conditions:default": {
-            {%- for dep in selectable.common %}
-            {%- set dep_crate = context.crates | get(key=dep.id) %}
-            "{{ crate_label(name = dep_crate.name, version = dep_crate.version, target = dep.target) }}": "{{ dep.alias }}",
-            {%- endfor %}
-        },
-    })
\ No newline at end of file
diff --git a/crate_universe/src/rendering/templates/partials/crate/binary.j2 b/crate_universe/src/rendering/templates/partials/crate/binary.j2
deleted file mode 100644
index 2cdc5d9..0000000
--- a/crate_universe/src/rendering/templates/partials/crate/binary.j2
+++ /dev/null
@@ -1,18 +0,0 @@
-rust_binary(
-    name = "{{ target.crate_name }}__bin",
-    deps = [
-        {%- if crate.library_target_name %}
-        ":{{ crate.library_target_name }}",
-        {%- endif %}
-        {%- for dep in crate.common_attrs | get(key="extra_deps", default=[]) %}
-        "{{ dep }}",
-        {%- endfor %}
-    ] + {% set deps = crate.common_attrs | get(key="deps", default=Null) %}{% include "partials/crate/deps.j2" %},
-    proc_macro_deps = [
-        {%- for dep in crate.common_attrs | get(key="extra_proc_macro_deps", default=[]) %}
-        "{{ dep }}",
-        {%- endfor %}
-    ] + {% set deps = crate.common_attrs | get(key="proc_macro_deps", default=Null) %}{% include "partials/crate/deps.j2" %},
-    aliases = {% set selectable = common_aliases %}{% include "partials/crate/aliases.j2" -%},
-{% include "partials/crate/common_attrs.j2" %}
-)
diff --git a/crate_universe/src/rendering/templates/partials/crate/build_script.j2 b/crate_universe/src/rendering/templates/partials/crate/build_script.j2
deleted file mode 100644
index 9b1ff45..0000000
--- a/crate_universe/src/rendering/templates/partials/crate/build_script.j2
+++ /dev/null
@@ -1,84 +0,0 @@
-cargo_build_script(
-    # See comment associated with alias. Do not change this name
-    name = "{{ crate.name }}_build_script",
-    aliases = {% set selectable = build_aliases %}{% include "partials/crate/aliases.j2" -%},
-    build_script_env = {% set selectable = crate.build_script_attrs | get(key="build_script_env", default=Null) %}{% include "partials/starlark/selectable_dict.j2" -%},
-    compile_data = {% if crate.build_script_attrs | get(key="compile_data_glob") %}glob({{ crate.build_script_attrs.compile_data_glob | json_encode | safe }}) + {% endif %}{% set selectable = crate.build_script_attrs | get(key="compile_data", default=Null) %}{% include "partials/starlark/selectable_list.j2" %},
-    crate_name = "{{ sanitize_module_name(crate_name=target.crate_name) }}",
-    crate_root = "{{ target.crate_root }}",
-    crate_features = [
-        {%- if crate.common_attrs | get(key="crate_features", default=Null) %}
-        {%- for feature in crate.common_attrs.crate_features %}
-        "{{ feature }}",
-        {%- endfor %}
-        {%- endif %}
-    ],
-    data = {% if crate.build_script_attrs | get(key="data_glob") %}glob({{ crate.build_script_attrs.data_glob | json_encode | safe }}) + {% endif %}{% set selectable = crate.build_script_attrs | get(key="data", default=Null) %}{% include "partials/starlark/selectable_list.j2" %},
-    deps = [
-        {%- for dep in crate.build_script_attrs | get(key="extra_deps", default=[]) %}
-        "{{ dep }}",
-        {%- endfor %}
-    ] + {% set deps = crate.build_script_attrs | get(key="deps", default=Null) %}{% include "partials/crate/deps.j2" %},
-    edition = "{{ crate.common_attrs.edition }}",
-    {%- if crate.common_attrs.linker_script %}
-    linker_script = "{{ crate.common_attrs.linker_script }}",
-    {%- endif %}
-    {%- if crate.build_script_attrs | get(key="links", default=Null) %}
-    links = "{{ crate.build_script_attrs.links }}",
-    {%- endif %}
-    proc_macro_deps = [
-        {%- for dep in crate.build_script_attrs | get(key="extra_proc_macro_deps", default=[]) %}
-        "{{ dep }}",
-        {%- endfor %}
-    ] + {% set deps = crate.build_script_attrs | get(key="proc_macro_deps", default=Null) %}{% include "partials/crate/deps.j2" %},
-    rustc_env = {% set selectable = crate.build_script_attrs | get(key="rustc_env", default=Null) %}{% include "partials/starlark/selectable_dict.j2" -%},
-    rustc_env_files = {% set selectable = crate.build_script_attrs | get(key="rustc_env_files", default=Null) %}{% include "partials/starlark/selectable_list.j2" %},
-    rustc_flags = [
-        # 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
-        "--cap-lints=allow",
-        {%- if crate.common_attrs | get(key="rustc_flags", default=Null) %}
-
-        # User provided rustc_flags
-        {%- for rustc_flag in crate.common_attrs.rustc_flags %}
-        "{{ rustc_flag }}",
-        {%- endfor %}
-        {%- endif %}
-    ],
-    srcs = {% set glob = target.srcs %}{% include "partials/starlark/glob.j2" -%},
-    tools = {% set selectable = crate.build_script_attrs | get(key="tools", default=Null) %}{% include "partials/starlark/selectable_list.j2" %},
-    version = "{{ crate.common_attrs.version }}",
-    tags = [
-        {%- if crate.common_attrs | get(key="tags", default=Null) %}
-        {%- for tag in crate.common_attrs.tags %}
-        "{{ tag }}",
-        {%- endfor %}
-        {%- endif %}
-        "cargo-bazel",
-        "manual",
-        "noclippy",
-        "norustfmt",
-    ],
-    {%- if crate.build_script_attrs | get(key="toolchains", default=Null) %}
-    toolchains = [
-        {%- for toolchain in crate.build_script_attrs.toolchains %}
-        "{{ toolchain }}",
-        {%- endfor %}
-    ],
-    {%- endif %}
-    visibility = ["//visibility:private"],
-)
-alias(
-    # Because `cargo_build_script` does some invisible target name mutating to
-    # determine the package and crate name for a build script, the Bazel
-    # target namename of any build script cannot be the Cargo canonical name
-    # of `build_script_build` without losing out on having certain Cargo
-    # environment variables set.
-    name = "{{ target.crate_name }}",
-    actual = "{{ crate.name }}_build_script",
-    tags = [
-        "manual",
-    ],
-)
diff --git a/crate_universe/src/rendering/templates/partials/crate/common_attrs.j2 b/crate_universe/src/rendering/templates/partials/crate/common_attrs.j2
deleted file mode 100644
index c1cccbb..0000000
--- a/crate_universe/src/rendering/templates/partials/crate/common_attrs.j2
+++ /dev/null
@@ -1,41 +0,0 @@
-    compile_data = {% if crate.common_attrs | get(key="compile_data_glob") %}glob(include = {{ crate.common_attrs.compile_data_glob | json_encode | safe }}, exclude = ["BUILD", "BUILD.bazel", "WORKSPACE", "WORKSPACE.bazel"]) + {% endif %}{% set selectable = crate.common_attrs | get(key="compile_data", default=default_select_list) %}{% include "partials/starlark/selectable_list.j2" -%},
-    crate_root = "{{ target.crate_root }}",
-    crate_features = [
-        {%- for feature in crate.common_attrs | get(key="crate_features", default=[]) %}
-        "{{ feature }}",
-        {%- endfor %}
-    ],
-    data = {% if crate.common_attrs | get(key="data_glob") %}glob(include = {{ crate.common_attrs.data_glob | json_encode | safe }}, exclude = ["BUILD", "BUILD.bazel", "WORKSPACE", "WORKSPACE.bazel"]) + {% endif %}{% set selectable = crate.common_attrs | get(key="data", default=default_select_list) %}{% include "partials/starlark/selectable_list.j2" -%},
-    edition = "{{ crate.common_attrs.edition }}",
-    {%- if crate.common_attrs | get(key="linker_script", default=Null) %}
-    linker_script = "{{ crate.common_attrs.linker_script }}",
-    {%- endif %}
-    rustc_env = {% set selectable = crate.common_attrs | get(key="rustc_env", default=Null) %}{% include "partials/starlark/selectable_dict.j2" -%},
-    rustc_env_files = {% set selectable = crate.common_attrs | get(key="rustc_env_files", default=Null) %}{% include "partials/starlark/selectable_list.j2" -%},
-    rustc_flags = [
-        # 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
-        "--cap-lints=allow",
-        {%- if crate.common_attrs | get(key="rustc_flags", default=Null) %}
-
-        # User provided rustc_flags
-        {%- for rustc_flag in crate.common_attrs.rustc_flags %}
-        "{{ rustc_flag }}",
-        {%- endfor %}
-        {%- endif %}
-    ],
-    srcs = {% set glob = target.srcs %}{% include "partials/starlark/glob.j2" -%},
-    version = "{{ crate.common_attrs.version }}",
-    tags = [
-        {%- if crate.common_attrs | get(key="tags", default=Null) %}
-        {%- for tag in crate.common_attrs.tags %}
-        "{{ tag }}",
-        {%- endfor %}
-        {%- endif %}
-        "cargo-bazel",
-        "manual",
-        "noclippy",
-        "norustfmt",
-    ],
diff --git a/crate_universe/src/rendering/templates/partials/crate/deps.j2 b/crate_universe/src/rendering/templates/partials/crate/deps.j2
deleted file mode 100644
index 0e0bf71..0000000
--- a/crate_universe/src/rendering/templates/partials/crate/deps.j2
+++ /dev/null
@@ -1,36 +0,0 @@
-select_with_or({
-    {%- set selectable = deps | default(value=default_select_list) %}
-    {%- for cfg, values in selectable.selects %}
-        # {{ cfg }}
-    {%- if cfg in context.conditions and context.conditions[cfg] | length %}
-        (
-            {%- for triple in context.conditions[cfg] %}
-            "{{ platform_label(triple = triple) }}",
-            {%- endfor %}
-        ): [
-            # Target Deps
-            {%- for dep in values %}
-            {%- set dep_crate = context.crates | get(key=dep.id) %}
-            "{{ crate_label(name = dep_crate.name, version = dep_crate.version, target = dep.target) }}",
-            {%- endfor %}
-
-            # Common Deps
-            {%- for common_dep in selectable.common %}
-            {%- set common_dep_crate = context.crates | get(key=common_dep.id) %}
-            "{{ crate_label(name = common_dep_crate.name, version = common_dep_crate.version, target = common_dep.target) }}",
-            {%- endfor %}
-        ],
-    {%- else %}
-            #
-            # No supported platform triples for cfg: '{{ cfg }}'
-            # Skipped dependencies: {{ values | json_encode | safe }}
-            #
-    {%- endif %}
-    {%- endfor %}
-        "//conditions:default": [
-            {%- for common_dep in selectable.common %}
-            {%- set common_dep_crate = context.crates | get(key=common_dep.id) %}
-            "{{ crate_label(name = common_dep_crate.name, version = common_dep_crate.version, target = common_dep.target) }}",
-            {%- endfor %}
-        ],
-    })
\ No newline at end of file
diff --git a/crate_universe/src/rendering/templates/partials/crate/library.j2 b/crate_universe/src/rendering/templates/partials/crate/library.j2
deleted file mode 100644
index f678bd9..0000000
--- a/crate_universe/src/rendering/templates/partials/crate/library.j2
+++ /dev/null
@@ -1,15 +0,0 @@
-rust_library(
-    name = "{{ target.crate_name }}",
-    deps = [
-        {%- for dep in crate.common_attrs | get(key="extra_deps", default=[]) %}
-        "{{ dep }}",
-        {%- endfor %}
-    ] + {% set deps = crate.common_attrs | get(key="deps", default=Null) %}{% include "partials/crate/deps.j2" %},
-    proc_macro_deps = [
-        {%- for dep in crate.common_attrs | get(key="extra_proc_macro_deps", default=[]) %}
-        "{{ dep }}",
-        {%- endfor %}
-    ] + {% set deps = crate.common_attrs | get(key="proc_macro_deps", default=Null) %}{% include "partials/crate/deps.j2" %},
-    aliases = {% set selectable = common_aliases %}{% include "partials/crate/aliases.j2" -%},
-{% include "partials/crate/common_attrs.j2" %}
-)
diff --git a/crate_universe/src/rendering/templates/partials/crate/proc_macro.j2 b/crate_universe/src/rendering/templates/partials/crate/proc_macro.j2
deleted file mode 100644
index c0b9d1d..0000000
--- a/crate_universe/src/rendering/templates/partials/crate/proc_macro.j2
+++ /dev/null
@@ -1,15 +0,0 @@
-rust_proc_macro(
-    name = "{{ target.crate_name }}",
-    deps = [
-        {%- for dep in crate.common_attrs | get(key="extra_deps", default=[]) %}
-        "{{ dep }}",
-        {%- endfor %}
-    ] + {% set deps = crate.common_attrs | get(key="deps", default=Null) %}{% include "partials/crate/deps.j2" %},
-    proc_macro_deps = [
-        {%- for dep in crate.common_attrs | get(key="extra_proc_macro_deps", default=[]) %}
-        "{{ dep }}",
-        {%- endfor %}
-    ] + {% set deps = crate.common_attrs | get(key="proc_macro_deps", default=Null) %}{% include "partials/crate/deps.j2" %},
-    aliases = {% set selectable = common_aliases %}{% include "partials/crate/aliases.j2" -%},
-{% include "partials/crate/common_attrs.j2" %}
-)
diff --git a/crate_universe/src/rendering/templates/partials/starlark/glob.j2 b/crate_universe/src/rendering/templates/partials/starlark/glob.j2
deleted file mode 100644
index 67f70af..0000000
--- a/crate_universe/src/rendering/templates/partials/starlark/glob.j2
+++ /dev/null
@@ -1,12 +0,0 @@
-glob(
-        include = [
-            {%- for pattern in glob.include %}
-            "{{ pattern }}",
-            {%- endfor %}
-        ],
-        exclude = [
-            {%- for pattern in glob.exclude %}
-            "{{ pattern }}",
-            {%- endfor %}
-        ],
-    )
\ No newline at end of file
diff --git a/crate_universe/src/rendering/templates/partials/starlark/selectable_dict.j2 b/crate_universe/src/rendering/templates/partials/starlark/selectable_dict.j2
deleted file mode 100644
index 8012cc7..0000000
--- a/crate_universe/src/rendering/templates/partials/starlark/selectable_dict.j2
+++ /dev/null
@@ -1,36 +0,0 @@
-{%- set selectable = selectable | default(value=default_select_dict) %}
-{%- if selectable.selects | length -%}
-    selects.with_or({
-    {%- for cfg, map in selectable.selects %}
-    {%- if cfg in context.conditions and context.conditions[cfg] | length %}
-        # {{ cfg }}
-        (
-            {%- for triple in context.conditions[cfg] %}
-            "{{ platform_label(triple = triple) }}",
-            {%- endfor %}
-        ): {
-            {%- if selectable.common | length %}
-            {%- for key, val in selectable.common %}
-            "{{ key }}": "{{ val }}",
-            {%- endfor %}
-            {%- endif %}
-            {%- for key, val in map %}
-            "{{ key }}": "{{ val }}",
-            {%- endfor %}
-        },
-    {%- else %}
-        # No supported platform triples for cfg: '{{ cfg }}'
-        # Skipped dependencies: {{ map | json_encode| safe }}
-    {%- endif %}
-    {%- endfor %}
-        "//conditions:default": {},
-    })
-{%- else -%}
-    {
-        {%- if selectable.common | length %}
-        {%- for key, val in selectable.common %}
-        "{{ key }}": "{{ val }}",
-        {%- endfor %}
-        {%- endif %}
-    }
-{%- endif %}
\ No newline at end of file
diff --git a/crate_universe/src/rendering/templates/partials/starlark/selectable_list.j2 b/crate_universe/src/rendering/templates/partials/starlark/selectable_list.j2
deleted file mode 100644
index 2641713..0000000
--- a/crate_universe/src/rendering/templates/partials/starlark/selectable_list.j2
+++ /dev/null
@@ -1,31 +0,0 @@
-select_with_or({
-    {%- set selectable = selectable | default(value=default_select_list) %}
-    {%- for cfg, values in selectable.selects %}
-        # {{ cfg }}
-    {%- if cfg in context.conditions and context.conditions[cfg] | length %}
-        (
-            {%- for triple in context.conditions[cfg] %}
-            "{{ platform_label(triple = triple) }}",
-            {%- endfor %}
-        ): [
-            # Target Deps
-            {%- for val in values %}
-            "{{ val }}",
-            {%- endfor %}
-
-            # Common Deps
-            {%- for val in selectable.common %}
-            "{{ val }}",
-            {%- endfor %}
-        ],
-    {%- else %}
-            # No supported platform triples for cfg: '{{ cfg }}'
-            # Skipped dependencies: {{ values | json_encode | safe }}
-    {%- endif %}
-    {%- endfor %}
-        "//conditions:default": [
-            {%- for val in selectable.common %}
-            "{{ val }}",
-            {%- endfor %}
-        ],
-    })
\ No newline at end of file
diff --git a/crate_universe/src/splicing.rs b/crate_universe/src/splicing.rs
index 8d42e1e..fe525b9 100644
--- a/crate_universe/src/splicing.rs
+++ b/crate_universe/src/splicing.rs
@@ -1,24 +1,26 @@
 //! This module is responsible for finding a Cargo workspace
 
 pub(crate) mod cargo_config;
+mod crate_index_lookup;
 mod splicer;
 
-use std::collections::{BTreeMap, BTreeSet, HashMap};
+use std::collections::{BTreeMap, BTreeSet};
 use std::convert::TryFrom;
 use std::fs;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
-use anyhow::{bail, Context, Result};
+use anyhow::{anyhow, bail, Context, Result};
 use cargo_toml::Manifest;
-use hex::ToHex;
 use serde::{Deserialize, Serialize};
 
 use crate::config::CrateId;
-use crate::metadata::{CargoUpdateRequest, LockGenerator};
-use crate::utils::starlark::Label;
+use crate::metadata::{Cargo, CargoUpdateRequest, LockGenerator};
+use crate::utils;
+use crate::utils::starlark::{Label, SelectList};
 
 use self::cargo_config::CargoConfig;
+use self::crate_index_lookup::CrateIndexLookup;
 pub use self::splicer::*;
 
 type DirectPackageManifest = BTreeMap<String, cargo_toml::DependencyDetail>;
@@ -116,9 +118,16 @@
             .manifests
             .into_iter()
             .map(|(path, label)| {
-                let manifest = cargo_toml::Manifest::from_path(&path)
+                // We read the content of a manifest file to buffer and use `from_slice` to
+                // parse it. The reason is that the `from_path` version will resolve indirect
+                // path dependencies in the workspace to absolute path, which causes the hash
+                // to be unstable. Not resolving implicit data is okay here because the
+                // workspace manifest is also included in the hash.
+                // See https://github.com/bazelbuild/rules_rust/issues/2016
+                let manifest_content = fs::read(&path)
                     .with_context(|| format!("Failed to load manifest '{}'", path.display()))?;
-
+                let manifest = cargo_toml::Manifest::from_slice(&manifest_content)
+                    .with_context(|| format!("Failed to parse manifest '{}'", path.display()))?;
                 Ok((label, manifest))
             })
             .collect::<Result<BTreeMap<Label, Manifest>>>()?;
@@ -152,15 +161,19 @@
 #[derive(Debug, Default, Serialize, Deserialize)]
 pub struct WorkspaceMetadata {
     /// A mapping of crates to information about where their source can be downloaded
-    #[serde(serialize_with = "toml::ser::tables_last")]
     pub sources: BTreeMap<CrateId, SourceInfo>,
 
     /// The path from the root of a Bazel workspace to the root of the Cargo workspace
     pub workspace_prefix: Option<String>,
 
     /// Paths from the root of a Bazel workspace to a Cargo package
-    #[serde(serialize_with = "toml::ser::tables_last")]
     pub package_prefixes: BTreeMap<String, String>,
+
+    /// Feature set for each target triplet and crate.
+    ///
+    /// We store this here because it's computed during the splicing phase via
+    /// calls to "cargo tree" which need the full spliced workspace.
+    pub features: BTreeMap<CrateId, SelectList<String>>,
 }
 
 impl TryFrom<toml::Value> for WorkspaceMetadata {
@@ -193,7 +206,7 @@
 impl WorkspaceMetadata {
     fn new(
         splicing_manifest: &SplicingManifest,
-        member_manifests: HashMap<&PathBuf, String>,
+        member_manifests: BTreeMap<&PathBuf, String>,
     ) -> Result<Self> {
         let mut package_prefixes: BTreeMap<String, String> = member_manifests
             .iter()
@@ -233,14 +246,18 @@
             sources: BTreeMap::new(),
             workspace_prefix,
             package_prefixes,
+            features: BTreeMap::new(),
         })
     }
 
-    pub fn write_registry_urls(
+    pub fn write_registry_urls_and_feature_map(
+        cargo: &Cargo,
         lockfile: &cargo_lock::Lockfile,
-        manifest_path: &SplicedManifest,
+        features: BTreeMap<CrateId, SelectList<String>>,
+        input_manifest_path: &Path,
+        output_manifest_path: &Path,
     ) -> Result<()> {
-        let mut manifest = read_manifest(manifest_path.as_path_buf())?;
+        let mut manifest = read_manifest(input_manifest_path)?;
 
         let mut workspace_metaata = WorkspaceMetadata::try_from(
             manifest
@@ -253,7 +270,7 @@
                 .clone(),
         )?;
 
-        // Locate all packages soruced from a registry
+        // Locate all packages sourced from a registry
         let pkg_sources: Vec<&cargo_lock::Package> = lockfile
             .packages
             .iter()
@@ -270,8 +287,7 @@
         // Load the cargo config
         let cargo_config = {
             // Note that this path must match the one defined in `splicing::setup_cargo_config`
-            let config_path = manifest_path
-                .as_path_buf()
+            let config_path = input_manifest_path
                 .parent()
                 .unwrap()
                 .join(".cargo")
@@ -288,86 +304,84 @@
         let crate_indexes = index_urls
             .into_iter()
             .map(|url| {
-                let index = {
-                    // Ensure the correct registry is mapped based on the give Cargo config.
-                    let index_url = if let Some(config) = &cargo_config {
-                        if let Some(source) = config.get_source_from_url(&url) {
-                            if let Some(replace_with) = &source.replace_with {
-                                if let Some(replacement) = config.get_registry_index_url_by_name(replace_with) {
-                                    replacement
-                                } else {
-                                    bail!("Tried to replace registry {} with registry named {} but didn't have metadata about the replacement", url, replace_with);
-                                }
-                            } else {
-                                &url
-                            }
-                        } else {
-                            &url
-                        }
-                    } else {
-                        &url
+                // Ensure the correct registry is mapped based on the give Cargo config.
+                let index_url = if let Some(config) = &cargo_config {
+                    config.resolve_replacement_url(&url)?
+                } else {
+                    &url
+                };
+                let index = if cargo.use_sparse_registries_for_crates_io()?
+                    && index_url == utils::CRATES_IO_INDEX_URL
+                {
+                    CrateIndexLookup::Http(crates_index::SparseIndex::from_url(
+                        "sparse+https://index.crates.io/",
+                    )?)
+                } else if index_url.starts_with("sparse+https://") {
+                    CrateIndexLookup::Http(crates_index::SparseIndex::from_url(index_url)?)
+                } else {
+                    let index = {
+                        // Load the index for the current url
+                        let index =
+                            crates_index::Index::from_url(index_url).with_context(|| {
+                                format!("Failed to load index for url: {index_url}")
+                            })?;
+
+                        // Ensure each index has a valid index config
+                        index.index_config().with_context(|| {
+                            format!("`config.json` not found in index: {index_url}")
+                        })?;
+
+                        index
                     };
 
-                    // Load the index for the current url
-                    let index = crates_index::Index::from_url(index_url)
-                        .with_context(|| format!("Failed to load index for url: {}", index_url))?;
-
-                    // Ensure each index has a valid index config
-                    index.index_config().with_context(|| {
-                        format!("`config.json` not found in index: {}", index_url)
-                    })?;
-
-                    index
+                    CrateIndexLookup::Git(index)
                 };
-
                 Ok((url, index))
             })
-            .collect::<Result<BTreeMap<String, crates_index::Index>>>()
+            .collect::<Result<BTreeMap<String, _>>>()
             .context("Failed to locate crate indexes")?;
 
         // Get the download URL of each package based on it's registry url.
         let additional_sources = pkg_sources
             .iter()
-            .filter_map(|pkg| {
+            .map(|pkg| {
                 let source_id = pkg.source.as_ref().unwrap();
-                let index = &crate_indexes[&source_id.url().to_string()];
-                let index_config = index.index_config().unwrap();
-
-                index.crate_(pkg.name.as_str()).map(|crate_idx| {
-                    crate_idx
-                        .versions()
-                        .iter()
-                        .find(|v| v.version() == pkg.version.to_string())
-                        .and_then(|v| {
-                            v.download_url(&index_config).map(|url| {
-                                let crate_id =
-                                    CrateId::new(v.name().to_owned(), v.version().to_owned());
-                                let sha256 = pkg
-                                    .checksum
-                                    .as_ref()
-                                    .and_then(|sum| {
-                                        sum.as_sha256().map(|sum| sum.encode_hex::<String>())
-                                    })
-                                    .unwrap_or_else(|| v.checksum().encode_hex::<String>());
-                                let source_info = SourceInfo { url, sha256 };
-                                (crate_id, source_info)
-                            })
-                        })
+                let source_url = source_id.url().to_string();
+                let lookup = crate_indexes.get(&source_url).ok_or_else(|| {
+                    anyhow!(
+                        "Couldn't find crate_index data for SourceID {:?}",
+                        source_id
+                    )
+                })?;
+                lookup.get_source_info(pkg).map(|source_info| {
+                    (
+                        CrateId::new(pkg.name.as_str().to_owned(), pkg.version.to_string()),
+                        source_info,
+                    )
                 })
             })
-            .flatten();
+            .collect::<Result<Vec<_>>>()?;
 
-        workspace_metaata.sources.extend(additional_sources);
+        workspace_metaata
+            .sources
+            .extend(
+                additional_sources
+                    .into_iter()
+                    .filter_map(|(crate_id, source_info)| {
+                        source_info.map(|source_info| (crate_id, source_info))
+                    }),
+            );
+        workspace_metaata.features = features;
         workspace_metaata.inject_into(&mut manifest)?;
 
-        write_root_manifest(manifest_path.as_path_buf(), manifest)?;
+        write_root_manifest(output_manifest_path, manifest)?;
 
         Ok(())
     }
 
     fn inject_into(&self, manifest: &mut Manifest) -> Result<()> {
         let metadata_value = toml::Value::try_from(self)?;
-        let mut workspace = manifest.workspace.as_mut().unwrap();
+        let workspace = manifest.workspace.as_mut().unwrap();
 
         match &mut workspace.metadata {
             Some(data) => match data.as_table_mut() {
@@ -412,7 +426,7 @@
 pub fn generate_lockfile(
     manifest_path: &SplicedManifest,
     existing_lock: &Option<PathBuf>,
-    cargo_bin: &Path,
+    cargo_bin: Cargo,
     rustc_bin: &Path,
     update_request: &Option<CargoUpdateRequest>,
 ) -> Result<cargo_lock::Lockfile> {
@@ -429,8 +443,11 @@
     }
 
     // Generate the new lockfile
-    let lockfile = LockGenerator::new(PathBuf::from(cargo_bin), PathBuf::from(rustc_bin))
-        .generate(manifest_path.as_path_buf(), existing_lock, update_request)?;
+    let lockfile = LockGenerator::new(cargo_bin, PathBuf::from(rustc_bin)).generate(
+        manifest_path.as_path_buf(),
+        existing_lock,
+        update_request,
+    )?;
 
     // Write the lockfile to disk
     if !root_lockfile_path.exists() {
@@ -480,17 +497,47 @@
         assert_eq!(manifest.resolver_version, cargo_toml::Resolver::V2);
 
         // Check packages
-        assert_eq!(manifest.direct_packages.len(), 1);
+        assert_eq!(manifest.direct_packages.len(), 4);
         let package = manifest.direct_packages.get("rand").unwrap();
         assert_eq!(
             package,
             &cargo_toml::DependencyDetail {
-                default_features: Some(false),
+                default_features: false,
                 features: vec!["small_rng".to_owned()],
                 version: Some("0.8.5".to_owned()),
                 ..Default::default()
             }
         );
+        let package = manifest.direct_packages.get("cfg-if").unwrap();
+        assert_eq!(
+            package,
+            &cargo_toml::DependencyDetail {
+                git: Some("https://github.com/rust-lang/cfg-if.git".to_owned()),
+                rev: Some("b9c2246a".to_owned()),
+                default_features: true,
+                ..Default::default()
+            }
+        );
+        let package = manifest.direct_packages.get("log").unwrap();
+        assert_eq!(
+            package,
+            &cargo_toml::DependencyDetail {
+                git: Some("https://github.com/rust-lang/log.git".to_owned()),
+                branch: Some("master".to_owned()),
+                default_features: true,
+                ..Default::default()
+            }
+        );
+        let package = manifest.direct_packages.get("cargo_toml").unwrap();
+        assert_eq!(
+            package,
+            &cargo_toml::DependencyDetail {
+                git: Some("https://gitlab.com/crates.rs/cargo_toml.git".to_owned()),
+                tag: Some("v0.15.2".to_owned()),
+                default_features: true,
+                ..Default::default()
+            }
+        );
 
         // Check cargo config
         assert_eq!(
@@ -542,4 +589,43 @@
             PathBuf::from("/tmp/abs/path/workspace/.cargo/config.toml"),
         )
     }
+
+    #[test]
+    fn splicing_metadata_workspace_path() {
+        let runfiles = runfiles::Runfiles::create().unwrap();
+        let workspace_manifest_path = runfiles
+            .rlocation("rules_rust/crate_universe/test_data/metadata/workspace_path/Cargo.toml");
+        let workspace_path = workspace_manifest_path.parent().unwrap().to_path_buf();
+        let child_a_manifest_path = runfiles.rlocation(
+            "rules_rust/crate_universe/test_data/metadata/workspace_path/child_a/Cargo.toml",
+        );
+        let child_b_manifest_path = runfiles.rlocation(
+            "rules_rust/crate_universe/test_data/metadata/workspace_path/child_b/Cargo.toml",
+        );
+        let manifest = SplicingManifest {
+            direct_packages: BTreeMap::new(),
+            manifests: BTreeMap::from([
+                (
+                    workspace_manifest_path,
+                    Label::from_str("//:Cargo.toml").unwrap(),
+                ),
+                (
+                    child_a_manifest_path,
+                    Label::from_str("//child_a:Cargo.toml").unwrap(),
+                ),
+                (
+                    child_b_manifest_path,
+                    Label::from_str("//child_b:Cargo.toml").unwrap(),
+                ),
+            ]),
+            cargo_config: None,
+            resolver_version: cargo_toml::Resolver::V2,
+        };
+        let metadata = SplicingMetadata::try_from(manifest).unwrap();
+        let metadata = serde_json::to_string(&metadata).unwrap();
+        assert!(
+            !metadata.contains(workspace_path.to_str().unwrap()),
+            "serialized metadata should not contain absolute path"
+        );
+    }
 }
diff --git a/crate_universe/src/splicing/cargo_config.rs b/crate_universe/src/splicing/cargo_config.rs
index ffa42b7..09f715b 100644
--- a/crate_universe/src/splicing/cargo_config.rs
+++ b/crate_universe/src/splicing/cargo_config.rs
@@ -5,7 +5,8 @@
 use std::path::Path;
 use std::str::FromStr;
 
-use anyhow::Result;
+use crate::utils;
+use anyhow::{bail, Result};
 use serde::{Deserialize, Serialize};
 
 /// The [`[registry]`](https://doc.rust-lang.org/cargo/reference/config.html#registry)
@@ -34,7 +35,7 @@
 
 /// This is the default registry url per what's defined by Cargo.
 fn default_registry_url() -> String {
-    "https://github.com/rust-lang/crates.io-index".to_owned()
+    utils::CRATES_IO_INDEX_URL.to_owned()
 }
 
 #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
@@ -114,15 +115,21 @@
 }
 
 impl CargoConfig {
-    /// Load a Cargo conig from a path to a file on disk.
+    /// Load a Cargo config from a path to a file on disk.
     pub fn try_from_path(path: &Path) -> Result<Self> {
         let content = fs::read_to_string(path)?;
         Self::from_str(&content)
     }
 
-    /// Look up a reigstry [Source] by it's url.
+    /// Look up a registry [Source] by its url.
     pub fn get_source_from_url(&self, url: &str) -> Option<&Source> {
-        self.source.values().find(|v| v.registry == url)
+        if let Some(found) = self.source.values().find(|v| v.registry == url) {
+            Some(found)
+        } else if url == utils::CRATES_IO_INDEX_URL {
+            self.source.get("crates-io")
+        } else {
+            None
+        }
     }
 
     pub fn get_registry_index_url_by_name(&self, name: &str) -> Option<&str> {
@@ -134,6 +141,22 @@
             None
         }
     }
+
+    pub fn resolve_replacement_url<'a>(&'a self, url: &'a str) -> Result<&'a str> {
+        if let Some(source) = self.get_source_from_url(url) {
+            if let Some(replace_with) = &source.replace_with {
+                if let Some(replacement) = self.get_registry_index_url_by_name(replace_with) {
+                    Ok(replacement)
+                } else {
+                    bail!("Tried to replace registry {} with registry named {} but didn't have metadata about the replacement", url, replace_with);
+                }
+            } else {
+                Ok(url)
+            }
+        } else {
+            Ok(url)
+        }
+    }
 }
 
 #[cfg(test)]
@@ -241,4 +264,134 @@
             Some("https://artprod.mycompany/artifactory/git/cargo-remote.git"),
         );
     }
+
+    #[test]
+    fn registry_settings_get_source_from_url() {
+        let temp_dir = tempfile::tempdir().unwrap();
+        let config = temp_dir.as_ref().join("config.toml");
+
+        fs::write(
+            &config,
+            textwrap::dedent(
+                r##"
+                [source.some-mirror]
+                registry = "https://artmirror.mycompany/artifactory/cargo-mirror.git"
+            "##,
+            ),
+        )
+        .unwrap();
+
+        let config = CargoConfig::try_from_path(&config).unwrap();
+        assert_eq!(
+            config
+                .get_source_from_url("https://artmirror.mycompany/artifactory/cargo-mirror.git")
+                .map(|s| s.registry.as_str()),
+            Some("https://artmirror.mycompany/artifactory/cargo-mirror.git"),
+        );
+    }
+
+    #[test]
+    fn resolve_replacement_url_no_replacement() {
+        let temp_dir = tempfile::tempdir().unwrap();
+        let config = temp_dir.as_ref().join("config.toml");
+
+        fs::write(&config, "").unwrap();
+
+        let config = CargoConfig::try_from_path(&config).unwrap();
+
+        assert_eq!(
+            config
+                .resolve_replacement_url(utils::CRATES_IO_INDEX_URL)
+                .unwrap(),
+            utils::CRATES_IO_INDEX_URL
+        );
+        assert_eq!(
+            config
+                .resolve_replacement_url("https://artmirror.mycompany/artifactory/cargo-mirror.git")
+                .unwrap(),
+            "https://artmirror.mycompany/artifactory/cargo-mirror.git"
+        );
+    }
+
+    #[test]
+    fn resolve_replacement_url_registry() {
+        let temp_dir = tempfile::tempdir().unwrap();
+        let config = temp_dir.as_ref().join("config.toml");
+
+        fs::write(&config, textwrap::dedent(
+            r##"
+                [registries]
+                art-crates-remote = { index = "https://artprod.mycompany/artifactory/git/cargo-remote.git" }
+
+                [source.crates-io]
+                replace-with = "some-mirror"
+
+                [source.some-mirror]
+                registry = "https://artmirror.mycompany/artifactory/cargo-mirror.git"
+            "##,
+        )).unwrap();
+
+        let config = CargoConfig::try_from_path(&config).unwrap();
+        assert_eq!(
+            config
+                .resolve_replacement_url(utils::CRATES_IO_INDEX_URL)
+                .unwrap(),
+            "https://artmirror.mycompany/artifactory/cargo-mirror.git"
+        );
+        assert_eq!(
+            config
+                .resolve_replacement_url("https://artmirror.mycompany/artifactory/cargo-mirror.git")
+                .unwrap(),
+            "https://artmirror.mycompany/artifactory/cargo-mirror.git"
+        );
+        assert_eq!(
+            config
+                .resolve_replacement_url(
+                    "https://artprod.mycompany/artifactory/git/cargo-remote.git"
+                )
+                .unwrap(),
+            "https://artprod.mycompany/artifactory/git/cargo-remote.git"
+        );
+    }
+
+    #[test]
+    fn resolve_replacement_url_source() {
+        let temp_dir = tempfile::tempdir().unwrap();
+        let config = temp_dir.as_ref().join("config.toml");
+
+        fs::write(&config, textwrap::dedent(
+            r##"
+                [registries]
+                art-crates-remote = { index = "https://artprod.mycompany/artifactory/git/cargo-remote.git" }
+
+                [source.crates-io]
+                replace-with = "art-crates-remote"
+
+                [source.some-mirror]
+                registry = "https://artmirror.mycompany/artifactory/cargo-mirror.git"
+            "##,
+        )).unwrap();
+
+        let config = CargoConfig::try_from_path(&config).unwrap();
+        assert_eq!(
+            config
+                .resolve_replacement_url(utils::CRATES_IO_INDEX_URL)
+                .unwrap(),
+            "https://artprod.mycompany/artifactory/git/cargo-remote.git"
+        );
+        assert_eq!(
+            config
+                .resolve_replacement_url("https://artmirror.mycompany/artifactory/cargo-mirror.git")
+                .unwrap(),
+            "https://artmirror.mycompany/artifactory/cargo-mirror.git"
+        );
+        assert_eq!(
+            config
+                .resolve_replacement_url(
+                    "https://artprod.mycompany/artifactory/git/cargo-remote.git"
+                )
+                .unwrap(),
+            "https://artprod.mycompany/artifactory/git/cargo-remote.git"
+        );
+    }
 }
diff --git a/crate_universe/src/splicing/crate_index_lookup.rs b/crate_universe/src/splicing/crate_index_lookup.rs
new file mode 100644
index 0000000..3ba4fda
--- /dev/null
+++ b/crate_universe/src/splicing/crate_index_lookup.rs
@@ -0,0 +1,153 @@
+use crate::splicing::SourceInfo;
+use anyhow::{Context, Result};
+use crates_index::IndexConfig;
+use hex::ToHex;
+
+pub enum CrateIndexLookup {
+    Git(crates_index::Index),
+    Http(crates_index::SparseIndex),
+}
+
+impl CrateIndexLookup {
+    pub fn get_source_info(&self, pkg: &cargo_lock::Package) -> Result<Option<SourceInfo>> {
+        let index_config = self
+            .index_config()
+            .context("Failed to get crate index config")?;
+        let crate_ = match self {
+            // The crates we care about should all be in the cache already,
+            // because `cargo metadata` ran which should have fetched them.
+            Self::Http(index) => Some(
+                index
+                    .crate_from_cache(pkg.name.as_str())
+                    .with_context(|| format!("Failed to get crate from cache for {pkg:?}"))?,
+            ),
+            Self::Git(index) => index.crate_(pkg.name.as_str()),
+        };
+        let source_info = crate_.and_then(|crate_idx| {
+            crate_idx
+                .versions()
+                .iter()
+                .find(|v| v.version() == pkg.version.to_string())
+                .and_then(|v| {
+                    v.download_url(&index_config).map(|url| {
+                        let sha256 = pkg
+                            .checksum
+                            .as_ref()
+                            .and_then(|sum| sum.as_sha256().map(|sum| sum.encode_hex::<String>()))
+                            .unwrap_or_else(|| v.checksum().encode_hex::<String>());
+                        SourceInfo { url, sha256 }
+                    })
+                })
+        });
+        Ok(source_info)
+    }
+
+    fn index_config(&self) -> Result<IndexConfig, crates_index::Error> {
+        match self {
+            Self::Git(index) => index.index_config(),
+            Self::Http(index) => index.index_config(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::splicing::crate_index_lookup::CrateIndexLookup;
+    use semver::Version;
+    use std::ffi::OsString;
+
+    // TODO: Avoid global state (env vars) in these tests.
+    // TODO: These should be separate tests methods but they have conflicting state.
+
+    #[test]
+    fn sparse_index() {
+        let runfiles = runfiles::Runfiles::create().unwrap();
+        {
+            let _e = EnvVarResetter::set(
+                "CARGO_HOME",
+                runfiles.rlocation(
+                    "rules_rust/crate_universe/test_data/crate_indexes/lazy_static/cargo_home",
+                ),
+            );
+
+            let index = CrateIndexLookup::Http(
+                crates_index::SparseIndex::from_url("sparse+https://index.crates.io/").unwrap(),
+            );
+            let source_info = index
+                .get_source_info(&cargo_lock::Package {
+                    name: "lazy_static".parse().unwrap(),
+                    version: Version::parse("1.4.0").unwrap(),
+                    source: None,
+                    checksum: None,
+                    dependencies: Vec::new(),
+                    replace: None,
+                })
+                .unwrap()
+                .unwrap();
+            assert_eq!(
+                source_info.url,
+                "https://crates.io/api/v1/crates/lazy_static/1.4.0/download"
+            );
+            assert_eq!(
+                source_info.sha256,
+                "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+            );
+        }
+        {
+            let _e = EnvVarResetter::set("CARGO_HOME", runfiles.rlocation("rules_rust/crate_universe/test_data/crate_indexes/rewritten_lazy_static/cargo_home"));
+
+            let index = CrateIndexLookup::Http(
+                crates_index::SparseIndex::from_url("sparse+https://index.crates.io/").unwrap(),
+            );
+            let source_info = index
+                .get_source_info(&cargo_lock::Package {
+                    name: "lazy_static".parse().unwrap(),
+                    version: Version::parse("1.4.0").unwrap(),
+                    source: None,
+                    checksum: None,
+                    dependencies: Vec::new(),
+                    replace: None,
+                })
+                .unwrap()
+                .unwrap();
+            assert_eq!(
+                source_info.url,
+                "https://some-mirror.com/api/v1/crates/lazy_static/1.4.0/download"
+            );
+            assert_eq!(
+                source_info.sha256,
+                "fffffffffbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+            );
+        }
+    }
+
+    struct EnvVarResetter {
+        key: OsString,
+        value: Option<OsString>,
+    }
+
+    impl EnvVarResetter {
+        fn set<K: Into<OsString>, V: Into<OsString>>(key: K, value: V) -> EnvVarResetter {
+            let key = key.into();
+            let value = value.into();
+            let old_value = std::env::var_os(&key);
+
+            std::env::set_var(&key, value);
+
+            EnvVarResetter {
+                key,
+                value: old_value,
+            }
+        }
+    }
+
+    impl Drop for EnvVarResetter {
+        fn drop(&mut self) {
+            if let Some(old_value) = self.value.as_ref() {
+                std::env::set_var(&self.key, old_value);
+            } else {
+                std::env::remove_var(&self.key);
+            }
+        }
+    }
+}
diff --git a/crate_universe/src/splicing/splicer.rs b/crate_universe/src/splicing/splicer.rs
index 8374df4..63d2f21 100644
--- a/crate_universe/src/splicing/splicer.rs
+++ b/crate_universe/src/splicing/splicer.rs
@@ -1,6 +1,6 @@
 //! Utility for creating valid Cargo workspaces
 
-use std::collections::{BTreeSet, HashMap};
+use std::collections::{BTreeMap, BTreeSet};
 use std::fs;
 use std::path::{Path, PathBuf};
 
@@ -33,7 +33,7 @@
     },
     /// Splice a manifest from multiple disjoint Cargo manifests.
     MultiPackage {
-        manifests: &'a HashMap<PathBuf, Manifest>,
+        manifests: &'a BTreeMap<PathBuf, Manifest>,
         splicing_manifest: &'a SplicingManifest,
     },
 }
@@ -43,12 +43,12 @@
 
 impl<'a> SplicerKind<'a> {
     pub fn new(
-        manifests: &'a HashMap<PathBuf, Manifest>,
+        manifests: &'a BTreeMap<PathBuf, Manifest>,
         splicing_manifest: &'a SplicingManifest,
         cargo: &Path,
     ) -> Result<Self> {
         // First check for any workspaces in the provided manifests
-        let workspace_owned: HashMap<&PathBuf, &Manifest> = manifests
+        let workspace_owned: BTreeMap<&PathBuf, &Manifest> = manifests
             .iter()
             .filter(|(_, manifest)| is_workspace_owned(manifest))
             .collect();
@@ -57,11 +57,10 @@
 
         if !workspace_owned.is_empty() {
             // Filter for the root workspace manifest info
-            let (mut workspace_roots, workspace_packages): (
-                HashMap<&PathBuf, &Manifest>,
-                HashMap<&PathBuf, &Manifest>,
+            let (workspace_roots, workspace_packages): (
+                BTreeMap<&PathBuf, &Manifest>,
+                BTreeMap<&PathBuf, &Manifest>,
             ) = workspace_owned
-                .clone()
                 .into_iter()
                 .partition(|(_, manifest)| is_workspace_root(manifest));
 
@@ -96,7 +95,7 @@
 
             // Ensure all workspace owned manifests are members of the one workspace root
             // UNWRAP: Safe because we've checked workspace_roots isn't empty.
-            let (root_manifest_path, root_manifest) = workspace_roots.drain().last().unwrap();
+            let (root_manifest_path, root_manifest) = workspace_roots.into_iter().next().unwrap();
             let external_workspace_members: BTreeSet<String> = workspace_packages
                 .into_iter()
                 .filter(|(manifest_path, _)| {
@@ -118,7 +117,7 @@
                     .keys()
                     .map(|p| {
                         p.normalize()
-                            .with_context(|| format!("Failed to normalize path {:?}", p))
+                            .with_context(|| format!("Failed to normalize path {p:?}"))
                     })
                     .collect::<Result<_, _>>()?,
             )
@@ -165,7 +164,7 @@
             .map(|member| {
                 let path = root_manifest_dir.join(member).join("Cargo.toml");
                 path.normalize()
-                    .with_context(|| format!("Failed to normalize path {:?}", path))
+                    .with_context(|| format!("Failed to normalize path {path:?}"))
             })
             .collect::<Result<BTreeSet<normpath::BasePathBuf>, _>>()?;
 
@@ -178,10 +177,7 @@
             .map(|workspace_manifest_path| {
                 let label = Label::from_absolute_path(workspace_manifest_path.as_path())
                     .with_context(|| {
-                        format!(
-                            "Failed to identify label for path {:?}",
-                            workspace_manifest_path
-                        )
+                        format!("Failed to identify label for path {workspace_manifest_path:?}")
                     })?;
                 Ok(label.to_string())
             })
@@ -227,10 +223,12 @@
         Self::setup_cargo_config(&splicing_manifest.cargo_config, workspace_dir)?;
 
         // Add any additional depeendencies to the root package
-        Self::inject_direct_packages(&mut manifest, &splicing_manifest.direct_packages)?;
+        if !splicing_manifest.direct_packages.is_empty() {
+            Self::inject_direct_packages(&mut manifest, &splicing_manifest.direct_packages)?;
+        }
 
         let root_manifest_path = workspace_dir.join("Cargo.toml");
-        let member_manifests = HashMap::from([(*path, String::new())]);
+        let member_manifests = BTreeMap::from([(*path, String::new())]);
 
         // Write the generated metadata to the manifest
         let workspace_metadata = WorkspaceMetadata::new(splicing_manifest, member_manifests)?;
@@ -266,11 +264,13 @@
                 default_cargo_workspace_manifest(&splicing_manifest.resolver_version).workspace
         }
 
-        // Add any additional depeendencies to the root package
-        Self::inject_direct_packages(&mut manifest, &splicing_manifest.direct_packages)?;
+        // Add any additional dependencies to the root package
+        if !splicing_manifest.direct_packages.is_empty() {
+            Self::inject_direct_packages(&mut manifest, &splicing_manifest.direct_packages)?;
+        }
 
         let root_manifest_path = workspace_dir.join("Cargo.toml");
-        let member_manifests = HashMap::from([(*path, String::new())]);
+        let member_manifests = BTreeMap::from([(*path, String::new())]);
 
         // Write the generated metadata to the manifest
         let workspace_metadata = WorkspaceMetadata::new(splicing_manifest, member_manifests)?;
@@ -285,7 +285,7 @@
     /// Implementation for splicing together multiple Cargo packages/workspaces
     fn splice_multi_package(
         workspace_dir: &Path,
-        manifests: &&HashMap<PathBuf, Manifest>,
+        manifests: &&BTreeMap<PathBuf, Manifest>,
         splicing_manifest: &&SplicingManifest,
     ) -> Result<SplicedManifest> {
         let mut manifest = default_cargo_workspace_manifest(&splicing_manifest.resolver_version);
@@ -296,12 +296,27 @@
         let installations =
             Self::inject_workspace_members(&mut manifest, manifests, workspace_dir)?;
 
+        // Collect all patches from the manifests provided
+        for (_, sub_manifest) in manifests.iter() {
+            Self::inject_patches(&mut manifest, &sub_manifest.patch).with_context(|| {
+                format!(
+                    "Duplicate `[patch]` entries detected in {:#?}",
+                    manifests
+                        .keys()
+                        .map(|p| p.display().to_string())
+                        .collect::<Vec<String>>()
+                )
+            })?;
+        }
+
         // Write the generated metadata to the manifest
         let workspace_metadata = WorkspaceMetadata::new(splicing_manifest, installations)?;
         workspace_metadata.inject_into(&mut manifest)?;
 
         // Add any additional depeendencies to the root package
-        Self::inject_direct_packages(&mut manifest, &splicing_manifest.direct_packages)?;
+        if !splicing_manifest.direct_packages.is_empty() {
+            Self::inject_direct_packages(&mut manifest, &splicing_manifest.direct_packages)?;
+        }
 
         // Write the root manifest
         let root_manifest_path = workspace_dir.join("Cargo.toml");
@@ -390,7 +405,7 @@
                 fs::create_dir_all(&dot_cargo_dir)?;
             }
 
-            fs::copy(cargo_config_path, &dot_cargo_dir.join("config.toml"))?;
+            fs::copy(cargo_config_path, dot_cargo_dir.join("config.toml"))?;
         }
 
         Ok(())
@@ -400,9 +415,9 @@
     /// Cargo workspace members.
     fn inject_workspace_members<'b>(
         root_manifest: &mut Manifest,
-        manifests: &'b HashMap<PathBuf, Manifest>,
+        manifests: &'b BTreeMap<PathBuf, Manifest>,
         workspace_dir: &Path,
-    ) -> Result<HashMap<&'b PathBuf, String>> {
+    ) -> Result<BTreeMap<&'b PathBuf, String>> {
         manifests
             .iter()
             .map(|(path, manifest)| {
@@ -469,11 +484,42 @@
 
         Ok(())
     }
+
+    fn inject_patches(manifest: &mut Manifest, patches: &cargo_toml::PatchSet) -> Result<()> {
+        for (registry, new_patches) in patches.iter() {
+            // If there is an existing patch entry it will need to be merged
+            if let Some(existing_patches) = manifest.patch.get_mut(registry) {
+                // Error out if there are duplicate patches
+                existing_patches.extend(
+                    new_patches
+                        .iter()
+                        .map(|(pkg, info)| {
+                            if let Some(existing_info) = existing_patches.get(pkg) {
+                                // Only error if the patches are not identical
+                                if existing_info != info {
+                                    bail!(
+                                        "Duplicate patches were found for `[patch.{}] {}`",
+                                        registry,
+                                        pkg
+                                    );
+                                }
+                            }
+                            Ok((pkg.clone(), info.clone()))
+                        })
+                        .collect::<Result<cargo_toml::DepsSet>>()?,
+                );
+            } else {
+                manifest.patch.insert(registry.clone(), new_patches.clone());
+            }
+        }
+
+        Ok(())
+    }
 }
 
 pub struct Splicer {
     workspace_dir: PathBuf,
-    manifests: HashMap<PathBuf, Manifest>,
+    manifests: BTreeMap<PathBuf, Manifest>,
     splicing_manifest: SplicingManifest,
 }
 
@@ -482,13 +528,13 @@
         // Load all manifests
         let manifests = splicing_manifest
             .manifests
-            .iter()
-            .map(|(path, _)| {
+            .keys()
+            .map(|path| {
                 let m = read_manifest(path)
                     .with_context(|| format!("Failed to read manifest at {}", path.display()))?;
                 Ok((path.clone(), m))
             })
-            .collect::<Result<HashMap<PathBuf, Manifest>>>()?;
+            .collect::<Result<BTreeMap<PathBuf, Manifest>>>()?;
 
         Ok(Self {
             workspace_dir,
@@ -510,7 +556,7 @@
 pub fn default_cargo_package_manifest() -> cargo_toml::Manifest {
     // A manifest is generated with a fake workspace member so the [cargo_toml::Manifest::Workspace]
     // member is deseralized and is not `None`.
-    let manifest = cargo_toml::Manifest::from_str(
+    cargo_toml::Manifest::from_str(
         &toml::toml! {
             [package]
             name = DEFAULT_SPLICING_PACKAGE_NAME
@@ -524,9 +570,7 @@
         }
         .to_string(),
     )
-    .unwrap();
-
-    manifest
+    .unwrap()
 }
 
 pub fn default_splicing_package_crate_id() -> CrateId {
@@ -544,9 +588,8 @@
     let mut manifest = cargo_toml::Manifest::from_str(&textwrap::dedent(&format!(
         r#"
             [workspace]
-            resolver = "{}"
+            resolver = "{resolver_version}"
         "#,
-        resolver_version,
     )))
     .unwrap();
 
@@ -611,7 +654,7 @@
     }
 
     // TODO(https://gitlab.com/crates.rs/cargo_toml/-/issues/3)
-    let value = toml::Value::try_from(&manifest)?;
+    let value = toml::Value::try_from(manifest)?;
     fs::write(path, toml::to_string(&value)?)
         .context(format!("Failed to write manifest to {}", path.display()))
 }
@@ -749,7 +792,7 @@
 
         let (cargo_path, rustc_path) = get_cargo_and_rustc_paths();
 
-        let output = MetadataCommand::new()
+        MetadataCommand::new()
             .cargo_path(cargo_path)
             // Cargo detects config files based on `pwd` when running so
             // to ensure user provided Cargo config files are used, it's
@@ -757,25 +800,9 @@
             .current_dir(manifest_dir)
             .manifest_path(manifest_path)
             .other_options(["--offline".to_owned()])
-            .cargo_command()
             .env("RUSTC", rustc_path)
-            .output()
-            .unwrap();
-
-        if !output.status.success() {
-            eprintln!("{}", String::from_utf8_lossy(&output.stderr));
-            assert!(output.status.success());
-        }
-
-        let stdout = String::from_utf8(output.stdout).unwrap();
-
-        assert!(stdout
-            .lines()
-            .find(|line| line.starts_with('{'))
-            .ok_or(cargo_metadata::Error::NoJson)
-            .is_ok());
-
-        MetadataCommand::parse(stdout).unwrap()
+            .exec()
+            .unwrap()
     }
 
     fn mock_cargo_toml(path: &Path, name: &str) -> cargo_toml::Manifest {
@@ -823,14 +850,16 @@
                             "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
                             "url": "https://crates.io/"
                         }
-                    }
+                    },
+                    "features": {}
                 }
             })
         } else {
             serde_json::json!({
                 "cargo-bazel": {
                     "package_prefixes": {},
-                    "sources": {}
+                    "sources": {},
+                    "features": {}
                 }
             })
         };
@@ -863,7 +892,7 @@
 
             splicing_manifest.manifests.insert(
                 manifest_path,
-                Label::from_str(&format!("//{}:Cargo.toml", pkg)).unwrap(),
+                Label::from_str(&format!("//{pkg}:Cargo.toml")).unwrap(),
             );
         }
 
@@ -890,27 +919,23 @@
         }
         let root_pkg = workspace_root.join("root_pkg");
         let manifest_path = root_pkg.join("Cargo.toml");
-        fs::create_dir_all(&manifest_path.parent().unwrap()).unwrap();
+        fs::create_dir_all(manifest_path.parent().unwrap()).unwrap();
         fs::write(&manifest_path, toml::to_string(&manifest).unwrap()).unwrap();
         {
             File::create(root_pkg.join("BUILD.bazel")).unwrap();
         }
 
-        let sub_pkg_a = root_pkg.join("sub_pkg_a");
-        let sub_pkg_b = root_pkg.join("sub_pkg_b");
-        {
-            fs::create_dir_all(&sub_pkg_a).unwrap();
-            File::create(sub_pkg_a.join("BUILD.bazel")).unwrap();
-
-            fs::create_dir_all(&sub_pkg_b).unwrap();
-            File::create(sub_pkg_b.join("BUILD.bazel")).unwrap();
-        }
-
         splicing_manifest.manifests.insert(
             manifest_path,
             Label::from_str("//root_pkg:Cargo.toml").unwrap(),
         );
 
+        for sub_pkg in ["sub_pkg_a", "sub_pkg_b"] {
+            let sub_pkg_path = root_pkg.join(sub_pkg);
+            fs::create_dir_all(&sub_pkg_path).unwrap();
+            File::create(sub_pkg_path.join("BUILD.bazel")).unwrap();
+        }
+
         (splicing_manifest, cache_dir)
     }
 
@@ -925,7 +950,7 @@
 
             splicing_manifest.manifests.insert(
                 manifest_path,
-                Label::from_str(&format!("//{}:Cargo.toml", pkg)).unwrap(),
+                Label::from_str(&format!("//{pkg}:Cargo.toml")).unwrap(),
             );
         }
 
@@ -951,23 +976,19 @@
             File::create(workspace_root.join("WORKSPACE.bazel")).unwrap();
         }
         let manifest_path = workspace_root.join("Cargo.toml");
-        fs::create_dir_all(&manifest_path.parent().unwrap()).unwrap();
+        fs::create_dir_all(manifest_path.parent().unwrap()).unwrap();
         fs::write(&manifest_path, toml::to_string(&manifest).unwrap()).unwrap();
 
-        let sub_pkg_a = workspace_root.join("sub_pkg_a");
-        let sub_pkg_b = workspace_root.join("sub_pkg_b");
-        {
-            fs::create_dir_all(&sub_pkg_a).unwrap();
-            File::create(sub_pkg_a.join("BUILD.bazel")).unwrap();
-
-            fs::create_dir_all(&sub_pkg_b).unwrap();
-            File::create(sub_pkg_b.join("BUILD.bazel")).unwrap();
-        }
-
         splicing_manifest
             .manifests
             .insert(manifest_path, Label::from_str("//:Cargo.toml").unwrap());
 
+        for sub_pkg in ["sub_pkg_a", "sub_pkg_b"] {
+            let sub_pkg_path = workspace_root.join(sub_pkg);
+            fs::create_dir_all(&sub_pkg_path).unwrap();
+            File::create(sub_pkg_path.join("BUILD.bazel")).unwrap();
+        }
+
         (splicing_manifest, cache_dir)
     }
 
@@ -1012,11 +1033,11 @@
 
         if is_root {
             PackageId {
-                repr: format!("{} 0.0.1 (path+file://{})", name, workspace_root),
+                repr: format!("{name} 0.0.1 (path+file://{workspace_root})"),
             }
         } else {
             PackageId {
-                repr: format!("{} 0.0.1 (path+file://{}/{})", name, workspace_root, name,),
+                repr: format!("{name} 0.0.1 (path+file://{workspace_root}/{name})"),
             }
         }
     }
@@ -1050,6 +1071,13 @@
             mock_workspace_metadata(false, None)
         );
 
+        // Since no direct packages were added to the splicing manifest, the cargo_bazel
+        // deps target should __not__ have been injected into the manifest.
+        assert!(!metadata
+            .packages
+            .iter()
+            .any(|pkg| pkg.name == DEFAULT_SPLICING_PACKAGE_NAME));
+
         // Ensure lockfile was successfully spliced
         cargo_lock::Lockfile::load(workspace_root.as_ref().join("Cargo.lock")).unwrap();
     }
@@ -1083,6 +1111,13 @@
             mock_workspace_metadata(false, None)
         );
 
+        // Since no direct packages were added to the splicing manifest, the cargo_bazel
+        // deps target should __not__ have been injected into the manifest.
+        assert!(!metadata
+            .packages
+            .iter()
+            .any(|pkg| pkg.name == DEFAULT_SPLICING_PACKAGE_NAME));
+
         // Ensure lockfile was successfully spliced
         cargo_lock::Lockfile::load(workspace_root.as_ref().join("Cargo.lock")).unwrap();
     }
@@ -1155,7 +1190,7 @@
         fs::create_dir_all(external_manifest.parent().unwrap()).unwrap();
         fs::write(
             &external_manifest,
-            &textwrap::dedent(
+            textwrap::dedent(
                 r#"
                 [package]
                 name = "external_workspace_member"
@@ -1196,6 +1231,46 @@
     }
 
     #[test]
+    fn splice_workspace_no_root_pkg() {
+        let (splicing_manifest, cache_dir) = mock_splicing_manifest_with_workspace_in_root();
+
+        // Modify the root manifest to remove the rendered package
+        fs::write(
+            cache_dir.as_ref().join("Cargo.toml"),
+            textwrap::dedent(
+                r#"
+                [workspace]
+                members = [
+                    "sub_pkg_a",
+                    "sub_pkg_b",
+                ]
+                "#,
+            ),
+        )
+        .unwrap();
+
+        // Splice the workspace
+        let workspace_root = tempfile::tempdir().unwrap();
+        let workspace_manifest =
+            Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
+                .unwrap()
+                .splice_workspace(&cargo())
+                .unwrap();
+
+        let metadata = generate_metadata(workspace_manifest.as_path_buf());
+
+        // Since no direct packages were added to the splicing manifest, the cargo_bazel
+        // deps target should __not__ have been injected into the manifest.
+        assert!(!metadata
+            .packages
+            .iter()
+            .any(|pkg| pkg.name == DEFAULT_SPLICING_PACKAGE_NAME));
+
+        // Ensure lockfile was successfully spliced
+        cargo_lock::Lockfile::load(workspace_root.as_ref().join("Cargo.lock")).unwrap();
+    }
+
+    #[test]
     fn splice_package() {
         let (splicing_manifest, _cache_dir) = mock_splicing_manifest_with_package();
 
@@ -1255,8 +1330,6 @@
                 new_package_id("pkg_a", workspace_root.as_ref(), false),
                 new_package_id("pkg_b", workspace_root.as_ref(), false),
                 new_package_id("pkg_c", workspace_root.as_ref(), false),
-                // Multi package renderings always add a root package
-                new_package_id("direct-cargo-bazel-deps", workspace_root.as_ref(), true),
             ]
         );
 
@@ -1304,8 +1377,6 @@
                 new_package_id("pkg_a", workspace_root.as_ref(), false),
                 new_package_id("pkg_b", workspace_root.as_ref(), false),
                 new_package_id("pkg_c", workspace_root.as_ref(), false),
-                // Multi package renderings always add a root package
-                new_package_id("direct-cargo-bazel-deps", workspace_root.as_ref(), true),
             ]
         );
 
@@ -1320,6 +1391,254 @@
     }
 
     #[test]
+    fn splice_multi_package_with_direct_deps() {
+        let (mut splicing_manifest, _cache_dir) = mock_splicing_manifest_with_multi_package();
+
+        // Add a "direct dependency" entry
+        splicing_manifest.direct_packages.insert(
+            "fake_pkg".to_owned(),
+            cargo_toml::DependencyDetail {
+                version: Some("1.2.3".to_owned()),
+                ..cargo_toml::DependencyDetail::default()
+            },
+        );
+
+        // Splice the workspace
+        let workspace_root = tempfile::tempdir().unwrap();
+        let workspace_manifest =
+            Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
+                .unwrap()
+                .splice_workspace(&cargo())
+                .unwrap();
+
+        // Check the default resolver version
+        let cargo_manifest = cargo_toml::Manifest::from_str(
+            &fs::read_to_string(workspace_manifest.as_path_buf()).unwrap(),
+        )
+        .unwrap();
+
+        // Due to the addition of direct deps for splicing, this package should have been added to the root manfiest.
+        assert!(cargo_manifest.package.unwrap().name == DEFAULT_SPLICING_PACKAGE_NAME);
+    }
+
+    #[test]
+    fn splice_multi_package_with_patch() {
+        let (splicing_manifest, cache_dir) = mock_splicing_manifest_with_multi_package();
+
+        // Generate a patch entry
+        let expected = cargo_toml::PatchSet::from([(
+            "registry".to_owned(),
+            BTreeMap::from([(
+                "foo".to_owned(),
+                cargo_toml::Dependency::Simple("1.2.3".to_owned()),
+            )]),
+        )]);
+
+        // Insert the patch entry to the manifests
+        let manifest_path = cache_dir.as_ref().join("pkg_a").join("Cargo.toml");
+        let mut manifest =
+            cargo_toml::Manifest::from_str(&fs::read_to_string(&manifest_path).unwrap()).unwrap();
+        manifest.patch.extend(expected.clone());
+        fs::write(manifest_path, toml::to_string(&manifest).unwrap()).unwrap();
+
+        // Splice the workspace
+        let workspace_root = tempfile::tempdir().unwrap();
+        let workspace_manifest =
+            Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
+                .unwrap()
+                .splice_workspace(&cargo())
+                .unwrap();
+
+        // Ensure the patches match the expected value
+        let cargo_manifest = cargo_toml::Manifest::from_str(
+            &fs::read_to_string(workspace_manifest.as_path_buf()).unwrap(),
+        )
+        .unwrap();
+        assert_eq!(expected, cargo_manifest.patch);
+    }
+
+    #[test]
+    fn splice_multi_package_with_multiple_patch_registries() {
+        let (splicing_manifest, cache_dir) = mock_splicing_manifest_with_multi_package();
+
+        let mut expected = cargo_toml::PatchSet::new();
+
+        for pkg in ["pkg_a", "pkg_b"] {
+            // Generate a patch entry
+            let new_patch = cargo_toml::PatchSet::from([(
+                format!("{pkg}_registry"),
+                BTreeMap::from([(
+                    "foo".to_owned(),
+                    cargo_toml::Dependency::Simple("1.2.3".to_owned()),
+                )]),
+            )]);
+            expected.extend(new_patch.clone());
+
+            // Insert the patch entry to the manifests
+            let manifest_path = cache_dir.as_ref().join(pkg).join("Cargo.toml");
+            let mut manifest =
+                cargo_toml::Manifest::from_str(&fs::read_to_string(&manifest_path).unwrap())
+                    .unwrap();
+            manifest.patch.extend(new_patch);
+            fs::write(manifest_path, toml::to_string(&manifest).unwrap()).unwrap();
+        }
+
+        // Splice the workspace
+        let workspace_root = tempfile::tempdir().unwrap();
+        let workspace_manifest =
+            Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
+                .unwrap()
+                .splice_workspace(&cargo())
+                .unwrap();
+
+        // Ensure the patches match the expected value
+        let cargo_manifest = cargo_toml::Manifest::from_str(
+            &fs::read_to_string(workspace_manifest.as_path_buf()).unwrap(),
+        )
+        .unwrap();
+        assert_eq!(expected, cargo_manifest.patch);
+    }
+
+    #[test]
+    fn splice_multi_package_with_merged_patch_registries() {
+        let (splicing_manifest, cache_dir) = mock_splicing_manifest_with_multi_package();
+
+        let expected = cargo_toml::PatchSet::from([(
+            "registry".to_owned(),
+            cargo_toml::DepsSet::from([
+                (
+                    "foo-pkg_a".to_owned(),
+                    cargo_toml::Dependency::Simple("1.2.3".to_owned()),
+                ),
+                (
+                    "foo-pkg_b".to_owned(),
+                    cargo_toml::Dependency::Simple("1.2.3".to_owned()),
+                ),
+            ]),
+        )]);
+
+        for pkg in ["pkg_a", "pkg_b"] {
+            // Generate a patch entry
+            let new_patch = cargo_toml::PatchSet::from([(
+                "registry".to_owned(),
+                BTreeMap::from([(
+                    format!("foo-{pkg}"),
+                    cargo_toml::Dependency::Simple("1.2.3".to_owned()),
+                )]),
+            )]);
+
+            // Insert the patch entry to the manifests
+            let manifest_path = cache_dir.as_ref().join(pkg).join("Cargo.toml");
+            let mut manifest =
+                cargo_toml::Manifest::from_str(&fs::read_to_string(&manifest_path).unwrap())
+                    .unwrap();
+            manifest.patch.extend(new_patch);
+            fs::write(manifest_path, toml::to_string(&manifest).unwrap()).unwrap();
+        }
+
+        // Splice the workspace
+        let workspace_root = tempfile::tempdir().unwrap();
+        let workspace_manifest =
+            Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
+                .unwrap()
+                .splice_workspace(&cargo())
+                .unwrap();
+
+        // Ensure the patches match the expected value
+        let cargo_manifest = cargo_toml::Manifest::from_str(
+            &fs::read_to_string(workspace_manifest.as_path_buf()).unwrap(),
+        )
+        .unwrap();
+        assert_eq!(expected, cargo_manifest.patch);
+    }
+
+    #[test]
+    fn splice_multi_package_with_merged_identical_patch_registries() {
+        let (splicing_manifest, cache_dir) = mock_splicing_manifest_with_multi_package();
+
+        let expected = cargo_toml::PatchSet::from([(
+            "registry".to_owned(),
+            cargo_toml::DepsSet::from([(
+                "foo".to_owned(),
+                cargo_toml::Dependency::Simple("1.2.3".to_owned()),
+            )]),
+        )]);
+
+        for pkg in ["pkg_a", "pkg_b"] {
+            // Generate a patch entry
+            let new_patch = cargo_toml::PatchSet::from([(
+                "registry".to_owned(),
+                BTreeMap::from([(
+                    "foo".to_owned(),
+                    cargo_toml::Dependency::Simple("1.2.3".to_owned()),
+                )]),
+            )]);
+
+            // Insert the patch entry to the manifests
+            let manifest_path = cache_dir.as_ref().join(pkg).join("Cargo.toml");
+            let mut manifest =
+                cargo_toml::Manifest::from_str(&fs::read_to_string(&manifest_path).unwrap())
+                    .unwrap();
+            manifest.patch.extend(new_patch);
+            fs::write(manifest_path, toml::to_string(&manifest).unwrap()).unwrap();
+        }
+
+        // Splice the workspace
+        let workspace_root = tempfile::tempdir().unwrap();
+        let workspace_manifest =
+            Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
+                .unwrap()
+                .splice_workspace(&cargo())
+                .unwrap();
+
+        // Ensure the patches match the expected value
+        let cargo_manifest = cargo_toml::Manifest::from_str(
+            &fs::read_to_string(workspace_manifest.as_path_buf()).unwrap(),
+        )
+        .unwrap();
+        assert_eq!(expected, cargo_manifest.patch);
+    }
+
+    #[test]
+    fn splice_multi_package_with_conflicting_patch() {
+        let (splicing_manifest, cache_dir) = mock_splicing_manifest_with_multi_package();
+
+        let mut patch = 3;
+        for pkg in ["pkg_a", "pkg_b"] {
+            // Generate a patch entry
+            let new_patch = cargo_toml::PatchSet::from([(
+                "registry".to_owned(),
+                BTreeMap::from([(
+                    "foo".to_owned(),
+                    cargo_toml::Dependency::Simple(format!("1.2.{patch}")),
+                )]),
+            )]);
+
+            // Increment the patch semver to make the patch info unique.
+            patch += 1;
+
+            // Insert the patch entry to the manifests
+            let manifest_path = cache_dir.as_ref().join(pkg).join("Cargo.toml");
+            let mut manifest =
+                cargo_toml::Manifest::from_str(&fs::read_to_string(&manifest_path).unwrap())
+                    .unwrap();
+            manifest.patch.extend(new_patch);
+            fs::write(manifest_path, toml::to_string(&manifest).unwrap()).unwrap();
+        }
+
+        // Splice the workspace
+        let workspace_root = tempfile::tempdir().unwrap();
+        let result = Splicer::new(workspace_root.as_ref().to_path_buf(), splicing_manifest)
+            .unwrap()
+            .splice_workspace(&cargo());
+
+        // Confirm conflicting patches have been detected
+        assert!(result.is_err());
+        let err_str = result.err().unwrap().to_string();
+        assert!(err_str.starts_with("Duplicate `[patch]` entries detected in"));
+    }
+
+    #[test]
     fn cargo_config_setup() {
         let (mut splicing_manifest, _cache_dir) = mock_splicing_manifest_with_workspace_in_root();
 
@@ -1606,6 +1925,6 @@
 
     fn touch(path: &Path) {
         std::fs::create_dir_all(path.parent().unwrap()).unwrap();
-        std::fs::write(path, &[]).unwrap();
+        std::fs::write(path, []).unwrap();
     }
 }
diff --git a/crate_universe/src/test.rs b/crate_universe/src/test.rs
index 5b67351..3717ec4 100644
--- a/crate_universe/src/test.rs
+++ b/crate_universe/src/test.rs
@@ -83,6 +83,22 @@
         .unwrap()
     }
 
+    pub fn optional_deps_disabled() -> cargo_metadata::Metadata {
+        serde_json::from_str(include_str!(concat!(
+            env!("CARGO_MANIFEST_DIR"),
+            "/test_data/metadata/crate_optional_deps_disabled/metadata.json"
+        )))
+        .unwrap()
+    }
+
+    pub fn optional_deps_enabled() -> cargo_metadata::Metadata {
+        serde_json::from_str(include_str!(concat!(
+            env!("CARGO_MANIFEST_DIR"),
+            "/test_data/metadata/crate_optional_deps_enabled/metadata.json"
+        )))
+        .unwrap()
+    }
+
     pub fn common() -> cargo_metadata::Metadata {
         serde_json::from_str(include_str!(concat!(
             env!("CARGO_MANIFEST_DIR"),
@@ -127,6 +143,14 @@
         .unwrap()
     }
 
+    pub fn multi_cfg_dep() -> cargo_lock::Lockfile {
+        cargo_lock::Lockfile::from_str(include_str!(concat!(
+            env!("CARGO_MANIFEST_DIR"),
+            "/test_data/metadata/multi_cfg_dep/Cargo.lock"
+        )))
+        .unwrap()
+    }
+
     pub fn no_deps() -> cargo_lock::Lockfile {
         cargo_lock::Lockfile::from_str(include_str!(concat!(
             env!("CARGO_MANIFEST_DIR"),
diff --git a/crate_universe/src/utils.rs b/crate_universe/src/utils.rs
index 8953c81..c7f12c2 100644
--- a/crate_universe/src/utils.rs
+++ b/crate_universe/src/utils.rs
@@ -2,6 +2,8 @@
 
 pub mod starlark;
 
+pub const CRATES_IO_INDEX_URL: &str = "https://github.com/rust-lang/crates.io-index";
+
 /// Convert a string into a valid crate module name by applying transforms to invalid characters
 pub fn sanitize_module_name(name: &str) -> String {
     name.replace('-', "_")
diff --git a/crate_universe/src/utils/starlark.rs b/crate_universe/src/utils/starlark.rs
index 57007b7..d99bbab 100644
--- a/crate_universe/src/utils/starlark.rs
+++ b/crate_universe/src/utils/starlark.rs
@@ -3,10 +3,283 @@
 mod glob;
 mod label;
 mod select;
+mod serialize;
+mod target_compatible_with;
+
+use std::collections::BTreeSet as Set;
+
+use serde::{Serialize, Serializer};
+use serde_starlark::Error as StarlarkError;
 
 pub use glob::*;
 pub use label::*;
 pub use select::*;
+pub use target_compatible_with::*;
 
 pub type SelectStringList = SelectList<String>;
 pub type SelectStringDict = SelectDict<String>;
+
+#[derive(Serialize)]
+#[serde(untagged)]
+pub enum Starlark {
+    Load(Load),
+    Package(Package),
+    ExportsFiles(ExportsFiles),
+    Filegroup(Filegroup),
+    Alias(Alias),
+    CargoBuildScript(CargoBuildScript),
+    #[serde(serialize_with = "serialize::rust_proc_macro")]
+    RustProcMacro(RustProcMacro),
+    #[serde(serialize_with = "serialize::rust_library")]
+    RustLibrary(RustLibrary),
+    #[serde(serialize_with = "serialize::rust_binary")]
+    RustBinary(RustBinary),
+
+    #[serde(skip_serializing)]
+    Verbatim(String),
+}
+
+pub struct Load {
+    pub bzl: String,
+    pub items: Set<String>,
+}
+
+pub struct Package {
+    pub default_visibility: Set<String>,
+}
+
+pub struct ExportsFiles {
+    pub paths: Set<String>,
+    pub globs: Glob,
+}
+
+#[derive(Serialize)]
+#[serde(rename = "filegroup")]
+pub struct Filegroup {
+    pub name: String,
+    pub srcs: Glob,
+}
+
+#[derive(Serialize)]
+#[serde(rename = "alias")]
+pub struct Alias {
+    pub name: String,
+    pub actual: String,
+    pub tags: Set<String>,
+}
+
+#[derive(Serialize)]
+#[serde(rename = "cargo_build_script")]
+pub struct CargoBuildScript {
+    pub name: String,
+    #[serde(
+        skip_serializing_if = "SelectDict::is_empty",
+        serialize_with = "SelectDict::serialize_starlark"
+    )]
+    pub aliases: SelectDict<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectDict::is_empty",
+        serialize_with = "SelectDict::serialize_starlark"
+    )]
+    pub build_script_env: SelectDict<WithOriginalConfigurations<String>>,
+    #[serde(skip_serializing_if = "Data::is_empty")]
+    pub compile_data: Data,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub crate_features: SelectList<String>,
+    pub crate_name: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub crate_root: Option<String>,
+    #[serde(skip_serializing_if = "Data::is_empty")]
+    pub data: Data,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub deps: SelectList<WithOriginalConfigurations<String>>,
+    pub edition: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub linker_script: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub links: Option<String>,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub proc_macro_deps: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectDict::is_empty",
+        serialize_with = "SelectDict::serialize_starlark"
+    )]
+    pub rustc_env: SelectDict<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub rustc_env_files: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub rustc_flags: SelectList<WithOriginalConfigurations<String>>,
+    pub srcs: Glob,
+    #[serde(skip_serializing_if = "Set::is_empty")]
+    pub tags: Set<String>,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub tools: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(skip_serializing_if = "Set::is_empty")]
+    pub toolchains: Set<String>,
+    pub version: String,
+    pub visibility: Set<String>,
+}
+
+#[derive(Serialize)]
+pub struct RustProcMacro {
+    pub name: String,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub deps: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub proc_macro_deps: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectDict::is_empty",
+        serialize_with = "SelectDict::serialize_starlark"
+    )]
+    pub aliases: SelectDict<WithOriginalConfigurations<String>>,
+    #[serde(flatten)]
+    pub common: CommonAttrs,
+}
+
+#[derive(Serialize)]
+pub struct RustLibrary {
+    pub name: String,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub deps: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub proc_macro_deps: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectDict::is_empty",
+        serialize_with = "SelectDict::serialize_starlark"
+    )]
+    pub aliases: SelectDict<WithOriginalConfigurations<String>>,
+    #[serde(flatten)]
+    pub common: CommonAttrs,
+    #[serde(skip_serializing_if = "std::ops::Not::not")]
+    pub disable_pipelining: bool,
+}
+
+#[derive(Serialize)]
+pub struct RustBinary {
+    pub name: String,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub deps: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub proc_macro_deps: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectDict::is_empty",
+        serialize_with = "SelectDict::serialize_starlark"
+    )]
+    pub aliases: SelectDict<WithOriginalConfigurations<String>>,
+    #[serde(flatten)]
+    pub common: CommonAttrs,
+}
+
+#[derive(Serialize)]
+pub struct CommonAttrs {
+    #[serde(skip_serializing_if = "Data::is_empty")]
+    pub compile_data: Data,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub crate_features: SelectList<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub crate_root: Option<String>,
+    #[serde(skip_serializing_if = "Data::is_empty")]
+    pub data: Data,
+    pub edition: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub linker_script: Option<String>,
+    #[serde(
+        skip_serializing_if = "SelectDict::is_empty",
+        serialize_with = "SelectDict::serialize_starlark"
+    )]
+    pub rustc_env: SelectDict<WithOriginalConfigurations<String>>,
+    #[serde(
+        skip_serializing_if = "SelectList::is_empty",
+        serialize_with = "SelectList::serialize_starlark"
+    )]
+    pub rustc_env_files: SelectList<WithOriginalConfigurations<String>>,
+    #[serde(skip_serializing_if = "Vec::is_empty")]
+    pub rustc_flags: Vec<String>,
+    pub srcs: Glob,
+    #[serde(skip_serializing_if = "Set::is_empty")]
+    pub tags: Set<String>,
+    #[serde(
+        serialize_with = "serialize_target_compatible_with",
+        skip_serializing_if = "Option::is_none"
+    )]
+    pub target_compatible_with: Option<TargetCompatibleWith>,
+    pub version: String,
+}
+
+fn serialize_target_compatible_with<S>(
+    value: &Option<TargetCompatibleWith>,
+    serializer: S,
+) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    // SAFETY: Option::is_none causes serialization to get skipped.
+    value.as_ref().unwrap().serialize_starlark(serializer)
+}
+
+pub struct Data {
+    pub glob: Glob,
+    pub select: SelectList<WithOriginalConfigurations<String>>,
+}
+
+impl Package {
+    pub fn default_visibility_public() -> Self {
+        let mut default_visibility = Set::new();
+        default_visibility.insert("//visibility:public".to_owned());
+        Package { default_visibility }
+    }
+}
+
+pub fn serialize(starlark: &[Starlark]) -> Result<String, StarlarkError> {
+    let mut content = String::new();
+    for call in starlark {
+        if !content.is_empty() {
+            content.push('\n');
+        }
+        if let Starlark::Verbatim(comment) = call {
+            content.push_str(comment);
+        } else {
+            content.push_str(&serde_starlark::to_string(call)?);
+        }
+    }
+    Ok(content)
+}
diff --git a/crate_universe/src/utils/starlark/glob.rs b/crate_universe/src/utils/starlark/glob.rs
index 23b17a5..a7bcebb 100644
--- a/crate_universe/src/utils/starlark/glob.rs
+++ b/crate_universe/src/utils/starlark/glob.rs
@@ -1,16 +1,93 @@
-use serde::{Deserialize, Serialize};
+use std::collections::BTreeSet;
+use std::fmt;
 
-#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Clone)]
+use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer};
+use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
+use serde::ser::{Serialize, SerializeStruct, Serializer};
+
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
 pub struct Glob {
-    pub include: Vec<String>,
-    pub exclude: Vec<String>,
+    pub include: BTreeSet<String>,
+    pub exclude: BTreeSet<String>,
 }
 
 impl Glob {
     pub fn new_rust_srcs() -> Self {
         Self {
-            include: vec!["**/*.rs".to_owned()],
-            ..Default::default()
+            include: BTreeSet::from(["**/*.rs".to_owned()]),
+            exclude: BTreeSet::new(),
         }
     }
+
+    pub fn is_empty(&self) -> bool {
+        self.include.is_empty()
+        // Note: self.exclude intentionally not considered. A glob is empty if
+        // there are no included globs. A glob cannot have only excludes.
+    }
+}
+
+impl Serialize for Glob {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        if self.exclude.is_empty() {
+            // Serialize as glob([...]).
+            serializer.serialize_newtype_struct("glob", &self.include)
+        } else {
+            // Serialize as glob(include = [...], exclude = [...]).
+            let mut call = serializer.serialize_struct("glob", 2)?;
+            call.serialize_field("include", &self.include)?;
+            call.serialize_field("exclude", &self.exclude)?;
+            call.end()
+        }
+    }
+}
+
+struct GlobVisitor;
+
+impl<'de> Deserialize<'de> for Glob {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        deserializer.deserialize_any(GlobVisitor)
+    }
+}
+
+impl<'de> Visitor<'de> for GlobVisitor {
+    type Value = Glob;
+
+    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str("glob")
+    }
+
+    // Deserialize ["included","globs","only"]
+    fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
+    where
+        A: SeqAccess<'de>,
+    {
+        Ok(Glob {
+            include: BTreeSet::deserialize(SeqAccessDeserializer::new(seq))?,
+            exclude: BTreeSet::new(),
+        })
+    }
+
+    // Deserialize {"include":["included","globs"],"exclude":["excluded","globs"]}
+    fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
+    where
+        A: MapAccess<'de>,
+    {
+        #[derive(serde::Deserialize)]
+        struct GlobMap {
+            include: BTreeSet<String>,
+            exclude: BTreeSet<String>,
+        }
+
+        let glob_map = GlobMap::deserialize(MapAccessDeserializer::new(map))?;
+        Ok(Glob {
+            include: glob_map.include,
+            exclude: glob_map.exclude,
+        })
+    }
 }
diff --git a/crate_universe/src/utils/starlark/label.rs b/crate_universe/src/utils/starlark/label.rs
index a21c81e..c074a62 100644
--- a/crate_universe/src/utils/starlark/label.rs
+++ b/crate_universe/src/utils/starlark/label.rs
@@ -20,16 +20,17 @@
     type Err = anyhow::Error;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let re = Regex::new(r"^(@[\w\d\-_\.]*)?/{0,2}([\w\d\-_\./]+)?:?([\+\w\d\-_\./]+)$")?;
+        let re = Regex::new(r"^(@@?[\w\d\-_\.]*)?/{0,2}([\w\d\-_\./+]+)?(:([\+\w\d\-_\./]+))?$")?;
         let cap = re
             .captures(s)
-            .with_context(|| format!("Failed to parse label from string: {}", s))?;
+            .with_context(|| format!("Failed to parse label from string: {s}"))?;
 
         let repository = cap
             .get(1)
             .map(|m| m.as_str().trim_start_matches('@').to_owned());
+
         let package = cap.get(2).map(|m| m.as_str().to_owned());
-        let mut target = cap.get(3).map(|m| m.as_str().to_owned());
+        let mut target = cap.get(4).map(|m| m.as_str().to_owned());
 
         if target.is_none() {
             if let Some(pkg) = &package {
@@ -56,14 +57,14 @@
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // Add the repository
         if let Some(repo) = &self.repository {
-            write!(f, "@{}", repo)?;
+            write!(f, "@{repo}")?;
         }
 
         write!(f, "//")?;
 
         // Add the package
         if let Some(pkg) = &self.package {
-            write!(f, "{}", pkg)?;
+            write!(f, "{pkg}")?;
         }
 
         write!(f, ":{}", self.target)?;
@@ -192,6 +193,15 @@
     use tempfile::tempdir;
 
     #[test]
+    fn full_label_bzlmod() {
+        let label = Label::from_str("@@repo//package/sub_package:target").unwrap();
+        assert_eq!(label.to_string(), "@repo//package/sub_package:target");
+        assert_eq!(label.repository.unwrap(), "repo");
+        assert_eq!(label.package.unwrap(), "package/sub_package");
+        assert_eq!(label.target, "target");
+    }
+
+    #[test]
     fn full_label() {
         let label = Label::from_str("@repo//package/sub_package:target").unwrap();
         assert_eq!(label.to_string(), "@repo//package/sub_package:target");
@@ -258,13 +268,25 @@
     }
 
     #[test]
+    fn label_contains_plus() {
+        let label = Label::from_str("@repo//vendor/wasi-0.11.0+wasi-snapshot-preview1:BUILD.bazel")
+            .unwrap();
+        assert_eq!(label.repository.unwrap(), "repo");
+        assert_eq!(
+            label.package.unwrap(),
+            "vendor/wasi-0.11.0+wasi-snapshot-preview1"
+        );
+        assert_eq!(label.target, "BUILD.bazel");
+    }
+
+    #[test]
     fn invalid_double_colon() {
         assert!(Label::from_str("::target").is_err());
     }
 
     #[test]
-    fn invalid_double_at() {
-        assert!(Label::from_str("@@repo//pkg:target").is_err());
+    fn invalid_triple_at() {
+        assert!(Label::from_str("@@@repo//pkg:target").is_err());
     }
 
     #[test]
@@ -282,8 +304,8 @@
         let actual_file = subdir.join("greatgrandchild");
         create_dir_all(subdir).unwrap();
         {
-            File::create(&workspace).unwrap();
-            File::create(&build_file).unwrap();
+            File::create(workspace).unwrap();
+            File::create(build_file).unwrap();
             File::create(&actual_file).unwrap();
         }
         let label = Label::from_absolute_path(&actual_file).unwrap();
@@ -300,7 +322,7 @@
         let actual_file = subdir.join("greatgrandchild");
         create_dir_all(subdir).unwrap();
         {
-            File::create(&build_file).unwrap();
+            File::create(build_file).unwrap();
             File::create(&actual_file).unwrap();
         }
         let err = Label::from_absolute_path(&actual_file)
@@ -318,7 +340,7 @@
         let actual_file = subdir.join("greatgrandchild");
         create_dir_all(subdir).unwrap();
         {
-            File::create(&workspace).unwrap();
+            File::create(workspace).unwrap();
             File::create(&actual_file).unwrap();
         }
         let err = Label::from_absolute_path(&actual_file)
diff --git a/crate_universe/src/utils/starlark/select.rs b/crate_universe/src/utils/starlark/select.rs
index 4a8a3cc..f01acb0 100644
--- a/crate_universe/src/utils/starlark/select.rs
+++ b/crate_universe/src/utils/starlark/select.rs
@@ -1,6 +1,11 @@
-use serde::{Deserialize, Serialize};
 use std::collections::{btree_set, BTreeMap, BTreeSet};
-use std::iter::once;
+use std::iter::{once, FromIterator};
+
+use serde::ser::{SerializeMap, SerializeTupleStruct, Serializer};
+use serde::{Deserialize, Serialize};
+use serde_starlark::{FunctionCall, LineComment, MULTILINE};
+
+use crate::utils::starlark::serialize::MultilineArray;
 
 pub trait SelectMap<T, U> {
     // A selectable should also implement a `map` function allowing one type of selectable
@@ -19,8 +24,16 @@
 
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Clone)]
 pub struct SelectList<T: Ord> {
+    // Invariant: any T in `common` is not anywhere in `selects`.
     common: BTreeSet<T>,
+    // Invariant: none of the sets are empty.
     selects: BTreeMap<String, BTreeSet<T>>,
+    // Elements that used to be in `selects` before the most recent
+    // `remap_configurations` operation, but whose old configuration did not get
+    // mapped to any new configuration. They could be ignored, but are preserved
+    // here to generate comments that help the user understand what happened.
+    #[serde(skip_serializing_if = "BTreeSet::is_empty", default = "BTreeSet::new")]
+    unmapped: BTreeSet<T>,
 }
 
 impl<T: Ord> Default for SelectList<T> {
@@ -28,6 +41,7 @@
         Self {
             common: BTreeSet::new(),
             selects: BTreeMap::new(),
+            unmapped: BTreeSet::new(),
         }
     }
 }
@@ -37,25 +51,22 @@
     pub fn insert(&mut self, value: T, configuration: Option<String>) {
         match configuration {
             None => {
+                self.selects.retain(|_, set| {
+                    set.remove(&value);
+                    !set.is_empty()
+                });
                 self.common.insert(value);
             }
             Some(cfg) => {
-                match self.selects.get_mut(&cfg) {
-                    None => {
-                        let mut set = BTreeSet::new();
-                        set.insert(value);
-                        self.selects.insert(cfg, set);
-                    }
-                    Some(set) => {
-                        set.insert(value);
-                    }
-                };
+                if !self.common.contains(&value) {
+                    self.selects.entry(cfg).or_default().insert(value);
+                }
             }
-        };
+        }
     }
 
     // TODO: This should probably be added to the [Select] trait
-    pub fn get_iter<'a>(&'a self, config: Option<&String>) -> Option<btree_set::Iter<T>> {
+    pub fn get_iter(&self, config: Option<&String>) -> Option<btree_set::Iter<T>> {
         match config {
             Some(conf) => self.selects.get(conf).map(|set| set.iter()),
             None => Some(self.common.iter()),
@@ -63,8 +74,208 @@
     }
 
     /// Determine whether or not the select should be serialized
-    pub fn should_skip_serializing(&self) -> bool {
-        self.common.is_empty() && self.selects.is_empty()
+    pub fn is_empty(&self) -> bool {
+        self.common.is_empty() && self.selects.is_empty() && self.unmapped.is_empty()
+    }
+
+    /// Maps configuration names by `f`. This function must be injective
+    /// (that is `a != b --> f(a) != f(b)`).
+    pub fn map_configuration_names<F>(self, mut f: F) -> Self
+    where
+        F: FnMut(String) -> String,
+    {
+        Self {
+            common: self.common,
+            selects: self.selects.into_iter().map(|(k, v)| (f(k), v)).collect(),
+            unmapped: self.unmapped,
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
+pub struct WithOriginalConfigurations<T> {
+    value: T,
+    original_configurations: Option<BTreeSet<String>>,
+}
+
+impl<T: Ord + Clone> SelectList<T> {
+    /// Generates a new SelectList re-keyed by the given configuration mapping.
+    /// This mapping maps from configurations in the current SelectList to sets of
+    /// configurations in the new SelectList.
+    pub fn remap_configurations(
+        self,
+        mapping: &BTreeMap<String, BTreeSet<String>>,
+    ) -> SelectList<WithOriginalConfigurations<T>> {
+        // Map new configuration -> value -> old configurations.
+        let mut remapped: BTreeMap<String, BTreeMap<T, BTreeSet<String>>> = BTreeMap::new();
+        // Map value -> old configurations.
+        let mut unmapped: BTreeMap<T, BTreeSet<String>> = BTreeMap::new();
+
+        for (original_configuration, values) in self.selects {
+            match mapping.get(&original_configuration) {
+                Some(configurations) => {
+                    for configuration in configurations {
+                        for value in &values {
+                            remapped
+                                .entry(configuration.clone())
+                                .or_default()
+                                .entry(value.clone())
+                                .or_default()
+                                .insert(original_configuration.clone());
+                        }
+                    }
+                }
+                None => {
+                    for value in values {
+                        unmapped
+                            .entry(value)
+                            .or_default()
+                            .insert(original_configuration.clone());
+                    }
+                }
+            }
+        }
+
+        SelectList {
+            common: self
+                .common
+                .into_iter()
+                .map(|value| WithOriginalConfigurations {
+                    value,
+                    original_configurations: None,
+                })
+                .collect(),
+            selects: remapped
+                .into_iter()
+                .map(|(new_configuration, value_to_original_configuration)| {
+                    (
+                        new_configuration,
+                        value_to_original_configuration
+                            .into_iter()
+                            .map(
+                                |(value, original_configurations)| WithOriginalConfigurations {
+                                    value,
+                                    original_configurations: Some(original_configurations),
+                                },
+                            )
+                            .collect(),
+                    )
+                })
+                .collect(),
+            unmapped: unmapped
+                .into_iter()
+                .map(
+                    |(value, original_configurations)| WithOriginalConfigurations {
+                        value,
+                        original_configurations: Some(original_configurations),
+                    },
+                )
+                .collect(),
+        }
+    }
+}
+
+#[derive(Serialize)]
+#[serde(rename = "selects.NO_MATCHING_PLATFORM_TRIPLES")]
+struct NoMatchingPlatformTriples;
+
+// TODO: after removing the remaining tera template usages of SelectList, this
+// inherent method should become the Serialize impl.
+impl<T: Ord> SelectList<T> {
+    pub fn serialize_starlark<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        T: Serialize,
+        S: Serializer,
+    {
+        // Output looks like:
+        //
+        //     [
+        //         "common...",
+        //     ] + select({
+        //         "configuration": [
+        //             "value...",  # cfg(whatever)
+        //         ],
+        //         "//conditions:default": [],
+        //     })
+        //
+        // The common part and select are each omitted if they are empty (except
+        // if the entire thing is empty, in which case we serialize the common
+        // part to get an empty array).
+        //
+        // If there are unmapped entries, we include them like this:
+        //
+        //     [
+        //         "common...",
+        //     ] + selects.with_unmapped({
+        //         "configuration": [
+        //             "value...",  # cfg(whatever)
+        //         ],
+        //         "//conditions:default": [],
+        //         selects.NO_MATCHING_PLATFORM_TRIPLES: [
+        //             "value...",  # cfg(obscure)
+        //         ],
+        //     })
+
+        let mut plus = serializer.serialize_tuple_struct("+", MULTILINE)?;
+
+        if !self.common.is_empty() || self.selects.is_empty() && self.unmapped.is_empty() {
+            plus.serialize_field(&MultilineArray(&self.common))?;
+        }
+
+        if !self.selects.is_empty() || !self.unmapped.is_empty() {
+            struct SelectInner<'a, T: Ord>(&'a SelectList<T>);
+
+            impl<'a, T> Serialize for SelectInner<'a, T>
+            where
+                T: Ord + Serialize,
+            {
+                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+                where
+                    S: Serializer,
+                {
+                    let mut map = serializer.serialize_map(Some(MULTILINE))?;
+                    for (cfg, value) in &self.0.selects {
+                        map.serialize_entry(cfg, &MultilineArray(value))?;
+                    }
+                    map.serialize_entry("//conditions:default", &[] as &[T])?;
+                    if !self.0.unmapped.is_empty() {
+                        map.serialize_entry(
+                            &NoMatchingPlatformTriples,
+                            &MultilineArray(&self.0.unmapped),
+                        )?;
+                    }
+                    map.end()
+                }
+            }
+
+            let function = if self.unmapped.is_empty() {
+                "select"
+            } else {
+                "selects.with_unmapped"
+            };
+
+            plus.serialize_field(&FunctionCall::new(function, [SelectInner(self)]))?;
+        }
+
+        plus.end()
+    }
+}
+
+impl<T> Serialize for WithOriginalConfigurations<T>
+where
+    T: Serialize,
+{
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        if let Some(original_configurations) = &self.original_configurations {
+            let comment =
+                Vec::from_iter(original_configurations.iter().map(String::as_str)).join(", ");
+            LineComment::new(&self.value, &comment).serialize(serializer)
+        } else {
+            self.value.serialize(serializer)
+        }
     }
 }
 
@@ -82,21 +293,43 @@
     type Mapped = SelectList<U>;
 
     fn map<F: Copy + Fn(T) -> U>(self, func: F) -> Self::Mapped {
+        let common: BTreeSet<U> = self.common.into_iter().map(func).collect();
+        let selects: BTreeMap<String, BTreeSet<U>> = self
+            .selects
+            .into_iter()
+            .filter_map(|(key, set)| {
+                let set: BTreeSet<U> = set
+                    .into_iter()
+                    .map(func)
+                    .filter(|value| !common.contains(value))
+                    .collect();
+                if set.is_empty() {
+                    None
+                } else {
+                    Some((key, set))
+                }
+            })
+            .collect();
         SelectList {
-            common: self.common.into_iter().map(func).collect(),
-            selects: self
-                .selects
-                .into_iter()
-                .map(|(key, map)| (key, map.into_iter().map(func).collect()))
-                .collect(),
+            common,
+            selects,
+            unmapped: self.unmapped.into_iter().map(func).collect(),
         }
     }
 }
 
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Clone)]
 pub struct SelectDict<T: Ord> {
+    // Invariant: keys in this map are not in any of the inner maps of `selects`.
     common: BTreeMap<String, T>,
+    // Invariant: none of the inner maps are empty.
     selects: BTreeMap<String, BTreeMap<String, T>>,
+    // Elements that used to be in `selects` before the most recent
+    // `remap_configurations` operation, but whose old configuration did not get
+    // mapped to any new configuration. They could be ignored, but are preserved
+    // here to generate comments that help the user understand what happened.
+    #[serde(skip_serializing_if = "BTreeMap::is_empty", default = "BTreeMap::new")]
+    unmapped: BTreeMap<String, T>,
 }
 
 impl<T: Ord> Default for SelectDict<T> {
@@ -104,35 +337,200 @@
         Self {
             common: BTreeMap::new(),
             selects: BTreeMap::new(),
+            unmapped: BTreeMap::new(),
         }
     }
 }
 
 impl<T: Ord> SelectDict<T> {
-    // TODO: This should probably be added to the [Select] trait
-    pub fn insert(&mut self, value: BTreeMap<String, T>, configuration: Option<String>) {
+    pub fn insert(&mut self, key: String, value: T, configuration: Option<String>) {
         match configuration {
             None => {
-                self.common.extend(value);
+                self.selects.retain(|_, map| {
+                    map.remove(&key);
+                    !map.is_empty()
+                });
+                self.common.insert(key, value);
             }
             Some(cfg) => {
-                match self.selects.get_mut(&cfg) {
-                    None => {
-                        let mut set = BTreeMap::new();
-                        set.extend(value);
-                        self.selects.insert(cfg, set);
-                    }
-                    Some(set) => {
-                        set.extend(value);
-                    }
-                };
+                if !self.common.contains_key(&key) {
+                    self.selects.entry(cfg).or_default().insert(key, value);
+                }
             }
-        };
+        }
     }
 
-    /// Determine whether or not the select should be serialized
-    pub fn should_skip_serializing(&self) -> bool {
-        self.common.is_empty() && self.selects.is_empty()
+    pub fn extend(&mut self, entries: BTreeMap<String, T>, configuration: Option<String>) {
+        for (key, value) in entries {
+            self.insert(key, value, configuration.clone());
+        }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.common.is_empty() && self.selects.is_empty() && self.unmapped.is_empty()
+    }
+}
+
+impl<T: Ord + Clone> SelectDict<T> {
+    /// Generates a new SelectDict re-keyed by the given configuration mapping.
+    /// This mapping maps from configurations in the current SelectDict to sets
+    /// of configurations in the new SelectDict.
+    pub fn remap_configurations(
+        self,
+        mapping: &BTreeMap<String, BTreeSet<String>>,
+    ) -> SelectDict<WithOriginalConfigurations<T>> {
+        // Map new configuration -> entry -> old configurations.
+        let mut remapped: BTreeMap<String, BTreeMap<(String, T), BTreeSet<String>>> =
+            BTreeMap::new();
+        // Map entry -> old configurations.
+        let mut unmapped: BTreeMap<(String, T), BTreeSet<String>> = BTreeMap::new();
+
+        for (original_configuration, entries) in self.selects {
+            match mapping.get(&original_configuration) {
+                Some(configurations) => {
+                    for configuration in configurations {
+                        for (key, value) in &entries {
+                            remapped
+                                .entry(configuration.clone())
+                                .or_default()
+                                .entry((key.clone(), value.clone()))
+                                .or_default()
+                                .insert(original_configuration.clone());
+                        }
+                    }
+                }
+                None => {
+                    for (key, value) in entries {
+                        unmapped
+                            .entry((key, value))
+                            .or_default()
+                            .insert(original_configuration.clone());
+                    }
+                }
+            }
+        }
+
+        SelectDict {
+            common: self
+                .common
+                .into_iter()
+                .map(|(key, value)| {
+                    (
+                        key,
+                        WithOriginalConfigurations {
+                            value,
+                            original_configurations: None,
+                        },
+                    )
+                })
+                .collect(),
+            selects: remapped
+                .into_iter()
+                .map(|(new_configuration, entry_to_original_configuration)| {
+                    (
+                        new_configuration,
+                        entry_to_original_configuration
+                            .into_iter()
+                            .map(|((key, value), original_configurations)| {
+                                (
+                                    key,
+                                    WithOriginalConfigurations {
+                                        value,
+                                        original_configurations: Some(original_configurations),
+                                    },
+                                )
+                            })
+                            .collect(),
+                    )
+                })
+                .collect(),
+            unmapped: unmapped
+                .into_iter()
+                .map(|((key, value), original_configurations)| {
+                    (
+                        key,
+                        WithOriginalConfigurations {
+                            value,
+                            original_configurations: Some(original_configurations),
+                        },
+                    )
+                })
+                .collect(),
+        }
+    }
+}
+
+// TODO: after removing the remaining tera template usages of SelectDict, this
+// inherent method should become the Serialize impl.
+impl<T: Ord + Serialize> SelectDict<T> {
+    pub fn serialize_starlark<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        // If there are no platform-specific entries, we output just an ordinary
+        // dict.
+        //
+        // If there are platform-specific ones, we use the following. Ideally it
+        // could be done as `dicts.add({...}, select({...}))` but bazel_skylib's
+        // dicts.add does not support selects.
+        //
+        //     select({
+        //         "configuration": {
+        //             "common-key": "common-value",
+        //             "plat-key": "plat-value",  # cfg(whatever)
+        //         },
+        //         "//conditions:default": {},
+        //     })
+        //
+        // If there are unmapped entries, we include them like this:
+        //
+        //     selects.with_unmapped({
+        //         "configuration": {
+        //             "common-key": "common-value",
+        //             "plat-key": "plat-value",  # cfg(whatever)
+        //         },
+        //         "//conditions:default": [],
+        //         selects.NO_MATCHING_PLATFORM_TRIPLES: {
+        //             "unmapped-key": "unmapped-value",  # cfg(obscure)
+        //         },
+        //     })
+
+        if self.selects.is_empty() && self.unmapped.is_empty() {
+            return self.common.serialize(serializer);
+        }
+
+        struct SelectInner<'a, T: Ord>(&'a SelectDict<T>);
+
+        impl<'a, T> Serialize for SelectInner<'a, T>
+        where
+            T: Ord + Serialize,
+        {
+            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+            where
+                S: Serializer,
+            {
+                let mut map = serializer.serialize_map(Some(MULTILINE))?;
+                for (cfg, value) in &self.0.selects {
+                    let mut combined = BTreeMap::new();
+                    combined.extend(&self.0.common);
+                    combined.extend(value);
+                    map.serialize_entry(cfg, &combined)?;
+                }
+                map.serialize_entry("//conditions:default", &self.0.common)?;
+                if !self.0.unmapped.is_empty() {
+                    map.serialize_entry(&NoMatchingPlatformTriples, &self.0.unmapped)?;
+                }
+                map.end()
+            }
+        }
+
+        let function = if self.unmapped.is_empty() {
+            "select"
+        } else {
+            "selects.with_unmapped"
+        };
+
+        FunctionCall::new(function, [SelectInner(self)]).serialize(serializer)
     }
 }
 
@@ -146,21 +544,132 @@
     }
 }
 
-impl<T: Ord, U: Ord> SelectMap<T, U> for SelectDict<T> {
-    type Mapped = SelectDict<U>;
+#[cfg(test)]
+mod test {
+    use super::*;
 
-    fn map<F: Copy + Fn(T) -> U>(self, func: F) -> Self::Mapped {
-        SelectDict {
-            common: self
-                .common
-                .into_iter()
-                .map(|(key, val)| (key, func(val)))
-                .collect(),
-            selects: self
-                .selects
-                .into_iter()
-                .map(|(key, map)| (key, map.into_iter().map(|(k, v)| (k, func(v))).collect()))
-                .collect(),
-        }
+    use indoc::indoc;
+
+    #[test]
+    fn remap_select_list_configurations() {
+        let mut select_list = SelectList::default();
+        select_list.insert("dep-a".to_owned(), Some("cfg(macos)".to_owned()));
+        select_list.insert("dep-b".to_owned(), Some("cfg(macos)".to_owned()));
+        select_list.insert("dep-d".to_owned(), Some("cfg(macos)".to_owned()));
+        select_list.insert("dep-a".to_owned(), Some("cfg(x86_64)".to_owned()));
+        select_list.insert("dep-c".to_owned(), Some("cfg(x86_64)".to_owned()));
+        select_list.insert("dep-e".to_owned(), Some("cfg(pdp11)".to_owned()));
+        select_list.insert("dep-d".to_owned(), None);
+
+        let mapping = BTreeMap::from([
+            (
+                "cfg(macos)".to_owned(),
+                BTreeSet::from(["x86_64-macos".to_owned(), "aarch64-macos".to_owned()]),
+            ),
+            (
+                "cfg(x86_64)".to_owned(),
+                BTreeSet::from(["x86_64-linux".to_owned(), "x86_64-macos".to_owned()]),
+            ),
+        ]);
+
+        let mut expected = SelectList::default();
+        expected.insert(
+            WithOriginalConfigurations {
+                value: "dep-a".to_owned(),
+                original_configurations: Some(BTreeSet::from([
+                    "cfg(macos)".to_owned(),
+                    "cfg(x86_64)".to_owned(),
+                ])),
+            },
+            Some("x86_64-macos".to_owned()),
+        );
+        expected.insert(
+            WithOriginalConfigurations {
+                value: "dep-b".to_owned(),
+                original_configurations: Some(BTreeSet::from(["cfg(macos)".to_owned()])),
+            },
+            Some("x86_64-macos".to_owned()),
+        );
+        expected.insert(
+            WithOriginalConfigurations {
+                value: "dep-c".to_owned(),
+                original_configurations: Some(BTreeSet::from(["cfg(x86_64)".to_owned()])),
+            },
+            Some("x86_64-macos".to_owned()),
+        );
+        expected.insert(
+            WithOriginalConfigurations {
+                value: "dep-a".to_owned(),
+                original_configurations: Some(BTreeSet::from(["cfg(macos)".to_owned()])),
+            },
+            Some("aarch64-macos".to_owned()),
+        );
+        expected.insert(
+            WithOriginalConfigurations {
+                value: "dep-b".to_owned(),
+                original_configurations: Some(BTreeSet::from(["cfg(macos)".to_owned()])),
+            },
+            Some("aarch64-macos".to_owned()),
+        );
+        expected.insert(
+            WithOriginalConfigurations {
+                value: "dep-a".to_owned(),
+                original_configurations: Some(BTreeSet::from(["cfg(x86_64)".to_owned()])),
+            },
+            Some("x86_64-linux".to_owned()),
+        );
+        expected.insert(
+            WithOriginalConfigurations {
+                value: "dep-c".to_owned(),
+                original_configurations: Some(BTreeSet::from(["cfg(x86_64)".to_owned()])),
+            },
+            Some("x86_64-linux".to_owned()),
+        );
+        expected.insert(
+            WithOriginalConfigurations {
+                value: "dep-d".to_owned(),
+                original_configurations: None,
+            },
+            None,
+        );
+
+        expected.unmapped.insert(WithOriginalConfigurations {
+            value: "dep-e".to_owned(),
+            original_configurations: Some(BTreeSet::from(["cfg(pdp11)".to_owned()])),
+        });
+
+        let select_list = select_list.remap_configurations(&mapping);
+        assert_eq!(select_list, expected);
+
+        let expected_starlark = indoc! {r#"
+            [
+                "dep-d",
+            ] + selects.with_unmapped({
+                "aarch64-macos": [
+                    "dep-a",  # cfg(macos)
+                    "dep-b",  # cfg(macos)
+                ],
+                "x86_64-linux": [
+                    "dep-a",  # cfg(x86_64)
+                    "dep-c",  # cfg(x86_64)
+                ],
+                "x86_64-macos": [
+                    "dep-a",  # cfg(macos), cfg(x86_64)
+                    "dep-b",  # cfg(macos)
+                    "dep-c",  # cfg(x86_64)
+                ],
+                "//conditions:default": [],
+                selects.NO_MATCHING_PLATFORM_TRIPLES: [
+                    "dep-e",  # cfg(pdp11)
+                ],
+            })
+        "#};
+
+        assert_eq!(
+            select_list
+                .serialize_starlark(serde_starlark::Serializer)
+                .unwrap(),
+            expected_starlark,
+        );
     }
 }
diff --git a/crate_universe/src/utils/starlark/serialize.rs b/crate_universe/src/utils/starlark/serialize.rs
new file mode 100644
index 0000000..4d362c7
--- /dev/null
+++ b/crate_universe/src/utils/starlark/serialize.rs
@@ -0,0 +1,125 @@
+use serde::ser::{SerializeSeq, SerializeStruct, SerializeTupleStruct, Serializer};
+use serde::Serialize;
+use serde_starlark::{FunctionCall, MULTILINE, ONELINE};
+
+use super::{
+    Data, ExportsFiles, Load, Package, RustBinary, RustLibrary, RustProcMacro, SelectList,
+};
+
+// For structs that contain #[serde(flatten)], a quirk of how Serde processes
+// that attribute is that they get serialized as a map, not struct. In Starlark
+// unlike in JSON, maps and structs are differently serialized, so we need to
+// help fill in the function name or else we'd get a Starlark map instead.
+pub fn rust_proc_macro<S>(rule: &RustProcMacro, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    FunctionCall::new("rust_proc_macro", rule).serialize(serializer)
+}
+
+pub fn rust_library<S>(rule: &RustLibrary, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    FunctionCall::new("rust_library", rule).serialize(serializer)
+}
+
+pub fn rust_binary<S>(rule: &RustBinary, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    FunctionCall::new("rust_binary", rule).serialize(serializer)
+}
+
+// Serialize an array with each element on its own line, even if there is just a
+// single element which serde_starlark would ordinarily place on the same line
+// as the array brackets.
+pub struct MultilineArray<'a, A>(pub &'a A);
+
+impl<'a, A, T> Serialize for MultilineArray<'a, A>
+where
+    &'a A: IntoIterator<Item = &'a T>,
+    T: Serialize + 'a,
+{
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut array = serializer.serialize_seq(Some(serde_starlark::MULTILINE))?;
+        for element in self.0 {
+            array.serialize_element(element)?;
+        }
+        array.end()
+    }
+}
+
+// TODO: This can go away after SelectList's derived Serialize impl (used by
+// tera) goes away and `serialize_starlark` becomes its real Serialize impl.
+#[derive(Serialize)]
+#[serde(transparent)]
+pub struct SelectListWrapper<'a, T: Ord + Serialize>(
+    #[serde(serialize_with = "SelectList::serialize_starlark")] &'a SelectList<T>,
+);
+
+impl Serialize for Load {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let line = if self.items.len() > 1 {
+            MULTILINE
+        } else {
+            ONELINE
+        };
+        let mut call = serializer.serialize_tuple_struct("load", line)?;
+        call.serialize_field(&self.bzl)?;
+        for item in &self.items {
+            call.serialize_field(item)?;
+        }
+        call.end()
+    }
+}
+
+impl Serialize for Package {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut call = serializer.serialize_struct("package", ONELINE)?;
+        call.serialize_field("default_visibility", &self.default_visibility)?;
+        call.end()
+    }
+}
+
+impl Serialize for ExportsFiles {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut call = serializer.serialize_tuple_struct("exports_files", MULTILINE)?;
+        call.serialize_field(&FunctionCall::new("+", (&self.paths, &self.globs)))?;
+        call.end()
+    }
+}
+
+impl Data {
+    pub fn is_empty(&self) -> bool {
+        self.glob.is_empty() && self.select.is_empty()
+    }
+}
+
+impl Serialize for Data {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut plus = serializer.serialize_tuple_struct("+", MULTILINE)?;
+        if !self.glob.is_empty() {
+            plus.serialize_field(&self.glob)?;
+        }
+        if !self.select.is_empty() || self.glob.is_empty() {
+            plus.serialize_field(&SelectListWrapper(&self.select))?;
+        }
+        plus.end()
+    }
+}
diff --git a/crate_universe/src/utils/starlark/target_compatible_with.rs b/crate_universe/src/utils/starlark/target_compatible_with.rs
new file mode 100644
index 0000000..1c58392
--- /dev/null
+++ b/crate_universe/src/utils/starlark/target_compatible_with.rs
@@ -0,0 +1,92 @@
+use std::collections::BTreeSet;
+
+use serde::ser::{SerializeMap, SerializeTupleStruct, Serializer};
+use serde::{Deserialize, Serialize};
+use serde_starlark::{FunctionCall, MULTILINE};
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Clone)]
+pub struct TargetCompatibleWith {
+    target_triples: BTreeSet<String>,
+}
+
+impl TargetCompatibleWith {
+    pub fn new(target_triples: BTreeSet<String>) -> Self {
+        TargetCompatibleWith { target_triples }
+    }
+
+    pub fn serialize_starlark<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        // Output looks like:
+        //
+        //     select({
+        //         "configuration": [],
+        //         "//conditions:default": ["@platforms//:incompatible"],
+        //     })
+
+        let mut plus = serializer.serialize_tuple_struct("+", MULTILINE)?;
+
+        struct SelectInner<'a>(&'a BTreeSet<String>);
+
+        impl<'a> Serialize for SelectInner<'a> {
+            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+            where
+                S: Serializer,
+            {
+                let mut map = serializer.serialize_map(Some(MULTILINE))?;
+                for cfg in self.0 {
+                    map.serialize_entry(cfg, &[] as &[String])?;
+                }
+                map.serialize_entry(
+                    "//conditions:default",
+                    &["@platforms//:incompatible".to_owned()] as &[String],
+                )?;
+                map.end()
+            }
+        }
+
+        plus.serialize_field(&FunctionCall::new(
+            "select",
+            [SelectInner(&self.target_triples)],
+        ))?;
+
+        plus.end()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    use indoc::indoc;
+
+    #[test]
+    fn target_compatible_with() {
+        let target_compatible_with = TargetCompatibleWith::new(BTreeSet::from([
+            "@rules_rust//rust/platform:wasm32-unknown-unknown".to_owned(),
+            "@rules_rust//rust/platform:wasm32-wasi".to_owned(),
+            "@rules_rust//rust/platform:x86_64-apple-darwin".to_owned(),
+            "@rules_rust//rust/platform:x86_64-pc-windows-msvc".to_owned(),
+            "@rules_rust//rust/platform:x86_64-unknown-linux-gnu".to_owned(),
+        ]));
+
+        let expected_starlark = indoc! {r#"
+            select({
+                "@rules_rust//rust/platform:wasm32-unknown-unknown": [],
+                "@rules_rust//rust/platform:wasm32-wasi": [],
+                "@rules_rust//rust/platform:x86_64-apple-darwin": [],
+                "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [],
+                "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [],
+                "//conditions:default": ["@platforms//:incompatible"],
+            })
+        "#};
+
+        assert_eq!(
+            target_compatible_with
+                .serialize_starlark(serde_starlark::Serializer)
+                .unwrap(),
+            expected_starlark,
+        );
+    }
+}