piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 1 | # |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 2 | # Copyright 2010-2012 Free Software Foundation, Inc. |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 3 | # |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 4 | # This file was generated by gr_modtool, a tool from the GNU Radio framework |
| 5 | # This file is a part of gr-gsm |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 6 | # |
| 7 | # GNU Radio is free software; you can redistribute it and/or modify |
| 8 | # it under the terms of the GNU General Public License as published by |
| 9 | # the Free Software Foundation; either version 3, or (at your option) |
| 10 | # any later version. |
| 11 | # |
| 12 | # GNU Radio is distributed in the hope that it will be useful, |
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | # GNU General Public License for more details. |
| 16 | # |
| 17 | # You should have received a copy of the GNU General Public License |
| 18 | # along with GNU Radio; see the file COPYING. If not, write to |
| 19 | # the Free Software Foundation, Inc., 51 Franklin Street, |
| 20 | # Boston, MA 02110-1301, USA. |
| 21 | # |
| 22 | """ |
| 23 | Creates the swig_doc.i SWIG interface file. |
| 24 | Execute using: python swig_doc.py xml_path outputfilename |
| 25 | |
| 26 | The file instructs SWIG to transfer the doxygen comments into the |
| 27 | python docstrings. |
| 28 | |
| 29 | """ |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 30 | from __future__ import unicode_literals |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 31 | |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 32 | import sys, time |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 33 | |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 34 | from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile |
| 35 | from doxyxml import DoxyOther, base |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 36 | |
| 37 | def py_name(name): |
| 38 | bits = name.split('_') |
| 39 | return '_'.join(bits[1:]) |
| 40 | |
| 41 | def make_name(name): |
| 42 | bits = name.split('_') |
| 43 | return bits[0] + '_make_' + '_'.join(bits[1:]) |
| 44 | |
| 45 | |
| 46 | class Block(object): |
| 47 | """ |
| 48 | Checks if doxyxml produced objects correspond to a gnuradio block. |
| 49 | """ |
| 50 | |
| 51 | @classmethod |
| 52 | def includes(cls, item): |
| 53 | if not isinstance(item, DoxyClass): |
| 54 | return False |
| 55 | # Check for a parsing error. |
| 56 | if item.error(): |
| 57 | return False |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 58 | friendname = make_name(item.name()) |
| 59 | is_a_block = item.has_member(friendname, DoxyFriend) |
| 60 | # But now sometimes the make function isn't a friend so check again. |
| 61 | if not is_a_block: |
| 62 | is_a_block = di.has_member(friendname, DoxyFunction) |
| 63 | return is_a_block |
| 64 | |
| 65 | class Block2(object): |
| 66 | """ |
| 67 | Checks if doxyxml produced objects correspond to a new style |
| 68 | gnuradio block. |
| 69 | """ |
| 70 | |
| 71 | @classmethod |
| 72 | def includes(cls, item): |
| 73 | if not isinstance(item, DoxyClass): |
| 74 | return False |
| 75 | # Check for a parsing error. |
| 76 | if item.error(): |
| 77 | return False |
| 78 | is_a_block2 = item.has_member('make', DoxyFunction) and item.has_member('sptr', DoxyOther) |
| 79 | return is_a_block2 |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 80 | |
| 81 | |
| 82 | def utoascii(text): |
| 83 | """ |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 84 | Convert unicode text into ascii and escape quotes and backslashes. |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 85 | """ |
| 86 | if text is None: |
| 87 | return '' |
| 88 | out = text.encode('ascii', 'replace') |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 89 | # swig will require us to replace blackslash with 4 backslashes |
| 90 | out = out.replace(b'\\', b'\\\\\\\\') |
| 91 | out = out.replace(b'"', b'\\"').decode('ascii') |
| 92 | return str(out) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 93 | |
| 94 | |
| 95 | def combine_descriptions(obj): |
| 96 | """ |
| 97 | Combines the brief and detailed descriptions of an object together. |
| 98 | """ |
| 99 | description = [] |
| 100 | bd = obj.brief_description.strip() |
| 101 | dd = obj.detailed_description.strip() |
| 102 | if bd: |
| 103 | description.append(bd) |
| 104 | if dd: |
| 105 | description.append(dd) |
| 106 | return utoascii('\n\n'.join(description)).strip() |
| 107 | |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 108 | def format_params(parameteritems): |
| 109 | output = ['Args:'] |
| 110 | template = ' {0} : {1}' |
| 111 | for pi in parameteritems: |
| 112 | output.append(template.format(pi.name, pi.description)) |
| 113 | return '\n'.join(output) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 114 | |
| 115 | entry_templ = '%feature("docstring") {name} "{docstring}"' |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 116 | def make_entry(obj, name=None, templ="{description}", description=None, params=[]): |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 117 | """ |
| 118 | Create a docstring entry for a swig interface file. |
| 119 | |
| 120 | obj - a doxyxml object from which documentation will be extracted. |
| 121 | name - the name of the C object (defaults to obj.name()) |
| 122 | templ - an optional template for the docstring containing only one |
| 123 | variable named 'description'. |
| 124 | description - if this optional variable is set then it's value is |
| 125 | used as the description instead of extracting it from obj. |
| 126 | """ |
| 127 | if name is None: |
| 128 | name=obj.name() |
| 129 | if "operator " in name: |
| 130 | return '' |
| 131 | if description is None: |
| 132 | description = combine_descriptions(obj) |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 133 | if params: |
| 134 | description += '\n\n' |
| 135 | description += utoascii(format_params(params)) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 136 | docstring = templ.format(description=description) |
| 137 | if not docstring: |
| 138 | return '' |
| 139 | return entry_templ.format( |
| 140 | name=name, |
| 141 | docstring=docstring, |
| 142 | ) |
| 143 | |
| 144 | |
| 145 | def make_func_entry(func, name=None, description=None, params=None): |
| 146 | """ |
| 147 | Create a function docstring entry for a swig interface file. |
| 148 | |
| 149 | func - a doxyxml object from which documentation will be extracted. |
| 150 | name - the name of the C object (defaults to func.name()) |
| 151 | description - if this optional variable is set then it's value is |
| 152 | used as the description instead of extracting it from func. |
| 153 | params - a parameter list that overrides using func.params. |
| 154 | """ |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 155 | #if params is None: |
| 156 | # params = func.params |
| 157 | #params = [prm.declname for prm in params] |
| 158 | #if params: |
| 159 | # sig = "Params: (%s)" % ", ".join(params) |
| 160 | #else: |
| 161 | # sig = "Params: (NONE)" |
| 162 | #templ = "{description}\n\n" + sig |
| 163 | #return make_entry(func, name=name, templ=utoascii(templ), |
| 164 | # description=description) |
| 165 | return make_entry(func, name=name, description=description, params=params) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 166 | |
| 167 | |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 168 | def make_class_entry(klass, description=None, ignored_methods=[], params=None): |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 169 | """ |
| 170 | Create a class docstring for a swig interface file. |
| 171 | """ |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 172 | if params is None: |
| 173 | params = klass.params |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 174 | output = [] |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 175 | output.append(make_entry(klass, description=description, params=params)) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 176 | for func in klass.in_category(DoxyFunction): |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 177 | if func.name() not in ignored_methods: |
| 178 | name = klass.name() + '::' + func.name() |
| 179 | output.append(make_func_entry(func, name=name)) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 180 | return "\n\n".join(output) |
| 181 | |
| 182 | |
| 183 | def make_block_entry(di, block): |
| 184 | """ |
| 185 | Create class and function docstrings of a gnuradio block for a |
| 186 | swig interface file. |
| 187 | """ |
| 188 | descriptions = [] |
| 189 | # Get the documentation associated with the class. |
| 190 | class_desc = combine_descriptions(block) |
| 191 | if class_desc: |
| 192 | descriptions.append(class_desc) |
| 193 | # Get the documentation associated with the make function |
| 194 | make_func = di.get_member(make_name(block.name()), DoxyFunction) |
| 195 | make_func_desc = combine_descriptions(make_func) |
| 196 | if make_func_desc: |
| 197 | descriptions.append(make_func_desc) |
| 198 | # Get the documentation associated with the file |
| 199 | try: |
| 200 | block_file = di.get_member(block.name() + ".h", DoxyFile) |
| 201 | file_desc = combine_descriptions(block_file) |
| 202 | if file_desc: |
| 203 | descriptions.append(file_desc) |
| 204 | except base.Base.NoSuchMember: |
| 205 | # Don't worry if we can't find a matching file. |
| 206 | pass |
| 207 | # And join them all together to make a super duper description. |
| 208 | super_description = "\n\n".join(descriptions) |
| 209 | # Associate the combined description with the class and |
| 210 | # the make function. |
| 211 | output = [] |
| 212 | output.append(make_class_entry(block, description=super_description)) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 213 | output.append(make_func_entry(make_func, description=super_description, |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 214 | params=block.params)) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 215 | return "\n\n".join(output) |
| 216 | |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 217 | def make_block2_entry(di, block): |
| 218 | """ |
| 219 | Create class and function docstrings of a new style gnuradio block for a |
| 220 | swig interface file. |
| 221 | """ |
| 222 | descriptions = [] |
| 223 | # For new style blocks all the relevant documentation should be |
| 224 | # associated with the 'make' method. |
| 225 | class_description = combine_descriptions(block) |
| 226 | make_func = block.get_member('make', DoxyFunction) |
| 227 | make_description = combine_descriptions(make_func) |
| 228 | description = class_description + "\n\nConstructor Specific Documentation:\n\n" + make_description |
| 229 | # Associate the combined description with the class and |
| 230 | # the make function. |
| 231 | output = [] |
| 232 | output.append(make_class_entry( |
| 233 | block, description=description, |
| 234 | ignored_methods=['make'], params=make_func.params)) |
| 235 | makename = block.name() + '::make' |
| 236 | output.append(make_func_entry( |
| 237 | make_func, name=makename, description=description, |
| 238 | params=make_func.params)) |
| 239 | return "\n\n".join(output) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 240 | |
| 241 | def make_swig_interface_file(di, swigdocfilename, custom_output=None): |
| 242 | |
| 243 | output = [""" |
| 244 | /* |
| 245 | * This file was automatically generated using swig_doc.py. |
| 246 | * |
| 247 | * Any changes to it will be lost next time it is regenerated. |
| 248 | */ |
| 249 | """] |
| 250 | |
| 251 | if custom_output is not None: |
| 252 | output.append(custom_output) |
| 253 | |
| 254 | # Create docstrings for the blocks. |
| 255 | blocks = di.in_category(Block) |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 256 | blocks2 = di.in_category(Block2) |
| 257 | |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 258 | make_funcs = set([]) |
| 259 | for block in blocks: |
| 260 | try: |
| 261 | make_func = di.get_member(make_name(block.name()), DoxyFunction) |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 262 | # Don't want to risk writing to output twice. |
| 263 | if make_func.name() not in make_funcs: |
| 264 | make_funcs.add(make_func.name()) |
| 265 | output.append(make_block_entry(di, block)) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 266 | except block.ParsingError: |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 267 | sys.stderr.write('Parsing error for block {0}\n'.format(block.name())) |
| 268 | raise |
| 269 | |
| 270 | for block in blocks2: |
| 271 | try: |
| 272 | make_func = block.get_member('make', DoxyFunction) |
| 273 | make_func_name = block.name() +'::make' |
| 274 | # Don't want to risk writing to output twice. |
| 275 | if make_func_name not in make_funcs: |
| 276 | make_funcs.add(make_func_name) |
| 277 | output.append(make_block2_entry(di, block)) |
| 278 | except block.ParsingError: |
| 279 | sys.stderr.write('Parsing error for block {0}\n'.format(block.name())) |
| 280 | raise |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 281 | |
| 282 | # Create docstrings for functions |
| 283 | # Don't include the make functions since they have already been dealt with. |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 284 | funcs = [f for f in di.in_category(DoxyFunction) |
| 285 | if f.name() not in make_funcs and not f.name().startswith('std::')] |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 286 | for f in funcs: |
| 287 | try: |
| 288 | output.append(make_func_entry(f)) |
| 289 | except f.ParsingError: |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 290 | sys.stderr.write('Parsing error for function {0}\n'.format(f.name())) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 291 | |
| 292 | # Create docstrings for classes |
| 293 | block_names = [block.name() for block in blocks] |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 294 | block_names += [block.name() for block in blocks2] |
| 295 | klasses = [k for k in di.in_category(DoxyClass) |
| 296 | if k.name() not in block_names and not k.name().startswith('std::')] |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 297 | for k in klasses: |
| 298 | try: |
| 299 | output.append(make_class_entry(k)) |
| 300 | except k.ParsingError: |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 301 | sys.stderr.write('Parsing error for class {0}\n'.format(k.name())) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 302 | |
| 303 | # Docstrings are not created for anything that is not a function or a class. |
| 304 | # If this excludes anything important please add it here. |
| 305 | |
| 306 | output = "\n\n".join(output) |
| 307 | |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 308 | swig_doc = open(swigdocfilename, 'w') |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 309 | swig_doc.write(output) |
| 310 | swig_doc.close() |
| 311 | |
| 312 | if __name__ == "__main__": |
| 313 | # Parse command line options and set up doxyxml. |
| 314 | err_msg = "Execute using: python swig_doc.py xml_path outputfilename" |
| 315 | if len(sys.argv) != 3: |
Vasil Velichkov | 1789ae2 | 2019-08-13 20:32:05 +0000 | [diff] [blame^] | 316 | raise Exception(err_msg) |
piotr | 437f546 | 2014-02-04 17:57:25 +0100 | [diff] [blame] | 317 | xml_path = sys.argv[1] |
| 318 | swigdocfilename = sys.argv[2] |
| 319 | di = DoxyIndex(xml_path) |
| 320 | |
| 321 | # gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined! |
| 322 | # This is presumably a bug in SWIG. |
| 323 | #msg_q = di.get_member(u'gr_msg_queue', DoxyClass) |
| 324 | #insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction) |
| 325 | #delete_head = msg_q.get_member(u'delete_head', DoxyFunction) |
| 326 | output = [] |
| 327 | #output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail')) |
| 328 | #output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head')) |
| 329 | custom_output = "\n\n".join(output) |
| 330 | |
| 331 | # Generate the docstrings interface file. |
| 332 | make_swig_interface_file(di, swigdocfilename, custom_output=custom_output) |