2017-07-01 18:07:00 +02:00
|
|
|
# See utils/checkpackagelib/readme.txt before editing this file.
|
2017-02-19 23:17:17 +01:00
|
|
|
# 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.
|
|
|
|
|
2018-11-04 05:12:05 +01:00
|
|
|
import os
|
2017-02-19 23:17:21 +01:00
|
|
|
import re
|
|
|
|
|
2018-08-11 05:48:27 +02:00
|
|
|
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
|
2019-05-08 19:34:27 +02:00
|
|
|
from checkpackagelib.lib import Utf8Characters # noqa: F401
|
2021-12-26 19:49:16 +01:00
|
|
|
from checkpackagelib.tool import NotExecutable # noqa: F401
|
2017-02-19 23:17:21 +01:00
|
|
|
|
2019-01-27 19:59:42 +01:00
|
|
|
# used in more than one check
|
|
|
|
start_conditional = ["ifdef", "ifeq", "ifndef", "ifneq"]
|
2020-12-09 16:32:18 +01:00
|
|
|
continue_conditional = ["elif", "else"]
|
2019-01-27 19:59:42 +01:00
|
|
|
end_conditional = ["endif"]
|
|
|
|
|
2017-02-19 23:17:21 +01:00
|
|
|
|
2022-07-31 21:35:20 +02:00
|
|
|
class DoNotInstallToHostdirUsr(_CheckFunction):
|
|
|
|
INSTALL_TO_HOSTDIR_USR = re.compile(r"^[^#].*\$\(HOST_DIR\)/usr")
|
|
|
|
|
|
|
|
def check_line(self, lineno, text):
|
|
|
|
if self.INSTALL_TO_HOSTDIR_USR.match(text.rstrip()):
|
|
|
|
return ["{}:{}: install files to $(HOST_DIR)/ instead of $(HOST_DIR)/usr/"
|
|
|
|
.format(self.filename, lineno),
|
|
|
|
text]
|
|
|
|
|
|
|
|
|
2022-11-27 14:07:26 +01:00
|
|
|
class Ifdef(_CheckFunction):
|
|
|
|
IFDEF = re.compile(r"^\s*(else\s+|)(ifdef|ifndef)\s")
|
|
|
|
|
|
|
|
def check_line(self, lineno, text):
|
|
|
|
m = self.IFDEF.search(text)
|
|
|
|
if m is None:
|
|
|
|
return
|
|
|
|
word = m.group(2)
|
|
|
|
if word == 'ifdef':
|
|
|
|
return ["{}:{}: use ifeq ($(SYMBOL),y) instead of ifdef SYMBOL"
|
|
|
|
.format(self.filename, lineno),
|
|
|
|
text]
|
|
|
|
else:
|
|
|
|
return ["{}:{}: use ifneq ($(SYMBOL),y) instead of ifndef SYMBOL"
|
|
|
|
.format(self.filename, lineno),
|
|
|
|
text]
|
|
|
|
|
|
|
|
|
2017-02-19 23:17:21 +01:00
|
|
|
class Indent(_CheckFunction):
|
2020-08-12 16:28:59 +02:00
|
|
|
COMMENT = re.compile(r"^\s*#")
|
2020-12-09 16:32:18 +01:00
|
|
|
CONDITIONAL = re.compile(r"^\s*({})\s".format("|".join(start_conditional + end_conditional + continue_conditional)))
|
2017-02-19 23:17:21 +01:00
|
|
|
ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$")
|
2020-08-12 16:28:59 +02:00
|
|
|
END_DEFINE = re.compile(r"^\s*endef\s")
|
|
|
|
MAKEFILE_TARGET = re.compile(r"^[^# \t]+:\s")
|
|
|
|
START_DEFINE = re.compile(r"^\s*define\s")
|
2017-02-19 23:17:21 +01:00
|
|
|
|
|
|
|
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
|
2020-12-09 16:32:18 +01:00
|
|
|
if not self.backslash and self.CONDITIONAL.search(text):
|
2017-02-19 23:17:21 +01:00
|
|
|
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]
|
|
|
|
|
|
|
|
|
2019-01-27 19:59:43 +01:00
|
|
|
class OverriddenVariable(_CheckFunction):
|
utils/checkpackagelib/lib_mk.py: fix check for overridden variable
Currently this .mk snippet results in unexpected behavior from
check-package:
|VAR_1 = VALUE1
|ifeq (condition)
|VAR_1 := $(VAR_1), VALUE2
|endif
Fix commit "163f160a8e utils/{check-package, checkpackagelib}:
consistently use raw strings for re.compile" that ended up doing this:
- CONCATENATING = re.compile("^([A-Z0-9_]+)\s*(\+|:|)=\s*\$\(\\1\)")
+ CONCATENATING = re.compile(r"^([A-Z0-9_]+)\s*(\+|:|)=\s*\$\(\\1\)")
But raw strings do not expect escaping when referencing \1 and the
pattern ends up searching for a raw '\\1' instead of an occurrence of
the first pattern inside parenthesis.
|$ python3
|Python 3.8.10 (default, Sep 28 2021, 16:10:42)
|[GCC 9.3.0] on linux
|Type "help", "copyright", "credits" or "license" for more information.
|>>> import re
|>>> p1 = re.compile('(foo)bar\\1')
|>>> p2 = re.compile(r'(foo)bar\\1')
|>>> p3 = re.compile(r'(foo)bar\1')
|>>> s1 = 'foobarfoo'
|>>> s2 = 'foobar\\1'
|>>> print(p1.search(s1))
|<re.Match object; span=(0, 9), match='foobarfoo'>
|>>> print(p2.search(s1))
|None
|>>> print(p3.search(s1))
|<re.Match object; span=(0, 9), match='foobarfoo'>
|>>> print(p1.search(s2))
|None
|>>> print(p2.search(s2))
|<re.Match object; span=(0, 8), match='foobar\\1'>
|>>> print(p3.search(s2))
|None
|>>>
So use '\1' instead of '\\1' in the raw string.
Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com>
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: Titouan Christophe <titouan.christophe@railnova.eu>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
2021-11-16 00:53:36 +01:00
|
|
|
CONCATENATING = re.compile(r"^([A-Z0-9_]+)\s*(\+|:|)=\s*\$\(\1\)")
|
2020-08-12 16:28:59 +02:00
|
|
|
END_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(end_conditional)))
|
2019-01-27 19:59:43 +01:00
|
|
|
OVERRIDING_ASSIGNMENTS = [':=', "="]
|
2020-08-12 16:28:59 +02:00
|
|
|
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*"])))
|
2021-11-10 18:52:18 +01:00
|
|
|
FORBIDDEN_OVERRIDDEN = re.compile(r"^[A-Z0-9_]+({})".format("|".join([
|
2022-02-23 21:36:31 +01:00
|
|
|
r"_CONF_OPTS\s*=\s*",
|
2021-11-10 18:52:18 +01:00
|
|
|
r"_DEPENDENCIES\s*=\s*"])))
|
2019-01-27 19:59:43 +01:00
|
|
|
|
|
|
|
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:
|
2021-11-10 18:52:18 +01:00
|
|
|
if self.FORBIDDEN_OVERRIDDEN.search(text):
|
|
|
|
return ["{}:{}: conditional override of variable {}"
|
|
|
|
.format(self.filename, lineno, variable),
|
|
|
|
text]
|
2019-01-27 19:59:43 +01:00
|
|
|
if variable not in self.unconditionally_set:
|
|
|
|
self.conditionally_set.append(variable)
|
|
|
|
return
|
|
|
|
if self.CONCATENATING.search(text):
|
2019-10-26 10:46:01 +02:00
|
|
|
return ["{}:{}: immediate assignment to append to variable {}"
|
|
|
|
.format(self.filename, lineno, variable),
|
|
|
|
text]
|
2019-01-27 19:59:43 +01:00
|
|
|
if self.USUALLY_OVERRIDDEN.search(text):
|
|
|
|
return
|
|
|
|
if assignment in self.OVERRIDING_ASSIGNMENTS:
|
|
|
|
return ["{}:{}: conditional override of variable {}"
|
|
|
|
.format(self.filename, lineno, variable),
|
|
|
|
text]
|
|
|
|
|
|
|
|
|
2017-02-19 23:17:21 +01:00
|
|
|
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]
|
|
|
|
|
|
|
|
|
2018-01-13 22:39:43 +01:00
|
|
|
class RemoveDefaultPackageSourceVariable(_CheckFunction):
|
|
|
|
packages_that_may_contain_default_source = ["binutils", "gcc", "gdb"]
|
|
|
|
|
|
|
|
def before(self):
|
2018-11-04 05:12:05 +01:00
|
|
|
package, _ = os.path.splitext(os.path.basename(self.filename))
|
2018-01-13 22:39:43 +01:00
|
|
|
package_upper = package.replace("-", "_").upper()
|
|
|
|
self.package = package
|
|
|
|
self.FIND_SOURCE = re.compile(
|
2020-08-12 16:28:59 +02:00
|
|
|
r"^{}_SOURCE\s*=\s*{}-\$\({}_VERSION\)\.tar\.gz"
|
2018-01-13 22:39:43 +01:00
|
|
|
.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]
|
|
|
|
|
|
|
|
|
2017-02-19 23:17:21 +01:00
|
|
|
class SpaceBeforeBackslash(_CheckFunction):
|
2018-03-07 10:48:12 +01:00
|
|
|
TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH = re.compile(r"^.*( |\t ?)\\$")
|
2017-02-19 23:17:21 +01:00
|
|
|
|
|
|
|
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]
|
|
|
|
|
|
|
|
|
2017-02-19 23:17:24 +01:00
|
|
|
class TypoInPackageVariable(_CheckFunction):
|
2020-08-12 16:28:59 +02:00
|
|
|
ALLOWED = re.compile(r"|".join([
|
2017-02-19 23:17:24 +01:00
|
|
|
"ACLOCAL_DIR",
|
|
|
|
"ACLOCAL_HOST_DIR",
|
2020-02-04 17:15:19 +01:00
|
|
|
"ACLOCAL_PATH",
|
2017-02-19 23:17:24 +01:00
|
|
|
"BR_CCACHE_INITIAL_SETUP",
|
2018-04-01 07:08:29 +02:00
|
|
|
"BR_LIBC",
|
2017-02-19 23:17:24 +01:00
|
|
|
"BR_NO_CHECK_HASH_FOR",
|
2021-11-29 07:16:47 +01:00
|
|
|
"GCC_TARGET",
|
2018-04-01 07:08:25 +02:00
|
|
|
"LINUX_EXTENSIONS",
|
2017-02-19 23:17:24 +01:00
|
|
|
"LINUX_POST_PATCH_HOOKS",
|
|
|
|
"LINUX_TOOLS",
|
|
|
|
"LUA_RUN",
|
|
|
|
"MKFS_JFFS2",
|
|
|
|
"MKIMAGE_ARCH",
|
2018-04-01 07:08:21 +02:00
|
|
|
"PACKAGES_PERMISSIONS_TABLE",
|
2017-02-19 23:17:24 +01:00
|
|
|
"PKG_CONFIG_HOST_BINARY",
|
2018-04-01 07:08:21 +02:00
|
|
|
"SUMTOOL",
|
2017-02-19 23:17:24 +01:00
|
|
|
"TARGET_FINALIZE_HOOKS",
|
2018-04-01 07:08:21 +02:00
|
|
|
"TARGETS_ROOTFS",
|
2017-02-19 23:17:24 +01:00
|
|
|
"XTENSA_CORE_NAME"]))
|
2021-11-29 07:16:47 +01:00
|
|
|
VARIABLE = re.compile(r"^(define\s+)?([A-Z0-9_]+_[A-Z0-9_]+)")
|
2017-02-19 23:17:24 +01:00
|
|
|
|
|
|
|
def before(self):
|
2018-11-04 05:12:05 +01:00
|
|
|
package, _ = os.path.splitext(os.path.basename(self.filename))
|
2017-02-19 23:17:24 +01:00
|
|
|
package = package.replace("-", "_").upper()
|
|
|
|
# linux tools do not use LINUX_TOOL_ prefix for variables
|
|
|
|
package = package.replace("LINUX_TOOL_", "")
|
2018-04-01 07:08:25 +02:00
|
|
|
# linux extensions do not use LINUX_EXT_ prefix for variables
|
|
|
|
package = package.replace("LINUX_EXT_", "")
|
2017-02-19 23:17:24 +01:00
|
|
|
self.package = package
|
2021-11-29 07:16:47 +01:00
|
|
|
self.REGEX = re.compile(r"(HOST_|ROOTFS_)?({}_[A-Z0-9_]+)".format(package))
|
2017-02-19 23:17:24 +01:00
|
|
|
self.FIND_VIRTUAL = re.compile(
|
2020-08-12 16:28:59 +02:00
|
|
|
r"^{}_PROVIDES\s*(\+|)=\s*(.*)".format(package))
|
2017-02-19 23:17:24 +01:00
|
|
|
self.virtual = []
|
|
|
|
|
|
|
|
def check_line(self, lineno, text):
|
|
|
|
m = self.VARIABLE.search(text)
|
|
|
|
if m is None:
|
|
|
|
return
|
|
|
|
|
2021-11-29 07:16:47 +01:00
|
|
|
variable = m.group(2)
|
2017-02-19 23:17:24 +01:00
|
|
|
|
|
|
|
# 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]
|
|
|
|
|
|
|
|
|
2017-02-19 23:17:21 +01:00
|
|
|
class UselessFlag(_CheckFunction):
|
2020-08-12 16:28:59 +02:00
|
|
|
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)))
|
2017-02-19 23:17:21 +01:00
|
|
|
|
|
|
|
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]
|
|
|
|
|
2017-12-02 05:28:24 +01:00
|
|
|
if self.DEFAULT_AUTOTOOLS_FLAG.search(text) and not text.lstrip().startswith("HOST_"):
|
2017-02-19 23:17:21 +01:00
|
|
|
return ["{}:{}: useless default value "
|
|
|
|
"({}#_infrastructure_for_autotools_based_packages)"
|
|
|
|
.format(self.filename, lineno, self.url_to_manual),
|
|
|
|
text]
|
2018-07-09 03:56:47 +02:00
|
|
|
|
|
|
|
|
|
|
|
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]
|
2024-02-10 22:24:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
class CPEVariables(_CheckFunction):
|
|
|
|
"""
|
|
|
|
Check that the values for the CPE variables are not the default.
|
|
|
|
- CPE_ID_* variables must not be set to their default
|
|
|
|
- CPE_ID_VALID must not be set if a non-default CPE_ID variable is set
|
|
|
|
"""
|
|
|
|
def before(self):
|
|
|
|
pkg, _ = os.path.splitext(os.path.basename(self.filename))
|
|
|
|
self.CPE_fields_defaults = {
|
|
|
|
"VALID": "NO",
|
|
|
|
"PREFIX": "cpe:2.3:a",
|
|
|
|
"VENDOR": f"{pkg}_project",
|
|
|
|
"PRODUCT": pkg,
|
|
|
|
"VERSION": None,
|
|
|
|
"UPDATE": "*",
|
|
|
|
}
|
|
|
|
self.valid = None
|
|
|
|
self.non_defaults = 0
|
|
|
|
self.CPE_FIELDS_RE = re.compile(
|
|
|
|
r"^\s*(.+_CPE_ID_({}))\s*=\s*(.+)$"
|
|
|
|
.format("|".join(self.CPE_fields_defaults)),
|
|
|
|
)
|
|
|
|
self.VERSION_RE = re.compile(
|
|
|
|
rf"^(HOST_)?{pkg.upper().replace('-', '_')}_VERSION\s*=\s*(.+)$",
|
|
|
|
)
|
|
|
|
self.COMMENT_RE = re.compile(r"^\s*#.*")
|
|
|
|
|
|
|
|
def check_line(self, lineno, text):
|
|
|
|
text = self.COMMENT_RE.sub('', text.rstrip())
|
|
|
|
|
|
|
|
# WARNING! The VERSION_RE can _also_ match the same lines as CPE_FIELDS_RE,
|
|
|
|
# but not the other way around. So we must first check for CPE_FIELDS_RE,
|
|
|
|
# and if not matched, then and only then check for VERSION_RE.
|
|
|
|
match = self.CPE_FIELDS_RE.match(text)
|
|
|
|
if match:
|
|
|
|
var, field, val = match.groups()
|
|
|
|
return self._check_field(lineno, text, field, var, val)
|
|
|
|
|
|
|
|
match = self.VERSION_RE.match(text)
|
|
|
|
if match:
|
|
|
|
self.CPE_fields_defaults["VERSION"] = match.groups()[1]
|
|
|
|
|
|
|
|
def after(self):
|
|
|
|
# "VALID" counts in the non-defaults; so when "VALID" is present,
|
|
|
|
# 1 non-default means only "VALID" is present, so that's OK.
|
|
|
|
if self.valid and self.non_defaults > 1:
|
|
|
|
return ["{}:{}: 'YES' is implied when a non-default CPE_ID field is specified: {} ({}#cpe-id)".format(
|
|
|
|
self.filename,
|
|
|
|
self.valid["lineno"],
|
|
|
|
self.valid["text"],
|
|
|
|
self.url_to_manual,
|
|
|
|
)]
|
|
|
|
|
|
|
|
def _check_field(self, lineno, text, field, var, val):
|
|
|
|
if field == "VERSION" and self.CPE_fields_defaults[field] is None:
|
|
|
|
return ["{}:{}: expecting package version to be set before CPE_ID_VERSION".format(
|
|
|
|
self.filename,
|
|
|
|
lineno,
|
|
|
|
)]
|
|
|
|
if val == self.CPE_fields_defaults[field]:
|
|
|
|
return ["{}:{}: '{}' is the default value for {} ({}#cpe-id)".format(
|
|
|
|
self.filename,
|
|
|
|
lineno,
|
|
|
|
val,
|
|
|
|
var,
|
|
|
|
self.url_to_manual,
|
|
|
|
)]
|
|
|
|
else:
|
|
|
|
if field == "VALID":
|
|
|
|
self.valid = {"lineno": lineno, "text": text}
|
|
|
|
self.non_defaults += 1
|