diff options
Diffstat (limited to 'scripts/checkkconfigsymbols.py')
-rw-r--r-- | scripts/checkkconfigsymbols.py | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py new file mode 100644 index 0000000..f944089 --- /dev/null +++ b/scripts/checkkconfigsymbols.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +"""Find Kconfig identifieres that are referenced but not defined.""" + +# Copyright (C) 2014 Valentin Rothberg <valentinrothberg@gmail.com> +# Copyright (C) 2014 Stefan Hengelein <stefan.hengelein@fau.de> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. + + +import os +import re +from subprocess import Popen, PIPE, STDOUT + +# REGEX EXPRESSIONS +OPERATORS = r"&|\(|\)|\||\!" +FEATURE = r"\w*[A-Z]{1}\w*" +CONFIG_DEF = r"^\s*(?:menu){,1}config\s+(" + FEATURE + r")\s*" +EXPR = r"(?:" + OPERATORS + r"|\s|" + FEATURE + r")+" +STMT = r"^\s*(?:if|select|depends\s+on)\s+" + EXPR + +# REGEX OBJECTS +REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$") +REGEX_FEATURE = re.compile(r"(" + FEATURE + r")") +REGEX_SOURCE_FEATURE = re.compile(r"(?:D|\W|\b)+CONFIG_(" + FEATURE + r")") +REGEX_KCONFIG_DEF = re.compile(CONFIG_DEF) +REGEX_KCONFIG_EXPR = re.compile(EXPR) +REGEX_KCONFIG_STMT = re.compile(STMT) +REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$") +REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$") + + +def main(): + """Main function of this module.""" + source_files = [] + kconfig_files = [] + defined_features = set() + referenced_features = dict() + + # use 'git ls-files' to get the worklist + pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True) + (stdout, _) = pop.communicate() # wait until finished + if len(stdout) > 0 and stdout[-1] == "\n": + stdout = stdout[:-1] + + for gitfile in stdout.rsplit("\n"): + if ".git" in gitfile or "ChangeLog" in gitfile or \ + os.path.isdir(gitfile): + continue + if REGEX_FILE_KCONFIG.match(gitfile): + kconfig_files.append(gitfile) + else: + # All non-Kconfig files are checked for consistency + source_files.append(gitfile) + + for sfile in source_files: + parse_source_file(sfile, referenced_features) + + for kfile in kconfig_files: + parse_kconfig_file(kfile, defined_features, referenced_features) + + print "Undefined symbol used\tFile list" + for feature in sorted(referenced_features): + if feature not in defined_features: + if feature.endswith("_MODULE"): + # Avoid false positives for kernel modules + if feature[:-len("_MODULE")] in defined_features: + continue + if "FOO" in feature or "BAR" in feature: + continue + files = referenced_features.get(feature) + print "%s:\t%s" % (feature, ", ".join(files)) + + +def parse_source_file(sfile, referenced_features): + """Parse @sfile for referenced Kconfig features.""" + lines = [] + with open(sfile, "r") as stream: + lines = stream.readlines() + + for line in lines: + if not "CONFIG_" in line: + continue + features = REGEX_SOURCE_FEATURE.findall(line) + for feature in features: + if not REGEX_FILTER_FEATURES.search(feature): + continue + paths = referenced_features.get(feature, set()) + paths.add(sfile) + referenced_features[feature] = paths + + +def get_features_in_line(line): + """Return mentioned Kconfig features in @line.""" + return REGEX_FEATURE.findall(line) + + +def parse_kconfig_file(kfile, defined_features, referenced_features): + """Parse @kfile and update feature definitions and references.""" + lines = [] + skip = False + + with open(kfile, "r") as stream: + lines = stream.readlines() + + for i in range(len(lines)): + line = lines[i] + line = line.strip('\n') + line = line.split("#")[0] # Ignore Kconfig comments + + if REGEX_KCONFIG_DEF.match(line): + feature_def = REGEX_KCONFIG_DEF.findall(line) + defined_features.add(feature_def[0]) + skip = False + elif REGEX_KCONFIG_HELP.match(line): + skip = True + elif skip: + # Ignore content of help messages + pass + elif REGEX_KCONFIG_STMT.match(line): + features = get_features_in_line(line) + # Multi-line statements + while line.endswith("\\"): + i += 1 + line = lines[i] + line = line.strip('\n') + features.extend(get_features_in_line(line)) + for feature in set(features): + paths = referenced_features.get(feature, set()) + paths.add(kfile) + referenced_features[feature] = paths + + +if __name__ == "__main__": + main() |