diff options
author | John Parent <john.parent@kitware.com> | 2022-11-30 18:45:06 -0500 |
---|---|---|
committer | Peter Scheibel <scheibel1@llnl.gov> | 2023-01-09 09:14:17 -0800 |
commit | 9f0bb4301f0514a9367ca9289ceb6a5cf2b3bfa0 (patch) | |
tree | 10a7508248afb7983153c93f5526caa98e3793e8 | |
parent | 288e72814491c9ea579f5baa4c039c69ba2a0ca2 (diff) | |
download | spack-9f0bb4301f0514a9367ca9289ceb6a5cf2b3bfa0.tar.gz spack-9f0bb4301f0514a9367ca9289ceb6a5cf2b3bfa0.tar.bz2 spack-9f0bb4301f0514a9367ca9289ceb6a5cf2b3bfa0.tar.xz spack-9f0bb4301f0514a9367ca9289ceb6a5cf2b3bfa0.zip |
Support ASCI control
Windows CMD prompt does not automatically support ASCI color control
characters on the console from Python. Enable this behavior by
accessing the current console and allowing the interpreation of ASCI
control characters from Python via the win32 API.
-rw-r--r-- | lib/spack/llnl/util/tty/color.py | 58 | ||||
-rw-r--r-- | lib/spack/spack/main.py | 4 |
2 files changed, 62 insertions, 0 deletions
diff --git a/lib/spack/llnl/util/tty/color.py b/lib/spack/llnl/util/tty/color.py index 83e8316914..a468d19634 100644 --- a/lib/spack/llnl/util/tty/color.py +++ b/lib/spack/llnl/util/tty/color.py @@ -107,6 +107,64 @@ color_when_values = {"always": True, "auto": None, "never": False} _force_color = None +def try_enable_terminal_color_on_windows(): + """Turns coloring in Windows terminal by enabling VTP in Windows consoles (CMD/PWSH/CONHOST) + Method based on the link below + https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing + + Note: No-op on non windows platforms + """ + if sys.platform == "win32": + import ctypes + import msvcrt + from ctypes import wintypes + + try: + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + DISABLE_NEWLINE_AUTO_RETURN = 0x0008 + kernel32 = ctypes.WinDLL("kernel32") + + def _err_check(result, func, args): + if not result: + raise ctypes.WinError(ctypes.get_last_error()) + return args + + kernel32.GetConsoleMode.errcheck = _err_check + kernel32.GetConsoleMode.argtypes = ( + wintypes.HANDLE, # hConsoleHandle, i.e. GetStdHandle output type + ctypes.POINTER(wintypes.DWORD), # result of GetConsoleHandle + ) + kernel32.SetConsoleMode.errcheck = _err_check + kernel32.SetConsoleMode.argtypes = ( + wintypes.HANDLE, # hConsoleHandle, i.e. GetStdHandle output type + wintypes.DWORD, # result of GetConsoleHandle + ) + # Use conout$ here to handle a redirectired stdout/get active console associated + # with spack + with open(r"\\.\CONOUT$", "w") as conout: + # Link above would use kernel32.GetStdHandle(-11) however this would not handle + # a redirected stdout appropriately, so we always refer to the current CONSOLE out + # which is defined as conout$ on Windows. + # linked example is follow more or less to the letter beyond this point + con_handle = msvcrt.get_osfhandle(conout.fileno()) + dw_orig_mode = wintypes.DWORD() + kernel32.GetConsoleMode(con_handle, ctypes.byref(dw_orig_mode)) + dw_new_mode_request = ( + ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN + ) + dw_new_mode = dw_new_mode_request | dw_orig_mode.value + kernel32.SetConsoleMode(con_handle, wintypes.DWORD(dw_new_mode)) + except OSError: + # We failed to enable color support for associated console + # report and move on but spack will no longer attempt to + # color + global _force_color + _force_color = False + from . import debug + + debug("Unable to support color on Windows terminal") + + def _color_when_value(when): """Raise a ValueError for an invalid color setting. diff --git a/lib/spack/spack/main.py b/lib/spack/spack/main.py index b95a8562eb..8153839abc 100644 --- a/lib/spack/spack/main.py +++ b/lib/spack/spack/main.py @@ -605,6 +605,10 @@ def setup_main_options(args): for config_var in args.config_vars or []: spack.config.add(fullpath=config_var, scope="command_line") + # On Windows10 console handling for ASCI/VT100 sequences is not + # on by default. Turn on before we try to write to console + # with color + color.try_enable_terminal_color_on_windows() # when to use color (takes always, auto, or never) color.set_color_when(args.color) |