• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_component174        """
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_component187        """
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