Skip to content

Commit 535efdb

Browse files
committed
fix several problems with stdout/stderr
- subprocess calls should always send stderr outwards (no capture) - add option to capture or not capture stdout so $(shell) will be captured but recipes will go outwards (no capture) - set stdout to flush on every print() to match GNU Make which uses setbuf() (which isn't available to python) because otherwise stdout can race between pymake process and subprocess stdout - start tests on $(SHELLFLAGS) which is what led to finding these problems with stdout
1 parent ed214ed commit 535efdb

5 files changed

Lines changed: 69 additions & 24 deletions

File tree

pymake/functions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ def eval(self, symbol_table):
8484
if self.fmt:
8585
t = self.token_list[0]
8686
filename, linenumber = t.get_pos()
87-
print(self.fmt.format(filename, linenumber, msg), file=self.fh)
87+
print(self.fmt.format(filename, linenumber, msg), file=self.fh, flush=True)
8888
else:
89-
print("%s" % msg, file=self.fh)
89+
print("%s" % msg, file=self.fh, flush=True)
9090

9191
return ""
9292

pymake/pymake.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -502,14 +502,14 @@ def check_prefixes(s):
502502
s, ignore_failure, silent = check_prefixes(s)
503503

504504
if not silent and not args.silent:
505-
print(s)
505+
print(s,flush=True)
506506

507507
if args.dry_run:
508-
print(s)
508+
print(s, flush=True)
509509
continue
510510

511511
exit_code = 0
512-
ret = shell.execute(s, symtable)
512+
ret = shell.execute(s, symtable, capture=False)
513513

514514
#
515515
# !!! Run a Sub-Make !!!
@@ -535,7 +535,8 @@ def check_prefixes(s):
535535
ret.stdout = ""
536536

537537
exit_code = ret.exit_code
538-
print(ret.stdout,end="")
538+
# print(ret.stderr,end="")
539+
# print(ret.stdout,end="")
539540
if exit_code != 0:
540541
print("make:", ret.stderr, file=sys.stderr, end="")
541542
print("make: *** [%r: %s] Error %d %s" % (recipe.get_pos(), rule.target, exit_code, "(ignored)" if ignore_failure else ""), file=sys.stderr)

pymake/shell.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __init__(self):
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,21 +115,36 @@ 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+
118125
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-
)
126+
if capture:
127+
p = subprocess.run(cmd,
128+
shell=False,
129+
stdout=subprocess.PIPE,
130+
universal_newlines=True,
131+
check=False, # we'll check returncode ourselves
132+
env=env
133+
)
134+
else:
135+
p = subprocess.run(cmd,
136+
shell=False,
137+
universal_newlines=True,
138+
check=False, # we'll check returncode ourselves
139+
env=env
140+
)
141+
127142
logger.debug("shell ts=%f exit status=%r", ts, p.returncode)
128143
# if p.returncode != 0:
129144
# breakpoint()
130145
return_status.exit_code = p.returncode
131146
return_status.stdout = p.stdout
132-
return_status.stderr = p.stderr
147+
# return_status.stderr = p.stderr
133148
except OSError as err:
134149
logger.error("shell ts=%f error=\"%s\"", ts, err)
135150
return_status.exit_code = 127
@@ -165,9 +180,10 @@ def execute_tokens(token_list, symbol_table):
165180
symbol_table.allow_recursion()
166181

167182
# GNU Make returns one whitespace separated string, no CR/LF
168-
# "all other newlines are replaced by spaces." gnu_make.pdf
183+
# "If the result of the execution ends in a newline, that one newline is
184+
# removed; all other newlines are replaced by spaces." GNU Make PDF
169185
exe_result.stdout = exe_result.stdout.strip().replace("\n", " ")
170-
exe_result.stderr = exe_result.stderr.strip().replace("\n", " ")
186+
# exe_result.stderr = exe_result.stderr.strip().replace("\n", " ")
171187

172188
# save shell status
173189
pos = token_list[0].get_pos()
@@ -188,7 +204,7 @@ def execute_tokens(token_list, symbol_table):
188204
# otherwise report stderr (if any)
189205
if exe_result.stderr:
190206
logger.error("command at %r failed with exit_code=%d", pos, exe_result.exit_code)
191-
error_message(pos, exe_result.stderr)
207+
# error_message(pos, exe_result.stderr)
192208
else:
193209
logger.error("command at %r failed with exit_code=%d (but stderr empty)", pos, exe_result.exit_code)
194210

tests/test_export.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
# The subprocess.run_test() will raise error on non-zero exit.
1313

1414
def run_test(makefile, expect, extra_args=None, extra_env=None):
15-
output = run.pymake_string(makefile, extra_args, extra_env)
15+
output = run.gnumake_string(makefile, extra_args, extra_env)
1616
if _debug:
17-
print("pymake output=",output)
17+
print("gnumake output=>>>",output, "<<<")
1818
run.verify(output,expect)
19-
output = run.gnumake_string(makefile, extra_args, extra_env)
19+
output = run.pymake_string(makefile, extra_args, extra_env)
2020
if _debug:
21-
print("gnumake output=",output)
21+
print("pymake output=>>>",output, "<<<")
2222
run.verify(output,expect)
2323

2424
def test1():

tests/test_shellflags.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python3
2+
# SPDX-License-Identifier: GPL-2.0
3+
4+
import run
5+
6+
def test_simple():
7+
makefile="""
8+
$(info .SHELLFLAGS=$(.SHELLFLAGS))
9+
@:;@:
10+
"""
11+
a = run.gnumake_string(makefile)
12+
13+
b = run.pymake_string(makefile)
14+
15+
assert a==b
16+
17+
def test_x_flag():
18+
makefile="""
19+
.SHELLFLAGS+=-x
20+
$(info .SHELLFLAGS=$(.SHELLFLAGS))
21+
@:;@:
22+
"""
23+
a = run.gnumake_string(makefile)
24+
25+
b = run.pymake_string(makefile)
26+
27+
assert a==b
28+

0 commit comments

Comments
 (0)