lib_mk.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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 DoNotInstallToHostdirUsr(_CheckFunction):
  20. INSTALL_TO_HOSTDIR_USR = re.compile(r"^[^#].*\$\(HOST_DIR\)/usr")
  21. def check_line(self, lineno, text):
  22. if self.INSTALL_TO_HOSTDIR_USR.match(text.rstrip()):
  23. return ["{}:{}: install files to $(HOST_DIR)/ instead of $(HOST_DIR)/usr/"
  24. .format(self.filename, lineno),
  25. text]
  26. class Ifdef(_CheckFunction):
  27. IFDEF = re.compile(r"^\s*(else\s+|)(ifdef|ifndef)\s")
  28. def check_line(self, lineno, text):
  29. m = self.IFDEF.search(text)
  30. if m is None:
  31. return
  32. word = m.group(2)
  33. if word == 'ifdef':
  34. return ["{}:{}: use ifeq ($(SYMBOL),y) instead of ifdef SYMBOL"
  35. .format(self.filename, lineno),
  36. text]
  37. else:
  38. return ["{}:{}: use ifneq ($(SYMBOL),y) instead of ifndef SYMBOL"
  39. .format(self.filename, lineno),
  40. text]
  41. class Indent(_CheckFunction):
  42. COMMENT = re.compile(r"^\s*#")
  43. CONDITIONAL = re.compile(r"^\s*({})\s".format("|".join(start_conditional + end_conditional + continue_conditional)))
  44. ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
  45. END_DEFINE = re.compile(r"^\s*endef\s")
  46. MAKEFILE_TARGET = re.compile(r"^[^# \t]+:\s")
  47. START_DEFINE = re.compile(r"^\s*define\s")
  48. def before(self):
  49. self.define = False
  50. self.backslash = False
  51. self.makefile_target = False
  52. def check_line(self, lineno, text):
  53. if self.START_DEFINE.search(text):
  54. self.define = True
  55. return
  56. if self.END_DEFINE.search(text):
  57. self.define = False
  58. return
  59. expect_tabs = False
  60. if self.define or self.backslash or self.makefile_target:
  61. expect_tabs = True
  62. if not self.backslash and self.CONDITIONAL.search(text):
  63. expect_tabs = False
  64. # calculate for next line
  65. if self.ENDS_WITH_BACKSLASH.search(text):
  66. self.backslash = True
  67. else:
  68. self.backslash = False
  69. if self.MAKEFILE_TARGET.search(text):
  70. self.makefile_target = True
  71. return
  72. if text.strip() == "":
  73. self.makefile_target = False
  74. return
  75. # comment can be indented or not inside define ... endef, so ignore it
  76. if self.define and self.COMMENT.search(text):
  77. return
  78. if expect_tabs:
  79. if not text.startswith("\t"):
  80. return ["{}:{}: expected indent with tabs"
  81. .format(self.filename, lineno),
  82. text]
  83. else:
  84. if text.startswith("\t"):
  85. return ["{}:{}: unexpected indent with tabs"
  86. .format(self.filename, lineno),
  87. text]
  88. class OverriddenVariable(_CheckFunction):
  89. CONCATENATING = re.compile(r"^([A-Z0-9_]+)\s*(\+|:|)=\s*\$\(\1\)")
  90. END_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(end_conditional)))
  91. OVERRIDING_ASSIGNMENTS = [':=', "="]
  92. START_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(start_conditional)))
  93. VARIABLE = re.compile(r"^([A-Z0-9_]+)\s*((\+|:|)=)")
  94. USUALLY_OVERRIDDEN = re.compile(r"^[A-Z0-9_]+({})".format("|".join([
  95. r"_ARCH\s*=\s*",
  96. r"_CPU\s*=\s*",
  97. r"_SITE\s*=\s*",
  98. r"_SOURCE\s*=\s*",
  99. r"_VERSION\s*=\s*"])))
  100. FORBIDDEN_OVERRIDDEN = re.compile(r"^[A-Z0-9_]+({})".format("|".join([
  101. r"_CONF_OPTS\s*=\s*",
  102. r"_DEPENDENCIES\s*=\s*"])))
  103. def before(self):
  104. self.conditional = 0
  105. self.unconditionally_set = []
  106. self.conditionally_set = []
  107. def check_line(self, lineno, text):
  108. if self.START_CONDITIONAL.search(text):
  109. self.conditional += 1
  110. return
  111. if self.END_CONDITIONAL.search(text):
  112. self.conditional -= 1
  113. return
  114. m = self.VARIABLE.search(text)
  115. if m is None:
  116. return
  117. variable, assignment = m.group(1, 2)
  118. if self.conditional == 0:
  119. if variable in self.conditionally_set:
  120. self.unconditionally_set.append(variable)
  121. if assignment in self.OVERRIDING_ASSIGNMENTS:
  122. return ["{}:{}: unconditional override of variable {} previously conditionally set"
  123. .format(self.filename, lineno, variable),
  124. text]
  125. if variable not in self.unconditionally_set:
  126. self.unconditionally_set.append(variable)
  127. return
  128. if assignment in self.OVERRIDING_ASSIGNMENTS:
  129. return ["{}:{}: unconditional override of variable {}"
  130. .format(self.filename, lineno, variable),
  131. text]
  132. else:
  133. if self.FORBIDDEN_OVERRIDDEN.search(text):
  134. return ["{}:{}: conditional override of variable {}"
  135. .format(self.filename, lineno, variable),
  136. text]
  137. if variable not in self.unconditionally_set:
  138. self.conditionally_set.append(variable)
  139. return
  140. if self.CONCATENATING.search(text):
  141. return ["{}:{}: immediate assignment to append to variable {}"
  142. .format(self.filename, lineno, variable),
  143. text]
  144. if self.USUALLY_OVERRIDDEN.search(text):
  145. return
  146. if assignment in self.OVERRIDING_ASSIGNMENTS:
  147. return ["{}:{}: conditional override of variable {}"
  148. .format(self.filename, lineno, variable),
  149. text]
  150. class PackageHeader(_CheckFunction):
  151. def before(self):
  152. self.skip = False
  153. def check_line(self, lineno, text):
  154. if self.skip or lineno > 6:
  155. return
  156. if lineno in [1, 5]:
  157. if lineno == 1 and text.startswith("include "):
  158. self.skip = True
  159. return
  160. if text.rstrip() != "#" * 80:
  161. return ["{}:{}: should be 80 hashes ({}#writing-rules-mk)"
  162. .format(self.filename, lineno, self.url_to_manual),
  163. text,
  164. "#" * 80]
  165. elif lineno in [2, 4]:
  166. if text.rstrip() != "#":
  167. return ["{}:{}: should be 1 hash ({}#writing-rules-mk)"
  168. .format(self.filename, lineno, self.url_to_manual),
  169. text]
  170. elif lineno == 6:
  171. if text.rstrip() != "":
  172. return ["{}:{}: should be a blank line ({}#writing-rules-mk)"
  173. .format(self.filename, lineno, self.url_to_manual),
  174. text]
  175. class RemoveDefaultPackageSourceVariable(_CheckFunction):
  176. packages_that_may_contain_default_source = ["binutils", "gcc", "gdb"]
  177. def before(self):
  178. package, _ = os.path.splitext(os.path.basename(self.filename))
  179. package_upper = package.replace("-", "_").upper()
  180. self.package = package
  181. self.FIND_SOURCE = re.compile(
  182. r"^{}_SOURCE\s*=\s*{}-\$\({}_VERSION\)\.tar\.gz"
  183. .format(package_upper, package, package_upper))
  184. def check_line(self, lineno, text):
  185. if self.FIND_SOURCE.search(text):
  186. if self.package in self.packages_that_may_contain_default_source:
  187. return
  188. return ["{}:{}: remove default value of _SOURCE variable "
  189. "({}#generic-package-reference)"
  190. .format(self.filename, lineno, self.url_to_manual),
  191. text]
  192. class SpaceBeforeBackslash(_CheckFunction):
  193. TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH = re.compile(r"^.*( |\t ?)\\$")
  194. def check_line(self, lineno, text):
  195. if self.TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH.match(text.rstrip()):
  196. return ["{}:{}: use only one space before backslash"
  197. .format(self.filename, lineno),
  198. text]
  199. class TrailingBackslash(_CheckFunction):
  200. ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
  201. def before(self):
  202. self.backslash = False
  203. def check_line(self, lineno, text):
  204. last_line_ends_in_backslash = self.backslash
  205. # calculate for next line
  206. if self.ENDS_WITH_BACKSLASH.search(text):
  207. self.backslash = True
  208. self.lastline = text
  209. return
  210. self.backslash = False
  211. if last_line_ends_in_backslash and text.strip() == "":
  212. return ["{}:{}: remove trailing backslash"
  213. .format(self.filename, lineno - 1),
  214. self.lastline]
  215. class TypoInPackageVariable(_CheckFunction):
  216. ALLOWED = re.compile(r"|".join([
  217. "ACLOCAL_DIR",
  218. "ACLOCAL_HOST_DIR",
  219. "ACLOCAL_PATH",
  220. "BR_CCACHE_INITIAL_SETUP",
  221. "BR_LIBC",
  222. "BR_NO_CHECK_HASH_FOR",
  223. "GCC_TARGET",
  224. "LINUX_EXTENSIONS",
  225. "LINUX_POST_PATCH_HOOKS",
  226. "LINUX_TOOLS",
  227. "LUA_RUN",
  228. "MKFS_JFFS2",
  229. "MKIMAGE_ARCH",
  230. "PACKAGES_PERMISSIONS_TABLE",
  231. "PKG_CONFIG_HOST_BINARY",
  232. "SUMTOOL",
  233. "TARGET_FINALIZE_HOOKS",
  234. "TARGETS_ROOTFS",
  235. "XTENSA_CORE_NAME"]))
  236. VARIABLE = re.compile(r"^(define\s+)?([A-Z0-9_]+_[A-Z0-9_]+)")
  237. def before(self):
  238. package, _ = os.path.splitext(os.path.basename(self.filename))
  239. package = package.replace("-", "_").upper()
  240. # linux tools do not use LINUX_TOOL_ prefix for variables
  241. package = package.replace("LINUX_TOOL_", "")
  242. # linux extensions do not use LINUX_EXT_ prefix for variables
  243. package = package.replace("LINUX_EXT_", "")
  244. self.package = package
  245. self.REGEX = re.compile(r"(HOST_|ROOTFS_)?({}_[A-Z0-9_]+)".format(package))
  246. self.FIND_VIRTUAL = re.compile(
  247. r"^{}_PROVIDES\s*(\+|)=\s*(.*)".format(package))
  248. self.virtual = []
  249. def check_line(self, lineno, text):
  250. m = self.VARIABLE.search(text)
  251. if m is None:
  252. return
  253. variable = m.group(2)
  254. # allow to set variables for virtual package this package provides
  255. v = self.FIND_VIRTUAL.search(text)
  256. if v:
  257. self.virtual += v.group(2).upper().split()
  258. return
  259. for virtual in self.virtual:
  260. if variable.startswith("{}_".format(virtual)):
  261. return
  262. if self.ALLOWED.match(variable):
  263. return
  264. if self.REGEX.search(text) is None:
  265. return ["{}:{}: possible typo: {} -> *{}*"
  266. .format(self.filename, lineno, variable, self.package),
  267. text]
  268. class UselessFlag(_CheckFunction):
  269. DEFAULT_AUTOTOOLS_FLAG = re.compile(r"^.*{}".format("|".join([
  270. r"_AUTORECONF\s*=\s*NO",
  271. r"_LIBTOOL_PATCH\s*=\s*YES"])))
  272. DEFAULT_GENERIC_FLAG = re.compile(r"^.*{}".format("|".join([
  273. r"_INSTALL_IMAGES\s*=\s*NO",
  274. r"_REDISTRIBUTE\s*=\s*YES",
  275. r"_INSTALL_STAGING\s*=\s*NO",
  276. r"_INSTALL_TARGET\s*=\s*YES"])))
  277. END_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(end_conditional)))
  278. START_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(start_conditional)))
  279. def before(self):
  280. self.conditional = 0
  281. def check_line(self, lineno, text):
  282. if self.START_CONDITIONAL.search(text):
  283. self.conditional += 1
  284. return
  285. if self.END_CONDITIONAL.search(text):
  286. self.conditional -= 1
  287. return
  288. # allow non-default conditionally overridden by default
  289. if self.conditional > 0:
  290. return
  291. if self.DEFAULT_GENERIC_FLAG.search(text):
  292. return ["{}:{}: useless default value ({}#"
  293. "_infrastructure_for_packages_with_specific_build_systems)"
  294. .format(self.filename, lineno, self.url_to_manual),
  295. text]
  296. if self.DEFAULT_AUTOTOOLS_FLAG.search(text) and not text.lstrip().startswith("HOST_"):
  297. return ["{}:{}: useless default value "
  298. "({}#_infrastructure_for_autotools_based_packages)"
  299. .format(self.filename, lineno, self.url_to_manual),
  300. text]
  301. class VariableWithBraces(_CheckFunction):
  302. VARIABLE_WITH_BRACES = re.compile(r"^[^#].*[^$]\${\w+}")
  303. def check_line(self, lineno, text):
  304. if self.VARIABLE_WITH_BRACES.match(text.rstrip()):
  305. return ["{}:{}: use $() to delimit variables, not ${{}}"
  306. .format(self.filename, lineno),
  307. text]
  308. class CPEVariables(_CheckFunction):
  309. """
  310. Check that the values for the CPE variables are not the default.
  311. - CPE_ID_* variables must not be set to their default
  312. - CPE_ID_VALID must not be set if a non-default CPE_ID variable is set
  313. """
  314. def before(self):
  315. pkg, _ = os.path.splitext(os.path.basename(self.filename))
  316. self.CPE_fields_defaults = {
  317. "VALID": "NO",
  318. "PREFIX": "cpe:2.3:a",
  319. "VENDOR": f"{pkg}_project",
  320. "PRODUCT": pkg,
  321. "VERSION": None,
  322. "UPDATE": "*",
  323. }
  324. self.valid = None
  325. self.non_defaults = 0
  326. self.CPE_FIELDS_RE = re.compile(
  327. r"^\s*(.+_CPE_ID_({}))\s*=\s*(.+)$"
  328. .format("|".join(self.CPE_fields_defaults)),
  329. )
  330. self.VERSION_RE = re.compile(
  331. rf"^(HOST_)?{pkg.upper().replace('-', '_')}_VERSION\s*=\s*(.+)$",
  332. )
  333. self.COMMENT_RE = re.compile(r"^\s*#.*")
  334. def check_line(self, lineno, text):
  335. text = self.COMMENT_RE.sub('', text.rstrip())
  336. # WARNING! The VERSION_RE can _also_ match the same lines as CPE_FIELDS_RE,
  337. # but not the other way around. So we must first check for CPE_FIELDS_RE,
  338. # and if not matched, then and only then check for VERSION_RE.
  339. match = self.CPE_FIELDS_RE.match(text)
  340. if match:
  341. var, field, val = match.groups()
  342. return self._check_field(lineno, text, field, var, val)
  343. match = self.VERSION_RE.match(text)
  344. if match:
  345. self.CPE_fields_defaults["VERSION"] = match.groups()[1]
  346. def after(self):
  347. # "VALID" counts in the non-defaults; so when "VALID" is present,
  348. # 1 non-default means only "VALID" is present, so that's OK.
  349. if self.valid and self.non_defaults > 1:
  350. return ["{}:{}: 'YES' is implied when a non-default CPE_ID field is specified: {} ({}#cpe-id)".format(
  351. self.filename,
  352. self.valid["lineno"],
  353. self.valid["text"],
  354. self.url_to_manual,
  355. )]
  356. def _check_field(self, lineno, text, field, var, val):
  357. if field == "VERSION" and self.CPE_fields_defaults[field] is None:
  358. return ["{}:{}: expecting package version to be set before CPE_ID_VERSION".format(
  359. self.filename,
  360. lineno,
  361. )]
  362. if val == self.CPE_fields_defaults[field]:
  363. return ["{}:{}: '{}' is the default value for {} ({}#cpe-id)".format(
  364. self.filename,
  365. lineno,
  366. val,
  367. var,
  368. self.url_to_manual,
  369. )]
  370. else:
  371. if field == "VALID":
  372. self.valid = {"lineno": lineno, "text": text}
  373. self.non_defaults += 1