aboutsummaryrefslogtreecommitdiff
path: root/xtask/src/not_bash.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-02-14 16:14:25 +0000
committerGitHub <[email protected]>2020-02-14 16:14:25 +0000
commit94323c89963b7d78039625b8e643d79bc2f1f261 (patch)
tree65f028204494f3b4517e5c38f82b73a825e977f5 /xtask/src/not_bash.rs
parentd46b555e31d04feeb6310c52248bba7ff267c453 (diff)
parentf2e8dfc8203bfd7cc69f58149577e207f6636fd3 (diff)
Merge #3136
3136: Not bash r=matklad a=matklad More declarative installation Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'xtask/src/not_bash.rs')
-rw-r--r--xtask/src/not_bash.rs123
1 files changed, 123 insertions, 0 deletions
diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs
new file mode 100644
index 000000000..56d6c6c2d
--- /dev/null
+++ b/xtask/src/not_bash.rs
@@ -0,0 +1,123 @@
1//! A bad shell -- small cross platform module for writing glue code
2use std::{
3 cell::RefCell,
4 env,
5 ffi::OsStr,
6 fs,
7 path::PathBuf,
8 process::{Command, Stdio},
9};
10
11use anyhow::{bail, Context, Result};
12
13macro_rules! _run {
14 ($($expr:expr),*) => {
15 run!($($expr),*; echo = true)
16 };
17 ($($expr:expr),* ; echo = $echo:expr) => {
18 $crate::not_bash::run_process(format!($($expr),*), $echo)
19 };
20}
21pub(crate) use _run as run;
22
23pub struct Pushd {
24 _p: (),
25}
26
27pub fn pushd(path: impl Into<PathBuf>) -> Pushd {
28 Env::with(|env| env.pushd(path.into()));
29 Pushd { _p: () }
30}
31
32impl Drop for Pushd {
33 fn drop(&mut self) {
34 Env::with(|env| env.popd())
35 }
36}
37
38pub fn rm(glob: &str) -> Result<()> {
39 let cwd = Env::with(|env| env.cwd());
40 ls(glob)?.into_iter().try_for_each(|it| fs::remove_file(cwd.join(it)))?;
41 Ok(())
42}
43
44pub fn ls(glob: &str) -> Result<Vec<PathBuf>> {
45 let cwd = Env::with(|env| env.cwd());
46 let mut res = Vec::new();
47 for entry in fs::read_dir(&cwd)? {
48 let entry = entry?;
49 if matches(&entry.file_name(), glob) {
50 let path = entry.path();
51 let path = path.strip_prefix(&cwd).unwrap();
52 res.push(path.to_path_buf())
53 }
54 }
55 return Ok(res);
56
57 fn matches(file_name: &OsStr, glob: &str) -> bool {
58 assert!(glob.starts_with('*'));
59 file_name.to_string_lossy().ends_with(&glob[1..])
60 }
61}
62
63#[doc(hidden)]
64pub fn run_process(cmd: String, echo: bool) -> Result<String> {
65 run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd))
66}
67
68fn run_process_inner(cmd: &str, echo: bool) -> Result<String> {
69 let cwd = Env::with(|env| env.cwd());
70 let mut args = shelx(cmd);
71 let binary = args.remove(0);
72
73 if echo {
74 println!("> {}", cmd)
75 }
76
77 let output = Command::new(binary)
78 .args(args)
79 .current_dir(cwd)
80 .stdin(Stdio::null())
81 .stderr(Stdio::inherit())
82 .output()?;
83 let stdout = String::from_utf8(output.stdout)?;
84
85 if echo {
86 print!("{}", stdout)
87 }
88
89 if !output.status.success() {
90 bail!("{}", output.status)
91 }
92
93 Ok(stdout)
94}
95
96// FIXME: some real shell lexing here
97fn shelx(cmd: &str) -> Vec<String> {
98 cmd.split_whitespace().map(|it| it.to_string()).collect()
99}
100
101#[derive(Default)]
102struct Env {
103 pushd_stack: Vec<PathBuf>,
104}
105
106impl Env {
107 fn with<F: FnOnce(&mut Env) -> T, T>(f: F) -> T {
108 thread_local! {
109 static ENV: RefCell<Env> = Default::default();
110 }
111 ENV.with(|it| f(&mut *it.borrow_mut()))
112 }
113
114 fn pushd(&mut self, dir: PathBuf) {
115 self.pushd_stack.push(dir)
116 }
117 fn popd(&mut self) {
118 self.pushd_stack.pop().unwrap();
119 }
120 fn cwd(&self) -> PathBuf {
121 self.pushd_stack.last().cloned().unwrap_or_else(|| env::current_dir().unwrap())
122 }
123}