lib_mk.py 13 KB

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