aboutsummaryrefslogtreecommitdiff
path: root/crates/proc_macro_api/src/process.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-08-13 11:39:57 +0100
committerGitHub <[email protected]>2020-08-13 11:39:57 +0100
commitc2b1503fe41e6912b45d9ccf15e3c5ae6aad9439 (patch)
tree977254cd7b4fa53da65836cd0f119ef404e7dd62 /crates/proc_macro_api/src/process.rs
parent4abdf323af5bc693f8b9ff3455e19ee1dff572a8 (diff)
parent2119dc23e80d77f1abc789e3d99c34d429e17905 (diff)
Merge #5745
5745: Rename ra_proc_macro -> proc_macro_api r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/proc_macro_api/src/process.rs')
-rw-r--r--crates/proc_macro_api/src/process.rs201
1 files changed, 201 insertions, 0 deletions
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
new file mode 100644
index 000000000..51ffcaa78
--- /dev/null
+++ b/crates/proc_macro_api/src/process.rs
@@ -0,0 +1,201 @@
1//! Handle process life-time and message passing for proc-macro client
2
3use std::{
4 convert::{TryFrom, TryInto},
5 ffi::{OsStr, OsString},
6 io::{self, BufRead, BufReader, Write},
7 path::{Path, PathBuf},
8 process::{Child, Command, Stdio},
9 sync::{Arc, Weak},
10};
11
12use crossbeam_channel::{bounded, Receiver, Sender};
13use tt::Subtree;
14
15use crate::{
16 msg::{ErrorCode, Message, Request, Response, ResponseError},
17 rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind},
18};
19
20#[derive(Debug, Default)]
21pub(crate) struct ProcMacroProcessSrv {
22 inner: Option<Weak<Sender<Task>>>,
23}
24
25#[derive(Debug)]
26pub(crate) struct ProcMacroProcessThread {
27 // XXX: drop order is significant
28 sender: Arc<Sender<Task>>,
29 handle: jod_thread::JoinHandle<()>,
30}
31
32impl ProcMacroProcessSrv {
33 pub fn run(
34 process_path: PathBuf,
35 args: impl IntoIterator<Item = impl AsRef<OsStr>>,
36 ) -> io::Result<(ProcMacroProcessThread, ProcMacroProcessSrv)> {
37 let process = Process::run(process_path, args)?;
38
39 let (task_tx, task_rx) = bounded(0);
40 let handle = jod_thread::spawn(move || {
41 client_loop(task_rx, process);
42 });
43
44 let task_tx = Arc::new(task_tx);
45 let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) };
46 let thread = ProcMacroProcessThread { handle, sender: task_tx };
47
48 Ok((thread, srv))
49 }
50
51 pub fn find_proc_macros(
52 &self,
53 dylib_path: &Path,
54 ) -> Result<Vec<(String, ProcMacroKind)>, tt::ExpansionError> {
55 let task = ListMacrosTask { lib: dylib_path.to_path_buf() };
56
57 let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?;
58 Ok(result.macros)
59 }
60
61 pub fn custom_derive(
62 &self,
63 dylib_path: &Path,
64 subtree: &Subtree,
65 derive_name: &str,
66 ) -> Result<Subtree, tt::ExpansionError> {
67 let task = ExpansionTask {
68 macro_body: subtree.clone(),
69 macro_name: derive_name.to_string(),
70 attributes: None,
71 lib: dylib_path.to_path_buf(),
72 };
73
74 let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?;
75 Ok(result.expansion)
76 }
77
78 pub fn send_task<R>(&self, req: Request) -> Result<R, tt::ExpansionError>
79 where
80 R: TryFrom<Response, Error = &'static str>,
81 {
82 let sender = match &self.inner {
83 None => return Err(tt::ExpansionError::Unknown("No sender is found.".to_string())),
84 Some(it) => it,
85 };
86
87 let (result_tx, result_rx) = bounded(0);
88 let sender = match sender.upgrade() {
89 None => {
90 return Err(tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
91 }
92 Some(it) => it,
93 };
94 sender.send(Task { req, result_tx }).unwrap();
95 let res = result_rx
96 .recv()
97 .map_err(|_| tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;
98
99 match res {
100 Some(Response::Error(err)) => {
101 return Err(tt::ExpansionError::ExpansionError(err.message));
102 }
103 Some(res) => Ok(res.try_into().map_err(|err| {
104 tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err))
105 })?),
106 None => Err(tt::ExpansionError::Unknown("Empty result".into())),
107 }
108 }
109}
110
111fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
112 let (mut stdin, mut stdout) = match process.stdio() {
113 None => return,
114 Some(it) => it,
115 };
116
117 for task in task_rx {
118 let Task { req, result_tx } = task;
119
120 match send_request(&mut stdin, &mut stdout, req) {
121 Ok(res) => result_tx.send(res).unwrap(),
122 Err(_err) => {
123 let res = Response::Error(ResponseError {
124 code: ErrorCode::ServerErrorEnd,
125 message: "Server closed".into(),
126 });
127 result_tx.send(res.into()).unwrap();
128 // Restart the process
129 if process.restart().is_err() {
130 break;
131 }
132 let stdio = match process.stdio() {
133 None => break,
134 Some(it) => it,
135 };
136 stdin = stdio.0;
137 stdout = stdio.1;
138 }
139 }
140 }
141}
142
143struct Task {
144 req: Request,
145 result_tx: Sender<Option<Response>>,
146}
147
148struct Process {
149 path: PathBuf,
150 args: Vec<OsString>,
151 child: Child,
152}
153
154impl Drop for Process {
155 fn drop(&mut self) {
156 let _ = self.child.kill();
157 }
158}
159
160impl Process {
161 fn run(
162 path: PathBuf,
163 args: impl IntoIterator<Item = impl AsRef<OsStr>>,
164 ) -> io::Result<Process> {
165 let args = args.into_iter().map(|s| s.as_ref().into()).collect();
166 let child = mk_child(&path, &args)?;
167 Ok(Process { path, args, child })
168 }
169
170 fn restart(&mut self) -> io::Result<()> {
171 let _ = self.child.kill();
172 self.child = mk_child(&self.path, &self.args)?;
173 Ok(())
174 }
175
176 fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
177 let stdin = self.child.stdin.take()?;
178 let stdout = self.child.stdout.take()?;
179 let read = BufReader::new(stdout);
180
181 Some((stdin, read))
182 }
183}
184
185fn mk_child(path: &Path, args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> io::Result<Child> {
186 Command::new(&path)
187 .args(args)
188 .stdin(Stdio::piped())
189 .stdout(Stdio::piped())
190 .stderr(Stdio::inherit())
191 .spawn()
192}
193
194fn send_request(
195 mut writer: &mut impl Write,
196 mut reader: &mut impl BufRead,
197 req: Request,
198) -> io::Result<Option<Response>> {
199 req.write(&mut writer)?;
200 Ok(Response::read(&mut reader)?)
201}