ldgen: add backward-compatibility with previous mapping fragment style
This commit is contained in:
parent
94bc240a5e
commit
c81ebbf38e
3 changed files with 374 additions and 1 deletions
|
@ -14,6 +14,7 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
import os
|
||||
import re
|
||||
|
||||
from sdkconfig import SDKConfig
|
||||
from pyparsing import OneOrMore
|
||||
|
@ -176,7 +177,9 @@ class FragmentFile():
|
|||
fragment.setParseAction(fragment_parse_action)
|
||||
fragment.ignore("#" + restOfLine)
|
||||
|
||||
fragment_stmt << (Group(fragment) | Group(fragment_conditional))
|
||||
deprecated_mapping = DeprecatedMapping.get_fragment_grammar(sdkconfig, fragment_file.name).setResultsName("value")
|
||||
|
||||
fragment_stmt << (Group(deprecated_mapping) | Group(fragment) | Group(fragment_conditional))
|
||||
|
||||
def fragment_stmt_parsed(pstr, loc, toks):
|
||||
stmts = list()
|
||||
|
@ -324,6 +327,93 @@ class Mapping(Fragment):
|
|||
return grammars
|
||||
|
||||
|
||||
class DeprecatedMapping():
|
||||
"""
|
||||
Encapsulates a mapping fragment, which defines what targets the input sections of mappable entties are placed under.
|
||||
"""
|
||||
|
||||
# Name of the default condition entry
|
||||
DEFAULT_CONDITION = "default"
|
||||
MAPPING_ALL_OBJECTS = "*"
|
||||
|
||||
@staticmethod
|
||||
def get_fragment_grammar(sdkconfig, fragment_file):
|
||||
|
||||
# Match header [mapping]
|
||||
header = Suppress("[") + Suppress("mapping") + Suppress("]")
|
||||
|
||||
# There are three possible patterns for mapping entries:
|
||||
# obj:symbol (scheme)
|
||||
# obj (scheme)
|
||||
# * (scheme)
|
||||
obj = Fragment.ENTITY.setResultsName("object")
|
||||
symbol = Suppress(":") + Fragment.IDENTIFIER.setResultsName("symbol")
|
||||
scheme = Suppress("(") + Fragment.IDENTIFIER.setResultsName("scheme") + Suppress(")")
|
||||
|
||||
pattern1 = Group(obj + symbol + scheme)
|
||||
pattern2 = Group(obj + scheme)
|
||||
pattern3 = Group(Literal(Mapping.MAPPING_ALL_OBJECTS).setResultsName("object") + scheme)
|
||||
|
||||
mapping_entry = pattern1 | pattern2 | pattern3
|
||||
|
||||
# To simplify parsing, classify groups of condition-mapping entry into two types: normal and default
|
||||
# A normal grouping is one with a non-default condition. The default grouping is one which contains the
|
||||
# default condition
|
||||
mapping_entries = Group(ZeroOrMore(mapping_entry)).setResultsName("mappings")
|
||||
|
||||
normal_condition = Suppress(":") + originalTextFor(SDKConfig.get_expression_grammar())
|
||||
default_condition = Optional(Suppress(":") + Literal(DeprecatedMapping.DEFAULT_CONDITION))
|
||||
|
||||
normal_group = Group(normal_condition.setResultsName("condition") + mapping_entries)
|
||||
default_group = Group(default_condition + mapping_entries).setResultsName("default_group")
|
||||
|
||||
normal_groups = Group(ZeroOrMore(normal_group)).setResultsName("normal_groups")
|
||||
|
||||
# Any mapping fragment definition can have zero or more normal group and only one default group as a last entry.
|
||||
archive = Suppress("archive") + Suppress(":") + Fragment.ENTITY.setResultsName("archive")
|
||||
entries = Suppress("entries") + Suppress(":") + (normal_groups + default_group).setResultsName("entries")
|
||||
|
||||
mapping = Group(header + archive + entries)
|
||||
mapping.ignore("#" + restOfLine)
|
||||
|
||||
def parsed_deprecated_mapping(pstr, loc, toks):
|
||||
fragment = Mapping()
|
||||
fragment.archive = toks[0].archive
|
||||
fragment.name = re.sub(r"[^0-9a-zA-Z]+", "_", fragment.archive)
|
||||
|
||||
fragment.entries = set()
|
||||
condition_true = False
|
||||
for entries in toks[0].entries[0]:
|
||||
condition = next(iter(entries.condition.asList())).strip()
|
||||
condition_val = sdkconfig.evaluate_expression(condition)
|
||||
|
||||
if condition_val:
|
||||
for entry in entries[1]:
|
||||
fragment.entries.add((entry.object, None if entry.symbol == '' else entry.symbol, entry.scheme))
|
||||
condition_true = True
|
||||
break
|
||||
|
||||
if not fragment.entries and not condition_true:
|
||||
try:
|
||||
entries = toks[0].entries[1][1]
|
||||
except IndexError:
|
||||
entries = toks[0].entries[1][0]
|
||||
for entry in entries:
|
||||
fragment.entries.add((entry.object, None if entry.symbol == '' else entry.symbol, entry.scheme))
|
||||
|
||||
if not fragment.entries:
|
||||
fragment.entries.add(("*", None, "default"))
|
||||
|
||||
dep_warning = str(ParseFatalException(pstr, loc,
|
||||
"Warning: Deprecated old-style mapping fragment parsed in file %s." % fragment_file))
|
||||
|
||||
print(dep_warning)
|
||||
return fragment
|
||||
|
||||
mapping.setParseAction(parsed_deprecated_mapping)
|
||||
return mapping
|
||||
|
||||
|
||||
FRAGMENT_TYPES = {
|
||||
"sections": Sections,
|
||||
"scheme": Scheme,
|
||||
|
|
|
@ -735,5 +735,246 @@ entries:
|
|||
FragmentFile(test_fragment, self.sdkconfig)
|
||||
|
||||
|
||||
class DeprecatedMappingTest(FragmentTest):
|
||||
|
||||
def test_valid_grammar(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj:symbol (noflash)
|
||||
# Comments should not matter
|
||||
obj (noflash)
|
||||
# Nor should whitespace
|
||||
obj : symbol_2 ( noflash )
|
||||
obj_2 ( noflash )
|
||||
* (noflash)
|
||||
""")
|
||||
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
|
||||
self.assertEqual("lib.a", fragment_file.fragments[0].archive)
|
||||
self.assertEqual("lib_a", fragment_file.fragments[0].name)
|
||||
|
||||
expected = {("obj", "symbol", "noflash"),
|
||||
("obj", None, "noflash"),
|
||||
("obj", "symbol_2", "noflash"),
|
||||
("obj_2", None, "noflash"),
|
||||
("*", None, "noflash")
|
||||
}
|
||||
|
||||
self.assertEqual(expected, fragment_file.fragments[0].entries)
|
||||
|
||||
def test_explicit_blank_default_w_others(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: A = n
|
||||
obj_a (noflash)
|
||||
: default
|
||||
""")
|
||||
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
|
||||
expected = {("*", None, "default")}
|
||||
|
||||
self.assertEqual(expected, fragment_file.fragments[0].entries)
|
||||
|
||||
def test_implicit_blank_default_w_others(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: A = n
|
||||
obj_a (noflash)
|
||||
""")
|
||||
|
||||
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
|
||||
expected = {("*", None, "default")}
|
||||
|
||||
self.assertEqual(expected, fragment_file.fragments[0].entries)
|
||||
|
||||
def test_explicit_blank_default(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: default
|
||||
""")
|
||||
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
|
||||
expected = {("*", None, "default")}
|
||||
|
||||
self.assertEqual(expected, fragment_file.fragments[0].entries)
|
||||
|
||||
def test_implicit_blank_default(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: default
|
||||
""")
|
||||
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
|
||||
expected = {("*", None, "default")}
|
||||
|
||||
self.assertEqual(expected, fragment_file.fragments[0].entries)
|
||||
|
||||
def test_multiple_entries(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: A = n
|
||||
obj_a1 (noflash)
|
||||
obj_a2 (noflash)
|
||||
: B = n
|
||||
obj_b1 (noflash)
|
||||
obj_b2 (noflash)
|
||||
obj_b3 (noflash)
|
||||
: C = n
|
||||
obj_c1 (noflash)
|
||||
""")
|
||||
|
||||
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
|
||||
expected = {("obj_b1", None, "noflash"),
|
||||
("obj_b2", None, "noflash"),
|
||||
("obj_b3", None, "noflash")}
|
||||
self.assertEqual(expected, fragment_file.fragments[0].entries)
|
||||
|
||||
def test_blank_entries(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: A = n
|
||||
obj_a (noflash)
|
||||
: B = n
|
||||
: C = n
|
||||
obj_c (noflash)
|
||||
: default
|
||||
obj (noflash)
|
||||
""")
|
||||
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
|
||||
expected = {("*", None, "default")}
|
||||
self.assertEqual(expected, fragment_file.fragments[0].entries)
|
||||
|
||||
def test_blank_first_condition(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj_a (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_b (noflash)
|
||||
""")
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
FragmentFile(test_fragment, self.sdkconfig)
|
||||
|
||||
def test_nonlast_default_1(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: default
|
||||
obj_a (noflash)
|
||||
: CONFIG_A = y
|
||||
obj_A (noflash)
|
||||
""")
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
FragmentFile(test_fragment, self.sdkconfig)
|
||||
|
||||
def test_nonlast_default_2(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: A = y
|
||||
obj_A (noflash)
|
||||
: default
|
||||
obj_a (noflash)
|
||||
: B = y
|
||||
obj_B (noflash
|
||||
""")
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
FragmentFile(test_fragment, self.sdkconfig)
|
||||
|
||||
def test_nonlast_default_3(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: A = y
|
||||
obj_A (noflash)
|
||||
:
|
||||
obj_a (noflash)
|
||||
: B = y
|
||||
obj_B (noflash
|
||||
""")
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
FragmentFile(test_fragment, self.sdkconfig)
|
||||
|
||||
def test_duplicate_default_1(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_A (noflash)
|
||||
: default
|
||||
obj_a (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_B (noflash)
|
||||
: default
|
||||
obj_a (noflash)
|
||||
""")
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
FragmentFile(test_fragment, self.sdkconfig)
|
||||
|
||||
def test_duplicate_default_2(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_A (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_a (noflash)
|
||||
: default
|
||||
obj_B (noflash)
|
||||
:
|
||||
obj_a (noflash)
|
||||
""")
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
FragmentFile(test_fragment, self.sdkconfig)
|
||||
|
||||
def test_mixed_deprecated_mapping(self):
|
||||
test_fragment = self.create_fragment_file(u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: A = n
|
||||
obj_A (noflash)
|
||||
: default
|
||||
obj_B (noflash)
|
||||
|
||||
|
||||
[mapping:test]
|
||||
archive: lib.a
|
||||
entries:
|
||||
if A = n:
|
||||
obj_A (noflash)
|
||||
else:
|
||||
obj_B (noflash)
|
||||
""")
|
||||
|
||||
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
|
||||
self.assertEqual(2, len(fragment_file.fragments))
|
||||
|
||||
self.assertEqual(fragment_file.fragments[0].entries,
|
||||
fragment_file.fragments[1].entries)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -1190,6 +1190,48 @@ entries:
|
|||
|
||||
self.compare_rules(expected, actual)
|
||||
|
||||
def test_rule_generation_condition_with_deprecated_mapping(self):
|
||||
generation_with_condition = u"""
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: PERFORMANCE_LEVEL = 0
|
||||
: PERFORMANCE_LEVEL = 1
|
||||
obj1 (noflash)
|
||||
: PERFORMANCE_LEVEL = 2
|
||||
obj1 (noflash)
|
||||
obj2 (noflash)
|
||||
: PERFORMANCE_LEVEL = 3
|
||||
obj1 (noflash)
|
||||
obj2 (noflash)
|
||||
obj3 (noflash)
|
||||
"""
|
||||
|
||||
for perf_level in range(0, 4):
|
||||
self.sdkconfig.config.syms["PERFORMANCE_LEVEL"].set_value(str(perf_level))
|
||||
|
||||
self.model.mappings = {}
|
||||
self.add_fragments(generation_with_condition)
|
||||
|
||||
actual = self.model.generate_rules(self.sections_info)
|
||||
expected = self.generate_default_rules()
|
||||
|
||||
if perf_level < 4:
|
||||
for append_no in range(1, perf_level + 1):
|
||||
flash_text_default = self.get_default("flash_text", expected)
|
||||
flash_rodata_default = self.get_default("flash_rodata", expected)
|
||||
|
||||
iram_rule = PlacementRule("lib.a", "obj" + str(append_no), None, self.model.sections["text"].entries, "iram0_text")
|
||||
dram_rule = PlacementRule("lib.a", "obj" + str(append_no), None, self.model.sections["rodata"].entries, "dram0_data")
|
||||
|
||||
flash_text_default.add_exclusion(iram_rule)
|
||||
flash_rodata_default.add_exclusion(dram_rule)
|
||||
|
||||
expected["iram0_text"].append(iram_rule)
|
||||
expected["dram0_data"].append(dram_rule)
|
||||
|
||||
self.compare_rules(expected, actual)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in a new issue