Skip to content

Commit

Permalink
Add a tool to run x.py from any subdirectory
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
casey committed Nov 4, 2020
1 parent 4760b8f commit 5fc22f1
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
5 changes: 5 additions & 0 deletions src/tools/x/Cargo.lock
Original file line number Diff line number Diff line change
@@ -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"
7 changes: 7 additions & 0 deletions src/tools/x/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions src/tools/x/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# x

`x` invokes `x.py` from any subdirectory.
92 changes: 92 additions & 0 deletions src/tools/x/src/main.rs
Original file line number Diff line number Diff line change
@@ -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);
}

3 comments on commit 5fc22f1

@thinkerou
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the name is x.py? thanks!

@casey
Copy link
Contributor Author

@casey casey commented on 5fc22f1 Feb 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure! I think perhaps it was just a short letter that wasn't taken by another command, but I wasn't around when it was added, so I can't say for sure.

@thinkerou
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@casey got it, thank you for your reply!

Please sign in to comment.