diff options
author | Xavier Claessens <xavier.claessens@collabora.com> | 2022-09-02 20:46:29 -0400 |
---|---|---|
committer | Xavier Claessens <xclaesse@gmail.com> | 2022-09-22 11:29:03 -0400 |
commit | 59d561d4c18d3101e8eb03b37ed1ed71996a3b00 (patch) | |
tree | 6ec0c354bb2252c2e253b576d82b3bc8306a636d | |
parent | 3111ce6aae9ff686da418ce6f144d77c5d552201 (diff) | |
download | meson-59d561d4c18d3101e8eb03b37ed1ed71996a3b00.zip meson-59d561d4c18d3101e8eb03b37ed1ed71996a3b00.tar.gz meson-59d561d4c18d3101e8eb03b37ed1ed71996a3b00.tar.bz2 |
mlog: Add support for pager
It is useful to redirect some outputs, such as "meson configure" to a
pager (e.h. less). This is similar to most git commands.
-rw-r--r-- | mesonbuild/mconf.py | 4 | ||||
-rw-r--r-- | mesonbuild/mlog.py | 39 |
2 files changed, 42 insertions, 1 deletions
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 9f2d516..1feecbc 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -310,6 +310,7 @@ class Conf: def run(options): coredata.parse_cmd_line_options(options) builddir = os.path.abspath(os.path.realpath(options.builddir)) + mlog.start_pager() c = None try: c = Conf(builddir) @@ -336,4 +337,7 @@ def run(options): if c is not None and c.build is not None: mintro.write_meson_info_file(c.build, [e]) raise e + except BrokenPipeError: + # Pager quit before we wrote everything. + pass return 0 diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 93fd5db..cd06738 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -17,6 +17,8 @@ import io import sys import time import platform +import shlex +import subprocess import typing as T from contextlib import contextmanager from pathlib import Path @@ -80,6 +82,7 @@ log_errors_only = False # type: bool _in_ci = 'CI' in os.environ # type: bool _logged_once = set() # type: T.Set[T.Tuple[str, ...]] log_warnings_counter = 0 # type: int +log_pager: T.Optional['subprocess.Popen'] = None def disable() -> None: global log_disable_stdout @@ -115,6 +118,7 @@ def shutdown() -> T.Optional[str]: log_file = None exception_around_goer.close() return path + stop_pager() return None class AnsiDecorator: @@ -227,7 +231,8 @@ def force_print(*args: str, nested: bool, **kwargs: T.Any) -> None: # _Something_ is going to get printed. try: - print(raw, end='') + output = log_pager.stdin if log_pager else None + print(raw, end='', file=output) except UnicodeEncodeError: cleaned = raw.encode('ascii', 'replace').decode('ascii') print(cleaned, end='') @@ -397,3 +402,35 @@ def nested(name: str = '') -> T.Generator[None, None, None]: yield finally: log_depth.pop() + +def start_pager() -> None: + if not colorize_console(): + return + if 'PAGER' in os.environ: + pager_cmd = shlex.split(os.environ['PAGER']) + else: + # "R" : support color + # "X" : do not clear the screen when leaving the pager + # "F" : skip the pager if content fit into the screen + pager_cmd = ['less', '-RXF'] + global log_pager + assert log_pager is None + try: + log_pager = subprocess.Popen(pager_cmd, stdin=subprocess.PIPE, + text=True, encoding='utf-8') + except Exception as e: + # Ignore errors, unless it is a user defined pager. + if 'PAGER' in os.environ: + from .mesonlib import MesonException + raise MesonException(f'Failed to start pager: {str(e)}') + +def stop_pager() -> None: + global log_pager + if log_pager: + try: + log_pager.stdin.flush() + log_pager.stdin.close() + except BrokenPipeError: + pass + log_pager.wait() + log_pager = None |