3 # Copyright (c) 2008 The Tor Project, Inc.
4 # See LICENSE for licensing information.
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.
18 # So, here's your workflow:
20 # 0. Make sure you're running a bourne shell for the redirects below.
21 # 1. make doxygen 1>doxygen.stdout 2>doxygen.stderr.
22 # 2. grep Warning doxygen.stderr | grep -v 'is not documented' | less
23 # [This will tell you about all the bogus doxygen output you have]
24 # 3. python ./contrib/redox.py <doxygen.stderr
25 # [This will make lots of .newdoc files with DOCDOC comments for
26 # whatever was missing documentation.]
27 # 4. Look over those .newdoc files, and see which docdoc comments you
28 # want to merge into the main file. If it's all good, just run
29 # "mv fname.c.newdoc fname.c". Otherwise, you'll need to merge
30 # the parts you like by hand.
32 # Which files should we ignore warning from? Mostly, these are external
33 # files that we've snarfed in from somebody else, whose C we do no intend
34 # to document for them.
35 SKIP_FILES
= [ "OpenBSD_malloc_Linux.c",
43 # What names of things never need javadoc
44 SKIP_NAME_PATTERNS
= [ r
'^.*_c_id$' ]
46 # Which types of things should get DOCDOC comments added if they are
47 # missing documentation? Recognized types are in KINDS below.
48 ADD_DOCDOCS_TO_TYPES
= [ 'function', 'type', 'typedef' ]
49 # ADD_DOCDOCS_TO_TYPES += [ 'variable', 'define' ]
51 # ====================
52 # The rest of this should not need hacking.
57 KINDS
= [ "type", "field", "typedef", "define", "function", "variable" ]
59 NODOC_LINE_RE
= re
.compile(r
'^([^:]+):(\d+): (\w+): (.*) is not documented\.$')
61 THING_RE
= re
.compile(r
'^Member ([a-zA-Z0-9_]+).*\((typedef|define|function|variable)\) of (file|class) ')
63 SKIP_NAMES
= [re
.compile(s
) for s
in SKIP_NAME_PATTERNS
]
65 def parsething(thing
):
66 """I figure out what 'foobar baz in quux quum is not documented' means,
67 and return: the name of the foobar, and the kind of the foobar.
69 if thing
.startswith("Compound "):
70 tp
, name
= "type", thing
.split()[1]
72 m
= THING_RE
.match(thing
)
77 name
, tp
, parent
= m
.groups()
79 if tp
== 'variable' or tp
== 'function':
85 """I snarf doxygen stderr from stdin, and parse all the "foo has no
86 documentation messages. I return a map from filename to lists
87 of tuples of (alleged line number, name of thing, kind of thing)
90 for line
in sys
.stdin
:
91 m
= NODOC_LINE_RE
.match(line
)
93 file, line
, tp
, thing
= m
.groups()
94 assert tp
== 'Warning'
95 name
, kind
= parsething(thing
)
96 errs
.setdefault(file, []).append((int(line
), name
, kind
))
100 def findline(lines
, lineno
, ident
):
101 """Given a list of all the lines in the file (adjusted so 1-indexing works),
102 a line number that ident is alledgedly on, and ident, I figure out
103 the line where ident was really declared."""
104 for lineno
in xrange(lineno
, 0, -1):
105 if ident
in lines
[lineno
]:
110 FUNC_PAT
= re
.compile(r
"^[A-Za-z0-9_]+\(")
112 def hascomment(lines
, lineno
, kind
):
113 """I return true if it looks like there's already a good comment about
114 the thing on lineno of lines of type kind. """
115 if "*/" in lines
[lineno
-1]:
117 if kind
== 'function' and FUNC_PAT
.match(lines
[lineno
]):
118 if "*/" in lines
[lineno
-2]:
122 def hasdocdoc(lines
, lineno
, kind
):
123 """I return true if it looks like there's already a docdoc comment about
124 the thing on lineno of lines of type kind."""
125 if "DOCDOC" in lines
[lineno
] or "DOCDOC" in lines
[lineno
-1]:
127 if kind
== 'function' and FUNC_PAT
.match(lines
[lineno
]):
128 if "DOCDOC" in lines
[lineno
-2]:
132 def checkf(fn
, errs
):
133 """I go through the output of read() for a single file, and build a list
134 of tuples of things that want DOCDOC comments. Each tuple has:
135 the line number where the comment goes; the kind of thing; its name.
137 for skip
in SKIP_FILES
:
138 if fn
.endswith(skip
):
145 lines
.extend( open(fn
, 'r').readlines() )
149 for line
, name
, kind
in errs
:
150 if any(pat
.match(name
) for pat
in SKIP_NAMES
):
153 if kind
not in ADD_DOCDOCS_TO_TYPES
:
156 ln
= findline(lines
, line
, name
)
158 print "Couldn't find the definition of %s allegedly on %s of %s"%(
161 if hasdocdoc(lines
, line
, kind
):
162 # print "Has a DOCDOC"
163 # print fn, line, name, kind
164 # print "\t",lines[line-2],
165 # print "\t",lines[line-1],
166 # print "\t",lines[line],
170 if kind
== 'function' and FUNC_PAT
.match(lines
[ln
]):
173 comments
.append((ln
, kind
, name
))
177 def applyComments(fn
, entries
):
178 """I apply lots of comments to the file in fn, making a new .newdoc file.
184 lines
.extend( open(fn
, 'r').readlines() )
188 # Process the comments in reverse order by line number, so that
189 # the line numbers for the ones we haven't added yet remain valid
190 # until we add them. Standard trick.
194 for ln
, kind
, name
in entries
:
196 lines
.insert(ln
, "/* DOCDOC %s */\n"%name
)
199 outf
= open(fn
+".newdoc", 'w')
200 for line
in lines
[1:]:
204 print "Added %s DOCDOCs to %s" %(N
, fn
)
208 for fn
, errs
in e
.iteritems():
209 comments
= checkf(fn
, errs
)
211 applyComments(fn
, comments
)