2323
2424class ShellReturn :
2525 def __init__ (self ):
26+ # note: stderr is never captured
2627 self .stdout = None
27- self .stderr = None
2828 self .exit_code = None
2929 self .errmsg = None
3030 self .is_submake = False
3131
3232
33- def execute (cmd_str , symbol_table , use_default_shell = True ):
33+ def execute (cmd_str , symbol_table , use_default_shell = True , capture = True ):
3434 """execute a string with the shell, returning a bunch of useful info"""
3535
3636 # capture a timestamp so we can match shell debug messages
@@ -79,7 +79,7 @@ def execute(cmd_str, symbol_table, use_default_shell=True):
7979 if shell :
8080 cmd .extend (shell_list )
8181 if shellflags :
82- cmd .append ( shellflags )
82+ cmd .extend ([ f for f in shellflags . split ()] )
8383 cmd .append (cmd_str )
8484
8585 env = symbol_table .get_exports ()
@@ -115,27 +115,35 @@ def execute(cmd_str, symbol_table, use_default_shell=True):
115115# outfile.write(" ".join(cmd))
116116# outfile.write("\n\n\n")
117117
118+ # definitely need to capture stdout when we're running a sub-make because
119+ # that's how we determine the shell arguments to the actual sub-make
120+ if cmd_str .startswith (submake .getname ()):
121+ capture = True
122+
123+ logger .debug ("cmd=>>>%s<<<" , cmd )
124+
125+ kwargs = {
126+ "shell" :False ,
127+ "universal_newlines" :True ,
128+ "check" :False , # we'll check returncode ourselves
129+ "env" :env
130+ }
131+ if capture :
132+ kwargs ["stdout" ] = subprocess .PIPE
133+
118134 try :
119- p = subprocess .run (cmd ,
120- shell = False ,
121- stdout = subprocess .PIPE ,
122- stderr = subprocess .PIPE ,
123- universal_newlines = True ,
124- check = False , # we'll check returncode ourselves
125- env = env
126- )
135+ p = subprocess .run (cmd , ** kwargs )
136+ #
127137 logger .debug ("shell ts=%f exit status=%r" , ts , p .returncode )
128138# if p.returncode != 0:
129139# breakpoint()
130140 return_status .exit_code = p .returncode
131141 return_status .stdout = p .stdout
132- return_status .stderr = p .stderr
133142 except OSError as err :
134143 logger .error ("shell ts=%f error=\" %s\" " , ts , err )
144+ # match gnu make's output
135145 return_status .exit_code = 127
136146 return_status .stdout = ""
137- # match gnu make's output
138- return_status .stderr = err .strerror
139147
140148 if cmd_str .startswith (submake .getname ()):
141149 return_status .is_submake = True
@@ -165,9 +173,9 @@ def execute_tokens(token_list, symbol_table):
165173 symbol_table .allow_recursion ()
166174
167175 # GNU Make returns one whitespace separated string, no CR/LF
168- # "all other newlines are replaced by spaces." gnu_make.pdf
176+ # "If the result of the execution ends in a newline, that one newline is
177+ # removed; all other newlines are replaced by spaces." GNU Make PDF
169178 exe_result .stdout = exe_result .stdout .strip ().replace ("\n " , " " )
170- exe_result .stderr = exe_result .stderr .strip ().replace ("\n " , " " )
171179
172180 # save shell status
173181 pos = token_list [0 ].get_pos ()
@@ -185,12 +193,8 @@ def execute_tokens(token_list, symbol_table):
185193 if exe_result .errmsg :
186194 error_message (pos , exe_result .errmsg )
187195 else :
188- # otherwise report stderr (if any)
189- if exe_result .stderr :
190- logger .error ("command at %r failed with exit_code=%d" , pos , exe_result .exit_code )
191- error_message (pos , exe_result .stderr )
192- else :
193- logger .error ("command at %r failed with exit_code=%d (but stderr empty)" , pos , exe_result .exit_code )
196+ # stderr already sent to the world
197+ logger .error ("command at %r failed with exit_code=%d" , pos , exe_result .exit_code )
194198
195199 return exe_result .stdout
196200
0 commit comments