checkIncludes.py 2.3 KB

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