fragments.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. #
  2. # Copyright 2021 Espressif Systems (Shanghai) CO LTD
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import abc
  17. import os
  18. import re
  19. from collections import namedtuple
  20. from enum import Enum
  21. from entity import Entity
  22. from pyparsing import (Combine, Forward, Group, Keyword, Literal, OneOrMore, Optional, Or, ParseFatalException,
  23. Suppress, Word, ZeroOrMore, alphanums, alphas, delimitedList, indentedBlock, nums,
  24. originalTextFor, restOfLine)
  25. from sdkconfig import SDKConfig
  26. class FragmentFile():
  27. """
  28. Processes a fragment file and stores all parsed fragments. For
  29. more information on how this class interacts with classes for the different fragment types,
  30. see description of Fragment.
  31. """
  32. def __init__(self, fragment_file, sdkconfig):
  33. try:
  34. fragment_file = open(fragment_file, 'r')
  35. except TypeError:
  36. pass
  37. path = os.path.realpath(fragment_file.name)
  38. indent_stack = [1]
  39. class parse_ctx:
  40. fragment = None # current fragment
  41. key = '' # current key
  42. keys = list() # list of keys parsed
  43. key_grammar = None # current key grammar
  44. @staticmethod
  45. def reset():
  46. parse_ctx.fragment_instance = None
  47. parse_ctx.key = ''
  48. parse_ctx.keys = list()
  49. parse_ctx.key_grammar = None
  50. def fragment_type_parse_action(toks):
  51. parse_ctx.reset()
  52. parse_ctx.fragment = FRAGMENT_TYPES[toks[0]]() # create instance of the fragment
  53. return None
  54. def expand_conditionals(toks, stmts):
  55. try:
  56. stmt = toks['value']
  57. stmts.append(stmt)
  58. except KeyError:
  59. try:
  60. conditions = toks['conditional']
  61. for condition in conditions:
  62. try:
  63. _toks = condition[1]
  64. _cond = condition[0]
  65. if sdkconfig.evaluate_expression(_cond):
  66. expand_conditionals(_toks, stmts)
  67. break
  68. except IndexError:
  69. expand_conditionals(condition[0], stmts)
  70. except KeyError:
  71. for tok in toks:
  72. expand_conditionals(tok, stmts)
  73. def key_body_parsed(pstr, loc, toks):
  74. stmts = list()
  75. expand_conditionals(toks, stmts)
  76. if parse_ctx.key_grammar.min and len(stmts) < parse_ctx.key_grammar.min:
  77. raise ParseFatalException(pstr, loc, "fragment requires at least %d values for key '%s'" %
  78. (parse_ctx.key_grammar.min, parse_ctx.key))
  79. if parse_ctx.key_grammar.max and len(stmts) > parse_ctx.key_grammar.max:
  80. raise ParseFatalException(pstr, loc, "fragment requires at most %d values for key '%s'" %
  81. (parse_ctx.key_grammar.max, parse_ctx.key))
  82. try:
  83. parse_ctx.fragment.set_key_value(parse_ctx.key, stmts)
  84. except Exception as e:
  85. raise ParseFatalException(pstr, loc, "unable to add key '%s'; %s" % (parse_ctx.key, str(e)))
  86. return None
  87. key = Word(alphanums + '_') + Suppress(':')
  88. key_stmt = Forward()
  89. condition_block = indentedBlock(key_stmt, indent_stack)
  90. key_stmts = OneOrMore(condition_block)
  91. key_body = Suppress(key) + key_stmts
  92. key_body.setParseAction(key_body_parsed)
  93. condition = originalTextFor(SDKConfig.get_expression_grammar()).setResultsName('condition')
  94. if_condition = Group(Suppress('if') + condition + Suppress(':') + condition_block)
  95. elif_condition = Group(Suppress('elif') + condition + Suppress(':') + condition_block)
  96. else_condition = Group(Suppress('else') + Suppress(':') + condition_block)
  97. conditional = (if_condition + Optional(OneOrMore(elif_condition)) + Optional(else_condition)).setResultsName('conditional')
  98. def key_parse_action(pstr, loc, toks):
  99. key = toks[0]
  100. if key in parse_ctx.keys:
  101. raise ParseFatalException(pstr, loc, "duplicate key '%s' value definition" % parse_ctx.key)
  102. parse_ctx.key = key
  103. parse_ctx.keys.append(key)
  104. try:
  105. parse_ctx.key_grammar = parse_ctx.fragment.get_key_grammars()[key]
  106. key_grammar = parse_ctx.key_grammar.grammar
  107. except KeyError:
  108. raise ParseFatalException(pstr, loc, "key '%s' is not supported by fragment" % key)
  109. except Exception as e:
  110. raise ParseFatalException(pstr, loc, "unable to parse key '%s'; %s" % (key, str(e)))
  111. key_stmt << (conditional | Group(key_grammar).setResultsName('value'))
  112. return None
  113. def name_parse_action(pstr, loc, toks):
  114. parse_ctx.fragment.name = toks[0]
  115. key.setParseAction(key_parse_action)
  116. ftype = Word(alphas).setParseAction(fragment_type_parse_action)
  117. fid = Suppress(':') + Word(alphanums + '_.').setResultsName('name')
  118. fid.setParseAction(name_parse_action)
  119. header = Suppress('[') + ftype + fid + Suppress(']')
  120. def fragment_parse_action(pstr, loc, toks):
  121. key_grammars = parse_ctx.fragment.get_key_grammars()
  122. required_keys = set([k for (k,v) in key_grammars.items() if v.required])
  123. present_keys = required_keys.intersection(set(parse_ctx.keys))
  124. if present_keys != required_keys:
  125. raise ParseFatalException(pstr, loc, 'required keys %s for fragment not found' %
  126. list(required_keys - present_keys))
  127. return parse_ctx.fragment
  128. fragment_stmt = Forward()
  129. fragment_block = indentedBlock(fragment_stmt, indent_stack)
  130. fragment_if_condition = Group(Suppress('if') + condition + Suppress(':') + fragment_block)
  131. fragment_elif_condition = Group(Suppress('elif') + condition + Suppress(':') + fragment_block)
  132. fragment_else_condition = Group(Suppress('else') + Suppress(':') + fragment_block)
  133. fragment_conditional = (fragment_if_condition + Optional(OneOrMore(fragment_elif_condition)) +
  134. Optional(fragment_else_condition)).setResultsName('conditional')
  135. fragment = (header + OneOrMore(indentedBlock(key_body, indent_stack, False))).setResultsName('value')
  136. fragment.setParseAction(fragment_parse_action)
  137. fragment.ignore('#' + restOfLine)
  138. deprecated_mapping = DeprecatedMapping.get_fragment_grammar(sdkconfig, fragment_file.name).setResultsName('value')
  139. fragment_stmt << (Group(deprecated_mapping) | Group(fragment) | Group(fragment_conditional))
  140. def fragment_stmt_parsed(pstr, loc, toks):
  141. stmts = list()
  142. expand_conditionals(toks, stmts)
  143. return stmts
  144. parser = ZeroOrMore(fragment_stmt)
  145. parser.setParseAction(fragment_stmt_parsed)
  146. self.fragments = parser.parseFile(fragment_file, parseAll=True)
  147. for fragment in self.fragments:
  148. fragment.path = path
  149. class Fragment():
  150. """
  151. Base class for a fragment that can be parsed from a fragment file. All fragments
  152. share the common grammar:
  153. [type:name]
  154. key1:value1
  155. key2:value2
  156. ...
  157. Supporting a new fragment type means deriving a concrete class which specifies
  158. key-value pairs that the fragment supports and what to do with the parsed key-value pairs.
  159. The new fragment must also be appended to FRAGMENT_TYPES, specifying the
  160. keyword for the type and the derived class.
  161. The key of the key-value pair is a simple keyword string. Other parameters
  162. that describe the key-value pair is specified in Fragment.KeyValue:
  163. 1. grammar - pyparsing grammar to parse the value of key-value pair
  164. 2. min - the minimum number of value in the key entry, None means no minimum
  165. 3. max - the maximum number of value in the key entry, None means no maximum
  166. 4. required - if the key-value pair is required in the fragment
  167. Setting min=max=1 means that the key has a single value.
  168. FragmentFile provides conditional expression evaluation, enforcing
  169. the parameters for Fragment.Keyvalue.
  170. """
  171. __metaclass__ = abc.ABCMeta
  172. KeyValue = namedtuple('KeyValue', 'grammar min max required')
  173. IDENTIFIER = Word(alphas + '_', alphanums + '_')
  174. ENTITY = Word(alphanums + '.-_$')
  175. @abc.abstractmethod
  176. def set_key_value(self, key, parse_results):
  177. pass
  178. @abc.abstractmethod
  179. def get_key_grammars(self):
  180. pass
  181. class Sections(Fragment):
  182. """
  183. Fragment which contains list of input sections.
  184. [sections:<name>]
  185. entries:
  186. .section1
  187. .section2
  188. ...
  189. """
  190. # Unless quoted, symbol names start with a letter, underscore, or point
  191. # and may include any letters, underscores, digits, points, and hyphens.
  192. GNU_LD_SYMBOLS = Word(alphas + '_.', alphanums + '._-')
  193. entries_grammar = Combine(GNU_LD_SYMBOLS + Optional('+'))
  194. grammars = {
  195. 'entries': Fragment.KeyValue(entries_grammar.setResultsName('section'), 1, None, True)
  196. }
  197. """
  198. Utility function that returns a list of sections given a sections fragment entry,
  199. with the '+' notation and symbol concatenation handled automatically.
  200. """
  201. @staticmethod
  202. def get_section_data_from_entry(sections_entry, symbol=None):
  203. if not symbol:
  204. sections = list()
  205. sections.append(sections_entry.replace('+', ''))
  206. sections.append(sections_entry.replace('+', '.*'))
  207. return sections
  208. else:
  209. if sections_entry.endswith('+'):
  210. section = sections_entry.replace('+', '.*')
  211. expansion = section.replace('.*', '.' + symbol)
  212. return (section, expansion)
  213. else:
  214. return (sections_entry, None)
  215. def set_key_value(self, key, parse_results):
  216. if key == 'entries':
  217. self.entries = set()
  218. for result in parse_results:
  219. self.entries.add(result['section'])
  220. def get_key_grammars(self):
  221. return self.__class__.grammars
  222. class Scheme(Fragment):
  223. """
  224. Fragment which defines where the input sections defined in a Sections fragment
  225. is going to end up, the target. The targets are markers in a linker script template
  226. (see LinkerScript in linker_script.py).
  227. [scheme:<name>]
  228. entries:
  229. sections1 -> target1
  230. ...
  231. """
  232. grammars = {
  233. 'entries': Fragment.KeyValue(Fragment.IDENTIFIER.setResultsName('sections') + Suppress('->') +
  234. Fragment.IDENTIFIER.setResultsName('target'), 1, None, True)
  235. }
  236. def set_key_value(self, key, parse_results):
  237. if key == 'entries':
  238. self.entries = set()
  239. for result in parse_results:
  240. self.entries.add((result['sections'], result['target']))
  241. def get_key_grammars(self):
  242. return self.__class__.grammars
  243. class Mapping(Fragment):
  244. """
  245. Fragment which attaches a scheme to entities (see Entity in entity.py), specifying where the input
  246. sections of the entity will end up.
  247. [mapping:<name>]
  248. archive: lib1.a
  249. entries:
  250. obj1:symbol1 (scheme1); section1 -> target1 KEEP SURROUND(sym1) ...
  251. obj2 (scheme2)
  252. ...
  253. Ultimately, an `entity (scheme)` entry generates an
  254. input section description (see https://sourceware.org/binutils/docs/ld/Input-Section.html)
  255. in the output linker script. It is possible to attach 'flags' to the
  256. `entity (scheme)` to generate different output commands or to
  257. emit additional keywords in the generated input section description. The
  258. input section description, as well as other output commands, is defined in
  259. output_commands.py.
  260. """
  261. class Flag():
  262. PRE_POST = (Optional(Suppress(',') + Suppress('pre').setParseAction(lambda: True).setResultsName('pre')) +
  263. Optional(Suppress(',') + Suppress('post').setParseAction(lambda: True).setResultsName('post')))
  264. class Surround(Flag):
  265. def __init__(self, symbol):
  266. self.symbol = symbol
  267. self.pre = True
  268. self.post = True
  269. @staticmethod
  270. def get_grammar():
  271. # SURROUND(symbol)
  272. #
  273. # '__symbol_start', '__symbol_end' is generated before and after
  274. # the corresponding input section description, respectively.
  275. grammar = (Keyword('SURROUND').suppress() +
  276. Suppress('(') +
  277. Fragment.IDENTIFIER.setResultsName('symbol') +
  278. Suppress(')'))
  279. grammar.setParseAction(lambda tok: Mapping.Surround(tok.symbol))
  280. return grammar
  281. def __eq__(self, other):
  282. return (isinstance(other, Mapping.Surround) and
  283. self.symbol == other.symbol)
  284. class Align(Flag):
  285. def __init__(self, alignment, pre=True, post=False):
  286. self.alignment = alignment
  287. self.pre = pre
  288. self.post = post
  289. @staticmethod
  290. def get_grammar():
  291. # ALIGN(alignment, [, pre, post]).
  292. #
  293. # Generates alignment command before and/or after the corresponding
  294. # input section description, depending whether pre, post or
  295. # both are specified.
  296. grammar = (Keyword('ALIGN').suppress() +
  297. Suppress('(') +
  298. Word(nums).setResultsName('alignment') +
  299. Mapping.Flag.PRE_POST +
  300. Suppress(')'))
  301. def on_parse(tok):
  302. alignment = int(tok.alignment)
  303. if tok.pre == '' and tok.post == '':
  304. res = Mapping.Align(alignment)
  305. elif tok.pre != '' and tok.post == '':
  306. res = Mapping.Align(alignment, tok.pre)
  307. elif tok.pre == '' and tok.post != '':
  308. res = Mapping.Align(alignment, False, tok.post)
  309. else:
  310. res = Mapping.Align(alignment, tok.pre, tok.post)
  311. return res
  312. grammar.setParseAction(on_parse)
  313. return grammar
  314. def __eq__(self, other):
  315. return (isinstance(other, Mapping.Align) and
  316. self.alignment == other.alignment and
  317. self.pre == other.pre and
  318. self.post == other.post)
  319. class Keep(Flag):
  320. def __init__(self):
  321. pass
  322. @staticmethod
  323. def get_grammar():
  324. # KEEP()
  325. #
  326. # Surrounds input section description with KEEP command.
  327. grammar = Keyword('KEEP()').setParseAction(Mapping.Keep)
  328. return grammar
  329. def __eq__(self, other):
  330. return isinstance(other, Mapping.Keep)
  331. class Sort(Flag):
  332. class Type(Enum):
  333. NAME = 0
  334. ALIGNMENT = 1
  335. INIT_PRIORITY = 2
  336. def __init__(self, first, second=None):
  337. self.first = first
  338. self.second = second
  339. @staticmethod
  340. def get_grammar():
  341. # SORT([sort_by_first, sort_by_second])
  342. #
  343. # where sort_by_first, sort_by_second = {name, alignment, init_priority}
  344. #
  345. # Emits SORT_BY_NAME, SORT_BY_ALIGNMENT or SORT_BY_INIT_PRIORITY
  346. # depending on arguments. Nested sort follows linker script rules.
  347. keywords = Keyword('name') | Keyword('alignment') | Keyword('init_priority')
  348. grammar = (Keyword('SORT').suppress() + Suppress('(') +
  349. keywords.setResultsName('first') +
  350. Optional(Suppress(',') + keywords.setResultsName('second')) + Suppress(')'))
  351. grammar.setParseAction(lambda tok: Mapping.Sort(tok.first, tok.second if tok.second != '' else None))
  352. return grammar
  353. def __eq__(self, other):
  354. return (isinstance(other, Mapping.Sort) and
  355. self.first == other.first and
  356. self.second == other.second)
  357. def __init__(self):
  358. Fragment.__init__(self)
  359. self.entries = set()
  360. # k = (obj, symbol, scheme)
  361. # v = list((section, target), Mapping.Flag))
  362. self.flags = dict()
  363. self.deprecated = False
  364. def set_key_value(self, key, parse_results):
  365. if key == 'archive':
  366. self.archive = parse_results[0]['archive']
  367. elif key == 'entries':
  368. for result in parse_results:
  369. obj = None
  370. symbol = None
  371. scheme = None
  372. obj = result['object']
  373. try:
  374. symbol = result['symbol']
  375. except KeyError:
  376. pass
  377. scheme = result['scheme']
  378. mapping = (obj, symbol, scheme)
  379. self.entries.add(mapping)
  380. try:
  381. parsed_flags = result['sections_target_flags']
  382. except KeyError:
  383. parsed_flags = []
  384. if parsed_flags:
  385. entry_flags = []
  386. for pf in parsed_flags:
  387. entry_flags.append((pf.sections, pf.target, list(pf.flags)))
  388. try:
  389. existing_flags = self.flags[mapping]
  390. except KeyError:
  391. existing_flags = list()
  392. self.flags[mapping] = existing_flags
  393. existing_flags.extend(entry_flags)
  394. def get_key_grammars(self):
  395. # There are three possible patterns for mapping entries:
  396. # obj:symbol (scheme)
  397. # obj (scheme)
  398. # * (scheme)
  399. # Flags can be specified for section->target in the scheme specified, ex:
  400. # obj (scheme); section->target SURROUND(symbol), section2->target2 ALIGN(4)
  401. obj = Fragment.ENTITY.setResultsName('object')
  402. symbol = Suppress(':') + Fragment.IDENTIFIER.setResultsName('symbol')
  403. scheme = Suppress('(') + Fragment.IDENTIFIER.setResultsName('scheme') + Suppress(')')
  404. # The flags are specified for section->target in the scheme specified
  405. sections_target = Scheme.grammars['entries'].grammar
  406. flag = Or([f.get_grammar() for f in [Mapping.Keep, Mapping.Align, Mapping.Surround, Mapping.Sort]])
  407. section_target_flags = Group(sections_target + Group(OneOrMore(flag)).setResultsName('flags'))
  408. pattern1 = obj + symbol
  409. pattern2 = obj
  410. pattern3 = Literal(Entity.ALL).setResultsName('object')
  411. entry = ((pattern1 | pattern2 | pattern3) + scheme +
  412. Optional(Suppress(';') + delimitedList(section_target_flags).setResultsName('sections_target_flags')))
  413. grammars = {
  414. 'archive': Fragment.KeyValue(Or([Fragment.ENTITY, Word(Entity.ALL)]).setResultsName('archive'), 1, 1, True),
  415. 'entries': Fragment.KeyValue(entry, 0, None, True)
  416. }
  417. return grammars
  418. class DeprecatedMapping():
  419. """
  420. Mapping fragment with old grammar in versions older than ESP-IDF v4.0. Does not conform to
  421. requirements of the Fragment class and thus is limited when it comes to conditional expression
  422. evaluation.
  423. """
  424. # Name of the default condition entry
  425. DEFAULT_CONDITION = 'default'
  426. @staticmethod
  427. def get_fragment_grammar(sdkconfig, fragment_file):
  428. # Match header [mapping]
  429. header = Suppress('[') + Suppress('mapping') + Suppress(']')
  430. # There are three possible patterns for mapping entries:
  431. # obj:symbol (scheme)
  432. # obj (scheme)
  433. # * (scheme)
  434. obj = Fragment.ENTITY.setResultsName('object')
  435. symbol = Suppress(':') + Fragment.IDENTIFIER.setResultsName('symbol')
  436. scheme = Suppress('(') + Fragment.IDENTIFIER.setResultsName('scheme') + Suppress(')')
  437. pattern1 = Group(obj + symbol + scheme)
  438. pattern2 = Group(obj + scheme)
  439. pattern3 = Group(Literal(Entity.ALL).setResultsName('object') + scheme)
  440. mapping_entry = pattern1 | pattern2 | pattern3
  441. # To simplify parsing, classify groups of condition-mapping entry into two types: normal and default
  442. # A normal grouping is one with a non-default condition. The default grouping is one which contains the
  443. # default condition
  444. mapping_entries = Group(ZeroOrMore(mapping_entry)).setResultsName('mappings')
  445. normal_condition = Suppress(':') + originalTextFor(SDKConfig.get_expression_grammar())
  446. default_condition = Optional(Suppress(':') + Literal(DeprecatedMapping.DEFAULT_CONDITION))
  447. normal_group = Group(normal_condition.setResultsName('condition') + mapping_entries)
  448. default_group = Group(default_condition + mapping_entries).setResultsName('default_group')
  449. normal_groups = Group(ZeroOrMore(normal_group)).setResultsName('normal_groups')
  450. # Any mapping fragment definition can have zero or more normal group and only one default group as a last entry.
  451. archive = Suppress('archive') + Suppress(':') + Fragment.ENTITY.setResultsName('archive')
  452. entries = Suppress('entries') + Suppress(':') + (normal_groups + default_group).setResultsName('entries')
  453. mapping = Group(header + archive + entries)
  454. mapping.ignore('#' + restOfLine)
  455. def parsed_deprecated_mapping(pstr, loc, toks):
  456. fragment = Mapping()
  457. fragment.archive = toks[0].archive
  458. fragment.name = re.sub(r'[^0-9a-zA-Z]+', '_', fragment.archive)
  459. fragment.deprecated = True
  460. fragment.entries = set()
  461. condition_true = False
  462. for entries in toks[0].entries[0]:
  463. condition = next(iter(entries.condition.asList())).strip()
  464. condition_val = sdkconfig.evaluate_expression(condition)
  465. if condition_val:
  466. for entry in entries[1]:
  467. fragment.entries.add((entry.object, None if entry.symbol == '' else entry.symbol, entry.scheme))
  468. condition_true = True
  469. break
  470. if not fragment.entries and not condition_true:
  471. try:
  472. entries = toks[0].entries[1][1]
  473. except IndexError:
  474. entries = toks[0].entries[1][0]
  475. for entry in entries:
  476. fragment.entries.add((entry.object, None if entry.symbol == '' else entry.symbol, entry.scheme))
  477. if not fragment.entries:
  478. fragment.entries.add(('*', None, 'default'))
  479. dep_warning = str(ParseFatalException(pstr, loc,
  480. 'Warning: Deprecated old-style mapping fragment parsed in file %s.' % fragment_file))
  481. print(dep_warning)
  482. return fragment
  483. mapping.setParseAction(parsed_deprecated_mapping)
  484. return mapping
  485. FRAGMENT_TYPES = {
  486. 'sections': Sections,
  487. 'scheme': Scheme,
  488. 'mapping': Mapping
  489. }