aboutsummaryrefslogtreecommitdiff
path: root/xtask/src/not_bash.rs
diff options
context:
space:
mode:
Diffstat (limited to 'xtask/src/not_bash.rs')
-rw-r--r--xtask/src/not_bash.rs165
1 files changed, 165 insertions, 0 deletions
diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs
new file mode 100644
index 000000000..3e30e7279
--- /dev/null
+++ b/xtask/src/not_bash.rs
@@ -0,0 +1,165 @@
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::{Path, PathBuf},
8 process::{Command, Stdio},
9};
10
11use anyhow::{bail, Context, Result};
12
13pub mod fs2 {
14 use std::{fs, path::Path};
15
16 use anyhow::{Context, Result};
17
18 pub fn read_dir<P: AsRef<Path>>(path: P) -> Result<fs::ReadDir> {
19 let path = path.as_ref();
20 fs::read_dir(path).with_context(|| format!("Failed to read {}", path.display()))
21 }
22
23 pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
24 let path = path.as_ref();
25 fs::write(path, contents).with_context(|| format!("Failed to write {}", path.display()))
26 }
27
28 pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<u64> {
29 let from = from.as_ref();
30 let to = to.as_ref();
31 fs::copy(from, to)
32 .with_context(|| format!("Failed to copy {} to {}", from.display(), to.display()))
33 }
34
35 pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
36 let path = path.as_ref();
37 fs::remove_file(path).with_context(|| format!("Failed to remove file {}", path.display()))
38 }
39
40 pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
41 let path = path.as_ref();
42 fs::remove_dir_all(path).with_context(|| format!("Failed to remove dir {}", path.display()))
43 }
44}
45
46macro_rules! _run {
47 ($($expr:expr),*) => {
48 run!($($expr),*; echo = true)
49 };
50 ($($expr:expr),* ; echo = $echo:expr) => {
51 $crate::not_bash::run_process(format!($($expr),*), $echo)
52 };
53}
54pub(crate) use _run as run;
55
56pub struct Pushd {
57 _p: (),
58}
59
60pub fn pushd(path: impl Into<PathBuf>) -> Pushd {
61 Env::with(|env| env.pushd(path.into()));
62 Pushd { _p: () }
63}
64
65impl Drop for Pushd {
66 fn drop(&mut self) {
67 Env::with(|env| env.popd())
68 }
69}
70
71pub fn rm(glob: &str) -> Result<()> {
72 let cwd = Env::with(|env| env.cwd());
73 ls(glob)?.into_iter().try_for_each(|it| fs::remove_file(cwd.join(it)))?;
74 Ok(())
75}
76
77pub fn rm_rf(path: impl AsRef<Path>) -> Result<()> {
78 let path = path.as_ref();
79 if path.is_file() {
80 fs2::remove_file(path)
81 } else {
82 fs2::remove_dir_all(path)
83 }
84}
85
86pub fn ls(glob: &str) -> Result<Vec<PathBuf>> {
87 let cwd = Env::with(|env| env.cwd());
88 let mut res = Vec::new();
89 for entry in fs::read_dir(&cwd)? {
90 let entry = entry?;
91 if matches(&entry.file_name(), glob) {
92 let path = entry.path();
93 let path = path.strip_prefix(&cwd).unwrap();
94 res.push(path.to_path_buf())
95 }
96 }
97 return Ok(res);
98
99 fn matches(file_name: &OsStr, glob: &str) -> bool {
100 assert!(glob.starts_with('*'));
101 file_name.to_string_lossy().ends_with(&glob[1..])
102 }
103}
104
105#[doc(hidden)]
106pub fn run_process(cmd: String, echo: bool) -> Result<String> {
107 run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd))
108}
109
110fn run_process_inner(cmd: &str, echo: bool) -> Result<String> {
111 let cwd = Env::with(|env| env.cwd());
112 let mut args = shelx(cmd);
113 let binary = args.remove(0);
114
115 if echo {
116 println!("> {}", cmd)
117 }
118
119 let output = Command::new(binary)
120 .args(args)
121 .current_dir(cwd)
122 .stdin(Stdio::null())
123 .stderr(Stdio::inherit())
124 .output()?;
125 let stdout = String::from_utf8(output.stdout)?;
126
127 if echo {
128 print!("{}", stdout)
129 }
130
131 if !output.status.success() {
132 bail!("{}", output.status)
133 }
134
135 Ok(stdout.trim().to_string())
136}
137
138// FIXME: some real shell lexing here
139fn shelx(cmd: &str) -> Vec<String> {
140 cmd.split_whitespace().map(|it| it.to_string()).collect()
141}
142
143#[derive(Default)]
144struct Env {
145 pushd_stack: Vec<PathBuf>,
146}
147
148impl Env {
149 fn with<F: FnOnce(&mut Env) -> T, T>(f: F) -> T {
150 thread_local! {
151 static ENV: RefCell<Env> = Default::default();
152 }
153 ENV.with(|it| f(&mut *it.borrow_mut()))
154 }
155
156 fn pushd(&mut self, dir: PathBuf) {
157 self.pushd_stack.push(dir)
158 }
159 fn popd(&mut self) {
160 self.pushd_stack.pop().unwrap();
161 }
162 fn cwd(&self) -> PathBuf {
163 self.pushd_stack.last().cloned().unwrap_or_else(|| env::current_dir().unwrap())
164 }
165}