Skip to content

Commit c581062

Browse files
Use colored ANSI console for error/warning messages
1 parent b075c5a commit c581062

File tree

1 file changed

+46
-25
lines changed

1 file changed

+46
-25
lines changed

bootstrap.py

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import traceback
3535
import urllib
3636
import ssl
37+
import ctypes
3738

3839
ssl._create_default_https_context = ssl._create_unverified_context
3940

@@ -69,6 +70,11 @@
6970

7071
BOOTSTRAP_VERSION = "1.0.7 (2025)"
7172

73+
class Colors:
74+
GREEN = '\033[92m'
75+
WARNING = '\033[91m'
76+
NORMAL = '\033[0m'
77+
7278
SRC_DIR_BASE = "src"
7379
ARCHIVE_DIR_BASE = "archives"
7480
SNAPSHOT_DIR_BASE = "snapshots"
@@ -93,15 +99,30 @@
9399
TOOL_COMMAND_TAR = "tar"
94100
TOOL_COMMAND_UNZIP = "unzip"
95101

102+
ansi_console = True
103+
96104
if platform.system() == "Windows":
97105
os.environ['CYGWIN'] = "nodosfilewarning"
106+
ansi_console = False
107+
if sys.getwindowsversion().major >= 10:
108+
ansi_console = True
109+
kernel32 = ctypes.windll.kernel32
110+
handle = kernel32.GetStdHandle(-11)
111+
mode = ctypes.c_uint32()
112+
kernel32.GetConsoleMode(handle, ctypes.byref(mode))
113+
kernel32.SetConsoleMode(handle, mode.value | 0x0004)
98114

99115
if not sys.version_info[0] >= 3:
100116
raise ValueError("I require Python 3.0 or a later version")
101117

102118
def log(string):
103119
print("--- " + string)
104120

121+
def warning(string):
122+
if ansi_console:
123+
print(Colors.WARNING, "--- " + string, Colors.NORMAL)
124+
else:
125+
print("--- " + string)
105126

106127
def dlog(string):
107128
if DEBUG_OUTPUT:
@@ -232,13 +253,13 @@ def extractFile(filename, target_dir):
232253
try:
233254
zfile = zipfile.ZipFile(filename)
234255
except zipfile.BadZipFile:
235-
print("WARNING: Invalid ZIP file '" + filename + "'")
256+
warning("WARNING: Invalid ZIP file '" + filename + "'")
236257
if os.path.exists(filename) and os.path.getsize(filename) == 0:
237-
print("WARNING: Zero-sized file was deleted. Run the script again.")
258+
warning("WARNING: Zero-sized file was deleted. Run the script again.")
238259
os.remove(filename)
239260
else:
240-
print("WARNING: Try deleting the cached file and run the script again.")
241-
raise RuntimeError("Updating to revision not implemented for SVN.") from None
261+
warning("WARNING: Try deleting the cached file and run the script again.")
262+
raise RuntimeError("Invalid ZIP file '" + filename + "'") from None
242263
extract_dir = os.path.commonprefix(zfile.namelist())
243264
hasFolder = False
244265
for fname in zfile.namelist():
@@ -331,7 +352,7 @@ def createArchiveFromDirectory(src_dir_name, archive_name, delete_existing_archi
331352

332353
def downloadSCP(hostname, username, path, target_dir):
333354
if not scp_available:
334-
log("ERROR: missing Python packages [paramiko, scp]; cannot continue.")
355+
warning("ERROR: missing Python packages [paramiko, scp]; cannot continue.")
335356
raise RuntimeError("Missing Python packages [paramiko, scp]; cannot continue.")
336357
ssh = paramiko.SSHClient()
337358
ssh.load_system_host_keys()
@@ -436,7 +457,7 @@ def applyPatchFile(patch_name, dir_name, pnum):
436457
arguments = argumentsBinary
437458
res = executeCommand(TOOL_COMMAND_PATCH + " --dry-run " + arguments, quiet = True)
438459
if res != 0:
439-
log("ERROR: patch application failure; has this patch already been applied?")
460+
warning("ERROR: patch application failure; has this patch already been applied?")
440461
executeCommand(TOOL_COMMAND_PATCH + " --dry-run " + arguments, printCommand = True)
441462
exit(255)
442463
else:
@@ -462,7 +483,7 @@ def findToolCommand(command, paths_to_search, required = False):
462483
break;
463484

464485
if required and not found:
465-
log("WARNING: command " + command + " not found, but required by script")
486+
warning("WARNING: command " + command + " not found, but required by script")
466487

467488
dlog("Found '" + command + "' as " + command_res)
468489
return command_res
@@ -472,16 +493,16 @@ def readJSONData(filename):
472493
try:
473494
json_data = open(filename).read()
474495
except:
475-
log("ERROR: Could not read JSON file: " + filename)
496+
warning("ERROR: Could not read JSON file: " + filename)
476497
return None
477498

478499
try:
479500
data = json.loads(json_data)
480501
except json.JSONDecodeError as e:
481-
log("ERROR: Could not parse JSON document: {}\n {} (line {}:{})\n".format(filename, e.msg, e.lineno, e.colno))
502+
warning("ERROR: Could not parse JSON document: {}\n {} (line {}:{})\n".format(filename, e.msg, e.lineno, e.colno))
482503
return None
483504
except:
484-
log("ERROR: Could not parse JSON document: " + filename)
505+
warning("ERROR: Could not parse JSON document: " + filename)
485506
return None
486507

487508
return data
@@ -651,11 +672,11 @@ def main(argv):
651672
opt_names += opt_names_local
652673
dlog("Name file contains: " + ", ".join(opt_names_local))
653674
except:
654-
log("ERROR: cannot parse name file '" + name_file + "'")
675+
warning("ERROR: cannot parse name file '" + name_file + "'")
655676
return -1
656677

657678
if force_fallback and not FALLBACK_URL:
658-
log("Error: cannot force usage of the fallback location without specifying a fallback URL")
679+
warning("Error: cannot force usage of the fallback location without specifying a fallback URL")
659680
return -1;
660681

661682
state_filename = os.path.join(os.path.dirname(os.path.splitext(bootstrap_filename)[0]), \
@@ -673,7 +694,7 @@ def main(argv):
673694
# some sanity checking
674695
for library in data:
675696
if library.get('name', None) is None:
676-
log("ERROR: Invalid schema: library object does not have a 'name'")
697+
warning("ERROR: Invalid schema: library object does not have a 'name'")
677698
return -1
678699

679700
# read local libraries data, if available
@@ -687,7 +708,7 @@ def main(argv):
687708
# some sanity checking
688709
for local_library in local_data:
689710
if local_library.get('name', None) is None:
690-
log("ERROR: Invalid schema: local library object does not have a 'name'")
711+
warning("ERROR: Invalid schema: local library object does not have a 'name'")
691712
return -1
692713

693714
# merge canonical and local library data, if applicable; local libraries take precedence
@@ -775,10 +796,10 @@ def main(argv):
775796
# download source
776797
if source is not None:
777798
if 'type' not in source:
778-
log("ERROR: Invalid schema for '" + name + "': 'source' object must have a 'type'")
799+
warning("ERROR: Invalid schema for '" + name + "': 'source' object must have a 'type'")
779800
return -1
780801
if 'url' not in source:
781-
log("ERROR: Invalid schema for '" + name + "': 'source' object must have a 'url'")
802+
warning("ERROR: Invalid schema for '" + name + "': 'source' object must have a 'url'")
782803
return -1
783804
src_type = source['type']
784805
src_url = source['url']
@@ -872,10 +893,10 @@ def main(argv):
872893
# post-processing
873894
if post is not None:
874895
if 'type' not in post:
875-
log("ERROR: Invalid schema for '" + name + "': 'postprocess' object must have a 'type'")
896+
warning("ERROR: Invalid schema for '" + name + "': 'postprocess' object must have a 'type'")
876897
return -1
877898
if 'file' not in post:
878-
log("ERROR: Invalid schema for '" + name + "': 'postprocess' object must have a 'file'")
899+
warning("ERROR: Invalid schema for '" + name + "': 'postprocess' object must have a 'file'")
879900
return -1
880901
post_type = post['type']
881902
post_file = post['file']
@@ -885,7 +906,7 @@ def main(argv):
885906
elif post_type == "script":
886907
runPythonScript(post_file)
887908
else:
888-
log("ERROR: Unknown post-processing type '" + post_type + "' for " + name)
909+
warning("ERROR: Unknown post-processing type '" + post_type + "' for " + name)
889910
return -1
890911

891912
# add to cached state
@@ -894,23 +915,23 @@ def main(argv):
894915
# write out cached state
895916
writeJSONData(sdata, state_filename)
896917
except urllib.error.URLError as e:
897-
log("ERROR: Failure to bootstrap library '" + name + "' (urllib.error.URLError: reason " + str(e.reason) + ")")
918+
warning("ERROR: Failure to bootstrap library '" + name + "' (urllib.error.URLError: reason " + str(e.reason) + ")")
898919
if break_on_first_error:
899920
exit(-1)
900921
traceback.print_exc()
901922
failed_libraries.append(name)
902923
except:
903-
log("ERROR: Failure to bootstrap library '" + name + "' (reason: " + str(sys.exc_info()[0]) + ")")
924+
warning("ERROR: Failure to bootstrap library '" + name + "' (reason: " + str(sys.exc_info()[0]) + ")")
904925
if break_on_first_error:
905926
exit(-1)
906927
traceback.print_exc()
907928
failed_libraries.append(name)
908929

909930
if failed_libraries:
910-
log("***************************************")
911-
log("FAILURE to bootstrap the following libraries:")
912-
log(', '.join(failed_libraries))
913-
log("***************************************")
931+
warning("***************************************")
932+
warning("FAILURE to bootstrap the following libraries:")
933+
warning(', '.join(failed_libraries))
934+
warning("***************************************")
914935
return -1
915936

916937
log("Finished")

0 commit comments

Comments
 (0)