lib_config.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. import re
  6. from checkpackagelib.base import _CheckFunction
  7. from checkpackagelib.lib import ConsecutiveEmptyLines # noqa: F401
  8. from checkpackagelib.lib import EmptyLastLine # noqa: F401
  9. from checkpackagelib.lib import NewlineAtEof # noqa: F401
  10. from checkpackagelib.lib import TrailingSpace # noqa: F401
  11. def _empty_or_comment(text):
  12. line = text.strip()
  13. # ignore empty lines and comment lines indented or not
  14. return line == "" or line.startswith("#")
  15. def _part_of_help_text(text):
  16. return text.startswith("\t ")
  17. # used in more than one check
  18. entries_that_should_not_be_indented = [
  19. "choice", "comment", "config", "endchoice", "endif", "endmenu", "if",
  20. "menu", "menuconfig", "source"]
  21. class AttributesOrder(_CheckFunction):
  22. attributes_order_convention = {
  23. "bool": 1, "prompt": 1, "string": 1, "default": 2, "depends": 3,
  24. "select": 4, "help": 5}
  25. def before(self):
  26. self.state = 0
  27. def check_line(self, lineno, text):
  28. if _empty_or_comment(text) or _part_of_help_text(text):
  29. return
  30. attribute = text.split()[0]
  31. if attribute in entries_that_should_not_be_indented:
  32. self.state = 0
  33. return
  34. if attribute not in self.attributes_order_convention.keys():
  35. return
  36. new_state = self.attributes_order_convention[attribute]
  37. wrong_order = self.state > new_state
  38. # save to process next line
  39. self.state = new_state
  40. if wrong_order:
  41. return ["{}:{}: attributes order: type, default, depends on,"
  42. " select, help ({}#_config_files)"
  43. .format(self.filename, lineno, self.url_to_manual),
  44. text]
  45. class CommentsMenusPackagesOrder(_CheckFunction):
  46. menu_of_packages = [""]
  47. package = [""]
  48. print_package_warning = [True]
  49. def before(self):
  50. self.state = ""
  51. self.level = 0
  52. self.new_package = ""
  53. def get_level(self):
  54. return len(self.state.split('-')) - 1
  55. def initialize_package_level_elements(self, text):
  56. try:
  57. self.menu_of_packages[self.level] = text[:-1]
  58. self.package[self.level] = ""
  59. self.print_package_warning[self.level] = True
  60. except IndexError:
  61. self.menu_of_packages.append(text[:-1])
  62. self.package.append("")
  63. self.print_package_warning.append(True)
  64. def initialize_level_elements(self, text):
  65. self.level = self.get_level()
  66. self.initialize_package_level_elements(text)
  67. def check_line(self, lineno, text):
  68. # We only want to force sorting for the top-level menus
  69. if self.filename not in ["package/Config.in",
  70. "package/Config.in.host"]:
  71. return
  72. source_line = re.match(r'^\s*source ".*/([^/]*)/Config.in(.host)?"', text)
  73. if text.startswith("comment ") or text.startswith("if ") or \
  74. text.startswith("menu "):
  75. if text.startswith("comment"):
  76. if not self.state.endswith("-comment"):
  77. self.state += "-comment"
  78. elif text.startswith("if") or text.startswith("menu"):
  79. if text.startswith("if"):
  80. self.state += "-if"
  81. elif text.startswith("menu"):
  82. self.state += "-menu"
  83. self.initialize_level_elements(text)
  84. elif text.startswith("endif") or text.startswith("endmenu"):
  85. if self.state.endswith("comment"):
  86. self.state = self.state[:-8]
  87. if text.startswith("endif"):
  88. self.state = self.state[:-3]
  89. elif text.startswith("endmenu"):
  90. self.state = self.state[:-5]
  91. self.level = self.get_level()
  92. elif source_line:
  93. self.new_package = source_line.group(1)
  94. # We order _ before A, so replace it with .
  95. new_package_ord = self.new_package.replace('_', '.')
  96. if self.package[self.level] != "" and \
  97. self.print_package_warning[self.level] and \
  98. new_package_ord < self.package[self.level]:
  99. self.print_package_warning[self.level] = False
  100. prefix = "{}:{}: ".format(self.filename, lineno)
  101. spaces = " " * len(prefix)
  102. return ["{prefix}Packages in: {menu},\n"
  103. "{spaces}are not alphabetically ordered;\n"
  104. "{spaces}correct order: '-', '_', digits, capitals, lowercase;\n"
  105. "{spaces}first incorrect package: {package}"
  106. .format(prefix=prefix, spaces=spaces,
  107. menu=self.menu_of_packages[self.level],
  108. package=self.new_package),
  109. text]
  110. self.package[self.level] = new_package_ord
  111. class HelpText(_CheckFunction):
  112. HELP_TEXT_FORMAT = re.compile("^\t .{,62}$")
  113. URL_ONLY = re.compile("^(http|https|git)://\S*$")
  114. def before(self):
  115. self.help_text = False
  116. def check_line(self, lineno, text):
  117. if _empty_or_comment(text):
  118. return
  119. entry = text.split()[0]
  120. if entry in entries_that_should_not_be_indented:
  121. self.help_text = False
  122. return
  123. if text.strip() == "help":
  124. self.help_text = True
  125. return
  126. if not self.help_text:
  127. return
  128. if self.HELP_TEXT_FORMAT.match(text.rstrip()):
  129. return
  130. if self.URL_ONLY.match(text.strip()):
  131. return
  132. return ["{}:{}: help text: <tab><2 spaces><62 chars>"
  133. " ({}#writing-rules-config-in)"
  134. .format(self.filename, lineno, self.url_to_manual),
  135. text,
  136. "\t " + "123456789 " * 6 + "12"]
  137. class Indent(_CheckFunction):
  138. ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
  139. entries_that_should_be_indented = [
  140. "bool", "default", "depends", "help", "prompt", "select", "string"]
  141. def before(self):
  142. self.backslash = False
  143. def check_line(self, lineno, text):
  144. if _empty_or_comment(text) or _part_of_help_text(text):
  145. self.backslash = False
  146. return
  147. entry = text.split()[0]
  148. last_line_ends_in_backslash = self.backslash
  149. # calculate for next line
  150. if self.ENDS_WITH_BACKSLASH.search(text):
  151. self.backslash = True
  152. else:
  153. self.backslash = False
  154. if last_line_ends_in_backslash:
  155. if text.startswith("\t"):
  156. return
  157. return ["{}:{}: continuation line should be indented using tabs"
  158. .format(self.filename, lineno),
  159. text]
  160. if entry in self.entries_that_should_be_indented:
  161. if not text.startswith("\t{}".format(entry)):
  162. return ["{}:{}: should be indented with one tab"
  163. " ({}#_config_files)"
  164. .format(self.filename, lineno, self.url_to_manual),
  165. text]
  166. elif entry in entries_that_should_not_be_indented:
  167. if not text.startswith(entry):
  168. # four Config.in files have a special but legitimate indentation rule
  169. if self.filename in ["package/Config.in",
  170. "package/Config.in.host",
  171. "package/kodi/Config.in",
  172. "package/x11r7/Config.in"]:
  173. return
  174. return ["{}:{}: should not be indented"
  175. .format(self.filename, lineno),
  176. text]