diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77418d45b03c..bfde0f643e53 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -98,7 +98,7 @@ repos: name: doc-status language: python entry: python doc/tools/doc_status.py - args: [doc/classes, modules/*/doc_classes, platform/*/doc_classes] + args: [doc/classes, modules/*/doc_classes, platform/*/doc_classes, -c] pass_filenames: false files: ^(doc/classes|.*/doc_classes)/.*\.xml$ diff --git a/SConstruct b/SConstruct index fa324b2aa0fa..003ad6625b07 100644 --- a/SConstruct +++ b/SConstruct @@ -58,31 +58,13 @@ import gles3_builders import glsl_builders import methods import scu_builders -from methods import print_error, print_warning +from methods import Ansi, print_error, print_info, print_warning from platform_methods import architecture_aliases, architectures, compatibility_platform_aliases if ARGUMENTS.get("target", "editor") == "editor": _helper_module("editor.editor_builders", "editor/editor_builders.py") _helper_module("editor.template_builders", "editor/template_builders.py") -# Enable ANSI escape code support on Windows 10 and later (for colored console output). -# -if sys.stdout.isatty() and sys.platform == "win32": - try: - from ctypes import WinError, byref, windll # type: ignore - from ctypes.wintypes import DWORD # type: ignore - - stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) - mode = DWORD(0) - if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): - raise WinError() - mode = DWORD(mode.value | 4) - if not windll.kernel32.SetConsoleMode(stdout_handle, mode): - raise WinError() - except Exception as e: - methods._colorize = False - print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}") - # Scan possible build platforms platform_list = [] # list of platforms @@ -630,7 +612,7 @@ detect.configure(env) print(f'Building for platform "{env["platform"]}", architecture "{env["arch"]}", target "{env["target"]}".') if env.dev_build: - print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).") + print_info("Developer build, with debug optimization level and debug symbols (unless overridden).") # Enforce our minimal compiler version requirements cc_version = methods.get_compiler_version(env) @@ -1095,10 +1077,10 @@ def print_elapsed_time(): time_centiseconds = round((elapsed_time_sec % 1) * 100) print( "{}[Time elapsed: {}.{:02}]{}".format( - methods.ANSI.GRAY, + Ansi.GRAY, time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_centiseconds, - methods.ANSI.RESET, + Ansi.RESET, ) ) diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py index 57c03bfdeef9..dc52a38bddda 100755 --- a/doc/tools/doc_status.py +++ b/doc/tools/doc_status.py @@ -3,18 +3,21 @@ import fnmatch import math import os -import platform import re import sys import xml.etree.ElementTree as ET from typing import Dict, List, Set +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) + +from methods import COLOR_SUPPORTED, Ansi, toggle_color + ################################################################################ # Config # ################################################################################ flags = { - "c": platform.platform() != "Windows", # Disable by default on windows, since we use ANSI escape codes + "c": COLOR_SUPPORTED, "b": False, "g": False, "s": False, @@ -85,16 +88,16 @@ "Constructors", ] colors = { - "name": [36], # cyan - "part_big_problem": [4, 31], # underline, red - "part_problem": [31], # red - "part_mostly_good": [33], # yellow - "part_good": [32], # green - "url": [4, 34], # underline, blue - "section": [1, 4], # bold, underline - "state_off": [36], # cyan - "state_on": [1, 35], # bold, magenta/plum - "bold": [1], # bold + "name": [Ansi.CYAN], # cyan + "part_big_problem": [Ansi.RED, Ansi.UNDERLINE], # underline, red + "part_problem": [Ansi.RED], # red + "part_mostly_good": [Ansi.YELLOW], # yellow + "part_good": [Ansi.GREEN], # green + "url": [Ansi.BLUE, Ansi.UNDERLINE], # underline, blue + "section": [Ansi.BOLD, Ansi.UNDERLINE], # bold, underline + "state_off": [Ansi.CYAN], # cyan + "state_on": [Ansi.BOLD, Ansi.MAGENTA], # bold, magenta/plum + "bold": [Ansi.BOLD], # bold } overall_progress_description_weight = 10 @@ -111,13 +114,8 @@ def validate_tag(elem: ET.Element, tag: str) -> None: def color(color: str, string: str) -> str: - if flags["c"] and terminal_supports_color(): - color_format = "" - for code in colors[color]: - color_format += "\033[" + str(code) + "m" - return color_format + string + "\033[0m" - else: - return string + color_format = "".join([str(x) for x in colors[color]]) + return f"{color_format}{string}{Ansi.RESET}" ansi_escape = re.compile(r"\x1b[^m]*m") @@ -127,16 +125,6 @@ def nonescape_len(s: str) -> int: return len(ansi_escape.sub("", s)) -def terminal_supports_color(): - p = sys.platform - supported_platform = p != "Pocket PC" and (p != "win32" or "ANSICON" in os.environ) - - is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty() - if not supported_platform or not is_a_tty: - return False - return True - - ################################################################################ # Classes # ################################################################################ @@ -342,6 +330,7 @@ def generate_for_class(c: ET.Element): table_column_names.append("Docs URL") table_columns.append("url") +toggle_color(flags["c"]) ################################################################################ # Help # diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 51b6b9befccc..a10b5d6e7822 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -10,10 +10,10 @@ from collections import OrderedDict from typing import Any, Dict, List, Optional, TextIO, Tuple, Union -# Import hardcoded version information from version.py -root_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../") -sys.path.append(root_directory) # Include the root directory -import version # noqa: E402 +sys.path.insert(0, root_directory := os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) + +import version +from methods import Ansi, toggle_color # $DOCS_URL/path/to/page.html(#fragment-tag) GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") @@ -90,8 +90,6 @@ ] strings_l10n: Dict[str, str] = {} -STYLES: Dict[str, str] = {} - CLASS_GROUPS: Dict[str, str] = { "global": "Globals", "node": "Nodes", @@ -699,31 +697,7 @@ def main() -> None: ) args = parser.parse_args() - should_color = bool(args.color or sys.stdout.isatty() or os.environ.get("CI")) - - # Enable ANSI escape code support on Windows 10 and later (for colored console output). - # - if should_color and sys.stdout.isatty() and sys.platform == "win32": - try: - from ctypes import WinError, byref, windll # type: ignore - from ctypes.wintypes import DWORD # type: ignore - - stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) - mode = DWORD(0) - if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): - raise WinError() - mode = DWORD(mode.value | 4) - if not windll.kernel32.SetConsoleMode(stdout_handle, mode): - raise WinError() - except Exception: - should_color = False - - STYLES["red"] = "\x1b[91m" if should_color else "" - STYLES["green"] = "\x1b[92m" if should_color else "" - STYLES["yellow"] = "\x1b[93m" if should_color else "" - STYLES["bold"] = "\x1b[1m" if should_color else "" - STYLES["regular"] = "\x1b[22m" if should_color else "" - STYLES["reset"] = "\x1b[0m" if should_color else "" + toggle_color(args.color) # Retrieve heading translations for the given language. if not args.dry_run and args.lang != "en": @@ -834,16 +808,16 @@ def main() -> None: if state.script_language_parity_check.hit_count > 0: if not args.verbose: print( - f'{STYLES["yellow"]}{state.script_language_parity_check.hit_count} code samples failed parity check. Use --verbose to get more information.{STYLES["reset"]}' + f"{Ansi.YELLOW}{state.script_language_parity_check.hit_count} code samples failed parity check. Use --verbose to get more information.{Ansi.RESET}" ) else: print( - f'{STYLES["yellow"]}{state.script_language_parity_check.hit_count} code samples failed parity check:{STYLES["reset"]}' + f"{Ansi.YELLOW}{state.script_language_parity_check.hit_count} code samples failed parity check:{Ansi.RESET}" ) for class_name in state.script_language_parity_check.hit_map.keys(): class_hits = state.script_language_parity_check.hit_map[class_name] - print(f'{STYLES["yellow"]}- {len(class_hits)} hits in class "{class_name}"{STYLES["reset"]}') + print(f'{Ansi.YELLOW}- {len(class_hits)} hits in class "{class_name}"{Ansi.RESET}') for context, error in class_hits: print(f" - {error} in {format_context_name(context)}") @@ -853,24 +827,22 @@ def main() -> None: if state.num_warnings >= 2: print( - f'{STYLES["yellow"]}{state.num_warnings} warnings were found in the class reference XML. Please check the messages above.{STYLES["reset"]}' + f"{Ansi.YELLOW}{state.num_warnings} warnings were found in the class reference XML. Please check the messages above.{Ansi.RESET}" ) elif state.num_warnings == 1: print( - f'{STYLES["yellow"]}1 warning was found in the class reference XML. Please check the messages above.{STYLES["reset"]}' + f"{Ansi.YELLOW}1 warning was found in the class reference XML. Please check the messages above.{Ansi.RESET}" ) if state.num_errors >= 2: print( - f'{STYLES["red"]}{state.num_errors} errors were found in the class reference XML. Please check the messages above.{STYLES["reset"]}' + f"{Ansi.RED}{state.num_errors} errors were found in the class reference XML. Please check the messages above.{Ansi.RESET}" ) elif state.num_errors == 1: - print( - f'{STYLES["red"]}1 error was found in the class reference XML. Please check the messages above.{STYLES["reset"]}' - ) + print(f"{Ansi.RED}1 error was found in the class reference XML. Please check the messages above.{Ansi.RESET}") if state.num_warnings == 0 and state.num_errors == 0: - print(f'{STYLES["green"]}No warnings or errors found in the class reference XML.{STYLES["reset"]}') + print(f"{Ansi.GREEN}No warnings or errors found in the class reference XML.{Ansi.RESET}") if not args.dry_run: print(f"Wrote reStructuredText files for each class to: {args.output}") else: @@ -881,12 +853,12 @@ def main() -> None: def print_error(error: str, state: State) -> None: - print(f'{STYLES["red"]}{STYLES["bold"]}ERROR:{STYLES["regular"]} {error}{STYLES["reset"]}') + print(f"{Ansi.RED}{Ansi.BOLD}ERROR:{Ansi.REGULAR} {error}{Ansi.RESET}") state.num_errors += 1 def print_warning(warning: str, state: State) -> None: - print(f'{STYLES["yellow"]}{STYLES["bold"]}WARNING:{STYLES["regular"]} {warning}{STYLES["reset"]}') + print(f"{Ansi.YELLOW}{Ansi.BOLD}WARNING:{Ansi.REGULAR} {warning}{Ansi.RESET}") state.num_warnings += 1 diff --git a/methods.py b/methods.py index be290f812800..a00b9c6ee42d 100644 --- a/methods.py +++ b/methods.py @@ -10,26 +10,63 @@ from enum import Enum from io import StringIO, TextIOWrapper from pathlib import Path -from typing import Generator, List, Optional, Union, cast +from typing import Final, Generator, List, Optional, Union, cast # Get the "Godot" folder name ahead of time base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/" base_folder_only = os.path.basename(os.path.normpath(base_folder_path)) -# Listing all the folders we have converted -# for SCU in scu_builders.py -_scu_folders = set() + +################################################################################ +# COLORIZE +################################################################################ + +IS_CI: Final[bool] = bool(os.environ.get("CI")) +IS_TTY: Final[bool] = bool(sys.stdout.isatty()) + + +def _color_supported() -> bool: + """ + Enables ANSI escape code support on Windows 10 and later (for colored console output). + See here: https://github.com/python/cpython/issues/73245 + """ + if sys.platform == "win32" and IS_TTY: + try: + from ctypes import WinError, byref, windll # type: ignore + from ctypes.wintypes import DWORD # type: ignore + + stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) + mode = DWORD(0) + if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): + raise WinError() + mode = DWORD(mode.value | 4) + if not windll.kernel32.SetConsoleMode(stdout_handle, mode): + raise WinError() + except (TypeError, OSError) as e: + print(f"Failed to enable ANSI escape code support, disabling color output.\n{e}", file=sys.stderr) + return False + + return IS_TTY or IS_CI + + # Colors are disabled in non-TTY environments such as pipes. This means # that if output is redirected to a file, it won't contain color codes. # Colors are always enabled on continuous integration. -_colorize = bool(sys.stdout.isatty() or os.environ.get("CI")) +COLOR_SUPPORTED: Final[bool] = _color_supported() +_can_color: bool = COLOR_SUPPORTED -def set_scu_folders(scu_folders): - global _scu_folders - _scu_folders = scu_folders +def toggle_color(value: Optional[bool] = None) -> None: + """ + Explicitly toggle color codes, regardless of support. + + - `value`: An optional boolean to explicitly set the color + state instead of toggling. + """ + global _can_color + _can_color = value if value is not None else not _can_color -class ANSI(Enum): +class Ansi(Enum): """ Enum class for adding ansi colorcodes directly into strings. Automatically converts values to strings representing their @@ -39,6 +76,7 @@ class ANSI(Enum): RESET = "\x1b[0m" BOLD = "\x1b[1m" + DIM = "\x1b[2m" ITALIC = "\x1b[3m" UNDERLINE = "\x1b[4m" STRIKETHROUGH = "\x1b[9m" @@ -53,24 +91,49 @@ class ANSI(Enum): CYAN = "\x1b[36m" WHITE = "\x1b[37m" - PURPLE = "\x1b[38;5;93m" - PINK = "\x1b[38;5;206m" - ORANGE = "\x1b[38;5;214m" - GRAY = "\x1b[38;5;244m" + LIGHT_BLACK = "\x1b[90m" + LIGHT_RED = "\x1b[91m" + LIGHT_GREEN = "\x1b[92m" + LIGHT_YELLOW = "\x1b[93m" + LIGHT_BLUE = "\x1b[94m" + LIGHT_MAGENTA = "\x1b[95m" + LIGHT_CYAN = "\x1b[96m" + LIGHT_WHITE = "\x1b[97m" + + GRAY = LIGHT_BLACK if IS_CI else BLACK + """ + Special case. GitHub Actions doesn't convert `BLACK` to gray as expected, but does convert `LIGHT_BLACK`. + By implementing `GRAY`, we handle both cases dynamically, while still allowing for explicit values if desired. + """ def __str__(self) -> str: - global _colorize - return str(self.value) if _colorize else "" + global _can_color + return str(self.value) if _can_color else "" + + +def print_info(*values: object) -> None: + """Prints a informational message with formatting.""" + print(f"{Ansi.GRAY}{Ansi.BOLD}INFO:{Ansi.REGULAR}", *values, Ansi.RESET) def print_warning(*values: object) -> None: """Prints a warning message with formatting.""" - print(f"{ANSI.YELLOW}{ANSI.BOLD}WARNING:{ANSI.REGULAR}", *values, ANSI.RESET, file=sys.stderr) + print(f"{Ansi.YELLOW}{Ansi.BOLD}WARNING:{Ansi.REGULAR}", *values, Ansi.RESET, file=sys.stderr) def print_error(*values: object) -> None: """Prints an error message with formatting.""" - print(f"{ANSI.RED}{ANSI.BOLD}ERROR:{ANSI.REGULAR}", *values, ANSI.RESET, file=sys.stderr) + print(f"{Ansi.RED}{Ansi.BOLD}ERROR:{Ansi.REGULAR}", *values, Ansi.RESET, file=sys.stderr) + + +# Listing all the folders we have converted +# for SCU in scu_builders.py +_scu_folders = set() + + +def set_scu_folders(scu_folders): + global _scu_folders + _scu_folders = scu_folders def add_source_files_orig(self, sources, files, allow_gen=False): @@ -163,7 +226,7 @@ def get_version_info(module_version_string="", silent=False): if os.getenv("BUILD_NAME") is not None: build_name = str(os.getenv("BUILD_NAME")) if not silent: - print(f"Using custom build name: '{build_name}'.") + print_info(f"Using custom build name: '{build_name}'.") import version @@ -185,7 +248,7 @@ def get_version_info(module_version_string="", silent=False): if os.getenv("GODOT_VERSION_STATUS") is not None: version_info["status"] = str(os.getenv("GODOT_VERSION_STATUS")) if not silent: - print(f"Using version status '{version_info['status']}', overriding the original '{version.status}'.") + print_info(f"Using version status '{version_info['status']}', overriding the original '{version.status}'.") # Parse Git hash if we're in a Git repo. githash = "" @@ -441,7 +504,7 @@ def mySpawn(sh, escape, cmd, args, env): def no_verbose(env): - colors = [ANSI.BLUE, ANSI.BOLD, ANSI.REGULAR, ANSI.RESET] + colors = [Ansi.BLUE, Ansi.BOLD, Ansi.REGULAR, Ansi.RESET] # There is a space before "..." to ensure that source file names can be # Ctrl + clicked in the VS Code terminal. @@ -792,7 +855,7 @@ def show_progress(env): # Progress reporting is not available in non-TTY environments since it messes with the output # (for example, when writing to a file). Ninja has its own progress/tracking tool that clashes # with ours. - if not env["progress"] or not sys.stdout.isatty() or env["ninja"]: + if not env["progress"] or not IS_TTY or env["ninja"]: return NODE_COUNT_FILENAME = f"{base_folder_path}.scons_node_count" @@ -1480,7 +1543,7 @@ def generate_copyright_header(filename: str) -> str: """ filename = filename.split("/")[-1].ljust(MARGIN) if len(filename) > MARGIN: - print(f'WARNING: Filename "{filename}" too large for copyright header.') + print_warning(f'Filename "{filename}" too large for copyright header.') return TEMPLATE % filename diff --git a/misc/scripts/install_d3d12_sdk_windows.py b/misc/scripts/install_d3d12_sdk_windows.py old mode 100755 new mode 100644 index 2e5e4fce181d..fa9ce780513f --- a/misc/scripts/install_d3d12_sdk_windows.py +++ b/misc/scripts/install_d3d12_sdk_windows.py @@ -6,16 +6,9 @@ import sys import urllib.request -# Enable ANSI escape code support on Windows 10 and later (for colored console output). -# -if sys.platform == "win32": - from ctypes import byref, c_int, windll +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) - stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) - mode = c_int(0) - windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode)) - mode = c_int(mode.value | 4) - windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode) +from methods import Ansi # Base Godot dependencies path # If cross-compiling (no LOCALAPPDATA), we install in `bin` @@ -49,7 +42,7 @@ os.makedirs(deps_folder) # Mesa NIR -print("\x1b[1m[1/3] Mesa NIR\x1b[0m") +print(f"{Ansi.BOLD}[1/3] Mesa NIR{Ansi.RESET}") if os.path.isfile(mesa_archive): os.remove(mesa_archive) print(f"Downloading Mesa NIR {mesa_filename} ...") @@ -76,7 +69,7 @@ dlltool = shutil.which("x86_64-w64-mingw32-dlltool") or "" has_mingw = gendef != "" and dlltool != "" -print("\x1b[1m[2/3] WinPixEventRuntime\x1b[0m") +print(f"{Ansi.BOLD}[2/3] WinPixEventRuntime{Ansi.RESET}") if os.path.isfile(pix_archive): os.remove(pix_archive) print(f"Downloading WinPixEventRuntime {pix_version} ...") @@ -107,7 +100,7 @@ print(f"WinPixEventRuntime {pix_version} installed successfully.\n") # DirectX 12 Agility SDK -print("\x1b[1m[3/3] DirectX 12 Agility SDK\x1b[0m") +print(f"{Ansi.BOLD}[3/3] DirectX 12 Agility SDK{Ansi.RESET}") if os.path.isfile(agility_sdk_archive): os.remove(agility_sdk_archive) print(f"Downloading DirectX 12 Agility SDK {agility_sdk_version} ...") @@ -123,5 +116,5 @@ print(f"DirectX 12 Agility SDK {agility_sdk_version} installed successfully.\n") # Complete message -print(f'\x1b[92mAll Direct3D 12 SDK components were installed to "{deps_folder}" successfully!\x1b[0m') -print('\x1b[92mYou can now build Godot with Direct3D 12 support enabled by running "scons d3d12=yes".\x1b[0m') +print(f'{Ansi.GREEN}All Direct3D 12 SDK components were installed to "{deps_folder}" successfully!{Ansi.RESET}') +print(f'{Ansi.GREEN}You can now build Godot with Direct3D 12 support enabled by running "scons d3d12=yes".{Ansi.RESET}') diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index fb705eaa50a8..0d6ac4b7b24f 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -1,29 +1,13 @@ #!/usr/bin/env python -from misc.utility.scons_hints import * import atexit -import sys import time +from typing import TYPE_CHECKING import methods -# Enable ANSI escape code support on Windows 10 and later (for colored console output). -# -if sys.stdout.isatty() and sys.platform == "win32": - try: - from ctypes import WinError, byref, windll # type: ignore - from ctypes.wintypes import DWORD # type: ignore - - stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) - mode = DWORD(0) - if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): - raise WinError() - mode = DWORD(mode.value | 4) - if not windll.kernel32.SetConsoleMode(stdout_handle, mode): - raise WinError() - except Exception as e: - methods._colorize = False - methods.print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}") +if TYPE_CHECKING: + from misc.utility.scons_hints import * # For the reference: # - CCFLAGS are compilation flags shared between C and C++ @@ -793,10 +777,10 @@ def print_elapsed_time(): time_centiseconds = round((elapsed_time_sec % 1) * 100) print( "{}[Time elapsed: {}.{:02}]{}".format( - methods.ANSI.GRAY, + methods.Ansi.GRAY, time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_centiseconds, - methods.ANSI.RESET, + methods.Ansi.RESET, ) ) diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py index 43bf56abfeee..f29d3dfb3e5d 100644 --- a/modules/text_server_adv/gdextension_build/methods.py +++ b/modules/text_server_adv/gdextension_build/methods.py @@ -1,49 +1,13 @@ import os import sys -from enum import Enum -# Colors are disabled in non-TTY environments such as pipes. This means -# that if output is redirected to a file, it won't contain color codes. -# Colors are always enabled on continuous integration. -_colorize = bool(sys.stdout.isatty() or os.environ.get("CI")) +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../")) - -class ANSI(Enum): - """ - Enum class for adding ansi colorcodes directly into strings. - Automatically converts values to strings representing their - internal value, or an empty string in a non-colorized scope. - """ - - RESET = "\x1b[0m" - - BOLD = "\x1b[1m" - ITALIC = "\x1b[3m" - UNDERLINE = "\x1b[4m" - STRIKETHROUGH = "\x1b[9m" - REGULAR = "\x1b[22;23;24;29m" - - BLACK = "\x1b[30m" - RED = "\x1b[31m" - GREEN = "\x1b[32m" - YELLOW = "\x1b[33m" - BLUE = "\x1b[34m" - MAGENTA = "\x1b[35m" - CYAN = "\x1b[36m" - WHITE = "\x1b[37m" - - PURPLE = "\x1b[38;5;93m" - PINK = "\x1b[38;5;206m" - ORANGE = "\x1b[38;5;214m" - GRAY = "\x1b[38;5;244m" - - def __str__(self) -> str: - global _colorize - return str(self.value) if _colorize else "" +from methods import Ansi def no_verbose(env): - colors = [ANSI.BLUE, ANSI.BOLD, ANSI.REGULAR, ANSI.RESET] + colors = [Ansi.BLUE, Ansi.BOLD, Ansi.REGULAR, Ansi.RESET] # There is a space before "..." to ensure that source file names can be # Ctrl + clicked in the VS Code terminal. diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index dc849d58149a..72606760dff0 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -1,29 +1,13 @@ #!/usr/bin/env python -from misc.utility.scons_hints import * import atexit -import sys import time +from typing import TYPE_CHECKING import methods -# Enable ANSI escape code support on Windows 10 and later (for colored console output). -# -if sys.stdout.isatty() and sys.platform == "win32": - try: - from ctypes import WinError, byref, windll # type: ignore - from ctypes.wintypes import DWORD # type: ignore - - stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) - mode = DWORD(0) - if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): - raise WinError() - mode = DWORD(mode.value | 4) - if not windll.kernel32.SetConsoleMode(stdout_handle, mode): - raise WinError() - except Exception as e: - methods._colorize = False - methods.print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}") +if TYPE_CHECKING: + from misc.utility.scons_hints import * # For the reference: # - CCFLAGS are compilation flags shared between C and C++ @@ -335,10 +319,10 @@ def print_elapsed_time(): time_centiseconds = round((elapsed_time_sec % 1) * 100) print( "{}[Time elapsed: {}.{:02}]{}".format( - methods.ANSI.GRAY, + methods.Ansi.GRAY, time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_centiseconds, - methods.ANSI.RESET, + methods.Ansi.RESET, ) ) diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py index 43bf56abfeee..f29d3dfb3e5d 100644 --- a/modules/text_server_fb/gdextension_build/methods.py +++ b/modules/text_server_fb/gdextension_build/methods.py @@ -1,49 +1,13 @@ import os import sys -from enum import Enum -# Colors are disabled in non-TTY environments such as pipes. This means -# that if output is redirected to a file, it won't contain color codes. -# Colors are always enabled on continuous integration. -_colorize = bool(sys.stdout.isatty() or os.environ.get("CI")) +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../")) - -class ANSI(Enum): - """ - Enum class for adding ansi colorcodes directly into strings. - Automatically converts values to strings representing their - internal value, or an empty string in a non-colorized scope. - """ - - RESET = "\x1b[0m" - - BOLD = "\x1b[1m" - ITALIC = "\x1b[3m" - UNDERLINE = "\x1b[4m" - STRIKETHROUGH = "\x1b[9m" - REGULAR = "\x1b[22;23;24;29m" - - BLACK = "\x1b[30m" - RED = "\x1b[31m" - GREEN = "\x1b[32m" - YELLOW = "\x1b[33m" - BLUE = "\x1b[34m" - MAGENTA = "\x1b[35m" - CYAN = "\x1b[36m" - WHITE = "\x1b[37m" - - PURPLE = "\x1b[38;5;93m" - PINK = "\x1b[38;5;206m" - ORANGE = "\x1b[38;5;214m" - GRAY = "\x1b[38;5;244m" - - def __str__(self) -> str: - global _colorize - return str(self.value) if _colorize else "" +from methods import Ansi def no_verbose(env): - colors = [ANSI.BLUE, ANSI.BOLD, ANSI.REGULAR, ANSI.RESET] + colors = [Ansi.BLUE, Ansi.BOLD, Ansi.REGULAR, Ansi.RESET] # There is a space before "..." to ensure that source file names can be # Ctrl + clicked in the VS Code terminal. diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 6c9f33684901..a421d5697da9 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -3,7 +3,7 @@ import sys from typing import TYPE_CHECKING -from methods import get_compiler_version, print_error, print_warning, using_gcc +from methods import get_compiler_version, print_error, print_info, print_warning, using_gcc from platform_methods import detect_arch, validate_arch if TYPE_CHECKING: @@ -492,7 +492,7 @@ def configure(env: "SConsEnvironment"): else: # The default crash handler depends on glibc, so if the host uses # a different libc (BSD libc, musl), libexecinfo is required. - print("Note: Using `execinfo=no` disables the crash handler on platforms where glibc is missing.") + print_info("Using `execinfo=no` disables the crash handler on platforms where glibc is missing.") else: env.Append(CPPDEFINES=["CRASH_HANDLER_ENABLED"]) diff --git a/platform/web/detect.py b/platform/web/detect.py index 25a5bbe5a596..0620c93f4914 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -13,7 +13,7 @@ ) from SCons.Util import WhereIs -from methods import get_compiler_version, print_error, print_warning +from methods import get_compiler_version, print_error, print_info, print_warning from platform_methods import validate_arch if TYPE_CHECKING: @@ -107,7 +107,7 @@ def configure(env: "SConsEnvironment"): env.Append(LINKFLAGS=["-sASSERTIONS=1"]) if env.editor_build and env["initial_memory"] < 64: - print("Note: Forcing `initial_memory=64` as it is required for the web editor.") + print_info("Forcing `initial_memory=64` as it is required for the web editor.") env["initial_memory"] = 64 env.Append(LINKFLAGS=["-sINITIAL_MEMORY=%sMB" % env["initial_memory"]])