# -*- coding: utf-8 -*- import argparse import traceback import sys import os import re import xml.etree.ElementTree as ET class ParserBase(object): def __init__(self): self.result = True def __del__(self): pass def get_result(self): return self.result class ParserBaseEx(ParserBase): def __init__(self, ssdk_dir, project): super().__init__() self.prj_dir, _ = os.path.split(project) self.prj_dir = self.prj_dir.replace("\\", '/') self.board_map = { "e3_gateway": "E3640", "e3_324_dev_kit": "E3640", "e3_display": "E3340", "e3_324_ref_display": "E3340", "d3_gateway": "D3248", "d3_display": "D3248", "e3_176_ref": "E3110", "e3_144_ref": "E3205", } self.skip_boards = ["e3_master_slave"] self.boards_dir = ssdk_dir.replace('\\', '/') if not self.boards_dir.endswith('/'): self.boards_dir += '/' self.boards_dir += 'boards/' self.board_name = self.prj_dir.replace(self.boards_dir, '').split('/')[0] def __del__(self): pass def is_skip_board(self): if self.board_name.lower() in self.skip_boards: return True return False class CustomArgvarsParser(ParserBaseEx): def __init__(self, ssdk_dir, custom_argvars): super().__init__(ssdk_dir, custom_argvars) self.custom_argvars = custom_argvars.replace("\\", '/') self.project_name = self.prj_dir.replace(self.boards_dir, '').split('/')[-2] self._load_xml() # print("project_name", self.project_name) # print("board_name=", self.board_name) def __del__(self): pass def check_part_id(self): part_id = self.get_part_id() if len(part_id) == 0: print(" [Error] '%s' : PART_ID macro is undefined" % self.custom_argvars) self.result = False elif self.board_name in self.board_map.keys()and not (part_id == self.board_map[self.board_name]): print(" [Error] '%s' : PART ID [%s] is invalid, it must be configured as '%s'" % (self.custom_argvars, part_id, self.board_map[self.board_name])) self.result = False return True def _load_xml(self): data = open(self.custom_argvars).read() index = data.find(' IlinkAutoLibEnable 1 ''' # print(" ", option.find('name').text) # print(" ", option.find('state').text) if not option.find('state').text == '1': print(" [Error] '%s' : IlinkAutoLibEnable is %s, it must be configured as 1" % (self.ewp, option.find('state').text)) self.result = False elif option.find('name').text == 'IlinkExtraOptions': ''' ''' error_des = "" if _config.lower() == 'flashdebug': if self.board not in self.sfs_map: sfs_result = True # skip undefined sfs map continue error_des = " [Error] '%s' : IlinkExtraOptions (sfs) is invalid, it must be configured as %s" % (self.ewp, self.sfs_map[self.board]) sfs_result = False for state in option.findall('state'): # print(" ", state.text) if self._is_sf_core(): if self._is_hyper_flash_driver(): error_des = " [Error] '%s' : IlinkExtraOptions (sfs) is invalid, it must be configured as sfs_s26h-hyperflash.img" % self.ewp if state.text and len(state.text) and not -1 == state.text.find('sfs_s26h-hyperflash.img'): sfs_result = True error_des = "" break elif self._is_nor_flash_driver(): error_des = " [Error] '%s' : IlinkExtraOptions (sfs) is invalid, it must be configured as sfs_mt35-1-1-1.img" % self.ewp if state.text and len(state.text) and not -1 == state.text.find('sfs_mt35-1-1-1.img'): sfs_result = True error_des = "" break elif self.board in self.nor_flash_boards: error_des = " [Error] '%s' : IlinkExtraOptions (sfs) is invalid, it must be configured as sfs_mt35-1-1-1.img" % self.ewp if state.text and len(state.text) and not -1 == state.text.find('sfs_mt35-1-1-1.img'): sfs_result = True error_des = "" break elif self._is_xip_hyper_flash(): error_des = " [Error] '%s' : IlinkExtraOptions (sfs) is invalid, it must be configured as sfs_s26h-hyperflash.img" % self.ewp if state.text and len(state.text) and not -1 == state.text.find('sfs_s26h-hyperflash.img'): sfs_result = True error_des = "" break elif self._is_xip_nor_flash(): error_des = " [Error] '%s' : IlinkExtraOptions (sfs) is invalid, it must be configured as sfs_mt35-1-1-1.img" % self.ewp if state.text and len(state.text) and not -1 == state.text.find('sfs_mt35-1-1-1.img'): sfs_result = True error_des = "" break else: if state.text and len(state.text) and not -1 == state.text.find(self.sfs_map[self.board]): sfs_result = True error_des = "" break else: sfs_result = True if state.text and len(state.text) and not (-1 == state.text.find('SFS_BIN,')): sfs_result = False error_des = " [Error] '%s' : IlinkExtraOptions (sfs) is invalid, does not need to configure sfs for %s core" % (self.ewp, self.core) break else: # debug verison sfs_result = True for state in option.findall('state'): if state.text and len(state.text) and not (-1 == state.text.find('SFS_BIN,')): sfs_result = False error_des = " [Error] '%s' : IlinkExtraOptions (sfs) is invalid, does not need to configure sfs for debug version" % self.ewp break if not sfs_result: print(" ", option.find('name').text) for state in option.findall('state'): print(" ", state.text) print(error_des) self.result = False elif setting.find('name').text == 'General': for option in setting.findall("data/option"): if option.find('name').text == 'GenLowLevelInterface': ''' ''' # print(" ", option.find('name').text) # print(" ", option.find('state').text) if not option.find('state').text == '0': print(" [Error] '%s' : GenLowLevelInterface is %s, it must be configured as 0" % (self.ewp, option.find('state').text)) self.result = False elif setting.find('name').text == 'OBJCOPY': for option in setting.findall("data/option"): if option.find('name').text == 'OOCObjCopyEnable' and _config.lower() == 'debug': ''' ''' # print(" ", option.find('name').text) # print(" ", option.find('state').text) if not option.find('state').text == '1': print(" [Error] '%s' : OOCObjCopyEnable is %s, it must be configured as 1 for Debug version" % (self.ewp, option.find('state').text)) self.result = False elif option.find('name').text == 'OOCOutputFile': ''' ''' # print(" ", option.find('name').text) # print(" ", option.find('state').text) if not _config.lower() == 'flashdebug' and \ option.find('state').text: if self.core in self.out_map.keys() and not option.find('state').text == self.out_map[self.core]: print(" [Error] '%s' : OOCOutputFile is %s, it must be configured as %s" % (self.ewp, option.find('state').text, self.out_map[self.core])) self.result = False elif self.core not in self.out_map.keys() and not option.find('state').text == self.out_map['sf']: print(" [Error] '%s' : OOCOutputFile is %s, it must be configured as %s" % (self.ewp, option.find('state').text, self.out_map['sf'])) self.result = False def check_files(self): for group in self.root.findall("group"): # print(group.find('name').text) self._enum_group(group) def _check_file(self, file): if -1 == file.find('$PROJ_DIR$'): print(" [Error] \'%s\' is not a relative path" % file) self.result = False return False _file = file.replace('$PROJ_DIR$', self.prj_dir) _file = _file.replace('$PART_ID$', self.part_id) _file = _file.replace("\\", '/') if not os.path.exists(_file): print(" [Error] \'%s\' : \'%s\' don't exist" % (self.ewp, file)) self.result = False return False match = re.search('^.+/devices/([DE]\d+)/.+$', file.replace("\\", '/'), re.I) if match: print(" [Error] \'%s\' - \'%s\' : The part id can not use fixed value [%s], must be used macro definition" % (self.ewp, file, match.group(1))) self.result = False return False return True def _enum_group(self, parent): # print(">>>", parent.find('name').text) for file in parent.findall("file"): # print(" ", file.find('name').text) _file = file.find('name').text self._check_file(_file) for sub_group in parent.findall("group"): self._enum_group(sub_group) class EclipsePrjParser(ParserBaseEx): def __init__(self, ssdk_dir, project): super().__init__(ssdk_dir, project) self.project = project.replace("\\", '/') self.prj_dir, _ = os.path.split(self.project) self.tree = ET.parse(self.project) self.root = self.tree.getroot() self.PART_PATH = "" self.part_id = "" self.macro_map = {} def __del__(self): pass def check_project_name(self): # print(self.prj_dir.split('/')) if not self.prj_dir.split('/')[-2] == self.root.find('name').text: print(" [Error] '%s' : Project name is incorrect, please modify '%s' to '%s'" % (self.project, self.root.find('name').text, self.prj_dir.split('/')[-2])) self.result = False return False return True def check_part_id(self): if self.root.find('variableList/variable') is None: print(" [Error] '%s' : Undefined PART_ID macro" % self.project) self.result = False return False has_part_id_macro = False has_part_path_macro = False for variable in self.root.findall("variableList/variable"): # print(variable.find('name').text) self.macro_map[variable.find('name').text] = variable.find('value').text if variable.find('name').text == 'PART_ID': has_part_id_macro = True self.part_id = variable.find('value').text elif variable.find('name').text == 'PART_PATH': self.PART_PATH = variable.find('value').text if not (-1 == variable.find('value').text.find('$%7BPART_ID%7D')): has_part_path_macro = True # print(self.macro_map) if not has_part_id_macro: print(" [Error] '%s' : PART_ID macro is undefined" % self.project) self.result = False return False if not has_part_path_macro: print(" [Error] '%s' : PART_PATH macro is undefined" % self.project) self.result = False return False if len(self.part_id) == 0: print(" [Error] '%s' : PART_ID macro is undefined" % self.project) self.result = False return False elif self.board_name in self.board_map.keys() and not (self.part_id == self.board_map[self.board_name]): print(" [Error] '%s' : PART ID [%s] is invalid, it must be configured as '%s'" % (self.project, self.part_id, self.board_map[self.board_name])) self.result = False return False return True def get_part_id(self): return self.part_id def get_depth(self): return self.PART_PATH def check_files(self): for link_src in self.root.findall("linkedResources/link"): if link_src.find('type').text == '1': # print(link_src.find('name').text) # print(link_src.find('type').text) if link_src.find('locationURI') is None: # print('location', link_src.find('location').text) self._check_file(link_src.find('location').text) elif link_src.find('location') is None: # print('locationURI', link_src.find('locationURI').text) self._check_file(link_src.find('locationURI').text) def _check_file(self, file): match = re.search('^.+/devices/([DE]\d+)/.+$', file.replace("\\", '/'), re.I) if match: print(" [Error] \'%s\' - \'%s\' : The part id can not use fixed value [%s], must be used macro definition" % (self.project, file, match.group(1))) self.result = False return False return True class EclipseCprjParser(ParserBaseEx): def __init__(self, ssdk_dir, project): super().__init__(ssdk_dir, project) self.project = project.replace("\\", '/') self.prj_dir, _ = os.path.split(self.project) self.tree = ET.parse(self.project) self.root = self.tree.getroot() self.part_id = "" self.out_map = { 'bootloader': 'bootloader', "sf": "sf", "sp0": "sp0", "sp1": "sp1", "sx0": "sx0", "sx1": "sx1", } def __del__(self): pass def check_macro(self): find = False for macro in self.root.findall("storageModule/cconfiguration/storageModule/macros"): # print(macro.find('stringMacro').attrib['name']) # print(macro.find('stringMacro').attrib['value']) self.part_id = macro.find('stringMacro').attrib['value'] find = True if not find: self.result = False print(" [Error] '%s' : PART_ID macro is undefined" % self.project) return False if len(self.part_id) == 0: print(" [Error] '%s' : PART_ID macro is undefined" % self.project) self.result = False return False elif self.board_name in self.board_map.keys() and not (self.part_id == self.board_map[self.board_name]): print(" [Error] '%s' : PART ID [%s] is invalid, it must be configured as '%s'" % (self.project, self.part_id, self.board_map[self.board_name])) self.result = False return False return True def check_includes(self): Assembler_tag = "GNU Arm Cross Assembler".lower() CC_tag = "GNU Arm Cross C Compiler".lower() CPP_tag = "GNU Arm Cross C++ Compiler".lower() Include_tag = "Include paths (-I)".lower() Linker_tag = "GNU Arm Cross C Linker".lower() LD_tag = "Script files (-T)".lower() Image_tag = "GNU Arm Cross Create Flash Image".lower() Type_tag = "Output file format (-O)".lower() superClass = "ilg.gnuarmeclipse.managedbuild.cross.tool.createflash".lower() Bin_tag = 'ilg.gnuarmeclipse.managedbuild.cross.option.createflash.choice.binary'.lower() for tool in self.root.findall("storageModule/cconfiguration/storageModule/configuration/folderInfo/toolChain/"): if "name" not in tool.attrib.keys(): continue if tool.attrib['name'].lower() in [Assembler_tag, CC_tag, CPP_tag, Linker_tag, Image_tag]: _tool_name = tool.attrib['name'].lower() if _tool_name == Image_tag: if 'superClass' in tool.attrib.keys() and tool.attrib['superClass'].lower() == superClass: _result = False _error = " [Error] '%s' : artifactName is empty, it must be configured as %s" % \ (self.project, Bin_tag) for option_item in list(tool): # tool.getchildren(): if "value" not in option_item.attrib.keys(): continue if option_item.attrib['value'].lower() == Bin_tag: _result = True _error = "" break else: # print("_attr_name", option_item.attrib['value']) _error = " [Error] '%s' : artifactName is %s, it must be configured as %s" % \ (self.project, option_item.attrib['value'], Bin_tag) _result = False if not _result and len(_error): print(_error) self.result = False continue for option_item in list(tool): # tool.getchildren(): if "name" not in option_item.attrib.keys(): continue _attr_name = option_item.attrib['name'] if _attr_name.lower() in [Include_tag, LD_tag]: # print("_attr_name", _attr_name) for listOptionValue in list(option_item): # option_item.getchildren(): # print(listOptionValue.attrib['value']) self._check_file(listOptionValue.attrib['value']) def check_output(self): prj_name = self.prj_dir.split('/')[-2] for config in self.root.findall("storageModule/cconfiguration/storageModule/configuration"): if "artifactName" in config.attrib.keys(): if prj_name in self.out_map.keys() and not config.attrib['artifactName'] == self.out_map[prj_name] and \ not config.attrib['artifactName'] == '${ProjName}': print(" [Error] '%s' : artifactName is %s, it must be configured as %s" % (self.project, config.attrib['artifactName'], self.out_map[prj_name])) self.result = False elif prj_name not in self.out_map.keys() and not config.attrib['artifactName'] == self.out_map['sf'] and \ not config.attrib['artifactName'] == '${ProjName}': print(" [Error] '%s' : artifactName is %s, it must be configured as %s" % (self.project, config.attrib['artifactName'], self.out_map['sf'])) self.result = False elif config.attrib['artifactName'] == '${ProjName}' and prj_name not in self.out_map.keys(): print(" [Error] '%s' : artifactName is %s, it must be configured as %s" % (self.project, config.attrib['artifactName'], self.out_map['sf'])) self.result = False def _check_file(self, file): match = re.search('^.+/devices/([DE]\d+)/.+$', file.replace("\\", '/'), re.I) if match: print(" [Error] \'%s\' - \'%s\' : The part id can not use fixed value [%s], must be used macro definition" % (self.project, file, match.group(1))) self.result = False return False return True class VerifyBase(object): def __init__(self, ssdk_dir, board_dir=''): self.ssdk_dir = ssdk_dir.replace("\\", '/') self.board_dir = board_dir.replace("\\", '/') self.ssdk_dir = self._rm_slash(self.ssdk_dir) self.board_dir = self._rm_slash(self.board_dir) if 0 == len(self.board_dir): self.board_dir = self.ssdk_dir + '/boards' self.result = True def __del__(self): pass def _rm_slash(self, src): # remove /slash if src.endswith('/'): return src[:-1] return src def get_projects(self, project_type): # project_type in ['IAR', 'IAR_Norflash', 'Eclipse', 'Eclipse_Norflash'] project_dir = [] for path, subdirs, files in os.walk(self.board_dir): for sub_dir in subdirs: if sub_dir in project_type: _project = os.path.join(path, sub_dir) _project = _project.replace("\\", '/') project_dir.append(_project) return project_dir def get_result(self): return self.result class IarVerify(VerifyBase): _iar_ext = [ 'custom_argvars', 'ewd', 'ewp', 'eww' ] def verify(self): print("Start scanning and checking iar project ...") _projects = self.get_projects(['IAR', 'IAR_Norflash']) for proj in _projects: _project_name = proj.split('/')[-2] # print(proj, _project_name) _tmp_prj_files = [] board = "" core = "" part_id = "" # check *.custom_argvars skip_check = False for file in os.listdir(proj): if file.endswith('custom_argvars'): obj = CustomArgvarsParser(self.ssdk_dir, os.path.join(proj, file).replace("\\", '/')) if obj.is_skip_board(): skip_check = True continue print("Check the custom_argvars file :", os.path.join(proj, file).replace("/", '\\')) obj.check_part_id() board = obj.get_board_name() core = obj.get_cord_name() part_id = obj.get_part_id() if self.result: self.result = obj.get_result() # check *.ewp # print("board = ", board) # print("core = ", core) # print("part_id = ", part_id) if skip_check: continue for file in os.listdir(proj): _tmp_prj_files.append(file.split('.')[-1]) if file.endswith('ewp'): print("Check the ewp file :", os.path.join(proj, file).replace("/", '\\')) obj = EwpParser(os.path.join(proj, file).replace("\\", '/'), board, core, part_id) obj.check_options() obj.check_files() if self.result: self.result = obj.get_result() for item in self._iar_ext: if item not in _tmp_prj_files: print(" [Error] '%s' : Not find '%s' file" % (proj, item)) self.result = False print('\n') print("Finished scanning and checking iar project") return self.result class EclipseVerify(VerifyBase): def verify(self): print("Start scanning and checking eclipse project ...") _projects = self.get_projects(['Eclipse', 'Eclipse_Norflash']) for proj in _projects: _project_name = proj.split('/')[-2] # print(proj, _project_name) obj = EclipsePrjParser(self.ssdk_dir, os.path.join(proj, '.project')) if obj.is_skip_board(): continue print("Check the .project file :", os.path.join(proj, '.project').replace("/", '\\')) obj.check_project_name() obj.check_part_id() obj.check_files() if self.result: self.result = obj.get_result() print("Check the .cproject file :", os.path.join(proj, '.cproject').replace("/", '\\')) c_obj = EclipseCprjParser(self.ssdk_dir, os.path.join(proj, '.cproject')) c_obj.check_macro() c_obj.check_includes() c_obj.check_output() if self.result: self.result = c_obj.get_result() print("Finished scanning and checking eclipse project") return self.result class VerifyIdeProject(object): """Object for command-line tool.""" def __init__(self): """Initializer method.""" pass def version(self, _): """Implements the 'version' sub-command.""" print('1.23.1.1') def verify(self, args): result = False try: iar = IarVerify(args.ssdk_dir, args.project_dir) eclipse = EclipseVerify(args.ssdk_dir, args.project_dir) result = iar.verify() eclipse_result = eclipse.verify() if result: result = eclipse_result except Exception: result = False print("[Error] %s" % traceback.format_exc()) finally: if result: print('Successfully verified project files.') else: print('[Error] Invalid project configuration found.') sys.exit(1) return result def run(self, argv): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(title='subcommands') sub_parser = subparsers.add_parser('version', help='Prints version of VerifyIdeProject.') sub_parser.set_defaults(func=self.version) sub_parser = subparsers.add_parser('verify', help="Verify IDE project for ssdk's boards.") sub_parser.add_argument('--ssdk_dir', help='Path to ssdk project root directory.', type=str, required=True) sub_parser.add_argument('--project_dir', help='Path to ide project directory.', type=str, default="") sub_parser.set_defaults(func=self.verify) args = parser.parse_args(argv[1:]) try: func = args.func except AttributeError: parser.error("invalid arguments") return func(args) # python project_verify.py verify --ssdk_dir D:\SDRapidStudio\ide\workspace\2.1 # python project_verify.py verify --ssdk_dir D:\SDRapidStudio\ide\workspace\2.1 --project_dir D:\SDRapidStudio\ide\workspace\2.1\boards\e3_gateway if __name__ == '__main__': tool = VerifyIdeProject() tool.run(sys.argv)