makepad_studio/build_manager/
child_process.rs

1use std::{
2    process::{Command, Child, Stdio},
3    sync::mpsc::{self, Sender, Receiver},
4    thread,
5    io::prelude::*,
6    io::BufReader,
7    str,
8    path::PathBuf,
9};
10use crate::makepad_platform::cx_stdin::aux_chan;
11
12pub struct ChildProcess {
13    pub child: Child,
14    pub stdin_sender: Sender<ChildStdIn>,
15    pub line_sender: Sender<ChildStdIO>,
16    pub line_receiver: Receiver<ChildStdIO>,
17    pub aux_chan_host_endpoint: aux_chan::HostEndpoint,
18}
19
20pub enum ChildStdIO {
21    StdOut(String),
22    StdErr(String),
23    Term,
24    Kill
25}
26
27pub enum ChildStdIn {
28    Send(String),
29    Term,
30}
31
32impl ChildProcess {
33    
34    pub fn start(cmd: &str, args: &[String], current_dir: PathBuf, env: &[(&str, &str)]) -> Result<ChildProcess, std::io::Error> {
35        let (aux_chan_host_endpoint, aux_chan_client_endpoint) =
36            aux_chan::make_host_and_client_endpoint_pair()?;
37        
38        let aux_chan_client_endpoint_inheritable =
39            aux_chan_client_endpoint.into_child_process_inheritable()?;
40        
41        let mut cmd_build = Command::new(cmd);
42        
43        cmd_build.args(args)
44            .args(aux_chan_client_endpoint_inheritable.extra_args_for_client_spawning())
45            .stdin(Stdio::piped())
46            .stdout(Stdio::piped())
47            .stderr(Stdio::piped())
48            .current_dir(current_dir);
49        
50        for (key, value) in env {
51            cmd_build.env(key, value);
52        }
53        
54        let mut child = cmd_build.spawn()?;
55
56        // In the parent process, an inherited fd doesn't need to exist past
57        // the spawning of the child process (which clones non-`CLOEXEC` fds).
58        drop(aux_chan_client_endpoint_inheritable);
59        
60        let (line_sender, line_receiver) = mpsc::channel();
61        let (stdin_sender, stdin_receiver) = mpsc::channel();
62
63        let mut stdin = child.stdin.take().expect("stdin cannot be taken!");
64        let stdout = child.stdout.take().expect("stdout cannot be taken!");
65        let stderr = child.stderr.take().expect("stderr cannot be taken!");
66        
67        let _stdout_thread = {
68            let line_sender = line_sender.clone();
69            let stdin_sender = stdin_sender.clone();
70            thread::spawn(move || {
71                let mut reader = BufReader::new(stdout);
72                loop{
73                    let mut line = String::new();
74                    if let Ok(len) = reader.read_line(&mut line){
75                        if len == 0{
76                            break
77                        }
78                        if line_sender.send(ChildStdIO::StdOut(line)).is_err(){
79                            break;
80                        }
81                    }
82                    else{
83                        let _ = line_sender.send(ChildStdIO::Term);
84                        let _ = stdin_sender.send(ChildStdIn::Term);
85                        break;
86                    }
87                }
88            })
89        };
90        
91        let _stderr_thread = {
92            let line_sender = line_sender.clone();
93            thread::spawn(move || {
94                let mut reader = BufReader::new(stderr);
95                loop{
96                    let mut line = String::new();
97                    if let Ok(len) = reader.read_line(&mut line){
98                        if len == 0{
99                            break
100                        }
101                        if line_sender.send(ChildStdIO::StdErr(line)).is_err(){
102                            break
103                        };
104                    }
105                    else{
106                        break;
107                    }
108                }
109            });
110        };
111
112        let _stdin_thread = {
113            thread::spawn(move || {
114                while let Ok(line) = stdin_receiver.recv() {
115                    match line {
116                        ChildStdIn::Send(line) => {
117                            if let Err(_) = stdin.write_all(line.as_bytes()){
118                                //println!("Stdin send error {}", e);
119                            }
120                            let _ = stdin.flush();
121                        }
122                        ChildStdIn::Term=>{
123                            break;
124                        }
125                    }
126                }
127            });
128        };
129        Ok(ChildProcess {
130            stdin_sender,
131            line_sender,
132            child,
133            line_receiver,
134            aux_chan_host_endpoint,
135        })
136    }
137    
138    pub fn wait(mut self) {
139        let _ = self.child.wait();
140    }
141    
142    pub fn kill(mut self) {
143        let _ = self.stdin_sender.send(ChildStdIn::Term);
144        let _ = self.child.kill();
145        let _ = self.child.wait();
146    }
147}