-
-
Notifications
You must be signed in to change notification settings - Fork 15k
Enable Cargo's new build-dir layout #155439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
210119b
676a208
ca74f29
6df137e
52ceaf7
de3fd29
d7a58fa
61f3e08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2666,8 +2666,8 @@ pub fn run_cargo( | |
| ) -> Vec<PathBuf> { | ||
| // `target_root_dir` looks like $dir/$target/release | ||
| let target_root_dir = stamp.path().parent().unwrap(); | ||
| // `target_deps_dir` looks like $dir/$target/release/deps | ||
| let target_deps_dir = target_root_dir.join("deps"); | ||
| // `target_build_dir` looks like $dir/$target/release/build | ||
| let target_build_dir = target_root_dir.join("build"); | ||
| // `host_root_dir` looks like $dir/release | ||
| let host_root_dir = target_root_dir | ||
| .parent() | ||
|
|
@@ -2738,7 +2738,7 @@ pub fn run_cargo( | |
|
|
||
| // If this was output in the `deps` dir then this is a precise file | ||
| // name (hash included) so we start tracking it. | ||
| if filename.starts_with(&target_deps_dir) { | ||
| if filename.starts_with(&target_build_dir) { | ||
| deps.push((filename.to_path_buf(), DependencyType::Target)); | ||
| continue; | ||
| } | ||
|
|
@@ -2773,11 +2773,18 @@ pub fn run_cargo( | |
|
|
||
| // Ok now we need to actually find all the files listed in `toplevel`. We've | ||
| // got a list of prefix/extensions and we basically just need to find the | ||
| // most recent file in the `deps` folder corresponding to each one. | ||
| let contents = target_deps_dir | ||
| // most recent file in the `build` folder corresponding to each one. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not yet read the code by myself. Just wonder why bootstrap are doing this? And if bootstrap needs to do it, it should be a feature in Cargo, at least a (perma-)unstable feature.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See the commit block around line 2374. Basically we need the non-uplifted name that contains the unique hash. The artifact notifications provide the uplifted name where possible. |
||
| // | ||
| // Cargo's build folder is structured as `build/<pkg>/<hash>/out/<artifacts>` so | ||
| // we need to traverse multiple directory layers to get to actual files. | ||
| let read_dir = |path: &Path| path.read_dir().ok().into_iter().flatten().filter_map(Result::ok); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. checks failed because if there are any lingering files/dirs in Update the original code to not panic if we call |
||
| let contents = target_build_dir | ||
| .read_dir() | ||
| .unwrap_or_else(|e| panic!("Couldn't read {}: {}", target_deps_dir.display(), e)) | ||
| .map(|e| t!(e)) | ||
| .unwrap_or_else(|e| panic!("Couldn't read {}: {}", target_build_dir.display(), e)) | ||
| .map(|e| e.unwrap()) | ||
| .flat_map(|e| read_dir(&e.path())) | ||
| .flat_map(|e| read_dir(&e.path())) | ||
| .flat_map(|e| read_dir(&e.path())) | ||
| .map(|e| (e.path(), e.file_name().into_string().unwrap(), t!(e.metadata()))) | ||
| .collect::<Vec<_>>(); | ||
| for (prefix, extension, expected_len) in toplevel { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| //! This module is explictly not `mod`ed as it's shared across multiple crates | ||
| //! like bootstrap and compiletest via `#[path]` moduled declarations. | ||
| //! It's important to keep this file isolated from the rest of build_helper so it can be compiled | ||
| //! without build_helper. | ||
|
|
||
| // Roughly match the `std::process::Command` API | ||
| #![allow(dead_code, unreachable_pub)] | ||
|
|
||
| use std::ffi::{OsStr, OsString}; | ||
| use std::io::Write; | ||
| use std::path::Path; | ||
| use std::process::{Command, CommandEnvs}; | ||
|
|
||
| use tempfile::NamedTempFile; | ||
|
|
||
| /// A wrapper around [`Command`] that adds support for arg files. | ||
| /// This is useful as we have some commands that can get very long and at times | ||
| /// hit the OS limit (usually Windows) | ||
| /// | ||
| /// This implementation is based off the `ProcessBuilder` implementation in Cargo | ||
| /// but simplified. | ||
| /// | ||
| /// NOTE: In most scenarios we want to avoid arg files as it makes debugging more complicated | ||
| /// so we try to avoid it if the command is not close to the OS limit. | ||
| #[derive(Debug)] | ||
| pub struct ArgFileCommand { | ||
| command: Command, | ||
| args: Vec<OsString>, | ||
| } | ||
|
|
||
| impl ArgFileCommand { | ||
| pub fn new<S: AsRef<OsStr>>(program: S) -> Self { | ||
| let command = Command::new(program); | ||
| Self { command, args: Vec::new() } | ||
| } | ||
| pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self { | ||
| self.args.push(arg.as_ref().to_os_string()); | ||
| self | ||
| } | ||
|
|
||
| pub fn args<I, S>(&mut self, args: I) -> &mut Self | ||
| where | ||
| I: IntoIterator<Item = S>, | ||
| S: AsRef<OsStr>, | ||
| { | ||
| self.args.extend(args.into_iter().map(|s| s.as_ref().to_os_string())); | ||
| self | ||
| } | ||
|
|
||
| pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self | ||
| where | ||
| K: AsRef<OsStr>, | ||
| V: AsRef<OsStr>, | ||
| { | ||
| self.command.env(key, val); | ||
| self | ||
| } | ||
|
|
||
| pub fn get_envs(&self) -> CommandEnvs<'_> { | ||
| self.command.get_envs() | ||
| } | ||
|
|
||
| pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self { | ||
| self.command.env_remove(key); | ||
| self | ||
| } | ||
|
|
||
| pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self { | ||
| self.command.current_dir(dir); | ||
| self | ||
| } | ||
|
|
||
| pub fn stdin(&mut self, stdin: std::process::Stdio) -> &mut Self { | ||
| self.command.stdin(stdin); | ||
| self | ||
| } | ||
|
|
||
| pub fn build(mut self) -> std::io::Result<(Command, Option<NamedTempFile>)> { | ||
| // On Windows there is a hard limit of ~32KB, so we cut off at 30KB to | ||
| // give some buffer just incase. | ||
| #[cfg(windows)] | ||
| let threshold: usize = 30 * 1024; | ||
| // On unix the limit is defined by ARG_MAX. If its not explicitly set we set it to 1MB | ||
| // which is fairly large but lower than the ~2MB that it defaults to on most systems. | ||
| #[cfg(unix)] | ||
| let threshold: usize = | ||
| std::env::var("ARG_MAX").ok().and_then(|v| v.parse().ok()).unwrap_or(1024 * 1024); | ||
|
|
||
| let total_arg_len: usize = self.args.iter().map(|a| a.len() + 1).sum(); | ||
| if total_arg_len <= threshold { | ||
| self.command.args(self.args); | ||
| return Ok((self.command, None)); | ||
| } | ||
|
|
||
| let mut tmp = tempfile::Builder::new().prefix("bootstrap-argfile.").tempfile()?; | ||
|
|
||
| let mut arg = OsString::from("@"); | ||
| arg.push(tmp.path()); | ||
| self.command.arg(arg); | ||
|
|
||
| let mut buf = Vec::with_capacity(total_arg_len); | ||
| for arg in &self.args { | ||
| let arg = arg.to_str().ok_or_else(|| { | ||
| std::io::Error::other(format!( | ||
| "argument for argfile contains invalid UTF-8 characters: `{}`", | ||
| arg.to_string_lossy() | ||
| )) | ||
| })?; | ||
| if arg.contains('\n') { | ||
| return Err(std::io::Error::other(format!( | ||
| "argument for argfile contains newlines: `{arg}`" | ||
| ))); | ||
| } | ||
| writeln!(buf, "{arg}")?; | ||
| } | ||
| tmp.write_all(&buf)?; | ||
| tmp.flush()?; | ||
|
|
||
| Ok((self.command, Some(tmp))) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds ~10 new dependencies to bootstrap, which is a bit unfortunate, we have been trying to avoid depending on
tempfilebefore. But for therustc.rsusage we'd have to pre-generate a temporary directory in the build dir and let it store the argfile there, that is annoying, because Cargo could invoke multiple rustc instances in parallel.Let's keep it, but it makes me a bit grouchy 😆