|
4 | 4 | # |
5 | 5 | # SPDX-License-Identifier: Apache-2.0 |
6 | 6 |
|
| 7 | +import collections |
7 | 8 | import sys |
8 | 9 | import subprocess |
9 | 10 | import re |
@@ -195,6 +196,7 @@ def run(self): |
195 | 196 |
|
196 | 197 | self.check_no_undef_within_kconfig(kconf) |
197 | 198 | self.check_top_menu_not_too_long(kconf) |
| 199 | + self.check_no_undef_outside_kconfig(kconf) |
198 | 200 |
|
199 | 201 | def get_modules(self, modules_file): |
200 | 202 | """ |
@@ -295,6 +297,126 @@ def check_top_menu_not_too_long(self, kconf): |
295 | 297 | entries, then bump the 'max_top_items' variable in {}. |
296 | 298 | """.format(max_top_items, n_top_items, __file__)) |
297 | 299 |
|
| 300 | + def check_no_undef_outside_kconfig(self, kconf): |
| 301 | + """ |
| 302 | + Checks that there are no references to undefined Kconfig symbols |
| 303 | + outside Kconfig files (any CONFIG_FOO where no FOO symbol exists) |
| 304 | + """ |
| 305 | + # Grep for symbol references. |
| 306 | + # |
| 307 | + # Example output line for a reference to CONFIG_BAZ at line 17 of |
| 308 | + # foo/bar.c: |
| 309 | + # |
| 310 | + # foo/bar.c<null>17<null>CONFIG_BAZ |
| 311 | + # |
| 312 | + # Skip the samples/ and tests/ directories for now. They often contain |
| 313 | + # Kconfig files that are not part of the main Kconfig tree, which will |
| 314 | + # trigger false positives until we do something fancier. Skip |
| 315 | + # doc/releases too, which often references removed symbols. |
| 316 | + grep_cmd = "git grep --only-matching --line-number -I --null " \ |
| 317 | + "--extended-regexp --word-regexp CONFIG_[A-Z0-9_]+ " \ |
| 318 | + "-- :!samples :!tests :!doc/releases" |
| 319 | + |
| 320 | + grep_process = subprocess.Popen(grep_cmd.split(), |
| 321 | + stdout=subprocess.PIPE, |
| 322 | + stderr=subprocess.PIPE, |
| 323 | + cwd=self.zephyr_base) |
| 324 | + |
| 325 | + grep_stdout, grep_stderr = grep_process.communicate() |
| 326 | + # Fail if there's anything on stderr too, so that it doesn't get missed |
| 327 | + if grep_process.returncode or grep_stderr: |
| 328 | + self.error("'{}' failed with exit code {} (while searching for " |
| 329 | + "Kconfig symbol references)\n\nstdout:\n{}\n\n" |
| 330 | + "stderr:\n{}" |
| 331 | + .format(grep_cmd, grep_process.returncode, grep_stdout, |
| 332 | + grep_stderr)) |
| 333 | + |
| 334 | + defined_syms = set(sym.name for sym in kconf.unique_defined_syms) |
| 335 | + undef_to_locs = collections.defaultdict(list) |
| 336 | + |
| 337 | + # splitlines() supports various line terminators |
| 338 | + for line in grep_stdout.decode("utf-8").splitlines(): |
| 339 | + path, lineno, sym_name = line.split("\0") |
| 340 | + |
| 341 | + # [7:] removes the "CONFIG_" prefix |
| 342 | + if sym_name[7:] not in defined_syms and \ |
| 343 | + sym_name not in UNDEF_KCONFIG_WHITELIST: |
| 344 | + |
| 345 | + undef_to_locs[sym_name].append("{}:{}".format(path, lineno)) |
| 346 | + |
| 347 | + if not undef_to_locs: |
| 348 | + return |
| 349 | + |
| 350 | + # String that describes all referenced but undefined Kconfig symbols, |
| 351 | + # in alphabetical order, along with the locations where they're |
| 352 | + # referenced. Example: |
| 353 | + # |
| 354 | + # CONFIG_ALSO_MISSING arch/xtensa/core/fatal.c:273 |
| 355 | + # CONFIG_MISSING arch/xtensa/core/fatal.c:264, subsys/fb/cfb.c:20 |
| 356 | + undef_desc = "\n".join( |
| 357 | + "{:35} {}".format(sym_name, ", ".join(locs)) |
| 358 | + for sym_name, locs in sorted(undef_to_locs.items())) |
| 359 | + |
| 360 | + self.add_failure(""" |
| 361 | +Found references to undefined Kconfig symbols. If any of these are false |
| 362 | +positives, then add them to UNDEF_KCONFIG_WHITELIST in {} in the ci-tools |
| 363 | +repo.\n\n{}""".format(__file__, undef_desc)) |
| 364 | + |
| 365 | + |
| 366 | +# Many of these are either symbols used as examples or due to token pasting |
| 367 | +# (CONFIG_FOO_#x, etc.). Note that the list is sorted alphabetically. |
| 368 | +UNDEF_KCONFIG_WHITELIST = { |
| 369 | + "CONFIG_2ND_LVL_INTR_", |
| 370 | + "CONFIG_3RD_LVL_INTR_", |
| 371 | + "CONFIG_APP_LINK_WITH_", |
| 372 | + "CONFIG_CDC_ACM_PORT_NAME_", |
| 373 | + "CONFIG_CLOCK_STM32_PLL_SRC_", |
| 374 | + "CONFIG_CLOCK_STM32_SYSCLK_SRC_", |
| 375 | + "CONFIG_CMU", |
| 376 | + "CONFIG_COUNTER_RTC", |
| 377 | + "CONFIG_COUNTER_RTC_STM32_CLOCK_SRC", |
| 378 | + "CONFIG_COUNTER_TIMER", |
| 379 | + "CONFIG_DEEP_SLEEP", # #defined by RV32M1 in ext/ |
| 380 | + "CONFIG_DESCRIPTION", |
| 381 | + "CONFIG_ERR", |
| 382 | + "CONFIG_ESP_DIF_LIBRARY", # Referenced in CMake comment |
| 383 | + "CONFIG_EXPERIMENTAL", |
| 384 | + "CONFIG_FFT", # Used as an example in cmake/extensions.cmake |
| 385 | + "CONFIG_FLAG", # Used as an example |
| 386 | + "CONFIG_FOO", |
| 387 | + "CONFIG_FOO_LOG_LEVEL", |
| 388 | + "CONFIG_FOO_SETTING_1", |
| 389 | + "CONFIG_FOO_SETTING_2", |
| 390 | + "CONFIG_GPIO_SIFIVE_", |
| 391 | + "CONFIG_I2C_GPIO_", |
| 392 | + "CONFIG_I2S_CAVS_", |
| 393 | + "CONFIG_LIS2DW12_INT_PIN", |
| 394 | + "CONFIG_MODULES", |
| 395 | + "CONFIG_MYFEATURE", |
| 396 | + "CONFIG_MY_DRIVER_0", |
| 397 | + "CONFIG_NORMAL_SLEEP", # #defined by RV32M1 in ext/ |
| 398 | + "CONFIG_OPT", |
| 399 | + "CONFIG_OPT_0", |
| 400 | + "CONFIG_PWM_", |
| 401 | + "CONFIG_REG1", |
| 402 | + "CONFIG_REG2", |
| 403 | + "CONFIG_SEL", |
| 404 | + "CONFIG_SOC_SERIES_", |
| 405 | + "CONFIG_SOC_WATCH", # Issue 13749 |
| 406 | + "CONFIG_SOME_BOOL", |
| 407 | + "CONFIG_SOME_INT", |
| 408 | + "CONFIG_SOME_OTHER_BOOL", |
| 409 | + "CONFIG_SOME_STRING", |
| 410 | + "CONFIG_SPI_", |
| 411 | + "CONFIG_STD_CPP", # Referenced in CMake comment |
| 412 | + "CONFIG_TEST1", |
| 413 | + "CONFIG_TYPE_BOOLEAN", |
| 414 | + "CONFIG_UART_", |
| 415 | + "CONFIG_USB_CONSOLE", |
| 416 | + "CONFIG_USB_HID_DEVICE_NAME_", |
| 417 | + "CONFIG_WHATEVER", |
| 418 | +} |
| 419 | + |
298 | 420 |
|
299 | 421 | class Codeowners(ComplianceTest): |
300 | 422 | """ |
|
0 commit comments