1#!/usr/bin/env python3 2# encoding=utf-8 3# ========================================================================= 4# @brief Config Target Definitions File 5 6# Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED. 7# Licensed under the Apache License, Version 2.0 (the "License"); 8# you may not use this file except in compliance with the License. 9# You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, software 14# distributed under the License is distributed on an "AS IS" BASIS, 15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16# See the License for the specific language governing permissions and 17# limitations under the License. 18# ========================================================================= 19import multiprocessing 20import os 21import sys 22import json 23import copy 24import importlib 25from utils.build_utils import fn_get_subdirs, rm_pyc, target_config_path 26 27all_target = {} 28all_group = {} 29all_target_templates = {} 30chip_group_target = {} 31chip_copy_target = {} 32 33from target_config.common_config import CommonConfig 34from utils.build_utils import exec_shell, root_path, get_platform_name, output_root 35 36rm_pyc(target_config_path) 37for name in fn_get_subdirs(target_config_path): 38 load_fmt = "target_config.%s.target_config" %name 39 try: 40 load_mod = importlib.import_module(load_fmt) 41 except: 42 print("[WARN] config/target_config/%s/target_config.py is not exist, you can delete it" % name) 43 continue 44 all_target_templates.update(load_mod.target_template) 45 46 load_fmt = "target_config.%s.config" %name 47 load_mod = importlib.import_module(load_fmt) 48 if "target_copy" in load_mod.__dict__: 49 chip_copy_target[name] = load_mod.target_copy 50 all_target.update(load_mod.target) 51 all_group.update(load_mod.target_group) 52 chip_group_target[name] = list(load_mod.target.keys()) + list(load_mod.target_group.keys()) 53 54# 判断key_list中的关键字是否都在string中 55def is_list_in_str(key_list, string): 56 for item in key_list: 57 if item not in string: 58 return False 59 return True 60 61 62class TargetEnvironment: 63 def __init__(self, target_name, extra_defines=None): 64 65 self.config = {} 66 self.is_implement_target = False 67 if extra_defines is None: 68 extra_defines = [] 69 org_target = copy.deepcopy(all_target[target_name]) 70 # sdk target不需要和template合并 71 if org_target.get('build_type', None) == 'SDK': 72 self.config = copy.deepcopy(org_target) 73 if extra_defines: 74 self.config['defines'] = extra_defines 75 return 76 77 self.merge_target_config(target_name, extra_defines) 78 79 com_config = CommonConfig(self.config['arch']) 80 81 self.merge_common_config(com_config) 82 self.merge_component_set(com_config) 83 self.merge_defines_set(com_config) 84 85 if self.is_enable_hso(): 86 self.config["defines"].append("HSO_SUPPORT") 87 88 for key, value in self.config.items(): 89 if isinstance(value, list): 90 self.deal_with_delete_symbol(key) 91 92 self.config["build_platform"] = get_platform_name() 93 self.config['target_command'] = target_name 94 if org_target.get('product_type'): 95 self.config['product_type'] = org_target.get('product_type') 96 97 def merge_target_config(self, target_name, extra_defines): 98 """ 将target_name对应的diff配置同diff配置中base_target_name对应的template配置合并 99 Args: 100 target_name: diff配置的key 101 extra_defines: 从命令-def=XXX,YYY,ZZZ=x中接收的额外宏 102 """ 103 org_target = copy.deepcopy(all_target[target_name]) 104 base_target_name = org_target.pop('base_target_name') 105 if base_target_name in all_target: 106 temp_target = copy.deepcopy(all_target[base_target_name]) 107 self.is_implement_target = True 108 org_target = self.merge_dict(temp_target, org_target) 109 base_target_name = temp_target['base_target_name'] 110 111 self.config = copy.deepcopy(all_target_templates[base_target_name]) 112 113 for key, value in org_target.items(): 114 if not isinstance(value, list): 115 self.config[key] = value 116 continue 117 if key == 'defines': 118 self.merge_defines(value) 119 continue 120 self.extend(key, value) 121 self.merge_defines(extra_defines) 122 123 def merge_dict(self, dict1, dict2): 124 res = copy.deepcopy(dict2) 125 for key, value in dict1.items(): 126 if not isinstance(value, list): 127 res[key] = value 128 continue 129 if key not in res: 130 res[key] = [] 131 res[key].extend(value) 132 return res 133 134 def merge_common_config(self, com_config): 135 """ 将公共配置项合并到config中 136 Args: 137 com_config: 公共配置实例 138 """ 139 self.pretend('ccflags', com_config.get_ram_ccflags()) 140 self.extend('rom_ccflags', com_config.get_rom_ccflags()) 141 self.extend('linkflags', com_config.get_linkflags()) 142 self.extend('std_libs', com_config.get_std_libs()) 143 self.add('arch_family', com_config.get_arch_family()) 144 145 def merge_defines(self, defines, extend=True): 146 """ 147 将参数defines合并到config的defines项中, 对于XXX=y这种宏会被替换 148 例: 149 config的defines项中有:XXX=y 150 参数defines中有:XXX=z 151 则合并后config的defines项中:XXX=y => XXX=z 152 """ 153 if extend: 154 self.extend('defines', defines) 155 special_define = {} 156 for define in defines: 157 if '=' in define: 158 key = define.split('=')[0] 159 val = define.split('=')[1] 160 special_define[key] = val 161 for idx, define in enumerate(self.config['defines']): 162 if '=' not in define: 163 continue 164 key = define.split('=')[0] 165 if key in special_define: 166 self.config['defines'][idx] = '%s=%s' % (key, special_define[key]) 167 if extend: 168 self.config['defines'].extend(defines) 169 self.config['defines'] = list({}.fromkeys(self.config['defines']).keys()) 170 171 def merge_component_set(self, com_config): 172 """ 173 处理ram/rom_component_set项中的删除标识, 并将其value合并到ram/rom_component中 174 """ 175 self.deal_with_delete_symbol('ram_component_set') 176 self.deal_with_delete_symbol('rom_component_set') 177 178 for ram_set in self.config['ram_component_set']: 179 self.extend('ram_component', com_config.get_component_set(ram_set)) 180 181 for rom_set in self.config['rom_component_set']: 182 self.extend('rom_component', com_config.get_component_set(rom_set)) 183 184 def merge_defines_set(self, com_config): 185 """ 186 处理ram/rom_component_set项中的删除标识, 并将其value合并到ram/rom_component中 187 """ 188 self.deal_with_delete_symbol('defines_set') 189 190 for defines_set in self.config['defines_set']: 191 self.extend('defines', com_config.get_definse(defines_set)) 192 193 def deal_with_delete_symbol(self, key): 194 """ 195 处理self.config中key项中的删除标识 196 """ 197 198 if key not in self.config: 199 self.config[key] = [] 200 return 201 lst = self.config[key] 202 normal_lst = [] 203 reserve_lst = [] 204 delete_lst = [] 205 define_replace = [] 206 for item in lst: 207 if item.startswith("[r]"): 208 reserve_lst.append(item[3:]) 209 continue 210 if not item.startswith("-:"): 211 normal_lst.append(item) 212 continue 213 if key == 'defines' and "=" in item: 214 define_replace.append(item[2:]) 215 continue 216 delete_lst.append(item[2:]) 217 218 normal_lst = list({}.fromkeys(normal_lst).keys()) 219 for del_item in delete_lst: 220 if del_item in normal_lst: 221 normal_lst.remove(del_item) 222 self.config[key] = normal_lst 223 self.merge_defines(define_replace, False) 224 self.config[key].extend(reserve_lst) 225 226 def add_component_defines(self): 227 self.config["target_component_defines"] = [] 228 if 'ram_component' in self.config: 229 self.config["target_component_defines"].extend( 230 ["SUPPORT_" + x.upper() for x in self.config['ram_component']] 231 ) 232 if 'rom_component' in self.config: 233 self.config["target_component_defines"].extend( 234 ["SUPPORT_" + x.upper() for x in self.config['rom_component']] 235 ) 236 def is_enable_hso(self): 237 return self.get("hso_enable") 238 239 def get_tool_chain(self): 240 toolchain_dir = os.path.join(root_path, 'build', 'toolchains') 241 toolchain_file = "%s.cmake" % (self.get('tool_chain')) 242 return os.path.join(toolchain_dir, toolchain_file) 243 244 def get(self, key, cmake_type=True): 245 """ 246 返回self.config中item项的值, 247 若此值为list类型且cmake_type为True则用";"连接为字符串再返回(cmake中列表中元素用;隔开) 248 """ 249 if key not in self.config: 250 return None 251 if isinstance(self.config[key], list) and cmake_type: 252 return ";".join(self.config[key]) 253 return self.config[key] 254 255 def set(self, key, val): 256 if key not in self.config: 257 raise f'{key} not in ' + self.config['target_command'] 258 if not isinstance(self.config[key], type(val)): 259 raise 'type of value is not same with ' + self.config['target_command'] + 'config' 260 self.config[key] = val 261 262 def get_output_path(self): 263 chip = self.get('chip') 264 core = self.get('core') 265 target_name = self.get('target_command') 266 return os.path.join(output_root, chip, core, target_name) 267 268 def get_target_template(self): 269 if self.is_implement_target: 270 temp = all_target[self.config['target_command']]['base_target_name'] 271 res = all_target[temp]['base_target_name'] 272 else: 273 res = all_target[self.config['target_command']]['base_target_name'] 274 return res 275 276 def add(self, key, value): 277 if key in self.config: 278 raise 279 self.config[key] = value 280 281 def extend(self, key, value): 282 if not isinstance(value, list): 283 return 284 if key not in self.config: 285 self.config[key] = [] 286 self.config[key].extend(value) 287 288 def pretend(self, key, value): 289 if not isinstance(value, list): 290 return 291 if key not in self.config: 292 self.config[key] = [] 293 value.extend(self.config[key]) 294 self.config[key] = copy.deepcopy(value) 295 296 def append(self, key, value): 297 if key not in self.config: 298 self.config[key] = [] 299 if not isinstance(self.config[key], list): 300 return 301 if value in self.config[key]: 302 return 303 self.config[key].append(value) 304 305 def remove(self, key, value): 306 if key not in self.config: 307 return 308 if not isinstance(self.config[key], list): 309 return 310 if value not in self.config[key]: 311 return 312 self.config[key].remove(value) 313 314 def dump(self, dump_file=False): 315 info = json.dumps(self.config, indent=4, ensure_ascii=False) 316 if not dump_file: 317 print(info) 318 return info 319 with open(os.path.join(self.get_output_path(), "env_config.json"), "w") as f: 320 f.write(info) 321 return info 322 323 def is_config_refresh(self): 324 config_file = os.path.join(self.get_output_path(), "env_config.json") 325 if not os.path.exists(config_file): 326 return True 327 with open(config_file, "r") as f: 328 config = json.load(f) 329 return config != self.config 330 331class BuildEnvironment: 332 """ command line param handler 333 receive param and parse it 334 """ 335 336 def __init__(self, param_list): 337 self.python_path = sys.executable 338 param_list = param_list[1:] 339 self.extr_defines = [] 340 self.thread = multiprocessing.cpu_count() 341 self.need_clean = False 342 self.no_symbol_link = False 343 self.component = [] 344 self.build_level = 'normal' 345 self.build_as_lib = False 346 self.build_as_lib_output_file = '' 347 self.target_names = [] 348 self.group_names = [] 349 self.generator = 'Unix Makefiles' 350 self.no_hso = False 351 self.open_kconfig = False 352 if get_platform_name() == "windows": 353 self.generator = "Ninja" 354 self.dump = False 355 self.build_time = '' 356 self.parse_cmd(param_list) 357 358 def parse_cmd(self, param_list): 359 """ parse param 360 """ 361 keys_of_target_name = [] 362 for param in param_list: 363 if param.startswith('-j'): 364 self.thread = int(param[2:]) 365 elif param.startswith('-def='): 366 self.extr_defines.extend(param[5:].split(',')) 367 elif param.startswith('-build_time='): 368 self.build_time = param[12:].split(',')[0] 369 elif param.startswith('-component='): 370 self.component.extend(param[11:].split(',')) 371 elif param.startswith('-out_libs='): 372 self.build_as_lib = True 373 self.build_as_lib_output_file = param[10:] 374 elif param == '-c': 375 self.need_clean = True 376 elif param == '-ninja': 377 self.generator = 'Ninja' 378 elif param == '-release' or param == '-debug' or param == '-normal': 379 self.build_level = param[1:] 380 elif param == '-dump': 381 self.dump = True 382 elif param == '-nhso': 383 self.no_hso = True 384 elif param == '-nsymlink': 385 self.no_symbol_link = True 386 elif param == 'menuconfig' or param == 'defconfig' or param == 'allyesconfig' or param == 'allnoconfig': 387 self.open_kconfig = True 388 self.kconfig_param = param 389 else: 390 keys_of_target_name.append(param) 391 self.match_target_names(keys_of_target_name) 392 393 def get_target_names_by_group_name(self, group_name): 394 targets = [] 395 items = all_group[group_name] 396 return items 397 398 def is_group(self, item): 399 return item in all_group 400 401 def is_target(self, item): 402 return item in all_target 403 404 def is_copy_target(self, item): 405 for chip in chip_copy_target: 406 if item in chip_copy_target[chip]: 407 return True 408 return False 409 410 def get_chip_name(self, item): 411 for chip, targets in chip_group_target.items(): 412 if item in targets: 413 return chip 414 return None 415 416 def add_target_names(self, target_name): 417 if target_name in all_group: 418 self.group_names.append(target_name) 419 if target_name in all_target: 420 self.target_names.append(target_name) 421 422 def match_target_names(self, keys_of_target_name): 423 """ 根据匹配关键字在all_target和all_group中匹配target_name以供选择 424 """ 425 if len(keys_of_target_name) == 1: 426 temp_name = keys_of_target_name[0] 427 if temp_name in all_group or temp_name in all_target: 428 self.add_target_names(temp_name) 429 return 430 431 normal_match_list = [] 432 group_match_list = [] 433 for key in all_target: 434 if is_list_in_str(keys_of_target_name, key): 435 normal_match_list.append(key) 436 for key in all_group: 437 if is_list_in_str(keys_of_target_name, key): 438 group_match_list.append(key) 439 440 all_match_list = normal_match_list + group_match_list 441 # all_match_list为空, 报错 442 if not all_match_list: 443 print("Target_name invalid and No matching target_name exists!") 444 for item in all_target.keys(): 445 print(item) 446 sys.exit(1) 447 # 匹配项只有一个, 无需选择直接返回 448 if len(all_match_list) == 1: 449 self.add_target_names(all_match_list[0]) 450 return 451 # 按序号打印匹配的target_name 452 print("Target_name invalid") 453 print("Here are the matching target_names") 454 cur_index = 0 455 for index in range(len(normal_match_list)): 456 print("%-3d : %s" % (cur_index, normal_match_list[index])) 457 cur_index += 1 458 print("\nHere are the matching aliases target_name") 459 for index in range(len(group_match_list)): 460 print("%-3d : %s" % (cur_index, group_match_list[index])) 461 cur_index += 1 462 print("\nDo you want to build them?") 463 print("Input index numbers for build or q for quit(num/q):", end='') 464 465 # 输入序号或q/Q 466 select_index = input() 467 if select_index.lower() == 'q': 468 sys.exit(0) 469 try: 470 select_indexes = [int(x) for x in select_index.split()] 471 for index in select_indexes: 472 self.add_target_names(all_match_list[index]) 473 except: 474 print("ERROR INPUT!!") 475 sys.exit(1) 476