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 os
12import re
13from string import Template
14
15import hdf_utils
16from .hdf_dot_config_file import HdfDotConfigFile
17
18
19class HdfModuleKconfigFile(object):
20    def __init__(self, root, module, k_path):
21        self.root = root
22        self.module = module
23        self.k_path = k_path
24        self.module_models = {
25            'self': {},
26            'children': []
27        }
28        self.lines = []
29        self.dot_config = None
30        self.config_re = re.compile(r'\s*config\s+([a-zA-Z0-9_\-]+)')
31        self.depends_on_re = re.compile(r'depends\s+on\s+([a-zA-Z0-9_\-]+)')
32        self.choice_re = re.compile(r'^\s*choice\s*$')
33        self.endchoice_re = re.compile(r'^\s*endchoice\s*$')
34        self.config_splitter = 'DRIVERS_HDF_'
35        self.top_res = [
36            (self.config_re, self._parse_config),
37            (self.choice_re, self._parse_choice),
38            (self.endchoice_re, None)
39        ]
40
41    def _add_model(self, name_, config_item_, depends_on_item_, enabled_):
42        model = {'name': name_,
43                 'config_item': config_item_,
44                 'depends_on_item': depends_on_item_,
45                 'enabled': enabled_}
46        if name_ == self.module:
47            self.module_models['self'] = model
48        else:
49            self.module_models.get('children').append(model)
50
51    def _is_any_top_res_match(self, line):
52        for re_pair in self.top_res:
53            if re_pair[0].search(line):
54                return True
55        return False
56
57    def _block_top_match(self, current_index, parent_item):
58        line = self.lines[current_index]
59        for re_pair in self.top_res:
60            match_obj = re_pair[0].search(line)
61            if match_obj:
62                func_obj = re_pair[1]
63                if func_obj is not None:
64                    return func_obj(match_obj, current_index, parent_item)
65        return 0
66
67    def _parse_config(self, match_obj, start, parent_item):
68        config_item = match_obj.group(1)
69        parts = config_item.split(self.config_splitter)
70        valid_parts = [part for part in parts if part]
71        if not valid_parts:
72            return
73        config_name = valid_parts[-1].lower()
74        depends_on_item = ''
75        end = start + 1
76        while end < len(self.lines):
77            line = self.lines[end]
78            match_obj = self.depends_on_re.search(line)
79            if match_obj:
80                depends_on_item = match_obj.group(1)
81                break
82            if self._is_any_top_res_match(line):
83                break
84            end += 1
85        if not depends_on_item:
86            depends_on_item = parent_item
87            block_lines = end - start
88        else:
89            block_lines = end - start + 1
90        enabled = self.dot_config.is_enabled(config_item, depends_on_item)
91        self._add_model(config_name, config_item, depends_on_item, enabled)
92        return block_lines
93
94    def _parse_choice(self, _match_obj, start, _parent_item):
95        end = start + 1
96        common_depends_on_item = ''
97        depends_on_obj = None
98        while end < len(self.lines):
99            line = self.lines[end]
100            match_obj = self.depends_on_re.search(line)
101            if match_obj:
102                if not depends_on_obj:
103                    depends_on_obj = match_obj
104                    common_depends_on_item = match_obj.group(1)
105                end += 1
106                continue
107            if self.endchoice_re.search(line):
108                end += 1
109                return end - start + 1
110            consumed = self._block_top_match(end, common_depends_on_item)
111            if consumed:
112                end += consumed
113            else:
114                end += 1
115
116    def get_models(self):
117        if not os.path.exists(self.k_path):
118            return
119        self.lines = hdf_utils.read_file_lines(self.k_path)
120        dot_config_path = hdf_utils.get_liteos_a_dot_config_path(self.root)
121        self.dot_config = HdfDotConfigFile(dot_config_path)
122        index = 0
123        while index < len(self.lines):
124            consume = self._block_top_match(index, '')
125            if consume:
126                index += consume
127            else:
128                index += 1
129        return self.module_models
130
131    def _get_driver_kconfig_item(self, driver):
132        templates_dir = hdf_utils.get_templates_lite_dir()
133        template = os.path.join(templates_dir, 'hdf_driver_kconfig.template')
134        template_str = hdf_utils.read_file(template)
135        mod_converter = hdf_utils.WordsConverter(self.module)
136        drv_converter = hdf_utils.WordsConverter(driver)
137        data_model = {
138            'driver_upper_case': drv_converter.upper_case(),
139            'driver_lower_case': drv_converter.lower_case(),
140            'module_upper_case': mod_converter.upper_case()
141        }
142        config_item = 'DRIVERS_HDF_%s' % drv_converter.upper_case()
143        depends_on_item = 'DRIVERS_HDF_%s' % mod_converter.upper_case()
144        config_option = {'name': drv_converter.lower_case(),
145                         'config_item': config_item,
146                         'depends_on_item': depends_on_item,
147                         'enabled': False}
148        config = Template(template_str).safe_substitute(data_model)
149        return config_option, config
150
151    def _get_begin_end_flag(self, driver):
152        id_ = hdf_utils.get_id(self.module, driver)
153        begin_flag = '\n# <begin %s\n' % id_
154        end_flag = '\n# %s end>\n' % id_
155        return begin_flag, end_flag
156
157    def add_driver(self, driver):
158        file_content = hdf_utils.read_file(self.k_path)
159        begin_flag, end_flag = self._get_begin_end_flag(driver)
160        k_option, k_item = self._get_driver_kconfig_item(driver)
161        new_content = hdf_utils.SectionContent(begin_flag, k_item, end_flag)
162        old_content = hdf_utils.SectionContent(begin_flag, '', end_flag)
163        old_range = hdf_utils.find_section(file_content, old_content)
164        if old_range:
165            hdf_utils.replace_and_save(file_content, self.k_path,
166                                       old_range, new_content)
167        else:
168            hdf_utils.append_and_save(file_content, self.k_path, new_content)
169        return k_option
170
171    def delete_driver(self, driver):
172        if not os.path.exists(self.k_path):
173            return
174        file_content = hdf_utils.read_file(self.k_path)
175        begin_flag, end_flag = self._get_begin_end_flag(driver)
176        old_content = hdf_utils.SectionContent(begin_flag, '', end_flag)
177        old_range = hdf_utils.find_section(file_content, old_content)
178        if not old_range:
179            return
180        hdf_utils.delete_and_save(file_content, self.k_path, old_range)
181