1 //! Run `x.py` from any subdirectory of a rust compiler checkout.
3 //! We prefer `exec`, to avoid adding an extra process in the process tree.
4 //! However, since `exec` isn't available on Windows, we indirect through
5 //! `exec_or_status`, which will call `exec` on unix and `status` on Windows.
7 //! We use `python`, `python3`, or `python2` as the python interpreter to run
8 //! `x.py`, in that order of preference.
12 process::{self, Command, ExitStatus},
15 const PYTHON: &str = "python";
16 const PYTHON2: &str = "python2";
17 const PYTHON3: &str = "python3";
19 fn python() -> &'static str {
20 let val = match env::var_os("PATH") {
22 None => return PYTHON,
25 let mut python2 = false;
26 let mut python3 = false;
28 for dir in env::split_paths(&val) {
29 // `python` should always take precedence over python2 / python3 if it exists
30 if dir.join(PYTHON).exists() {
34 python2 |= dir.join(PYTHON2).exists();
35 python3 |= dir.join(PYTHON3).exists();
44 // We would have returned early if we found that python is installed ...
45 // maybe this should panic with an error instead?
51 fn exec_or_status(command: &mut Command) -> io::Result<ExitStatus> {
52 use std::os::unix::process::CommandExt;
57 fn exec_or_status(command: &mut Command) -> io::Result<ExitStatus> {
62 let current = match env::current_dir() {
65 eprintln!("Failed to get current directory: {err}");
70 for dir in current.ancestors() {
71 let candidate = dir.join("x.py");
73 if candidate.exists() {
74 let mut python = Command::new(python());
76 python.arg(&candidate).args(env::args().skip(1)).current_dir(dir);
78 let result = exec_or_status(&mut python);
82 eprintln!("Failed to invoke `{}`: {}", candidate.display(), error);
85 process::exit(status.code().unwrap_or(1));
92 "x.py not found. Please run inside of a checkout of `https://github.com/rust-lang/rust`."