7ca9fc3170
After some discussion, we found out that "tools" has the four first letters identical to the "toolchain" subfolder, which makes it a bit unpractical with tab-completion. So, this commit renames "tools" to "utils", which is more tab-completion-friendly. This has been discussed with Arnout and Yann. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
224 lines
7.5 KiB
Python
224 lines
7.5 KiB
Python
# 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]
|