lib_mk.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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 checkpackagelib.base import _CheckFunction
  8. from checkpackagelib.lib import ConsecutiveEmptyLines # noqa: F401
  9. from checkpackagelib.lib import EmptyLastLine # noqa: F401
  10. from checkpackagelib.lib import NewlineAtEof # noqa: F401
  11. from checkpackagelib.lib import TrailingSpace # noqa: F401
  12. from checkpackagelib.lib import Utf8Characters # noqa: F401
  13. # used in more than one check
  14. start_conditional = ["ifdef", "ifeq", "ifndef", "ifneq"]
  15. end_conditional = ["endif"]
  16. class Indent(_CheckFunction):
  17. COMMENT = re.compile("^\s*#")
  18. CONDITIONAL = re.compile("^\s*({})\s".format("|".join(start_conditional + end_conditional)))
  19. ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
  20. END_DEFINE = re.compile("^\s*endef\s")
  21. MAKEFILE_TARGET = re.compile("^[^# \t]+:\s")
  22. START_DEFINE = re.compile("^\s*define\s")
  23. def before(self):
  24. self.define = False
  25. self.backslash = False
  26. self.makefile_target = False
  27. def check_line(self, lineno, text):
  28. if self.START_DEFINE.search(text):
  29. self.define = True
  30. return
  31. if self.END_DEFINE.search(text):
  32. self.define = False
  33. return
  34. expect_tabs = False
  35. if self.define or self.backslash or self.makefile_target:
  36. expect_tabs = True
  37. if self.CONDITIONAL.search(text):
  38. expect_tabs = False
  39. # calculate for next line
  40. if self.ENDS_WITH_BACKSLASH.search(text):
  41. self.backslash = True
  42. else:
  43. self.backslash = False
  44. if self.MAKEFILE_TARGET.search(text):
  45. self.makefile_target = True
  46. return
  47. if text.strip() == "":
  48. self.makefile_target = False
  49. return
  50. # comment can be indented or not inside define ... endef, so ignore it
  51. if self.define and self.COMMENT.search(text):
  52. return
  53. if expect_tabs:
  54. if not text.startswith("\t"):
  55. return ["{}:{}: expected indent with tabs"
  56. .format(self.filename, lineno),
  57. text]
  58. else:
  59. if text.startswith("\t"):
  60. return ["{}:{}: unexpected indent with tabs"
  61. .format(self.filename, lineno),
  62. text]
  63. class OverriddenVariable(_CheckFunction):
  64. CONCATENATING = re.compile("^([A-Z0-9_]+)\s*(\+|:|)=\s*\$\(\\1\)")
  65. END_CONDITIONAL = re.compile("^\s*({})".format("|".join(end_conditional)))
  66. OVERRIDING_ASSIGNMENTS = [':=', "="]
  67. START_CONDITIONAL = re.compile("^\s*({})".format("|".join(start_conditional)))
  68. VARIABLE = re.compile("^([A-Z0-9_]+)\s*((\+|:|)=)")
  69. USUALLY_OVERRIDDEN = re.compile("^[A-Z0-9_]+({})".format("|".join([
  70. "_ARCH\s*=\s*",
  71. "_CPU\s*=\s*",
  72. "_SITE\s*=\s*",
  73. "_SOURCE\s*=\s*",
  74. "_VERSION\s*=\s*"])))
  75. def before(self):
  76. self.conditional = 0
  77. self.unconditionally_set = []
  78. self.conditionally_set = []
  79. def check_line(self, lineno, text):
  80. if self.START_CONDITIONAL.search(text):
  81. self.conditional += 1
  82. return
  83. if self.END_CONDITIONAL.search(text):
  84. self.conditional -= 1
  85. return
  86. m = self.VARIABLE.search(text)
  87. if m is None:
  88. return
  89. variable, assignment = m.group(1, 2)
  90. if self.conditional == 0:
  91. if variable in self.conditionally_set:
  92. self.unconditionally_set.append(variable)
  93. if assignment in self.OVERRIDING_ASSIGNMENTS:
  94. return ["{}:{}: unconditional override of variable {} previously conditionally set"
  95. .format(self.filename, lineno, variable),
  96. text]
  97. if variable not in self.unconditionally_set:
  98. self.unconditionally_set.append(variable)
  99. return
  100. if assignment in self.OVERRIDING_ASSIGNMENTS:
  101. return ["{}:{}: unconditional override of variable {}"
  102. .format(self.filename, lineno, variable),
  103. text]
  104. else:
  105. if variable not in self.unconditionally_set:
  106. self.conditionally_set.append(variable)
  107. return
  108. if self.CONCATENATING.search(text):
  109. return
  110. if self.USUALLY_OVERRIDDEN.search(text):
  111. return
  112. if assignment in self.OVERRIDING_ASSIGNMENTS:
  113. return ["{}:{}: conditional override of variable {}"
  114. .format(self.filename, lineno, variable),
  115. text]
  116. class PackageHeader(_CheckFunction):
  117. def before(self):
  118. self.skip = False
  119. def check_line(self, lineno, text):
  120. if self.skip or lineno > 6:
  121. return
  122. if lineno in [1, 5]:
  123. if lineno == 1 and text.startswith("include "):
  124. self.skip = True
  125. return
  126. if text.rstrip() != "#" * 80:
  127. return ["{}:{}: should be 80 hashes ({}#writing-rules-mk)"
  128. .format(self.filename, lineno, self.url_to_manual),
  129. text,
  130. "#" * 80]
  131. elif lineno in [2, 4]:
  132. if text.rstrip() != "#":
  133. return ["{}:{}: should be 1 hash ({}#writing-rules-mk)"
  134. .format(self.filename, lineno, self.url_to_manual),
  135. text]
  136. elif lineno == 6:
  137. if text.rstrip() != "":
  138. return ["{}:{}: should be a blank line ({}#writing-rules-mk)"
  139. .format(self.filename, lineno, self.url_to_manual),
  140. text]
  141. class RemoveDefaultPackageSourceVariable(_CheckFunction):
  142. packages_that_may_contain_default_source = ["binutils", "gcc", "gdb"]
  143. PACKAGE_NAME = re.compile("/([^/]+)\.mk")
  144. def before(self):
  145. package = self.PACKAGE_NAME.search(self.filename).group(1)
  146. package_upper = package.replace("-", "_").upper()
  147. self.package = package
  148. self.FIND_SOURCE = re.compile(
  149. "^{}_SOURCE\s*=\s*{}-\$\({}_VERSION\)\.tar\.gz"
  150. .format(package_upper, package, package_upper))
  151. def check_line(self, lineno, text):
  152. if self.FIND_SOURCE.search(text):
  153. if self.package in self.packages_that_may_contain_default_source:
  154. return
  155. return ["{}:{}: remove default value of _SOURCE variable "
  156. "({}#generic-package-reference)"
  157. .format(self.filename, lineno, self.url_to_manual),
  158. text]
  159. class SpaceBeforeBackslash(_CheckFunction):
  160. TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH = re.compile(r"^.*( |\t ?)\\$")
  161. def check_line(self, lineno, text):
  162. if self.TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH.match(text.rstrip()):
  163. return ["{}:{}: use only one space before backslash"
  164. .format(self.filename, lineno),
  165. text]
  166. class TrailingBackslash(_CheckFunction):
  167. ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
  168. def before(self):
  169. self.backslash = False
  170. def check_line(self, lineno, text):
  171. last_line_ends_in_backslash = self.backslash
  172. # calculate for next line
  173. if self.ENDS_WITH_BACKSLASH.search(text):
  174. self.backslash = True
  175. self.lastline = text
  176. return
  177. self.backslash = False
  178. if last_line_ends_in_backslash and text.strip() == "":
  179. return ["{}:{}: remove trailing backslash"
  180. .format(self.filename, lineno - 1),
  181. self.lastline]
  182. class TypoInPackageVariable(_CheckFunction):
  183. ALLOWED = re.compile("|".join([
  184. "ACLOCAL_DIR",
  185. "ACLOCAL_HOST_DIR",
  186. "BR_CCACHE_INITIAL_SETUP",
  187. "BR_LIBC",
  188. "BR_NO_CHECK_HASH_FOR",
  189. "LINUX_EXTENSIONS",
  190. "LINUX_POST_PATCH_HOOKS",
  191. "LINUX_TOOLS",
  192. "LUA_RUN",
  193. "MKFS_JFFS2",
  194. "MKIMAGE_ARCH",
  195. "PACKAGES_PERMISSIONS_TABLE",
  196. "PKG_CONFIG_HOST_BINARY",
  197. "SUMTOOL",
  198. "TARGET_FINALIZE_HOOKS",
  199. "TARGETS_ROOTFS",
  200. "XTENSA_CORE_NAME"]))
  201. PACKAGE_NAME = re.compile("/([^/]+)\.mk")
  202. VARIABLE = re.compile("^([A-Z0-9_]+_[A-Z0-9_]+)\s*(\+|)=")
  203. def before(self):
  204. package = self.PACKAGE_NAME.search(self.filename).group(1)
  205. package = package.replace("-", "_").upper()
  206. # linux tools do not use LINUX_TOOL_ prefix for variables
  207. package = package.replace("LINUX_TOOL_", "")
  208. # linux extensions do not use LINUX_EXT_ prefix for variables
  209. package = package.replace("LINUX_EXT_", "")
  210. self.package = package
  211. self.REGEX = re.compile("^(HOST_|ROOTFS_)?({}_[A-Z0-9_]+)".format(package))
  212. self.FIND_VIRTUAL = re.compile(
  213. "^{}_PROVIDES\s*(\+|)=\s*(.*)".format(package))
  214. self.virtual = []
  215. def check_line(self, lineno, text):
  216. m = self.VARIABLE.search(text)
  217. if m is None:
  218. return
  219. variable = m.group(1)
  220. # allow to set variables for virtual package this package provides
  221. v = self.FIND_VIRTUAL.search(text)
  222. if v:
  223. self.virtual += v.group(2).upper().split()
  224. return
  225. for virtual in self.virtual:
  226. if variable.startswith("{}_".format(virtual)):
  227. return
  228. if self.ALLOWED.match(variable):
  229. return
  230. if self.REGEX.search(text) is None:
  231. return ["{}:{}: possible typo: {} -> *{}*"
  232. .format(self.filename, lineno, variable, self.package),
  233. text]
  234. class UselessFlag(_CheckFunction):
  235. DEFAULT_AUTOTOOLS_FLAG = re.compile("^.*{}".format("|".join([
  236. "_AUTORECONF\s*=\s*NO",
  237. "_LIBTOOL_PATCH\s*=\s*YES"])))
  238. DEFAULT_GENERIC_FLAG = re.compile("^.*{}".format("|".join([
  239. "_INSTALL_IMAGES\s*=\s*NO",
  240. "_INSTALL_REDISTRIBUTE\s*=\s*YES",
  241. "_INSTALL_STAGING\s*=\s*NO",
  242. "_INSTALL_TARGET\s*=\s*YES"])))
  243. END_CONDITIONAL = re.compile("^\s*({})".format("|".join(end_conditional)))
  244. START_CONDITIONAL = re.compile("^\s*({})".format("|".join(start_conditional)))
  245. def before(self):
  246. self.conditional = 0
  247. def check_line(self, lineno, text):
  248. if self.START_CONDITIONAL.search(text):
  249. self.conditional += 1
  250. return
  251. if self.END_CONDITIONAL.search(text):
  252. self.conditional -= 1
  253. return
  254. # allow non-default conditionally overridden by default
  255. if self.conditional > 0:
  256. return
  257. if self.DEFAULT_GENERIC_FLAG.search(text):
  258. return ["{}:{}: useless default value ({}#"
  259. "_infrastructure_for_packages_with_specific_build_systems)"
  260. .format(self.filename, lineno, self.url_to_manual),
  261. text]
  262. if self.DEFAULT_AUTOTOOLS_FLAG.search(text) and not text.lstrip().startswith("HOST_"):
  263. return ["{}:{}: useless default value "
  264. "({}#_infrastructure_for_autotools_based_packages)"
  265. .format(self.filename, lineno, self.url_to_manual),
  266. text]
  267. class VariableWithBraces(_CheckFunction):
  268. VARIABLE_WITH_BRACES = re.compile(r"^[^#].*[^$]\${\w+}")
  269. def check_line(self, lineno, text):
  270. if self.VARIABLE_WITH_BRACES.match(text.rstrip()):
  271. return ["{}:{}: use $() to delimit variables, not ${{}}"
  272. .format(self.filename, lineno),
  273. text]