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 7import os 8import re 9 10from checkpackagelib.base import _CheckFunction 11from checkpackagelib.lib import ConsecutiveEmptyLines # noqa: F401 12from checkpackagelib.lib import EmptyLastLine # noqa: F401 13from checkpackagelib.lib import NewlineAtEof # noqa: F401 14from checkpackagelib.lib import TrailingSpace # noqa: F401 15from checkpackagelib.lib import Utf8Characters # noqa: F401 16 17# used in more than one check 18start_conditional = ["ifdef", "ifeq", "ifndef", "ifneq"] 19continue_conditional = ["elif", "else"] 20end_conditional = ["endif"] 21 22 23class Indent(_CheckFunction): 24 COMMENT = re.compile(r"^\s*#") 25 CONDITIONAL = re.compile(r"^\s*({})\s".format("|".join(start_conditional + end_conditional + continue_conditional))) 26 ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$") 27 END_DEFINE = re.compile(r"^\s*endef\s") 28 MAKEFILE_TARGET = re.compile(r"^[^# \t]+:\s") 29 START_DEFINE = re.compile(r"^\s*define\s") 30 31 def before(self): 32 self.define = False 33 self.backslash = False 34 self.makefile_target = False 35 36 def check_line(self, lineno, text): 37 if self.START_DEFINE.search(text): 38 self.define = True 39 return 40 if self.END_DEFINE.search(text): 41 self.define = False 42 return 43 44 expect_tabs = False 45 if self.define or self.backslash or self.makefile_target: 46 expect_tabs = True 47 if not self.backslash and self.CONDITIONAL.search(text): 48 expect_tabs = False 49 50 # calculate for next line 51 if self.ENDS_WITH_BACKSLASH.search(text): 52 self.backslash = True 53 else: 54 self.backslash = False 55 56 if self.MAKEFILE_TARGET.search(text): 57 self.makefile_target = True 58 return 59 if text.strip() == "": 60 self.makefile_target = False 61 return 62 63 # comment can be indented or not inside define ... endef, so ignore it 64 if self.define and self.COMMENT.search(text): 65 return 66 67 if expect_tabs: 68 if not text.startswith("\t"): 69 return ["{}:{}: expected indent with tabs" 70 .format(self.filename, lineno), 71 text] 72 else: 73 if text.startswith("\t"): 74 return ["{}:{}: unexpected indent with tabs" 75 .format(self.filename, lineno), 76 text] 77 78 79class OverriddenVariable(_CheckFunction): 80 CONCATENATING = re.compile(r"^([A-Z0-9_]+)\s*(\+|:|)=\s*\$\(\\1\)") 81 END_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(end_conditional))) 82 OVERRIDING_ASSIGNMENTS = [':=', "="] 83 START_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(start_conditional))) 84 VARIABLE = re.compile(r"^([A-Z0-9_]+)\s*((\+|:|)=)") 85 USUALLY_OVERRIDDEN = re.compile(r"^[A-Z0-9_]+({})".format("|".join([ 86 r"_ARCH\s*=\s*", 87 r"_CPU\s*=\s*", 88 r"_SITE\s*=\s*", 89 r"_SOURCE\s*=\s*", 90 r"_VERSION\s*=\s*"]))) 91 92 def before(self): 93 self.conditional = 0 94 self.unconditionally_set = [] 95 self.conditionally_set = [] 96 97 def check_line(self, lineno, text): 98 if self.START_CONDITIONAL.search(text): 99 self.conditional += 1 100 return 101 if self.END_CONDITIONAL.search(text): 102 self.conditional -= 1 103 return 104 105 m = self.VARIABLE.search(text) 106 if m is None: 107 return 108 variable, assignment = m.group(1, 2) 109 110 if self.conditional == 0: 111 if variable in self.conditionally_set: 112 self.unconditionally_set.append(variable) 113 if assignment in self.OVERRIDING_ASSIGNMENTS: 114 return ["{}:{}: unconditional override of variable {} previously conditionally set" 115 .format(self.filename, lineno, variable), 116 text] 117 118 if variable not in self.unconditionally_set: 119 self.unconditionally_set.append(variable) 120 return 121 if assignment in self.OVERRIDING_ASSIGNMENTS: 122 return ["{}:{}: unconditional override of variable {}" 123 .format(self.filename, lineno, variable), 124 text] 125 else: 126 if variable not in self.unconditionally_set: 127 self.conditionally_set.append(variable) 128 return 129 if self.CONCATENATING.search(text): 130 return ["{}:{}: immediate assignment to append to variable {}" 131 .format(self.filename, lineno, variable), 132 text] 133 if self.USUALLY_OVERRIDDEN.search(text): 134 return 135 if assignment in self.OVERRIDING_ASSIGNMENTS: 136 return ["{}:{}: conditional override of variable {}" 137 .format(self.filename, lineno, variable), 138 text] 139 140 141class PackageHeader(_CheckFunction): 142 def before(self): 143 self.skip = False 144 145 def check_line(self, lineno, text): 146 if self.skip or lineno > 6: 147 return 148 149 if lineno in [1, 5]: 150 if lineno == 1 and text.startswith("include "): 151 self.skip = True 152 return 153 if text.rstrip() != "#" * 80: 154 return ["{}:{}: should be 80 hashes ({}#writing-rules-mk)" 155 .format(self.filename, lineno, self.url_to_manual), 156 text, 157 "#" * 80] 158 elif lineno in [2, 4]: 159 if text.rstrip() != "#": 160 return ["{}:{}: should be 1 hash ({}#writing-rules-mk)" 161 .format(self.filename, lineno, self.url_to_manual), 162 text] 163 elif lineno == 6: 164 if text.rstrip() != "": 165 return ["{}:{}: should be a blank line ({}#writing-rules-mk)" 166 .format(self.filename, lineno, self.url_to_manual), 167 text] 168 169 170class RemoveDefaultPackageSourceVariable(_CheckFunction): 171 packages_that_may_contain_default_source = ["binutils", "gcc", "gdb"] 172 173 def before(self): 174 package, _ = os.path.splitext(os.path.basename(self.filename)) 175 package_upper = package.replace("-", "_").upper() 176 self.package = package 177 self.FIND_SOURCE = re.compile( 178 r"^{}_SOURCE\s*=\s*{}-\$\({}_VERSION\)\.tar\.gz" 179 .format(package_upper, package, package_upper)) 180 181 def check_line(self, lineno, text): 182 if self.FIND_SOURCE.search(text): 183 184 if self.package in self.packages_that_may_contain_default_source: 185 return 186 187 return ["{}:{}: remove default value of _SOURCE variable " 188 "({}#generic-package-reference)" 189 .format(self.filename, lineno, self.url_to_manual), 190 text] 191 192 193class SpaceBeforeBackslash(_CheckFunction): 194 TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH = re.compile(r"^.*( |\t ?)\\$") 195 196 def check_line(self, lineno, text): 197 if self.TAB_OR_MULTIPLE_SPACES_BEFORE_BACKSLASH.match(text.rstrip()): 198 return ["{}:{}: use only one space before backslash" 199 .format(self.filename, lineno), 200 text] 201 202 203class TrailingBackslash(_CheckFunction): 204 ENDS_WITH_BACKSLASH = re.compile(r"^[^#].*\\$") 205 206 def before(self): 207 self.backslash = False 208 209 def check_line(self, lineno, text): 210 last_line_ends_in_backslash = self.backslash 211 212 # calculate for next line 213 if self.ENDS_WITH_BACKSLASH.search(text): 214 self.backslash = True 215 self.lastline = text 216 return 217 self.backslash = False 218 219 if last_line_ends_in_backslash and text.strip() == "": 220 return ["{}:{}: remove trailing backslash" 221 .format(self.filename, lineno - 1), 222 self.lastline] 223 224 225class TypoInPackageVariable(_CheckFunction): 226 ALLOWED = re.compile(r"|".join([ 227 "ACLOCAL_DIR", 228 "ACLOCAL_HOST_DIR", 229 "ACLOCAL_PATH", 230 "BR_CCACHE_INITIAL_SETUP", 231 "BR_LIBC", 232 "BR_NO_CHECK_HASH_FOR", 233 "LINUX_EXTENSIONS", 234 "LINUX_POST_PATCH_HOOKS", 235 "LINUX_TOOLS", 236 "LUA_RUN", 237 "MKFS_JFFS2", 238 "MKIMAGE_ARCH", 239 "PACKAGES_PERMISSIONS_TABLE", 240 "PKG_CONFIG_HOST_BINARY", 241 "SUMTOOL", 242 "TARGET_FINALIZE_HOOKS", 243 "TARGETS_ROOTFS", 244 "XTENSA_CORE_NAME"])) 245 VARIABLE = re.compile(r"^([A-Z0-9_]+_[A-Z0-9_]+)\s*(\+|)=") 246 247 def before(self): 248 package, _ = os.path.splitext(os.path.basename(self.filename)) 249 package = package.replace("-", "_").upper() 250 # linux tools do not use LINUX_TOOL_ prefix for variables 251 package = package.replace("LINUX_TOOL_", "") 252 # linux extensions do not use LINUX_EXT_ prefix for variables 253 package = package.replace("LINUX_EXT_", "") 254 self.package = package 255 self.REGEX = re.compile(r"^(HOST_|ROOTFS_)?({}_[A-Z0-9_]+)".format(package)) 256 self.FIND_VIRTUAL = re.compile( 257 r"^{}_PROVIDES\s*(\+|)=\s*(.*)".format(package)) 258 self.virtual = [] 259 260 def check_line(self, lineno, text): 261 m = self.VARIABLE.search(text) 262 if m is None: 263 return 264 265 variable = m.group(1) 266 267 # allow to set variables for virtual package this package provides 268 v = self.FIND_VIRTUAL.search(text) 269 if v: 270 self.virtual += v.group(2).upper().split() 271 return 272 for virtual in self.virtual: 273 if variable.startswith("{}_".format(virtual)): 274 return 275 276 if self.ALLOWED.match(variable): 277 return 278 if self.REGEX.search(text) is None: 279 return ["{}:{}: possible typo: {} -> *{}*" 280 .format(self.filename, lineno, variable, self.package), 281 text] 282 283 284class UselessFlag(_CheckFunction): 285 DEFAULT_AUTOTOOLS_FLAG = re.compile(r"^.*{}".format("|".join([ 286 r"_AUTORECONF\s*=\s*NO", 287 r"_LIBTOOL_PATCH\s*=\s*YES"]))) 288 DEFAULT_GENERIC_FLAG = re.compile(r"^.*{}".format("|".join([ 289 r"_INSTALL_IMAGES\s*=\s*NO", 290 r"_INSTALL_REDISTRIBUTE\s*=\s*YES", 291 r"_INSTALL_STAGING\s*=\s*NO", 292 r"_INSTALL_TARGET\s*=\s*YES"]))) 293 END_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(end_conditional))) 294 START_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(start_conditional))) 295 296 def before(self): 297 self.conditional = 0 298 299 def check_line(self, lineno, text): 300 if self.START_CONDITIONAL.search(text): 301 self.conditional += 1 302 return 303 if self.END_CONDITIONAL.search(text): 304 self.conditional -= 1 305 return 306 307 # allow non-default conditionally overridden by default 308 if self.conditional > 0: 309 return 310 311 if self.DEFAULT_GENERIC_FLAG.search(text): 312 return ["{}:{}: useless default value ({}#" 313 "_infrastructure_for_packages_with_specific_build_systems)" 314 .format(self.filename, lineno, self.url_to_manual), 315 text] 316 317 if self.DEFAULT_AUTOTOOLS_FLAG.search(text) and not text.lstrip().startswith("HOST_"): 318 return ["{}:{}: useless default value " 319 "({}#_infrastructure_for_autotools_based_packages)" 320 .format(self.filename, lineno, self.url_to_manual), 321 text] 322 323 324class VariableWithBraces(_CheckFunction): 325 VARIABLE_WITH_BRACES = re.compile(r"^[^#].*[^$]\${\w+}") 326 327 def check_line(self, lineno, text): 328 if self.VARIABLE_WITH_BRACES.match(text.rstrip()): 329 return ["{}:{}: use $() to delimit variables, not ${{}}" 330 .format(self.filename, lineno), 331 text] 332