464 lines
17 KiB
Python
464 lines
17 KiB
Python
|
#!/usr/bin/env python3
|
|||
|
# encoding=utf-8
|
|||
|
# =========================================================================
|
|||
|
# @brief Config Target Definitions File
|
|||
|
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2022-2022. All rights reserved.
|
|||
|
# =========================================================================
|
|||
|
import multiprocessing
|
|||
|
import os
|
|||
|
import sys
|
|||
|
import json
|
|||
|
import copy
|
|||
|
import importlib
|
|||
|
from utils.build_utils import fn_get_subdirs, rm_pyc, target_config_path
|
|||
|
|
|||
|
all_target = {}
|
|||
|
all_group = {}
|
|||
|
all_target_templates = {}
|
|||
|
chip_group_target = {}
|
|||
|
chip_copy_target = {}
|
|||
|
|
|||
|
from target_config.common_config import CommonConfig
|
|||
|
from utils.build_utils import exec_shell, root_path, get_platform_name, output_root
|
|||
|
|
|||
|
rm_pyc(target_config_path)
|
|||
|
for name in fn_get_subdirs(target_config_path):
|
|||
|
load_fmt = "target_config.%s.target_config" %name
|
|||
|
try:
|
|||
|
load_mod = importlib.import_module(load_fmt)
|
|||
|
except:
|
|||
|
print("[WARN] config/target_config/%s/target_config.py is not exist, you can delete it" % name)
|
|||
|
continue
|
|||
|
all_target_templates.update(load_mod.target_template)
|
|||
|
|
|||
|
load_fmt = "target_config.%s.config" %name
|
|||
|
load_mod = importlib.import_module(load_fmt)
|
|||
|
if "target_copy" in load_mod.__dict__:
|
|||
|
chip_copy_target[name] = load_mod.target_copy
|
|||
|
all_target.update(load_mod.target)
|
|||
|
all_group.update(load_mod.target_group)
|
|||
|
chip_group_target[name] = list(load_mod.target.keys()) + list(load_mod.target_group.keys())
|
|||
|
|
|||
|
# 判断key_list中的关键字是否都在string中
|
|||
|
def is_list_in_str(key_list, string):
|
|||
|
for item in key_list:
|
|||
|
if item not in string:
|
|||
|
return False
|
|||
|
return True
|
|||
|
|
|||
|
|
|||
|
class TargetEnvironment:
|
|||
|
def __init__(self, target_name, extra_defines=None):
|
|||
|
|
|||
|
self.config = {}
|
|||
|
self.is_implement_target = False
|
|||
|
if extra_defines is None:
|
|||
|
extra_defines = []
|
|||
|
org_target = copy.deepcopy(all_target[target_name])
|
|||
|
# sdk target不需要和template合并
|
|||
|
if org_target.get('build_type', None) == 'SDK':
|
|||
|
self.config = copy.deepcopy(org_target)
|
|||
|
if extra_defines:
|
|||
|
self.config['defines'] = extra_defines
|
|||
|
return
|
|||
|
|
|||
|
self.merge_target_config(target_name, extra_defines)
|
|||
|
|
|||
|
com_config = CommonConfig(self.config['arch'])
|
|||
|
|
|||
|
self.merge_common_config(com_config)
|
|||
|
self.merge_component_set(com_config)
|
|||
|
self.merge_defines_set(com_config)
|
|||
|
|
|||
|
if self.is_enable_hso():
|
|||
|
self.config["defines"].append("HSO_SUPPORT")
|
|||
|
|
|||
|
for key, value in self.config.items():
|
|||
|
if isinstance(value, list):
|
|||
|
self.deal_with_delete_symbol(key)
|
|||
|
|
|||
|
self.config["build_platform"] = get_platform_name()
|
|||
|
self.config['target_command'] = target_name
|
|||
|
if org_target.get('product_type'):
|
|||
|
self.config['product_type'] = org_target.get('product_type')
|
|||
|
|
|||
|
def merge_target_config(self, target_name, extra_defines):
|
|||
|
""" 将target_name对应的diff配置同diff配置中base_target_name对应的template配置合并
|
|||
|
Args:
|
|||
|
target_name: diff配置的key
|
|||
|
extra_defines: 从命令-def=XXX,YYY,ZZZ=x中接收的额外宏
|
|||
|
"""
|
|||
|
org_target = copy.deepcopy(all_target[target_name])
|
|||
|
base_target_name = org_target.pop('base_target_name')
|
|||
|
if base_target_name in all_target:
|
|||
|
temp_target = copy.deepcopy(all_target[base_target_name])
|
|||
|
self.is_implement_target = True
|
|||
|
org_target = self.merge_dict(temp_target, org_target)
|
|||
|
base_target_name = temp_target['base_target_name']
|
|||
|
|
|||
|
self.config = copy.deepcopy(all_target_templates[base_target_name])
|
|||
|
|
|||
|
for key, value in org_target.items():
|
|||
|
if not isinstance(value, list):
|
|||
|
self.config[key] = value
|
|||
|
continue
|
|||
|
if key == 'defines':
|
|||
|
self.merge_defines(value)
|
|||
|
continue
|
|||
|
self.extend(key, value)
|
|||
|
self.merge_defines(extra_defines)
|
|||
|
|
|||
|
def merge_dict(self, dict1, dict2):
|
|||
|
res = copy.deepcopy(dict2)
|
|||
|
for key, value in dict1.items():
|
|||
|
if not isinstance(value, list):
|
|||
|
res[key] = value
|
|||
|
continue
|
|||
|
if key not in res:
|
|||
|
res[key] = []
|
|||
|
res[key].extend(value)
|
|||
|
return res
|
|||
|
|
|||
|
def merge_common_config(self, com_config):
|
|||
|
""" 将公共配置项合并到config中
|
|||
|
Args:
|
|||
|
com_config: 公共配置实例
|
|||
|
"""
|
|||
|
self.pretend('ccflags', com_config.get_ram_ccflags())
|
|||
|
self.extend('rom_ccflags', com_config.get_rom_ccflags())
|
|||
|
self.extend('linkflags', com_config.get_linkflags())
|
|||
|
self.extend('std_libs', com_config.get_std_libs())
|
|||
|
self.add('arch_family', com_config.get_arch_family())
|
|||
|
|
|||
|
def merge_defines(self, defines, extend=True):
|
|||
|
"""
|
|||
|
将参数defines合并到config的defines项中, 对于XXX=y这种宏会被替换
|
|||
|
例:
|
|||
|
config的defines项中有:XXX=y
|
|||
|
参数defines中有:XXX=z
|
|||
|
则合并后config的defines项中:XXX=y => XXX=z
|
|||
|
"""
|
|||
|
if extend:
|
|||
|
self.extend('defines', defines)
|
|||
|
special_define = {}
|
|||
|
for define in defines:
|
|||
|
if '=' in define:
|
|||
|
key = define.split('=')[0]
|
|||
|
val = define.split('=')[1]
|
|||
|
special_define[key] = val
|
|||
|
for idx, define in enumerate(self.config['defines']):
|
|||
|
if '=' not in define:
|
|||
|
continue
|
|||
|
key = define.split('=')[0]
|
|||
|
if key in special_define:
|
|||
|
self.config['defines'][idx] = '%s=%s' % (key, special_define[key])
|
|||
|
if extend:
|
|||
|
self.config['defines'].extend(defines)
|
|||
|
self.config['defines'] = list({}.fromkeys(self.config['defines']).keys())
|
|||
|
|
|||
|
def merge_component_set(self, com_config):
|
|||
|
"""
|
|||
|
处理ram/rom_component_set项中的删除标识, 并将其value合并到ram/rom_component中
|
|||
|
"""
|
|||
|
self.deal_with_delete_symbol('ram_component_set')
|
|||
|
self.deal_with_delete_symbol('rom_component_set')
|
|||
|
|
|||
|
for ram_set in self.config['ram_component_set']:
|
|||
|
self.extend('ram_component', com_config.get_component_set(ram_set))
|
|||
|
|
|||
|
for rom_set in self.config['rom_component_set']:
|
|||
|
self.extend('rom_component', com_config.get_component_set(rom_set))
|
|||
|
|
|||
|
def merge_defines_set(self, com_config):
|
|||
|
"""
|
|||
|
处理ram/rom_component_set项中的删除标识, 并将其value合并到ram/rom_component中
|
|||
|
"""
|
|||
|
self.deal_with_delete_symbol('defines_set')
|
|||
|
|
|||
|
for defines_set in self.config['defines_set']:
|
|||
|
self.extend('defines', com_config.get_definse(defines_set))
|
|||
|
|
|||
|
def deal_with_delete_symbol(self, key):
|
|||
|
"""
|
|||
|
处理self.config中key项中的删除标识
|
|||
|
"""
|
|||
|
|
|||
|
if key not in self.config:
|
|||
|
self.config[key] = []
|
|||
|
return
|
|||
|
lst = self.config[key]
|
|||
|
normal_lst = []
|
|||
|
reserve_lst = []
|
|||
|
delete_lst = []
|
|||
|
define_replace = []
|
|||
|
for item in lst:
|
|||
|
if item.startswith("[r]"):
|
|||
|
reserve_lst.append(item[3:])
|
|||
|
continue
|
|||
|
if not item.startswith("-:"):
|
|||
|
normal_lst.append(item)
|
|||
|
continue
|
|||
|
if key == 'defines' and "=" in item:
|
|||
|
define_replace.append(item[2:])
|
|||
|
continue
|
|||
|
delete_lst.append(item[2:])
|
|||
|
|
|||
|
normal_lst = list({}.fromkeys(normal_lst).keys())
|
|||
|
for del_item in delete_lst:
|
|||
|
if del_item in normal_lst:
|
|||
|
normal_lst.remove(del_item)
|
|||
|
self.config[key] = normal_lst
|
|||
|
self.merge_defines(define_replace, False)
|
|||
|
self.config[key].extend(reserve_lst)
|
|||
|
|
|||
|
def add_component_defines(self):
|
|||
|
self.config["target_component_defines"] = []
|
|||
|
if 'ram_component' in self.config:
|
|||
|
self.config["target_component_defines"].extend(
|
|||
|
["SUPPORT_" + x.upper() for x in self.config['ram_component']]
|
|||
|
)
|
|||
|
if 'rom_component' in self.config:
|
|||
|
self.config["target_component_defines"].extend(
|
|||
|
["SUPPORT_" + x.upper() for x in self.config['rom_component']]
|
|||
|
)
|
|||
|
def is_enable_hso(self):
|
|||
|
return self.get("hso_enable")
|
|||
|
|
|||
|
def get_tool_chain(self):
|
|||
|
toolchain_dir = os.path.join(root_path, 'build', 'toolchains')
|
|||
|
toolchain_file = "%s.cmake" % (self.get('tool_chain'))
|
|||
|
return os.path.join(toolchain_dir, toolchain_file)
|
|||
|
|
|||
|
def get(self, key, cmake_type=True):
|
|||
|
"""
|
|||
|
返回self.config中item项的值,
|
|||
|
若此值为list类型且cmake_type为True则用";"连接为字符串再返回(cmake中列表中元素用;隔开)
|
|||
|
"""
|
|||
|
if key not in self.config:
|
|||
|
return None
|
|||
|
if isinstance(self.config[key], list) and cmake_type:
|
|||
|
return ";".join(self.config[key])
|
|||
|
return self.config[key]
|
|||
|
|
|||
|
def set(self, key, val):
|
|||
|
if key not in self.config:
|
|||
|
raise f'{key} not in ' + self.config['target_command']
|
|||
|
if not isinstance(self.config[key], type(val)):
|
|||
|
raise 'type of value is not same with ' + self.config['target_command'] + 'config'
|
|||
|
self.config[key] = val
|
|||
|
|
|||
|
def get_output_path(self):
|
|||
|
chip = self.get('chip')
|
|||
|
core = self.get('core')
|
|||
|
target_name = self.get('target_command')
|
|||
|
return os.path.join(output_root, chip, core, target_name)
|
|||
|
|
|||
|
def get_target_template(self):
|
|||
|
if self.is_implement_target:
|
|||
|
temp = all_target[self.config['target_command']]['base_target_name']
|
|||
|
res = all_target[temp]['base_target_name']
|
|||
|
else:
|
|||
|
res = all_target[self.config['target_command']]['base_target_name']
|
|||
|
return res
|
|||
|
|
|||
|
def add(self, key, value):
|
|||
|
if key in self.config:
|
|||
|
raise
|
|||
|
self.config[key] = value
|
|||
|
|
|||
|
def extend(self, key, value):
|
|||
|
if not isinstance(value, list):
|
|||
|
return
|
|||
|
if key not in self.config:
|
|||
|
self.config[key] = []
|
|||
|
self.config[key].extend(value)
|
|||
|
|
|||
|
def pretend(self, key, value):
|
|||
|
if not isinstance(value, list):
|
|||
|
return
|
|||
|
if key not in self.config:
|
|||
|
self.config[key] = []
|
|||
|
value.extend(self.config[key])
|
|||
|
self.config[key] = copy.deepcopy(value)
|
|||
|
|
|||
|
def append(self, key, value):
|
|||
|
if key not in self.config:
|
|||
|
self.config[key] = []
|
|||
|
if not isinstance(self.config[key], list):
|
|||
|
return
|
|||
|
if value in self.config[key]:
|
|||
|
return
|
|||
|
self.config[key].append(value)
|
|||
|
|
|||
|
def remove(self, key, value):
|
|||
|
if key not in self.config:
|
|||
|
return
|
|||
|
if not isinstance(self.config[key], list):
|
|||
|
return
|
|||
|
if value not in self.config[key]:
|
|||
|
return
|
|||
|
self.config[key].remove(value)
|
|||
|
|
|||
|
def dump(self, dump_file=False):
|
|||
|
info = json.dumps(self.config, indent=4, ensure_ascii=False)
|
|||
|
if not dump_file:
|
|||
|
print(info)
|
|||
|
return info
|
|||
|
with open(os.path.join(self.get_output_path(), "env_config.json"), "w") as f:
|
|||
|
f.write(info)
|
|||
|
return info
|
|||
|
|
|||
|
def is_config_refresh(self):
|
|||
|
config_file = os.path.join(self.get_output_path(), "env_config.json")
|
|||
|
if not os.path.exists(config_file):
|
|||
|
return True
|
|||
|
with open(config_file, "r") as f:
|
|||
|
config = json.load(f)
|
|||
|
return config != self.config
|
|||
|
|
|||
|
class BuildEnvironment:
|
|||
|
""" command line param handler
|
|||
|
receive param and parse it
|
|||
|
"""
|
|||
|
|
|||
|
def __init__(self, param_list):
|
|||
|
self.python_path = sys.executable
|
|||
|
param_list = param_list[1:]
|
|||
|
self.extr_defines = []
|
|||
|
self.thread = multiprocessing.cpu_count()
|
|||
|
self.need_clean = False
|
|||
|
self.no_symbol_link = False
|
|||
|
self.component = []
|
|||
|
self.build_level = 'normal'
|
|||
|
self.build_as_lib = False
|
|||
|
self.build_as_lib_output_file = ''
|
|||
|
self.target_names = []
|
|||
|
self.group_names = []
|
|||
|
self.generator = 'Unix Makefiles'
|
|||
|
self.no_hso = False
|
|||
|
self.open_kconfig = False
|
|||
|
if get_platform_name() == "windows":
|
|||
|
self.generator = "Ninja"
|
|||
|
self.dump = False
|
|||
|
self.build_time = ''
|
|||
|
self.parse_cmd(param_list)
|
|||
|
|
|||
|
def parse_cmd(self, param_list):
|
|||
|
""" parse param
|
|||
|
"""
|
|||
|
keys_of_target_name = []
|
|||
|
for param in param_list:
|
|||
|
if param.startswith('-j'):
|
|||
|
self.thread = int(param[2:])
|
|||
|
elif param.startswith('-def='):
|
|||
|
self.extr_defines.extend(param[5:].split(','))
|
|||
|
elif param.startswith('-build_time='):
|
|||
|
self.build_time = param[12:].split(',')[0]
|
|||
|
elif param.startswith('-component='):
|
|||
|
self.component.extend(param[11:].split(','))
|
|||
|
elif param.startswith('-out_libs='):
|
|||
|
self.build_as_lib = True
|
|||
|
self.build_as_lib_output_file = param[10:]
|
|||
|
elif param == '-c':
|
|||
|
self.need_clean = True
|
|||
|
elif param == '-ninja':
|
|||
|
self.generator = 'Ninja'
|
|||
|
elif param == '-release' or param == '-debug' or param == '-normal':
|
|||
|
self.build_level = param[1:]
|
|||
|
elif param == '-dump':
|
|||
|
self.dump = True
|
|||
|
elif param == '-nhso':
|
|||
|
self.no_hso = True
|
|||
|
elif param == '-nsymlink':
|
|||
|
self.no_symbol_link = True
|
|||
|
elif param == 'menuconfig' or param == 'defconfig' or param == 'allyesconfig' or param == 'allnoconfig':
|
|||
|
self.open_kconfig = True
|
|||
|
self.kconfig_param = param
|
|||
|
else:
|
|||
|
keys_of_target_name.append(param)
|
|||
|
self.match_target_names(keys_of_target_name)
|
|||
|
|
|||
|
def get_target_names_by_group_name(self, group_name):
|
|||
|
targets = []
|
|||
|
items = all_group[group_name]
|
|||
|
return items
|
|||
|
|
|||
|
def is_group(self, item):
|
|||
|
return item in all_group
|
|||
|
|
|||
|
def is_target(self, item):
|
|||
|
return item in all_target
|
|||
|
|
|||
|
def is_copy_target(self, item):
|
|||
|
for chip in chip_copy_target:
|
|||
|
if item in chip_copy_target[chip]:
|
|||
|
return True
|
|||
|
return False
|
|||
|
|
|||
|
def get_chip_name(self, item):
|
|||
|
for chip, targets in chip_group_target.items():
|
|||
|
if item in targets:
|
|||
|
return chip
|
|||
|
return None
|
|||
|
|
|||
|
def add_target_names(self, target_name):
|
|||
|
if target_name in all_group:
|
|||
|
self.group_names.append(target_name)
|
|||
|
if target_name in all_target:
|
|||
|
self.target_names.append(target_name)
|
|||
|
|
|||
|
def match_target_names(self, keys_of_target_name):
|
|||
|
""" 根据匹配关键字在all_target和all_group中匹配target_name以供选择
|
|||
|
"""
|
|||
|
if len(keys_of_target_name) == 1:
|
|||
|
temp_name = keys_of_target_name[0]
|
|||
|
if temp_name in all_group or temp_name in all_target:
|
|||
|
self.add_target_names(temp_name)
|
|||
|
return
|
|||
|
|
|||
|
normal_match_list = []
|
|||
|
group_match_list = []
|
|||
|
for key in all_target:
|
|||
|
if is_list_in_str(keys_of_target_name, key):
|
|||
|
normal_match_list.append(key)
|
|||
|
for key in all_group:
|
|||
|
if is_list_in_str(keys_of_target_name, key):
|
|||
|
group_match_list.append(key)
|
|||
|
|
|||
|
all_match_list = normal_match_list + group_match_list
|
|||
|
# all_match_list为空, 报错
|
|||
|
if not all_match_list:
|
|||
|
print("Target_name invalid and No matching target_name exists!")
|
|||
|
for item in all_target.keys():
|
|||
|
print(item)
|
|||
|
sys.exit(1)
|
|||
|
# 匹配项只有一个, 无需选择直接返回
|
|||
|
if len(all_match_list) == 1:
|
|||
|
self.add_target_names(all_match_list[0])
|
|||
|
return
|
|||
|
# 按序号打印匹配的target_name
|
|||
|
print("Target_name invalid")
|
|||
|
print("Here are the matching target_names")
|
|||
|
cur_index = 0
|
|||
|
for index in range(len(normal_match_list)):
|
|||
|
print("%-3d : %s" % (cur_index, normal_match_list[index]))
|
|||
|
cur_index += 1
|
|||
|
print("\nHere are the matching aliases target_name")
|
|||
|
for index in range(len(group_match_list)):
|
|||
|
print("%-3d : %s" % (cur_index, group_match_list[index]))
|
|||
|
cur_index += 1
|
|||
|
print("\nDo you want to build them?")
|
|||
|
print("Input index numbers for build or q for quit(num/q):", end='')
|
|||
|
|
|||
|
# 输入序号或q/Q
|
|||
|
select_index = input()
|
|||
|
if select_index.lower() == 'q':
|
|||
|
sys.exit(0)
|
|||
|
try:
|
|||
|
select_indexes = [int(x) for x in select_index.split()]
|
|||
|
for index in select_indexes:
|
|||
|
self.add_target_names(all_match_list[index])
|
|||
|
except:
|
|||
|
print("ERROR INPUT!!")
|
|||
|
sys.exit(1)
|