@@ -3,9 +3,11 @@ use std::{
3
3
io:: { BufRead , BufReader } ,
4
4
path:: Path ,
5
5
process:: { Command , Stdio } ,
6
+ sync:: mpsc,
7
+ thread,
6
8
} ;
7
9
8
- use anyhow:: anyhow ;
10
+ use anyhow;
9
11
use rand:: Rng ;
10
12
use regex:: Regex ;
11
13
@@ -30,15 +32,15 @@ pub fn run_process(
30
32
command. envs ( & envs) ;
31
33
}
32
34
let status = command. args ( args) . status ( ) . map_err ( |e| {
33
- anyhow ! (
35
+ anyhow:: anyhow !(
34
36
"Failed to execute {} {}: {}" ,
35
37
name,
36
38
args. first( ) . unwrap( ) ,
37
39
e
38
40
)
39
41
} ) ?;
40
42
if !status. success ( ) {
41
- return Err ( anyhow ! ( "{}" , error_msg) ) ;
43
+ return Err ( anyhow:: anyhow !( "{}" , error_msg) ) ;
42
44
}
43
45
anyhow:: Ok ( ( ) )
44
46
}
@@ -61,71 +63,95 @@ pub fn run_process_for_workspace<'a>(
61
63
. iter ( )
62
64
. for_each ( |ex| args. extend ( [ "--exclude" , ex] ) ) ;
63
65
group_info ! ( "Command line: cargo {}" , args. join( " " ) ) ;
66
+ // process
64
67
let mut child = Command :: new ( name)
65
68
. args ( & args)
66
69
. stdout ( Stdio :: piped ( ) )
67
70
. stderr ( Stdio :: piped ( ) )
68
71
. spawn ( )
69
72
. map_err ( |e| {
70
- anyhow ! ( format!(
73
+ anyhow:: anyhow !( format!(
71
74
"Failed to start {} {}: {}" ,
72
75
name,
73
76
args. first( ) . unwrap( ) ,
74
77
e
75
78
) )
76
79
} ) ?;
77
80
78
- let mut ignore_error = false ;
79
- let mut close_group = false ;
81
+ // handle stdout and stderr in dedicated threads using a MPSC channel for synchronization
82
+ let ( tx, rx) = mpsc:: channel ( ) ;
83
+ // stdout processing thread
80
84
if let Some ( stdout) = child. stdout . take ( ) {
81
- let reader = BufReader :: new ( stdout) ;
82
- reader. lines ( ) . for_each ( |line| {
83
- if let Ok ( line) = line {
84
- println ! ( "{}" , line) ;
85
+ let tx = tx. clone ( ) ;
86
+ thread:: spawn ( move || {
87
+ let reader = BufReader :: new ( stdout) ;
88
+ for line in reader. lines ( ) . map_while ( Result :: ok) {
89
+ tx. send ( ( line, false ) ) . unwrap ( ) ;
85
90
}
86
91
} ) ;
87
92
}
93
+ // stderr processing thread
88
94
if let Some ( stderr) = child. stderr . take ( ) {
89
- let reader = BufReader :: new ( stderr ) ;
90
- reader . lines ( ) . for_each ( |line | {
91
- let mut skip_line = false ;
92
- if let Ok ( line) = line {
93
- if let Some ( rx ) = & group_rx {
94
- let cleaned_line = standardize_slashes ( & remove_ansi_codes ( & line ) ) ;
95
- if let Some ( caps ) = rx . captures ( & cleaned_line ) {
96
- let crate_name = & caps [ 1 ] ;
97
- if close_group {
98
- endgroup ! ( ) ;
99
- }
100
- close_group = true ;
101
- group ! ( "{}: {}" , group_name . unwrap_or ( "Group" ) , crate_name ) ;
102
- }
103
- }
104
- if let Some ( log ) = ignore_log {
105
- if line . contains ( log ) {
106
- if let Some ( msg ) = ignore_msg {
107
- warn ! ( "{}" , msg ) ;
108
- }
109
- ignore_error = true ;
110
- skip_line = true ;
111
- }
95
+ let tx = tx . clone ( ) ;
96
+ thread :: spawn ( move | | {
97
+ let reader = BufReader :: new ( stderr ) ;
98
+ for line in reader . lines ( ) . map_while ( Result :: ok ) {
99
+ tx . send ( ( line , true ) ) . unwrap ( ) ;
100
+ }
101
+ } ) ;
102
+ }
103
+ // Drop the sender once all the logs have been processed to close the channel
104
+ drop ( tx ) ;
105
+
106
+ // Process the stdout to inject log groups
107
+ let mut ignore_error = false ;
108
+ let mut close_group = false ;
109
+ for ( line , is_stderr ) in rx . iter ( ) {
110
+ let mut skip_line = false ;
111
+
112
+ if let Some ( rx ) = & group_rx {
113
+ let cleaned_line = standardize_slashes ( & remove_ansi_codes ( & line ) ) ;
114
+ if let Some ( caps ) = rx . captures ( & cleaned_line ) {
115
+ let crate_name = & caps [ 1 ] ;
116
+ if close_group {
117
+ endgroup ! ( ) ;
112
118
}
113
- if !skip_line {
114
- eprintln ! ( "{}" , line) ;
119
+ close_group = true ;
120
+ group ! ( "{}: {}" , group_name. unwrap_or( "Group" ) , crate_name) ;
121
+ }
122
+ }
123
+
124
+ if let Some ( log) = ignore_log {
125
+ if line. contains ( log) {
126
+ if let Some ( msg) = ignore_msg {
127
+ warn ! ( "{}" , msg) ;
115
128
}
129
+ ignore_error = true ;
130
+ skip_line = true ;
116
131
}
117
- } ) ;
132
+ }
133
+
134
+ if !skip_line {
135
+ if is_stderr {
136
+ eprintln ! ( "{}" , line) ;
137
+ } else {
138
+ println ! ( "{}" , line) ;
139
+ }
140
+ }
118
141
}
142
+
119
143
if close_group {
120
144
endgroup ! ( ) ;
121
145
}
146
+
122
147
let status = child
123
148
. wait ( )
124
149
. expect ( "Should be able to wait for the process to finish." ) ;
150
+
125
151
if status. success ( ) || ignore_error {
126
152
anyhow:: Ok ( ( ) )
127
153
} else {
128
- Err ( anyhow ! ( "{}" , error_msg) )
154
+ Err ( anyhow:: anyhow !( "{}" , error_msg) )
129
155
}
130
156
}
131
157
@@ -152,7 +178,7 @@ pub fn run_process_for_package(
152
178
. stdout ( Stdio :: inherit ( ) )
153
179
. stderr ( Stdio :: inherit ( ) )
154
180
. output ( )
155
- . map_err ( |e| anyhow ! ( "Failed to execute process for '{}': {}" , name, e) ) ?;
181
+ . map_err ( |e| anyhow:: anyhow !( "Failed to execute process for '{}': {}" , name, e) ) ?;
156
182
157
183
if output. status . success ( ) {
158
184
return anyhow:: Ok ( ( ) ) ;
@@ -166,7 +192,7 @@ pub fn run_process_for_package(
166
192
return anyhow:: Ok ( ( ) ) ;
167
193
}
168
194
}
169
- Err ( anyhow ! ( "{}" , error_msg) )
195
+ Err ( anyhow:: anyhow !( "{}" , error_msg) )
170
196
}
171
197
172
198
/// Return a random port between 3000 and 9999
0 commit comments