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: Option<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)], aux_chan:bool) -> Result<ChildProcess, std::io::Error> {
35        let (mut child, aux_chan_host_endpoint) = if aux_chan{
36            let (aux_chan_host_endpoint, aux_chan_client_endpoint) =
37                aux_chan::make_host_and_client_endpoint_pair()?;
38            
39            let aux_chan_client_endpoint_inheritable =
40                aux_chan_client_endpoint.into_child_process_inheritable()?;
41            let mut cmd_build = Command::new(cmd);
42                cmd_build.args(args)
43                .args(aux_chan_client_endpoint_inheritable.extra_args_for_client_spawning())
44                .stdin(Stdio::piped())
45                .stdout(Stdio::piped())
46                .stderr(Stdio::piped())
47                .current_dir(current_dir);
48                        
49            for (key, value) in env {
50                cmd_build.env(key, value);
51            }
52                        
53            let child = cmd_build.spawn()?;
54            drop(aux_chan_client_endpoint_inheritable);
55            (child, Some(aux_chan_host_endpoint))        
56        }
57        else{
58            let mut cmd_build = Command::new(cmd);
59             cmd_build.args(args)
60            .stdin(Stdio::piped())
61            .stdout(Stdio::piped())
62            .stderr(Stdio::piped())
63            .current_dir(current_dir);
64                                    
65            for (key, value) in env {
66                cmd_build.env(key, value);
67            }
68            (cmd_build.spawn()?, None)
69        };
70
71        // In the parent process, an inherited fd doesn't need to exist past
72        // the spawning of the child process (which clones non-`CLOEXEC` fds).
73        
74        let (line_sender, line_receiver) = mpsc::channel();
75        let (stdin_sender, stdin_receiver) = mpsc::channel();
76
77        let mut stdin = child.stdin.take().expect("stdin cannot be taken!");
78        let stdout = child.stdout.take().expect("stdout cannot be taken!");
79        let stderr = child.stderr.take().expect("stderr cannot be taken!");
80        
81        let _stdout_thread = {
82            let line_sender = line_sender.clone();
83            let stdin_sender = stdin_sender.clone();
84            thread::spawn(move || {
85                let mut reader = BufReader::new(stdout);
86                loop{
87                    let mut line = String::new();
88                    if let Ok(len) = reader.read_line(&mut line){
89                        if len == 0{
90                            break
91                        }
92                        if line_sender.send(ChildStdIO::StdOut(line)).is_err(){
93                            break;
94                        }
95                    }
96                    else{
97                        let _ = line_sender.send(ChildStdIO::Term);
98                        let _ = stdin_sender.send(ChildStdIn::Term);
99                        break;
100                    }
101                }
102            })
103        };
104        
105        let _stderr_thread = {
106            let line_sender = line_sender.clone();
107            thread::spawn(move || {
108                let mut reader = BufReader::new(stderr);
109                loop{
110                    let mut line = String::new();
111                    if let Ok(len) = reader.read_line(&mut line){
112                        if len == 0{
113                            break
114                        }
115                        if line_sender.send(ChildStdIO::StdErr(line)).is_err(){
116                            break
117                        };
118                    }
119                    else{
120                        break;
121                    }
122                }
123            });
124        };
125
126        let _stdin_thread = {
127            thread::spawn(move || {
128                while let Ok(line) = stdin_receiver.recv() {
129                    match line {
130                        ChildStdIn::Send(line) => {
131                            if let Err(_) = stdin.write_all(line.as_bytes()){
132                                //println!("Stdin send error {}",e);
133                                
134                            }
135                            let _ = stdin.flush();
136                        }
137                        ChildStdIn::Term=>{
138                            break;
139                        }
140                    }
141                }
142            });
143        };
144        Ok(ChildProcess {
145            stdin_sender,
146            line_sender,
147            child,
148            line_receiver,
149            aux_chan_host_endpoint,
150        })
151    }
152    
153    pub fn wait(mut self) {
154        let _ = self.child.wait();
155    }
156    
157    pub fn kill(mut self) {
158        let _ = self.stdin_sender.send(ChildStdIn::Term);
159        let _ = self.child.kill();
160        let _ = self.child.wait();
161    }
162}