diff --git a/.github/workflows/check_board.yml b/.github/workflows/check_board.yml new file mode 100644 index 000000000..bd0e72344 --- /dev/null +++ b/.github/workflows/check_board.yml @@ -0,0 +1,27 @@ +name: Check Board Headers + +on: + push: + paths: + - 'src/boards/include/boards/**' + - 'tools/check_board_header.py' + - 'tools/check_all_board_headers.sh' + - '.github/workflows/check_board.yml' + pull_request: + paths: + - 'src/boards/include/boards/**' + - 'tools/check_board_header.py' + - 'tools/check_all_board_headers.sh' + - '.github/workflows/check_board.yml' + +jobs: + check-board-headers: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Board Headers + run: | + tools/check_all_board_headers.sh diff --git a/tools/check_all_board_headers.sh b/tools/check_all_board_headers.sh index bdbe3f773..290921791 100755 --- a/tools/check_all_board_headers.sh +++ b/tools/check_all_board_headers.sh @@ -1,7 +1,12 @@ #!/bin/bash + +EXIT_CODE=0 + for HEADER in src/boards/include/boards/*.h; do tools/check_board_header.py $HEADER if [[ $? -ne 0 ]]; then - break + EXIT_CODE=1 fi done + +exit $EXIT_CODE diff --git a/tools/check_board_header.py b/tools/check_board_header.py index da7b53408..5a5c84e63 100755 --- a/tools/check_board_header.py +++ b/tools/check_board_header.py @@ -20,6 +20,14 @@ from collections import namedtuple +if sys.version_info < (3, 11): + # Python <3.11 doesn't have ExceptionGroup, so define a simple one + class ExceptionGroup(Exception): + def __init__(self, message, errors): + message += "\n" + "\n".join(str(e) for e in errors) + super().__init__(message) + + # warnings off by default, because some boards use the same pin for multiple purposes show_warnings = False @@ -65,6 +73,7 @@ def list_to_string_with(lst, joiner): def read_defines_from(header_file, defines_dict): + errors = [] with open(header_file) as fh: last_ifndef = None last_ifndef_lineno = -1 @@ -84,7 +93,7 @@ def read_defines_from(header_file, defines_dict): if m: name = m.group(1) value = m.group(2) - raise Exception("{}:{} \"// {} {}={}\" should be replaced with \"{}({}, {})\"".format(board_header, lineno, old_comment, name, value, new_macro, name, value)) + errors.append(Exception("{}:{} \"// {} {}={}\" should be replaced with \"{}({}, {})\"".format(board_header, lineno, old_comment, name, value, new_macro, name, value))) # look for "pico_board_cmake_set(BLAH_BLAH, 42)" m = re.match(r"^\s*pico_board_cmake_set\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*,\s*(.*)\s*\)\s*$", line) @@ -94,11 +103,11 @@ def read_defines_from(header_file, defines_dict): value = m.group(2) # check all uppercase if name != name.upper(): - raise Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name))) # check for multiply-defined values if name in cmake_settings: if cmake_settings[name].value != value: - raise Exception("{}:{} Conflicting values for pico_board_cmake_set({}) ({} and {})".format(board_header, lineno, name, cmake_settings[name].value, value)) + errors.append(Exception("{}:{} Conflicting values for pico_board_cmake_set({}) ({} and {})".format(board_header, lineno, name, cmake_settings[name].value, value))) else: if show_warnings: warnings.warn("{}:{} Multiple values for pico_board_cmake_set({}) ({} and {})".format(board_header, lineno, name, cmake_settings[name].value, value)) @@ -114,7 +123,7 @@ def read_defines_from(header_file, defines_dict): value = m.group(2) # check all uppercase if name != name.upper(): - raise Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name))) if name not in cmake_default_settings: cmake_default_settings[name] = DefineType(name, value, None, lineno) continue @@ -130,7 +139,7 @@ def read_defines_from(header_file, defines_dict): if m: validity_stack.pop() continue - + if validity_stack[-1]: # look for "#include "foo.h" m = re.match(r"""^\s*#include\s+"(.+?)"\s*$""", line) @@ -140,7 +149,7 @@ def read_defines_from(header_file, defines_dict): assert include.endswith(".h") # assume that the include is also in the boards directory assert "/" not in include or include.startswith("boards/") - read_defines_from(os.path.join(os.path.dirname(board_header), os.path.basename(include)), defines) + errors.extend(read_defines_from(os.path.join(os.path.dirname(board_header), os.path.basename(include)), defines)) continue # look for "#if BLAH_BLAH" @@ -175,11 +184,11 @@ def read_defines_from(header_file, defines_dict): value = m.group(2) # check all uppercase if name != name.upper(): - raise Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name))) # check that adjacent #ifndef and #define lines match up if last_ifndef_lineno + 1 == lineno: if last_ifndef != name: - raise Exception("{}:{} #ifndef {} / #define {} mismatch".format(board_header, last_ifndef_lineno, last_ifndef, name)) + errors.append(Exception("{}:{} #ifndef {} / #define {} mismatch".format(board_header, last_ifndef_lineno, last_ifndef, name))) if value: try: # most board-defines are integer values @@ -197,18 +206,21 @@ def read_defines_from(header_file, defines_dict): # check for multiply-defined values if name in defines_dict: if defines_dict[name].value != value: - raise Exception("{}:{} Conflicting definitions for {} ({} and {})".format(board_header, lineno, name, defines_dict[name].value, value)) + errors.append(Exception("{}:{} Conflicting definitions for {} ({} and {})".format(board_header, lineno, name, defines_dict[name].value, value))) else: if show_warnings: warnings.warn("{}:{} Multiple definitions for {} ({} and {})".format(board_header, lineno, name, defines_dict[name].value, value)) else: defines_dict[name] = DefineType(name, value, resolved_value, lineno) + return errors if board_header_basename == "amethyst_fpga.h": defines['PICO_RP2350'] = DefineType('PICO_RP2350', 1, 1, -1) defines['PICO_RP2350A'] = DefineType('PICO_RP2350A', 0, 0, -1) +errors = [] + with open(board_header) as header_fh: last_ifndef = None last_ifndef_lineno = -1 @@ -220,18 +232,18 @@ def read_defines_from(header_file, defines_dict): line = re.sub(r"(?<=\S)\s*//.*$", "", line) # look for board-detection comment - if re.match("^\s*// For board detection", line): + if re.match(r"^\s*// For board detection", line): board_detection_is_next = True continue # check include-suggestion - m = re.match("^\s*// This header may be included by other board headers as \"(.+?)\"", line) + m = re.match(r"""^\s*// This header may be included by other board headers as "(.+?)"$""", line) if m: include_suggestion = m.group(1) if include_suggestion == expected_include_suggestion: has_include_suggestion = True else: - raise Exception("{}:{} Suggests including \"{}\" but file is named \"{}\"".format(board_header, lineno, include_suggestion, expected_include_suggestion)) + errors.append(Exception("{}:{} Suggests including \"{}\" but file is named \"{}\"".format(board_header, lineno, include_suggestion, expected_include_suggestion))) continue # look for "// old_comment BLAH_BLAH=42" and suggest changing it to "new_macro(BLAH_BLAH, 42)" @@ -243,7 +255,7 @@ def read_defines_from(header_file, defines_dict): if m: name = m.group(1) value = m.group(2) - raise Exception("{}:{} \"// {} {}={}\" should be replaced with \"{}({}, {})\"".format(board_header, lineno, old_comment, name, value, new_macro, name, value)) + errors.append(Exception("{}:{} \"// {} {}={}\" should be replaced with \"{}({}, {})\"".format(board_header, lineno, old_comment, name, value, new_macro, name, value))) # look for "pico_board_cmake_set(BLAH_BLAH, 42)" m = re.match(r"^\s*pico_board_cmake_set\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*,\s*(.*)\s*\)\s*$", line) @@ -253,10 +265,10 @@ def read_defines_from(header_file, defines_dict): value = m.group(2) # check all uppercase if name != name.upper(): - raise Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name))) # check for multiply-defined values if name in cmake_settings: - raise Exception("{}:{} Multiple values for pico_board_cmake_set({}) ({} and {})".format(board_header, lineno, name, cmake_settings[name].value, value)) + errors.append(Exception("{}:{} Multiple values for pico_board_cmake_set({}) ({} and {})".format(board_header, lineno, name, cmake_settings[name].value, value))) else: if value: try: @@ -275,10 +287,10 @@ def read_defines_from(header_file, defines_dict): value = m.group(2) # check all uppercase if name != name.upper(): - raise Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name))) # check for multiply-defined values if name in cmake_default_settings: - raise Exception("{}:{} Multiple values for pico_board_cmake_set_default({}) ({} and {})".format(board_header, lineno, name, cmake_default_settings[name].value, value)) + errors.append(Exception("{}:{} Multiple values for pico_board_cmake_set_default({}) ({} and {})".format(board_header, lineno, name, cmake_default_settings[name].value, value))) else: if value: try: @@ -300,7 +312,7 @@ def read_defines_from(header_file, defines_dict): if m: validity_stack.pop() continue - + if validity_stack[-1]: # look for "#include "foo.h" m = re.match(r"""^\s*#include\s+"(.+?)"\s*$""", line) @@ -310,7 +322,7 @@ def read_defines_from(header_file, defines_dict): assert include.endswith(".h") # assume that the include is also in the boards directory assert "/" not in include or include.startswith("boards/") - read_defines_from(os.path.join(os.path.dirname(board_header), os.path.basename(include)), defines) + errors.extend(read_defines_from(os.path.join(os.path.dirname(board_header), os.path.basename(include)), defines)) continue # look for "#if BLAH_BLAH" @@ -344,11 +356,11 @@ def read_defines_from(header_file, defines_dict): value = m.group(2) # check all uppercase if name != name.upper(): - raise Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name))) # check that adjacent #ifndef and #define lines match up if last_ifndef_lineno + 1 == lineno: if last_ifndef != name: - raise Exception("{}:{} #ifndef {} / #define {} mismatch".format(board_header, last_ifndef_lineno, last_ifndef, name)) + errors.append(Exception("{}:{} #ifndef {} / #define {} mismatch".format(board_header, last_ifndef_lineno, last_ifndef, name))) if value: try: # most board-defines are integer values @@ -367,28 +379,28 @@ def read_defines_from(header_file, defines_dict): if re.match(r"^_BOARDS_(\w+)_H$", name): # check it has an #ifndef if last_ifndef_lineno +1 != lineno: - raise Exception("{}:{} Include-guard #define {} is missing an #ifndef".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Include-guard #define {} is missing an #ifndef".format(board_header, lineno, name))) if value: - raise Exception("{}:{} Include-guard #define {} shouldn't have a value".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Include-guard #define {} shouldn't have a value".format(board_header, lineno, name))) if any(defines[d].lineno >= 0 for d in defines): - raise Exception("{}:{} Include-guard #define {} should be the first define".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Include-guard #define {} should be the first define".format(board_header, lineno, name))) if name == expected_include_guard: has_include_guard = True else: - raise Exception("{}:{} Found include-guard #define {} but expected {}".format(board_header, lineno, name, expected_include_guard)) + errors.append(Exception("{}:{} Found include-guard #define {} but expected {}".format(board_header, lineno, name, expected_include_guard))) # check board-detection define if board_detection_is_next: board_detection_is_next = False if value: - raise Exception("{}:{} Board-detection #define {} shouldn't have a value".format(board_header, lineno, name)) + errors.append(Exception("{}:{} Board-detection #define {} shouldn't have a value".format(board_header, lineno, name))) # this is a bit messy because pico.h does "#define RASPBERRYPI_PICO" and metrotech_xerxes_rp2040.h does "#define XERXES_RP2040" if name.endswith(expected_board_detection) or expected_board_detection.endswith(name): has_board_detection = True else: - raise Exception("{}:{} Board-detection #define {} should end with {}".format(board_header, lineno, name, expected_board_detection)) + errors.append(Exception("{}:{} Board-detection #define {} should end with {}".format(board_header, lineno, name, expected_board_detection))) # check for multiply-defined values if name in defines: - raise Exception("{}:{} Multiple definitions for {} ({} and {})".format(board_header, lineno, name, defines[name].value, value)) + errors.append(Exception("{}:{} Multiple definitions for {} ({} and {})".format(board_header, lineno, name, defines[name].value, value))) else: defines[name] = DefineType(name, value, resolved_value, lineno) continue @@ -404,16 +416,20 @@ def read_defines_from(header_file, defines_dict): else: for setting in compulsory_cmake_settings: if setting not in cmake_settings: - raise Exception("{} is missing a pico_board_cmake_set({}, XXX) call".format(board_header, setting)) + errors.append(Exception("{} is missing a pico_board_cmake_set({}, XXX) call".format(board_header, setting))) + + # Must be raised before continuing, in case compulsory settings are missing + if errors: + raise ExceptionGroup("Errors in {}".format(board_header), errors) if cmake_settings['PICO_PLATFORM'].value == "rp2040": chip = 'RP2040' other_chip = 'RP2350' elif cmake_settings['PICO_PLATFORM'].value == "rp2350": other_chip = 'RP2040' if 'PICO_RP2350B' in defines: - raise Exception("{} sets #define {} {} (should probably be #define {} {})".format(board_header, 'PICO_RP2350B', defines['PICO_RP2350B'].resolved_value, 'PICO_RP2350A', 1 - defines['PICO_RP2350B'].resolved_value)) + errors.append(Exception("{} sets #define {} {} (should probably be #define {} {})".format(board_header, 'PICO_RP2350B', defines['PICO_RP2350B'].resolved_value, 'PICO_RP2350A', 1 - defines['PICO_RP2350B'].resolved_value))) if 'PICO_RP2350A' not in defines: - raise Exception("{} has no #define for {} (set to 1 for RP2350A, or 0 for RP2350B)".format(board_header, 'PICO_RP2350A')) + errors.append(Exception("{} has no #define for {} (set to 1 for RP2350A, or 0 for RP2350B)".format(board_header, 'PICO_RP2350A'))) else: if defines['PICO_RP2350A'].resolved_value == 1: chip = 'RP2350A' @@ -421,28 +437,33 @@ def read_defines_from(header_file, defines_dict): chip = 'RP2350B' if not board_header.endswith("amethyst_fpga.h"): if 'PICO_RP2350_A2_SUPPORTED' not in cmake_default_settings: - raise Exception("{} uses chip {} but is missing a pico_board_cmake_set_default({}, XXX) call".format(board_header, chip, 'PICO_RP2350_A2_SUPPORTED')) + errors.append(Exception("{} uses chip {} but is missing a pico_board_cmake_set_default({}, XXX) call".format(board_header, chip, 'PICO_RP2350_A2_SUPPORTED'))) if 'PICO_RP2350_A2_SUPPORTED' not in defines: - raise Exception("{} uses chip {} but is missing a #define {}".format(board_header, chip, 'PICO_RP2350_A2_SUPPORTED')) - if defines['PICO_RP2350_A2_SUPPORTED'].resolved_value != 1: - raise Exception("{} sets #define {} {} (should be 1)".format(board_header, chip, 'PICO_RP2350_A2_SUPPORTED', defines['PICO_RP2350_A2_SUPPORTED'].resolved_value)) + errors.append(Exception("{} uses chip {} but is missing a #define {}".format(board_header, chip, 'PICO_RP2350_A2_SUPPORTED'))) + elif defines['PICO_RP2350_A2_SUPPORTED'].resolved_value != 1: + errors.append(Exception("{} sets #define {} {} (should be 1)".format(board_header, chip, 'PICO_RP2350_A2_SUPPORTED', defines['PICO_RP2350_A2_SUPPORTED'].resolved_value))) for setting in compulsory_cmake_default_settings: if setting not in cmake_default_settings: - raise Exception("{} is missing a pico_board_cmake_set_default({}, XXX) call".format(board_header, setting)) + errors.append(Exception("{} is missing a pico_board_cmake_set_default({}, XXX) call".format(board_header, setting))) for setting in matching_cmake_default_settings: if setting in cmake_default_settings and setting not in defines: - raise Exception("{} has pico_board_cmake_set_default({}, XXX) but is missing a matching #define".format(board_header, setting)) + errors.append(Exception("{} has pico_board_cmake_set_default({}, XXX) but is missing a matching #define".format(board_header, setting))) elif setting in defines and setting not in cmake_default_settings: - raise Exception("{} has #define {} but is missing a matching pico_board_cmake_set_default({}, XXX) call".format(board_header, setting, setting)) + errors.append(Exception("{} has #define {} but is missing a matching pico_board_cmake_set_default({}, XXX) call".format(board_header, setting, setting))) elif setting in defines and setting in cmake_default_settings: if cmake_default_settings[setting].value != defines[setting].resolved_value: - raise Exception("{} has mismatched pico_board_cmake_set_default and #define values for {}".format(board_header, setting)) + errors.append(Exception("{} has mismatched pico_board_cmake_set_default and #define values for {}".format(board_header, setting))) for setting in compulsory_defines: if setting not in defines: - raise Exception("{} is missing a #define {}".format(board_header, setting)) + errors.append(Exception("{} is missing a #define {}".format(board_header, setting))) if chip is None: - raise Exception("Couldn't determine chip for {}".format(board_header)) + errors.append(Exception("Couldn't determine chip for {}".format(board_header))) + +# Must be raised before continuing, in case chip is not determined +if errors: + raise ExceptionGroup("Errors in {}".format(board_header), errors) + interfaces_json = chip_interfaces[chip] if not os.path.isfile(interfaces_json): raise Exception("{} doesn't exist".format(interfaces_json)) @@ -465,14 +486,14 @@ def read_defines_from(header_file, defines_dict): # check for other-chip defines if other_chip in name: - raise Exception("{}:{} Header is for {} and so shouldn't have settings for {} ({})".format(board_header, define.lineno, chip, other_chip, name)) + errors.append(Exception("{}:{} Header is for {} and so shouldn't have settings for {} ({})".format(board_header, define.lineno, chip, other_chip, name))) # check for pin-conflicts if name.endswith("_PIN"): if define.resolved_value is None: - raise Exception("{}:{} {} is set to an undefined value".format(board_header, define.lineno, name)) + errors.append(Exception("{}:{} {} is set to an undefined value".format(board_header, define.lineno, name))) elif not isinstance(define.resolved_value, int): - raise Exception("{}:{} {} resolves to a non-integer value {}".format(board_header, define.lineno, name, define.resolved_value)) + errors.append(Exception("{}:{} {} resolves to a non-integer value {}".format(board_header, define.lineno, name, define.resolved_value))) else: if define.resolved_value in pins and define.resolved_value == define.value: if show_warnings: @@ -480,7 +501,7 @@ def read_defines_from(header_file, defines_dict): pins[define.resolved_value].append(define) else: if define.resolved_value not in allowed_pins: - raise Exception("{}:{} Pin {} for {} isn't a valid pin-number".format(board_header, define.lineno, define.resolved_value, name)) + errors.append(Exception("{}:{} Pin {} for {} isn't a valid pin-number".format(board_header, define.lineno, define.resolved_value, name))) pins[define.resolved_value] = [define] # check for invalid DEFAULT mappings @@ -492,44 +513,52 @@ def read_defines_from(header_file, defines_dict): if interface == "WS2812": continue if interface not in allowed_interfaces: - raise Exception("{}:{} {} is defined but {} isn't in {}".format(board_header, define.lineno, name, interface, interfaces_json)) + errors.append(Exception("{}:{} {} is defined but {} isn't in {}".format(board_header, define.lineno, name, interface, interfaces_json))) + continue if instance_name not in defines: - raise Exception("{}:{} {} is defined but {} isn't defined".format(board_header, define.lineno, name, instance_name)) + errors.append(Exception("{}:{} {} is defined but {} isn't defined".format(board_header, define.lineno, name, instance_name))) + continue instance_define = defines[instance_name] instance_num = instance_define.resolved_value if instance_num not in allowed_interfaces[interface]["instances"]: - raise Exception("{}:{} {} is set to an invalid instance {}".format(board_header, instance_define.lineno, instance_define, instance_num)) + errors.append(Exception("{}:{} {} is set to an invalid instance {}".format(board_header, instance_define.lineno, instance_define, instance_num))) + continue interface_instance = allowed_interfaces[interface]["instances"][instance_num] if function not in interface_instance: - raise Exception("{}:{} {} is defined but {} isn't a valid function for {}".format(board_header, define.lineno, name, function, instance_define)) + errors.append(Exception("{}:{} {} is defined but {} isn't a valid function for {}".format(board_header, define.lineno, name, function, instance_define))) + continue if define.resolved_value not in interface_instance[function]: - raise Exception("{}:{} {} is set to {} which isn't a valid pin for {} on {} {}".format(board_header, define.lineno, name, define.resolved_value, function, interface, instance_num)) + errors.append(Exception("{}:{} {} is set to {} which isn't a valid pin for {} on {} {}".format(board_header, define.lineno, name, define.resolved_value, function, interface, instance_num))) # check that each used DEFAULT interface includes (at least) the expected pin-functions m = re.match("^PICO_DEFAULT_([A-Z0-9]+)$", name) if m: interface = m.group(1) if interface not in allowed_interfaces: - raise Exception("{}:{} {} is defined but {} isn't in {}".format(board_header, define.lineno, name, interface, interfaces_json)) + errors.append(Exception("{}:{} {} is defined but {} isn't in {}".format(board_header, define.lineno, name, interface, interfaces_json))) + continue if "expected_functions" in allowed_interfaces[interface]: expected_functions = allowed_interfaces[interface]["expected_functions"] if "required" in expected_functions: for function in expected_functions["required"]: expected_function_pin = "{}_{}_PIN".format(name, function) if expected_function_pin not in defines: - raise Exception("{}:{} {} is defined but {} isn't defined".format(board_header, define.lineno, name, expected_function_pin)) + errors.append(Exception("{}:{} {} is defined but {} isn't defined".format(board_header, define.lineno, name, expected_function_pin))) if "one_of" in expected_functions: expected_function_pins = list("{}_{}_PIN".format(name, function) for function in expected_functions["one_of"]) if not any(func_pin in defines for func_pin in expected_function_pins): - raise Exception("{}:{} {} is defined but none of {} are defined".format(board_header, define.lineno, name, list_to_string_with(expected_function_pins, "or"))) + errors.append(Exception("{}:{} {} is defined but none of {} are defined".format(board_header, define.lineno, name, list_to_string_with(expected_function_pins, "or")))) if not has_include_guard: - raise Exception("{} has no include-guard (expected {})".format(board_header, expected_include_guard)) + errors.append(Exception("{} has no include-guard (expected {})".format(board_header, expected_include_guard))) if not has_board_detection and expected_board_detection != "NONE": - raise Exception("{} has no board-detection #define (expected {})".format(board_header, expected_board_detection)) + errors.append(Exception("{} has no board-detection #define (expected {})".format(board_header, expected_board_detection))) # lots of headers don't have this #if not has_include_suggestion: # raise Exception("{} has no include-suggestion (expected {})".format(board_header, expected_include_suggestion)) +if errors: + raise ExceptionGroup("Errors in {}".format(board_header), errors) + # Check that #if / #ifdef / #ifndef / #else / #endif are correctly balanced assert len(validity_stack) == 1 and validity_stack[0] diff --git a/tools/extract_build_defines.py b/tools/extract_build_defines.py index 69181cf88..8df16ee88 100755 --- a/tools/extract_build_defines.py +++ b/tools/extract_build_defines.py @@ -28,7 +28,7 @@ # Python <3.11 doesn't have ExceptionGroup, so define a simple one class ExceptionGroup(Exception): def __init__(self, message, errors): - message += "\n" + "\n".join(e.__str__() for e in errors) + message += "\n" + "\n".join(str(e) for e in errors) super().__init__(message) logger = logging.getLogger(__name__) diff --git a/tools/extract_cmake_configs.py b/tools/extract_cmake_configs.py index 6bd5c9b0e..b86df28dc 100755 --- a/tools/extract_cmake_configs.py +++ b/tools/extract_cmake_configs.py @@ -28,7 +28,7 @@ # Python <3.11 doesn't have ExceptionGroup, so define a simple one class ExceptionGroup(Exception): def __init__(self, message, errors): - message += "\n" + "\n".join(e.__str__() for e in errors) + message += "\n" + "\n".join(str(e) for e in errors) super().__init__(message) logger = logging.getLogger(__name__) diff --git a/tools/extract_cmake_functions.py b/tools/extract_cmake_functions.py index 38e47ca15..48020768e 100755 --- a/tools/extract_cmake_functions.py +++ b/tools/extract_cmake_functions.py @@ -26,7 +26,7 @@ # Python <3.11 doesn't have ExceptionGroup, so define a simple one class ExceptionGroup(Exception): def __init__(self, message, errors): - message += "\n" + "\n".join(e.__str__() for e in errors) + message += "\n" + "\n".join(str(e) for e in errors) super().__init__(message) logger = logging.getLogger(__name__) diff --git a/tools/extract_configs.py b/tools/extract_configs.py index 71e639031..a95870c6a 100755 --- a/tools/extract_configs.py +++ b/tools/extract_configs.py @@ -28,7 +28,7 @@ # Python <3.11 doesn't have ExceptionGroup, so define a simple one class ExceptionGroup(Exception): def __init__(self, message, errors): - message += "\n" + "\n".join(e.__str__() for e in errors) + message += "\n" + "\n".join(str(e) for e in errors) super().__init__(message) logger = logging.getLogger(__name__)