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.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
from sdkconfig import SDKConfig
|
from sdkconfig import SDKConfig
|
||||||
from pyparsing import OneOrMore
|
from pyparsing import OneOrMore
|
||||||
|
@ -176,7 +177,9 @@ class FragmentFile():
|
||||||
fragment.setParseAction(fragment_parse_action)
|
fragment.setParseAction(fragment_parse_action)
|
||||||
fragment.ignore("#" + restOfLine)
|
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):
|
def fragment_stmt_parsed(pstr, loc, toks):
|
||||||
stmts = list()
|
stmts = list()
|
||||||
|
@ -324,6 +327,93 @@ class Mapping(Fragment):
|
||||||
return grammars
|
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 = {
|
FRAGMENT_TYPES = {
|
||||||
"sections": Sections,
|
"sections": Sections,
|
||||||
"scheme": Scheme,
|
"scheme": Scheme,
|
||||||
|
|
|
@ -735,5 +735,246 @@ entries:
|
||||||
FragmentFile(test_fragment, self.sdkconfig)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1190,6 +1190,48 @@ entries:
|
||||||
|
|
||||||
self.compare_rules(expected, actual)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in a new issue