| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 | 
							- #!/usr/bin/python3
 
- # Copyright 2018 The Tor Project, Inc.  See LICENSE file for licensing info.
 
- """This script looks through all the directories for files matching *.c or
 
-    *.h, and checks their #include directives to make sure that only "permitted"
 
-    headers are included.
 
-    Any #include directives with angle brackets (like #include <stdio.h>) are
 
-    ignored -- only directives with quotes (like #include "foo.h") are
 
-    considered.
 
-    To decide what includes are permitted, this script looks at a .may_include
 
-    file in each directory.  This file contains empty lines, #-prefixed
 
-    comments, filenames (like "lib/foo/bar.h") and file globs (like lib/*/*.h)
 
-    for files that are permitted.
 
- """
 
- from __future__ import print_function
 
- import fnmatch
 
- import os
 
- import re
 
- import sys
 
- # Global: Have there been any errors?
 
- trouble = False
 
- if sys.version_info[0] <= 2:
 
-     def open_file(fname):
 
-         return open(fname, 'r')
 
- 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. """
 
-     global trouble
 
-     trouble = True
 
-     print(msg, file=sys.stderr)
 
- def fname_is_c(fname):
 
-     """ Return true iff 'fname' is the name of a file that we should
 
-         search for possibly disallowed #include directives. """
 
-     return fname.endswith(".h") or fname.endswith(".c")
 
- 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):
 
-         for pattern in self.patterns:
 
-             if fnmatch.fnmatchcase(path, pattern):
 
-                 self.usedPatterns.add(pattern)
 
-                 return True
 
-         return False
 
-     def applyToLines(self, lines, context=""):
 
-         lineno = 0
 
-         for line in lines:
 
-             lineno += 1
 
-             m = INCLUDE_PATTERN.match(line)
 
-             if m:
 
-                 include = m.group(1)
 
-                 if not self.includeOk(include):
 
-                     err("Forbidden include of {} on line {}{}".format(
 
-                         include, lineno, context))
 
-     def applyToFile(self, fname):
 
-         with open_file(fname) as f:
 
-             #print(fname)
 
-             self.applyToLines(iter(f), " of {}".format(fname))
 
-     def noteUnusedRules(self):
 
-         for p in self.patterns:
 
-             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])
 
-     with open_file(fname) as f:
 
-         for line in f:
 
-             line = line.strip()
 
-             if line.startswith("#") or not line:
 
-                 continue
 
-             result.addPattern(line)
 
-     return result
 
- list_unused = False
 
- log_sorted_levels = 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))
 
-         for fname in fnames:
 
-             if fname_is_c(fname):
 
-                 rules.applyToFile(os.path.join(dirpath,fname))
 
-         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 and log_sorted_levels:
 
-         print(n, cur_level)
 
-     if n > 100:
 
-         break
 
- if uses_dirs:
 
-     print("There are circular .may_include dependencies in here somewhere:",
 
-           uses_dirs)
 
-     sys.exit(1)
 
 
  |