2
1

lib_mk.py 12 KB

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