Skip to content

Commit 90d06dc

Browse files
Merge pull request #645 from eddyb/build-std
Build the stdlib from rust-src sources.
2 parents f636e7c + e8e8983 commit 90d06dc

File tree

2 files changed

+152
-72
lines changed

2 files changed

+152
-72
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
target
22
*.pyc
3+
/cache/
4+
/rust.git/

collector/src/bin/rustc-perf-collector/sysroot.rs

+150-72
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use anyhow::{anyhow, Context};
22
use chrono::{DateTime, Utc};
33
use collector::Sha;
4-
use std::ffi::OsStr;
54
use std::fmt;
65
use std::fs::{self, File};
76
use std::io::{BufReader, Read};
8-
use std::path::{Path, PathBuf};
7+
use std::path::PathBuf;
98
use tar::Archive;
109
use xz2::bufread::XzDecoder;
1110

@@ -40,10 +39,116 @@ impl Sysroot {
4039
};
4140

4241
download.get_and_extract(ModuleVariant::Rustc)?;
43-
download.get_and_extract(ModuleVariant::Std)?;
42+
// HACK(eddyb) commented out because we build our own stdlib
43+
// (see `fn build_std` below).
44+
// download.get_and_extract(ModuleVariant::Std)?;
4445
download.get_and_extract(ModuleVariant::Cargo)?;
46+
download.get_and_extract(ModuleVariant::RustSrc)?;
4547

46-
download.into_sysroot()
48+
let sysroot_dir = download.directory.join(&download.rust_sha);
49+
let sysroot = download.into_sysroot()?;
50+
51+
// FIXME(eddyb) remove this once we no longer need to
52+
// build our own stdlib (see `fn build_std` below).
53+
sysroot.build_std(sysroot_dir)?;
54+
55+
Ok(sysroot)
56+
}
57+
58+
/// Build `std`+`test`+`proc_macro` in a similar way to Cargo's `-Zbuild-std`
59+
/// feature, but only once, and move the resulting libraries into the sysroot.
60+
///
61+
/// We only need this until https://github.com/rust-lang/cargo/pull/8073
62+
/// reaches beta, because then `rust-lang/rust` builds will have that
63+
/// treatment. For now, we only have access to that Cargo change here,
64+
/// using the newly built Cargo.
65+
///
66+
/// For more background on why we need this, see this comment:
67+
/// https://github.com/rust-lang/rust/issues/69060#issuecomment-604928032
68+
/// (in short, Cargo used to include `rustc -vV` output, which contains
69+
/// the commit hash, into `-Cmetadata`, producing different `std`s,
70+
/// and making the perf runs incomparable, up to several % of difference).
71+
fn build_std(&self, sysroot_dir: PathBuf) -> anyhow::Result<()> {
72+
// Make sure everything below gets absolute directories.
73+
let sysroot_dir = sysroot_dir.canonicalize()?;
74+
75+
let sysroot_rustlib_dir = sysroot_dir.join("lib/rustlib");
76+
let rust_src_dir = sysroot_rustlib_dir.join("src/rust");
77+
78+
// HACK(eddyb) add a top-level `Cargo.toml` that has the necessary
79+
// `patch.crates-io` entries for `rustc-std-workspace-{core,alloc,std}`.
80+
// (maybe `rust-src` should include such a `Cargo.toml`?)
81+
fs::write(
82+
rust_src_dir.join("Cargo.toml"),
83+
"\
84+
[workspace]
85+
members = ['src/libtest']
86+
87+
[patch.crates-io]
88+
# See comments in `tools/rustc-std-workspace-core/README.md` for what's going on
89+
# here
90+
rustc-std-workspace-core = { path = 'src/tools/rustc-std-workspace-core' }
91+
rustc-std-workspace-alloc = { path = 'src/tools/rustc-std-workspace-alloc' }
92+
rustc-std-workspace-std = { path = 'src/tools/rustc-std-workspace-std' }
93+
",
94+
)?;
95+
96+
// HACK(eddyb) we need `std` to run the build scripts to build `std`.
97+
let vanilla_sysroot_dir = {
98+
let vanilla_download = SysrootDownload {
99+
directory: sysroot_dir.join("vanilla-sysroot"),
100+
rust_sha: self.sha.clone(),
101+
triple: self.triple.clone(),
102+
};
103+
vanilla_download.get_and_extract(ModuleVariant::Std)?;
104+
vanilla_download.directory.join(vanilla_download.rust_sha)
105+
};
106+
107+
let rustflags = format!(
108+
"--sysroot={sysroot} --remap-path-prefix={remap_from}={remap_to}",
109+
sysroot = vanilla_sysroot_dir.display(),
110+
remap_from = rust_src_dir.display(),
111+
remap_to = "/rustc/REDACTED_SHA_HASH/"
112+
);
113+
114+
// Run Cargo to produce `$local_build_target_dir/release/deps/lib*.rlib`.
115+
let local_build_target_dir = sysroot_dir.join("build-std-target");
116+
let cargo_status = std::process::Command::new(&self.cargo)
117+
.env("RUSTC", &self.rustc)
118+
.env("RUSTFLAGS", rustflags)
119+
.env("__CARGO_DEFAULT_LIB_METADATA", "rustc-perf-std")
120+
.args(&["build", "--release"])
121+
.arg("--target-dir")
122+
.arg(&local_build_target_dir)
123+
.args(&["--features", "panic-unwind", "--features", "backtrace"])
124+
.arg("--manifest-path")
125+
.arg(rust_src_dir.join("src/libtest/Cargo.toml"))
126+
.status()?;
127+
if !cargo_status.success() {
128+
return Err(anyhow!(
129+
"unable to build stdlib for {} triple {}",
130+
self.sha,
131+
self.triple
132+
));
133+
}
134+
135+
// Move all of the `rlib` files into the main sysroot.
136+
let sysroot_target_lib_dir = sysroot_rustlib_dir.join(&self.triple).join("lib");
137+
for entry in fs::read_dir(local_build_target_dir.join("release/deps"))? {
138+
let entry = entry?;
139+
let path = entry.path();
140+
if let (Some(name), Some(ext)) = (path.file_name(), path.extension()) {
141+
if ext == "rlib" {
142+
fs::rename(&path, sysroot_target_lib_dir.join(name))?;
143+
}
144+
}
145+
}
146+
147+
// Clean up, to avoid accidental usage of these directories.
148+
fs::remove_dir_all(vanilla_sysroot_dir)?;
149+
fs::remove_dir_all(local_build_target_dir)?;
150+
151+
Ok(())
47152
}
48153
}
49154

@@ -66,14 +171,15 @@ struct SysrootDownload {
66171
triple: String,
67172
}
68173

69-
const MODULE_URL: &str =
70-
"https://rust-lang-ci2.s3.amazonaws.com/rustc-builds/@SHA@/@MODULE@-nightly-@[email protected]";
174+
const BASE_URL: &str = "https://rust-lang-ci2.s3.amazonaws.com/rustc-builds";
71175

176+
// FIXME(eddyb) rename to just `Component`.
72177
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
73178
enum ModuleVariant {
74179
Cargo,
75180
Rustc,
76181
Std,
182+
RustSrc,
77183
}
78184

79185
impl fmt::Display for ModuleVariant {
@@ -82,47 +188,45 @@ impl fmt::Display for ModuleVariant {
82188
ModuleVariant::Cargo => write!(f, "cargo"),
83189
ModuleVariant::Rustc => write!(f, "rustc"),
84190
ModuleVariant::Std => write!(f, "rust-std"),
191+
ModuleVariant::RustSrc => write!(f, "rust-src"),
85192
}
86193
}
87194
}
88195

89196
impl ModuleVariant {
90197
fn url(&self, sysroot: &SysrootDownload, triple: &str) -> String {
91-
MODULE_URL
92-
.replace("@MODULE@", &self.to_string())
93-
.replace("@SHA@", &sysroot.rust_sha)
94-
.replace("@TRIPLE@", triple)
198+
let suffix = if *self == ModuleVariant::RustSrc {
199+
String::new()
200+
} else {
201+
format!("-{}", triple)
202+
};
203+
format!(
204+
"{base}/{sha}/{module}-nightly{suffix}.tar.xz",
205+
base = BASE_URL,
206+
module = self,
207+
sha = sysroot.rust_sha,
208+
suffix = suffix,
209+
)
95210
}
96211
}
97212

98213
impl SysrootDownload {
99214
fn into_sysroot(self) -> anyhow::Result<Sysroot> {
215+
let sysroot_bin_dir = self.directory.join(&self.rust_sha).join("bin");
216+
let sysroot_bin = |name| {
217+
let path = sysroot_bin_dir.join(name);
218+
path.canonicalize().with_context(|| {
219+
format!(
220+
"failed to canonicalize {} path for {}: {:?}",
221+
name, self.rust_sha, path
222+
)
223+
})
224+
};
225+
100226
Ok(Sysroot {
101-
rustc: self
102-
.directory
103-
.join(&self.rust_sha)
104-
.join("rustc/bin/rustc")
105-
.canonicalize()
106-
.with_context(|| {
107-
format!("failed to canonicalize rustc path for {}", self.rust_sha)
108-
})?,
109-
rustdoc: self
110-
.directory
111-
.join(&self.rust_sha)
112-
.join("rustc/bin/rustdoc")
113-
.canonicalize()
114-
.with_context(|| {
115-
format!("failed to canonicalize rustdoc path for {}", self.rust_sha)
116-
})?,
117-
cargo: {
118-
let path = self.directory.join(&self.rust_sha).join("cargo/bin/cargo");
119-
path.canonicalize().with_context(|| {
120-
format!(
121-
"failed to canonicalize cargo path for {}: {:?}",
122-
self.rust_sha, path
123-
)
124-
})?
125-
},
227+
rustc: sysroot_bin("rustc")?,
228+
rustdoc: sysroot_bin("rustdoc")?,
229+
cargo: sysroot_bin("cargo")?,
126230
sha: self.rust_sha,
127231
triple: self.triple,
128232
})
@@ -161,19 +265,21 @@ impl SysrootDownload {
161265
}
162266

163267
return Err(anyhow!(
164-
"unable to download sha {} triple {} module {}",
268+
"unable to download sha {} triple {} module {} from {}",
165269
self.rust_sha,
166270
self.triple,
167-
variant
271+
variant,
272+
url
168273
));
169274
}
170275

171276
fn extract<T: Read>(&self, variant: ModuleVariant, reader: T) -> anyhow::Result<()> {
172-
let is_std = variant == ModuleVariant::Std;
173277
let mut archive = Archive::new(reader);
174-
let std_prefix = format!("rust-std-{}/lib/rustlib", self.triple);
175-
176-
let mut to_link = Vec::new();
278+
let prefix = if variant == ModuleVariant::Std {
279+
format!("rust-std-{}", self.triple)
280+
} else {
281+
variant.to_string()
282+
};
177283

178284
let unpack_into = self.directory.join(&self.rust_sha);
179285

@@ -184,21 +290,11 @@ impl SysrootDownload {
184290
assert!(components.next().is_some(), "strip container directory");
185291
let path = components.as_path();
186292

187-
let path = if is_std {
188-
if let Ok(path) = path.strip_prefix(&std_prefix) {
189-
if path.extension() == Some(OsStr::new("dylib")) {
190-
to_link.push(path.to_owned());
191-
continue;
192-
} else {
193-
Path::new("rustc/lib/rustlib").join(path)
194-
}
195-
} else {
196-
continue;
197-
}
293+
let path = if let Ok(path) = path.strip_prefix(&prefix) {
294+
unpack_into.join(path)
198295
} else {
199-
path.into()
296+
continue;
200297
};
201-
let path = unpack_into.join(path);
202298
fs::create_dir_all(&path.parent().unwrap()).with_context(|| {
203299
format!(
204300
"could not create intermediate directories for {}",
@@ -208,24 +304,6 @@ impl SysrootDownload {
208304
entry.unpack(path)?;
209305
}
210306

211-
let link_dst_prefix = unpack_into.join(format!("rustc/lib/rustlib/{}/lib", self.triple));
212-
let link_src_prefix = format!("{}/lib", self.triple);
213-
for path in to_link {
214-
let src = unpack_into.join("rustc/lib").join(
215-
path.strip_prefix(&link_src_prefix)
216-
.with_context(|| format!("stripping prefix from: {:?}", path))?,
217-
);
218-
let dst = link_dst_prefix.join(&path);
219-
fs::create_dir_all(&dst.parent().unwrap()).with_context(|| {
220-
format!(
221-
"could not create intermediate directories for {}",
222-
dst.display()
223-
)
224-
})?;
225-
log::trace!("linking {} to {}", src.display(), dst.display());
226-
fs::hard_link(src, dst)?;
227-
}
228-
229307
Ok(())
230308
}
231309
}

0 commit comments

Comments
 (0)