1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2020-2021 Huawei Device Co., Ltd.
5#
6# HDF is dual licensed: you can use it either under the terms of
7# the GPL, or the BSD license, at your option.
8# See the LICENSE file in the root of this repository for complete details.
9
10
11import json
12import os
13import re
14import ast
15import platform
16from string import Template
17
18import hdf_utils
19from hdf_tool_exception import HdfToolException
20from .hdf_command_error_code import CommandErrorCode
21
22
23class HdfLiteScan(object):
24    def __init__(self, root, vendor, board):
25        self.root = root
26        self.vendor = vendor
27        self.board = board
28
29        self.build_path = hdf_utils.get_vendor_gn_path(self.root)
30        if not os.path.exists(self.build_path):
31            raise HdfToolException('file: %s not exist' % self.build_path,
32                                   CommandErrorCode.TARGET_NOT_EXIST)
33
34        self.framework_dir = hdf_utils.get_module_dir(self.root)
35        if not os.path.exists(self.framework_dir):
36            raise HdfToolException('file: %s not exist' % self.framework_dir,
37                                   CommandErrorCode.TARGET_NOT_EXIST)
38
39        self.hcs_path = hdf_utils.get_hcs_file_path(self.root, vendor, board)
40        if not os.path.exists(self.hcs_path):
41            raise HdfToolException('file: %s not exist' % self.hcs_path,
42                                   CommandErrorCode.TARGET_NOT_EXIST)
43
44        dot_file_list = hdf_utils.get_dot_configs_path(root, vendor, board)
45        self.dot_file = list(filter(
46            lambda x: x.endswith("debug_tee.config"), dot_file_list))
47        self.contents = hdf_utils.read_file_lines(self.build_path)
48        self.contents_config_enable = hdf_utils.read_file_lines(
49            self.dot_file[0])
50        self.re_temp = r"^modules"
51        self.re_temp_model = r'"[a-z 0-9]+'
52        self.re_temp_module_switch = r"^module_switch"
53        self.re_temp_loscfg = r"LOSCFG[_ A-Z 0-9]+"
54        self.re_temp_sources = "^sources"
55        self.re_source_file1 = r"\$[A-Z _ / a-z 0-9 .]+\.c"
56        self.re_source_file2 = r"[_ / a-z 0-9]+\.c"
57        self.re_temp_header = r"^hdf_driver\(module_name\)"
58        self.re_temp_if = r"^if"
59        self.re_left_macro = r'^[A-Z _ 0-9]+'
60        self.re_right_macro = r'\$[A-Z _ 0-9]+'
61
62    def scan_build(self):
63        start_index = 0
64        end_index = 0
65        state = 0
66        for index, line in enumerate(self.contents):
67            if re.compile(self.re_temp).match(line.strip()):
68                start_index = index
69                state += 1
70            elif line.strip() == "[" and start_index > 0:
71                state += 1
72            elif line.strip() == "]" and start_index > 0:
73                state -= 1
74                if state == 0:
75                    end_index = index + 1
76
77        model_list = []
78        for i in self.contents[start_index + 1: end_index - 1]:
79            model_name = re.compile(self.re_temp_model).match(i.strip())
80            if model_name:
81                model_list.append(i.strip())
82        return list(set(model_list))
83
84    def _get_model_file_dict(self, liteos_model):
85        parent_path_model = os.path.sep.join(
86            self.build_path.split(os.path.sep)[:-1])
87        model_path_dict = {}
88        for model_path in liteos_model:
89            model_file_path = os.path.join(parent_path_model, model_path[1:-2])
90            model_name = model_path[1:-2].split("/")[0]
91            if model_path_dict.get(model_name, None):
92                model_path_dict.get(model_name).append(model_file_path)
93            else:
94                model_path_dict[model_name] = [model_file_path]
95        return model_path_dict
96
97    def model_able_scan(self, model_path_dict):
98        for model_name, value in model_path_dict.items():
99            for path in value:
100                model_build = os.path.join(path, "BUILD.gn")
101                if not os.path.exists(model_build):
102                    continue
103                model_build_lines = hdf_utils.read_file_lines(model_build)
104                model_path_dict = self.model_build_file_able(
105                    model_build_lines, model_path_dict, model_name, path)
106        return model_path_dict
107
108    def model_build_file_able(self, model_build_lines, model_path_dict,
109                              model_name, path):
110        status = True
111        for line in model_build_lines:
112            if re.search(self.re_temp_module_switch, line) and status:
113                enable_key = '%s=y\n' % re.search(
114                    self.re_temp_loscfg, line.strip()).group()
115                if enable_key not in self.contents_config_enable:
116                    model_path_dict[model_name].remove(path)
117                else:
118                    status = False
119        return model_path_dict
120
121    def get_need_result_only(self, model_path_dict, name,
122                             import_gni_path, return_dict, need_replace):
123        parent = model_path_dict[name][0]
124        model_build = os.path.join(parent, "BUILD.gn")
125        if os.path.exists(model_build):
126            model_build_lines = hdf_utils.read_file_lines(model_build)
127            key_index = self.get_index(model_config_lines=model_build_lines)
128            enable_path_dict = self.enable_dict(
129                index_list=key_index,
130                enable_lines=model_build_lines,
131                path=parent)
132            replace_path = {}
133            import_gni_path, replace_path, temp_name = self.build_able_analyze(
134                model_build_lines, replace_path, import_gni_path)
135            enable_path_dict[temp_name] = enable_path_dict.pop("temp")
136            return_dict[name] = enable_path_dict
137            need_replace[name] = replace_path
138        return need_replace, return_dict, enable_path_dict, import_gni_path
139
140    def build_able_analyze(self, model_build_lines, replace_path, import_gni_path):
141        for index, line in enumerate(model_build_lines):
142            if re.search(self.re_temp_module_switch, line.strip()):
143                temp_name = re.search(self.re_temp_loscfg,
144                                      line.strip()).group()
145            elif re.search(self.re_left_macro, line.strip()):
146                if line.strip().endswith("="):
147                    temp_str = line.strip() + \
148                               model_build_lines[index + 1].strip()
149                    replace_path = self.build_line_analyze(temp_str, replace_path)
150                else:
151                    temp_str = line.strip()
152                    replace_path = self.build_line_analyze(temp_str, replace_path)
153            elif re.search(r"^import", line.strip()):
154                import_gni_path.append(line.strip())
155        return import_gni_path, replace_path, temp_name
156
157    def build_line_analyze(self, temp_str, replace_path):
158        if re.search(self.re_right_macro, temp_str):
159            temp = re.search(
160                self.re_right_macro, temp_str).group()
161            temp_re_list = re.sub(
162                self.re_right_macro,
163                '${' + temp[1:] + "}",
164                temp_str).split("=")
165            replace_path[temp_re_list[0].strip()] = \
166                temp_re_list[-1].strip().strip("\"")
167        else:
168            temp_split_list = temp_str.split("=")
169            replace_path[temp_split_list[0].strip()] = \
170                temp_split_list[-1].strip().strip("\"")
171        return replace_path
172
173    def get_need_result_list(self, model_path_dict, name, import_replace_name,
174                             import_gni_path, return_dict, need_replace):
175        multiple_path = {}
176        for path in model_path_dict[name]:
177            model_build = os.path.join(path, "BUILD.gn")
178            if not os.path.exists(model_build):
179                continue
180            model_build_lines = hdf_utils.read_file_lines(model_build)
181            key_index = self.get_index(
182                model_config_lines=model_build_lines)
183            replace_path = {}
184            enable_path_dict = self.enable_dict(
185                index_list=key_index,
186                enable_lines=model_build_lines,
187                path=path
188            )
189            for index, line in enumerate(model_build_lines):
190                if re.search(self.re_temp_module_switch, line.strip()):
191                    temp_name = re.search(self.re_temp_loscfg,
192                                          line.strip()).group()
193                elif re.search(self.re_left_macro, line.strip()):
194                    replace_path = self.get_macro_replace_path(
195                        line, model_build_lines,
196                        index, replace_path, import_replace_name)
197                elif re.search(r"^import", line.strip()):
198                    import_gni_path.append(line.strip())
199            enable_path_dict[temp_name] = enable_path_dict.pop("temp")
200            multiple_path.update(enable_path_dict)
201            if need_replace.get(name):
202                need_replace[name].update(replace_path)
203            else:
204                need_replace[name] = replace_path
205        return_dict[name] = multiple_path
206        return need_replace, return_dict, enable_path_dict, import_gni_path
207
208    def get_macro_replace_path(self, line, model_build_lines,
209             index, replace_path, import_replace_name):
210        if line.strip().endswith("="):
211            temp_str = line.strip() + \
212                       model_build_lines[index + 1].strip()
213            replace_path = self.build_line_analyze(
214                temp_str, replace_path)
215        else:
216            temp_str = line.strip()
217            if re.search(self.re_right_macro, temp_str):
218                temp = re.search(self.re_right_macro,
219                                 temp_str).group()
220                temp_re_list = re.sub(self.re_right_macro,
221                                      '${' + temp[1:] + "}",
222                                      temp_str).split("=")
223                import_replace_name.add(temp[1:])
224                replace_path[temp_re_list[0].strip()] = \
225                    temp_re_list[-1].strip().strip("\"")
226        return replace_path
227
228    def get_need_result(self, model_path_dict):
229        return_dict_temp = {}
230        need_replace_temp = {}
231        import_gni_path = []
232        import_replace_name = set([])
233        for name in list(model_path_dict.keys()):
234            if len(model_path_dict[name]) == 0:
235                pass
236            else:
237                if len(model_path_dict[name]) == 1:
238                    need_replace, return_dict, \
239                    enable_path_dict, import_gni_path = \
240                        self.get_need_result_only(
241                            model_path_dict, name,
242                            import_gni_path, return_dict_temp, need_replace_temp)
243                else:
244                    need_replace, return_dict, \
245                    enable_path_dict, import_gni_path = \
246                        self.get_need_result_list(
247                            model_path_dict, name, import_replace_name,
248                            import_gni_path, return_dict_temp, need_replace_temp)
249
250        for k_name in list(return_dict.keys()):
251            for model_detail in list(return_dict.get(k_name).keys()):
252                enable_key = '%s=y\n' % model_detail
253                if enable_key not in self.contents_config_enable:
254                    del return_dict.get(k_name)[model_detail]
255                    continue
256                if not return_dict.get(k_name).get(model_detail):
257                    del return_dict.get(k_name)[model_detail]
258                else:
259                    return_dict = self.format_replace_string(
260                        return_dict, model_name=k_name,
261                        model_detail=model_detail)
262        return return_dict, need_replace, \
263               import_gni_path, import_replace_name
264
265    def format_replace_string(self, return_dict, model_name, model_detail):
266        temp_list = []
267        for info in return_dict[model_name][model_detail]['drivers_sources']:
268            if re.search(self.re_right_macro, info):
269                temp = re.search(self.re_right_macro, info).group()
270                temp_re_str = re.sub(self.re_right_macro,
271                                   '${' + temp[1:] + "}", info)
272                temp_list.append(temp_re_str.strip().strip("\""))
273            else:
274                temp_list.append(info.strip())
275        return_dict[model_name][model_detail]['drivers_sources'] = temp_list
276        return return_dict
277
278    def get_index(self, model_config_lines):
279        index_list = []
280        count = 0
281        for index, line in enumerate(model_config_lines):
282            if re.search(self.re_temp_header, line.strip())\
283                    or re.search(self.re_temp_if, line.strip()):
284                count += 1
285                index_list.append(index)
286            elif line.strip() == "}":
287                count -= 1
288                if count == 0:
289                    index_list.append(index)
290        return index_list
291
292    def enable_dict(self, index_list, enable_lines, path):
293        config_name = ['BUILD.gn', 'Kconfig', 'Makefile']
294        driver_configs = []
295        result_dict = {}
296        for file_name in os.listdir(path):
297            if file_name in config_name:
298                driver_configs.append(os.path.join(path, file_name))
299        for index_num in range(len(index_list) - 1):
300            temp_dict = {}
301            header_name, temp_list = self.get_header_list(
302                enable_lines, index_list, index_num)
303            if header_name is not None:
304                if header_name.find("if") != -1:
305                    enable_name = re.search(
306                        self.re_temp_loscfg, header_name).group()
307                else:
308                    enable_name = "temp"
309            else:
310                enable_name = "temp"
311            temp_res_list = self.eval_source_str_path(temp_list)
312            replace_finish = []
313            for temp_path in temp_res_list:
314                if temp_path.startswith('$'):
315                    replace_finish.append(temp_path)
316                else:
317                    replace_finish.append(os.path.join(path, temp_path))
318            temp_dict["driver_configs"] = driver_configs
319            temp_dict["drivers_sources"] = replace_finish
320            result_dict[enable_name] = temp_dict
321        return result_dict
322
323    def eval_source_str_path(self, temp_list):
324        temp_res_list = []
325        for temp_info in temp_list:
326            source_file_path1 = re.search(
327                self.re_source_file1, temp_info.strip())
328            source_file_path2 = re.search(
329                self.re_source_file2, temp_info.strip())
330            if source_file_path1:
331                temp_res_list.append(source_file_path1.group())
332            elif source_file_path2:
333                list1 = temp_info.strip().split("=")
334                try:
335                    str1 = ast.literal_eval(list1[-1].strip())[0]
336                except NameError:
337                    continue
338                finally:
339                    pass
340                temp_res_list.append(re.search(
341                    self.re_source_file2, str1.strip()).group())
342        return temp_res_list
343
344    def get_header_list(self, enable_lines, index_list, index_num):
345        count = 0
346        temp_list = []
347        header_name = None
348        for line in enable_lines[index_list[index_num]: index_list[index_num + 1]]:
349            if re.search(self.re_temp_sources, line.strip()):
350                if line.strip().find("+=") > 0 and \
351                        line.strip().endswith("]"):
352                    header_name = enable_lines[index_list[index_num]].strip()
353                    temp_list.append(line)
354                    continue
355                if line.strip().find("+=") > 0:
356                    count += 1
357                    header_name = enable_lines[index_list[index_num]].strip()
358                else:
359                    count += 1
360            elif line.strip() == "]":
361                count -= 1
362                if count == 0:
363                    break
364            if count > 0:
365                temp_list.append(line)
366        return header_name, temp_list
367
368    def replace_file_path(self, return_dict, import_gni_path,
369                          import_replace_name, need_replace):
370        test_re = r'\([/ a-z . - " _]+\)'
371        import_path = []
372        template_replace_dict = {}
373        for i in list(set(import_gni_path)):
374            temp_path = re.search(test_re, i).group()[2:-2]
375            if os.path.exists(self.root + temp_path):
376                self.found_import_file_path(
377                    temp_path, import_path,
378                    import_replace_name, template_replace_dict)
379        temp_template = Template(json.dumps(need_replace))
380        replace_result_dict = json.loads(temp_template.substitute(
381            template_replace_dict))
382        for key_model in list(return_dict.keys()):
383            replace_result_dict[key_model].update(template_replace_dict)
384            replace_dict = json.loads(json.dumps(
385                replace_result_dict[key_model]).replace("//", '/'))
386            temp_template1 = Template(
387                json.dumps(return_dict[key_model]))
388            temp_key_model = json.loads(temp_template1.
389                                        substitute(replace_dict))
390            temp_res_key_model = self.driver_source_filter(temp_key_model)
391            return_dict[key_model] = temp_res_key_model
392        return_dict["deconfig"] = self.dot_file[0]
393        return_dict["build"] = self.build_path
394        return_dict["hcs_path"] = self.hcs_path
395        return json.dumps(return_dict, indent=4)
396
397    def driver_source_filter(self, temp_key_model):
398        for enable_name_lower in list(temp_key_model.keys()):
399            if enable_name_lower.find("HDF") != -1:
400                key_name = ''.join(["HDF", enable_name_lower.
401                                   split("HDF")[-1]]).lower()
402            else:
403                key_name = enable_name_lower.lower()
404            temp_key_model[key_name] = temp_key_model.pop(enable_name_lower)
405            for file_path in temp_key_model[key_name]["drivers_sources"]:
406                if not os.path.exists(file_path):
407                    temp_key_model[key_name]["drivers_sources"].remove(file_path)
408        return temp_key_model
409
410    def get_model_scan(self):
411        liteos_model = self.scan_build()
412        model_path_dict = self._get_model_file_dict(liteos_model=liteos_model)
413        model_path_dict = self.model_able_scan(model_path_dict)
414        return_dict, need_replace, import_gni_path, import_replace_name =\
415            self.get_need_result(model_path_dict)
416        return self.replace_file_path(return_dict, import_gni_path,
417                                      import_replace_name, need_replace)
418
419    def found_import_file_path(self, temp_path, import_path,
420                               import_replace_name, template_replace_dict):
421        import_info = hdf_utils.read_file_lines(self.root + temp_path)
422        for temp_import in import_info:
423            if re.search(r"^import", temp_import.strip()):
424                import_path.append(temp_import.strip("import")[2:-3])
425        for import_element in import_path:
426            import_info.extend(hdf_utils.
427                               read_file_lines(self.root + import_element[1:]))
428        for name in import_replace_name:
429            for temp_import in import_info:
430                re_replace = "^%s" % name
431                if re.search(re_replace, temp_import.strip()):
432                    replace_line = temp_import.strip().split("=")
433                    template_replace_dict[replace_line[0].strip()] =\
434                        self.root + replace_line[-1]\
435                            .strip().strip('"')
436                    break
437