Просмотр исходного кода

Update the check-includes script to enforce some naming and no-circularity rules

Nick Mathewson 6 лет назад
Родитель
Сommit
c3c8c926bf
1 измененных файлов с 64 добавлено и 0 удалено
  1. 64 0
      scripts/maint/checkIncludes.py

+ 64 - 0
scripts/maint/checkIncludes.py

@@ -33,6 +33,9 @@ else:
     def open_file(fname):
     def open_file(fname):
         return open(fname, 'r', encoding='utf-8')
         return open(fname, 'r', encoding='utf-8')
 
 
+def warn(msg):
+    print(msg, file=sys.stderr)
+
 def err(msg):
 def err(msg):
     """ Declare that an error has happened, and remember that there has
     """ Declare that an error has happened, and remember that there has
         been an error. """
         been an error. """
@@ -48,14 +51,34 @@ def fname_is_c(fname):
 INCLUDE_PATTERN = re.compile(r'\s*#\s*include\s+"([^"]*)"')
 INCLUDE_PATTERN = re.compile(r'\s*#\s*include\s+"([^"]*)"')
 RULES_FNAME = ".may_include"
 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):
 class Rules(object):
     """ A 'Rules' object is the parsed version of a .may_include file. """
     """ A 'Rules' object is the parsed version of a .may_include file. """
     def __init__(self, dirpath):
     def __init__(self, dirpath):
         self.dirpath = dirpath
         self.dirpath = dirpath
+        if dirpath.startswith("src/"):
+            self.incpath = dirpath[4:]
+        else:
+            self.incpath = dirpath
         self.patterns = []
         self.patterns = []
         self.usedPatterns = set()
         self.usedPatterns = set()
 
 
     def addPattern(self, pattern):
     def addPattern(self, pattern):
+        if not pattern_is_normal(pattern):
+            warn("Unusual pattern {} in {}".format(pattern, self.dirpath))
         self.patterns.append(pattern)
         self.patterns.append(pattern)
 
 
     def includeOk(self, path):
     def includeOk(self, path):
@@ -86,6 +109,20 @@ class Rules(object):
             if p not in self.usedPatterns:
             if p not in self.usedPatterns:
                 print("Pattern {} in {} was never used.".format(p, self.dirpath))
                 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):
 def load_include_rules(fname):
     """ Read a rules file from 'fname', and return it as a Rules object. """
     """ Read a rules file from 'fname', and return it as a Rules object. """
     result = Rules(os.path.split(fname)[0])
     result = Rules(os.path.split(fname)[0])
@@ -99,6 +136,8 @@ def load_include_rules(fname):
 
 
 list_unused = False
 list_unused = False
 
 
+uses_dirs = { }
+
 for dirpath, dirnames, fnames in os.walk("src"):
 for dirpath, dirnames, fnames in os.walk("src"):
     if ".may_include" in fnames:
     if ".may_include" in fnames:
         rules = load_include_rules(os.path.join(dirpath, RULES_FNAME))
         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:
         if list_unused:
             rules.noteUnusedRules()
             rules.noteUnusedRules()
 
 
+        uses_dirs[rules.incpath] = rules.getAllowedDirectories()
+
 if trouble:
 if trouble:
     err(
     err(
 """To change which includes are allowed in a C file, edit the {}
 """To change which includes are allowed in a C file, edit the {}
 files in its enclosing directory.""".format(RULES_FNAME))
 files in its enclosing directory.""".format(RULES_FNAME))
     sys.exit(1)
     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)