Skip to content

Commit 45f2d63

Browse files
committed
shell_helpers: create a check_stdout
./build-doc --dry-run was failing if asciidoctor is not installed Also catch BrokenPipeError on ./build --dry-run all | less if you quit less quickly.
1 parent 6a92995 commit 45f2d63

File tree

6 files changed

+94
-41
lines changed

6 files changed

+94
-41
lines changed

build

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import cli_function
77
import collections
88
import common
99
import copy
10+
import math
1011
import subprocess
1112
import shell_helpers
1213
from shell_helpers import LF
@@ -513,21 +514,27 @@ Which components to build. Default: qemu-buildroot
513514
['python3', '-m', 'pip', 'install', '--user', LF] +
514515
self.sh.add_newlines(sorted(python3_pkgs))
515516
)
516-
git_version_tuple = tuple(int(x) for x in subprocess.check_output(['git', '--version']).decode().split(' ')[-1].split('.'))
517517
git_cmd_common = [
518518
'git', LF,
519519
'submodule', LF,
520520
'update', LF,
521521
'--init', LF,
522522
'--recursive', LF,
523523
]
524-
if git_version_tuple >= (2, 9, 0):
525-
# https://stackoverflow.com/questions/26957237/how-to-make-git-clone-faster-with-multiple-threads/52327638#52327638
526-
git_cmd_common.extend(['--jobs', str(len(os.sched_getaffinity(0))), LF])
527-
if git_version_tuple >= (2, 10, 0):
528-
# * https://stackoverflow.com/questions/32944468/how-to-show-progress-for-submodule-fetching
529-
# * https://stackoverflow.com/questions/4640020/progress-indicator-for-git-clone
530-
git_cmd_common.extend(['--progress', LF])
524+
if self.env['dry_run']:
525+
git_version_tuple = (math.inf, math.inf, math.inf)
526+
else:
527+
git_version_tuple = tuple(
528+
int(x) for x in self.sh.check_output(['git', '--version']) \
529+
.split(' ')[-1].split('.')
530+
)
531+
if git_version_tuple >= (2, 9, 0):
532+
# https://stackoverflow.com/questions/26957237/how-to-make-git-clone-faster-with-multiple-threads/52327638#52327638
533+
git_cmd_common.extend(['--jobs', str(len(os.sched_getaffinity(0))), LF])
534+
if git_version_tuple >= (2, 10, 0):
535+
# * https://stackoverflow.com/questions/32944468/how-to-show-progress-for-submodule-fetching
536+
# * https://stackoverflow.com/questions/4640020/progress-indicator-for-git-clone
537+
git_cmd_common.extend(['--progress', LF])
531538
def submodule_ids_to_cmd(submodules):
532539
return self.sh.add_newlines([os.path.join(common.consts['submodules_dir'], x) for x in sorted(submodules)])
533540
if submodules:

build-doc

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#build-the-documentatio
3535

3636
# Check that all local files linked from README exist.
3737
external_link_re = re.compile('^https?://')
38-
for link in subprocess.check_output([
38+
for link in self.sh.check_output([
3939
os.path.join(asciidoctor_dir, 'extract-link-targets'),
4040
self.env['readme']
41-
]).decode().splitlines():
41+
]).splitlines():
4242
if not external_link_re.match(link):
4343
if not os.path.lexists(link):
4444
self.log_error('broken link: ' + link)
@@ -48,17 +48,18 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#build-the-documentatio
4848
header_ids = set()
4949
grep_line_location_re = re.compile('^(.*?:\d+):')
5050
grep_line_hash_re = re.compile('^([a-z0-9_-]+)')
51-
for header_id in subprocess.check_output([
51+
for header_id in self.sh.check_output([
5252
os.path.join(asciidoctor_dir, 'extract-header-ids'),
5353
self.env['readme']
54-
]).decode().splitlines():
54+
]).splitlines():
5555
header_ids.add(header_id)
56-
for grep_line in subprocess.check_output([
56+
for grep_line in self.sh.check_output([
5757
'git',
5858
'grep',
5959
'--fixed-strings',
60-
self.env['github_repo_id_url'] + '#'
61-
]).decode().splitlines():
60+
self.env['github_repo_id_url'] + '#',
61+
LF
62+
]).splitlines():
6263
url_index = grep_line.index(self.env['github_repo_id_url'])
6364
hash_start_index = url_index + len(self.env['github_repo_id_url'])
6465
if len(grep_line) > hash_start_index:

build-docker

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ See also: https://github.com/cirosantilli/linux-kernel-module-cheatTODO#ubuntu-g
2222
container_name = 'lkmc-guest'
2323
target_dir = os.path.join('/root', 'linux-kernel-module-cheat')
2424
os.makedirs(build_dir, exist_ok=True)
25-
containers = subprocess.check_output([
25+
containers = self.sh.check_output([
2626
'docker',
2727
'ps',
2828
'-a',

common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,7 +1066,7 @@ def assert_is_subpath(self, subpath, parents):
10661066
)
10671067

10681068
def get_elf_entry(self, elf_file_path):
1069-
readelf_header = subprocess.check_output([
1069+
readelf_header = self.sh.check_output([
10701070
self.get_toolchain_tool('readelf'),
10711071
'-h',
10721072
elf_file_path
@@ -1592,7 +1592,7 @@ def _build_one(
15921592
if 'cc_flags' in package:
15931593
cc_flags.extend(package['cc_flags'])
15941594
else:
1595-
pkg_config_output = subprocess.check_output([
1595+
pkg_config_output = self.sh.check_output([
15961596
self.env['pkg_config'],
15971597
'--cflags',
15981598
package_key

release-upload

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import json
44
import os
5-
import subprocess
65
import sys
76
import urllib.error
87

@@ -19,7 +18,7 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#release-upload
1918

2019
def timed_main(self):
2120
# https://stackoverflow.com/questions/3404936/show-which-git-tag-you-are-on
22-
tag = subprocess.check_output([
21+
tag = self.sh.check_output([
2322
'git',
2423
'describe',
2524
'--exact-match',

shell_helpers.py

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import base64
44
import distutils.file_util
5+
import io
56
import itertools
67
import os
78
import shlex
@@ -54,8 +55,12 @@ def _print_thread_safe(cls, string):
5455
The initial use case was test-gdb which must create a thread for GDB to run the program in parallel.
5556
'''
5657
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
5964

6065
def add_newlines(self, cmd):
6166
out = []
@@ -72,6 +77,17 @@ def base64_encode(self, string):
7277
def base64_decode(self, string):
7378
return base64.b64decode(string.encode()).decode()
7479

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+
7591
def chmod(self, path, add_rm_abs='+', mode_delta=stat.S_IXUSR):
7692
'''
7793
TODO extend further, shell print equivalent.
@@ -245,6 +261,8 @@ def run_cmd(
245261
extra_paths=None,
246262
delete_env=None,
247263
raise_on_failure=True,
264+
*,
265+
out_str=None,
248266
**kwargs
249267
):
250268
'''
@@ -261,6 +279,9 @@ def run_cmd(
261279
:param out_file: if not None, write the stdout and stderr of the command the file
262280
:type out_file: str
263281
282+
:param out_str: if not None, append the stdout and stderr string to this list
283+
:type out_str: Union(List,None)
284+
264285
:param show_stdout: wether to show stdout and stderr on the terminal or not
265286
:type show_stdout: bool
266287
@@ -270,7 +291,7 @@ def run_cmd(
270291
:return: exit status of the command
271292
:rtype: int
272293
'''
273-
if out_file is None:
294+
if out_file is None and out_str is None:
274295
if show_stdout:
275296
stdout = None
276297
stderr = None
@@ -299,14 +320,21 @@ def run_cmd(
299320
if key in env:
300321
del env[key]
301322
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+
)
303330

304331
# Otherwise, if called from a non-main thread:
305332
# ValueError: signal only works in main thread
306333
if threading.current_thread() == threading.main_thread():
307334
# Otherwise Ctrl + C gives:
308335
# - 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
310338
sigint_old = signal.getsignal(signal.SIGINT)
311339
signal.signal(signal.SIGINT, signal.SIG_IGN)
312340

@@ -320,23 +348,39 @@ def run_cmd(
320348
cmd = self.strip_newlines(cmd)
321349
if not self.dry_run:
322350
# 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:
337375
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())
340384
if threading.current_thread() == threading.main_thread():
341385
signal.signal(signal.SIGINT, sigint_old)
342386
#signal.signal(signal.SIGPIPE, sigpipe_old)
@@ -347,6 +391,8 @@ def run_cmd(
347391
raise e
348392
return returncode
349393
else:
394+
if not out_str is None:
395+
out_str.append('')
350396
return 0
351397

352398
def shlex_split(self, string):

0 commit comments

Comments
 (0)