2
2
3
3
import base64
4
4
import distutils .file_util
5
+ import io
5
6
import itertools
6
7
import os
7
8
import shlex
@@ -54,8 +55,12 @@ def _print_thread_safe(cls, string):
54
55
The initial use case was test-gdb which must create a thread for GDB to run the program in parallel.
55
56
'''
56
57
with cls ._print_lock :
57
- sys .stdout .write (string + '\n ' )
58
- sys .stdout .flush ()
58
+ try :
59
+ print (string , flush = True )
60
+ except BrokenPipeError :
61
+ # https://stackoverflow.com/questions/26692284/how-to-prevent-brokenpipeerror-when-doing-a-flush-in-python
62
+ # https://stackoverflow.com/questions/16314321/suppressing-printout-of-exception-ignored-message-in-python-3
63
+ pass
59
64
60
65
def add_newlines (self , cmd ):
61
66
out = []
@@ -72,6 +77,17 @@ def base64_encode(self, string):
72
77
def base64_decode (self , string ):
73
78
return base64 .b64decode (string .encode ()).decode ()
74
79
80
+ def check_output (self , * args , ** kwargs ):
81
+ out_str = []
82
+ self .run_cmd (
83
+ * args ,
84
+ out_str = out_str ,
85
+ show_stdout = False ,
86
+ show_cmd = False ,
87
+ ** kwargs
88
+ )
89
+ return out_str [0 ]
90
+
75
91
def chmod (self , path , add_rm_abs = '+' , mode_delta = stat .S_IXUSR ):
76
92
'''
77
93
TODO extend further, shell print equivalent.
@@ -245,6 +261,8 @@ def run_cmd(
245
261
extra_paths = None ,
246
262
delete_env = None ,
247
263
raise_on_failure = True ,
264
+ * ,
265
+ out_str = None ,
248
266
** kwargs
249
267
):
250
268
'''
@@ -261,6 +279,9 @@ def run_cmd(
261
279
:param out_file: if not None, write the stdout and stderr of the command the file
262
280
:type out_file: str
263
281
282
+ :param out_str: if not None, append the stdout and stderr string to this list
283
+ :type out_str: Union(List,None)
284
+
264
285
:param show_stdout: wether to show stdout and stderr on the terminal or not
265
286
:type show_stdout: bool
266
287
@@ -270,7 +291,7 @@ def run_cmd(
270
291
:return: exit status of the command
271
292
:rtype: int
272
293
'''
273
- if out_file is None :
294
+ if out_file is None and out_str is None :
274
295
if show_stdout :
275
296
stdout = None
276
297
stderr = None
@@ -299,14 +320,21 @@ def run_cmd(
299
320
if key in env :
300
321
del env [key ]
301
322
if show_cmd :
302
- self .print_cmd (cmd , cwd = cwd , cmd_file = cmd_file , extra_env = extra_env , extra_paths = extra_paths )
323
+ self .print_cmd (
324
+ cmd ,
325
+ cwd = cwd ,
326
+ cmd_file = cmd_file ,
327
+ extra_env = extra_env ,
328
+ extra_paths = extra_paths
329
+ )
303
330
304
331
# Otherwise, if called from a non-main thread:
305
332
# ValueError: signal only works in main thread
306
333
if threading .current_thread () == threading .main_thread ():
307
334
# Otherwise Ctrl + C gives:
308
335
# - ugly Python stack trace for gem5 (QEMU takes over terminal and is fine).
309
- # - kills Python, and that then kills GDB: https://stackoverflow.com/questions/19807134/does-python-always-raise-an-exception-if-you-do-ctrlc-when-a-subprocess-is-exec
336
+ # - kills Python, and that then kills GDB:
337
+ # https://stackoverflow.com/questions/19807134/does-python-always-raise-an-exception-if-you-do-ctrlc-when-a-subprocess-is-exec
310
338
sigint_old = signal .getsignal (signal .SIGINT )
311
339
signal .signal (signal .SIGINT , signal .SIG_IGN )
312
340
@@ -320,23 +348,39 @@ def run_cmd(
320
348
cmd = self .strip_newlines (cmd )
321
349
if not self .dry_run :
322
350
# https://stackoverflow.com/questions/15535240/python-popen-write-to-stdout-and-log-file-simultaneously/52090802#52090802
323
- with subprocess .Popen (cmd , stdout = stdout , stderr = stderr , env = env , ** kwargs ) as proc :
324
- if out_file is not None :
325
- os .makedirs (os .path .split (os .path .abspath (out_file ))[0 ], exist_ok = True )
326
- with open (out_file , 'bw' ) as logfile :
327
- while True :
328
- byte = proc .stdout .read (1 )
329
- if byte :
330
- if show_stdout :
331
- sys .stdout .buffer .write (byte )
332
- try :
333
- sys .stdout .flush ()
334
- except BlockingIOError :
335
- # TODO understand. Why, Python, why.
336
- pass
351
+ with subprocess .Popen (
352
+ cmd ,
353
+ stdout = stdout ,
354
+ stderr = stderr ,
355
+ env = env ,
356
+ ** kwargs
357
+ ) as proc :
358
+ if out_file is not None or out_str is not None :
359
+ if out_file is not None :
360
+ os .makedirs (os .path .split (os .path .abspath (out_file ))[0 ], exist_ok = True )
361
+ if out_file is not None :
362
+ logfile = open (out_file , 'bw' )
363
+ logfile_str = []
364
+ while True :
365
+ byte = proc .stdout .read (1 )
366
+ if byte :
367
+ if show_stdout :
368
+ sys .stdout .buffer .write (byte )
369
+ try :
370
+ sys .stdout .flush ()
371
+ except BlockingIOError :
372
+ # TODO understand. Why, Python, why.
373
+ pass
374
+ if out_file is not None :
337
375
logfile .write (byte )
338
- else :
339
- break
376
+ if out_str is not None :
377
+ logfile_str .append (byte )
378
+ else :
379
+ break
380
+ if out_file is not None :
381
+ logfile .close ()
382
+ if out_str is not None :
383
+ out_str .append ((b'' .join (logfile_str )).decode ())
340
384
if threading .current_thread () == threading .main_thread ():
341
385
signal .signal (signal .SIGINT , sigint_old )
342
386
#signal.signal(signal.SIGPIPE, sigpipe_old)
@@ -347,6 +391,8 @@ def run_cmd(
347
391
raise e
348
392
return returncode
349
393
else :
394
+ if not out_str is None :
395
+ out_str .append ('' )
350
396
return 0
351
397
352
398
def shlex_split (self , string ):
0 commit comments