diff --git a/scripts/maint/checkIncludes.py b/scripts/maint/checkIncludes.py index 46a3f39638..ecda3fc565 100755 --- a/scripts/maint/checkIncludes.py +++ b/scripts/maint/checkIncludes.py @@ -33,6 +33,9 @@ else: def open_file(fname): return open(fname, 'r', encoding='utf-8') +def warn(msg): + print(msg, file=sys.stderr) + def err(msg): """ Declare that an error has happened, and remember that there has been an error. """ @@ -48,14 +51,34 @@ def fname_is_c(fname): INCLUDE_PATTERN = re.compile(r'\s*#\s*include\s+"([^"]*)"') RULES_FNAME = ".may_include" +ALLOWED_PATTERNS = [ + re.compile(r'^.*\*\.(h|inc)$'), + re.compile(r'^.*/.*\.h$'), + re.compile(r'^ext/.*\.c$'), + re.compile(r'^orconfig.h$'), + re.compile(r'^micro-revision.i$'), +] + +def pattern_is_normal(s): + for p in ALLOWED_PATTERNS: + if p.match(s): + return True + return False + class Rules(object): """ A 'Rules' object is the parsed version of a .may_include file. """ def __init__(self, dirpath): self.dirpath = dirpath + if dirpath.startswith("src/"): + self.incpath = dirpath[4:] + else: + self.incpath = dirpath self.patterns = [] self.usedPatterns = set() def addPattern(self, pattern): + if not pattern_is_normal(pattern): + warn("Unusual pattern {} in {}".format(pattern, self.dirpath)) self.patterns.append(pattern) def includeOk(self, path): @@ -86,6 +109,20 @@ class Rules(object): if p not in self.usedPatterns: print("Pattern {} in {} was never used.".format(p, self.dirpath)) + def getAllowedDirectories(self): + allowed = [] + for p in self.patterns: + m = re.match(r'^(.*)/\*\.(h|inc)$', p) + if m: + allowed.append(m.group(1)) + continue + m = re.match(r'^(.*)/[^/]*$', p) + if m: + allowed.append(m.group(1)) + continue + + return allowed + def load_include_rules(fname): """ Read a rules file from 'fname', and return it as a Rules object. """ result = Rules(os.path.split(fname)[0]) @@ -99,6 +136,8 @@ def load_include_rules(fname): list_unused = False +uses_dirs = { } + for dirpath, dirnames, fnames in os.walk("src"): if ".may_include" in fnames: rules = load_include_rules(os.path.join(dirpath, RULES_FNAME)) @@ -108,8 +147,33 @@ for dirpath, dirnames, fnames in os.walk("src"): if list_unused: rules.noteUnusedRules() + uses_dirs[rules.incpath] = rules.getAllowedDirectories() + if trouble: err( """To change which includes are allowed in a C file, edit the {} files in its enclosing directory.""".format(RULES_FNAME)) sys.exit(1) + +all_levels = [] + +n = 0 +while uses_dirs: + n += 0 + cur_level = [] + for k in list(uses_dirs): + uses_dirs[k] = [ d for d in uses_dirs[k] + if (d in uses_dirs and d != k)] + if uses_dirs[k] == []: + cur_level.append(k) + for k in cur_level: + del uses_dirs[k] + n += 1 + if cur_level: + print(n, cur_level) + if n > 100: + break + +if uses_dirs: + print("Circular dependencies in here somewhere:", uses_dirs) + sys.exit(1)