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