lib_mk.py 9.3 KB

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