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