def __init__(self, proj_file, devkit_root, workspace_file): """ parse an XML file and store the root element, file and dir name """ if not os.path.isfile(proj_file): bdex.raise_bd_err('INVALID_PROJECT_FILE', proj_file) if not os.path.isfile(workspace_file): bdex.raise_bd_err('INVALID_WORKSPACE_FILE', workspace_file) if not os.path.isdir(devkit_root): bdex.raise_bd_err('INVALID_DEVKIT_ROOT', devkit_root) self.workspace_root = os.path.abspath(workspace_file) self.devkit_root = os.path.abspath(devkit_root) self.proj_fobj = None self.proj_fname = None current_wsroot = self._get_wsroot_from_workspace_file( workspace_file, devkit_root) self.aliases = Aliases(proj_file, sdk=devkit_root, wsroot=current_wsroot) try: self.proj_fname = proj_file.name self.proj_fobj = proj_file except AttributeError: self.proj_fname = proj_file self.proj_fobj = open(proj_file) basename = os.path.basename(self.proj_fname) self.proj_projname = os.path.splitext(basename)[0] self.proj_dirname = os.path.abspath(os.path.dirname(self.proj_fname)) try: tree = ET.ElementTree(file=self.proj_fobj) except ET.ParseError as excep: line, col = excep.position file_name = os.path.abspath(proj_file) bdex.raise_bd_err('INVALID_XML', file_name, excep.msg, line, col) self.tree_root = tree.getroot() self.validate_xml()
class ParseProject(object): """ Parse an x2p project """ def __init__(self, project_file, sdk=None, wsroot=None): self.xml = parsers.get_xml(project_file) self.aliases = Aliases(project_file, sdk=sdk, wsroot=wsroot) self.xml_tree = self.xml.parse() self.files = self._parse_files() self.configurations = self._parse_configurations() def _parse_files(self): files = [] for file_element in self.xml_tree.iter(file_xpath): file_path = file_element.get(path_attr_str) file_path = self.aliases.expand(file_path) if not os.path.isabs(file_path): file_path = os.path.normpath( os.path.join(self.xml.base_dir, file_path)) if not os.path.isfile(file_path): raise InvalidProjectElement( "File listed in project {} doesn't exist: {}".format( self.xml.filename, file_path)) files.append(file_path) return files def _parse_configurations(self): configurations_elements = self.xml_tree.findall(configurations_xpath) if len(configurations_elements) > 1: raise InvalidProjectElement( "Multiple <configurations> elements in {}".format( self.xml.filename)) elif len(configurations_elements) < 1: raise InvalidProjectElement( "No <configurations> elements found in {}".format( self.xml.filename)) configurations = {} for config in configurations_elements[0].findall(configuration_xpath): configuration = Configuration(config) configurations[configuration.name] = configuration return configurations def parse(self): return self.files, self.configurations
def _get_wsroot_from_workspace_file(self, workspace_file, devkit_root): aliases = Aliases(workspace_file, devkit_root) return aliases["wsroot://"]
class Project(object): """ Container for all project file infomation """ def __init__(self, proj_file, devkit_root, workspace_file): """ parse an XML file and store the root element, file and dir name """ if not os.path.isfile(proj_file): bdex.raise_bd_err('INVALID_PROJECT_FILE', proj_file) if not os.path.isfile(workspace_file): bdex.raise_bd_err('INVALID_WORKSPACE_FILE', workspace_file) if not os.path.isdir(devkit_root): bdex.raise_bd_err('INVALID_DEVKIT_ROOT', devkit_root) self.workspace_root = os.path.abspath(workspace_file) self.devkit_root = os.path.abspath(devkit_root) self.proj_fobj = None self.proj_fname = None current_wsroot = self._get_wsroot_from_workspace_file( workspace_file, devkit_root) self.aliases = Aliases(proj_file, sdk=devkit_root, wsroot=current_wsroot) try: self.proj_fname = proj_file.name self.proj_fobj = proj_file except AttributeError: self.proj_fname = proj_file self.proj_fobj = open(proj_file) basename = os.path.basename(self.proj_fname) self.proj_projname = os.path.splitext(basename)[0] self.proj_dirname = os.path.abspath(os.path.dirname(self.proj_fname)) try: tree = ET.ElementTree(file=self.proj_fobj) except ET.ParseError as excep: line, col = excep.position file_name = os.path.abspath(proj_file) bdex.raise_bd_err('INVALID_XML', file_name, excep.msg, line, col) self.tree_root = tree.getroot() self.validate_xml() def validate_xml(self): """ TODO check the XML is compliant with our form e.g. <property name= for Heracles files """ pass def get_properties(self): """ return the properties derived from the project file and folder names """ properties = collections.defaultdict(str) properties['OUTDIR'] = self.proj_dirname properties['IDE_PROJECT'] = self.proj_projname return properties def get_configurations(self): """ Return a list of all the configuration names found under the 'properties' tag. """ config_list = [] for config in self.tree_root.iterfind( './configurations/configuration'): config_list.append(config.attrib['name']) return config_list def get_elements_from_tree_path(self, tree_path, elem_name, type=""): """ Return a dict of the elem_name elements of a given tree path. """ def get_text(prop_val, prop): """ Ensure elem_name text is never None Replace 'sdk://' to the path to the Devkit root folder """ text = '' if prop.text != None: text = prop.text text = text.replace('sdk://', self.devkit_root + '\\') return text properties = collections.defaultdict(str) xpath_elements = "/*" xpath = tree_path + xpath_elements # for prop in self.tree_root.iterfind(xpath): # if prop.tag == elem_name: # # Catenate the text of properties with the 'alias' attribute onto the specified # # alias elem_name. properties is a defaultdict and creates the specified alias key # # if required. # # Do not add a new key to the properties dictionary for the alias itself # if 'alias' in prop.attrib: # name = prop.attrib['alias'] # properties[name] += ' ' + get_text(properties[name], prop) # else: # name = prop.attrib['name'] # if name in properties: # print 'WARNING: Duplicate %s %s' % (elem_name, name) # properties[name] = ' ' + get_text(properties[name], prop) for prop in self.tree_root.iterfind(xpath): if prop.tag == elem_name and 'alias' not in prop.attrib: name = prop.attrib['name'] if name in properties: print 'WARNING: Duplicate %s %s' % (elem_name, name) properties[name] = get_text(properties[name], prop) if type != "": if "type" in prop.attrib: if prop.attrib["type"] != type: properties.pop(name) else: properties.pop(name) for prop in self.tree_root.iterfind(xpath): if prop.tag == elem_name: # Catenate the text of properties with the 'alias' attribute onto the specified # alias elem_name. properties is a defaultdict and creates the specified alias key # if required. # Do not add a new key to the properties dictionary for the alias itself if 'alias' in prop.attrib: name = prop.attrib['alias'] properties[name] += ' ' + get_text(properties[name], prop) properties[name] = properties[name].strip() return properties def get_attributes_from_config(self, config_name): config_list = self.tree_root.iterfind( "./configurations/configuration/[@name='%s']" % config_name) # config_list for a given config_name should always be 1 element in length # config_list is a 'generator', which is iterable but not indexable, so we need to loop once. for config in config_list: return config.attrib def get_properties_from_config(self, config_name): return self.get_elements_from_tree_path( "./configurations/configuration/[@name='%s']" % config_name, "property") def get_capabilities_from_config(self, config_name): return self.get_elements_from_tree_path( "./configurations/configuration/[@name='%s']" % config_name, "group", "capability") def get_cap_props_from_config(self, config_name, capability): return self.get_elements_from_tree_path( "./configurations/configuration/[@name='%s']/group/[@name='%s']" % (config_name, capability), "property") def build_source_path(self, root_el, path): """ Recursively search all 'folder' tags for subordinate 'file' tags and retrieve source file paths. xIDE paths are complete, whereas Heracles ones use a nested folder structure like a file system and need to be built up into a path string. All paths have seperators normalised and are prepended with the path parameter . """ file_list = [] for child in root_el: if child.tag == 'file': if child.attrib['path'].startswith('root.pri'): sys.exit('ROOT PRI NO SLASHES') exp_path = self.aliases.expand(child.attrib['path']) file_path = os.path.join(path, exp_path) file_path = os.path.normpath(file_path) file_list.append(file_path) elif child.tag == 'folder': child_paths = self.build_source_path(child, path) file_list.extend(child_paths) elif child.tag == 'sdkfile': file_path = os.path.join(self.devkit_root, child.attrib['path']) file_path = os.path.normpath(file_path) for f in glob.glob(file_path): file_list.append(f) else: # Ignore any other kind of tag pass return file_list def get_source_files(self): """ Recursively search all 'folder' tags for subordinate 'file' tags and retrieve source file paths. xIDE paths are complete, whereas Heracles ones use a nested folder structure like a file system and need to be built up into a path string. All paths have seperators normalised and are prepended with the path parameter . """ file_list = self.build_source_path(self.tree_root, self.proj_dirname) # Check that all the files exist before returning the list try: for fname in file_list: fobj = open(fname) fobj.close() except IOError as excep: print "FAILED SOURCE" bdex.raise_bd_err('INVALID_SOURCE_FILE', fname, excep.strerror) return file_list def get_make_vars(self, config): """ merge project properties and config properties and return in a single dict """ proj_props = self.get_properties() make_vars = proj_props.copy() make_vars['IDE_CONFIG'] = config # Configuration properties take priority over project properties config_props = self.get_properties_from_config(config) make_vars.update(config_props) return make_vars def get_workspace_path(self): """ Find the workspace element in the file and return the path attribute, raising an error if this doesn't point at a real file relative to the project file's location. """ workspace_el = self.tree_root.find("workspace") if workspace_el is None: print "No workspace element in project file" return None if "path" not in workspace_el.attrib: bdex.raise_bd_err("INVALID WORKSPACE ELEMENT") ws_path = os.path.normpath( os.path.join(self.proj_dirname, workspace_el.attrib["path"])) if not os.path.isfile(ws_path): bdex.raise_bd_err("INVALID WORKSPACE FILE") return ws_path def _get_wsroot_from_workspace_file(self, workspace_file, devkit_root): aliases = Aliases(workspace_file, devkit_root) return aliases["wsroot://"]
def __init__(self, project_file, sdk=None, wsroot=None): self.xml = parsers.get_xml(project_file) self.aliases = Aliases(project_file, sdk=sdk, wsroot=wsroot) self.xml_tree = self.xml.parse() self.files = self._parse_files() self.configurations = self._parse_configurations()
def get_audio_elf_path_from_workspace(workspace_file, isSimTarget): """ Given a workspace file (x2w) will return the Path to the kymera_audio project contained within """ aliases = Aliases(workspace_file) workspace_tree = ElementTree.parse(workspace_file) workspace_root = workspace_tree.getroot() workspace_path = os.path.dirname(workspace_file) project_dict = {} workspace_name = os.path.basename(workspace_file) workspace_name, dont_care = os.path.splitext(workspace_name) main_proj_name = workspace_name + ".x2p" kymera_image_path = None dkcs_path = None bundle_image_list = [] projects_visited = [] for project in workspace_root.iter('project'): project_path = project.get('path') # Skip project items without a path attribute. if project_path is None: continue project_path = aliases.expand(project_path) if project_path not in projects_visited: projects_visited.append(project_path) project_path = os.path.abspath( os.path.join(workspace_path, project_path)) if "kymera_audio.x2p" in project_path: is_audio_proj, kymera_image_path = get_output_value_from_audio_proj( project_path, isSimTarget, False) if kymera_image_path: kymera_project_path = os.path.dirname(project_path) kymera_image_path = os.path.join(kymera_project_path, kymera_image_path) kymera_image_path = os.path.abspath(kymera_image_path) dkcs_path = get_dkcs_path_value_from_audio_proj( project_path) if dkcs_path: dkcs_path = os.path.abspath( os.path.join(kymera_project_path, dkcs_path)) if os.path.exists(dkcs_path): for bundle_name in os.listdir(dkcs_path): bundle_name = os.path.join( os.path.abspath(dkcs_path), bundle_name) if os.path.splitext(bundle_name)[1] == ".elf": bundle_image_list.append(bundle_name) else: is_audio_proj, bundle_image_path = get_output_value_from_audio_proj( project_path, isSimTarget, True) if is_audio_proj: kymera_project_path = os.path.dirname(project_path) bundle_image_path = os.path.join(kymera_project_path, bundle_image_path) bundle_image_path = os.path.abspath(bundle_image_path) bundle_image_list.append(bundle_image_path) return kymera_image_path, bundle_image_list
def __init__(self, workspace_file): self.workspace_xml = parsers.get_xml(workspace_file) self.aliases = Aliases(workspace_file) self.default_project = None
class ParseWorkspace(object): """Parse an x2w workspace""" def __init__(self, workspace_file): self.workspace_xml = parsers.get_xml(workspace_file) self.aliases = Aliases(workspace_file) self.default_project = None def __repr__(self): return self.workspace_xml def __call__(self): return self.workspace_xml def _join_workspace_path_with_relative_project_path( self, relative_project_path): return os.path.abspath( os.path.join(self.workspace_xml.base_dir, relative_project_path)) def _get_absolute_project_path(self, project_xml): project_path = self._parse_project_path(project_xml) if not os.path.isabs(project_path): return self._join_workspace_path_with_relative_project_path( project_path) return project_path def _parse_project_path(self, element): if element is not None: return self.aliases.expand(element.get(path_attr_str)) else: raise InvalidProjectElement( "Project element has no name or path attribute") def _parse_project_attributes(self, element): if element is not None: name = element.get(name_attr_str) path = element.get(path_attr_str) return name, path else: raise InvalidProjectElement( "Project element has no name or path attribute") def _get_project_id_from_path(self, element): project_path = self._parse_project_path(element) return os.path.splitext(os.path.basename(project_path))[0] def _parse_project_name(self, element): project_id = element.get(name_attr_str) if project_id is None: project_id = self._get_project_id_from_path(element) return project_id def _parse_dependent_projects(self, parsed_project_xpath): dependencies = [] for dependency in parsed_project_xpath.findall(dependencies_xpath): dependencies.append(self._parse_project_name(dependency)) return dependencies def _update_project_dependencies(self, parsed_project_xpath, projects): for project_xml in parsed_project_xpath: project_id = self._parse_project_name(project_xml) dependencies = self._parse_dependent_projects(project_xml) for dependency in dependencies: projects[project_id].dependencies.append(projects[dependency]) def _parse_project(self, project_name, project_xml): project_file = self._get_absolute_project_path(project_xml) project = Project(project_name, project_file, sdk=self.aliases.sdk, wsroot=self.aliases.wsroot) return project @staticmethod def _project_is_default(element): return element.get(default_attr_str) == "yes" def _parse_projects_from_xml_source(self, parsed_project_xpath): projects = {} for project_xml in parsed_project_xpath: project_id = self._parse_project_name(project_xml) project = self._parse_project(project_id, project_xml) if self._project_is_default(project_xml): if self.default_project: raise InvalidProjectElement( ("More than one default project in: {ws}.\n" "Found: {new}\n" "Already had: {old}\n").format( ws=self.workspace_xml.filename, old=self.default_project.name, new=project.name)) self.default_project = project projects[project_id] = project if not self.default_project: raise ParseError( "Default project not found in workspace: {}".format( self.workspace_xml.filename)) return projects def parse(self): # This is a 2 stage process: 1. Parse all projects. 2. determine dependent projects parsed_project_xpath = self.workspace_xml.parse().findall( project_xpath) projects = self._parse_projects_from_xml_source(parsed_project_xpath) self._update_project_dependencies(parsed_project_xpath, projects) return projects