123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- # 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 os
- import re
- from checkpackagelib.base import _CheckFunction
- from checkpackagelib.lib import ConsecutiveEmptyLines # noqa: F401
- from checkpackagelib.lib import EmptyLastLine # noqa: F401
- from checkpackagelib.lib import NewlineAtEof # noqa: F401
- from checkpackagelib.lib import TrailingSpace # noqa: F401
- from checkpackagelib.lib import Utf8Characters # noqa: F401
- # used in more than one check
- start_conditional = ["ifdef", "ifeq", "ifndef", "ifneq"]
- end_conditional = ["endif"]
- class Indent(_CheckFunction):
- COMMENT = re.compile(r"^\s*#")
- CONDITIONAL = re.compile(r"^\s*({})\s".format("|".join(start_conditional + end_conditional)))
- ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
- END_DEFINE = re.compile(r"^\s*endef\s")
- MAKEFILE_TARGET = re.compile(r"^[^# \t]+:\s")
- START_DEFINE = re.compile(r"^\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 OverriddenVariable(_CheckFunction):
- CONCATENATING = re.compile(r"^([A-Z0-9_]+)\s*(\+|:|)=\s*\$\(\\1\)")
- END_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(end_conditional)))
- OVERRIDING_ASSIGNMENTS = [':=', "="]
- START_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(start_conditional)))
- VARIABLE = re.compile(r"^([A-Z0-9_]+)\s*((\+|:|)=)")
- USUALLY_OVERRIDDEN = re.compile(r"^[A-Z0-9_]+({})".format("|".join([
- r"_ARCH\s*=\s*",
- r"_CPU\s*=\s*",
- r"_SITE\s*=\s*",
- r"_SOURCE\s*=\s*",
- r"_VERSION\s*=\s*"])))
- def before(self):
- self.conditional = 0
- self.unconditionally_set = []
- self.conditionally_set = []
- 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
- m = self.VARIABLE.search(text)
- if m is None:
- return
- variable, assignment = m.group(1, 2)
- if self.conditional == 0:
- if variable in self.conditionally_set:
- self.unconditionally_set.append(variable)
- if assignment in self.OVERRIDING_ASSIGNMENTS:
- return ["{}:{}: unconditional override of variable {} previously conditionally set"
- .format(self.filename, lineno, variable),
- text]
- if variable not in self.unconditionally_set:
- self.unconditionally_set.append(variable)
- return
- if assignment in self.OVERRIDING_ASSIGNMENTS:
- return ["{}:{}: unconditional override of variable {}"
- .format(self.filename, lineno, variable),
- text]
- else:
- if variable not in self.unconditionally_set:
- self.conditionally_set.append(variable)
- return
- if self.CONCATENATING.search(text):
- return ["{}:{}: immediate assignment to append to variable {}"
- .format(self.filename, lineno, variable),
- text]
- if self.USUALLY_OVERRIDDEN.search(text):
- return
- if assignment in self.OVERRIDING_ASSIGNMENTS:
- return ["{}:{}: conditional override of variable {}"
- .format(self.filename, lineno, variable),
- 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 RemoveDefaultPackageSourceVariable(_CheckFunction):
- packages_that_may_contain_default_source = ["binutils", "gcc", "gdb"]
- def before(self):
- package, _ = os.path.splitext(os.path.basename(self.filename))
- package_upper = package.replace("-", "_").upper()
- self.package = package
- self.FIND_SOURCE = re.compile(
- r"^{}_SOURCE\s*=\s*{}-\$\({}_VERSION\)\.tar\.gz"
- .format(package_upper, package, package_upper))
- def check_line(self, lineno, text):
- if self.FIND_SOURCE.search(text):
- if self.package in self.packages_that_may_contain_default_source:
- return
- return ["{}:{}: remove default value of _SOURCE variable "
- "({}#generic-package-reference)"
- .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(r"|".join([
- "ACLOCAL_DIR",
- "ACLOCAL_HOST_DIR",
- "ACLOCAL_PATH",
- "BR_CCACHE_INITIAL_SETUP",
- "BR_LIBC",
- "BR_NO_CHECK_HASH_FOR",
- "LINUX_EXTENSIONS",
- "LINUX_POST_PATCH_HOOKS",
- "LINUX_TOOLS",
- "LUA_RUN",
- "MKFS_JFFS2",
- "MKIMAGE_ARCH",
- "PACKAGES_PERMISSIONS_TABLE",
- "PKG_CONFIG_HOST_BINARY",
- "SUMTOOL",
- "TARGET_FINALIZE_HOOKS",
- "TARGETS_ROOTFS",
- "XTENSA_CORE_NAME"]))
- VARIABLE = re.compile(r"^([A-Z0-9_]+_[A-Z0-9_]+)\s*(\+|)=")
- def before(self):
- package, _ = os.path.splitext(os.path.basename(self.filename))
- package = package.replace("-", "_").upper()
- # linux tools do not use LINUX_TOOL_ prefix for variables
- package = package.replace("LINUX_TOOL_", "")
- # linux extensions do not use LINUX_EXT_ prefix for variables
- package = package.replace("LINUX_EXT_", "")
- self.package = package
- self.REGEX = re.compile(r"^(HOST_|ROOTFS_)?({}_[A-Z0-9_]+)".format(package))
- self.FIND_VIRTUAL = re.compile(
- r"^{}_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(r"^.*{}".format("|".join([
- r"_AUTORECONF\s*=\s*NO",
- r"_LIBTOOL_PATCH\s*=\s*YES"])))
- DEFAULT_GENERIC_FLAG = re.compile(r"^.*{}".format("|".join([
- r"_INSTALL_IMAGES\s*=\s*NO",
- r"_INSTALL_REDISTRIBUTE\s*=\s*YES",
- r"_INSTALL_STAGING\s*=\s*NO",
- r"_INSTALL_TARGET\s*=\s*YES"])))
- END_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(end_conditional)))
- START_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(start_conditional)))
- 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) and not text.lstrip().startswith("HOST_"):
- return ["{}:{}: useless default value "
- "({}#_infrastructure_for_autotools_based_packages)"
- .format(self.filename, lineno, self.url_to_manual),
- text]
- class VariableWithBraces(_CheckFunction):
- VARIABLE_WITH_BRACES = re.compile(r"^[^#].*[^$]\${\w+}")
- def check_line(self, lineno, text):
- if self.VARIABLE_WITH_BRACES.match(text.rstrip()):
- return ["{}:{}: use $() to delimit variables, not ${{}}"
- .format(self.filename, lineno),
- text]
|