Rollup merge of #78658 - casey:x, r=Mark-Simulacrum

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 run `x.py` from any subdirectory, and only need to type `x`.

It can be installed with `cargo install --path src/tools/x`

This is a copy of a [binary I've been using myself when working on rust](https://github.com/casey/bootstrap), currently published to crates.io as `bootstrap`.

It could be changed to avoid indirecting through `x.py`, and instead call the bootstrap module directly. However, this seemed like the simplest thing possible, and won't break if the details of how the bootstrap module is invoked change.
This commit is contained in:
Mara Bos 2020-11-08 13:36:09 +01:00 committed by GitHub
commit 7d9ad6d949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 0 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -29,11 +29,17 @@ members = [
"src/tools/unicode-table-generator",
"src/tools/expand-yaml-anchors",
]
exclude = [
"build",
"compiler/rustc_codegen_cranelift",
# 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]

5
src/tools/x/Cargo.lock Normal file
View File

@ -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
src/tools/x/Cargo.toml Normal file
View File

@ -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
src/tools/x/README.md Normal file
View File

@ -0,0 +1,3 @@
# x
`x` invokes `x.py` from any subdirectory.

92
src/tools/x/src/main.rs Normal file
View File

@ -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);
}