# See utils/checkpackagelib/readme.txt before editing this file. # There are already dependency checks during the build, so below check # functions don't need to check for things already checked by exploring the # menu options using "make menuconfig" and by running "make" with appropriate # packages enabled. import re from base import _CheckFunction # Notice: ignore 'imported but unused' from pyflakes for check functions. from lib import ConsecutiveEmptyLines from lib import EmptyLastLine from lib import NewlineAtEof from lib import TrailingSpace class Indent(_CheckFunction): COMMENT = re.compile("^\s*#") CONDITIONAL = re.compile("^\s*(ifeq|ifneq|endif)\s") ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$") END_DEFINE = re.compile("^\s*endef\s") MAKEFILE_TARGET = re.compile("^[^# \t]+:\s") START_DEFINE = re.compile("^\s*define\s") def before(self): self.define = False self.backslash = False self.makefile_target = False def check_line(self, lineno, text): if self.START_DEFINE.search(text): self.define = True return if self.END_DEFINE.search(text): self.define = False return expect_tabs = False if self.define or self.backslash or self.makefile_target: expect_tabs = True if self.CONDITIONAL.search(text): expect_tabs = False # calculate for next line if self.ENDS_WITH_BACKSLASH.search(text): self.backslash = True else: self.backslash = False if self.MAKEFILE_TARGET.search(text): self.makefile_target = True return if text.strip() == "": self.makefile_target = False return # comment can be indented or not inside define ... endef, so ignore it if self.define and self.COMMENT.search(text): return if expect_tabs: if not text.startswith("\t"): return ["{}:{}: expected indent with tabs" .format(self.filename, lineno), text] else: if text.startswith("\t"): return ["{}:{}: unexpected indent with tabs" .format(self.filename, lineno), text] class PackageHeader(_CheckFunction): def before(self): self.skip = False def check_line(self, lineno, text): if self.skip or lineno > 6: return if lineno in [1, 5]: if lineno == 1 and text.startswith("include "): self.skip = True return if text.rstrip() != "#" * 80: return ["{}:{}: should be 80 hashes ({}#writing-rules-mk)" .format(self.filename, lineno, self.url_to_manual), text, "#" * 80] elif lineno in [2, 4]: if text.rstrip() != "#": return ["{}:{}: should be 1 hash ({}#writing-rules-mk)" .format(self.filename, lineno, self.url_to_manual), text] elif lineno == 6: if text.rstrip() != "": return ["{}:{}: should be a blank line ({}#writing-rules-mk)" .format(self.filename, lineno, self.url_to_manual), text] class SpaceBeforeBackslash(_CheckFunction): TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH = re.compile(r"^.*( |\t)\\$") def check_line(self, lineno, text): if self.TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH.match(text.rstrip()): return ["{}:{}: use only one space before backslash" .format(self.filename, lineno), text] class TrailingBackslash(_CheckFunction): ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$") def before(self): self.backslash = False def check_line(self, lineno, text): last_line_ends_in_backslash = self.backslash # calculate for next line if self.ENDS_WITH_BACKSLASH.search(text): self.backslash = True self.lastline = text return self.backslash = False if last_line_ends_in_backslash and text.strip() == "": return ["{}:{}: remove trailing backslash" .format(self.filename, lineno - 1), self.lastline] class TypoInPackageVariable(_CheckFunction): ALLOWED = re.compile("|".join([ "ACLOCAL_DIR", "ACLOCAL_HOST_DIR", "BR_CCACHE_INITIAL_SETUP", "BR_NO_CHECK_HASH_FOR", "LINUX_POST_PATCH_HOOKS", "LINUX_TOOLS", "LUA_RUN", "MKFS_JFFS2", "MKIMAGE_ARCH", "PKG_CONFIG_HOST_BINARY", "TARGET_FINALIZE_HOOKS", "XTENSA_CORE_NAME"])) PACKAGE_NAME = re.compile("/([^/]+)\.mk") VARIABLE = re.compile("^([A-Z0-9_]+_[A-Z0-9_]+)\s*(\+|)=") def before(self): package = self.PACKAGE_NAME.search(self.filename).group(1) package = package.replace("-", "_").upper() # linux tools do not use LINUX_TOOL_ prefix for variables package = package.replace("LINUX_TOOL_", "") self.package = package self.REGEX = re.compile("^(HOST_)?({}_[A-Z0-9_]+)".format(package)) self.FIND_VIRTUAL = re.compile( "^{}_PROVIDES\s*(\+|)=\s*(.*)".format(package)) self.virtual = [] def check_line(self, lineno, text): m = self.VARIABLE.search(text) if m is None: return variable = m.group(1) # allow to set variables for virtual package this package provides v = self.FIND_VIRTUAL.search(text) if v: self.virtual += v.group(2).upper().split() return for virtual in self.virtual: if variable.startswith("{}_".format(virtual)): return if self.ALLOWED.match(variable): return if self.REGEX.search(text) is None: return ["{}:{}: possible typo: {} -> *{}*" .format(self.filename, lineno, variable, self.package), text] class UselessFlag(_CheckFunction): DEFAULT_AUTOTOOLS_FLAG = re.compile("^.*{}".format("|".join([ "_AUTORECONF\s*=\s*NO", "_LIBTOOL_PATCH\s*=\s*YES"]))) DEFAULT_GENERIC_FLAG = re.compile("^.*{}".format("|".join([ "_INSTALL_IMAGES\s*=\s*NO", "_INSTALL_REDISTRIBUTE\s*=\s*YES", "_INSTALL_STAGING\s*=\s*NO", "_INSTALL_TARGET\s*=\s*YES"]))) END_CONDITIONAL = re.compile("^\s*(endif)") START_CONDITIONAL = re.compile("^\s*(ifeq|ifneq)") def before(self): self.conditional = 0 def check_line(self, lineno, text): if self.START_CONDITIONAL.search(text): self.conditional += 1 return if self.END_CONDITIONAL.search(text): self.conditional -= 1 return # allow non-default conditionally overridden by default if self.conditional > 0: return if self.DEFAULT_GENERIC_FLAG.search(text): return ["{}:{}: useless default value ({}#" "_infrastructure_for_packages_with_specific_build_systems)" .format(self.filename, lineno, self.url_to_manual), text] if self.DEFAULT_AUTOTOOLS_FLAG.search(text): return ["{}:{}: useless default value " "({}#_infrastructure_for_autotools_based_packages)" .format(self.filename, lineno, self.url_to_manual), text]