6868from easybuild .tools .build_log import dry_run_msg , print_msg , time_str_since
6969from easybuild .tools .config import build_option
7070from easybuild .tools .hooks import RUN_SHELL_CMD , load_hooks , run_hook
71+ from easybuild .tools .output import COLOR_RED , COLOR_YELLOW , colorize
7172from easybuild .tools .utilities import trace_msg
7273
7374
8687)
8788
8889RunShellCmdResult = namedtuple ('RunShellCmdResult' , ('cmd' , 'exit_code' , 'output' , 'stderr' , 'work_dir' ,
89- 'out_file' , 'err_file' , 'thread_id' , 'task_id' ))
90+ 'out_file' , 'err_file' , 'cmd_sh' , ' thread_id' , 'task_id' ))
9091
9192
9293class RunShellCmdError (BaseException ):
@@ -101,6 +102,7 @@ def __init__(self, cmd_result, caller_info, *args, **kwargs):
101102 self .out_file = cmd_result .out_file
102103 self .stderr = cmd_result .stderr
103104 self .err_file = cmd_result .err_file
105+ self .cmd_sh = cmd_result .cmd_sh
104106
105107 self .caller_info = caller_info
106108
@@ -112,31 +114,37 @@ def print(self):
112114 Report failed shell command for this RunShellCmdError instance
113115 """
114116
115- def pad_4_spaces (msg ):
116- return ' ' * 4 + msg
117+ def pad_4_spaces (msg , color = None ):
118+ padded_msg = ' ' * 4 + msg
119+ if color :
120+ return colorize (padded_msg , color )
121+ else :
122+ return padded_msg
123+
124+ caller_file_name , caller_line_nr , caller_function_name = self .caller_info
125+ called_from_info = f"'{ caller_function_name } ' function in { caller_file_name } (line { caller_line_nr } )"
117126
118127 error_info = [
119128 '' ,
120- "ERROR: Shell command failed!" ,
129+ colorize ( "ERROR: Shell command failed!" , COLOR_RED ) ,
121130 pad_4_spaces (f"full command -> { self .cmd } " ),
122131 pad_4_spaces (f"exit code -> { self .exit_code } " ),
132+ pad_4_spaces (f"called from -> { called_from_info } " ),
123133 pad_4_spaces (f"working directory -> { self .work_dir } " ),
124134 ]
125135
126136 if self .out_file is not None :
127137 # if there's no separate file for error/warnings, then out_file includes both stdout + stderr
128138 out_info_msg = "output (stdout + stderr)" if self .err_file is None else "output (stdout) "
129- error_info .append (pad_4_spaces (f"{ out_info_msg } -> { self .out_file } " ))
139+ error_info .append (pad_4_spaces (f"{ out_info_msg } -> { self .out_file } " , color = COLOR_YELLOW ))
130140
131141 if self .err_file is not None :
132- error_info .append (pad_4_spaces (f"error/warnings (stderr) -> { self .err_file } " ))
142+ error_info .append (pad_4_spaces (f"error/warnings (stderr) -> { self .err_file } " , color = COLOR_YELLOW ))
133143
134- caller_file_name , caller_line_nr , caller_function_name = self .caller_info
135- called_from_info = f"'{ caller_function_name } ' function in { caller_file_name } (line { caller_line_nr } )"
136- error_info .extend ([
137- pad_4_spaces (f"called from -> { called_from_info } " ),
138- '' ,
139- ])
144+ if self .cmd_sh is not None :
145+ error_info .append (pad_4_spaces (f"interactive shell script -> { self .cmd_sh } " , color = COLOR_YELLOW ))
146+
147+ error_info .append ('' )
140148
141149 sys .stderr .write ('\n ' .join (error_info ) + '\n ' )
142150
@@ -254,6 +262,8 @@ def create_cmd_scripts(cmd_str, work_dir, env, tmpdir, out_file, err_file):
254262 ]))
255263 os .chmod (cmd_fp , 0o775 )
256264
265+ return cmd_fp
266+
257267
258268def _answer_question (stdout , proc , qa_patterns , qa_wait_patterns ):
259269 """
@@ -430,9 +440,9 @@ def to_cmd_str(cmd):
430440 else :
431441 cmd_err_fp = None
432442
433- create_cmd_scripts (cmd_str , work_dir , env , tmpdir , cmd_out_fp , cmd_err_fp )
443+ cmd_sh = create_cmd_scripts (cmd_str , work_dir , env , tmpdir , cmd_out_fp , cmd_err_fp )
434444 else :
435- tmpdir , cmd_out_fp , cmd_err_fp = None , None , None
445+ tmpdir , cmd_out_fp , cmd_err_fp , cmd_sh = None , None , None , None
436446
437447 interactive_msg = 'interactive ' if interactive else ''
438448
@@ -445,7 +455,8 @@ def to_cmd_str(cmd):
445455 dry_run_msg (msg , silent = silent )
446456
447457 return RunShellCmdResult (cmd = cmd_str , exit_code = 0 , output = '' , stderr = None , work_dir = work_dir ,
448- out_file = cmd_out_fp , err_file = cmd_err_fp , thread_id = thread_id , task_id = task_id )
458+ out_file = cmd_out_fp , err_file = cmd_err_fp , cmd_sh = cmd_sh ,
459+ thread_id = thread_id , task_id = task_id )
449460
450461 start_time = datetime .now ()
451462 if not hidden :
@@ -571,8 +582,9 @@ def to_cmd_str(cmd):
571582 except IOError as err :
572583 raise EasyBuildError (f"Failed to dump command output to temporary file: { err } " )
573584
574- res = RunShellCmdResult (cmd = cmd_str , exit_code = proc .returncode , output = output , stderr = stderr , work_dir = work_dir ,
575- out_file = cmd_out_fp , err_file = cmd_err_fp , thread_id = thread_id , task_id = task_id )
585+ res = RunShellCmdResult (cmd = cmd_str , exit_code = proc .returncode , output = output , stderr = stderr ,
586+ work_dir = work_dir , out_file = cmd_out_fp , err_file = cmd_err_fp , cmd_sh = cmd_sh ,
587+ thread_id = thread_id , task_id = task_id )
576588
577589 # always log command output
578590 cmd_name = cmd_str .split (' ' )[0 ]
0 commit comments