Merge branch 'feature/allow_multiple_fragment_definitions_for_library' into 'release/v3.3'

Combine definitions of multiple mapping fragments referring to the same library

See merge request idf/esp-idf!4243
This commit is contained in:
Angus Gratton 2019-05-20 13:58:25 +08:00
commit 8c57aa0242
2 changed files with 101 additions and 33 deletions

View file

@ -333,34 +333,37 @@ class GenerationModel:
all_mapping_rules = collections.defaultdict(list)
# Generate rules based on mapping fragments
for mapping in self.mappings.values():
for (condition, entries) in mapping.entries:
condition_true = False
for mapping_list in self.mappings.values():
for mapping in mapping_list:
for (condition, entries) in mapping.entries:
condition_true = False
# Only non-default condition are evaluated agains sdkconfig model
if condition != Mapping.DEFAULT_CONDITION:
try:
condition_true = sdkconfig.evaluate_expression(condition)
except Exception as e:
raise GenerationException(e.message, mapping)
else:
condition_true = True
if condition_true:
mapping_rules = list()
archive = mapping.archive
for (obj, symbol, scheme_name) in entries:
# Only non-default condition are evaluated agains sdkconfig model
if condition != Mapping.DEFAULT_CONDITION:
try:
self._add_mapping_rules(archive, obj, symbol, scheme_name, scheme_dictionary, mapping_rules)
except KeyError:
message = GenerationException.UNDEFINED_REFERENCE + " to scheme '" + scheme_name + "'."
raise GenerationException(message, mapping)
condition_true = sdkconfig.evaluate_expression(condition)
except Exception as e:
raise GenerationException(e.message, mapping)
else:
condition_true = True
all_mapping_rules[mapping.name] = mapping_rules
if condition_true:
break # Exit on first condition that evaluates to true
mapping_rules = list()
archive = mapping.archive
for (obj, symbol, scheme_name) in entries:
try:
self._add_mapping_rules(archive, obj, symbol, scheme_name, scheme_dictionary, mapping_rules)
except KeyError:
message = GenerationException.UNDEFINED_REFERENCE + " to scheme '" + scheme_name + "'."
raise GenerationException(message, mapping)
for mapping_rule in mapping_rules:
if mapping_rule not in all_mapping_rules[mapping.name]:
all_mapping_rules[mapping.name].append(mapping_rule)
break # Exit on first condition that evaluates to true
# Detect rule conflicts
for mapping_rules in all_mapping_rules.items():
@ -476,14 +479,23 @@ class GenerationModel:
else:
dict_to_append_to = self.mappings
# Raise exception when the fragment of the same type is already in the stored fragments
if fragment.name in dict_to_append_to.keys():
stored = dict_to_append_to[fragment.name].path
new = fragment.path
message = "Duplicate definition of fragment '%s' found in %s and %s." % (fragment.name, stored, new)
raise GenerationException(message)
if isinstance(fragment, Mapping):
try:
fragment_list = dict_to_append_to[fragment.name]
except KeyError:
fragment_list = list()
dict_to_append_to[fragment.name] = fragment_list
dict_to_append_to[fragment.name] = fragment
fragment_list.append(fragment)
else:
# Raise exception when the fragment of the same type is already in the stored fragments
if fragment.name in dict_to_append_to.keys():
stored = dict_to_append_to[fragment.name].path
new = fragment.path
message = "Duplicate definition of fragment '%s' found in %s and %s." % (fragment.name, stored, new)
raise GenerationException(message)
dict_to_append_to[fragment.name] = fragment
class TemplateModel:

View file

@ -64,7 +64,12 @@ class GenerationModelTest(unittest.TestCase):
def _add_mapping(self, text):
parser = Mapping.get_fragment_grammar()
fragment = parser.parseString(text, parseAll=True)
self.model.mappings[fragment[0].name] = fragment[0]
try:
mappings = self.model.mappings[fragment[0].name]
except KeyError:
mappings = list()
self.model.mappings[fragment[0].name] = mappings
mappings.append(fragment[0])
def _add_sections(self, text):
parser = Sections.get_fragment_grammar()
@ -297,7 +302,6 @@ class GenerationModelTest(unittest.TestCase):
self._add_mapping(normal)
actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
expected = self._generate_default_rules()
flash_text_default = self._get_default("flash_text", expected)
@ -1120,6 +1124,58 @@ class GenerationModelTest(unittest.TestCase):
self._compare_rules(expected, actual)
def test_rule_generation_multiple_mapping_definitions(self):
generation_with_condition1 = """
[mapping]
archive: lib.a
entries:
: PERFORMANCE_LEVEL = 0
: PERFORMANCE_LEVEL = 1
obj1 (noflash)
: PERFORMANCE_LEVEL = 2
obj1 (noflash)
: PERFORMANCE_LEVEL = 3
obj1 (noflash)
"""
generation_with_condition2 = """
[mapping]
archive: lib.a
entries:
: PERFORMANCE_LEVEL = 1
obj1 (noflash) # ignore duplicate definition
: PERFORMANCE_LEVEL = 2
obj2 (noflash)
: PERFORMANCE_LEVEL = 3
obj2 (noflash)
obj3 (noflash)
"""
self._add_mapping(generation_with_condition1)
self._add_mapping(generation_with_condition2)
for perf_level in range(0, 4):
self.sdkconfig.config.syms["PERFORMANCE_LEVEL"].set_value(str(perf_level))
actual = self.model.generate_rules(self.sdkconfig, self.sections_info)
expected = self._generate_default_rules()
flash_text_default = self._get_default("flash_text", expected)
flash_rodata_default = self._get_default("flash_rodata", expected)
if perf_level < 4:
for append_no in range(1, perf_level + 1):
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()