checkIncludes.py 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. #!/usr/bin/python3
  2. import fnmatch
  3. import os
  4. import re
  5. import sys
  6. trouble = False
  7. def err(msg):
  8. global trouble
  9. trouble = True
  10. print(msg, file=sys.stderr)
  11. def fname_is_c(fname):
  12. return fname.endswith(".h") or fname.endswith(".c")
  13. INCLUDE_PATTERN = re.compile(r'\s*#\s*include\s+"([^"]*)"')
  14. RULES_FNAME = ".may_include"
  15. class Rules(object):
  16. def __init__(self):
  17. self.patterns = []
  18. def addPattern(self, pattern):
  19. self.patterns.append(pattern)
  20. def includeOk(self, path):
  21. for pattern in self.patterns:
  22. if fnmatch.fnmatchcase(path, pattern):
  23. return True
  24. return False
  25. def applyToLines(self, lines, context=""):
  26. lineno = 0
  27. for line in lines:
  28. lineno += 1
  29. m = INCLUDE_PATTERN.match(line)
  30. if m:
  31. include = m.group(1)
  32. if not self.includeOk(include):
  33. err("Forbidden include of {} on line {}{}".format(
  34. include, lineno, context))
  35. def applyToFile(self, fname):
  36. with open(fname, 'r') as f:
  37. #print(fname)
  38. self.applyToLines(iter(f), " of {}".format(fname))
  39. def load_include_rules(fname):
  40. result = Rules()
  41. with open(fname, 'r') as f:
  42. for line in f:
  43. line = line.strip()
  44. if line.startswith("#") or not line:
  45. continue
  46. result.addPattern(line)
  47. return result
  48. for dirpath, dirnames, fnames in os.walk("src"):
  49. if ".may_include" in fnames:
  50. rules = load_include_rules(os.path.join(dirpath, RULES_FNAME))
  51. for fname in fnames:
  52. if fname_is_c(fname):
  53. rules.applyToFile(os.path.join(dirpath,fname))
  54. if trouble:
  55. err(
  56. """To change which includes are allowed in a C file, edit the {} files in its
  57. enclosing directory.""".format(RULES_FNAME))