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 platform
14import re
15from string import Template
16
17import hdf_utils
18from hdf_tool_exception import HdfToolException
19from .hdf_command_error_code import CommandErrorCode
20
21
22class HdfLinuxScan(object):
23    def __init__(self, root, vendor, board):
24        self.root = root
25        self.vendor = vendor
26        self.board = board
27        self.kernel = "linux"
28
29        self.makefile_path = hdf_utils.get_vendor_makefile_path(
30            root, kernel="linux")
31        if not os.path.exists(self.makefile_path):
32            raise HdfToolException(
33                'Makefile: %s not exist' % self.makefile_path,
34                CommandErrorCode.TARGET_NOT_EXIST)
35
36        self.framework_dir = hdf_utils.get_module_dir(self.root)
37        if not os.path.exists(self.framework_dir):
38            raise HdfToolException(
39                'file: %s not exist' % self.framework_dir,
40                CommandErrorCode.TARGET_NOT_EXIST)
41
42        self.hcs_path = hdf_utils.get_hcs_file_path(
43            self.root, self.vendor, self.board)
44        if not os.path.exists(self.hcs_path):
45            raise HdfToolException('file: %s not exist' % self.hcs_path,
46                                   CommandErrorCode.TARGET_NOT_EXIST)
47        self.contents = hdf_utils.read_file_lines(self.makefile_path)
48        self.driver_configs_list = ['Kconfig', 'Makefile']
49        self.re_temp1_model = r'model/[a-z _ 0-9]+'
50        self.re_temp2_obj = r"^obj-"
51        self.re_temp3_enable = r"CONFIG_DRIVERS_[A-Z _ 0-9]+"
52        self.re_temp4_include = r"^include"
53        self.re_temp_prefix0 = r"^../[. /]+"
54        self.re_temp_prefix1 = r"\$\(HDF_DIR_PREFIX\)"
55        self.re_temp_prefix2 = r"\$\(KHDF_AUDIO_BASE_ROOT_DIR\)"
56        self.re_temp_current = r"^\.\/"
57        self.te_temp5 = r"^[a-z]"
58
59    def scan_makefile(self):
60        model_enable_dict = {}
61        for index, lines in enumerate(self.contents):
62            temp_list = []
63            obj_result = re.search(self.re_temp2_obj, lines.strip())
64            if not obj_result:
65                continue
66            enable_name_result = re.search(
67                self.re_temp3_enable, lines.strip())
68            model_path = lines.split("+=")[-1].strip()
69            model_name_result = re.search(
70                self.re_temp1_model, model_path)
71            if not model_name_result:
72                continue
73            model_name = enable_name_result.group()
74            model_path = os.path.join(os.path.sep.join(
75                self.makefile_path.split(os.path.sep)[:-1]), model_path)
76            temp_list.append(model_path)
77            if os.path.exists(model_path):
78                if model_enable_dict.get(model_name, None):
79                    model_enable_dict[model_name] = \
80                        temp_list + model_enable_dict.get(model_name)
81                else:
82                    model_enable_dict[model_name] = temp_list
83        return model_enable_dict
84
85    def get_config_path(self):
86        model_defconfig_list = []
87        config_path = hdf_utils.\
88            get_config_config_path(root=self.root, kernel=self.kernel)
89        for roots, dirs, files in os.walk(config_path):
90            for file_name in files:
91                if file_name.endswith("small_defconfig"):
92                    model_defconfig_list.append(os.path.join(roots, file_name))
93        return model_defconfig_list
94
95    def enable_scan(self):
96        enable_list = []
97        judge_enable_dict = self.scan_makefile()
98        defconfig_path = self.get_config_path()[0]
99        config_enable_lines = hdf_utils.read_file_lines(defconfig_path)
100        for enable_name, model_path in judge_enable_dict.items():
101            # First determine whether the enable file is enabled
102            # (get the enable configuration file path)
103            if ('%s=y\n' % enable_name) not in config_enable_lines:
104                continue
105            if "Makefile" in os.listdir(model_path[0]):
106                if os.path.join(model_path[0],
107                                'Makefile') not in enable_list:
108                    enable_list.append(
109                        os.path.join(model_path[0], 'Makefile'))
110        return enable_list
111
112    def _get_model_file_dict(self):
113        model_file_dict = {}
114        for model_name in self.scan_makefile():
115            model_file_dict[model_name] = []
116            path = os.path.join(self.framework_dir, model_name)
117            for root_path, dirs, files in os.walk(path):
118                for file_name in files:
119                    model_file_dict.get(model_name).append(
120                        os.path.join(root_path, file_name))
121        return model_file_dict
122
123    def _get_model_name(self, model_makefile_path):
124        parent_path = "/".join(model_makefile_path.split("/")[:-1])
125        config_file_path = []
126        for i in os.listdir(parent_path):
127            if i in self.driver_configs_list:
128                config_file_path.append(os.path.join(parent_path, i))
129        model_name = re.search(
130            self.re_temp1_model, model_makefile_path).group().split("/")[-1]
131        return model_name
132
133    def scan_enable_dict(self, lines, test_dict, names):
134        re_temp3 = "CONFIG_[A-Z _ 0-9]+"
135        list1_12 = []
136        for index, line in enumerate(lines):
137            if re.search("^ifeq", line):
138                list1_12.append(index)
139            elif re.search("^endif", line):
140                list1_12.append(index + 1)
141        obj_list = self.get_obj(lines)
142
143        if list1_12:
144            obj_list.extend(list1_12)
145            obj_list.sort()
146            obj_list = list(filter(
147                lambda x: x >= list1_12[1] or x <= list1_12[0], obj_list))
148            if obj_list == list1_12:
149                obj_list.clear()
150
151        obj_list = self.split_list(obj_list)
152        if list1_12 in obj_list:
153            obj_list.remove(list1_12)
154
155        if list1_12:
156            names = re.search(re_temp3, lines[list1_12[0]].strip()).group()
157            test2_dict = {}
158            test_dict[names] = self.scan_enable_dict(
159                lines[list1_12[0] + 1:list1_12[1] - 1], test2_dict, names)
160            temp_lines = lines
161        else:
162            temp_lines = lines
163        temp_list00 = []
164        for i in obj_list:
165            if i[0] == i[1]:
166                continue
167            temp_result = re.search(re_temp3, temp_lines[i[0]].strip())
168            temp_list2 = []
169            if temp_result is not None:
170                key_name = temp_result.group()
171                temp_list00.append(key_name)
172            else:
173                key_name = names
174            for j in temp_lines[i[0]: i[1]]:
175                if j.strip().strip("\\").strip().split(".")[-1] == "o":
176                    temp_list2.append(j.strip().strip("\\").strip())
177            if key_name is None:
178                key_name = temp_list00[-1]
179
180            if test_dict.get(key_name, None):
181                if isinstance((test_dict[key_name]), list):
182                    test_dict[key_name] = test_dict[key_name] + temp_list2
183                else:
184                    test_dict[key_name] = test_dict[key_name][key_name] + temp_list2
185            else:
186                test_dict[key_name] = temp_list2
187
188        return test_dict
189
190    def get_obj(self, lines):
191        # 查找所有 obj 的开头和结尾
192        re_temp2 = "^obj-"
193        re_temp1 = "^ccflags"
194        status = 0
195        list1 = []
196        for index, line in enumerate(lines):
197            temp_result_obj = re.search(re_temp2, line.strip())
198            if temp_result_obj:
199                status = 1
200                list1.append(index)
201            temp_result_flag = re.search(re_temp1, line.strip())
202            if temp_result_flag and status == 1:
203                status = 2
204                list1.append(index)
205                break
206        #  如果status 为1时说明没有找到结尾,将整个长度的结尾
207        if status == 1:
208            list1.append(len(lines))
209        return list1
210
211    def split_list(self, src_list):
212        test_list1 = []
213        for i in range(len(src_list) - 1):
214            test_list1.append([src_list[i], src_list[i + 1]])
215        return test_list1
216
217    def get_model_scan(self):
218        enable_model_path_list = self.enable_scan()
219        result_dict = {}
220        for model_makefile_path in enable_model_path_list:
221            model_name = self._get_model_name(model_makefile_path)
222            model_makefile_lines = hdf_utils.\
223                read_file_lines(model_makefile_path)
224            enable_dict = {}
225            config_enable_lines = hdf_utils.\
226                read_file_lines(self.get_config_path()[0])
227            result = self.scan_enable_dict(
228                model_makefile_lines, enable_dict, names=None)
229            name_split_dict, enable_list = \
230                self.name_split_func(result, config_enable_lines)
231            enable_dict = self.path_replace(
232                lines=model_makefile_lines,
233                enable_list=enable_list,
234                makefile_path=model_makefile_path)
235            result = self._prefix_template_replace(
236                name_split_dict, enable_dict,
237                makefile_path=model_makefile_path)
238            if result_dict.get(model_name, None):
239                result_dict.get(model_name).update(result)
240            else:
241                result_dict[model_name] = result
242        result_dict["deconfig"] = self.get_config_path()[0]
243        result_dict["makefile_path"] = self.makefile_path
244        result_dict["hcs_path"] = self.hcs_path
245        return json.dumps(result_dict, indent=4)
246
247    def scann_driver_configs(self, makefile_path):
248        driver_configs_list = ['Kconfig', 'Makefile']
249        parent_path = "/".join(makefile_path.split("/")[:-1])
250        config_file_path = []
251        for i in os.listdir(parent_path):
252            if i in driver_configs_list:
253                config_file_path.append(os.path.join(
254                    parent_path, i).replace("/", os.path.sep))
255        return config_file_path
256
257    def _prefix_template_replace(self,
258                                 name_split_dict, enable_dict,
259                                 makefile_path):
260        config_file_path = self.scann_driver_configs(makefile_path)
261        return_dict = {}
262        temp_template = Template(json.dumps(name_split_dict))
263        replace_result_dict = json.loads(temp_template.substitute(enable_dict))
264        for result_k, result_v in replace_result_dict.items():
265            if not isinstance(result_v, dict):
266                return_dict[result_k] = {}
267                drivers_sources = []
268                drivers_sources_list = self.drivers_file_find(
269                    result_v, makefile_path, drivers_sources)
270                return_dict[result_k] = {
271                    "driver_configs": config_file_path,
272                    "drivers_sources": drivers_sources_list,
273                }
274            else:
275                return_dict[result_k] = {}
276                for result_assistant_k, result_assistant_v in result_v.items():
277                    drivers_sources = []
278                    drivers_sources_list = self.drivers_file_find(
279                        result_assistant_v, makefile_path, drivers_sources)
280                    return_dict.get(result_k)[result_assistant_k] = {
281                        "driver_configs": config_file_path,
282                        "drivers_sources": drivers_sources_list,
283                    }
284        return return_dict
285
286    def drivers_file_find(self, args_list, makefile_path, drivers_sources):
287        for info in args_list:
288            info = info.replace(".o", ".c")
289            prefix4_field = re.search(self.re_temp_current, info)
290            prefix5_field = re.search(self.te_temp5, info)
291            if prefix4_field or prefix5_field:
292                replace_field = "/".join(
293                    makefile_path.replace("\\", '/').
294                        split("/")[:-1]
295                ) + "/"
296                if prefix4_field:
297                    info = info.replace(prefix4_field.group(),
298                                  replace_field).strip()
299                elif prefix5_field:
300                    info = os.path.join(replace_field, info)
301            if info.find("=") != -1:
302                info = info.split("=")[-1].strip()
303            if os.path.exists(info):
304                drivers_sources.append(info)
305        return drivers_sources
306
307    def path_replace(self, lines, enable_list, makefile_path):
308        enable_dict = {}
309        include_list = []
310        for line in lines:
311            if re.search(self.re_temp4_include, line):
312                include_list.append(
313                    os.path.join(makefile_path.strip("Makefile"),
314                                 line.strip().split("/")[-1]))
315        include_lines = []
316        for include_file in include_list:
317            if os.path.exists(include_file):
318                with open(include_file, "r") as f:
319                    include_lines = f.readlines()
320        if include_lines:
321            lines.extend(include_lines)
322
323        for key_enable in list(set(enable_list)):
324            re_enable = '^%s' % key_enable
325            for line in lines:
326                result = re.search(re_enable, line)
327                if not result:
328                    continue
329                line = line.strip().split("=")[-1].strip()
330                enable_dict = self.parent_path_regular(
331                    line, enable_dict, key_enable, makefile_path)
332        return enable_dict
333
334    def parent_path_regular(self, line, enable_dict, key_enable, makefile_path):
335        result_replace_field = re.search(
336            self.re_temp_prefix0, line)
337        prefix1_field = re.search(self.re_temp_prefix1, line)
338        prefix2_field = re.search(self.re_temp_prefix2, line)
339        prefix3_field = re.search(self.re_temp_current, line)
340        if result_replace_field or prefix1_field \
341                or prefix2_field or prefix3_field:
342            if result_replace_field:
343                enable_dict[key_enable] = self.parent_path_splice(
344                    self.re_temp_prefix0, line)
345            if prefix1_field:
346                enable_dict[key_enable] = self.parent_path_splice(
347                    self.re_temp_prefix1, line)
348            if prefix2_field:
349                enable_dict[key_enable] = "/".join(
350                    [self.root, line.strip(
351                        prefix2_field.group() + "/").strip()])
352            if prefix3_field:
353                replace_field = "/".join(
354                    makefile_path.replace("\\", '/').split("/")[:-1])
355                enable_dict[key_enable] = line.replace(
356                    prefix3_field.group(), replace_field).strip()
357        else:
358            enable_dict[key_enable] = line.split("=")[-1].strip()
359        return enable_dict
360
361    def parent_path_splice(self, re_prefix, line):
362        re_split_list = re.split(re_prefix, line)
363        if re_split_list[-1].startswith("hdf_core"):
364            return "/".join([self.root, "drivers",
365                             re_split_list[-1].strip()])
366        else:
367            if re_split_list[-1].startswith("framework"):
368                return "/".join([self.root, "drivers/hdf_core",
369                                 re_split_list[-1].strip()])
370            else:
371                return "/".join([self.root, "drivers",
372                                 re_split_list[-1].strip()])
373
374    def name_split_func(self, result, config_enable_lines):
375        enable_list = []
376        name_split_dict = {}
377        for enable_key, enable_value in result.items():
378            key = "%s=y\n" % enable_key
379            if key not in config_enable_lines:
380                continue
381            if isinstance(enable_value, list):
382                if enable_key.find("HDF") != -1:
383                    k2 = ''.join(
384                        ["HDF", enable_key.split("HDF")[-1]]
385                    ).lower()
386                else:
387                    k2 = enable_key.lower()
388                name_split_dict[k2] = []
389                for name in enable_value:
390                    child_enable_list, str1 = self.get_driver(name)
391                    enable_list.extend(child_enable_list)
392                    name_split_dict.get(k2).append(str1)
393        return name_split_dict, enable_list
394
395    def get_driver(self, name):
396        if name.find("+=") != -1:
397            return self.get_name(str1=name.split("+=")[-1].strip())
398        else:
399            return self.get_name(str1=name.strip())
400
401    def operate_dict(self, enable_key, name_split_dict, enable_value,
402                     config_enable_lines, enable_list):
403        if enable_key.find("HDF") != -1:
404            child_enable_key = ''.join(
405                ["HDF", enable_key.split("HDF")[-1]]
406            ).lower()
407        else:
408            child_enable_key = enable_key.lower()
409        name_split_dict[child_enable_key] = {}
410        for k1, v1 in enable_value.items():
411            if k1.find("HDF") != -1:
412                grand_enable_key = ''.join(
413                    ["HDF", k1.split("HDF")[-1]]
414                ).lower()
415            else:
416                grand_enable_key = k1.lower()
417            # First judge the enables of the second level
418            # (if there is no enable, the next file is directly)
419            key1 = "%s=y\n" % k1
420            if key1 in config_enable_lines:
421                name_split_dict.get(child_enable_key)[grand_enable_key] = []
422                for name in v1:
423                    # Divided into several cases
424                    child_enable_list, str1 = self.get_driver(name)
425                    enable_list.extend(child_enable_list)
426                    name_split_dict.get(child_enable_key).get(
427                        grand_enable_key).append(str1)
428            else:
429                continue
430        return enable_list, name_split_dict
431
432    def get_name(self, str1):
433        temp_re = "[A-Z _ 0-9]+"
434        list1 = str1.split("/")
435        list2 = []
436        for i in list1:
437            if i.find('$(') != -1:
438                temp_name = re.search(temp_re, i)
439                if temp_name:
440                    list2.append(temp_name.group())
441        dict1 = {"$(": "${", ")": "}"}
442        for k, v in dict1.items():
443            str1 = str1.replace(k, v)
444        return list2, str1
445