def _parse_file(self, filename): """ """ logging.debug("Parse %s" % filename) try: # read the time of the last change self.modify_time = max(self.modify_time, os.stat(filename).st_mtime) # parse the xml-file xmltree = et.parse(filename).getroot() except OSError as e: raise ParserException(e) except (xml.parsers.expat.ExpatError, xml.etree.ElementTree.ParseError) as e: raise ParserException("while parsing xml-file '%s': %s" % (filename, e)) try: import lxml.etree # for validating # validate against the embedded DTD file try: parser = lxml.etree.XMLParser(dtd_validation=True) dummy = lxml.etree.parse(filename, parser) except lxml.etree.XMLSyntaxError as e: raise ParserException("Validation error in '%s': %s" % (filename, e)) else: logging.debug("Validation OK!") except ImportError as e: logging.warning("Warning: couldn't load 'lxml' module. No validation done!") # search for include and reference nodes and parse # the specified files first for node in xmltree.findall('include'): include_file = node.text if not os.path.isabs(include_file): include_file = os.path.join(self.include_path, include_file) self.include_path = os.path.dirname(os.path.abspath(include_file)) self._parse_file(include_file) self._parse_types(xmltree) self._evaluate_types(xmltree) self._create_type_hierarchy() self._parse_events(xmltree) self._check_events() self._parse_components(xmltree) self._evaluate_components(xmltree) self._parse_container(xmltree) # create expanded versions for all types and components for type in self.tree.types: type.flattened() for component in self.tree.components: component.flattened() self.tree.components.updateIndex() for container in self.tree.container: container.updateIndex()
def _makeRelativeToPlatform(self, file_name): """ All file names are relative to the xml device description file. This function makes them relative to the platform directory. That makes a lot of sense because the generated files are going to retain the same structure in this context (this sentence admittedly does not make as much sense...) """ # Check if we need to go up directories (if path starts with '../') go_up = 0 if file_name.startswith( './'): # do not know why you would want to do this file_name = file_name[ 2:] # but whatever => go ahead (btw. this code was written at 1:00 am) while file_name.startswith('../'): go_up = go_up + 1 file_name = file_name[3:] path_length = len(self.path.split(os.sep)) if go_up > path_length: raise ParserException( "Illegal file name: %s. Must not leave the platform directory." % (file_name)) path = os.sep.join(self.path.split(os.sep)[:(path_length - go_up)]) file_name = os.path.join(path, file_name) return file_name
def __addExtending(self, extending): for struct in self.extending: if struct.typeIdentifier == extending.typeIdentifier: raise ParserException( "Duplicate TypeIdentifier '%s' in Struct group extending '%s'. ('%s' and '%s')" % (extending.typeIdentifier.name, self.name, struct.name, extending.name)) self.extending.append(extending)
def evaluate(self, tree): self.description = xml_utils.get_description(self.node) self.string = xml_utils.get_string(self.node) self.unit = self.node.get('unit') try: self.subtype = SubType(self.node.get('type'), tree.types) except ParserException as e: raise ParserException("Error in definition of typedef '%s': %s" % (self.name, e))
def _check_events(self): eventIds = {} for event in self.tree.events: id = event.id if id in eventIds: raise ParserException("Duplicate Event-Identifier, '0x%02x' is used for '%s' and '%s'!" % (id, event.name, eventIds[id].name)) else: eventIds[id] = event
def evaluate(self, tree): if self.node is None: return self.description = xml_utils.get_description(self.node) self.string = xml_utils.get_string(self.node) for node in self.node.findall('element'): try: self.elements.append(self.Element(node, tree)) except ParserException as e: raise ParserException( "Error in definition of struct '%s': %s!" % (self.name, e)) basetype = self.node.get('extends') if basetype is not None: try: self.extends = tree.types[basetype] if not self.extends.isStruct: raise ParserException( "Struct '%s' is derived from non struct '%s'!" % (self.name, self.extends.name)) if self.extends.extends: raise ParserException( "Struct '%s' extends struct '%s'. Structs are only allowed to extend from those Structs, which do not extend anything!" % (self.name, self.extends.name)) self.__typeIdentifierName = self.node.get('typeIdentifier') if self.__typeIdentifierName is None: raise ParserException( "Struct '%s' does extend '%s', but does not provide the attribute 'typeIdentifier'!" % (self.name, self.extends.name)) except KeyError: raise ParserException( "Unknown super type '%s' in struct '%s'!" % (basetype, self.name)) self.node = None
def __init__(self, node): """ Constructor The name of the element has to be all upper case with underscores. """ self.name = node.get('name') if not re.match("^[0-9A-Z_]*$", self.name): raise ParserException( "Attribute name of element in enum has to be UPPER_UNDERSCORE_STYLE (found: '%s')" % (self.name)) self.string = node.get('string') if self.string is None: self.string = self.name self.description = xml_utils.get_description(node) self.string = xml_utils.get_string(node) value = node.get('value') self.value = None if (value is None) else int(value, 0)
def __init__(self, value, types): """ Constructor Keyword Arguments: value -- type name types -- list of all available types """ self.raw = value if value.endswith(']'): self.isArray = True self.name, number = value.split('[') self.count = number[:-1] else: self.isArray = False self.count = 1 self.name = value try: self.type = types[self.name] except KeyError: raise ParserException("Unknown type '%s'" % self.name) self.size = None
def __init__(self, node, tree): """ Constructor Keyword arguments: node -- XML node defining this event tree -- currently evaluted communication structure tree """ self.name = node.get('name') utils.check_name(self.name) self.id = xml_utils.get_identifier(node) self.description = xml_utils.get_description(node) self.rate = node.get('rate') type = node.get('type') if type is None: self.type = None else: try: self.type = tree.types[type] except KeyError as e: raise ParserException( "Type '%s' is not defined. Used by Event '%s')" % (type, self.name))
def create_hierarchy(self): """ Create hierarchy For this method self.size = 0 is used as sepecial value to detect loops in the definition of types. In normal operation size will never be zero, only during hierarchy creation. """ if self.level is not None: return self.size = 0 size = 0 self.level = 0 for element in self.elements: if element.size == 0: raise ParserException( "Loop in the definition of '%s' and '%s' detected!" % (self.name, self.element.name)) element.create_hierarchy() size += element.size self.level = max(self.level, element.level) if self.extends is not None: if self.extends.size == 0: raise ParserException( "Loop in the definition of '%s' and '%s' detected!" % (self.name, self.extends.name)) self.extends.create_hierarchy() typeIdentifierStructElement = self.extends.elements[0] if not typeIdentifierStructElement.subtype.type.isEnum: raise ParserException("Struct '%s' is extended by Struct '%s'. " \ "Structs which are extended by other must have an element named " \ "'type' of any enum type as their first element! It is used for " \ "type distinguishing at runtime." \ % (self.extends.name, self.name)) if not typeIdentifierStructElement.name == 'type': raise ParserException("Struct '%s' is extended by Struct '%s'. Structs" \ "which are extended by other must have an element named 'type' as" \ "their first element! It is used for type distinguishing at runtime." \ % (self.extends.name, self.name)) for enumElement in typeIdentifierStructElement.subtype.type.elements: if enumElement.name == self.__typeIdentifierName: self.typeIdentifier = enumElement break if not self.typeIdentifier: raise ParserException("Struct '%s' extends Struct '%s', but it's " \ "typeIdentifier '%s' is not member of enum '%s' which is the " \ "type of '%s.type'." % (self.name, self.extends.name, self.__typeIdentifierName, typeIdentifierStructElement.subtype.type.name, self.extends.name)) self.extends.__addExtending(self) size += self.extends.size self.level = max(self.level, self.extends.level) if size > 48: raise ParserException("Struct '%s' is with %i Byte too big. The maximum " \ "packet size is 48 Byte!" % (self.name, size)) self.size = size self.level += 1
def getBuildList( self, platform_path, device_id, source_file_extentions=['.cpp', '.c', '.sx', '.S', '.h', '.hpp']): """ The data is put into a list of the format: [ ['static_file_name', 'static_output_file_name'], ['template_src_file','template_target_file',{'Dict': 'a', 'of': 'b', 'defines': 's'}]] Note: all file paths are relative to the platform path. """ # Turn Device String into Device Identifier if isinstance(device_id, str): device_id = DeviceIdentifier(device_id) # Initialize Return List build = [] # Check if there is a driver file and open if self.filename != None: try: f = os.path.join(platform_path, self.filename) xmltree = et.parse(f).getroot() except OSError as e: raise ParserException(e) except (xml.parsers.expat.ExpatError, xml.etree.ElementTree.ParseError) as e: raise ParserException("while parsing xml-file '%s': %s" % (f, e)) # TODO: Validate node = xmltree[0] # Check if name and type match # If they do not, why should everything else be correct?.... if node.get('type') != self.type: raise ParserException( "Error: Driver: Type in device file (%s) and type in driver file (%s) do not match. File: %s" % (self.type, node.get('type'), f)) if node.get('name') != self.name: raise ParserException( "Error: Driver: Name in device file (%s) and name in driver file (%s) do not match. File: %s" % (self.name, node.get('name'), f)) # Do we only have one default instance? if self.instances == None: self._parseDriverXml(device_id, node, 'default', build, {}) else: # handle several or at least one specific instance # Initialize Internal Target File Dictionary targets = {} for instance in self.instances: self._parseDriverXml(device_id, node, instance, build, targets) else: # if no xml driver file exists, just add all files that look like source files # Query all files in directory dir = os.path.join(platform_path, self.path) if not os.path.exists(dir): self.log.warn( "'%s' implementation for target '%s' does not exist!" % (self.path, device_id.string)) return build for source_file in os.listdir(dir): # Detect Static Source and Header Files if os.path.splitext(source_file)[1] in source_file_extentions: source_file = self._makeRelativeToPlatform(source_file) build.append([source_file, source_file]) # Detect Template Files elif os.path.splitext(source_file)[1] == '.in': template = self._makeRelativeToPlatform(source_file) output = self._makeRelativeToPlatform(source_file[:-3]) substitutions = self.substitutions substitutions.update({'output': os.path.split(output)[1]}) build.append([template, output, self.substitutions]) return build
def _parseDriverXml(self, device_id, driver_node, instance_id, build_list, targets): # First parse parameters: for node in driver_node: if node.tag == 'parameter': p = Parameter(node, self.log) # 1.) search for user parameters value = p.getValueFromUserParameterDict( self.user_parameters, instance_id) # 2.) if none are found search for parameters in driver file if value == None: value = p.getValueFromParameterDict( self.device_parameters, instance_id) # 3.) else use the default value if value == None: value = p.default # 4.) add value found to the substitution dict p.addValueToSubstitutions(self.substitutions, value) elif node.tag not in ['static', 'template']: f = DeviceElementBase(self, node) if f.appliesTo(device_id, self.properties): self.log.debug( "Found driver file substitution '%s' with value '%s'. In '%s/%s' driver xml" % (node.tag, node.text, self.type, self.name)) self.substitutions.update({node.tag: node.text}) # Then parse needed static/template files for node in driver_node: # Skip PArameters: if node.tag == 'parameter': continue # Check if instance id fits: if node.get('instances') != None and instance_id != None: if instance_id not in node.get('instances').split(','): continue if node.text == None or len(node.text) <= 0: self.log.error("Empty '%s' node in '%s/%s' driver xml." % (node.tag, self.type, self.name)) raise ParserException( "Error: Empty '%s' node in '%s/%s' driver xml." % (node.tag, self.type, self.name)) f = DeviceElementBase(self, node) if not f.appliesTo(device_id, self.properties): self.log.info( "Tag '%s' does not apply to target '%s'. In '%s/%s' driver xml." % (node.tag, device_id.string, self.type, self.name)) else: file_name = node.text.strip() if node.tag == 'static': if file_name not in build_list: # if it has not been added before static = file_name output = node.get('out') if output == None or len( output) <= 0: # if no out attribute was found output = static static = self._makeRelativeToPlatform(static) output = self._makeRelativeToPlatform(output) if self._checkTarget([static, output], targets): build_list.append([static, output]) # => add static file elif node.tag == 'template': template = file_name output = node.get('out') if output == None or len( output) <= 0: # if no out attribute was found output = template if output.endswith('.in'): output = output[:-3] else: # replace '{{id}}' with id output = output.replace('{{id}}', instance_id.lower()) template = self._makeRelativeToPlatform(template) output = self._makeRelativeToPlatform(output) if instance_id.isdigit(): sub_instance_id = int(instance_id) else: sub_instance_id = instance_id substitutions = dict(self.substitutions) if 'parameters' in substitutions: substitutions['parameters'] = dict( substitutions['parameters']) # substitutions['parameters'] = dict(substitutions['parameters']) if node.get('instances') != None: substitutions.update({'id': sub_instance_id}) substitutions.update({'output': os.path.split(output)[1]}) # self.log.debug("%s->%s: substitutions: %s" % (template, output, substitutions)) template_file = [template, output, substitutions] if self._checkTarget(template_file, targets): build_list.append( template_file ) # always append template files since they will get a different id