blob: 4e1ce2e475995836bcddd35778213f0418365405 [file] [log] [blame]
piotr437f5462014-02-04 17:57:25 +01001#
2# Copyright 2010,2011 Free Software Foundation, Inc.
3#
4# This file is part of GNU Radio
5#
6# GNU Radio is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 3, or (at your option)
9# any later version.
10#
11# GNU Radio is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with GNU Radio; see the file COPYING. If not, write to
18# the Free Software Foundation, Inc., 51 Franklin Street,
19# Boston, MA 02110-1301, USA.
20#
21"""
22Creates the swig_doc.i SWIG interface file.
23Execute using: python swig_doc.py xml_path outputfilename
24
25The file instructs SWIG to transfer the doxygen comments into the
26python docstrings.
27
28"""
29
30import sys
31
32try:
33 from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base
34except ImportError:
35 from gnuradio.doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base
36
37
38def py_name(name):
39 bits = name.split('_')
40 return '_'.join(bits[1:])
41
42def make_name(name):
43 bits = name.split('_')
44 return bits[0] + '_make_' + '_'.join(bits[1:])
45
46
47class Block(object):
48 """
49 Checks if doxyxml produced objects correspond to a gnuradio block.
50 """
51
52 @classmethod
53 def includes(cls, item):
54 if not isinstance(item, DoxyClass):
55 return False
56 # Check for a parsing error.
57 if item.error():
58 return False
59 return item.has_member(make_name(item.name()), DoxyFriend)
60
61
62def utoascii(text):
63 """
64 Convert unicode text into ascii and escape quotes.
65 """
66 if text is None:
67 return ''
68 out = text.encode('ascii', 'replace')
69 out = out.replace('"', '\\"')
70 return out
71
72
73def combine_descriptions(obj):
74 """
75 Combines the brief and detailed descriptions of an object together.
76 """
77 description = []
78 bd = obj.brief_description.strip()
79 dd = obj.detailed_description.strip()
80 if bd:
81 description.append(bd)
82 if dd:
83 description.append(dd)
84 return utoascii('\n\n'.join(description)).strip()
85
86
87entry_templ = '%feature("docstring") {name} "{docstring}"'
88def make_entry(obj, name=None, templ="{description}", description=None):
89 """
90 Create a docstring entry for a swig interface file.
91
92 obj - a doxyxml object from which documentation will be extracted.
93 name - the name of the C object (defaults to obj.name())
94 templ - an optional template for the docstring containing only one
95 variable named 'description'.
96 description - if this optional variable is set then it's value is
97 used as the description instead of extracting it from obj.
98 """
99 if name is None:
100 name=obj.name()
101 if "operator " in name:
102 return ''
103 if description is None:
104 description = combine_descriptions(obj)
105 docstring = templ.format(description=description)
106 if not docstring:
107 return ''
108 return entry_templ.format(
109 name=name,
110 docstring=docstring,
111 )
112
113
114def make_func_entry(func, name=None, description=None, params=None):
115 """
116 Create a function docstring entry for a swig interface file.
117
118 func - a doxyxml object from which documentation will be extracted.
119 name - the name of the C object (defaults to func.name())
120 description - if this optional variable is set then it's value is
121 used as the description instead of extracting it from func.
122 params - a parameter list that overrides using func.params.
123 """
124 if params is None:
125 params = func.params
126 params = [prm.declname for prm in params]
127 if params:
128 sig = "Params: (%s)" % ", ".join(params)
129 else:
130 sig = "Params: (NONE)"
131 templ = "{description}\n\n" + sig
132 return make_entry(func, name=name, templ=utoascii(templ),
133 description=description)
134
135
136def make_class_entry(klass, description=None):
137 """
138 Create a class docstring for a swig interface file.
139 """
140 output = []
141 output.append(make_entry(klass, description=description))
142 for func in klass.in_category(DoxyFunction):
143 name = klass.name() + '::' + func.name()
144 output.append(make_func_entry(func, name=name))
145 return "\n\n".join(output)
146
147
148def make_block_entry(di, block):
149 """
150 Create class and function docstrings of a gnuradio block for a
151 swig interface file.
152 """
153 descriptions = []
154 # Get the documentation associated with the class.
155 class_desc = combine_descriptions(block)
156 if class_desc:
157 descriptions.append(class_desc)
158 # Get the documentation associated with the make function
159 make_func = di.get_member(make_name(block.name()), DoxyFunction)
160 make_func_desc = combine_descriptions(make_func)
161 if make_func_desc:
162 descriptions.append(make_func_desc)
163 # Get the documentation associated with the file
164 try:
165 block_file = di.get_member(block.name() + ".h", DoxyFile)
166 file_desc = combine_descriptions(block_file)
167 if file_desc:
168 descriptions.append(file_desc)
169 except base.Base.NoSuchMember:
170 # Don't worry if we can't find a matching file.
171 pass
172 # And join them all together to make a super duper description.
173 super_description = "\n\n".join(descriptions)
174 # Associate the combined description with the class and
175 # the make function.
176 output = []
177 output.append(make_class_entry(block, description=super_description))
178 creator = block.get_member(block.name(), DoxyFunction)
179 output.append(make_func_entry(make_func, description=super_description,
180 params=creator.params))
181 return "\n\n".join(output)
182
183
184def make_swig_interface_file(di, swigdocfilename, custom_output=None):
185
186 output = ["""
187/*
188 * This file was automatically generated using swig_doc.py.
189 *
190 * Any changes to it will be lost next time it is regenerated.
191 */
192"""]
193
194 if custom_output is not None:
195 output.append(custom_output)
196
197 # Create docstrings for the blocks.
198 blocks = di.in_category(Block)
199 make_funcs = set([])
200 for block in blocks:
201 try:
202 make_func = di.get_member(make_name(block.name()), DoxyFunction)
203 make_funcs.add(make_func.name())
204 output.append(make_block_entry(di, block))
205 except block.ParsingError:
206 print('Parsing error for block %s' % block.name())
207
208 # Create docstrings for functions
209 # Don't include the make functions since they have already been dealt with.
210 funcs = [f for f in di.in_category(DoxyFunction) if f.name() not in make_funcs]
211 for f in funcs:
212 try:
213 output.append(make_func_entry(f))
214 except f.ParsingError:
215 print('Parsing error for function %s' % f.name())
216
217 # Create docstrings for classes
218 block_names = [block.name() for block in blocks]
219 klasses = [k for k in di.in_category(DoxyClass) if k.name() not in block_names]
220 for k in klasses:
221 try:
222 output.append(make_class_entry(k))
223 except k.ParsingError:
224 print('Parsing error for class %s' % k.name())
225
226 # Docstrings are not created for anything that is not a function or a class.
227 # If this excludes anything important please add it here.
228
229 output = "\n\n".join(output)
230
231 swig_doc = file(swigdocfilename, 'w')
232 swig_doc.write(output)
233 swig_doc.close()
234
235if __name__ == "__main__":
236 # Parse command line options and set up doxyxml.
237 err_msg = "Execute using: python swig_doc.py xml_path outputfilename"
238 if len(sys.argv) != 3:
239 raise StandardError(err_msg)
240 xml_path = sys.argv[1]
241 swigdocfilename = sys.argv[2]
242 di = DoxyIndex(xml_path)
243
244 # gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined!
245 # This is presumably a bug in SWIG.
246 #msg_q = di.get_member(u'gr_msg_queue', DoxyClass)
247 #insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction)
248 #delete_head = msg_q.get_member(u'delete_head', DoxyFunction)
249 output = []
250 #output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail'))
251 #output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head'))
252 custom_output = "\n\n".join(output)
253
254 # Generate the docstrings interface file.
255 make_swig_interface_file(di, swigdocfilename, custom_output=custom_output)