1#!/usr/bin/env python3 2# coding=utf-8 3# Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16""" 17* Description: Settings parsers. 18* Create: 2020-1-2 19""" 20 21import os 22import json 23 24from build_utils import color_red 25from build_utils import color_end 26 27__all__ = ["MconfParser", "BuildConfParser"] 28 29def nv_repeat_check(pairs): 30 key_list = [] 31 for key_temp in pairs: 32 if key_temp[0] not in key_list: 33 key_list.append(key_temp[0]) 34 else: 35 raise Exception("nv items(%s) repeat"%key_temp[0]) 36 pairs = dict(pairs) 37 return pairs 38 39class ParserError(Exception): 40 """ 41 Parse errors, highlight in red. 42 """ 43 def __init__(self, err): 44 emsg = "%s%s%s"%(color_red(), err, color_end()) 45 Exception.__init__(self, emsg) 46 pass 47 48""" 49Json format config file parser 50""" 51class BuildConfParser: 52 def __init__(self, conf_path): 53 if not os.path.isfile(conf_path): 54 raise ParserError("Configration file %s NOT found!"%conf_path) 55 56 with open(conf_path, 'r', encoding='utf-8') as conf: 57 try: 58 myread = conf.read() 59 self.conf_data = json.loads(myread) 60 self.nvconf_data = json.loads(myread, object_pairs_hook = nv_repeat_check) 61 except Exception as err: 62 msg = "%s\nParsing file:%s"%(err, conf_path) 63 raise ParserError(msg) 64 65 self.conf_data = self._parse(self.conf_data) 66 self.nvconf_data = self._parse(self.nvconf_data) 67 68 def get_conf_data(self): 69 return self.conf_data 70 71 def get_nvconf_data(self): 72 return self.nvconf_data 73 74 def get(self, option): 75 return self.conf_data.get(option) 76 77 def _parse(self, data): 78 """ 79 parse the python sentence starts with ### 80 """ 81 for key, value in data.items(): 82 if isinstance(value, dict): 83 # Recursion 84 value = self._parse(value) 85 if isinstance(value, list): 86 # Recursion 87 data[key] = self._parse_list(value) 88 if isinstance(value, int): 89 data[key] = value 90 if isinstance(value, str) and value.startswith('###'): 91 value = self._exec(value) 92 data[key] = value 93 return data 94 95 def _parse_list(self, values): 96 new_list = [] 97 for val in values: 98 if type(val) is str and val.startswith('###'): 99 value = self._exec(val) 100 new_list.append(value) 101 elif isinstance(val, dict): 102 new_list.append(self._parse(val)) 103 else: 104 new_list.append(val) 105 return new_list 106 107 def _exec(self, code): 108 """ 109 Execute the simple python sentence. 110 For the security reason, only allows 'os.path.join' to be input, as a path string 111 to support multiple platforms. 112 If it needs to support more python features, please use compile and eval, but careful about 113 the security issues. 114 """ 115 start = code.find("os.path.join") 116 if start < 0: 117 raise ParserError("The input doesn't support!") 118 lpt = code.find("(") 119 if lpt < 0 or lpt < start: 120 raise ParserError("The input doesn't support!") 121 rpt = code.find(")") 122 if rpt < 0 or rpt < lpt: 123 raise ParserError("The input doesn't support!") 124 path_parts = code[lpt + 1:rpt].split(",") 125 ret = "" 126 for part in path_parts: 127 ret = os.path.join(ret, part.lstrip(" '\"").rstrip(" '\"")) 128 return ret 129 130""" 131Menuconfig format config file parser 132""" 133class MconfParser: 134 def __init__(self, conf_path): 135 if not os.path.isfile(conf_path): 136 raise ParserError("Configration file %s NOT found!"%conf_path) 137 138 with open(conf_path, 'r', encoding='utf-8') as conf: 139 self.conf_data = conf.readlines() 140 141 self.conf_data = self._parse(self.conf_data) 142 143 def get(self, option): 144 data = self.conf_data.get(option) 145 if data is None: 146 # Option not found be treated as false. 147 return 'n' 148 # strip " when met string values. 149 return data.replace('"', '') 150 151 def _parse(self, data): 152 settings = {} 153 for option in data: 154 if self._option_is_valid(option) is True: 155 key, value = self._parse_option(option) 156 settings[key] = value.strip().replace('\n', '').replace('\r', '') 157 return settings 158 159 def _option_is_valid(self, option): 160 option = option.strip() 161 if (option is None) or (option == '') or (option.startswith('#') is True): 162 # skip blanks and comments. 163 return False 164 return True 165 166 def _parse_option(self, option): 167 cfg = option.split('=') 168 if len(cfg) == 2: 169 # like "KEY=value", length always be 2. return in KEY, value 170 return cfg[0], cfg[1] 171 else: 172 raise ParserError("Unknow format of the option:%s"%option) 173 174def test(): 175 """ 176 Test only. 177 """ 178 parser = BuildConfParser("build/config/riscv32_toolchain.json") 179 print(parser.get('TargetFolder')) 180 mparser = MconfParser("build/config/settings.json") 181 print(mparser.get('CONFIG_TARGET_SOFT_VER')) 182 183if __name__ == "__main__": 184 test() 185 186