From 27b4ffd2f4500650bd5a4f3752c801e0d4d03fd2 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Mon, 3 Aug 2026 15:32:46 +0200 Subject: [PATCH] ci(check): update check_soc_struct_headers script --- tools/ci/check_soc_struct_headers.py | 130 +++++++++++++++++---------- 1 file changed, 82 insertions(+), 48 deletions(-) diff --git a/tools/ci/check_soc_struct_headers.py b/tools/ci/check_soc_struct_headers.py index f121296f500..a4ed807cea5 100755 --- a/tools/ci/check_soc_struct_headers.py +++ b/tools/ci/check_soc_struct_headers.py @@ -1,12 +1,11 @@ #!/usr/bin/env python -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 - # A check script that just works at the time of writing... # # also builds a structure tree for further reference # -# Input file format must be similiar to those headers generated by regtool, or this script makes no sense at all +# Input file format must be similar to those headers generated by regtool, or this script makes no sense at all # # Known limitation: # 1. won't accept /* ... */ /* ... */': badly behavior with multiline comment @@ -21,18 +20,18 @@ # 5. typedef volatile struct xxx{}: xxx must exists # # Otherwise won't fail but warning - import os import re import sys from typing import Any +from typing import Optional class MemberField: member_type = '' bitfield = None - def __init__(self, m_type: str, m_bits: int=None) -> None: + def __init__(self, m_type: str, m_bits: Optional[int] = None) -> None: self.member_type = m_type self.bitfield = m_bits @@ -44,8 +43,8 @@ class MemberField: def __str__(self) -> str: if self.bitfield is None: - return '"Field type={}"'.format(self.member_type) - return '"Field type={} bit={}"'.format(self.member_type, self.bitfield) + return f'"Field type={self.member_type}"' + return f'"Field type={self.member_type} bit={self.bitfield}"' class SoCStructureHeaderChecker: @@ -56,8 +55,10 @@ class SoCStructureHeaderChecker: # capture: type_var_name __REGEXP_MATCH_STRUCT_UNION_END_NAME = r'^[\s]*}[\s]*([\w\[\]\*]*)[\s]*;[\s\S]*$' # capture: type, name, bitfield - __REGEXP_MATCH_BITFIELD_MEMBER = (r'^[\s]*(?:(?:volatile[\s]+)|(?:))([\w\*]+)[\s]+([\w\*]+(?:(?:\[[\s\S]*\])|(?:)))' - r'[\s]*(?:(?:[\s]*;)|(?::[\s]*([\d]+)[\s]*;))[\s\S]*$') + __REGEXP_MATCH_BITFIELD_MEMBER = ( + r'^[\s]*(?:(?:volatile[\s]+)|(?:))([\w\*]+)[\s]+([\w\*]+(?:(?:\[[\s\S]*\])|(?:)))' + r'[\s]*(?:(?:[\s]*;)|(?::[\s]*([\d]+)[\s]*;))[\s\S]*$' + ) # should be useless and can be safely deleted __REGEXP_MATCH_MULTILINE_COMMENT = r'^[\s]*[\/]{0,2}\*[\/]?[\s\S]*$' __REGEX_MATCH_SIMPLE_VAL_FIELD = r'^[\s]*(?:(?:volatile[\s]+)|(?:))([\w]+)[\s]+([\w\[\]\*]+)[\s]*;[\s]*$' @@ -65,16 +66,16 @@ class SoCStructureHeaderChecker: __REGEX_MATCH_ROOT_EXTERNAL = r'^[\s]*extern[\s]+([\w]+)[\s]+([\w]+)[\s]*;[\s]*$' __linecount = 0 - __fd = None # type: Any + __fd = None # type: Any __is_eof = False # generated reference tree - __ref_tree = dict() # type: dict + __ref_tree = dict() # type: dict # middle result of generated tree, shared # named typedef, or named struct/union. referd but will not delete - __temp_ref_types = dict() # type: dict + __temp_ref_types = dict() # type: dict - def __expand_type(self, member_type: str, bitfield: int=None) -> Any: + def __expand_type(self, member_type: str, bitfield: Optional[int] = None) -> Any: if member_type == 'uint32_t': return MemberField(member_type, bitfield) if bitfield is not None: @@ -84,7 +85,7 @@ class SoCStructureHeaderChecker: return self.__temp_ref_types[member_type] return None - def __getline(self, incomment:bool=False) -> Any: + def __getline(self, incomment: bool = False) -> Any: rawline = self.__fd.readline() if not rawline: self.__is_eof = True @@ -95,7 +96,7 @@ class SoCStructureHeaderChecker: pos = rawline.find('*/') if pos != -1: # set string that is behind comment - rawline = rawline[pos + 2:] + rawline = rawline[pos + 2 :] else: # continue multiple line return self.__getline(True) @@ -112,7 +113,7 @@ class SoCStructureHeaderChecker: pos = match_obj.groups()[1].find('*/') if pos != -1: # apply string that is behind comment - rawline = rawline + match_obj.groups()[1][pos + 2:] + rawline = rawline + match_obj.groups()[1][pos + 2 :] else: # multiple line return self.__getline(True) @@ -121,7 +122,7 @@ class SoCStructureHeaderChecker: # skip empty line return self.__getline() if rawline.count(';') > 1: - print('\033[0;34mINFO\033[0m: line: {}: possibily multiple expression within same line'.format(self.__linecount)) + print(f'\033[0;34mINFO\033[0m: line: {self.__linecount}: possibly multiple expression within same line') print(rawline) return rawline @@ -129,7 +130,7 @@ class SoCStructureHeaderChecker: ret_val = 0 # first check for anonymous register structs if is_typedef and is_volatile and name is None: - print('\033[0;31mERROR\033[0m: line {}: annoymous struct'.format(self.__linecount)) + print(f'\033[0;31mERROR\033[0m: line {self.__linecount}: anonymous struct') ret_val = -1 node_tree = dict() bitcount = 0 @@ -145,7 +146,8 @@ class SoCStructureHeaderChecker: if match_obj is not None: has_nested_struct_union = True ret, inherited_node_tree = self.__process_structure( - match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile') + match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile' + ) if ret != 0: ret_val = -2 if inherited_node_tree is not None: @@ -155,7 +157,9 @@ class SoCStructureHeaderChecker: match_obj = re.match(self.__REGEXP_MATCH_UNION_BEGIN, rawline) if match_obj is not None: has_nested_struct_union = True - ret, inherited_node_tree = self.__process_union(match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile') + ret, inherited_node_tree = self.__process_union( + match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile' + ) if ret != 0: ret_val = -2 if inherited_node_tree is not None: @@ -169,16 +173,21 @@ class SoCStructureHeaderChecker: if bitcount not in (0, 32): ret_val = -2 if is_typedef: - print('\033[0;31mERROR\033[0m: line {}: bitfield count is {}, type {}'.format(self.__linecount, bitcount, match_obj.groups()[0])) + print( + f'\033[0;31mERROR\033[0m: line {self.__linecount}: bitfield count is {bitcount}, ' + f'type {match_obj.groups()[0]}' + ) else: - print('\033[0;31mERROR\033[0m: line {}: bitfield count is {}, type {}, varname "{}"' - .format(self.__linecount, bitcount, name, match_obj.groups()[0])) + print( + f'\033[0;31mERROR\033[0m: line {self.__linecount}: bitfield count is {bitcount}, ' + f'type {name}, varname "{match_obj.groups()[0]}"' + ) parsed_varname = match_obj.groups()[0] if is_typedef: # is a typedef if match_obj.groups()[0] == '' or match_obj.groups()[0].find('[') != -1: # should be c error - print('\033[0;31mERROR\033[0m: line {}: C error'.format(self.__linecount)) + print(f'\033[0;31mERROR\033[0m: line {self.__linecount}: C error') ret_val = -3 if match_obj.groups()[0] in self.__temp_ref_types: # duplication, script bug: we are putting all types into same namespace @@ -212,7 +221,10 @@ class SoCStructureHeaderChecker: bitcount += field_bit # bitfield should be u32 if match_obj.groups()[0] != 'uint32_t': - print('\033[0;33mWARN\033[0m: line: {}: {} has type {}'.format(self.__linecount, match_obj.groups()[1], match_obj.groups()[0])) + print( + f'\033[0;33mWARN\033[0m: line: {self.__linecount}: {match_obj.groups()[1]} ' + f'has type {match_obj.groups()[0]}' + ) else: has_non_bitfield_member = True # append to node tree @@ -225,9 +237,9 @@ class SoCStructureHeaderChecker: node_tree[match_obj.groups()[1]] = member_node else: if '*' not in match_obj.groups()[0]: - print('\033[0;33mWARN\033[0m: line {}: unknown type {}'.format(self.__linecount, match_obj.groups()[0])) + print(f'\033[0;33mWARN\033[0m: line {self.__linecount}: unknown type {match_obj.groups()[0]}') else: - print('\033[0;33mWARN\033[0m: line {}: pointer type {}'.format(self.__linecount, match_obj.groups()[0])) + print(f'\033[0;33mWARN\033[0m: line {self.__linecount}: pointer type {match_obj.groups()[0]}') continue # check comments match_obj = re.match(self.__REGEXP_MATCH_MULTILINE_COMMENT, rawline) @@ -235,24 +247,29 @@ class SoCStructureHeaderChecker: # code comments continue # dump out unmatched condition - print(('\033[0;33mWARN\033[0m: line: {}: unexpected expression: {}'.format(self.__linecount, rawline)).replace('\n', '')) + print( + (f'\033[0;33mWARN\033[0m: line: {self.__linecount}: unexpected expression: {rawline}').replace('\n', '') + ) if bitcount != 0 and has_nested_struct_union: - print('\033[0;33mWARN\033[0m: line: {}: mixed bitfield member and nested structure/union'.format(self.__linecount)) + print(f'\033[0;33mWARN\033[0m: line: {self.__linecount}: mixed bitfield member and nested structure/union') if bitcount != 0 and has_non_bitfield_member: - print('\033[0;33mWARN\033[0m: line: {}: mixed bitfield member and non-bitfield member'.format(self.__linecount)) + print(f'\033[0;33mWARN\033[0m: line: {self.__linecount}: mixed bitfield member and non-bitfield member') if is_typedef and is_volatile and name is None: if parsed_varname != '': print('SUGGEST: {}'.format(parsed_varname.rstrip('t') + 's')) if name is not None and is_typedef and is_volatile and parsed_varname.rstrip('t') != name.rstrip('s'): - print('\033[0;33mWARN\033[0m: line: {}: different type and typedef name: {} {}'.format(self.__linecount, name, parsed_varname)) + print( + f'\033[0;33mWARN\033[0m: line: {self.__linecount}: different type and typedef name: ' + f'{name} {parsed_varname}' + ) return ret_val, node_tree def __process_union(self, name: str, is_typedef: bool, is_volatile: bool) -> Any: ret_val = 0 # first check for anonymous register structs if is_typedef and is_volatile and name is None: - print('\033[0;31mERROR\033[0m: line {}: annoymous union'.format(self.__linecount)) + print(f'\033[0;31mERROR\033[0m: line {self.__linecount}: anonymous union') ret_val = -1 node_tree = dict() # type: Any has_struct_count = 0 @@ -266,7 +283,8 @@ class SoCStructureHeaderChecker: if match_obj is not None: has_struct_count += 1 ret, inherited_node_tree = self.__process_structure( - match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile') + match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile' + ) if ret != 0: ret_val = -2 if inherited_node_tree is not None: @@ -276,7 +294,9 @@ class SoCStructureHeaderChecker: match_obj = re.match(self.__REGEXP_MATCH_UNION_BEGIN, rawline) if match_obj is not None: has_struct_count += 1 - ret, inherited_node_tree = self.__process_union(match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile') + ret, inherited_node_tree = self.__process_union( + match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile' + ) if ret != 0: ret_val = -2 if inherited_node_tree is not None: @@ -291,7 +311,7 @@ class SoCStructureHeaderChecker: # is a typedef if match_obj.groups()[0] == '': # should be c error - print('\033[0;31mERROR\033[0m: line {}: C error'.format(self.__linecount)) + print(f'\033[0;31mERROR\033[0m: line {self.__linecount}: C error') ret_val = -3 if match_obj.groups()[0] in self.__temp_ref_types: # duplication, script bug: we are putting all types into same namespace @@ -325,7 +345,11 @@ class SoCStructureHeaderChecker: if match_obj is not None: # expecting to see 'uint32_t val;' if match_obj.groups()[0] != 'uint32_t' or match_obj.groups()[1] != 'val': - print(('\033[0;33mWARN\033[0m: unexpected union member at {}: {}'.format(self.__linecount, rawline)).replace('\n', '')) + print( + (f'\033[0;33mWARN\033[0m: unexpected union member at {self.__linecount}: {rawline}').replace( + '\n', '' + ) + ) else: has_val_field_count += 1 # append to node tree @@ -334,21 +358,28 @@ class SoCStructureHeaderChecker: node_tree[match_obj.groups()[1]] = member_node else: if '*' not in match_obj.groups()[0]: - print('\033[0;31mERROR\033[0m: line {}: unknown type {}'.format(self.__linecount, match_obj.groups()[0])) + print(f'\033[0;31mWARN\033[0m: line {self.__linecount}: unknown type {match_obj.groups()[0]}') else: - print('\033[0;33mWARN\033[0m: line {}: pointer type {}'.format(self.__linecount, match_obj.groups()[0])) + print(f'\033[0;33mWARN\033[0m: line {self.__linecount}: pointer type {match_obj.groups()[0]}') continue # dump out unmatched condition - print(('\033[0;33mWARN\033[0m: line: {}: unexpected expression: {}'.format(self.__linecount, rawline)).replace('\n', '')) + print( + (f'\033[0;33mWARN\033[0m: line: {self.__linecount}: unexpected expression: {rawline}').replace('\n', '') + ) if not (has_struct_count == 1 and has_val_field_count == 1): - print('\033[0;34mINFO\033[0m: line: {}: not a typical union: {} nested structures, {} u32 val member' - .format(self.__linecount, has_struct_count, has_val_field_count)) + print( + f'\033[0;34mINFO\033[0m: line: {self.__linecount}: not a typical union: {has_struct_count} ' + f'nested structures, {has_val_field_count} u32 val member' + ) if is_typedef and is_volatile and name is None: if parsed_varname != '': print('SUGGEST: {}'.format(parsed_varname.rstrip('t') + 's')) if name is not None and is_typedef and is_volatile and parsed_varname.rstrip('t') != name.rstrip('s'): - print('\033[0;33mWARN\033[0m: line: {}: different type and typedef name: {} {}'.format(self.__linecount, name, parsed_varname)) + print( + f'\033[0;33mWARN\033[0m: line: {self.__linecount}: different type and typedef name: ' + f'{name} {parsed_varname}' + ) return ret_val, node_tree def __process_root(self) -> int: @@ -363,7 +394,8 @@ class SoCStructureHeaderChecker: match_obj = re.match(self.__REGEXP_MATCH_STRUCTURE_BEGIN, rawline) if match_obj is not None: ret, inherited_node_tree = self.__process_structure( - match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile') + match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile' + ) if ret != 0: ret_val = -2 if inherited_node_tree is not None: @@ -372,7 +404,9 @@ class SoCStructureHeaderChecker: continue match_obj = re.match(self.__REGEXP_MATCH_UNION_BEGIN, rawline) if match_obj is not None: - ret, inherited_node_tree = self.__process_union(match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile') + ret, inherited_node_tree = self.__process_union( + match_obj.groups()[2], match_obj.groups()[0] == 'typedef', match_obj.groups()[1] == 'volatile' + ) if ret != 0: ret_val = -2 if inherited_node_tree is not None: @@ -387,7 +421,7 @@ class SoCStructureHeaderChecker: return ret_val def check(self, file: str) -> int: - self.__fd = open(file, 'r', encoding='utf8') + self.__fd = open(file, encoding='utf8') self.__linecount = 0 self.__is_eof = False @@ -395,9 +429,9 @@ class SoCStructureHeaderChecker: self.__fd.close() if ret_val != 0: - print('\033[0;31mCHECK FAILED\033[0m:\t{}'.format(file)) + print(f'\033[0;31mCHECK FAILED\033[0m:\t{file}') else: - print('\033[0;32mCHECK PASSED\033[0m:\t{}'.format(file)) + print(f'\033[0;32mCHECK PASSED\033[0m:\t{file}') return ret_val def get_ref_tree(self) -> Any: @@ -410,7 +444,7 @@ def main() -> None: print('file not exist') exit(-1) checker = SoCStructureHeaderChecker() - print('CHECKING:\t{}'.format(sys.argv[1])) + print(f'CHECKING:\t{sys.argv[1]}') ret = checker.check(sys.argv[1]) if len(sys.argv) == 3 and sys.argv[2] == 'print': print(checker.get_ref_tree())