| use std::ffi::OsStr; | |
| use std::fs; | |
| use std::path::{Path, PathBuf}; | |
| use std::process::Output; | |
| use crate::rustc_info::get_rustc_path; | |
| use crate::utils::{ | |
| cargo_install, create_dir, get_sysroot_dir, git_clone_root_dir, remove_file, run_command, | |
| run_command_with_output, walk_dir, | |
| }; | |
| // This is needed on systems where nothing is configured. | |
| // git really needs something here, or it will fail. | |
| // Even using --author is not enough. | |
| const GIT_CMD: [&dyn AsRef<OsStr>; 9] = [ | |
| &"git", | |
| &"-c", | |
| &"user.name=None", | |
| &"-c", | |
| &"user.email=none@example.com", | |
| &"-c", | |
| &"core.autocrlf=false", | |
| &"-c", | |
| &"commit.gpgSign=false", | |
| ]; | |
| fn run_git_command( | |
| command: &dyn AsRef<OsStr>, | |
| input: &[&dyn AsRef<OsStr>], | |
| cwd: Option<&Path>, | |
| ) -> Result<Output, String> { | |
| let git_cmd = | |
| &GIT_CMD.into_iter().chain([command]).chain(input.iter().cloned()).collect::<Vec<_>>()[..]; | |
| run_command(git_cmd, cwd) | |
| } | |
| fn run_git_command_with_output( | |
| command: &dyn AsRef<OsStr>, | |
| input: &[&dyn AsRef<OsStr>], | |
| cwd: Option<&Path>, | |
| ) -> Result<(), String> { | |
| let git_cmd = | |
| &GIT_CMD.into_iter().chain([command]).chain(input.iter().cloned()).collect::<Vec<_>>()[..]; | |
| run_command_with_output(git_cmd, cwd) | |
| } | |
| fn prepare_libcore( | |
| sysroot_path: &Path, | |
| libgccjit12_patches: bool, | |
| cross_compile: bool, | |
| sysroot_source: Option<String>, | |
| ) -> Result<(), String> { | |
| let rustlib_dir: PathBuf; | |
| if let Some(path) = sysroot_source { | |
| rustlib_dir = Path::new(&path) | |
| .canonicalize() | |
| .map_err(|error| format!("Failed to canonicalize path: {error:?}"))?; | |
| if !rustlib_dir.is_dir() { | |
| return Err(format!("Custom sysroot path {rustlib_dir:?} not found")); | |
| } | |
| } else { | |
| let rustc_path = match get_rustc_path() { | |
| Some(path) => path, | |
| None => return Err("`rustc` path not found".to_string()), | |
| }; | |
| let parent = match rustc_path.parent() { | |
| Some(path) => path, | |
| None => return Err(format!("No parent for `{}`", rustc_path.display())), | |
| }; | |
| rustlib_dir = parent | |
| .join("../lib/rustlib/src/rust") | |
| .canonicalize() | |
| .map_err(|error| format!("Failed to canonicalize path: {error:?}"))?; | |
| if !rustlib_dir.is_dir() { | |
| return Err("Please install `rust-src` component".to_string()); | |
| } | |
| } | |
| let sysroot_dir = sysroot_path.join("sysroot_src"); | |
| if sysroot_dir.is_dir() | |
| && let Err(error) = fs::remove_dir_all(&sysroot_dir) | |
| { | |
| return Err(format!("Failed to remove `{}`: {:?}", sysroot_dir.display(), error,)); | |
| } | |
| let sysroot_library_dir = sysroot_dir.join("library"); | |
| create_dir(&sysroot_library_dir)?; | |
| run_command(&[&"cp", &"-r", &rustlib_dir.join("library"), &sysroot_dir], None)?; | |
| println!("[GIT] init (cwd): `{}`", sysroot_dir.display()); | |
| run_git_command(&"init", &[], Some(&sysroot_dir))?; | |
| println!("[GIT] add (cwd): `{}`", sysroot_dir.display()); | |
| run_git_command(&"add", &[&"."], Some(&sysroot_dir))?; | |
| println!("[GIT] commit (cwd): `{}`", sysroot_dir.display()); | |
| run_git_command(&"commit", &[&"-m", &"Initial commit", &"-q"], Some(&sysroot_dir))?; | |
| let mut patches = Vec::new(); | |
| walk_dir( | |
| "patches", | |
| &mut |_| Ok(()), | |
| &mut |file_path: &Path| { | |
| patches.push(file_path.to_path_buf()); | |
| Ok(()) | |
| }, | |
| false, | |
| )?; | |
| if cross_compile { | |
| walk_dir( | |
| "patches/cross_patches", | |
| &mut |_| Ok(()), | |
| &mut |file_path: &Path| { | |
| patches.push(file_path.to_path_buf()); | |
| Ok(()) | |
| }, | |
| false, | |
| )?; | |
| } | |
| if libgccjit12_patches { | |
| walk_dir( | |
| "patches/libgccjit12", | |
| &mut |_| Ok(()), | |
| &mut |file_path: &Path| { | |
| patches.push(file_path.to_path_buf()); | |
| Ok(()) | |
| }, | |
| false, | |
| )?; | |
| } | |
| patches.sort(); | |
| for file_path in patches { | |
| println!("[GIT] apply `{}`", file_path.display()); | |
| let path = Path::new("../../..").join(file_path); | |
| run_git_command_with_output(&"apply", &[&path], Some(&sysroot_dir))?; | |
| run_git_command_with_output(&"add", &[&"-A"], Some(&sysroot_dir))?; | |
| run_git_command_with_output( | |
| &"commit", | |
| &[&"-m", &format!("Patch {}", path.display())], | |
| Some(&sysroot_dir), | |
| )?; | |
| } | |
| println!("Successfully prepared libcore for building"); | |
| Ok(()) | |
| } | |
| // FIXME: remove when we can ignore warnings in rustdoc tests. | |
| fn prepare_rand() -> Result<(), String> { | |
| // Apply patch for the rand crate. | |
| let file_path = "patches/crates/0001-Remove-deny-warnings.patch"; | |
| let rand_dir = Path::new("build/rand"); | |
| println!("[GIT] apply `{file_path}`"); | |
| let path = Path::new("../..").join(file_path); | |
| run_git_command_with_output(&"apply", &[&path], Some(rand_dir))?; | |
| run_git_command_with_output(&"add", &[&"-A"], Some(rand_dir))?; | |
| run_git_command_with_output( | |
| &"commit", | |
| &[&"-m", &format!("Patch {}", path.display())], | |
| Some(rand_dir), | |
| )?; | |
| Ok(()) | |
| } | |
| // 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() { | |
| remove_file(&mv_target)?; | |
| } | |
| run_command(&[&"mv", &"target/debug/main", &"raytracer_cg_llvm"], Some(repo_dir))?; | |
| Ok(()) | |
| } | |
| fn clone_and_setup<F>(repo_url: &str, checkout_commit: &str, extra: Option<F>) -> Result<(), String> | |
| where | |
| F: Fn(&Path) -> Result<(), String>, | |
| { | |
| let clone_result = git_clone_root_dir(repo_url, Path::new(crate::BUILD_DIR), false)?; | |
| if !clone_result.ran_clone { | |
| println!("`{}` has already been cloned", clone_result.repo_name); | |
| } | |
| let repo_path = Path::new(crate::BUILD_DIR).join(&clone_result.repo_name); | |
| run_git_command(&"checkout", &[&"--", &"."], Some(&repo_path))?; | |
| run_git_command(&"checkout", &[&checkout_commit], Some(&repo_path))?; | |
| if let Some(extra) = extra { | |
| extra(&repo_path)?; | |
| } | |
| Ok(()) | |
| } | |
| struct PrepareArg { | |
| cross_compile: bool, | |
| only_libcore: bool, | |
| libgccjit12_patches: bool, | |
| sysroot_source: Option<String>, | |
| } | |
| impl PrepareArg { | |
| fn new() -> Result<Option<Self>, String> { | |
| let mut only_libcore = false; | |
| let mut cross_compile = false; | |
| let mut libgccjit12_patches = false; | |
| let mut sysroot_source = None; | |
| let mut args = std::env::args().skip(2); | |
| while let Some(arg) = args.next() { | |
| match arg.as_str() { | |
| "--only-libcore" => only_libcore = true, | |
| "--cross" => cross_compile = true, | |
| "--libgccjit12-patches" => libgccjit12_patches = true, | |
| "--sysroot-source" => { | |
| if let Some(path) = args.next() { | |
| sysroot_source = Some(path); | |
| } else { | |
| return Err( | |
| "Expected a value after `--sysroot-source`, found nothing".to_string() | |
| ); | |
| } | |
| } | |
| "--help" => { | |
| Self::usage(); | |
| return Ok(None); | |
| } | |
| a => return Err(format!("Unknown argument `{a}`")), | |
| } | |
| } | |
| Ok(Some(Self { cross_compile, only_libcore, libgccjit12_patches, sysroot_source })) | |
| } | |
| fn usage() { | |
| println!( | |
| r#" | |
| `prepare` command help: | |
| --only-libcore : Only setup libcore and don't clone other repositories | |
| --cross : Apply the patches needed to do cross-compilation | |
| --libgccjit12-patches : Apply patches needed for libgccjit12 | |
| --sysroot-source : Specify custom path for sysroot source | |
| --help : Show this help"# | |
| ) | |
| } | |
| } | |
| pub fn run() -> Result<(), String> { | |
| let args = match PrepareArg::new()? { | |
| Some(a) => a, | |
| None => return Ok(()), | |
| }; | |
| let sysroot_path = get_sysroot_dir(); | |
| prepare_libcore( | |
| &sysroot_path, | |
| args.libgccjit12_patches, | |
| args.cross_compile, | |
| args.sysroot_source, | |
| )?; | |
| if !args.only_libcore { | |
| cargo_install("hyperfine")?; | |
| let to_clone = &[ | |
| ( | |
| "https://github.com/rust-random/rand.git", | |
| "1f4507a8e1cf8050e4ceef95eeda8f64645b6719", | |
| None, | |
| ), | |
| ( | |
| "https://github.com/rust-lang/regex.git", | |
| "341f207c1071f7290e3f228c710817c280c8dca1", | |
| None, | |
| ), | |
| ( | |
| "https://github.com/ebobby/simple-raytracer", | |
| "804a7a21b9e673a482797aa289a18ed480e4d813", | |
| Some(build_raytracer), | |
| ), | |
| ]; | |
| for (repo_url, checkout_commit, cb) in to_clone { | |
| clone_and_setup(repo_url, checkout_commit, *cb)?; | |
| } | |
| prepare_rand()?; | |
| } | |
| println!("Successfully ran `prepare`"); | |
| Ok(()) | |
| } |