diff --git a/tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py b/tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py index 485d5ee93e..8d23a63421 100755 --- a/tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py +++ b/tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py @@ -33,7 +33,7 @@ from pyparsing import Word from pyparsing import alphas from pyparsing import hexnums from pyparsing import nums -from pyparsing import restOfLine +from pyparsing import rest_of_line pyparsing.usePackrat = True @@ -77,7 +77,7 @@ class KconfigWriter: def add_entry(self, name, entry_type, value): # type: (str, str, typing.Any) -> None if name in self.entries: - logging.info('Duplicate entry: {}'.format(name)) + logging.info(f'Duplicate entry: {name}') return # Format values for kconfig @@ -104,7 +104,7 @@ class KconfigWriter: self.kconfig_text.write(config_option) try: - with open(self.kconfig_path, 'r', encoding='utf-8') as f: + with open(self.kconfig_path, encoding='utf-8') as f: old_content = f.readlines() except FileNotFoundError: old_content = [''] @@ -122,7 +122,7 @@ class KconfigWriter: file_needs_update = True if file_needs_update: - print('\n' + 'Updating file: {}'.format(self.kconfig_path)) + print('\n' + f'Updating file: {self.kconfig_path}') with open(self.kconfig_path, 'w', encoding='utf-8') as f: f.writelines(new_content) @@ -136,12 +136,12 @@ def parse_include(inc_line): # type: (str) -> typing.Any[typing.Type[ParserElem # Comment with condition pattern condition_deli = OneOrMore(Group(Literal(' ') | Literal(':'))) - condition = CaselessLiteral('condition') + Optional(condition_deli) + restOfLine('condition') + condition = CaselessLiteral('condition') + Optional(condition_deli) + rest_of_line('condition') # Parse the include line # e.g. #include "../../beta3/include/soc/soc_caps.h" // recursive, condition: IDF_TARGET_ESP32C5_VERSION_BETA3 expr = Suppress('#include') + QuotedString('"')('inc_path') + recursive + Optional(condition) - res = expr.parseString(inc_line) + res = expr.parse_string(inc_line) return res @@ -169,7 +169,7 @@ def parse_define(define_line): # type: (str) -> typing.Any[typing.Type[ParserEl expr = ( '#define' + Optional(name)('name') + Optional(value) + Optional(ignore_pragma).set_results_name('ignore_pragma') ) - res = expr.parseString(define_line) + res = expr.parse_string(define_line) return res @@ -206,7 +206,7 @@ def generate_defines(soc_caps_dir, filename, always_write): # type: (Path, str, try: res = parse_define(line) except pyparsing.ParseException: - logging.debug('Failed to parse: {}'.format(line)) + logging.debug(f'Failed to parse: {line}') continue if res.ignore_pragma: @@ -233,8 +233,8 @@ def generate_defines(soc_caps_dir, filename, always_write): # type: (Path, str, def get_defines(header_path): # type: (Path) -> list[str] defines = [] - logging.info('Reading macros from {}...'.format(header_path)) - with open(header_path, 'r', encoding='utf-8') as f: + logging.info(f'Reading macros from {header_path}...') + with open(header_path, encoding='utf-8') as f: output = f.read() for line in output.split('\n'): @@ -309,6 +309,6 @@ if __name__ == '__main__': sys.exit(1) files_updated = [writer.update_file() for writer in writers if writer is not None] - print('Updated {} files'.format(sum(files_updated))) + print(f'Updated {sum(files_updated)} files') sys.exit(all(files_updated)) diff --git a/tools/ldgen/ldgen/entity.py b/tools/ldgen/ldgen/entity.py index f0ff9d2215..6b771f103e 100644 --- a/tools/ldgen/ldgen/entity.py +++ b/tools/ldgen/ldgen/entity.py @@ -1,5 +1,5 @@ # -# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 # import collections @@ -8,19 +8,19 @@ import os from enum import Enum from functools import total_ordering -from pyparsing import alphas from pyparsing import Group from pyparsing import Literal -from pyparsing import nums from pyparsing import OneOrMore from pyparsing import ParseException from pyparsing import Regex -from pyparsing import rest_of_line from pyparsing import SkipTo from pyparsing import Suppress from pyparsing import White from pyparsing import Word from pyparsing import ZeroOrMore +from pyparsing import alphas +from pyparsing import nums +from pyparsing import rest_of_line @total_ordering @@ -57,17 +57,19 @@ class Entity: elif archive_spec and obj_spec and symbol_spec: self.specificity = Entity.Specificity.SYMBOL else: - raise ValueError("Invalid arguments '(%s, %s, %s)'" % (archive, obj, symbol)) + raise ValueError(f"Invalid arguments '({archive}, {obj}, {symbol})'") self.archive = archive self.obj = obj self.symbol = symbol def __eq__(self, other): - return (self.specificity.value == other.specificity.value and - self.archive == other.archive and - self.obj == other.obj and - self.symbol == other.symbol) + return ( + self.specificity.value == other.specificity.value + and self.archive == other.archive + and self.obj == other.obj + and self.symbol == other.symbol + ) def __lt__(self, other): res = False @@ -89,7 +91,8 @@ class Entity: return hash(self.__repr__()) def __str__(self): - return '%s:%s %s' % self.__repr__() + archive, obj, symbol = self.__repr__() + return f'{archive}:{obj} {symbol}' def __repr__(self): return self.archive, self.obj, self.symbol @@ -123,15 +126,17 @@ class EntityDB: def add_sections_info(self, sections_info_dump): first_line = sections_info_dump.readline() - archive_path = (Literal('In archive').suppress() + - White().suppress() + - # trim the colon and line ending characters from archive_path - rest_of_line.set_results_name('archive_path').set_parse_action( - lambda s, loc, toks: s.rstrip(':\n\r '))) + archive_path = ( + Literal('In archive').suppress() + + White().suppress() + + + # trim the colon and line ending characters from archive_path + rest_of_line.set_results_name('archive_path').set_parse_action(lambda s, loc, toks: s.rstrip(':\n\r ')) + ) parser = archive_path try: - results = parser.parseString(first_line, parseAll=True) + results = parser.parse_string(first_line, parse_all=True) except ParseException as p: raise ParseException('Parsing sections info for library ' + sections_info_dump.name + ' failed. ' + p.msg) @@ -149,17 +154,20 @@ class EntityDB: # 00 {section} 0000000 ... # CONTENTS, ALLOC, .... - section_entry = (Suppress(Word(nums)) + Regex(r'\.\S+') + Suppress(rest_of_line) - + Suppress(ZeroOrMore(Word(alphas + '_') + Literal(',')) + Word(alphas + '_'))) + section_entry = ( + Suppress(Word(nums)) + + Regex(r'\.\S+') + + Suppress(rest_of_line) + + Suppress(ZeroOrMore(Word(alphas + '_') + Literal(',')) + Word(alphas + '_')) + ) - content = Group(object_line - + section_start - + section_header - + Group(OneOrMore(section_entry)).set_results_name('sections')) + content = Group( + object_line + section_start + section_header + Group(OneOrMore(section_entry)).set_results_name('sections') + ) parser = Group(ZeroOrMore(content)).set_results_name('contents') try: - results = parser.parseString(info.content, parseAll=True) + results = parser.parse_string(info.content, parse_all=True) except ParseException as p: raise ParseException('Unable to parse section info file ' + info.filename + '. ' + p.msg) @@ -191,13 +199,15 @@ class EntityDB: def _match_obj(self, archive, obj): objs = self.get_objects(archive) - match_objs = (fnmatch.filter(objs, obj + '.*.o') - + fnmatch.filter(objs, obj + '.o') - + fnmatch.filter(objs, obj + '.*.obj') - + fnmatch.filter(objs, obj + '.obj')) + match_objs = ( + fnmatch.filter(objs, obj + '.*.o') + + fnmatch.filter(objs, obj + '.o') + + fnmatch.filter(objs, obj + '.*.obj') + + fnmatch.filter(objs, obj + '.obj') + ) if len(match_objs) > 1: - raise ValueError("Multiple matches for object: '%s: %s': %s" % (archive, obj, str(match_objs))) + raise ValueError(f"Multiple matches for object: '{archive}: {obj}': {match_objs}") try: return match_objs[0] diff --git a/tools/ldgen/ldgen/fragments.py b/tools/ldgen/ldgen/fragments.py index 80cc167e20..d4f15f43c6 100644 --- a/tools/ldgen/ldgen/fragments.py +++ b/tools/ldgen/ldgen/fragments.py @@ -1,34 +1,28 @@ # -# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 # from typing import Any -from typing import Dict -from typing import List -from typing import Optional -from typing import Set -from typing import Tuple -from typing import Union -from pyparsing import alphanums -from pyparsing import alphas from pyparsing import Combine -from pyparsing import delimited_list +from pyparsing import DelimitedList from pyparsing import Forward from pyparsing import Group from pyparsing import IndentedBlock from pyparsing import Keyword from pyparsing import LineEnd from pyparsing import Literal -from pyparsing import nums from pyparsing import OneOrMore from pyparsing import Opt from pyparsing import ParseFatalException -from pyparsing import rest_of_line from pyparsing import SkipTo from pyparsing import Suppress from pyparsing import Word from pyparsing import ZeroOrMore +from pyparsing import alphanums +from pyparsing import alphas +from pyparsing import nums +from pyparsing import rest_of_line class Empty: @@ -47,10 +41,11 @@ class Fragment: """ Base class for a fragment that can be parsed from a fragment file. """ + IDENTIFIER = Word(alphas + '_', alphanums + '_') ENTITY = Word(alphanums + '.-_$+') - def __init__(self, name: str, entries: Set[Union[str, Tuple[str]]]): + def __init__(self, name: str, entries: set[str | tuple[str]]): self.name = name self.entries = entries self.path = '' @@ -69,6 +64,7 @@ class Sections(Fragment): .section2 ... """ + # Unless quoted, symbol names start with a letter, underscore, or point # and may include any letters, underscores, digits, points, and hyphens. ENTRY = Combine(Word(alphas + '_.', alphanums + '._-') + Opt('+')) + LineEnd().suppress() @@ -86,7 +82,7 @@ class Sections(Fragment): entries = {entry for entry in this[1] if entry} if not entries: - raise ParseFatalException(s, loc, 'Sections entries shouldn\'t be empty') + raise ParseFatalException(s, loc, "Sections entries shouldn't be empty") return Sections(name, entries) @@ -121,6 +117,7 @@ class Scheme(Fragment): sections1 -> target1 ... """ + ENTRY = Fragment.IDENTIFIER + Suppress('->') + Fragment.IDENTIFIER + LineEnd().suppress() @staticmethod @@ -136,7 +133,7 @@ class Scheme(Fragment): entries = {entry for entry in this[1] if entry} if not entries: - raise ParseFatalException(s, loc, 'Scheme entries shouldn\'t be empty') + raise ParseFatalException(s, loc, "Scheme entries shouldn't be empty") return Scheme(name, entries) @@ -153,10 +150,8 @@ class Surround(EntryFlag): '__symbol_start', '__symbol_end' is generated before and after the corresponding input section description, respectively. """ - SURROUND = (Keyword('SURROUND').suppress() - + Suppress('(') - + Fragment.IDENTIFIER - + Suppress(')')) + + SURROUND = Keyword('SURROUND').suppress() + Suppress('(') + Fragment.IDENTIFIER + Suppress(')') def __init__(self, symbol: str): self.symbol = symbol @@ -183,15 +178,11 @@ class Align(EntryFlag): input section description, depending on whether pre, post or both are specified. """ + PRE = Opt(Suppress(',') + Suppress('pre')).set_results_name('pre') POST = Opt(Suppress(',') + Suppress('post')).set_results_name('post') - ALIGN = (Keyword('ALIGN').suppress() - + Suppress('(') - + Word(nums) - + PRE - + POST - + Suppress(')')) + ALIGN = Keyword('ALIGN').suppress() + Suppress('(') + Word(nums) + PRE + POST + Suppress(')') def __init__(self, alignment, pre=True, post=False): self.alignment = alignment @@ -223,6 +214,7 @@ class Keep(EntryFlag): Surrounds input section description with KEEP command. """ + KEEP = Keyword('KEEP()') def __eq__(self, other): @@ -245,14 +237,16 @@ class Sort(EntryFlag): Emits SORT_BY_NAME, SORT_BY_ALIGNMENT or SORT_BY_INIT_PRIORITY depending on arguments. Nested sort follows linker script rules. """ - _keywords = Keyword('name') | Keyword('alignment') | Keyword('init_priority') - SORT = (Keyword('SORT').suppress() - + Suppress('(') - + Opt(_keywords.set_results_name('first') - + Opt(Suppress(',') + _keywords.set_results_name('second'))) - + Suppress(')')) - def __init__(self, first: Optional[str] = None, second: Optional[str] = None): + _keywords = Keyword('name') | Keyword('alignment') | Keyword('init_priority') + SORT = ( + Keyword('SORT').suppress() + + Suppress('(') + + Opt(_keywords.set_results_name('first') + Opt(Suppress(',') + _keywords.set_results_name('second'))) + + Suppress(')') + ) + + def __init__(self, first: str | None = None, second: str | None = None): self.first = first self.second = second @@ -270,14 +264,16 @@ class Sort(EntryFlag): class Flag: _section_target = Fragment.IDENTIFIER + Suppress('->') + Fragment.IDENTIFIER - _flag = (Surround.SURROUND.set_parse_action(Surround.parse) - | Align.ALIGN.set_parse_action(Align.parse) - | Keep.KEEP.set_parse_action(Keep.parse) - | Sort.SORT.set_parse_action(Sort.parse)) + _flag = ( + Surround.SURROUND.set_parse_action(Surround.parse) + | Align.ALIGN.set_parse_action(Align.parse) + | Keep.KEEP.set_parse_action(Keep.parse) + | Sort.SORT.set_parse_action(Sort.parse) + ) FLAG = _section_target + OneOrMore(_flag) - def __init__(self, section: str, target: str, flags: List[EntryFlag]): + def __init__(self, section: str, target: str, flags: list[EntryFlag]): self.section = section self.target = target self.flags = flags @@ -330,10 +326,12 @@ class Mapping(Fragment): # obj:symbol (scheme) # obj (scheme) # * (scheme) - _entry = (((_obj + Opt(Suppress(':') + _sym)) | _any.set_results_name('object')) - + Suppress('(') - + Fragment.IDENTIFIER.set_results_name('section') - + Suppress(')')) + _entry = ( + ((_obj + Opt(Suppress(':') + _sym)) | _any.set_results_name('object')) + + Suppress('(') + + Fragment.IDENTIFIER.set_results_name('section') + + Suppress(')') + ) ENTRY = _entry + LineEnd().suppress() ARCHIVE = (Word(alphanums + '.-_$+') | Literal('*')) + LineEnd().suppress() @@ -342,10 +340,9 @@ class Mapping(Fragment): # obj (scheme); # section->target SURROUND(symbol), # section2->target2 ALIGN(4) - ENTRY_WITH_FLAG = (_entry + Suppress(';') - + delimited_list(Flag.FLAG.set_parse_action(Flag.parse))) + ENTRY_WITH_FLAG = _entry + Suppress(';') + DelimitedList(Flag.FLAG.set_parse_action(Flag.parse)) - def __init__(self, archive: str, flags: Dict[Any, Flag], *args, **kwargs): + def __init__(self, archive: str, flags: dict[Any, Flag], *args, **kwargs): super().__init__(*args, **kwargs) self.archive = archive self.flags = flags @@ -365,9 +362,7 @@ class Mapping(Fragment): @staticmethod def parse_entry_with_flag(toks): entry = toks.object, toks.symbol or None, toks.section - return { - entry: [tok for tok in toks if isinstance(tok, Flag)] - } + return {entry: [tok for tok in toks if isinstance(tok, Flag)]} @staticmethod def parse_entries(toks): @@ -406,9 +401,9 @@ class FragmentFile: see description of Fragment. """ - def __init__(self, fragments: List[Fragment]): + def __init__(self, fragments: list[Fragment]): self.path = None # assign later, couldn't pass extra argument while parsing - self.fragments: List[Fragment] = fragments + self.fragments: list[Fragment] = fragments def __repr__(self): return str(self.__dict__) @@ -441,32 +436,26 @@ def parse_fragment_file(path, sdkconfig): def get_suite(_stmt): __stmt = Forward() __conditional = get_conditional_stmt(__stmt) - __stmt <<= (comment - | _stmt - | __conditional) + __stmt <<= comment | _stmt | __conditional return IndentedBlock(__stmt) def parse(toks): return FragmentFile([tok for tok in toks if not isinstance(tok, Empty)]) # comment - comment = (Literal('#') + rest_of_line).set_parse_action(lambda s, l, t: Empty()) + comment = (Literal('#') + rest_of_line).set_parse_action(lambda s, loc, toks: Empty()) # section section_entry = Sections.ENTRY.set_parse_action(Sections.parse_entry) section_entries_suite = get_suite(section_entry) section_header = Suppress('[sections:') + Fragment.IDENTIFIER + Suppress(']') + LineEnd().suppress() - section = Group(section_header - + Suppress('entries:') - + section_entries_suite).set_parse_action(Sections.parse) + section = Group(section_header + Suppress('entries:') + section_entries_suite).set_parse_action(Sections.parse) # scheme scheme_entry = Scheme.ENTRY.set_parse_action(Scheme.parse_entry) scheme_entries_suite = get_suite(scheme_entry) scheme_header = Suppress('[scheme:') + Fragment.IDENTIFIER + Suppress(']') + LineEnd().suppress() - scheme = Group(scheme_header - + Suppress('entries:') - + scheme_entries_suite).set_parse_action(Scheme.parse) + scheme = Group(scheme_header + Suppress('entries:') + scheme_entries_suite).set_parse_action(Scheme.parse) # mapping mapping_archive = Mapping.ARCHIVE mapping_archive_suite = get_suite(mapping_archive) @@ -476,18 +465,14 @@ def parse_fragment_file(path, sdkconfig): mapping_entries_suite = get_suite(mapping_entry | mapping_entry_with_flag) mapping_header = Suppress('[mapping:') + Fragment.IDENTIFIER + Suppress(']') - mapping = Group(mapping_header - + Group(Suppress('archive:') - + mapping_archive_suite).set_parse_action(Mapping.parse_archive) - + Group(Suppress('entries:') - + mapping_entries_suite).set_parse_action(Mapping.parse_entries) - ).set_parse_action(Mapping.parse) + mapping = Group( + mapping_header + + Group(Suppress('archive:') + mapping_archive_suite).set_parse_action(Mapping.parse_archive) + + Group(Suppress('entries:') + mapping_entries_suite).set_parse_action(Mapping.parse_entries) + ).set_parse_action(Mapping.parse) # highest level - fragment = (section - | scheme - | mapping - | get_conditional_stmt(section | scheme | mapping)) + fragment = section | scheme | mapping | get_conditional_stmt(section | scheme | mapping) parser = ZeroOrMore(fragment).ignore(comment).set_parse_action(parse) fragment_file = parser.parse_file(path, parse_all=True)[0] fragment_file.path = path diff --git a/tools/test_apps/build_system/ldgen_test/check_placements.py b/tools/test_apps/build_system/ldgen_test/check_placements.py index 5f515df9a7..2132ffb5fa 100644 --- a/tools/test_apps/build_system/ldgen_test/check_placements.py +++ b/tools/test_apps/build_system/ldgen_test/check_placements.py @@ -31,26 +31,24 @@ contents = subprocess.check_output([args.objdump, '-t', args.elf]).decode() def check_location(symbol, expected): pattern = ( LineStart() - + Word(hexnums).setResultsName('address') + + Word(hexnums).set_results_name('address') + Optional(Word(alphanums, exact=1)) + Optional(Word(alphanums, exact=1)) - + Word(alphanums + '._*').setResultsName('actual') + + Word(alphanums + '._*').set_results_name('actual') + Word(hexnums) + Literal(symbol) + LineEnd() ) try: - results = pattern.searchString(contents)[0] + results = pattern.search_string(contents)[0] except IndexError: - raise Exception("check placement fail: '%s' was not found" % (symbol)) + raise Exception(f"check placement fail: '{symbol}' was not found") if results.actual != expected: - raise Exception( - "check placement fail: '%s' was placed in '%s', not in '%s'" % (symbol, results.actual, expected) - ) + raise Exception(f"check placement fail: '{symbol}' was placed in '{results.actual}', not in '{expected}'") - print("check placement pass: '%s' was successfully placed in '%s'" % (symbol, results.actual)) + print(f"check placement pass: '{symbol}' was successfully placed in '{results.actual}'") return int(results.address, 16)