Merge pull request #93479 from Repiteo/scons/better-colored-output

SCons: Improve colored output
This commit is contained in:
Rémi Verschelde 2024-12-17 16:18:41 +01:00
commit 182b4741ea
No known key found for this signature in database
GPG Key ID: C3336907360768E1
12 changed files with 150 additions and 255 deletions

View File

@ -98,7 +98,7 @@ repos:
name: doc-status name: doc-status
language: python language: python
entry: python doc/tools/doc_status.py 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 pass_filenames: false
files: ^(doc/classes|.*/doc_classes)/.*\.xml$ files: ^(doc/classes|.*/doc_classes)/.*\.xml$

View File

@ -58,31 +58,13 @@ import gles3_builders
import glsl_builders import glsl_builders
import methods import methods
import scu_builders 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 from platform_methods import architecture_aliases, architectures, compatibility_platform_aliases
if ARGUMENTS.get("target", "editor") == "editor": if ARGUMENTS.get("target", "editor") == "editor":
_helper_module("editor.editor_builders", "editor/editor_builders.py") _helper_module("editor.editor_builders", "editor/editor_builders.py")
_helper_module("editor.template_builders", "editor/template_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).
# <https://github.com/python/cpython/issues/73245>
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 # Scan possible build platforms
platform_list = [] # list of platforms platform_list = [] # list of platforms
@ -635,7 +617,7 @@ detect.configure(env)
print(f'Building for platform "{env["platform"]}", architecture "{env["arch"]}", target "{env["target"]}".') print(f'Building for platform "{env["platform"]}", architecture "{env["arch"]}", target "{env["target"]}".')
if env.dev_build: 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 # Enforce our minimal compiler version requirements
cc_version = methods.get_compiler_version(env) cc_version = methods.get_compiler_version(env)
@ -1111,10 +1093,10 @@ def print_elapsed_time():
time_centiseconds = round((elapsed_time_sec % 1) * 100) time_centiseconds = round((elapsed_time_sec % 1) * 100)
print( print(
"{}[Time elapsed: {}.{:02}]{}".format( "{}[Time elapsed: {}.{:02}]{}".format(
methods.ANSI.GRAY, Ansi.GRAY,
time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
time_centiseconds, time_centiseconds,
methods.ANSI.RESET, Ansi.RESET,
) )
) )

View File

@ -3,18 +3,21 @@
import fnmatch import fnmatch
import math import math
import os import os
import platform
import re import re
import sys import sys
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from typing import Dict, List, Set 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 # # Config #
################################################################################ ################################################################################
flags = { flags = {
"c": platform.platform() != "Windows", # Disable by default on windows, since we use ANSI escape codes "c": COLOR_SUPPORTED,
"b": False, "b": False,
"g": False, "g": False,
"s": False, "s": False,
@ -85,16 +88,16 @@ table_column_names = [
"Constructors", "Constructors",
] ]
colors = { colors = {
"name": [36], # cyan "name": [Ansi.CYAN], # cyan
"part_big_problem": [4, 31], # underline, red "part_big_problem": [Ansi.RED, Ansi.UNDERLINE], # underline, red
"part_problem": [31], # red "part_problem": [Ansi.RED], # red
"part_mostly_good": [33], # yellow "part_mostly_good": [Ansi.YELLOW], # yellow
"part_good": [32], # green "part_good": [Ansi.GREEN], # green
"url": [4, 34], # underline, blue "url": [Ansi.BLUE, Ansi.UNDERLINE], # underline, blue
"section": [1, 4], # bold, underline "section": [Ansi.BOLD, Ansi.UNDERLINE], # bold, underline
"state_off": [36], # cyan "state_off": [Ansi.CYAN], # cyan
"state_on": [1, 35], # bold, magenta/plum "state_on": [Ansi.BOLD, Ansi.MAGENTA], # bold, magenta/plum
"bold": [1], # bold "bold": [Ansi.BOLD], # bold
} }
overall_progress_description_weight = 10 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: def color(color: str, string: str) -> str:
if flags["c"] and terminal_supports_color(): color_format = "".join([str(x) for x in colors[color]])
color_format = "" return f"{color_format}{string}{Ansi.RESET}"
for code in colors[color]:
color_format += "\033[" + str(code) + "m"
return color_format + string + "\033[0m"
else:
return string
ansi_escape = re.compile(r"\x1b[^m]*m") ansi_escape = re.compile(r"\x1b[^m]*m")
@ -127,16 +125,6 @@ def nonescape_len(s: str) -> int:
return len(ansi_escape.sub("", s)) 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 # # Classes #
################################################################################ ################################################################################
@ -342,6 +330,7 @@ if flags["u"]:
table_column_names.append("Docs URL") table_column_names.append("Docs URL")
table_columns.append("url") table_columns.append("url")
toggle_color(flags["c"])
################################################################################ ################################################################################
# Help # # Help #

View File

@ -10,10 +10,10 @@ import xml.etree.ElementTree as ET
from collections import OrderedDict from collections import OrderedDict
from typing import Any, Dict, List, Optional, TextIO, Tuple, Union from typing import Any, Dict, List, Optional, TextIO, Tuple, Union
# Import hardcoded version information from version.py sys.path.insert(0, root_directory := os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
root_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")
sys.path.append(root_directory) # Include the root directory import version
import version # noqa: E402 from methods import Ansi, toggle_color
# $DOCS_URL/path/to/page.html(#fragment-tag) # $DOCS_URL/path/to/page.html(#fragment-tag)
GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$")
@ -90,8 +90,6 @@ BASE_STRINGS = [
] ]
strings_l10n: Dict[str, str] = {} strings_l10n: Dict[str, str] = {}
STYLES: Dict[str, str] = {}
CLASS_GROUPS: Dict[str, str] = { CLASS_GROUPS: Dict[str, str] = {
"global": "Globals", "global": "Globals",
"node": "Nodes", "node": "Nodes",
@ -699,31 +697,7 @@ def main() -> None:
) )
args = parser.parse_args() args = parser.parse_args()
should_color = bool(args.color or sys.stdout.isatty() or os.environ.get("CI")) toggle_color(args.color)
# Enable ANSI escape code support on Windows 10 and later (for colored console output).
# <https://github.com/python/cpython/issues/73245>
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 ""
# Retrieve heading translations for the given language. # Retrieve heading translations for the given language.
if not args.dry_run and args.lang != "en": 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 state.script_language_parity_check.hit_count > 0:
if not args.verbose: if not args.verbose:
print( 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: else:
print( 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(): for class_name in state.script_language_parity_check.hit_map.keys():
class_hits = state.script_language_parity_check.hit_map[class_name] 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: for context, error in class_hits:
print(f" - {error} in {format_context_name(context)}") print(f" - {error} in {format_context_name(context)}")
@ -853,24 +827,22 @@ def main() -> None:
if state.num_warnings >= 2: if state.num_warnings >= 2:
print( 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: elif state.num_warnings == 1:
print( 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: if state.num_errors >= 2:
print( 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: elif state.num_errors == 1:
print( print(f"{Ansi.RED}1 error was found in the class reference XML. Please check the messages above.{Ansi.RESET}")
f'{STYLES["red"]}1 error was found in the class reference XML. Please check the messages above.{STYLES["reset"]}'
)
if state.num_warnings == 0 and state.num_errors == 0: 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: if not args.dry_run:
print(f"Wrote reStructuredText files for each class to: {args.output}") print(f"Wrote reStructuredText files for each class to: {args.output}")
else: else:
@ -881,12 +853,12 @@ def main() -> None:
def print_error(error: str, state: State) -> 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 state.num_errors += 1
def print_warning(warning: str, state: State) -> None: 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 state.num_warnings += 1

View File

@ -10,26 +10,63 @@ from collections import OrderedDict
from enum import Enum from enum import Enum
from io import StringIO, TextIOWrapper from io import StringIO, TextIOWrapper
from pathlib import Path 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 # Get the "Godot" folder name ahead of time
base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/" base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/"
base_folder_only = os.path.basename(os.path.normpath(base_folder_path)) 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 # 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. # that if output is redirected to a file, it won't contain color codes.
# Colors are always enabled on continuous integration. # 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): def toggle_color(value: Optional[bool] = None) -> None:
global _scu_folders """
_scu_folders = scu_folders 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. Enum class for adding ansi colorcodes directly into strings.
Automatically converts values to strings representing their Automatically converts values to strings representing their
@ -39,6 +76,7 @@ class ANSI(Enum):
RESET = "\x1b[0m" RESET = "\x1b[0m"
BOLD = "\x1b[1m" BOLD = "\x1b[1m"
DIM = "\x1b[2m"
ITALIC = "\x1b[3m" ITALIC = "\x1b[3m"
UNDERLINE = "\x1b[4m" UNDERLINE = "\x1b[4m"
STRIKETHROUGH = "\x1b[9m" STRIKETHROUGH = "\x1b[9m"
@ -53,24 +91,49 @@ class ANSI(Enum):
CYAN = "\x1b[36m" CYAN = "\x1b[36m"
WHITE = "\x1b[37m" WHITE = "\x1b[37m"
PURPLE = "\x1b[38;5;93m" LIGHT_BLACK = "\x1b[90m"
PINK = "\x1b[38;5;206m" LIGHT_RED = "\x1b[91m"
ORANGE = "\x1b[38;5;214m" LIGHT_GREEN = "\x1b[92m"
GRAY = "\x1b[38;5;244m" 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: def __str__(self) -> str:
global _colorize global _can_color
return str(self.value) if _colorize else "" 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: def print_warning(*values: object) -> None:
"""Prints a warning message with formatting.""" """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: def print_error(*values: object) -> None:
"""Prints an error message with formatting.""" """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): def add_source_files_orig(self, sources, files, allow_gen=False):
@ -164,7 +227,7 @@ def get_version_info(module_version_string="", silent=False):
if os.getenv("BUILD_NAME") is not None: if os.getenv("BUILD_NAME") is not None:
build_name = str(os.getenv("BUILD_NAME")) build_name = str(os.getenv("BUILD_NAME"))
if not silent: if not silent:
print(f"Using custom build name: '{build_name}'.") print_info(f"Using custom build name: '{build_name}'.")
import version import version
@ -186,7 +249,7 @@ def get_version_info(module_version_string="", silent=False):
if os.getenv("GODOT_VERSION_STATUS") is not None: if os.getenv("GODOT_VERSION_STATUS") is not None:
version_info["status"] = str(os.getenv("GODOT_VERSION_STATUS")) version_info["status"] = str(os.getenv("GODOT_VERSION_STATUS"))
if not silent: 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. # Parse Git hash if we're in a Git repo.
githash = "" githash = ""
@ -442,7 +505,7 @@ def use_windows_spawn_fix(self, platform=None):
def no_verbose(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 # There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal. # Ctrl + clicked in the VS Code terminal.
@ -797,7 +860,7 @@ def show_progress(env):
# Progress reporting is not available in non-TTY environments since it messes with the output # 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 # (for example, when writing to a file). Ninja has its own progress/tracking tool that clashes
# with ours. # 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 return
NODE_COUNT_FILENAME = f"{base_folder_path}.scons_node_count" NODE_COUNT_FILENAME = f"{base_folder_path}.scons_node_count"
@ -1485,7 +1548,7 @@ def generate_copyright_header(filename: str) -> str:
""" """
filename = filename.split("/")[-1].ljust(MARGIN) filename = filename.split("/")[-1].ljust(MARGIN)
if len(filename) > 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 return TEMPLATE % filename

21
misc/scripts/install_d3d12_sdk_windows.py Executable file → Normal file
View File

@ -6,16 +6,9 @@ import subprocess
import sys import sys
import urllib.request import urllib.request
# Enable ANSI escape code support on Windows 10 and later (for colored console output). sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
# <https://github.com/python/cpython/issues/73245>
if sys.platform == "win32":
from ctypes import byref, c_int, windll
stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) from methods import Ansi
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)
# Base Godot dependencies path # Base Godot dependencies path
# If cross-compiling (no LOCALAPPDATA), we install in `bin` # If cross-compiling (no LOCALAPPDATA), we install in `bin`
@ -49,7 +42,7 @@ if not os.path.exists(deps_folder):
os.makedirs(deps_folder) os.makedirs(deps_folder)
# Mesa NIR # 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): if os.path.isfile(mesa_archive):
os.remove(mesa_archive) os.remove(mesa_archive)
print(f"Downloading Mesa NIR {mesa_filename} ...") print(f"Downloading Mesa NIR {mesa_filename} ...")
@ -76,7 +69,7 @@ if dlltool == "":
dlltool = shutil.which("x86_64-w64-mingw32-dlltool") or "" dlltool = shutil.which("x86_64-w64-mingw32-dlltool") or ""
has_mingw = gendef != "" and dlltool != "" 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): if os.path.isfile(pix_archive):
os.remove(pix_archive) os.remove(pix_archive)
print(f"Downloading WinPixEventRuntime {pix_version} ...") print(f"Downloading WinPixEventRuntime {pix_version} ...")
@ -107,7 +100,7 @@ else:
print(f"WinPixEventRuntime {pix_version} installed successfully.\n") print(f"WinPixEventRuntime {pix_version} installed successfully.\n")
# DirectX 12 Agility SDK # 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): if os.path.isfile(agility_sdk_archive):
os.remove(agility_sdk_archive) os.remove(agility_sdk_archive)
print(f"Downloading DirectX 12 Agility SDK {agility_sdk_version} ...") print(f"Downloading DirectX 12 Agility SDK {agility_sdk_version} ...")
@ -123,5 +116,5 @@ os.remove(agility_sdk_archive)
print(f"DirectX 12 Agility SDK {agility_sdk_version} installed successfully.\n") print(f"DirectX 12 Agility SDK {agility_sdk_version} installed successfully.\n")
# Complete message # Complete message
print(f'\x1b[92mAll Direct3D 12 SDK components were installed to "{deps_folder}" successfully!\x1b[0m') print(f'{Ansi.GREEN}All Direct3D 12 SDK components were installed to "{deps_folder}" successfully!{Ansi.RESET}')
print('\x1b[92mYou can now build Godot with Direct3D 12 support enabled by running "scons d3d12=yes".\x1b[0m') print(f'{Ansi.GREEN}You can now build Godot with Direct3D 12 support enabled by running "scons d3d12=yes".{Ansi.RESET}')

View File

@ -1,29 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
from misc.utility.scons_hints import *
import atexit import atexit
import sys
import time import time
from typing import TYPE_CHECKING
import methods import methods
# Enable ANSI escape code support on Windows 10 and later (for colored console output). if TYPE_CHECKING:
# <https://github.com/python/cpython/issues/73245> from misc.utility.scons_hints import *
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}")
# For the reference: # For the reference:
# - CCFLAGS are compilation flags shared between C and C++ # - 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) time_centiseconds = round((elapsed_time_sec % 1) * 100)
print( print(
"{}[Time elapsed: {}.{:02}]{}".format( "{}[Time elapsed: {}.{:02}]{}".format(
methods.ANSI.GRAY, methods.Ansi.GRAY,
time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
time_centiseconds, time_centiseconds,
methods.ANSI.RESET, methods.Ansi.RESET,
) )
) )

View File

@ -1,49 +1,13 @@
import os import os
import sys import sys
from enum import Enum
# Colors are disabled in non-TTY environments such as pipes. This means sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../"))
# 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"))
from methods import Ansi
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 ""
def no_verbose(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 # There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal. # Ctrl + clicked in the VS Code terminal.

View File

@ -1,29 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
from misc.utility.scons_hints import *
import atexit import atexit
import sys
import time import time
from typing import TYPE_CHECKING
import methods import methods
# Enable ANSI escape code support on Windows 10 and later (for colored console output). if TYPE_CHECKING:
# <https://github.com/python/cpython/issues/73245> from misc.utility.scons_hints import *
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}")
# For the reference: # For the reference:
# - CCFLAGS are compilation flags shared between C and C++ # - 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) time_centiseconds = round((elapsed_time_sec % 1) * 100)
print( print(
"{}[Time elapsed: {}.{:02}]{}".format( "{}[Time elapsed: {}.{:02}]{}".format(
methods.ANSI.GRAY, methods.Ansi.GRAY,
time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
time_centiseconds, time_centiseconds,
methods.ANSI.RESET, methods.Ansi.RESET,
) )
) )

View File

@ -1,49 +1,13 @@
import os import os
import sys import sys
from enum import Enum
# Colors are disabled in non-TTY environments such as pipes. This means sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../"))
# 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"))
from methods import Ansi
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 ""
def no_verbose(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 # There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal. # Ctrl + clicked in the VS Code terminal.

View File

@ -3,7 +3,7 @@ import platform
import sys import sys
from typing import TYPE_CHECKING 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 from platform_methods import detect_arch, validate_arch
if TYPE_CHECKING: if TYPE_CHECKING:
@ -492,7 +492,7 @@ def configure(env: "SConsEnvironment"):
else: else:
# The default crash handler depends on glibc, so if the host uses # The default crash handler depends on glibc, so if the host uses
# a different libc (BSD libc, musl), libexecinfo is required. # 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: else:
env.Append(CPPDEFINES=["CRASH_HANDLER_ENABLED"]) env.Append(CPPDEFINES=["CRASH_HANDLER_ENABLED"])

View File

@ -13,7 +13,7 @@ from emscripten_helpers import (
) )
from SCons.Util import WhereIs 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 from platform_methods import validate_arch
if TYPE_CHECKING: if TYPE_CHECKING:
@ -107,7 +107,7 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-sASSERTIONS=1"]) env.Append(LINKFLAGS=["-sASSERTIONS=1"])
if env.editor_build and env["initial_memory"] < 64: 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["initial_memory"] = 64
env.Append(LINKFLAGS=["-sINITIAL_MEMORY=%sMB" % env["initial_memory"]]) env.Append(LINKFLAGS=["-sINITIAL_MEMORY=%sMB" % env["initial_memory"]])