summary refs log tree commit diff
diff options
context:
space:
mode:
authorCasey Rodarmor <casey@rodarmor.com>2020-11-01 19:43:51 -0800
committerCasey Rodarmor <casey@rodarmor.com>2020-11-03 19:40:02 -0800
commit5fc22f1431e209610040d92ba671966ab2781f0b (patch)
tree39e916153d6f038281aadfe1c01c6bc9dc7cc07c
parent4760b8fb886a3702ae11bfa7868d495b2675b5ed (diff)
downloadrust-5fc22f1431e209610040d92ba671966ab2781f0b.tar.gz
rust-5fc22f1431e209610040d92ba671966ab2781f0b.zip
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`
-rw-r--r--.gitignore1
-rw-r--r--Cargo.toml6
-rw-r--r--src/tools/x/Cargo.lock5
-rw-r--r--src/tools/x/Cargo.toml7
-rw-r--r--src/tools/x/README.md3
-rw-r--r--src/tools/x/src/main.rs92
6 files changed, 114 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 1c50d9b054d..5f7135e38d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@ __pycache__/
 /inst/
 /llvm/
 /mingw-build/
+/src/tools/x/target
 # Created by default with `src/ci/docker/run.sh`:
 /obj/
 /unicode-downloads
diff --git a/Cargo.toml b/Cargo.toml
index 02794d1028b..38fd72ace53 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,10 +29,16 @@ members = [
   "src/tools/unicode-table-generator",
   "src/tools/expand-yaml-anchors",
 ]
+
 exclude = [
   "build",
   # HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`.
   "obj",
+  # The `x` binary is a thin wrapper that calls `x.py`, which initializes
+  # submodules, before which workspace members cannot be invoked because
+  # not all `Cargo.toml` files are available, so we exclude the `x` binary,
+  # so it can be invoked before the current checkout is set up.
+  "src/tools/x",
 ]
 
 [profile.release.package.compiler_builtins]
diff --git a/src/tools/x/Cargo.lock b/src/tools/x/Cargo.lock
new file mode 100644
index 00000000000..723d6cb25ed
--- /dev/null
+++ b/src/tools/x/Cargo.lock
@@ -0,0 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "x"
+version = "0.1.0"
diff --git a/src/tools/x/Cargo.toml b/src/tools/x/Cargo.toml
new file mode 100644
index 00000000000..72c4948c617
--- /dev/null
+++ b/src/tools/x/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "x"
+version = "0.1.0"
+description = "Run x.py slightly more conveniently"
+authors = ["Casey Rodarmor <casey@rodarmor.com>"]
+edition = "2018"
+publish = false
diff --git a/src/tools/x/README.md b/src/tools/x/README.md
new file mode 100644
index 00000000000..3b3cf2847c2
--- /dev/null
+++ b/src/tools/x/README.md
@@ -0,0 +1,3 @@
+# x
+
+`x` invokes `x.py` from any subdirectory.
diff --git a/src/tools/x/src/main.rs b/src/tools/x/src/main.rs
new file mode 100644
index 00000000000..6c0311433d6
--- /dev/null
+++ b/src/tools/x/src/main.rs
@@ -0,0 +1,92 @@
+//! Run `x.py` 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 `python`, `python3`, or `python2` as the python interpreter to run
+//! `x.py`, in that order of preference.
+
+use std::{
+    env, io,
+    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) {
+        if dir.join(PYTHON).exists() {
+            return PYTHON;
+        }
+
+        python2 |= dir.join(PYTHON2).exists();
+        python3 |= dir.join(PYTHON3).exists();
+    }
+
+    if python3 {
+        PYTHON3
+    } else if python2 {
+        PYTHON2
+    } else {
+        PYTHON
+    }
+}
+
+#[cfg(unix)]
+fn exec_or_status(command: &mut Command) -> io::Result<ExitStatus> {
+    use std::os::unix::process::CommandExt;
+    Err(command.exec())
+}
+
+#[cfg(not(unix))]
+fn exec_or_status(command: &mut Command) -> io::Result<ExitStatus> {
+    command.status()
+}
+
+fn main() {
+    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 mut python = Command::new(python());
+
+            python.arg(&candidate).args(env::args().skip(1)).current_dir(dir);
+
+            let result = exec_or_status(&mut python);
+
+            match result {
+                Err(error) => {
+                    eprintln!("Failed to invoke `{}`: {}", candidate.display(), error);
+                }
+                Ok(status) => {
+                    process::exit(status.code().unwrap_or(1));
+                }
+            }
+        }
+    }
+
+    eprintln!(
+        "x.py not found. Please run inside of a checkout of `https://github.com/rust-lang/rust`."
+    );
+
+    process::exit(1);
+}