ldgen: implement flags support

Implement support for KEEP, ALIGN, emitting symbols and SORT.
Add appropriate tests
Defines default mapping in linker fragment file
This commit is contained in:
Renz Bagaporo
2021-02-03 18:19:09 +08:00
parent 48cda62bcf
commit b99777066f
12 changed files with 1404 additions and 259 deletions

View File

@@ -192,3 +192,8 @@ entries:
[scheme:wifi_slp_rx_iram] [scheme:wifi_slp_rx_iram]
entries: entries:
wifi_slp_rx_iram -> iram0_text wifi_slp_rx_iram -> iram0_text
[mapping:default]
archive: *
entries:
* (default)

View File

@@ -157,3 +157,8 @@ entries:
[scheme:wifi_slp_rx_iram] [scheme:wifi_slp_rx_iram]
entries: entries:
wifi_slp_rx_iram -> iram0_text wifi_slp_rx_iram -> iram0_text
[mapping:default]
archive: *
entries:
* (default)

View File

@@ -142,3 +142,8 @@ entries:
[scheme:wifi_slp_rx_iram] [scheme:wifi_slp_rx_iram]
entries: entries:
wifi_slp_rx_iram -> iram0_text wifi_slp_rx_iram -> iram0_text
[mapping:default]
archive: *
entries:
* (default)

View File

@@ -157,3 +157,8 @@ entries:
[scheme:wifi_slp_rx_iram] [scheme:wifi_slp_rx_iram]
entries: entries:
wifi_slp_rx_iram -> iram0_text wifi_slp_rx_iram -> iram0_text
[mapping:default]
archive: *
entries:
* (default)

View File

@@ -17,9 +17,12 @@ import abc
import os import os
import re import re
from collections import namedtuple from collections import namedtuple
from enum import Enum
from pyparsing import (Combine, Forward, Group, Literal, OneOrMore, Optional, ParseFatalException, Suppress, Word, from entity import Entity
ZeroOrMore, alphanums, alphas, indentedBlock, originalTextFor, restOfLine) from pyparsing import (Combine, Forward, Group, Keyword, Literal, OneOrMore, Optional, Or, ParseFatalException,
Suppress, Word, ZeroOrMore, alphanums, alphas, delimitedList, indentedBlock, nums,
originalTextFor, restOfLine)
from sdkconfig import SDKConfig from sdkconfig import SDKConfig
KeyGrammar = namedtuple('KeyGrammar', 'grammar min max required') KeyGrammar = namedtuple('KeyGrammar', 'grammar min max required')
@@ -267,11 +270,131 @@ class Mapping(Fragment):
Encapsulates a mapping fragment, which defines what targets the input sections of mappable entties are placed under. Encapsulates a mapping fragment, which defines what targets the input sections of mappable entties are placed under.
""" """
MAPPING_ALL_OBJECTS = '*' class Flag():
PRE_POST = (Optional(Suppress(',') + Suppress('pre').setParseAction(lambda: True).setResultsName('pre')) +
Optional(Suppress(',') + Suppress('post').setParseAction(lambda: True).setResultsName('post')))
class Emit(Flag):
def __init__(self, symbol, pre=True, post=True):
self.symbol = symbol
self.pre = pre
self.post = post
@staticmethod
def get_grammar():
# emit(symbol [, pre, post])
#
# __symbol_start, __symbol_end is generated before and after
# the corresponding input section description, respectively.
grammar = (Keyword('emit').suppress() +
Suppress('(') +
Fragment.IDENTIFIER.setResultsName('symbol') +
Mapping.Flag.PRE_POST +
Suppress(')'))
def on_parse(tok):
if tok.pre == '' and tok.post == '':
res = Mapping.Emit(tok.symbol)
elif tok.pre != '' and tok.post == '':
res = Mapping.Emit(tok.symbol, tok.pre, False)
elif tok.pre == '' and tok.post != '':
res = Mapping.Emit(tok.symbol, False, tok.post)
else:
res = Mapping.Emit(tok.symbol, tok.pre, tok.post)
return res
grammar.setParseAction(on_parse)
return grammar
def __eq__(self, other):
return (isinstance(other, Mapping.Emit) and
self.symbol == other.symbol and
self.pre == other.pre and
self.post == other.post)
class Align(Flag):
def __init__(self, alignment, pre=True, post=False):
self.alignment = alignment
self.pre = pre
self.post = post
@staticmethod
def get_grammar():
# align(alignment, [, pre, post])
grammar = (Keyword('align').suppress() +
Suppress('(') +
Word(nums).setResultsName('alignment') +
Mapping.Flag.PRE_POST +
Suppress(')'))
def on_parse(tok):
alignment = int(tok.alignment)
if tok.pre == '' and tok.post == '':
res = Mapping.Align(alignment)
elif tok.pre != '' and tok.post == '':
res = Mapping.Align(alignment, tok.pre)
elif tok.pre == '' and tok.post != '':
res = Mapping.Align(alignment, False, tok.post)
else:
res = Mapping.Align(alignment, tok.pre, tok.post)
return res
grammar.setParseAction(on_parse)
return grammar
def __eq__(self, other):
return (isinstance(other, Mapping.Align) and
self.alignment == other.alignment and
self.pre == other.pre and
self.post == other.post)
class Keep(Flag):
def __init__(self):
pass
@staticmethod
def get_grammar():
grammar = Keyword('keep').setParseAction(Mapping.Keep)
return grammar
def __eq__(self, other):
return isinstance(other, Mapping.Keep)
class Sort(Flag):
class Type(Enum):
NAME = 0
ALIGNMENT = 1
INIT_PRIORITY = 2
def __init__(self, first, second=None):
self.first = first
self.second = second
@staticmethod
def get_grammar():
# sort(sort_by_first [, sort_by_second])
keywords = Keyword('name') | Keyword('alignment') | Keyword('init_priority')
grammar = (Keyword('sort').suppress() + Suppress('(') +
keywords.setResultsName('first') +
Optional(Suppress(',') + keywords.setResultsName('second')) + Suppress(')'))
grammar.setParseAction(lambda tok: Mapping.Sort(tok.first, tok.second if tok.second != '' else None))
return grammar
def __eq__(self, other):
return (isinstance(other, Mapping.Sort) and
self.first == other.first and
self.second == other.second)
def __init__(self): def __init__(self):
Fragment.__init__(self) Fragment.__init__(self)
self.entries = set() self.entries = set()
# k = (obj, symbol, scheme)
# v = list((section, target), Mapping.Flag))
self.flags = dict()
self.deprecated = False self.deprecated = False
def set_key_value(self, key, parse_results): def set_key_value(self, key, parse_results):
@@ -283,40 +406,63 @@ class Mapping(Fragment):
symbol = None symbol = None
scheme = None scheme = None
try: obj = result['object']
obj = result['object']
except KeyError:
pass
try: try:
symbol = result['symbol'] symbol = result['symbol']
except KeyError: except KeyError:
pass pass
try: scheme = result['scheme']
scheme = result['scheme']
except KeyError:
pass
self.entries.add((obj, symbol, scheme)) mapping = (obj, symbol, scheme)
self.entries.add(mapping)
try:
parsed_flags = result['sections_target_flags']
except KeyError:
parsed_flags = []
if parsed_flags:
entry_flags = []
for pf in parsed_flags:
entry_flags.append((pf.sections, pf.target, list(pf.flags)))
try:
existing_flags = self.flags[mapping]
except KeyError:
existing_flags = list()
self.flags[mapping] = existing_flags
existing_flags.extend(entry_flags)
def get_key_grammars(self): def get_key_grammars(self):
# There are three possible patterns for mapping entries: # There are three possible patterns for mapping entries:
# obj:symbol (scheme) # obj:symbol (scheme)
# obj (scheme) # obj (scheme)
# * (scheme) # * (scheme)
# Flags can be specified for section->target in the scheme specified, ex:
# obj (scheme); section->target emit(symbol), section2->target2 align(4)
obj = Fragment.ENTITY.setResultsName('object') obj = Fragment.ENTITY.setResultsName('object')
symbol = Suppress(':') + Fragment.IDENTIFIER.setResultsName('symbol') symbol = Suppress(':') + Fragment.IDENTIFIER.setResultsName('symbol')
scheme = Suppress('(') + Fragment.IDENTIFIER.setResultsName('scheme') + Suppress(')') scheme = Suppress('(') + Fragment.IDENTIFIER.setResultsName('scheme') + Suppress(')')
pattern1 = obj + symbol + scheme # The flags are specified for section->target in the scheme specified
pattern2 = obj + scheme sections_target = Scheme.grammars['entries'].grammar
pattern3 = Literal(Mapping.MAPPING_ALL_OBJECTS).setResultsName('object') + scheme
entry = pattern1 | pattern2 | pattern3 flag = Or([f.get_grammar() for f in [Mapping.Keep, Mapping.Align, Mapping.Emit, Mapping.Sort]])
section_target_flags = Group(sections_target + Group(OneOrMore(flag)).setResultsName('flags'))
pattern1 = obj + symbol
pattern2 = obj
pattern3 = Literal(Entity.ALL).setResultsName('object')
entry = ((pattern1 | pattern2 | pattern3) + scheme +
Optional(Suppress(';') + delimitedList(section_target_flags).setResultsName('sections_target_flags')))
grammars = { grammars = {
'archive': KeyGrammar(Fragment.ENTITY.setResultsName('archive'), 1, 1, True), 'archive': KeyGrammar(Or([Fragment.ENTITY, Word(Entity.ALL)]).setResultsName('archive'), 1, 1, True),
'entries': KeyGrammar(entry, 0, None, True) 'entries': KeyGrammar(entry, 0, None, True)
} }
@@ -330,7 +476,6 @@ class DeprecatedMapping():
# Name of the default condition entry # Name of the default condition entry
DEFAULT_CONDITION = 'default' DEFAULT_CONDITION = 'default'
MAPPING_ALL_OBJECTS = '*'
@staticmethod @staticmethod
def get_fragment_grammar(sdkconfig, fragment_file): def get_fragment_grammar(sdkconfig, fragment_file):
@@ -348,7 +493,7 @@ class DeprecatedMapping():
pattern1 = Group(obj + symbol + scheme) pattern1 = Group(obj + symbol + scheme)
pattern2 = Group(obj + scheme) pattern2 = Group(obj + scheme)
pattern3 = Group(Literal(Mapping.MAPPING_ALL_OBJECTS).setResultsName('object') + scheme) pattern3 = Group(Literal(Entity.ALL).setResultsName('object') + scheme)
mapping_entry = pattern1 | pattern2 | pattern3 mapping_entry = pattern1 | pattern2 | pattern3

View File

@@ -22,72 +22,83 @@ from collections import namedtuple
from entity import Entity from entity import Entity
from fragments import Mapping, Scheme, Sections from fragments import Mapping, Scheme, Sections
from ldgen_common import LdGenFailure from ldgen_common import LdGenFailure
from output_commands import InputSectionDesc from output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress
class RuleNode(): class Placement():
class Section(): def __init__(self, node, sections, target, flags, explicit, force=False, dryrun=False):
self.node = node
self.sections = sections
self.target = target
self.flags = flags
def __init__(self, target, exclusions, explicit=False): self.exclusions = set()
self.target = target self.subplacements = set()
self.exclusions = set(exclusions)
# Indicate whether this node has been created explicitly from a mapping, # Force this placement to be output
# or simply just to create a path to the explicitly created node. self.force = force
#
# For example,
#
# lib.a
# obj:sym (scheme)
#
# Nodes for lib.a and obj will be created, but only the node for
# sym will have been created explicitly.
#
# This is used in deciding whether or not an output command should
# be emitted for this node, or for exclusion rule generation.
self.explicit = explicit
def __init__(self, parent, name, sections): # This placement was created from a mapping
# fragment entry.
self.explicit = explicit
# Find basis placement. A basis placement is a placement
# on the parent (or parent's parent and so on and so forth)
# that operates on the same section as this one.
parent = node.parent
candidate = None
while parent:
try:
candidate = parent.placements[sections]
except KeyError:
pass
if candidate and candidate.is_significant():
break
else:
parent = parent.parent
self.basis = candidate
if self.is_significant() and not dryrun and self.basis:
self.basis.add_exclusion(self)
def is_significant(self):
# Check if the placement is significant. Significant placements
# are the end of a basis chain (not self.basis) or a change
# in target (self.target != self.basis.target)
#
# Placement can also be a basis if it has flags
# (self.flags) or its basis has flags (self.basis.flags)
return (not self.basis or
self.target != self.basis.target or
(self.flags and not self.basis.flags) or
(not self.flags and self.basis.flags) or
self.force)
def force_significant(self):
if not self.is_significant():
self.force = True
if self.basis:
self.basis.add_exclusion(self)
def add_exclusion(self, exclusion):
self.exclusions.add(exclusion)
def add_subplacement(self, subplacement):
self.subplacements.add(subplacement)
class EntityNode():
def __init__(self, parent, name):
self.children = [] self.children = []
self.parent = parent self.parent = parent
self.name = name self.name = name
self.child_node = None self.child_t = EntityNode
self.entity = None self.entity = None
self.placements = dict()
self.sections = dict()
# A node inherits the section -> target entries from
# its parent. This is to simplify logic, avoiding
# going up the parental chain to try a 'basis' rule
# in creating exclusions. This relies on the fact that
# the mappings must be inserted from least to most specific.
# This sort is done in generate_rules().
if sections:
for (s, v) in sections.items():
self.sections[s] = RuleNode.Section(v.target, [], [])
def add_exclusion(self, sections, exclusion):
self.sections[sections].exclusions.add(exclusion)
# Recursively create exclusions in parents
if self.parent:
self.exclude_from_parent(sections)
def add_sections(self, sections, target):
try:
_sections = self.sections[sections]
if not _sections.explicit:
_sections.target = target
_sections.explicit = True
else:
if target != _sections.target:
raise GenerationException('Sections mapped to multiple targets')
except KeyError:
self.sections[sections] = RuleNode.Section(target, [], True)
def exclude_from_parent(self, sections):
self.parent.add_exclusion(sections, self.entity)
def add_child(self, entity): def add_child(self, entity):
child_specificity = self.entity.specificity.value + 1 child_specificity = self.entity.specificity.value + 1
@@ -99,7 +110,7 @@ class RuleNode():
assert(len(child) <= 1) assert(len(child) <= 1)
if not child: if not child:
child = self.child_node(self, name, self.sections) child = self.child_t(self, name)
self.children.append(child) self.children.append(child)
else: else:
child = child[0] child = child[0]
@@ -125,151 +136,176 @@ class RuleNode():
return commands return commands
def add_node_child(self, entity, sections, target, sections_db): def get_node_output_commands(self):
commands = collections.defaultdict(list)
for sections in self.get_output_sections():
placement = self.placements[sections]
if placement.is_significant():
assert(placement.node == self)
keep = False
sort = None
surround = []
placement_flags = placement.flags if placement.flags is not None else []
for flag in placement_flags:
if isinstance(flag, Mapping.Keep):
keep = True
elif isinstance(flag, Mapping.Sort):
sort = (flag.first, flag.second)
else: # emit or align
surround.append(flag)
for flag in surround:
if flag.pre:
if isinstance(flag, Mapping.Emit):
commands[placement.target].append(SymbolAtAddress('_%s_start' % flag.symbol))
else: # align
commands[placement.target].append(AlignAtAddress(flag.alignment))
# This is for expanded object node and symbol node placements without checking for
# the type.
placement_sections = frozenset(placement.sections)
command_sections = sections if sections == placement_sections else placement_sections
command = InputSectionDesc(placement.node.entity, command_sections, [e.node.entity for e in placement.exclusions], keep, sort)
commands[placement.target].append(command)
# Generate commands for intermediate, non-explicit exclusion placements here, so that they can be enclosed by
# flags that affect the parent placement.
for subplacement in placement.subplacements:
if not subplacement.flags and not subplacement.explicit:
command = InputSectionDesc(subplacement.node.entity, subplacement.sections,
[e.node.entity for e in subplacement.exclusions], keep, sort)
commands[placement.target].append(command)
for flag in surround:
if flag.post:
if isinstance(flag, Mapping.Emit):
commands[placement.target].append(SymbolAtAddress('_%s_end' % flag.symbol))
else: # align
commands[placement.target].append(AlignAtAddress(flag.alignment))
return commands
def self_placement(self, sections, target, flags, explicit=True, force=False):
placement = Placement(self, sections, target, flags, explicit, force)
self.placements[sections] = placement
return placement
def child_placement(self, entity, sections, target, flags, sections_db):
child = self.add_child(entity) child = self.add_child(entity)
child.insert(entity, sections, target, sections_db) child.insert(entity, sections, target, flags, sections_db)
def get_node_output_commands(self): def insert(self, entity, sections, target, flags, sections_db):
commands = collections.defaultdict(list)
for sections in self.get_section_keys():
info = self.sections[sections]
if info.exclusions or info.explicit:
command = InputSectionDesc(self.entity, sections, info.exclusions)
commands[info.target].append(command)
return commands
def insert(self, entity, sections, target, sections_db):
if self.entity.specificity == entity.specificity: if self.entity.specificity == entity.specificity:
if self.parent.sections[sections].target != target: # Since specificities match, create the placement in this node.
self.add_sections(sections, target) self.self_placement(sections, target, flags)
self.exclude_from_parent(sections)
else: else:
self.add_node_child(entity, sections, target, sections_db) # If not, create a child node and try to create the placement there.
self.child_placement(entity, sections, target, flags, sections_db)
def get_section_keys(self): def get_output_sections(self):
return sorted(self.sections.keys(), key=' '.join) return sorted(self.placements.keys(), key=' '.join)
class SymbolNode(RuleNode): class SymbolNode(EntityNode):
def __init__(self, parent, name, sections): def __init__(self, parent, name):
RuleNode.__init__(self, parent, name, sections) EntityNode.__init__(self, parent, name)
self.entity = Entity(self.parent.parent.name, self.parent.name, self.name) self.entity = Entity(self.parent.parent.name, self.parent.name)
def insert(self, entity, sections, target, sections_db):
self.add_sections(sections, target)
def get_node_output_commands(self):
commands = collections.defaultdict(list)
for sections in self.get_section_keys():
info = self.sections[sections]
if info.explicit:
command = InputSectionDesc(Entity(self.parent.parent.name, self.parent.name), sections, [])
commands[info.target].append(command)
return commands
class ObjectNode(RuleNode): class ObjectNode(EntityNode):
def __init__(self, parent, name, sections): def __init__(self, parent, name):
RuleNode.__init__(self, parent, name, sections) EntityNode.__init__(self, parent, name)
self.child_node = SymbolNode self.child_t = SymbolNode
self.expanded_sections = dict()
self.entity = Entity(self.parent.name, self.name) self.entity = Entity(self.parent.name, self.name)
self.subplacements = list()
def add_node_child(self, entity, sections, target, sections_db): def child_placement(self, entity, sections, target, flags, sections_db):
if self.sections[sections].target != target: child = self.add_child(entity)
symbol = entity.symbol sym_placement = Placement(child, sections, target, flags, True, dryrun=True)
match_sections = None
obj_sections = sections_db.get_sections(self.parent.name, self.name)
# The basis placement for sym_placement can either be
# an existing placement on this node, or nonexistent.
if sym_placement.is_significant():
try: try:
match_sections = self.expanded_sections[sections] obj_sections = self.placements[sections].sections
except KeyError: except KeyError:
match_sections = [] obj_sections = None
if not obj_sections or obj_sections == sections:
# Expand this section for the first time
found_sections = sections_db.get_sections(self.parent.name, self.name)
obj_sections = []
for s in sections: for s in sections:
match_sections.extend(fnmatch.filter(obj_sections, s)) obj_sections.extend(fnmatch.filter(found_sections, s))
if match_sections: if obj_sections:
symbol = entity.symbol
remove_sections = [s.replace('.*', '.%s' % symbol) for s in sections if '.*' in s] remove_sections = [s.replace('.*', '.%s' % symbol) for s in sections if '.*' in s]
filtered_sections = [s for s in match_sections if s not in remove_sections] filtered_sections = [s for s in obj_sections if s not in remove_sections]
if set(filtered_sections) != set(match_sections): # some sections removed if set(filtered_sections) != set(obj_sections):
child = self.add_child(entity) if sym_placement.basis:
child.insert(entity, frozenset(remove_sections), target, obj_sections) subplace = False
try:
# If existing placement exists, make sure that
# it is emitted.
obj_placement = self.placements[sections]
except KeyError:
# Create intermediate placement.
obj_placement = self.self_placement(sections, sym_placement.basis.target, None, False)
if obj_placement.basis.flags:
subplace = True
# Remember the result for node command generation if subplace:
self.expanded_sections[sections] = filtered_sections obj_placement.basis.add_subplacement(obj_placement)
self.exclude_from_parent(sections) self.subplacements.append(sections)
else:
obj_placement.force_significant()
def get_node_output_commands(self): obj_placement.sections = filtered_sections
commands = collections.defaultdict(list) sym_placement.basis = obj_placement
for sections in self.get_section_keys(): sym_placement.sections = remove_sections
info = self.sections[sections] child.placements[sections] = sym_placement
try: def get_output_sections(self):
match_sections = self.expanded_sections[sections] output_sections = [key for key in self.placements if key not in self.subplacements]
except KeyError: return sorted(output_sections, key=' '.join)
match_sections = []
if match_sections or info.explicit:
command_sections = match_sections if match_sections else sections
command = InputSectionDesc(self.entity, command_sections, [])
commands[info.target].append(command)
return commands
def exclude_from_parent(self, sections):
# Check if there is an explicit emmission for the parent node, which is an archive node.
# If there is, make the exclusion there. If not, make the exclusion on the root node.
# This is to avoid emitting unecessary command and exclusions for the archive node and
# from the root node, respectively.
if self.parent.sections[sections].explicit:
self.parent.add_exclusion(sections, self.entity)
else:
self.parent.parent.add_exclusion(sections, self.entity)
class ArchiveNode(RuleNode): class ArchiveNode(EntityNode):
def __init__(self, parent, name, sections): def __init__(self, parent, name):
RuleNode.__init__(self, parent, name, sections) EntityNode.__init__(self, parent, name)
self.child_node = ObjectNode self.child_t = ObjectNode
self.entity = Entity(self.name) self.entity = Entity(self.name)
class RootNode(RuleNode): class RootNode(EntityNode):
def __init__(self): def __init__(self):
RuleNode.__init__(self, None, Entity.ALL, None) EntityNode.__init__(self, None, Entity.ALL)
self.child_node = ArchiveNode self.child_t = ArchiveNode
self.entity = Entity('*') self.entity = Entity('*')
def insert(self, entity, sections, target, sections_db):
if self.entity.specificity == entity.specificity:
self.add_sections(sections, target)
else:
self.add_node_child(entity, sections, target, sections_db)
class Generation: class Generation:
""" """
Implements generation of placement rules based on collected sections, scheme and mapping fragment. Implements generation of placement based on collected sections, scheme and mapping fragment.
""" """
DEFAULT_SCHEME = 'default'
# Processed mapping, scheme and section entries # Processed mapping, scheme and section entries
EntityMapping = namedtuple('EntityMapping', 'entity sections_group target') EntityMapping = namedtuple('EntityMapping', 'entity sections_group target flags')
def __init__(self, check_mappings=False, check_mapping_exceptions=None): def __init__(self, check_mappings=False, check_mapping_exceptions=None):
self.schemes = {} self.schemes = {}
self.sections = {} self.placements = {}
self.mappings = {} self.mappings = {}
self.check_mappings = check_mappings self.check_mappings = check_mappings
@@ -279,7 +315,7 @@ class Generation:
else: else:
self.check_mapping_exceptions = [] self.check_mapping_exceptions = []
def _build_scheme_dictionary(self): def _prepare_scheme_dictionary(self):
scheme_dictionary = collections.defaultdict(dict) scheme_dictionary = collections.defaultdict(dict)
# Collect sections into buckets based on target name # Collect sections into buckets based on target name
@@ -292,7 +328,7 @@ class Generation:
sections_in_bucket = sections_bucket[target_name] sections_in_bucket = sections_bucket[target_name]
try: try:
sections = self.sections[sections_name] sections = self.placements[sections_name]
except KeyError: except KeyError:
message = GenerationException.UNDEFINED_REFERENCE + " to sections '" + sections_name + "'." message = GenerationException.UNDEFINED_REFERENCE + " to sections '" + sections_name + "'."
raise GenerationException(message, scheme) raise GenerationException(message, scheme)
@@ -324,12 +360,13 @@ class Generation:
return scheme_dictionary return scheme_dictionary
def get_section_strs(self, section): def _prepare_entity_mappings(self, scheme_dictionary, entities):
s_list = [Sections.get_section_data_from_entry(s) for s in section.entries] # Prepare entity mappings processed from mapping fragment entries.
return frozenset([item for sublist in s_list for item in sublist]) def get_section_strs(section):
s_list = [Sections.get_section_data_from_entry(s) for s in section.entries]
return frozenset([item for sublist in s_list for item in sublist])
def _generate_entity_mappings(self, scheme_dictionary, entities): entity_mappings = dict()
entity_mappings = []
for mapping in self.mappings.values(): for mapping in self.mappings.values():
archive = mapping.archive archive = mapping.archive
@@ -345,45 +382,77 @@ class Generation:
message = "'%s' not found" % str(entity) message = "'%s' not found" % str(entity)
raise GenerationException(message, mapping) raise GenerationException(message, mapping)
# Create placement rule for each 'section -> target' in the scheme. if (obj, symbol, scheme_name) in mapping.flags.keys():
# flags = mapping.flags[(obj, symbol, scheme_name)]
# For example. for the mapping entry: # Check if all section->target defined in the current
# # scheme.
# obj (scheme) for (s, t, f) in flags:
# if (t not in scheme_dictionary[scheme_name].keys() or
# The enumrated to: s not in [_s.name for _s in scheme_dictionary[scheme_name][t]]):
#
# obj (section1 -> target1) message = "%s->%s not defined in scheme '%s'" % (s, t, scheme_name)
# obj (section2 -> target2) raise GenerationException(message, mapping)
# ... else:
flags = None
# Create placement for each 'section -> target' in the scheme.
for (target, sections) in scheme_dictionary[scheme_name].items(): for (target, sections) in scheme_dictionary[scheme_name].items():
for section in sections: for section in sections:
entity_mappings.append(Generation.EntityMapping(entity, self.get_section_strs(section), target)) # Find the applicable flags
_flags = []
return entity_mappings if flags:
for (s, t, f) in flags:
if (s, t) == (section.name, target):
_flags.extend(f)
def generate_rules(self, entities): sections_str = get_section_strs(section)
scheme_dictionary = self._build_scheme_dictionary()
entity_mappings = self._generate_entity_mappings(scheme_dictionary, entities) key = (entity, section.name)
entity_mappings.sort(key=lambda m: m.entity) try:
existing = entity_mappings[key]
except KeyError:
existing = None
# Create root nodes dictionary for the default scheme, whose if not existing:
# key is the target name and value is a list of the root nodes for that target. entity_mappings[key] = Generation.EntityMapping(entity, sections_str, target, _flags)
else:
# Check for conflicts.
if (target != existing.target):
raise GenerationException('Sections mapped to multiple targets.', mapping)
# Combine flags here if applicable, to simplify
# insertion logic.
if (_flags or existing.flags):
if ((_flags and not existing.flags) or (not _flags and existing.flags)):
_flags.extend(existing.flags)
entity_mappings[key] = Generation.EntityMapping(entity,
sections_str,
target, _flags)
elif (_flags == existing.flags):
pass
else:
raise GenerationException('Conflicting flags specified.', mapping)
# Sort the mappings by specificity, so as to simplify
# insertion logic.
res = list(entity_mappings.values())
res.sort(key=lambda m: m.entity)
return res
def generate(self, entities):
scheme_dictionary = self._prepare_scheme_dictionary()
entity_mappings = self._prepare_entity_mappings(scheme_dictionary, entities)
root_node = RootNode() root_node = RootNode()
for (target, sections) in scheme_dictionary['default'].items():
for section in sections:
root_node.insert(Entity(), self.get_section_strs(section), target, entities)
for mapping in entity_mappings: for mapping in entity_mappings:
(entity, sections, target) = mapping (entity, sections, target, flags) = mapping
try: try:
root_node.insert(entity, sections, target, entities) root_node.insert(entity, sections, target, flags, entities)
except ValueError as e: except ValueError as e:
raise GenerationException(str(e)) raise GenerationException(str(e))
# Traverse the tree, creating the rules # Traverse the tree, creating the placements
commands = root_node.get_output_commands() commands = root_node.get_output_commands()
return commands return commands
@@ -398,7 +467,7 @@ class Generation:
if isinstance(fragment, Scheme): if isinstance(fragment, Scheme):
dict_to_append_to = self.schemes dict_to_append_to = self.schemes
elif isinstance(fragment, Sections): elif isinstance(fragment, Sections):
dict_to_append_to = self.sections dict_to_append_to = self.placements
else: else:
dict_to_append_to = self.mappings dict_to_append_to = self.mappings

View File

@@ -151,7 +151,7 @@ def main():
raise LdGenFailure('failed to parse %s\n%s' % (fragment_file.name, str(e))) raise LdGenFailure('failed to parse %s\n%s' % (fragment_file.name, str(e)))
generation_model.add_fragments_from_file(fragment_file) generation_model.add_fragments_from_file(fragment_file)
mapping_rules = generation_model.generate_rules(sections_infos) mapping_rules = generation_model.generate(sections_infos)
script_model = LinkerScript(input_file) script_model = LinkerScript(input_file)
script_model.fill(mapping_rules) script_model.fill(mapping_rules)

View File

@@ -17,9 +17,35 @@
from entity import Entity from entity import Entity
class AlignAtAddress():
def __init__(self, alignment):
self.alignment = alignment
def __str__(self):
return ('. = ALIGN(%d);' % self.alignment)
def __eq__(self, other):
return (isinstance(other, AlignAtAddress) and
self.alignment == other.alignment)
class SymbolAtAddress():
def __init__(self, symbol):
self.symbol = symbol
def __str__(self):
return ('%s = ABSOLUTE(.);' % self.symbol)
def __eq__(self, other):
return (isinstance(other, SymbolAtAddress) and
self.symbol == other.symbol)
class InputSectionDesc(): class InputSectionDesc():
def __init__(self, entity, sections, exclusions=None): def __init__(self, entity, sections, exclusions=None, keep=False, sort=None):
assert(entity.specificity != Entity.Specificity.SYMBOL) assert(entity.specificity != Entity.Specificity.SYMBOL)
self.entity = entity self.entity = entity
@@ -34,7 +60,12 @@ class InputSectionDesc():
else: else:
self.exclusions = set() self.exclusions = set()
self.keep = keep
self.sort = sort
def __str__(self): def __str__(self):
sections_string = '( )'
if self.sections: if self.sections:
exclusion_strings = [] exclusion_strings = []
@@ -57,22 +88,48 @@ class InputSectionDesc():
for section in sorted(self.sections): for section in sorted(self.sections):
section_strings.append(section) section_strings.append(section)
sections_string = '(%s)' % ' '.join(section_strings) if self.sort:
else: if self.sort == (None, None):
sections_string = '( )' pattern = 'SORT(%s)'
elif self.sort == ('name', None):
pattern = 'SORT_BY_NAME(%s)'
elif self.sort == ('alignment', None):
pattern = 'SORT_BY_ALIGNMENT(%s)'
elif self.sort == ('init_priority', None):
pattern = 'SORT_BY_INIT_PRIORITY(%s)'
elif self.sort == ('name', 'alignment'):
pattern = 'SORT_BY_NAME(SORT_BY_ALIGNMENT(%s))'
elif self.sort == ('alignment', 'name'):
pattern = 'SORT_BY_ALIGNMENT(SORT_BY_NAME(%s))'
elif self.sort == ('name', 'name'):
pattern = 'SORT_BY_NAME(SORT_BY_NAME(%s))'
elif self.sort == ('alignment', 'alignment'):
pattern = 'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(%s))'
else:
raise Exception('Invalid sort arguments')
command = None section_strings = [(pattern % s) for s in section_strings]
sections_string = '(%s)' % ' '.join(section_strings)
if self.entity.specificity == Entity.Specificity.NONE: if self.entity.specificity == Entity.Specificity.NONE:
command = '*%s' % (sections_string) entry = '*%s' % (sections_string)
elif self.entity.specificity == Entity.Specificity.ARCHIVE: elif self.entity.specificity == Entity.Specificity.ARCHIVE:
command = '*%s:%s' % (self.entity.archive, sections_string) entry = '*%s:%s' % (self.entity.archive, sections_string)
else: else:
command = '*%s:%s.*%s' % (self.entity.archive, self.entity.obj, sections_string) entry = '*%s:%s.*%s' % (self.entity.archive, self.entity.obj, sections_string)
return command if self.keep:
res = 'KEEP(%s)' % entry
else:
res = entry
return res
def __eq__(self, other): def __eq__(self, other):
return (self.entity == other.entity and return (isinstance(other, InputSectionDesc) and
self.entity == other.entity and
self.sections == other.sections and self.sections == other.sections and
self.exclusions == other.exclusions) self.exclusions == other.exclusions and
self.keep == other.keep and
self.sort == other.sort)

View File

@@ -82,3 +82,8 @@ entries:
[scheme:noflash_data] [scheme:noflash_data]
entries: entries:
rodata -> dram0_data rodata -> dram0_data
[mapping:default]
archive: *
entries:
* (default)

View File

@@ -23,11 +23,11 @@ from io import StringIO
from pyparsing import ParseException, ParseFatalException, Word, alphanums from pyparsing import ParseException, ParseFatalException, Word, alphanums
try: try:
from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar, Mapping
from sdkconfig import SDKConfig from sdkconfig import SDKConfig
except ImportError: except ImportError:
sys.path.append('../') sys.path.append('../')
from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar from fragments import FRAGMENT_TYPES, Fragment, FragmentFile, KeyGrammar, Mapping
from sdkconfig import SDKConfig from sdkconfig import SDKConfig
@@ -811,6 +811,208 @@ entries:
with self.assertRaises(ParseException): with self.assertRaises(ParseException):
FragmentFile(test_fragment, self.sdkconfig) FragmentFile(test_fragment, self.sdkconfig)
def test_keep_flag(self):
# Test parsing combinations and orders of flags
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (default);
text->flash_text keep,
rodata->flash_rodata keep keep
""")
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
fragment = fragment_file.fragments[0]
expected = [('text', 'flash_text', [Mapping.Keep()]),
('rodata', 'flash_rodata', [Mapping.Keep(), Mapping.Keep()])]
actual = fragment.flags[('obj1', None, 'default')]
self.assertEqual(expected, actual)
def test_align_flag(self):
# Test parsing combinations and orders of flags
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (default);
text->flash_text align(8),
rodata->flash_rodata align(8, pre),
data->dram0_data align(8, pre, post),
bss->dram0_bss align(8, post),
common->dram0_bss align(8, pre, post) align(8)
""")
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
fragment = fragment_file.fragments[0]
expected = [('text', 'flash_text', [Mapping.Align(8, True, False)]),
('rodata', 'flash_rodata', [Mapping.Align(8, True, False)]),
('data', 'dram0_data', [Mapping.Align(8, True, True)]),
('bss', 'dram0_bss', [Mapping.Align(8, False, True)]),
('common', 'dram0_bss', [Mapping.Align(8, True, True), Mapping.Align(8, True, False)])]
actual = fragment.flags[('obj1', None, 'default')]
self.assertEqual(expected, actual)
# Wrong post, pre order
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (noflash)
text->iram0_text align(8, post, pre)
""")
with self.assertRaises(ParseFatalException):
FragmentFile(test_fragment, self.sdkconfig)
def test_sort_flag(self):
# Test parsing combinations and orders of flags
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (default);
text->flash_text sort(name),
rodata->flash_rodata sort(alignment),
data->dram0_data sort(init_priority),
bss->dram0_bss sort(name, alignment),
common->dram0_bss sort(alignment, name),
iram->iram0_text sort(name, name),
dram->dram0_data sort(alignment, alignment)
""")
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
fragment = fragment_file.fragments[0]
expected = [('text', 'flash_text', [Mapping.Sort('name')]),
('rodata', 'flash_rodata', [Mapping.Sort('alignment')]),
('data', 'dram0_data', [Mapping.Sort('init_priority')]),
('bss', 'dram0_bss', [Mapping.Sort('name', 'alignment')]),
('common', 'dram0_bss', [Mapping.Sort('alignment', 'name')]),
('iram', 'iram0_text', [Mapping.Sort('name', 'name')]),
('dram', 'dram0_data', [Mapping.Sort('alignment', 'alignment')])]
actual = fragment.flags[('obj1', None, 'default')]
self.assertEqual(expected, actual)
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (default)
text->iram0_text sort(name) sort(alignment)
""")
def test_emit_flag(self):
# Test parsing combinations and orders of flags
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (default);
text->flash_text emit(sym1),
rodata->flash_rodata emit(sym2, pre),
data->dram0_data emit(sym3, post),
bss->dram0_bss emit(sym4, pre, post),
common->dram0_bss emit(sym5, pre, post) emit(sym6)
""")
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
fragment = fragment_file.fragments[0]
expected = [('text', 'flash_text', [Mapping.Emit('sym1', True, True)]),
('rodata', 'flash_rodata', [Mapping.Emit('sym2', True, False)]),
('data', 'dram0_data', [Mapping.Emit('sym3', False, True)]),
('bss', 'dram0_bss', [Mapping.Emit('sym4', True, True)]),
('common', 'dram0_bss', [Mapping.Emit('sym5', True, True), Mapping.Emit('sym6', True, True)])]
actual = fragment.flags[('obj1', None, 'default')]
self.assertEqual(expected, actual)
def test_flag_order(self):
# Test that the order in which the flags are specified is retained
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (default);
text->flash_text align(4) keep emit(sym1) align(8) sort(name),
rodata->flash_rodata keep align(4) keep emit(sym1) align(8) align(4) sort(name)
""")
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
fragment = fragment_file.fragments[0]
expected = [('text', 'flash_text', [Mapping.Align(4, True, False),
Mapping.Keep(),
Mapping.Emit('sym1', True, True),
Mapping.Align(8, True, False),
Mapping.Sort('name')]),
('rodata', 'flash_rodata', [Mapping.Keep(),
Mapping.Align(4, True, False),
Mapping.Keep(),
Mapping.Emit('sym1', True, True),
Mapping.Align(8, True, False),
Mapping.Align(4, True, False),
Mapping.Sort('name')])]
actual = fragment.flags[('obj1', None, 'default')]
self.assertEqual(expected, actual)
def test_flags_entries_multiple_flags(self):
# Not an error, generation step handles this, since
# it that step has a more complete information
# about all mappings.
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (default);
text->flash_text align(4) keep emit(sym1) sort(name),
text->flash_text align(4) keep emit(sym1) sort(name)
""")
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
fragment = fragment_file.fragments[0]
expected = [('text', 'flash_text', [Mapping.Align(4, True, False),
Mapping.Keep(),
Mapping.Emit('sym1', True, True),
Mapping.Sort('name')]),
('text', 'flash_text', [Mapping.Align(4, True, False),
Mapping.Keep(),
Mapping.Emit('sym1', True, True),
Mapping.Sort('name')])]
actual = fragment.flags[('obj1', None, 'default')]
self.assertEqual(expected, actual)
def test_flags_entries_multiple_flags_and_entries(self):
# Not an error, generation step handles this, since
# it that step has a more complete information
# about all mappings. This can happen across multiple
# mapping fragments.
test_fragment = self.create_fragment_file(u"""
[mapping:map]
archive: libmain.a
entries:
obj1 (default);
text->flash_text align(4) keep emit(sym1) sort(name)
obj1 (default);
text->flash_text align(4) keep emit(sym1) sort(name)
""")
fragment_file = FragmentFile(test_fragment, self.sdkconfig)
fragment = fragment_file.fragments[0]
expected = [('text', 'flash_text', [Mapping.Align(4, True, False),
Mapping.Keep(),
Mapping.Emit('sym1', True, True),
Mapping.Sort('name')]),
('text', 'flash_text', [Mapping.Align(4, True, False),
Mapping.Keep(),
Mapping.Emit('sym1', True, True),
Mapping.Sort('name')])]
actual = fragment.flags[('obj1', None, 'default')]
self.assertEqual(expected, actual)
class DeprecatedMappingTest(FragmentTest): class DeprecatedMappingTest(FragmentTest):

View File

@@ -33,7 +33,7 @@ from io import StringIO
from entity import Entity, EntityDB from entity import Entity, EntityDB
from fragments import FragmentFile from fragments import FragmentFile
from linker_script import LinkerScript from linker_script import LinkerScript
from output_commands import InputSectionDesc from output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress
from sdkconfig import SDKConfig from sdkconfig import SDKConfig
ROOT = Entity('*') ROOT = Entity('*')
@@ -120,29 +120,30 @@ class GenerationTest(unittest.TestCase):
self.assertEqual(set(expected.keys()), set(actual.keys())) self.assertEqual(set(expected.keys()), set(actual.keys()))
for target in sorted(actual.keys()): for target in sorted(actual.keys()):
message = 'failed target %s' % target
a_cmds = actual[target] a_cmds = actual[target]
e_cmds = expected[target] e_cmds = expected[target]
self.assertEqual(len(a_cmds), len(e_cmds)) self.assertEqual(len(a_cmds), len(e_cmds), message)
for a, e in zip(a_cmds, e_cmds): for a, e in zip(a_cmds, e_cmds):
self.assertEqual(a, e) self.assertEqual(a, e, message)
def get_default(self, target, rules): def get_default(self, target, rules):
return rules[target][0] return rules[target][0]
class DefaultMappingTest(GenerationTest):
def test_rule_generation_default(self): def test_rule_generation_default(self):
# Checks that default rules are generated from # Checks that default rules are generated from
# the default scheme properly and even if no mappings # the default scheme properly and even if no mappings
# are defined. # are defined.
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
self.compare_rules(expected, actual) self.compare_rules(expected, actual)
class DefaultMappingTest(GenerationTest):
def test_default_mapping_lib(self): def test_default_mapping_lib(self):
# Mapping a library with default mapping. This should not emit additional rules, # Mapping a library with default mapping. This should not emit additional rules,
# other than the default ones. # other than the default ones.
@@ -249,7 +250,7 @@ entries:
* (noflash) #1 * (noflash) #1
""" """
self.add_fragments(alt if alt else mapping) self.add_fragments(alt if alt else mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -287,7 +288,7 @@ entries:
""" """
self.add_fragments(alt if alt else mapping) self.add_fragments(alt if alt else mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -326,7 +327,7 @@ entries:
croutine:prvCheckPendingReadyList (noflash) #1 croutine:prvCheckPendingReadyList (noflash) #1
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -382,7 +383,7 @@ entries:
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -443,7 +444,7 @@ entries:
croutine:prvCheckPendingReadyList (default) #2 croutine:prvCheckPendingReadyList (default) #2
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -512,7 +513,7 @@ entries:
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -606,7 +607,7 @@ entries:
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -634,6 +635,40 @@ entries:
self.compare_rules(expected, actual) self.compare_rules(expected, actual)
def test_root_mapping_fragment(self):
# Test creation of a mapping fragment that maps '*'.
# This should generate another default command in iram0_text:
#
# iram0_text
# * (.custom_section) A
# * (.iram .iram.*)
mapping = u"""
[sections:custom_section]
entries:
.custom_section
[scheme:custom_scheme]
entries:
custom_section -> iram0_text
[mapping:default2]
archive: *
entries:
* (custom_scheme) #1
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
# Generate default command A
# Since these are the same 'specificity', the commands
# are arranged alphabetically.
expected['iram0_text'].append(expected['iram0_text'][0])
expected['iram0_text'][0] = InputSectionDesc(ROOT, ['.custom_section'], [])
self.compare_rules(expected, actual)
class AdvancedTest(GenerationTest): class AdvancedTest(GenerationTest):
@@ -671,7 +706,7 @@ entries:
croutine (noflash_data) #2 croutine (noflash_data) #2
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -725,7 +760,7 @@ entries:
croutine (noflash_data) #2 croutine (noflash_data) #2
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -764,7 +799,7 @@ entries:
self.add_fragments(alt if alt else mapping) self.add_fragments(alt if alt else mapping)
with self.assertRaises(GenerationException): with self.assertRaises(GenerationException):
self.generation.generate_rules(self.entities) self.generation.generate(self.entities)
def test_complex_mapping_case(self, alt=None): def test_complex_mapping_case(self, alt=None):
# Test a complex case where an object is mapped using # Test a complex case where an object is mapped using
@@ -818,7 +853,7 @@ entries:
""" """
self.add_fragments(alt if alt else mapping) self.add_fragments(alt if alt else mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -907,7 +942,7 @@ entries:
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -1004,7 +1039,7 @@ entries:
""" """
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -1046,7 +1081,7 @@ entries:
self.add_fragments(mapping) self.add_fragments(mapping)
with self.assertRaises(GenerationException): with self.assertRaises(GenerationException):
self.generation.generate_rules(self.entities) self.generation.generate(self.entities)
def test_disambiguated_obj(self): def test_disambiguated_obj(self):
# Test command generation for disambiguated entry. Should produce similar # Test command generation for disambiguated entry. Should produce similar
@@ -1059,7 +1094,7 @@ entries:
""" """
port = Entity('libfreertos.a', 'port.c') port = Entity('libfreertos.a', 'port.c')
self.add_fragments(mapping) self.add_fragments(mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
flash_text = expected['flash_text'] flash_text = expected['flash_text']
@@ -1085,6 +1120,60 @@ entries:
self.compare_rules(expected, actual) self.compare_rules(expected, actual)
def test_root_mapping_fragment_conflict(self):
# Test that root mapping fragments are also checked for
# conflicts.
#
# 'custom_scheme' entries conflict the 'default' scheme
# entries.
mapping = u"""
[scheme:custom_scheme]
entries:
flash_text -> iram0_text
[mapping:default2]
archive: *
entries:
* (custom_scheme)
"""
self.add_fragments(mapping)
with self.assertRaises(GenerationException):
self.generation.generate(self.entities)
def test_root_mapping_fragment_duplicate(self):
# Same root mappings have no effect.
#
# custom_scheme has the 'iram -> iram0_text' in common with
# default scheme
mapping = u"""
[sections:custom_section]
entries:
.custom_section
[scheme:custom_scheme]
entries:
iram -> iram0_text
custom_section -> iram0_text
[mapping:default2]
archive: *
entries:
* (custom_scheme)
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
# Generate default command A
# Since these are the same 'specificity', the commands
# are arranged alphabetically.
expected['iram0_text'].append(expected['iram0_text'][0])
expected['iram0_text'][0] = InputSectionDesc(ROOT, ['.custom_section'], [])
self.compare_rules(expected, actual)
class ConfigTest(GenerationTest): class ConfigTest(GenerationTest):
# Test command generation with conditions # Test command generation with conditions
@@ -1119,7 +1208,7 @@ entries:
self.add_fragments(scheme) self.add_fragments(scheme)
self.add_fragments(alt if alt else mapping) self.add_fragments(alt if alt else mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
if perf >= 1: if perf >= 1:
@@ -1145,6 +1234,11 @@ entries:
# Test that proper commands are generated # Test that proper commands are generated
# in conditional mapping entries. # in conditional mapping entries.
mapping = u""" mapping = u"""
[mapping:default]
archive: *
entries:
* (default)
[mapping:test] [mapping:test]
archive: lib.a archive: lib.a
entries: entries:
@@ -1165,7 +1259,7 @@ entries:
self.generation.mappings = {} self.generation.mappings = {}
self.add_fragments(alt if alt else mapping) self.add_fragments(alt if alt else mapping)
actual = self.generation.generate_rules(self.entities) actual = self.generation.generate(self.entities)
expected = self.generate_default_rules() expected = self.generate_default_rules()
if perf_level < 4 and perf_level > 0: if perf_level < 4 and perf_level > 0:
@@ -1208,6 +1302,11 @@ entries:
def test_conditional_entries_legacy_mapping_fragment(self): def test_conditional_entries_legacy_mapping_fragment(self):
# Test conditional entries on legacy mapping fragment grammar. # Test conditional entries on legacy mapping fragment grammar.
mapping = u""" mapping = u"""
[mapping:default]
archive: *
entries:
* (default)
[mapping] [mapping]
archive: lib.a archive: lib.a
entries: entries:
@@ -1228,6 +1327,11 @@ entries:
# Test conditional entries on legacy mapping fragment grammar # Test conditional entries on legacy mapping fragment grammar
# across multiple fragments. # across multiple fragments.
mapping = u""" mapping = u"""
[mapping:default]
archive: *
entries:
* (default)
[mapping] [mapping]
archive: lib.a archive: lib.a
entries: entries:
@@ -1257,6 +1361,11 @@ entries:
# Test conditional entries on new mapping fragment grammar. # Test conditional entries on new mapping fragment grammar.
# across multiple fragments. # across multiple fragments.
mapping = u""" mapping = u"""
[mapping:default]
archive: *
entries:
* (default)
[mapping:base] [mapping:base]
archive: lib.a archive: lib.a
entries: entries:
@@ -1282,5 +1391,442 @@ entries:
self.test_conditional_mapping(mapping) self.test_conditional_mapping(mapping)
class FlagTest(GenerationTest):
# Test correct generation of mapping fragment entries
# with flags.
def test_flags_basics(self):
# Test that input section commands additions are done (keep, sort).
# Test that order dependent commands are properly generated (align, emit)
# Normally, if an entry has the same mapping as parent, commands.
# are not emitted for them. However, if there are flags, they should be -
# only for the scheme entries that have flags, though.
# Flag entries split across multiple entries work.
#
# flash_text
# *((EXCLUDE_FILE(libfreertos:timers libfreertos:croutine).text ...) A
# KEEP(* (SORT_BY_NAME(EXCLUDE_FILE(libfreertos:timers).text) ...) B
#
# flash_rodata
# *((EXCLUDE_FILE(libfreertos:timers) .rodata ...) C
# _sym2_start D.1
# . = ALIGN(4) E.1
# KEEP(* (EXCLUDE_FILE(libfreertos:timers) .rodata ...) F
# _sym2_end D.2
# . = ALIGN(4) E.2
#
# iram0_text
# *(.iram .iram.*)
# . = ALIGN(4) G.1
# _sym1_start H.1
# libfreertos.a:croutine(.text .literal ...) I
# . = ALIGN(4) G.2
# _sym1_end H.2
mapping = u"""
[mapping:test]
archive: libfreertos.a
entries:
croutine (noflash_text);
text->iram0_text align(4, pre, post) emit(sym1, pre, post) #1
timers (default);
text->flash_text keep sort(name) #2
timers (default);
rodata->flash_rodata emit(sym2, pre, post) align(4, pre, post) #3
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
flash_text = expected['flash_text']
iram0_text = expected['iram0_text']
flash_rodata = expected['flash_rodata']
# Exclusions in flash_text for timers and croutine A
flash_text[0].exclusions.add(CROUTINE)
flash_text[0].exclusions.add(TIMERS)
# Command for #3 B
flash_text.append(InputSectionDesc(TIMERS, flash_text[0].sections, [], keep=True, sort=('name', None)))
# Exclusions in flash_rodata for timers C
flash_rodata[0].exclusions.add(TIMERS)
# Commands for #3 D.1, E.1, F, D.2, E.2
flash_rodata.append(SymbolAtAddress('_sym2_start'))
flash_rodata.append(AlignAtAddress(4))
flash_rodata.append(InputSectionDesc(TIMERS, flash_rodata[0].sections, []))
flash_rodata.append(SymbolAtAddress('_sym2_end'))
flash_rodata.append(AlignAtAddress(4))
# Commands for # G.1, H.1, I, G.2, H.2
iram0_text.append(AlignAtAddress(4))
iram0_text.append(SymbolAtAddress('_sym1_start'))
iram0_text.append(InputSectionDesc(CROUTINE, flash_text[0].sections, []))
iram0_text.append(AlignAtAddress(4))
iram0_text.append(SymbolAtAddress('_sym1_end'))
self.compare_rules(expected, actual)
def test_flags_intermediate_exclusion_command_root(self):
# Test that intermediate exclusion commands from root-level commands
# are included in the flags.
#
# flash_text
# _sym1_start A.1
# KEEP(* (EXCLUDE_FILE(libfreertos:croutine).text ...) B
# KEEP(libfreertos.a:croutine(...))) C
# _sym1_end A.2
#
# iram0_text
# *(.iram .iram.*)
# libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D
mapping = u"""
[mapping:default]
archive: *
entries:
# 1
* (default);
text->flash_text emit(sym1) keep #2
[mapping:test]
archive: libfreertos.a
entries:
croutine:prvCheckPendingReadyList (noflash_text) #3
"""
self.generation.mappings = {}
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
flash_text = expected['flash_text']
iram0_text = expected['iram0_text']
# Command for #2, pre A.1
flash_text.insert(0, SymbolAtAddress('_sym1_start'))
# Command for #1 with keep B
# and exclusion for #3
flash_text[1].keep = True
flash_text[1].exclusions.add(CROUTINE)
# Implicit exclusion command for #3 C
croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
filtered_sections.append('.text')
flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True))
# Command for #2, post A.2
flash_text.append(SymbolAtAddress('_sym1_end'))
# Command for #3 D
iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
self.compare_rules(expected, actual)
def test_flags_intermediate_exclusion_command_lib(self):
# Test that intermediate exclusion commands from lib-level commands
# are included in the flags.
#
# flash_text
# *(EXCLUDE_FILE(libfreertos.a).text ...)
# _sym1_start A.1
# KEEP(libfreertos.a(EXCLUDE_FILE(libfreertos:croutine).text.* ...)) B
# KEEP(libfreertos.a:croutine(...))) C
# _sym1_end A.2
#
# iram0_text
# *(.iram .iram.*)
# libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D
mapping = u"""
[mapping:test]
archive: libfreertos.a
entries:
# 1
* (default);
text->flash_text emit(sym1) keep #2
croutine:prvCheckPendingReadyList (noflash_text) #3
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
flash_text = expected['flash_text']
iram0_text = expected['iram0_text']
# Command for #2, pre A.1
flash_text.append(SymbolAtAddress('_sym1_start'))
flash_text[0].exclusions.add(FREERTOS)
# Command for #1 with keep B
# and exclusion for #3
flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True))
# Implicit exclusion command for #3 C
croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
filtered_sections.append('.text')
flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True))
# Command for #2, post A.2
flash_text.append(SymbolAtAddress('_sym1_end'))
# Command for #3 C
iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
self.compare_rules(expected, actual)
def test_flags_intermediate_exclusion_command_obj(self):
# Test that intermediate exclusion commands from obj-level commands
# are included in the flags.
#
# flash_text
# *(EXCLUDE_FILE(libfreertos.a).text ...)
# _sym1_start A.1
# KEEP(libfreertos.a:croutine(...))) B
# _sym1_end A.2
#
# iram0_text
# *(.iram .iram.*)
# libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) C
mapping = u"""
[mapping:test]
archive: libfreertos.a
entries:
# 1
croutine (default);
text->flash_text emit(sym1) keep #2
croutine:prvCheckPendingReadyList (noflash_text) #3
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
flash_text = expected['flash_text']
iram0_text = expected['iram0_text']
# Command for #2, pre A.1
flash_text.append(SymbolAtAddress('_sym1_start'))
flash_text[0].exclusions.add(CROUTINE)
# Implicit exclusion command for #3 B
croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
filtered_sections.append('.text')
flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), [], keep=True))
# Command for #2, post A.2
flash_text.append(SymbolAtAddress('_sym1_end'))
# Command for #3 C
iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
self.compare_rules(expected, actual)
def test_flags_separate_exclusion_command_if_explicit_root(self):
# Explicit commands are separated from the parent's flags.
#
# flash_text
# _sym1_start A.1
# KEEP(* (EXCLUDE_FILE(libfreertos:croutine).text ...) B
# _sym1_end A.2
# KEEP(libfreertos.a:croutine(...))) C
#
# iram0_text
# *(.iram .iram.*)
# libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D
mapping = u"""
[mapping:default]
archive: *
entries:
# 1
* (default);
text->flash_text emit(sym1) keep #2
[mapping:test]
archive: libfreertos.a
entries:
croutine (default) #3
croutine:prvCheckPendingReadyList (noflash_text) #4
"""
self.generation.mappings = {}
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
flash_text = expected['flash_text']
iram0_text = expected['iram0_text']
# Command for #2, pre A.1
flash_text.insert(0, SymbolAtAddress('_sym1_start'))
# Command for #1 with keep B
# and exclusion for #3
flash_text[1].keep = True
flash_text[1].exclusions.add(CROUTINE)
# Command for #2, post A.2
flash_text.append(SymbolAtAddress('_sym1_end'))
# Command for #3 C
croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
filtered_sections.append('.text')
flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
# Command for #4 D
iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
self.compare_rules(expected, actual)
def test_flags_separate_exclusion_command_if_explicit_lib(self):
# Explicit commands are separated from the parent's flags.
#
# flash_text
# *(EXCLUDE_FILE(libfreertos.a).text ...)
# _sym1_start A.1
# KEEP(libfreertos.a(EXCLUDE_FILE(libfreertos:croutine).text.* ...)) B
# _sym1_end A.2
# KEEP(libfreertos.a:croutine(...))) C
#
# iram0_text
# *(.iram .iram.*)
# libfreertos.a:croutine(.text.prvCheckPendingReadyList ...) D
mapping = u"""
[mapping:test]
archive: libfreertos.a
entries:
# 1
* (default);
text->flash_text emit(sym1) keep
croutine (default) #2
croutine:prvCheckPendingReadyList (noflash_text) #3
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
flash_text = expected['flash_text']
iram0_text = expected['iram0_text']
# Command for #2, pre A.1
flash_text.append(SymbolAtAddress('_sym1_start'))
flash_text[0].exclusions.add(FREERTOS)
# Command for #1 with keep B
# and exclusion for #3
flash_text.append(InputSectionDesc(FREERTOS, flash_text[0].sections, [CROUTINE], keep=True))
# Command for #2, post A.2
flash_text.append(SymbolAtAddress('_sym1_end'))
# Implicit exclusion command for #3 C
croutine_sections = self.entities.get_sections('libfreertos.a', 'croutine')
filtered_sections = fnmatch.filter(croutine_sections, '.literal.*')
filtered_sections.extend(fnmatch.filter(croutine_sections, '.text.*'))
filtered_sections = [s for s in filtered_sections if not s.endswith('prvCheckPendingReadyList')]
filtered_sections.append('.text')
flash_text.append(InputSectionDesc(CROUTINE, set(filtered_sections), []))
# Command for #3 C
iram0_text.append(InputSectionDesc(CROUTINE, set(['.text.prvCheckPendingReadyList', '.literal.prvCheckPendingReadyList']), []))
self.compare_rules(expected, actual)
def test_flag_additions(self):
# Test ability to add flags as long as no other mapping fragments
# does the same thing.
mapping = u"""
[mapping:default_add_flag]
archive: *
entries:
* (default);
text->flash_text keep
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
flash_text = expected['flash_text']
flash_text[0].keep = True
self.compare_rules(expected, actual)
def test_flags_flag_additions_duplicate(self):
# Test same flags added to same entity - these
# are ignored.
mapping = u"""
[mapping:default_add_flag_1]
archive: *
entries:
* (default);
text->flash_text keep
[mapping:default_add_flag_2]
archive: *
entries:
* (default);
text->flash_text keep
"""
self.add_fragments(mapping)
actual = self.generation.generate(self.entities)
expected = self.generate_default_rules()
flash_text = expected['flash_text']
flash_text[0].keep = True
self.compare_rules(expected, actual)
def test_flags_flag_additions_conflict(self):
# Test condition where multiple fragments specifies flags
# to same entity - should generate exception.
mapping = u"""
[mapping:default_add_flag_1]
archive: *
entries:
* (default);
text->flash_text align(2)
[mapping:default_add_flag_2]
archive: *
entries:
* (default);
text->flash_text emit(sym1)
"""
self.add_fragments(mapping)
with self.assertRaises(GenerationException):
self.generation.generate(self.entities)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -19,10 +19,10 @@ import sys
import unittest import unittest
try: try:
from output_commands import InputSectionDesc from output_commands import AlignAtAddress, InputSectionDesc, SymbolAtAddress
except ImportError: except ImportError:
sys.path.append('../') sys.path.append('../')
from output_commands import InputSectionDesc from output_commands import InputSectionDesc, SymbolAtAddress, AlignAtAddress
from entity import Entity from entity import Entity
@@ -34,7 +34,7 @@ CROUTINE = Entity('libfreertos.a', 'croutine')
class InputSectionDescTest(unittest.TestCase): class InputSectionDescTest(unittest.TestCase):
def test_output_00(self): def test_catch_all_placement(self):
# Test default (catch-all) command # Test default (catch-all) command
expected = '*(.literal .literal.* .text .text.*)' expected = '*(.literal .literal.* .text .text.*)'
@@ -44,7 +44,7 @@ class InputSectionDescTest(unittest.TestCase):
desc = InputSectionDesc(Entity(Entity.ALL), SECTIONS) desc = InputSectionDesc(Entity(Entity.ALL), SECTIONS)
self.assertEqual(expected, str(desc)) self.assertEqual(expected, str(desc))
def test_output_01(self): def test_lib_placement(self):
# Test library placement command # Test library placement command
expected = '*libfreertos.a:(.literal .literal.* .text .text.*)' expected = '*libfreertos.a:(.literal .literal.* .text .text.*)'
@@ -60,7 +60,7 @@ class InputSectionDescTest(unittest.TestCase):
desc = InputSectionDesc(Entity('libfreertos.a', Entity.ALL, Entity.ALL), SECTIONS) desc = InputSectionDesc(Entity('libfreertos.a', Entity.ALL, Entity.ALL), SECTIONS)
self.assertEqual(expected, str(desc)) self.assertEqual(expected, str(desc))
def test_output_02(self): def test_obj_placement(self):
# Test object placement command # Test object placement command
expected = '*libfreertos.a:croutine.*(.literal .literal.* .text .text.*)' expected = '*libfreertos.a:croutine.*(.literal .literal.* .text .text.*)'
@@ -79,7 +79,7 @@ class InputSectionDescTest(unittest.TestCase):
desc = InputSectionDesc(Entity('libfreertos.a', 'croutine.c'), SECTIONS) desc = InputSectionDesc(Entity('libfreertos.a', 'croutine.c'), SECTIONS)
self.assertEqual(expected, str(desc)) self.assertEqual(expected, str(desc))
def test_output_03(self): def test_invalid_entity(self):
# Invalid entity specification # Invalid entity specification
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
InputSectionDesc(Entity('libfreertos.a', 'croutine', 'prvCheckPendingReadyList'), SECTIONS) InputSectionDesc(Entity('libfreertos.a', 'croutine', 'prvCheckPendingReadyList'), SECTIONS)
@@ -90,7 +90,7 @@ class InputSectionDescTest(unittest.TestCase):
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
InputSectionDesc(Entity('libfreertos.a', 'croutine'), SECTIONS, [Entity('libfreertos.a', 'croutine', 'prvCheckPendingReadyList')]) InputSectionDesc(Entity('libfreertos.a', 'croutine'), SECTIONS, [Entity('libfreertos.a', 'croutine', 'prvCheckPendingReadyList')])
def test_output_04(self): def test_exclusions(self):
# Test exclusions # Test exclusions
# Library # Library
@@ -129,13 +129,114 @@ class InputSectionDescTest(unittest.TestCase):
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')]) desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')])
self.assertEqual(expected, str(desc)) self.assertEqual(expected, str(desc))
def test_output_05(self): def test_empty_sections(self):
# Test empty sections # Test empty sections
expected = '*libfreertos.a:croutine.*( )' expected = '*libfreertos.a:croutine.*( )'
desc = InputSectionDesc(Entity('libfreertos.a', 'croutine'), []) desc = InputSectionDesc(Entity('libfreertos.a', 'croutine'), [])
self.assertEqual(expected, str(desc)) self.assertEqual(expected, str(desc))
def test_keep(self):
# Test keep
expected = 'KEEP(*libfreertos.a:croutine.*( ))'
desc = InputSectionDesc(Entity('libfreertos.a', 'croutine'), [], keep=True)
self.assertEqual(expected, str(desc))
def test_sort(self):
# Test sort
expected = ('*libfreertos.a:croutine.*('
'SORT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal) '
'SORT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*) '
'SORT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text) '
'SORT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*)'
')')
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=(None, None))
self.assertEqual(expected, str(desc))
expected = ('*libfreertos.a:croutine.*('
'SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal) '
'SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*) '
'SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text) '
'SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*)'
')')
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('name', None))
self.assertEqual(expected, str(desc))
expected = ('*libfreertos.a:croutine.*('
'SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal) '
'SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*) '
'SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text) '
'SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*)'
')')
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('alignment', None))
self.assertEqual(expected, str(desc))
expected = ('*libfreertos.a:croutine.*('
'SORT_BY_INIT_PRIORITY(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal) '
'SORT_BY_INIT_PRIORITY(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*) '
'SORT_BY_INIT_PRIORITY(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text) '
'SORT_BY_INIT_PRIORITY(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*)'
')')
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('init_priority', None))
self.assertEqual(expected, str(desc))
expected = ('*libfreertos.a:croutine.*('
'SORT_BY_NAME(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal)) '
'SORT_BY_NAME(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*)) '
'SORT_BY_NAME(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text)) '
'SORT_BY_NAME(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*))'
')')
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('name', 'alignment'))
self.assertEqual(expected, str(desc))
expected = ('*libfreertos.a:croutine.*('
'SORT_BY_NAME(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal)) '
'SORT_BY_NAME(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*)) '
'SORT_BY_NAME(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text)) '
'SORT_BY_NAME(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*))'
')')
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('name', 'name'))
self.assertEqual(expected, str(desc))
expected = ('*libfreertos.a:croutine.*('
'SORT_BY_ALIGNMENT(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal)) '
'SORT_BY_ALIGNMENT(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*)) '
'SORT_BY_ALIGNMENT(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text)) '
'SORT_BY_ALIGNMENT(SORT_BY_NAME(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*))'
')')
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('alignment', 'name'))
self.assertEqual(expected, str(desc))
expected = ('*libfreertos.a:croutine.*('
'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal)) '
'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .literal.*)) '
'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text)) '
'SORT_BY_ALIGNMENT(SORT_BY_ALIGNMENT(EXCLUDE_FILE(*libfreertos.a:croutine.c.*) .text.*))'
')')
desc = InputSectionDesc(CROUTINE, SECTIONS, [Entity('libfreertos.a', 'croutine.c')], keep=False, sort=('alignment', 'alignment'))
self.assertEqual(expected, str(desc))
class SymbolAtAddressTest(unittest.TestCase):
def test_symbol(self):
symbol = 'test_symbol'
expected = '%s = ABSOLUTE(.);' % symbol
desc = SymbolAtAddress(symbol)
self.assertEqual(expected, str(desc))
class AlignAtAddressTest(unittest.TestCase):
def test_align(self):
align = 8
expected = '. = ALIGN(%d);' % 8
desc = AlignAtAddress(align)
self.assertEqual(expected, str(desc))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()