//! Run bootstrap from any subdirectory of a rust compiler checkout. //! //! We prefer `exec`, to avoid adding an extra process in the process tree. //! However, since `exec` isn't available on Windows, we indirect through //! `exec_or_status`, which will call `exec` on unix and `status` on Windows. //! //! We use `powershell.exe x.ps1` on Windows, and `sh -c x` on Unix, those are //! the ones that call `x.py`. We use `sh -c` on Unix, because it is a standard. //! We also don't use `pwsh` on Windows, because it is not installed by default; use std::{ env::{self, consts::EXE_EXTENSION}, io, path::Path, process::{self, Command, ExitStatus}, }; const PYTHON: &str = "python"; const PYTHON2: &str = "python2"; const PYTHON3: &str = "python3"; fn python() -> &'static str { let val = match env::var_os("PATH") { Some(val) => val, None => return PYTHON, }; let mut python2 = false; let mut python3 = false; for dir in env::split_paths(&val) { // `python` should always take precedence over python2 / python3 if it exists if dir.join(PYTHON).with_extension(EXE_EXTENSION).exists() { return PYTHON; } python2 |= dir.join(PYTHON2).with_extension(EXE_EXTENSION).exists(); python3 |= dir.join(PYTHON3).with_extension(EXE_EXTENSION).exists(); } // try 3 before 2 if python3 { PYTHON3 } else if python2 { PYTHON2 } else { // Python was not found on path, so exit eprintln!("Unable to find python in your PATH. Please check it is installed."); process::exit(1); } } #[cfg(windows)] fn x_command(dir: &Path) -> Command { let mut cmd = Command::new("powershell.exe"); cmd.args([ "-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "RemoteSigned", "-Command", "./x.ps1", ]) .current_dir(dir); cmd } #[cfg(unix)] fn x_command(dir: &Path) -> Command { Command::new(dir.join("x")) } #[cfg(not(any(windows, unix)))] fn x_command(_dir: &Path) -> Command { compile_error!("Unsupported platform"); } #[cfg(unix)] fn exec_or_status(command: &mut Command) -> io::Result { use std::os::unix::process::CommandExt; Err(command.exec()) } #[cfg(not(unix))] fn exec_or_status(command: &mut Command) -> io::Result { command.status() } fn handle_result(result: io::Result, cmd: Command) { match result { Err(error) => { eprintln!("Failed to invoke `{:?}`: {}", cmd, error); } Ok(status) => { process::exit(status.code().unwrap_or(1)); } } } fn main() { match env::args().skip(1).next().as_deref() { Some("--wrapper-version") => { let version = env!("CARGO_PKG_VERSION"); println!("{}", version); return; } _ => {} } let current = match env::current_dir() { Ok(dir) => dir, Err(err) => { eprintln!("Failed to get current directory: {err}"); process::exit(1); } }; for dir in current.ancestors() { let candidate = dir.join("x.py"); if candidate.exists() { let shell_script_candidate = dir.join("x"); let mut cmd: Command; if shell_script_candidate.exists() { cmd = x_command(dir); cmd.args(env::args().skip(1)).current_dir(dir); } else { // For older checkouts that do not have the x shell script, default to python cmd = Command::new(python()); cmd.arg(&candidate).args(env::args().skip(1)).current_dir(dir); } let result = exec_or_status(&mut cmd); handle_result(result, cmd); } } eprintln!( "x.py not found. Please run inside of a checkout of `https://github.com/rust-lang/rust`." ); process::exit(1); }