checkIncludes.py 2.4 KB

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