1414
1515mod flags;
1616mod options;
17+ mod output;
18+ mod rustc;
1719mod util;
1820
1921use std:: fs:: { copy, OpenOptions } ;
22+ use std:: io;
2023use std:: process:: { exit, Command , Stdio } ;
24+ use std:: sync:: mpsc:: sync_channel;
25+
26+ use output:: process_output;
2127
2228use crate :: options:: options;
2329
@@ -26,38 +32,83 @@ fn main() {
2632 Err ( err) => panic ! ( "process wrapper error: {}" , err) ,
2733 Ok ( v) => v,
2834 } ;
29- let stdout = if let Some ( stdout_file) = opts. stdout_file {
30- OpenOptions :: new ( )
31- . create ( true )
32- . truncate ( true )
33- . write ( true )
34- . open ( stdout_file)
35- . expect ( "process wrapper error: unable to open stdout file" )
36- . into ( )
37- } else {
38- Stdio :: inherit ( )
39- } ;
40- let stderr = if let Some ( stderr_file) = opts. stderr_file {
41- OpenOptions :: new ( )
42- . create ( true )
43- . truncate ( true )
44- . write ( true )
45- . open ( stderr_file)
46- . expect ( "process wrapper error: unable to open stderr file" )
47- . into ( )
48- } else {
49- Stdio :: inherit ( )
50- } ;
51- let status = Command :: new ( opts. executable )
35+
36+ let mut child = Command :: new ( opts. executable )
5237 . args ( opts. child_arguments )
5338 . env_clear ( )
5439 . envs ( opts. child_environment )
55- . stdout ( stdout )
56- . stderr ( stderr )
57- . status ( )
40+ . stdout ( Stdio :: piped ( ) )
41+ . stderr ( Stdio :: piped ( ) )
42+ . spawn ( )
5843 . expect ( "process wrapper error: failed to spawn child process" ) ;
5944
60- if status. success ( ) {
45+ let stdout: Box < dyn io:: Write + Send > = if let Some ( stdout_file) = opts. stdout_file {
46+ Box :: new (
47+ OpenOptions :: new ( )
48+ . create ( true )
49+ . truncate ( true )
50+ . write ( true )
51+ . open ( stdout_file)
52+ . expect ( "process wrapper error: unable to open stdout file" ) ,
53+ )
54+ } else {
55+ Box :: new ( io:: stdout ( ) )
56+ } ;
57+ let stderr: Box < dyn io:: Write + Send > = if let Some ( stderr_file) = opts. stderr_file {
58+ Box :: new (
59+ OpenOptions :: new ( )
60+ . create ( true )
61+ . truncate ( true )
62+ . write ( true )
63+ . open ( stderr_file)
64+ . expect ( "process wrapper error: unable to open stderr file" ) ,
65+ )
66+ } else {
67+ Box :: new ( io:: stderr ( ) )
68+ } ;
69+
70+ let child_stdout = Box :: new ( child. stdout . take ( ) . unwrap ( ) ) ;
71+ let child_stderr = Box :: new ( child. stderr . take ( ) . unwrap ( ) ) ;
72+
73+ if !opts. rustc_quit_on_rmeta {
74+ // Process output normally by forwarding stdout and stderr
75+ let stdout_thread = process_output ( child_stdout, stdout, |line| Some ( line) ) ;
76+ let stderr_thread = process_output ( child_stderr, stderr, |line| Some ( line) ) ;
77+ stdout_thread. join ( ) . unwrap ( ) . unwrap ( ) ;
78+ stderr_thread. join ( ) . unwrap ( ) . unwrap ( ) ;
79+ } else {
80+ let format = opts. rustc_output_format ;
81+ // Process json rustc output and kill the subprocess when we get a signal
82+ // that we emitted a metadata file.
83+ // This receiver will block until a corresponding send happens.
84+ let ( stop_sender_stdout, stop) = sync_channel ( 0 ) ;
85+ let stop_sender_stderr = stop_sender_stdout. clone ( ) ;
86+ let stdout_thread = process_output ( child_stdout, stdout, move |line| {
87+ rustc:: process_message ( line, format, & stop_sender_stdout)
88+ } ) ;
89+ let stderr_thread = process_output ( child_stderr, stderr, move |line| {
90+ rustc:: process_message ( line, format, & stop_sender_stderr)
91+ } ) ;
92+ if let Ok ( _) = stop. recv ( ) {
93+ // If recv returns Ok(), a signal was sent in this channel so we should terminate the child process.
94+ // We can safely ignore the Result from kill() as we don't care if the process already terminated.
95+ let _ = child. kill ( ) ;
96+ }
97+
98+ stdout_thread. join ( ) . unwrap ( ) . unwrap ( ) ;
99+ stderr_thread. join ( ) . unwrap ( ) . unwrap ( ) ;
100+ }
101+
102+ let status = child
103+ . wait ( )
104+ . expect ( "process wrapper error: failed to wait for child process" ) ;
105+ // If the child process is rustc and is killed after metadata generation, that's also a success.
106+ let ( code, success) = match status. code ( ) {
107+ // According to rust docs: Signal termination is not considered a success, and success is defined as a zero exit status.
108+ Some ( code) => ( code, code == 0 ) ,
109+ None => ( 0 , true ) ,
110+ } ;
111+ if success {
61112 if let Some ( tf) = opts. touch_file {
62113 OpenOptions :: new ( )
63114 . create ( true )
@@ -75,5 +126,5 @@ fn main() {
75126 }
76127 }
77128
78- exit ( status . code ( ) . unwrap ( ) )
129+ exit ( code)
79130}
0 commit comments