1- use regex:: Regex ;
21use std:: ffi:: OsStr ;
3- use std:: fs:: File ;
4- use std:: io:: { self , BufRead , BufReader , Seek , SeekFrom , Write } ;
5- use std:: process:: { self , Command , Stdio } ;
6- use std:: time:: Instant ;
7- use tempfile:: tempfile;
2+ use std:: io;
3+ use std:: process:: { self , Command } ;
84
95use crate :: errors:: * ;
10- use crate :: notifications:: * ;
11- use crate :: telemetry:: { Telemetry , TelemetryEvent } ;
12- use crate :: Cfg ;
136use rustup_utils:: { self , utils:: ExitCode } ;
147
158pub fn run_command_for_dir < S : AsRef < OsStr > > (
16- cmd : Command ,
17- arg0 : & str ,
18- args : & [ S ] ,
19- cfg : & Cfg ,
20- ) -> Result < ExitCode > {
21- if ( arg0 == "rustc" || arg0 == "rustc.exe" ) && cfg. telemetry_enabled ( ) ? {
22- return telemetry_rustc ( cmd, arg0, args, cfg) ;
23- }
24-
25- exec_command_for_dir_without_telemetry ( cmd, arg0, args)
26- }
27-
28- fn telemetry_rustc < S : AsRef < OsStr > > (
29- mut cmd : Command ,
30- arg0 : & str ,
31- args : & [ S ] ,
32- cfg : & Cfg ,
33- ) -> Result < ExitCode > {
34- #[ cfg( unix) ]
35- fn file_as_stdio ( file : & File ) -> Stdio {
36- use std:: os:: unix:: io:: { AsRawFd , FromRawFd } ;
37- unsafe { Stdio :: from_raw_fd ( file. as_raw_fd ( ) ) }
38- }
39-
40- #[ cfg( windows) ]
41- fn file_as_stdio ( file : & File ) -> Stdio {
42- use std:: os:: windows:: io:: { AsRawHandle , FromRawHandle } ;
43- unsafe { Stdio :: from_raw_handle ( file. as_raw_handle ( ) ) }
44- }
45-
46- let now = Instant :: now ( ) ;
47-
48- cmd. args ( args) ;
49-
50- let has_color_args = args. iter ( ) . any ( |e| {
51- let e = e. as_ref ( ) . to_str ( ) . unwrap_or ( "" ) ;
52- e. starts_with ( "--color" )
53- } ) ;
54-
55- if stderr_isatty ( ) && !has_color_args {
56- cmd. arg ( "--color" ) ;
57- cmd. arg ( "always" ) ;
58- }
59-
60- let mut cmd_err_file = tempfile ( ) . unwrap ( ) ;
61- let cmd_err_stdio = file_as_stdio ( & cmd_err_file) ;
62-
63- // FIXME rust-lang/rust#32254. It's not clear to me
64- // when and why this is needed.
65- let mut cmd = cmd
66- . stdin ( Stdio :: inherit ( ) )
67- . stdout ( Stdio :: inherit ( ) )
68- . stderr ( cmd_err_stdio)
69- . spawn ( )
70- . unwrap ( ) ;
71-
72- let status = cmd. wait ( ) ;
73-
74- let duration = now. elapsed ( ) ;
75-
76- let ms = ( duration. as_secs ( ) as u64 * 1000 ) + ( duration. subsec_nanos ( ) as u64 / 1000 / 1000 ) ;
77-
78- let t = Telemetry :: new ( cfg. rustup_dir . join ( "telemetry" ) ) ;
79-
80- match status {
81- Ok ( status) => {
82- let exit_code = status. code ( ) . unwrap_or ( 1 ) ;
83-
84- let re = Regex :: new ( r"\[(?P<error>E.{4})\]" ) . unwrap ( ) ;
85-
86- let mut buffer = String :: new ( ) ;
87- // Chose a HashSet instead of a Vec to avoid calls to sort() and dedup().
88- // The HashSet should be faster if there are a lot of errors, too.
89- let mut errors: Vec < String > = Vec :: new ( ) ;
90-
91- let stderr = io:: stderr ( ) ;
92- let mut handle = stderr. lock ( ) ;
93-
94- cmd_err_file. seek ( SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
95-
96- let mut buffered_stderr = BufReader :: new ( cmd_err_file) ;
97-
98- while buffered_stderr. read_line ( & mut buffer) . unwrap ( ) > 0 {
99- let b = buffer. to_owned ( ) ;
100- buffer. clear ( ) ;
101- let _ = handle. write ( b. as_bytes ( ) ) ;
102-
103- if let Some ( caps) = re. captures ( & b) {
104- if caps. len ( ) > 0 {
105- errors. push (
106- caps. name ( "error" )
107- . map ( |m| m. as_str ( ) )
108- . unwrap_or ( "" )
109- . to_owned ( ) ,
110- ) ;
111- }
112- } ;
113- }
114-
115- let e = if errors. is_empty ( ) {
116- None
117- } else {
118- Some ( errors)
119- } ;
120-
121- let te = TelemetryEvent :: RustcRun {
122- duration_ms : ms,
123- exit_code : exit_code,
124- errors : e,
125- } ;
126-
127- let _ = t. log_telemetry ( te) . map_err ( |xe| {
128- ( cfg. notify_handler ) ( Notification :: TelemetryCleanupError ( & xe) ) ;
129- } ) ;
130-
131- Ok ( ExitCode ( exit_code) )
132- }
133- Err ( e) => {
134- let exit_code = e. raw_os_error ( ) . unwrap_or ( 1 ) ;
135- let te = TelemetryEvent :: RustcRun {
136- duration_ms : ms,
137- exit_code : exit_code,
138- errors : None ,
139- } ;
140-
141- let _ = t. log_telemetry ( te) . map_err ( |xe| {
142- ( cfg. notify_handler ) ( Notification :: TelemetryCleanupError ( & xe) ) ;
143- } ) ;
144-
145- Err ( e) . chain_err ( || rustup_utils:: ErrorKind :: RunningCommand {
146- name : OsStr :: new ( arg0) . to_owned ( ) ,
147- } )
148- }
149- }
150- }
151-
152- fn exec_command_for_dir_without_telemetry < S : AsRef < OsStr > > (
1539 mut cmd : Command ,
15410 arg0 : & str ,
15511 args : & [ S ] ,
@@ -176,26 +32,3 @@ fn exec_command_for_dir_without_telemetry<S: AsRef<OsStr>>(
17632 Ok ( ExitCode ( status. code ( ) . unwrap ( ) ) )
17733 }
17834}
179-
180- #[ cfg( unix) ]
181- fn stderr_isatty ( ) -> bool {
182- use libc;
183- unsafe { libc:: isatty ( libc:: STDERR_FILENO ) != 0 }
184- }
185-
186- #[ cfg( windows) ]
187- fn stderr_isatty ( ) -> bool {
188- type DWORD = u32 ;
189- type BOOL = i32 ;
190- type HANDLE = * mut u8 ;
191- const STD_ERROR_HANDLE : DWORD = -12i32 as DWORD ;
192- extern "system" {
193- fn GetStdHandle ( which : DWORD ) -> HANDLE ;
194- fn GetConsoleMode ( hConsoleHandle : HANDLE , lpMode : * mut DWORD ) -> BOOL ;
195- }
196- unsafe {
197- let handle = GetStdHandle ( STD_ERROR_HANDLE ) ;
198- let mut out = 0 ;
199- GetConsoleMode ( handle, & mut out) != 0
200- }
201- }
0 commit comments