ldgen: add backward-compatibility with previous mapping fragment style

This commit is contained in:
Renz Christian Bagaporo 2019-04-02 16:40:43 +08:00
parent 94bc240a5e
commit c81ebbf38e
3 changed files with 374 additions and 1 deletions

View file

@ -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,

View file

@ -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()

View file

@ -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()