aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2022-09-02 20:46:29 -0400
committerXavier Claessens <xclaesse@gmail.com>2022-09-22 11:29:03 -0400
commit59d561d4c18d3101e8eb03b37ed1ed71996a3b00 (patch)
tree6ec0c354bb2252c2e253b576d82b3bc8306a636d
parent3111ce6aae9ff686da418ce6f144d77c5d552201 (diff)
downloadmeson-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.py4
-rw-r--r--mesonbuild/mlog.py39
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