lib_mk.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. # See utils/checkpackagelib/readme.txt before editing this file.
  2. # There are already dependency checks during the build, so below check
  3. # functions don't need to check for things already checked by exploring the
  4. # menu options using "make menuconfig" and by running "make" with appropriate
  5. # packages enabled.
  6. import re
  7. from base import _CheckFunction
  8. from lib import ConsecutiveEmptyLines # noqa: F401
  9. from lib import EmptyLastLine # noqa: F401
  10. from lib import NewlineAtEof # noqa: F401
  11. from lib import TrailingSpace # noqa: F401
  12. class Indent(_CheckFunction):
  13. COMMENT = re.compile("^\s*#")
  14. CONDITIONAL = re.compile("^\s*(ifeq|ifneq|endif)\s")
  15. ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
  16. END_DEFINE = re.compile("^\s*endef\s")
  17. MAKEFILE_TARGET = re.compile("^[^# \t]+:\s")
  18. START_DEFINE = re.compile("^\s*define\s")
  19. def before(self):
  20. self.define = False
  21. self.backslash = False
  22. self.makefile_target = False
  23. def check_line(self, lineno, text):
  24. if self.START_DEFINE.search(text):
  25. self.define = True
  26. return
  27. if self.END_DEFINE.search(text):
  28. self.define = False
  29. return
  30. expect_tabs = False
  31. if self.define or self.backslash or self.makefile_target:
  32. expect_tabs = True
  33. if self.CONDITIONAL.search(text):
  34. expect_tabs = False
  35. # calculate for next line
  36. if self.ENDS_WITH_BACKSLASH.search(text):
  37. self.backslash = True
  38. else:
  39. self.backslash = False
  40. if self.MAKEFILE_TARGET.search(text):
  41. self.makefile_target = True
  42. return
  43. if text.strip() == "":
  44. self.makefile_target = False
  45. return
  46. # comment can be indented or not inside define ... endef, so ignore it
  47. if self.define and self.COMMENT.search(text):
  48. return
  49. if expect_tabs:
  50. if not text.startswith("\t"):
  51. return ["{}:{}: expected indent with tabs"
  52. .format(self.filename, lineno),
  53. text]
  54. else:
  55. if text.startswith("\t"):
  56. return ["{}:{}: unexpected indent with tabs"
  57. .format(self.filename, lineno),
  58. text]
  59. class PackageHeader(_CheckFunction):
  60. def before(self):
  61. self.skip = False
  62. def check_line(self, lineno, text):
  63. if self.skip or lineno > 6:
  64. return
  65. if lineno in [1, 5]:
  66. if lineno == 1 and text.startswith("include "):
  67. self.skip = True
  68. return
  69. if text.rstrip() != "#" * 80:
  70. return ["{}:{}: should be 80 hashes ({}#writing-rules-mk)"
  71. .format(self.filename, lineno, self.url_to_manual),
  72. text,
  73. "#" * 80]
  74. elif lineno in [2, 4]:
  75. if text.rstrip() != "#":
  76. return ["{}:{}: should be 1 hash ({}#writing-rules-mk)"
  77. .format(self.filename, lineno, self.url_to_manual),
  78. text]
  79. elif lineno == 6:
  80. if text.rstrip() != "":
  81. return ["{}:{}: should be a blank line ({}#writing-rules-mk)"
  82. .format(self.filename, lineno, self.url_to_manual),
  83. text]
  84. class RemoveDefaultPackageSourceVariable(_CheckFunction):
  85. packages_that_may_contain_default_source = ["binutils", "gcc", "gdb"]
  86. PACKAGE_NAME = re.compile("/([^/]+)\.mk")
  87. def before(self):
  88. package = self.PACKAGE_NAME.search(self.filename).group(1)
  89. package_upper = package.replace("-", "_").upper()
  90. self.package = package
  91. self.FIND_SOURCE = re.compile(
  92. "^{}_SOURCE\s*=\s*{}-\$\({}_VERSION\)\.tar\.gz"
  93. .format(package_upper, package, package_upper))
  94. def check_line(self, lineno, text):
  95. if self.FIND_SOURCE.search(text):
  96. if self.package in self.packages_that_may_contain_default_source:
  97. return
  98. return ["{}:{}: remove default value of _SOURCE variable "
  99. "({}#generic-package-reference)"
  100. .format(self.filename, lineno, self.url_to_manual),
  101. text]
  102. class SpaceBeforeBackslash(_CheckFunction):
  103. TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH = re.compile(r"^.*( |\t)\\$")
  104. def check_line(self, lineno, text):
  105. if self.TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH.match(text.rstrip()):
  106. return ["{}:{}: use only one space before backslash"
  107. .format(self.filename, lineno),
  108. text]
  109. class TrailingBackslash(_CheckFunction):
  110. ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
  111. def before(self):
  112. self.backslash = False
  113. def check_line(self, lineno, text):
  114. last_line_ends_in_backslash = self.backslash
  115. # calculate for next line
  116. if self.ENDS_WITH_BACKSLASH.search(text):
  117. self.backslash = True
  118. self.lastline = text
  119. return
  120. self.backslash = False
  121. if last_line_ends_in_backslash and text.strip() == "":
  122. return ["{}:{}: remove trailing backslash"
  123. .format(self.filename, lineno - 1),
  124. self.lastline]
  125. class TypoInPackageVariable(_CheckFunction):
  126. ALLOWED = re.compile("|".join([
  127. "ACLOCAL_DIR",
  128. "ACLOCAL_HOST_DIR",
  129. "BR_CCACHE_INITIAL_SETUP",
  130. "BR_NO_CHECK_HASH_FOR",
  131. "LINUX_POST_PATCH_HOOKS",
  132. "LINUX_TOOLS",
  133. "LUA_RUN",
  134. "MKFS_JFFS2",
  135. "MKIMAGE_ARCH",
  136. "PKG_CONFIG_HOST_BINARY",
  137. "TARGET_FINALIZE_HOOKS",
  138. "XTENSA_CORE_NAME"]))
  139. PACKAGE_NAME = re.compile("/([^/]+)\.mk")
  140. VARIABLE = re.compile("^([A-Z0-9_]+_[A-Z0-9_]+)\s*(\+|)=")
  141. def before(self):
  142. package = self.PACKAGE_NAME.search(self.filename).group(1)
  143. package = package.replace("-", "_").upper()
  144. # linux tools do not use LINUX_TOOL_ prefix for variables
  145. package = package.replace("LINUX_TOOL_", "")
  146. self.package = package
  147. self.REGEX = re.compile("^(HOST_)?({}_[A-Z0-9_]+)".format(package))
  148. self.FIND_VIRTUAL = re.compile(
  149. "^{}_PROVIDES\s*(\+|)=\s*(.*)".format(package))
  150. self.virtual = []
  151. def check_line(self, lineno, text):
  152. m = self.VARIABLE.search(text)
  153. if m is None:
  154. return
  155. variable = m.group(1)
  156. # allow to set variables for virtual package this package provides
  157. v = self.FIND_VIRTUAL.search(text)
  158. if v:
  159. self.virtual += v.group(2).upper().split()
  160. return
  161. for virtual in self.virtual:
  162. if variable.startswith("{}_".format(virtual)):
  163. return
  164. if self.ALLOWED.match(variable):
  165. return
  166. if self.REGEX.search(text) is None:
  167. return ["{}:{}: possible typo: {} -> *{}*"
  168. .format(self.filename, lineno, variable, self.package),
  169. text]
  170. class UselessFlag(_CheckFunction):
  171. DEFAULT_AUTOTOOLS_FLAG = re.compile("^.*{}".format("|".join([
  172. "_AUTORECONF\s*=\s*NO",
  173. "_LIBTOOL_PATCH\s*=\s*YES"])))
  174. DEFAULT_GENERIC_FLAG = re.compile("^.*{}".format("|".join([
  175. "_INSTALL_IMAGES\s*=\s*NO",
  176. "_INSTALL_REDISTRIBUTE\s*=\s*YES",
  177. "_INSTALL_STAGING\s*=\s*NO",
  178. "_INSTALL_TARGET\s*=\s*YES"])))
  179. END_CONDITIONAL = re.compile("^\s*(endif)")
  180. START_CONDITIONAL = re.compile("^\s*(ifeq|ifneq)")
  181. def before(self):
  182. self.conditional = 0
  183. def check_line(self, lineno, text):
  184. if self.START_CONDITIONAL.search(text):
  185. self.conditional += 1
  186. return
  187. if self.END_CONDITIONAL.search(text):
  188. self.conditional -= 1
  189. return
  190. # allow non-default conditionally overridden by default
  191. if self.conditional > 0:
  192. return
  193. if self.DEFAULT_GENERIC_FLAG.search(text):
  194. return ["{}:{}: useless default value ({}#"
  195. "_infrastructure_for_packages_with_specific_build_systems)"
  196. .format(self.filename, lineno, self.url_to_manual),
  197. text]
  198. if self.DEFAULT_AUTOTOOLS_FLAG.search(text) and not text.lstrip().startswith("HOST_"):
  199. return ["{}:{}: useless default value "
  200. "({}#_infrastructure_for_autotools_based_packages)"
  201. .format(self.filename, lineno, self.url_to_manual),
  202. text]