2017-07-01 18:07:00 +02:00
|
|
|
# See utils/checkpackagelib/readme.txt before editing this file.
|
2017-02-19 23:17:17 +01:00
|
|
|
# Kconfig generates errors if someone introduces a typo like "boool" instead of
|
|
|
|
# "bool", so below check functions don't need to check for things already
|
|
|
|
# checked by running "make menuconfig".
|
|
|
|
|
2017-02-19 23:17:23 +01:00
|
|
|
import re
|
|
|
|
|
2018-08-11 05:48:27 +02:00
|
|
|
from checkpackagelib.base import _CheckFunction
|
|
|
|
from checkpackagelib.lib import ConsecutiveEmptyLines # noqa: F401
|
|
|
|
from checkpackagelib.lib import EmptyLastLine # noqa: F401
|
|
|
|
from checkpackagelib.lib import NewlineAtEof # noqa: F401
|
|
|
|
from checkpackagelib.lib import TrailingSpace # noqa: F401
|
2021-12-26 19:49:16 +01:00
|
|
|
from checkpackagelib.tool import NotExecutable # noqa: F401
|
2017-02-19 23:17:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
def _empty_or_comment(text):
|
|
|
|
line = text.strip()
|
|
|
|
# ignore empty lines and comment lines indented or not
|
|
|
|
return line == "" or line.startswith("#")
|
|
|
|
|
|
|
|
|
|
|
|
def _part_of_help_text(text):
|
|
|
|
return text.startswith("\t ")
|
|
|
|
|
|
|
|
|
|
|
|
# used in more than one check
|
|
|
|
entries_that_should_not_be_indented = [
|
|
|
|
"choice", "comment", "config", "endchoice", "endif", "endmenu", "if",
|
|
|
|
"menu", "menuconfig", "source"]
|
|
|
|
|
|
|
|
|
|
|
|
class AttributesOrder(_CheckFunction):
|
|
|
|
attributes_order_convention = {
|
|
|
|
"bool": 1, "prompt": 1, "string": 1, "default": 2, "depends": 3,
|
|
|
|
"select": 4, "help": 5}
|
|
|
|
|
|
|
|
def before(self):
|
|
|
|
self.state = 0
|
|
|
|
|
|
|
|
def check_line(self, lineno, text):
|
|
|
|
if _empty_or_comment(text) or _part_of_help_text(text):
|
|
|
|
return
|
|
|
|
|
|
|
|
attribute = text.split()[0]
|
|
|
|
|
|
|
|
if attribute in entries_that_should_not_be_indented:
|
|
|
|
self.state = 0
|
|
|
|
return
|
|
|
|
if attribute not in self.attributes_order_convention.keys():
|
|
|
|
return
|
|
|
|
new_state = self.attributes_order_convention[attribute]
|
|
|
|
wrong_order = self.state > new_state
|
|
|
|
|
|
|
|
# save to process next line
|
|
|
|
self.state = new_state
|
|
|
|
|
|
|
|
if wrong_order:
|
|
|
|
return ["{}:{}: attributes order: type, default, depends on,"
|
|
|
|
" select, help ({}#_config_files)"
|
|
|
|
.format(self.filename, lineno, self.url_to_manual),
|
|
|
|
text]
|
|
|
|
|
|
|
|
|
2019-06-11 22:49:46 +02:00
|
|
|
class CommentsMenusPackagesOrder(_CheckFunction):
|
|
|
|
def before(self):
|
2019-09-03 23:13:25 +02:00
|
|
|
self.level = 0
|
2019-10-05 14:22:19 +02:00
|
|
|
self.menu_of_packages = ["The top level menu"]
|
2019-09-03 23:13:28 +02:00
|
|
|
self.new_package = ""
|
2019-10-05 14:22:18 +02:00
|
|
|
self.package = [""]
|
|
|
|
self.print_package_warning = [True]
|
|
|
|
self.state = ""
|
2019-06-11 22:49:46 +02:00
|
|
|
|
|
|
|
def get_level(self):
|
|
|
|
return len(self.state.split('-')) - 1
|
|
|
|
|
2019-09-03 23:13:27 +02:00
|
|
|
def initialize_package_level_elements(self, text):
|
|
|
|
try:
|
|
|
|
self.menu_of_packages[self.level] = text[:-1]
|
|
|
|
self.package[self.level] = ""
|
|
|
|
self.print_package_warning[self.level] = True
|
|
|
|
except IndexError:
|
|
|
|
self.menu_of_packages.append(text[:-1])
|
|
|
|
self.package.append("")
|
|
|
|
self.print_package_warning.append(True)
|
|
|
|
|
|
|
|
def initialize_level_elements(self, text):
|
|
|
|
self.level = self.get_level()
|
|
|
|
self.initialize_package_level_elements(text)
|
|
|
|
|
2019-06-11 22:49:46 +02:00
|
|
|
def check_line(self, lineno, text):
|
2019-08-01 01:02:20 +02:00
|
|
|
# We only want to force sorting for the top-level menus
|
2019-10-05 14:22:20 +02:00
|
|
|
if self.filename not in ["fs/Config.in",
|
|
|
|
"package/Config.in",
|
|
|
|
"package/Config.in.host",
|
|
|
|
"package/kodi/Config.in"]:
|
2019-08-01 01:02:20 +02:00
|
|
|
return
|
|
|
|
|
2019-09-03 23:13:24 +02:00
|
|
|
source_line = re.match(r'^\s*source ".*/([^/]*)/Config.in(.host)?"', text)
|
|
|
|
|
2019-10-05 14:22:16 +02:00
|
|
|
if text.startswith("comment "):
|
|
|
|
if not self.state.endswith("-comment"):
|
|
|
|
self.state += "-comment"
|
2019-06-11 22:49:46 +02:00
|
|
|
|
2019-10-05 14:22:16 +02:00
|
|
|
self.initialize_level_elements(text)
|
|
|
|
|
|
|
|
elif text.startswith("if "):
|
|
|
|
self.state += "-if"
|
2019-06-11 22:49:46 +02:00
|
|
|
|
2019-10-05 14:22:16 +02:00
|
|
|
self.initialize_level_elements(text)
|
2019-06-11 22:49:46 +02:00
|
|
|
|
2019-10-05 14:22:16 +02:00
|
|
|
elif text.startswith("menu "):
|
|
|
|
if self.state.endswith("-comment"):
|
|
|
|
self.state = self.state[:-8]
|
2019-10-05 14:22:15 +02:00
|
|
|
|
2019-10-05 14:22:16 +02:00
|
|
|
self.state += "-menu"
|
2019-06-11 22:49:46 +02:00
|
|
|
|
2019-09-03 23:13:27 +02:00
|
|
|
self.initialize_level_elements(text)
|
2019-06-11 22:49:46 +02:00
|
|
|
|
|
|
|
elif text.startswith("endif") or text.startswith("endmenu"):
|
2019-10-05 14:22:17 +02:00
|
|
|
if self.state.endswith("-comment"):
|
2019-06-11 22:49:46 +02:00
|
|
|
self.state = self.state[:-8]
|
|
|
|
|
|
|
|
if text.startswith("endif"):
|
|
|
|
self.state = self.state[:-3]
|
|
|
|
|
|
|
|
elif text.startswith("endmenu"):
|
|
|
|
self.state = self.state[:-5]
|
|
|
|
|
2019-09-03 23:13:25 +02:00
|
|
|
self.level = self.get_level()
|
2019-09-03 23:13:26 +02:00
|
|
|
|
|
|
|
elif source_line:
|
2019-09-03 23:13:28 +02:00
|
|
|
self.new_package = source_line.group(1)
|
2019-06-11 22:49:46 +02:00
|
|
|
|
|
|
|
# We order _ before A, so replace it with .
|
2019-09-03 23:13:28 +02:00
|
|
|
new_package_ord = self.new_package.replace('_', '.')
|
2019-06-11 22:49:46 +02:00
|
|
|
|
2019-09-03 23:13:25 +02:00
|
|
|
if self.package[self.level] != "" and \
|
|
|
|
self.print_package_warning[self.level] and \
|
|
|
|
new_package_ord < self.package[self.level]:
|
|
|
|
self.print_package_warning[self.level] = False
|
2019-06-11 22:49:46 +02:00
|
|
|
prefix = "{}:{}: ".format(self.filename, lineno)
|
|
|
|
spaces = " " * len(prefix)
|
|
|
|
return ["{prefix}Packages in: {menu},\n"
|
|
|
|
"{spaces}are not alphabetically ordered;\n"
|
|
|
|
"{spaces}correct order: '-', '_', digits, capitals, lowercase;\n"
|
|
|
|
"{spaces}first incorrect package: {package}"
|
|
|
|
.format(prefix=prefix, spaces=spaces,
|
2019-09-03 23:13:25 +02:00
|
|
|
menu=self.menu_of_packages[self.level],
|
2019-09-03 23:13:28 +02:00
|
|
|
package=self.new_package),
|
2019-06-11 22:49:46 +02:00
|
|
|
text]
|
|
|
|
|
2019-09-03 23:13:25 +02:00
|
|
|
self.package[self.level] = new_package_ord
|
2019-06-11 22:49:46 +02:00
|
|
|
|
|
|
|
|
2017-02-19 23:17:23 +01:00
|
|
|
class HelpText(_CheckFunction):
|
2020-08-12 16:28:59 +02:00
|
|
|
HELP_TEXT_FORMAT = re.compile(r"^\t .{,62}$")
|
|
|
|
URL_ONLY = re.compile(r"^(http|https|git)://\S*$")
|
2017-02-19 23:17:23 +01:00
|
|
|
|
|
|
|
def before(self):
|
|
|
|
self.help_text = False
|
|
|
|
|
|
|
|
def check_line(self, lineno, text):
|
|
|
|
if _empty_or_comment(text):
|
|
|
|
return
|
|
|
|
|
|
|
|
entry = text.split()[0]
|
|
|
|
|
|
|
|
if entry in entries_that_should_not_be_indented:
|
|
|
|
self.help_text = False
|
|
|
|
return
|
|
|
|
if text.strip() == "help":
|
|
|
|
self.help_text = True
|
|
|
|
return
|
|
|
|
|
|
|
|
if not self.help_text:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.HELP_TEXT_FORMAT.match(text.rstrip()):
|
|
|
|
return
|
|
|
|
if self.URL_ONLY.match(text.strip()):
|
|
|
|
return
|
|
|
|
return ["{}:{}: help text: <tab><2 spaces><62 chars>"
|
|
|
|
" ({}#writing-rules-config-in)"
|
|
|
|
.format(self.filename, lineno, self.url_to_manual),
|
|
|
|
text,
|
|
|
|
"\t " + "123456789 " * 6 + "12"]
|
|
|
|
|
|
|
|
|
|
|
|
class Indent(_CheckFunction):
|
|
|
|
ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
|
|
|
|
entries_that_should_be_indented = [
|
|
|
|
"bool", "default", "depends", "help", "prompt", "select", "string"]
|
|
|
|
|
|
|
|
def before(self):
|
|
|
|
self.backslash = False
|
|
|
|
|
|
|
|
def check_line(self, lineno, text):
|
|
|
|
if _empty_or_comment(text) or _part_of_help_text(text):
|
|
|
|
self.backslash = False
|
|
|
|
return
|
|
|
|
|
|
|
|
entry = text.split()[0]
|
|
|
|
|
|
|
|
last_line_ends_in_backslash = self.backslash
|
|
|
|
|
|
|
|
# calculate for next line
|
|
|
|
if self.ENDS_WITH_BACKSLASH.search(text):
|
|
|
|
self.backslash = True
|
|
|
|
else:
|
|
|
|
self.backslash = False
|
|
|
|
|
|
|
|
if last_line_ends_in_backslash:
|
|
|
|
if text.startswith("\t"):
|
|
|
|
return
|
|
|
|
return ["{}:{}: continuation line should be indented using tabs"
|
|
|
|
.format(self.filename, lineno),
|
|
|
|
text]
|
|
|
|
|
|
|
|
if entry in self.entries_that_should_be_indented:
|
|
|
|
if not text.startswith("\t{}".format(entry)):
|
|
|
|
return ["{}:{}: should be indented with one tab"
|
|
|
|
" ({}#_config_files)"
|
|
|
|
.format(self.filename, lineno, self.url_to_manual),
|
|
|
|
text]
|
|
|
|
elif entry in entries_that_should_not_be_indented:
|
|
|
|
if not text.startswith(entry):
|
2018-04-01 07:08:49 +02:00
|
|
|
# four Config.in files have a special but legitimate indentation rule
|
|
|
|
if self.filename in ["package/Config.in",
|
|
|
|
"package/Config.in.host",
|
|
|
|
"package/kodi/Config.in",
|
|
|
|
"package/x11r7/Config.in"]:
|
|
|
|
return
|
2017-02-19 23:17:23 +01:00
|
|
|
return ["{}:{}: should not be indented"
|
|
|
|
.format(self.filename, lineno),
|
|
|
|
text]
|
2022-11-27 14:07:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
class RedefinedConfig(_CheckFunction):
|
|
|
|
CONFIG = re.compile(r"^\s*(menu|)config\s+(BR2_\w+)\b")
|
|
|
|
IF = re.compile(r"^\s*if\s+([^#]*)\b")
|
|
|
|
ENDIF = re.compile(r"^\s*endif\b")
|
|
|
|
|
|
|
|
def before(self):
|
|
|
|
self.configs = {}
|
|
|
|
self.conditional = []
|
|
|
|
|
|
|
|
def check_line(self, lineno, text):
|
|
|
|
if _empty_or_comment(text) or _part_of_help_text(text):
|
|
|
|
return
|
|
|
|
|
|
|
|
m = self.IF.search(text)
|
|
|
|
if m is not None:
|
|
|
|
condition = m.group(1)
|
|
|
|
self.conditional.append(condition)
|
|
|
|
return
|
|
|
|
|
|
|
|
m = self.ENDIF.search(text)
|
|
|
|
if m is not None:
|
|
|
|
self.conditional.pop()
|
|
|
|
return
|
|
|
|
|
|
|
|
m = self.CONFIG.search(text)
|
|
|
|
if m is None:
|
|
|
|
return
|
|
|
|
config = m.group(2)
|
|
|
|
|
|
|
|
key = (config, ' AND '.join(self.conditional))
|
|
|
|
if key in self.configs.keys():
|
|
|
|
previous_line = self.configs[key]
|
|
|
|
return ["{}:{}: config {} redeclared (previous line: {})"
|
|
|
|
.format(self.filename, lineno, config, previous_line),
|
|
|
|
text]
|
|
|
|
self.configs[key] = lineno
|