Bladeren bron

ldgen: add backward-compatibility with previous mapping fragment style

Renz Christian Bagaporo 6 jaren geleden
bovenliggende
commit
c81ebbf38e
3 gewijzigde bestanden met toevoegingen van 374 en 1 verwijderingen
  1. 91 1
      tools/ldgen/fragments.py
  2. 241 0
      tools/ldgen/test/test_fragments.py
  3. 42 0
      tools/ldgen/test/test_generation.py

+ 91 - 1
tools/ldgen/fragments.py

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

+ 241 - 0
tools/ldgen/test/test_fragments.py

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

+ 42 - 0
tools/ldgen/test/test_generation.py

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