Skip to content

Commit 5fc22f1

Browse files
committed
Add a tool to run x.py from any subdirectory
This adds a binary called `x` in `src/tools/x`. All it does is check the current directory and its ancestors for a file called `x.py`, and if it finds one, runs it. By installing x, you can easily `x.py` from any subdirectory. It can be installed globally with `cargo install --path src/tools/x`
1 parent 4760b8f commit 5fc22f1

File tree

6 files changed

+114
-0
lines changed

6 files changed

+114
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ __pycache__/
3131
/inst/
3232
/llvm/
3333
/mingw-build/
34+
/src/tools/x/target
3435
# Created by default with `src/ci/docker/run.sh`:
3536
/obj/
3637
/unicode-downloads

Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ members = [
2929
"src/tools/unicode-table-generator",
3030
"src/tools/expand-yaml-anchors",
3131
]
32+
3233
exclude = [
3334
"build",
3435
# HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`.
3536
"obj",
37+
# The `x` binary is a thin wrapper that calls `x.py`, which initializes
38+
# submodules, before which workspace members cannot be invoked because
39+
# not all `Cargo.toml` files are available, so we exclude the `x` binary,
40+
# so it can be invoked before the current checkout is set up.
41+
"src/tools/x",
3642
]
3743

3844
[profile.release.package.compiler_builtins]

src/tools/x/Cargo.lock

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# This file is automatically @generated by Cargo.
2+
# It is not intended for manual editing.
3+
[[package]]
4+
name = "x"
5+
version = "0.1.0"

src/tools/x/Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "x"
3+
version = "0.1.0"
4+
description = "Run x.py slightly more conveniently"
5+
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
6+
edition = "2018"
7+
publish = false

src/tools/x/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# x
2+
3+
`x` invokes `x.py` from any subdirectory.

src/tools/x/src/main.rs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//! Run `x.py` from any subdirectory of a rust compiler checkout.
2+
//!
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.
6+
//!
7+
//! We use `python`, `python3`, or `python2` as the python interpreter to run
8+
//! `x.py`, in that order of preference.
9+
10+
use std::{
11+
env, io,
12+
process::{self, Command, ExitStatus},
13+
};
14+
15+
const PYTHON: &str = "python";
16+
const PYTHON2: &str = "python2";
17+
const PYTHON3: &str = "python3";
18+
19+
fn python() -> &'static str {
20+
let val = match env::var_os("PATH") {
21+
Some(val) => val,
22+
None => return PYTHON,
23+
};
24+
25+
let mut python2 = false;
26+
let mut python3 = false;
27+
28+
for dir in env::split_paths(&val) {
29+
if dir.join(PYTHON).exists() {
30+
return PYTHON;
31+
}
32+
33+
python2 |= dir.join(PYTHON2).exists();
34+
python3 |= dir.join(PYTHON3).exists();
35+
}
36+
37+
if python3 {
38+
PYTHON3
39+
} else if python2 {
40+
PYTHON2
41+
} else {
42+
PYTHON
43+
}
44+
}
45+
46+
#[cfg(unix)]
47+
fn exec_or_status(command: &mut Command) -> io::Result<ExitStatus> {
48+
use std::os::unix::process::CommandExt;
49+
Err(command.exec())
50+
}
51+
52+
#[cfg(not(unix))]
53+
fn exec_or_status(command: &mut Command) -> io::Result<ExitStatus> {
54+
command.status()
55+
}
56+
57+
fn main() {
58+
let current = match env::current_dir() {
59+
Ok(dir) => dir,
60+
Err(err) => {
61+
eprintln!("Failed to get current directory: {}", err);
62+
process::exit(1);
63+
}
64+
};
65+
66+
for dir in current.ancestors() {
67+
let candidate = dir.join("x.py");
68+
69+
if candidate.exists() {
70+
let mut python = Command::new(python());
71+
72+
python.arg(&candidate).args(env::args().skip(1)).current_dir(dir);
73+
74+
let result = exec_or_status(&mut python);
75+
76+
match result {
77+
Err(error) => {
78+
eprintln!("Failed to invoke `{}`: {}", candidate.display(), error);
79+
}
80+
Ok(status) => {
81+
process::exit(status.code().unwrap_or(1));
82+
}
83+
}
84+
}
85+
}
86+
87+
eprintln!(
88+
"x.py not found. Please run inside of a checkout of `https://github.com/rust-lang/rust`."
89+
);
90+
91+
process::exit(1);
92+
}

0 commit comments

Comments
 (0)