redox.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. #!/usr/bin/python
  2. #
  3. # Copyright (c) 2008 The Tor Project, Inc.
  4. # See LICENSE for licensing information.
  5. #
  6. # Hi!
  7. # I'm redox.py, the Tor redocumentation tool!
  8. # I am a horrible hack!
  9. # I read the output of doxygen from stderr, and add missing DOCDOC comments
  10. # to tell you where documentation should go!
  11. # To use me, edit the stuff below...
  12. # ...and run 'make doxygen 2>doxygen.stderr' ...
  13. # ...and run ./contrib/redox.py < doxygen.stderr !
  14. # I'll make a bunch of new files by adding missing DOCDOC comments to your
  15. # source. Those files will have names like ./src/common/util.c.newdoc.
  16. # You will want to look over the changes by hand before checking them in.
  17. SKIP_FILES = [ "OpenBSD_malloc_Linux.c",
  18. "eventdns.c",
  19. "eventdns.h",
  20. "strlcat.c",
  21. "strlcpy.c",
  22. "aes.c",
  23. "aes.h" ]
  24. SKIP_NAME_PATTERNS = [ r'^.*_c_id$' ]
  25. ADD_DOCDOCS_TO_TYPES = [ 'function', 'type', 'typedef' ]
  26. # ADD_DOCDOCS_TO_TYPES += [ 'variable', 'define' ]
  27. # ====================
  28. # The rest of this should not need hacking.
  29. import re
  30. import sys
  31. KINDS = [ "type", "field", "typedef", "define", "function", "variable" ]
  32. NODOC_LINE_RE = re.compile(r'^([^:]+):(\d+): (\w+): (.*) is not documented\.$')
  33. THING_RE = re.compile(r'^Member ([a-zA-Z0-9_]+).*\((typedef|define|function|variable)\) of (file|class) ')
  34. SKIP_NAMES = [re.compile(s) for s in SKIP_NAME_PATTERNS]
  35. def parsething(thing):
  36. if thing.startswith("Compound "):
  37. tp, name = "type", thing.split()[1]
  38. else:
  39. m = THING_RE.match(thing)
  40. if not m:
  41. print thing
  42. return None, None
  43. else:
  44. name, tp, parent = m.groups()
  45. if parent == 'class':
  46. if tp == 'variable' or tp == 'function':
  47. tp = 'field'
  48. return name, tp
  49. def read():
  50. errs = {}
  51. for line in sys.stdin:
  52. m = NODOC_LINE_RE.match(line)
  53. if m:
  54. file, line, tp, thing = m.groups()
  55. assert tp == 'Warning'
  56. name, kind = parsething(thing)
  57. errs.setdefault(file, []).append((int(line), name, kind))
  58. return errs
  59. def findline(lines, lineno, ident):
  60. for lineno in xrange(lineno, 0, -1):
  61. if ident in lines[lineno]:
  62. return lineno
  63. return None
  64. FUNC_PAT = re.compile(r"^[A-Za-z0-9_]+\(")
  65. def hascomment(lines, lineno, kind):
  66. if "*/" in lines[lineno-1]:
  67. return True
  68. if kind == 'function' and FUNC_PAT.match(lines[lineno]):
  69. if "*/" in lines[lineno-2]:
  70. return True
  71. return False
  72. def hasdocdoc(lines, lineno, kind):
  73. if "DOCDOC" in lines[lineno] or "DOCDOC" in lines[lineno-1]:
  74. return True
  75. if kind == 'function' and FUNC_PAT.match(lines[lineno]):
  76. if "DOCDOC" in lines[lineno-2]:
  77. return True
  78. return False
  79. def checkf(fn, errs, comments):
  80. for skip in SKIP_FILES:
  81. if fn.endswith(skip):
  82. print "Skipping",fn
  83. return
  84. lines = [ None ]
  85. try:
  86. lines.extend( open(fn, 'r').readlines() )
  87. except IOError:
  88. return
  89. for line, name, kind in errs:
  90. if any(pat.match(name) for pat in SKIP_NAMES):
  91. continue
  92. if kind not in ADD_DOCDOCS_TO_TYPES:
  93. continue
  94. ln = findline(lines, line, name)
  95. if ln == None:
  96. print "Couldn't find the definition of %s allegedly on %s of %s"%(
  97. name, line, fn)
  98. else:
  99. if hasdocdoc(lines, line, kind):
  100. # print "Has a DOCDOC"
  101. # print fn, line, name, kind
  102. # print "\t",lines[line-2],
  103. # print "\t",lines[line-1],
  104. # print "\t",lines[line],
  105. # print "-------"
  106. pass
  107. else:
  108. if kind == 'function' and FUNC_PAT.match(lines[ln]):
  109. ln = ln - 1
  110. comments.setdefault(fn, []).append((ln, kind, name))
  111. def applyComments(fn, entries):
  112. N = 0
  113. lines = [ None ]
  114. try:
  115. lines.extend( open(fn, 'r').readlines() )
  116. except IOError:
  117. return
  118. entries.sort()
  119. entries.reverse()
  120. for ln, kind, name in entries:
  121. lines.insert(ln, "/* DOCDOC %s */\n"%name)
  122. N += 1
  123. outf = open(fn+".newdoc", 'w')
  124. for line in lines[1:]:
  125. outf.write(line)
  126. outf.close()
  127. print "Added %s DOCDOCs to %s" %(N, fn)
  128. e = read()
  129. comments = {}
  130. for fn, errs in e.iteritems():
  131. checkf(fn, errs, comments)
  132. for fn, entries in comments.iteritems():
  133. applyComments(fn, entries)