diff --git a/build_system/src/prepare.rs b/build_system/src/prepare.rs index 8e6183be629..9c31b5cb8b3 100644 --- a/build_system/src/prepare.rs +++ b/build_system/src/prepare.rs @@ -4,7 +4,7 @@ use std::fs; use std::path::Path; -fn prepare_libcore() -> Result<(), String> { +fn prepare_libcore(sysroot_path: &Path) -> Result<(), String> { let rustc_path = match get_rustc_path() { Some(path) => path, None => return Err("`rustc` path not found".to_owned()), @@ -15,14 +15,18 @@ fn prepare_libcore() -> Result<(), String> { None => return Err(format!("No parent for `{}`", rustc_path.display())), }; - let rustlib_dir = parent.join("../lib/rustlib/src/rust"); + let rustlib_dir = + parent + .join("../lib/rustlib/src/rust") + .canonicalize() + .map_err(|e| format!("Failed to canonicalize path: {e:?}"))?; if !rustlib_dir.is_dir() { return Err("Please install `rust-src` component".to_owned()); } - let sysroot_dir = Path::new("build_sysroot/sysroot_src"); + let sysroot_dir = sysroot_path.join("sysroot_src"); if sysroot_dir.is_dir() { - if let Err(e) = fs::remove_dir_all(sysroot_dir) { + if let Err(e) = fs::remove_dir_all(&sysroot_dir) { return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), e)); } } @@ -34,12 +38,12 @@ fn prepare_libcore() -> Result<(), String> { sysroot_library_dir.display(), ))?; - run_command(&[&"cp", &"-r", &rustlib_dir, &sysroot_library_dir], None)?; + run_command(&[&"cp", &"-r", &rustlib_dir.join("library"), &sysroot_dir], None)?; println!("[GIT] init (cwd): `{}`", sysroot_dir.display()); - run_command_with_output(&[&"git", &"init"], Some(&sysroot_dir))?; + run_command(&[&"git", &"init"], Some(&sysroot_dir))?; println!("[GIT] add (cwd): `{}`", sysroot_dir.display()); - run_command_with_output(&[&"git", &"add", &"."], Some(&sysroot_dir))?; + run_command(&[&"git", &"add", &"."], Some(&sysroot_dir))?; println!("[GIT] commit (cwd): `{}`", sysroot_dir.display()); // This is needed on systems where nothing is configured. @@ -47,11 +51,17 @@ fn prepare_libcore() -> Result<(), String> { // Even using --author is not enough. run_command(&[&"git", &"config", &"user.email", &"none@example.com"], Some(&sysroot_dir))?; run_command(&[&"git", &"config", &"user.name", &"None"], Some(&sysroot_dir))?; - run_command(&[&"git", &"config", &"core.autocrlf=false"], Some(&sysroot_dir))?; - run_command(&[&"git", &"config", &"commit.gpgSign=false"], Some(&sysroot_dir))?; + run_command(&[&"git", &"config", &"core.autocrlf", &"false"], Some(&sysroot_dir))?; + run_command(&[&"git", &"config", &"commit.gpgSign", &"false"], Some(&sysroot_dir))?; run_command(&[&"git", &"commit", &"-m", &"Initial commit", &"-q"], Some(&sysroot_dir))?; + let mut patches = Vec::new(); walk_dir("patches", |_| Ok(()), |file_path: &Path| { + patches.push(file_path.to_path_buf()); + Ok(()) + })?; + patches.sort(); + for file_path in patches { println!("[GIT] apply `{}`", file_path.display()); let path = Path::new("../..").join(file_path); run_command_with_output(&[&"git", &"apply", &path], Some(&sysroot_dir))?; @@ -60,8 +70,7 @@ fn prepare_libcore() -> Result<(), String> { &[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())], Some(&sysroot_dir), )?; - Ok(()) - })?; + } println!("Successfully prepared libcore for building"); Ok(()) } @@ -69,6 +78,11 @@ fn prepare_libcore() -> Result<(), String> { // build with cg_llvm for perf comparison fn build_raytracer(repo_dir: &Path) -> Result<(), String> { run_command(&[&"cargo", &"build"], Some(repo_dir))?; + let mv_target = repo_dir.join("raytracer_cg_llvm"); + if mv_target.is_file() { + std::fs::remove_file(&mv_target) + .map_err(|e| format!("Failed to remove file `{}`: {e:?}", mv_target.display()))?; + } run_command(&[&"mv", &"target/debug/main", &"raytracer_cg_llvm"], Some(repo_dir))?; Ok(()) } @@ -82,18 +96,21 @@ fn clone_and_setup(repo_url: &str, checkout_commit: &str, extra: Option) - println!("`{}` has already been cloned", clone_result.repo_name); } let repo_path = Path::new(&clone_result.repo_name); - run_command(&[&"git", &"checkout", &"--", &"."], Some(repo_path))?; - run_command(&[&"git", &"checkout", &checkout_commit], Some(repo_path))?; + run_command(&[&"git", &"checkout", &"--", &"."], Some(&repo_path))?; + run_command(&[&"git", &"checkout", &checkout_commit], Some(&repo_path))?; let filter = format!("-{}-", clone_result.repo_name); walk_dir("crate_patches", |_| Ok(()), |file_path| { let s = file_path.as_os_str().to_str().unwrap(); if s.contains(&filter) && s.ends_with(".patch") { - run_command(&[&"git", &"am", &s], Some(repo_path))?; + run_command_with_output( + &[&"git", &"am", &file_path.canonicalize().unwrap()], + Some(&repo_path), + )?; } Ok(()) })?; if let Some(extra) = extra { - extra(repo_path)?; + extra(&repo_path)?; } Ok(()) } @@ -136,7 +153,8 @@ pub fn run() -> Result<(), String> { Some(a) => a, None => return Ok(()), }; - prepare_libcore()?; + let sysroot_path = Path::new("build_sysroot"); + prepare_libcore(sysroot_path)?; if !args.only_libcore { cargo_install("hyperfine")?; diff --git a/build_system/src/utils.rs b/build_system/src/utils.rs index 145f40ec8ae..c350864dbd2 100644 --- a/build_system/src/utils.rs +++ b/build_system/src/utils.rs @@ -1,9 +1,10 @@ use std::ffi::OsStr; +use std::fmt::Debug; use std::fs; use std::path::Path; -use std::process::{Command, Output}; +use std::process::{Command, ExitStatus, Output}; -fn run_command_inner(input: &[&dyn AsRef], cwd: Option<&Path>) -> Command { +fn get_command_inner(input: &[&dyn AsRef], cwd: Option<&Path>) -> Command { let (cmd, args) = match input { [] => panic!("empty command"), [cmd, args @ ..] => (cmd, args), @@ -16,44 +17,67 @@ fn run_command_inner(input: &[&dyn AsRef], cwd: Option<&Path>) -> Command command } -pub fn run_command(input: &[&dyn AsRef], cwd: Option<&Path>) -> Result { - run_command_inner(input, cwd).output() - .map_err(|e| format!( - "Command `{}` failed to run: {e:?}", +fn check_exit_status( + input: &[&dyn AsRef], + cwd: Option<&Path>, + exit_status: ExitStatus, +) -> Result<(), String> { + if exit_status.success() { + Ok(()) + } else { + Err(format!( + "Command `{}`{} exited with status {:?}", input.iter() .map(|s| s.as_ref().to_str().unwrap()) .collect::>() .join(" "), + cwd.map(|cwd| format!(" (running in folder `{}`)", cwd.display())) + .unwrap_or_default(), + exit_status.code(), )) + } +} + +fn command_error(input: &[&dyn AsRef], cwd: &Option<&Path>, error: D) -> String { + format!( + "Command `{}`{} failed to run: {error:?}", + input.iter() + .map(|s| s.as_ref().to_str().unwrap()) + .collect::>() + .join(" "), + cwd.as_ref() + .map(|cwd| format!( + " (running in folder `{}`)", + cwd.display(), + )) + .unwrap_or_default(), + ) +} + +pub fn run_command(input: &[&dyn AsRef], cwd: Option<&Path>) -> Result { + let output = get_command_inner(input, cwd) + .output() + .map_err(|e| command_error(input, &cwd, e))?; + check_exit_status(input, cwd, output.status)?; + Ok(output) } pub fn run_command_with_output( input: &[&dyn AsRef], cwd: Option<&Path>, ) -> Result<(), String> { - run_command_inner(input, cwd).spawn() - .map_err(|e| format!( - "Command `{}` failed to run: {e:?}", - input.iter() - .map(|s| s.as_ref().to_str().unwrap()) - .collect::>() - .join(" "), - ))? + let exit_status = get_command_inner(input, cwd).spawn() + .map_err(|e| command_error(input, &cwd, e))? .wait() - .map_err(|e| format!( - "Failed to wait for command `{}` to run: {e:?}", - input.iter() - .map(|s| s.as_ref().to_str().unwrap()) - .collect::>() - .join(" "), - ))?; + .map_err(|e| command_error(input, &cwd, e))?; + check_exit_status(input, cwd, exit_status)?; Ok(()) } pub fn cargo_install(to_install: &str) -> Result<(), String> { let output = run_command(&[&"cargo", &"install", &"--list"], None)?; - let to_install = format!("{to_install} "); + let to_install_needle = format!("{to_install} "); // cargo install --list returns something like this: // // mdbook-toc v0.8.0: @@ -65,11 +89,14 @@ pub fn cargo_install(to_install: &str) -> Result<(), String> { if String::from_utf8(output.stdout) .unwrap() .lines() - .any(|line| line.ends_with(':') && line.starts_with(&to_install)) + .any(|line| line.ends_with(':') && line.starts_with(&to_install_needle)) { return Ok(()); } - run_command(&[&"cargo", &"install", &to_install], None)?; + // We voluntarily ignore this error. + if run_command_with_output(&[&"cargo", &"install", &to_install], None).is_err() { + println!("Skipping installation of `{to_install}`"); + } Ok(()) } @@ -85,20 +112,22 @@ pub fn git_clone(to_clone: &str, dest: Option<&Path>) -> Result repo_name.to_owned(), }; - let dest = dest.unwrap_or_else(|| Path::new(&repo_name)); + let dest = dest + .map(|dest| dest.join(&repo_name)) + .unwrap_or_else(|| Path::new(&repo_name).into()); if dest.is_dir() { return Ok(CloneResult { ran_clone: false, repo_name }); } - run_command(&[&"git", &"clone", &to_clone, &dest], None)?; + run_command_with_output(&[&"git", &"clone", &to_clone, &dest], None)?; Ok(CloneResult { ran_clone: true, repo_name }) } -pub fn walk_dir(dir: P, dir_cb: D, file_cb: F) -> Result<(), String> +pub fn walk_dir(dir: P, mut dir_cb: D, mut file_cb: F) -> Result<(), String> where P: AsRef, - D: Fn(&Path) -> Result<(), String>, - F: Fn(&Path) -> Result<(), String>, + D: FnMut(&Path) -> Result<(), String>, + F: FnMut(&Path) -> Result<(), String>, { let dir = dir.as_ref(); for entry in fs::read_dir(dir).map_err(|e| format!("Failed to read dir `{}`: {e:?}", dir.display()))? {