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