use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use super::build_sysroot::{BUILD_SYSROOT, ORIG_BUILD_SYSROOT, SYSROOT_RUSTC_VERSION, SYSROOT_SRC}; use super::path::{Dirs, RelPath}; use super::rustc_info::{get_default_sysroot, get_rustc_version}; use super::utils::{copy_dir_recursively, git_command, retry_spawn_and_wait, spawn_and_wait}; pub(crate) fn prepare(dirs: &Dirs) { RelPath::DOWNLOAD.ensure_fresh(dirs); spawn_and_wait(super::build_backend::CG_CLIF.fetch("cargo", "rustc", dirs)); prepare_sysroot(dirs); spawn_and_wait(super::build_sysroot::STANDARD_LIBRARY.fetch("cargo", "rustc", dirs)); spawn_and_wait(super::tests::LIBCORE_TESTS.fetch("cargo", "rustc", dirs)); super::tests::RAND_REPO.fetch(dirs); spawn_and_wait(super::tests::RAND.fetch("cargo", "rustc", dirs)); super::tests::REGEX_REPO.fetch(dirs); spawn_and_wait(super::tests::REGEX.fetch("cargo", "rustc", dirs)); super::tests::PORTABLE_SIMD_REPO.fetch(dirs); spawn_and_wait(super::tests::PORTABLE_SIMD.fetch("cargo", "rustc", dirs)); } fn prepare_sysroot(dirs: &Dirs) { let sysroot_src_orig = get_default_sysroot(Path::new("rustc")).join("lib/rustlib/src/rust"); assert!(sysroot_src_orig.exists()); eprintln!("[COPY] sysroot src"); // FIXME ensure builds error out or update the copy if any of the files copied here change BUILD_SYSROOT.ensure_fresh(dirs); copy_dir_recursively(&ORIG_BUILD_SYSROOT.to_path(dirs), &BUILD_SYSROOT.to_path(dirs)); fs::create_dir_all(SYSROOT_SRC.to_path(dirs).join("library")).unwrap(); copy_dir_recursively( &sysroot_src_orig.join("library"), &SYSROOT_SRC.to_path(dirs).join("library"), ); let rustc_version = get_rustc_version(Path::new("rustc")); fs::write(SYSROOT_RUSTC_VERSION.to_path(dirs), &rustc_version).unwrap(); eprintln!("[GIT] init"); init_git_repo(&SYSROOT_SRC.to_path(dirs)); apply_patches(dirs, "sysroot", &SYSROOT_SRC.to_path(dirs)); } pub(crate) struct GitRepo { url: GitRepoUrl, rev: &'static str, patch_name: &'static str, } enum GitRepoUrl { Github { user: &'static str, repo: &'static str }, } impl GitRepo { pub(crate) const fn github( user: &'static str, repo: &'static str, rev: &'static str, patch_name: &'static str, ) -> GitRepo { GitRepo { url: GitRepoUrl::Github { user, repo }, rev, patch_name } } pub(crate) const fn source_dir(&self) -> RelPath { match self.url { GitRepoUrl::Github { user: _, repo } => RelPath::DOWNLOAD.join(repo), } } pub(crate) fn fetch(&self, dirs: &Dirs) { match self.url { GitRepoUrl::Github { user, repo } => { clone_repo_shallow_github( dirs, &self.source_dir().to_path(dirs), user, repo, self.rev, ); } } apply_patches(dirs, self.patch_name, &self.source_dir().to_path(dirs)); } } #[allow(dead_code)] fn clone_repo(download_dir: &Path, repo: &str, rev: &str) { eprintln!("[CLONE] {}", repo); // Ignore exit code as the repo may already have been checked out git_command(None, "clone").arg(repo).arg(download_dir).spawn().unwrap().wait().unwrap(); let mut clean_cmd = git_command(download_dir, "checkout"); clean_cmd.arg("--").arg("."); spawn_and_wait(clean_cmd); let mut checkout_cmd = git_command(download_dir, "checkout"); checkout_cmd.arg("-q").arg(rev); spawn_and_wait(checkout_cmd); } fn clone_repo_shallow_github(dirs: &Dirs, download_dir: &Path, user: &str, repo: &str, rev: &str) { if cfg!(windows) { // Older windows doesn't have tar or curl by default. Fall back to using git. clone_repo(download_dir, &format!("https://github.com/{}/{}.git", user, repo), rev); return; } let archive_url = format!("https://github.com/{}/{}/archive/{}.tar.gz", user, repo, rev); let archive_file = RelPath::DOWNLOAD.to_path(dirs).join(format!("{}.tar.gz", rev)); let archive_dir = RelPath::DOWNLOAD.to_path(dirs).join(format!("{}-{}", repo, rev)); eprintln!("[DOWNLOAD] {}/{} from {}", user, repo, archive_url); // Remove previous results if they exists let _ = std::fs::remove_file(&archive_file); let _ = std::fs::remove_dir_all(&archive_dir); let _ = std::fs::remove_dir_all(&download_dir); // Download zip archive let mut download_cmd = Command::new("curl"); download_cmd .arg("--max-time") .arg("600") .arg("-y") .arg("30") .arg("-Y") .arg("10") .arg("--connect-timeout") .arg("30") .arg("--continue-at") .arg("-") .arg("--location") .arg("--output") .arg(&archive_file) .arg(archive_url); retry_spawn_and_wait(5, download_cmd); // Unpack tar archive let mut unpack_cmd = Command::new("tar"); unpack_cmd.arg("xf").arg(&archive_file).current_dir(RelPath::DOWNLOAD.to_path(dirs)); spawn_and_wait(unpack_cmd); // Rename unpacked dir to the expected name std::fs::rename(archive_dir, &download_dir).unwrap(); init_git_repo(&download_dir); // Cleanup std::fs::remove_file(archive_file).unwrap(); } fn init_git_repo(repo_dir: &Path) { let mut git_init_cmd = git_command(repo_dir, "init"); git_init_cmd.arg("-q"); spawn_and_wait(git_init_cmd); let mut git_add_cmd = git_command(repo_dir, "add"); git_add_cmd.arg("."); spawn_and_wait(git_add_cmd); let mut git_commit_cmd = git_command(repo_dir, "commit"); git_commit_cmd.arg("-m").arg("Initial commit").arg("-q"); spawn_and_wait(git_commit_cmd); } fn get_patches(dirs: &Dirs, crate_name: &str) -> Vec { let mut patches: Vec<_> = fs::read_dir(RelPath::PATCHES.to_path(dirs)) .unwrap() .map(|entry| entry.unwrap().path()) .filter(|path| path.extension() == Some(OsStr::new("patch"))) .filter(|path| { path.file_name() .unwrap() .to_str() .unwrap() .split_once("-") .unwrap() .1 .starts_with(crate_name) }) .collect(); patches.sort(); patches } fn apply_patches(dirs: &Dirs, crate_name: &str, target_dir: &Path) { if crate_name == "" { return; } for patch in get_patches(dirs, crate_name) { eprintln!( "[PATCH] {:?} <- {:?}", target_dir.file_name().unwrap(), patch.file_name().unwrap() ); let mut apply_patch_cmd = git_command(target_dir, "am"); apply_patch_cmd.arg(patch).arg("-q"); spawn_and_wait(apply_patch_cmd); } }