Source code

Revision control

Copy as Markdown

Other Tools

Teaches Rust's build system to vendor `std`'s dependencies into the
`rust-src` component.
This was originally landed in <https://github.com/rust-lang/rust/pull/78790>,
and <https://github.com/rust-lang/cargo/pull/8834>, but was backed out for
causing breakage in other situations.
Since then, things have changed in cargo, such that it is now possible to use
a simpler approach that only relies on tweaking what is shipped in rust.
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 4957de2e1b7..b903e1be4aa 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -941,6 +941,101 @@ fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
&dst_src,
);
+ // cargo's -Z build-std doesn't work properly alongside vendoring.
+ // To work around the issue, we vendor the libstd dependencies in the
+ // rust-src package, and alter libstd's Cargo.toml to contain
+ // patch.crates-io entries pointing to the vendored versions.
+ let root_dir = dst_src.join("library");
+ let root_lock = root_dir.join("Cargo.lock");
+ if root_lock.exists() {
+ use std::collections::HashMap;
+
+ use toml::map::Map;
+ use toml::Value;
+
+ let dst_vendor = dst_src.join("vendor");
+
+ let mut cmd = command(&builder.initial_cargo);
+ cmd.arg("vendor").arg(&dst_vendor).current_dir(&root_dir);
+ // library/std/Cargo.toml uses the `public-dependency` nightly cargo feature.
+ cmd.env("RUSTC_BOOTSTRAP", "1");
+ builder.info("Dist src");
+ let _time = timeit(builder);
+ builder.run(
+ &mut cmd,
+ crate::utils::exec::OutputMode::Print,
+ crate::utils::exec::OutputMode::Print,
+ );
+
+ // Ideally, we would add the .cargo/config.toml that `cargo vendor` outputs,
+ // but cargo doesn't read it when building libstd. So instead, we add
+ // [patch.crates-io] entries to Cargo.toml for all vendored packages.
+ let cargo_toml_path = root_lock.with_extension("toml");
+ let cargo_toml_str = t!(std::fs::read_to_string(&cargo_toml_path));
+ let mut manifest: Value = t!(toml::from_str(&cargo_toml_str));
+
+ let patch_table = t!(manifest
+ .get_mut("patch")
+ .and_then(|p| p.as_table_mut())
+ .and_then(|p| p.get_mut("crates-io"))
+ .and_then(|c| c.as_table_mut())
+ .ok_or("[patch.crates-io] section not found"));
+
+ let mut packages_by_name: HashMap<String, Vec<String>> = HashMap::new();
+
+ for entry in builder.read_dir(&dst_vendor) {
+ let path = entry.path();
+
+ // Check for Cargo.toml
+ let crate_cargo_toml_path = path.join("Cargo.toml");
+ if !crate_cargo_toml_path.exists() {
+ continue;
+ }
+
+ // Parse package name from Cargo.toml
+ let crate_cargo_toml_str = t!(std::fs::read_to_string(&crate_cargo_toml_path));
+ let crate_cargo_toml: Value = t!(toml::from_str(&crate_cargo_toml_str));
+ let pkg_name = t!(crate_cargo_toml
+ .get("package")
+ .and_then(|p| p.get("name"))
+ .and_then(|n| n.as_str())
+ .ok_or("package.name not found"));
+ let dir_name = path.file_name().unwrap().to_string_lossy().into_owned();
+ packages_by_name
+ .entry(pkg_name.to_string())
+ .or_insert_with(Vec::new)
+ .push(dir_name);
+ }
+
+ for (pkg_name, dirs) in &packages_by_name {
+ for dir_name in dirs.iter() {
+ // What we want in the normal case:
+ // [patch.crates-io.crate]
+ // path = "../vendor/crate"
+ // When the directory name is different (usually when it contains a
+ // version) we want the following:
+ // [patch.crates-io.crate-version]
+ // package = "crate"
+ // path = "../vendor/crate-version"
+ let mut patch_value = Map::new();
+ let name = dir_name.replace('.', "_").replace('+', "_");
+ if &name != pkg_name {
+ patch_value.insert("package".to_string(), pkg_name.clone().into());
+ }
+ patch_value
+ .insert("path".to_string(), format!("../vendor/{}", dir_name).into());
+ patch_table.insert(name, patch_value.into());
+ }
+ }
+
+ // Cargo.toml may be a hardlink to the one in the repository, and
+ // we don't want to modify that, so break the hardlink first.
+ t!(std::fs::remove_file(&cargo_toml_path));
+
+ let new_cargo_toml = t!(toml::to_string_pretty(&manifest));
+ t!(std::fs::write(&cargo_toml_path, new_cargo_toml));
+ }
+
tarball.generate()
}
}