xref: /OK3568_Linux_fs/buildroot/utils/checkpackagelib/lib_config.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1# See utils/checkpackagelib/readme.txt before editing this file.
2# Kconfig generates errors if someone introduces a typo like "boool" instead of
3# "bool", so below check functions don't need to check for things already
4# checked by running "make menuconfig".
5
6import re
7
8from checkpackagelib.base import _CheckFunction
9from checkpackagelib.lib import ConsecutiveEmptyLines  # noqa: F401
10from checkpackagelib.lib import EmptyLastLine          # noqa: F401
11from checkpackagelib.lib import NewlineAtEof           # noqa: F401
12from checkpackagelib.lib import TrailingSpace          # noqa: F401
13
14
15def _empty_or_comment(text):
16    line = text.strip()
17    # ignore empty lines and comment lines indented or not
18    return line == "" or line.startswith("#")
19
20
21def _part_of_help_text(text):
22    return text.startswith("\t  ")
23
24
25# used in more than one check
26entries_that_should_not_be_indented = [
27    "choice", "comment", "config", "endchoice", "endif", "endmenu", "if",
28    "menu", "menuconfig", "source"]
29
30
31class AttributesOrder(_CheckFunction):
32    attributes_order_convention = {
33        "bool": 1, "prompt": 1, "string": 1, "default": 2, "depends": 3,
34        "select": 4, "help": 5}
35
36    def before(self):
37        self.state = 0
38
39    def check_line(self, lineno, text):
40        if _empty_or_comment(text) or _part_of_help_text(text):
41            return
42
43        attribute = text.split()[0]
44
45        if attribute in entries_that_should_not_be_indented:
46            self.state = 0
47            return
48        if attribute not in self.attributes_order_convention.keys():
49            return
50        new_state = self.attributes_order_convention[attribute]
51        wrong_order = self.state > new_state
52
53        # save to process next line
54        self.state = new_state
55
56        if wrong_order:
57            return ["{}:{}: attributes order: type, default, depends on,"
58                    " select, help ({}#_config_files)"
59                    .format(self.filename, lineno, self.url_to_manual),
60                    text]
61
62
63class CommentsMenusPackagesOrder(_CheckFunction):
64    def before(self):
65        self.level = 0
66        self.menu_of_packages = ["The top level menu"]
67        self.new_package = ""
68        self.package = [""]
69        self.print_package_warning = [True]
70        self.state = ""
71
72    def get_level(self):
73        return len(self.state.split('-')) - 1
74
75    def initialize_package_level_elements(self, text):
76        try:
77            self.menu_of_packages[self.level] = text[:-1]
78            self.package[self.level] = ""
79            self.print_package_warning[self.level] = True
80        except IndexError:
81            self.menu_of_packages.append(text[:-1])
82            self.package.append("")
83            self.print_package_warning.append(True)
84
85    def initialize_level_elements(self, text):
86        self.level = self.get_level()
87        self.initialize_package_level_elements(text)
88
89    def check_line(self, lineno, text):
90        # We only want to force sorting for the top-level menus
91        if self.filename not in ["fs/Config.in",
92                                 "package/Config.in",
93                                 "package/Config.in.host",
94                                 "package/kodi/Config.in"]:
95            return
96
97        source_line = re.match(r'^\s*source ".*/([^/]*)/Config.in(.host)?"', text)
98
99        if text.startswith("comment "):
100            if not self.state.endswith("-comment"):
101                self.state += "-comment"
102
103            self.initialize_level_elements(text)
104
105        elif text.startswith("if "):
106            self.state += "-if"
107
108            self.initialize_level_elements(text)
109
110        elif text.startswith("menu "):
111            if self.state.endswith("-comment"):
112                self.state = self.state[:-8]
113
114            self.state += "-menu"
115
116            self.initialize_level_elements(text)
117
118        elif text.startswith("endif") or text.startswith("endmenu"):
119            if self.state.endswith("-comment"):
120                self.state = self.state[:-8]
121
122            if text.startswith("endif"):
123                self.state = self.state[:-3]
124
125            elif text.startswith("endmenu"):
126                self.state = self.state[:-5]
127
128            self.level = self.get_level()
129
130        elif source_line:
131            self.new_package = source_line.group(1)
132
133            # We order _ before A, so replace it with .
134            new_package_ord = self.new_package.replace('_', '.')
135
136            if self.package[self.level] != "" and \
137               self.print_package_warning[self.level] and \
138               new_package_ord < self.package[self.level]:
139                self.print_package_warning[self.level] = False
140                prefix = "{}:{}: ".format(self.filename, lineno)
141                spaces = " " * len(prefix)
142                return ["{prefix}Packages in: {menu},\n"
143                        "{spaces}are not alphabetically ordered;\n"
144                        "{spaces}correct order: '-', '_', digits, capitals, lowercase;\n"
145                        "{spaces}first incorrect package: {package}"
146                        .format(prefix=prefix, spaces=spaces,
147                                menu=self.menu_of_packages[self.level],
148                                package=self.new_package),
149                        text]
150
151            self.package[self.level] = new_package_ord
152
153
154class HelpText(_CheckFunction):
155    HELP_TEXT_FORMAT = re.compile(r"^\t  .{,62}$")
156    URL_ONLY = re.compile(r"^(http|https|git)://\S*$")
157
158    def before(self):
159        self.help_text = False
160
161    def check_line(self, lineno, text):
162        if _empty_or_comment(text):
163            return
164
165        entry = text.split()[0]
166
167        if entry in entries_that_should_not_be_indented:
168            self.help_text = False
169            return
170        if text.strip() == "help":
171            self.help_text = True
172            return
173
174        if not self.help_text:
175            return
176
177        if self.HELP_TEXT_FORMAT.match(text.rstrip()):
178            return
179        if self.URL_ONLY.match(text.strip()):
180            return
181        return ["{}:{}: help text: <tab><2 spaces><62 chars>"
182                " ({}#writing-rules-config-in)"
183                .format(self.filename, lineno, self.url_to_manual),
184                text,
185                "\t  " + "123456789 " * 6 + "12"]
186
187
188class Indent(_CheckFunction):
189    ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
190    entries_that_should_be_indented = [
191        "bool", "default", "depends", "help", "prompt", "select", "string"]
192
193    def before(self):
194        self.backslash = False
195
196    def check_line(self, lineno, text):
197        if _empty_or_comment(text) or _part_of_help_text(text):
198            self.backslash = False
199            return
200
201        entry = text.split()[0]
202
203        last_line_ends_in_backslash = self.backslash
204
205        # calculate for next line
206        if self.ENDS_WITH_BACKSLASH.search(text):
207            self.backslash = True
208        else:
209            self.backslash = False
210
211        if last_line_ends_in_backslash:
212            if text.startswith("\t"):
213                return
214            return ["{}:{}: continuation line should be indented using tabs"
215                    .format(self.filename, lineno),
216                    text]
217
218        if entry in self.entries_that_should_be_indented:
219            if not text.startswith("\t{}".format(entry)):
220                return ["{}:{}: should be indented with one tab"
221                        " ({}#_config_files)"
222                        .format(self.filename, lineno, self.url_to_manual),
223                        text]
224        elif entry in entries_that_should_not_be_indented:
225            if not text.startswith(entry):
226                # four Config.in files have a special but legitimate indentation rule
227                if self.filename in ["package/Config.in",
228                                     "package/Config.in.host",
229                                     "package/kodi/Config.in",
230                                     "package/x11r7/Config.in"]:
231                    return
232                return ["{}:{}: should not be indented"
233                        .format(self.filename, lineno),
234                        text]
235