mkdb: use six for python2/3 compat
[gtk-doc.git] / gtkdoc / mkdb.py
blob382ee4cc7478bd4ad03c73153c0a10e45958562f
1 # -*- python; coding: utf-8 -*-
3 # gtk-doc - GTK DocBook documentation generator.
4 # Copyright (C) 1998 Damon Chaplin
5 # 2007-2016 Stefan Sauer
7 # This program 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 2 of the License, or
10 # (at your option) any later version.
12 # This program 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.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 """
23 Creates the DocBook files from the source comments.
24 """
26 from __future__ import print_function
27 from six import iteritems
29 from collections import OrderedDict
30 import logging
31 import os
32 import re
33 import string
34 import sys
36 from . import common, md_to_db
38 # Options
39 MODULE = None
40 DB_OUTPUT_DIR = None
41 INLINE_MARKUP_MODE = None
42 DEFAULT_STABILITY = None
43 NAME_SPACE = ''
44 ROOT_DIR = '.'
46 # These global arrays store information on signals. Each signal has an entry
47 # in each of these arrays at the same index, like a multi-dimensional array.
48 SignalObjects = [] # The GtkObject which emits the signal.
49 SignalNames = [] # The signal name.
50 SignalReturns = [] # The return type.
51 SignalFlags = [] # Flags for the signal
52 SignalPrototypes = [] # The rest of the prototype of the signal handler.
54 # These global arrays store information on Args. Each Arg has an entry
55 # in each of these arrays at the same index, like a multi-dimensional array.
56 ArgObjects = [] # The GtkObject which has the Arg.
57 ArgNames = [] # The Arg name.
58 ArgTypes = [] # The Arg type - gint, GtkArrowType etc.
59 ArgFlags = [] # How the Arg can be used - readable/writable etc.
60 ArgNicks = [] # The nickname of the Arg.
61 ArgBlurbs = [] # Docstring of the Arg.
62 ArgDefaults = [] # Default value of the Arg.
63 ArgRanges = [] # The range of the Arg type
65 # These global hashes store declaration info keyed on a symbol name.
66 Declarations = {}
67 DeclarationTypes = {}
68 DeclarationConditional = {}
69 DeclarationOutput = {}
70 Deprecated = {}
71 Since = {}
72 StabilityLevel = {}
73 StructHasTypedef = {}
75 # These global hashes store the existing documentation.
76 SymbolDocs = {}
77 SymbolParams = {}
78 SymbolAnnotations = {}
80 # These global hashes store documentation scanned from the source files.
81 SourceSymbolDocs = {}
82 SourceSymbolParams = {}
83 SourceSymbolSourceFile = {}
84 SourceSymbolSourceLine = {}
86 # all documentation goes in here, so we can do coverage analysis
87 AllSymbols = {}
88 AllIncompleteSymbols = {}
89 AllUnusedSymbols = {}
90 AllDocumentedSymbols = {}
92 # Undeclared yet documented symbols
93 UndeclaredSymbols = {}
95 # These global arrays store GObject, subclasses and the hierarchy (also of
96 # non-object derived types).
97 Objects = []
98 ObjectLevels = []
99 ObjectRoots = {}
101 Interfaces = {}
102 Prerequisites = {}
104 # holds the symbols which are mentioned in <MODULE>-sections.txt and in which
105 # section they are defined
106 KnownSymbols = {}
107 SymbolSection = {}
108 SymbolSectionId = {}
110 # collects index entries
111 IndexEntriesFull = {}
112 IndexEntriesSince = {}
113 IndexEntriesDeprecated = {}
115 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
116 PreProcessorDirectives = {
117 'assert', 'define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef',
118 'include', 'line', 'pragma', 'unassert', 'undef', 'warning'
121 # remember used annotation (to write minimal glossary)
122 AnnotationsUsed = {}
124 # the regexp that parses the annotation is in ScanSourceFile()
125 AnnotationDefinition = {
126 # the GObjectIntrospection annotations are defined at:
127 # https://live.gnome.org/GObjectIntrospection/Annotations
128 'allow-none': "NULL is OK, both for passing and for returning.",
129 'nullable': "NULL may be passed as the value in, out, in-out; or as a return value.",
130 'not nullable': "NULL must not be passed as the value in, out, in-out; or as a return value.",
131 'optional': "NULL may be passed instead of a pointer to a location.",
132 'array': "Parameter points to an array of items.",
133 'attribute': "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
134 'attributes': "Free-form key-value pairs.",
135 'closure': "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
136 'constructor': "This symbol is a constructor, not a static method.",
137 'destroy': "This parameter is a 'destroy_data', for callbacks.",
138 'default': "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
139 'element-type': "Generics and defining elements of containers and arrays.",
140 'error-domains': "Typed errors. Similar to throws in Java.",
141 'foreign': "This is a foreign struct.",
142 'get-value-func': "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
143 'in': "Parameter for input. Default is <acronym>transfer none</acronym>.",
144 'inout': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
145 'in-out': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
146 'method': "This is a method",
147 'not-error': "A GError parameter is not to be handled like a normal GError.",
148 'out': "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
149 'out caller-allocates': "Out parameter, where caller must allocate storage.",
150 'out callee-allocates': "Out parameter, where caller must allocate storage.",
151 'ref-func': "The specified function is used to ref a struct, must be a GTypeInstance.",
152 'rename-to': "Rename the original symbol's name to SYMBOL.",
153 'scope call': "The callback is valid only during the call to the method.",
154 'scope async': "The callback is valid until first called.",
155 'scope notified': "The callback is valid until the GDestroyNotify argument is called.",
156 'set-value-func': "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
157 'skip': "Exposed in C code, not necessarily available in other languages.",
158 'transfer container': "Free data container after the code is done.",
159 'transfer floating': "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
160 'transfer full': "Free data after the code is done.",
161 'transfer none': "Don't free data after the code is done.",
162 'type': "Override the parsed C type with given type.",
163 'unref-func': "The specified function is used to unref a struct, must be a GTypeInstance.",
164 'virtual': "This is the invoker for a virtual method.",
165 'value': "The specified value overrides the evaluated value of the constant.",
166 # Stability Level definition
167 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
168 'Stable': '''The intention of a Stable interface is to enable arbitrary third parties to
169 develop applications to these interfaces, release them, and have confidence that
170 they will run on all minor releases of the product (after the one in which the
171 interface was introduced, and within the same major release). Even at a major
172 release, incompatible changes are expected to be rare, and to have strong
173 justifications.
174 ''',
175 'Unstable': '''Unstable interfaces are experimental or transitional. They are typically used to
176 give outside developers early access to new or rapidly changing technology, or
177 to provide an interim solution to a problem where a more general solution is
178 anticipated. No claims are made about either source or binary compatibility from
179 one minor release to the next.
181 The Unstable interface level is a warning that these interfaces are subject to
182 change without warning and should not be used in unbundled products.
184 Given such caveats, customer impact need not be a factor when considering
185 incompatible changes to an Unstable interface in a major or minor release.
186 Nonetheless, when such changes are introduced, the changes should still be
187 mentioned in the release notes for the affected release.
188 ''',
189 'Private': '''An interface that can be used within the GNOME stack itself, but that is not
190 documented for end-users. Such functions should only be used in specified and
191 documented ways.
192 ''',
195 # Function and other declaration output settings.
196 RETURN_TYPE_FIELD_WIDTH = 20
197 MAX_SYMBOL_FIELD_WIDTH = 40
199 # XML header
200 doctype_header = None
202 # refentry template
203 REFENTRY = string.Template('''${header}
204 <refentry id="${section_id}">
205 <refmeta>
206 <refentrytitle role="top_of_page" id="${section_id}.top_of_page">${title}</refentrytitle>
207 <manvolnum>3</manvolnum>
208 <refmiscinfo>${MODULE} Library${image}</refmiscinfo>
209 </refmeta>
210 <refnamediv>
211 <refname>${title}</refname>
212 <refpurpose>${short_desc}</refpurpose>
213 </refnamediv>
214 ${stability}
215 ${functions_synop}${args_synop}${signals_synop}${object_anchors}${other_synop}${hierarchy}${prerequisites}${derived}${interfaces}${implementations}
216 ${include_output}
217 <refsect1 id="${section_id}.description" role="desc">
218 <title role="desc.title">Description</title>
219 ${extralinks}${long_desc}
220 </refsect1>
221 <refsect1 id="${section_id}.functions_details" role="details">
222 <title role="details.title">Functions</title>
223 ${functions_details}
224 </refsect1>
225 <refsect1 id="${section_id}.other_details" role="details">
226 <title role="details.title">Types and Values</title>
227 ${other_details}
228 </refsect1>
229 ${args_desc}${signals_desc}${see_also}
230 </refentry>
231 ''')
234 def Run(options):
235 global MODULE, INLINE_MARKUP_MODE, DEFAULT_STABILITY, NAME_SPACE, \
236 DB_OUTPUT_DIR, doctype_header
238 # We should pass the options variable around instead of this global variable horror
239 # but too much of the code expects these to be around. Fix this once the transition is done.
240 MODULE = options.module
241 INLINE_MARKUP_MODE = options.xml_mode or options.sgml_mode
242 DEFAULT_STABILITY = options.default_stability
243 NAME_SPACE = options.name_space
245 main_sgml_file = options.main_sgml_file
246 if not main_sgml_file:
247 # backwards compatibility
248 if os.path.exists(MODULE + "-docs.sgml"):
249 main_sgml_file = MODULE + "-docs.sgml"
250 else:
251 main_sgml_file = MODULE + "-docs.xml"
253 # extract docbook header or define default
254 doctype_header = GetDocbookHeader(main_sgml_file)
256 # This is where we put all the DocBook output.
257 DB_OUTPUT_DIR = DB_OUTPUT_DIR if DB_OUTPUT_DIR else os.path.join(ROOT_DIR, "xml")
258 if not os.path.isdir(DB_OUTPUT_DIR):
259 os.mkdir(DB_OUTPUT_DIR)
261 ReadKnownSymbols(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
262 ReadSignalsFile(os.path.join(ROOT_DIR, MODULE + ".signals"))
263 ReadArgsFile(os.path.join(ROOT_DIR, MODULE + ".args"))
264 ReadObjectHierarchy(os.path.join(ROOT_DIR, MODULE + ".hierarchy"))
265 ReadInterfaces(os.path.join(ROOT_DIR, MODULE + ".interfaces"))
266 ReadPrerequisites(os.path.join(ROOT_DIR, MODULE + ".prerequisites"))
268 ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-decl.txt"), 0)
269 if os.path.isfile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt")):
270 ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt"), 1)
272 # Scan sources
273 if options.source_suffixes:
274 suffix_list = ['.' + ext for ext in options.source_suffixes.split(',')]
275 else:
276 suffix_list = ['.c', '.h']
278 source_dirs = options.source_dir
279 ignore_files = options.ignore_files
280 logging.info(" ignore files: " + ignore_files)
281 for sdir in source_dirs:
282 ReadSourceDocumentation(sdir, suffix_list, source_dirs, ignore_files)
284 changed, book_top, book_bottom = OutputDB(os.path.join(ROOT_DIR, MODULE + "-sections.txt"), options)
285 OutputBook(main_sgml_file, book_top, book_bottom)
287 logging.info("All files created: %d", changed)
289 # If any of the DocBook files have changed, update the timestamp file (so
290 # it can be used for Makefile dependencies).
291 if changed or not os.path.exists(os.path.join(ROOT_DIR, "sgml.stamp")):
293 # try to detect the common prefix
294 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
295 if NAME_SPACE == '':
296 NAME_SPACE = DetermineNamespace()
298 logging.info('namespace prefix ="%s"', NAME_SPACE)
300 OutputIndex("api-index-full", IndexEntriesFull)
301 OutputIndex("api-index-deprecated", IndexEntriesDeprecated)
302 OutputSinceIndexes()
303 OutputAnnotationGlossary()
305 open(os.path.join(ROOT_DIR, 'sgml.stamp'), 'w').write('timestamp')
308 def OutputObjectList():
309 """This outputs the alphabetical list of objects, in a columned table."""
310 # FIXME: Currently this also outputs ancestor objects which may not actually
311 # be in this module.
312 cols = 3
314 # FIXME: use .xml
315 old_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.sgml")
316 new_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.new")
318 OUTPUT = open(new_object_index, 'w')
320 OUTPUT.write('''%s
321 <informaltable pgwide="1" frame="none">
322 <tgroup cols="%s">
323 <colspec colwidth="1*"/>
324 <colspec colwidth="1*"/>
325 <colspec colwidth="1*"/>
326 <tbody>
327 ''' % (MakeDocHeader("informaltable"), cols))
329 count = 0
330 object = None
331 for object in sorted(Objects):
332 xref = MakeXRef(object)
333 if count % cols == 0:
334 OUTPUT.write("<row>\n")
335 OUTPUT.write("<entry>%s</entry>\n" % xref)
336 if count % cols == cols - 1:
337 OUTPUT.write("</row>\n")
338 count += 1
340 if count == 0:
341 # emit an empty row, since empty tables are invalid
342 OUTPUT.write("<row><entry> </entry></row>\n")
344 else:
345 if count % cols > 0:
346 OUTPUT.write("</row>\n")
348 OUTPUT.write('''</tbody></tgroup></informaltable>\n''')
349 OUTPUT.close()
351 common.UpdateFileIfChanged(old_object_index, new_object_index, 0)
354 def TrimTextBlock(desc):
355 """Trims extra whitespace.
357 Empty lines inside a block are preserved.
358 Args:
359 desc (str): the text block to trim. May contain newlines.
362 # strip trailing spaces on every line
363 return re.sub(r'\s+$', '\n', desc.lstrip(), flags=re.MULTILINE)
366 def OutputDB(file, options):
367 """Generate docbook files.
369 This collects the output for each section of the docs, and outputs each file
370 when the end of the section is found.
372 Args:
373 file (str): the $MODULE-sections.txt file which contains all of the
374 functions/macros/structs etc. being documented, organised
375 into sections and subsections.
376 options: commandline options
379 logging.info("Reading: %s", file)
380 INPUT = open(file)
381 filename = ''
382 book_top = ''
383 book_bottom = ''
384 includes = options.default_includes or ''
385 section_includes = ''
386 in_section = 0
387 title = ''
388 section_id = ''
389 subsection = ''
390 num_symbols = 0
391 changed = 0
392 functions_synop = ''
393 other_synop = ''
394 functions_details = ''
395 other_details = ''
396 signals_synop = ''
397 signals_desc = ''
398 args_synop = ''
399 child_args_synop = ''
400 style_args_synop = ''
401 args_desc = ''
402 child_args_desc = ''
403 style_args_desc = ''
404 hierarchy_str = ''
405 hierarchy = []
406 interfaces = ''
407 implementations = ''
408 prerequisites = ''
409 derived = ''
410 file_objects = []
411 file_def_line = {}
412 symbol_def_line = {}
414 MergeSourceDocumentation()
416 line_number = 0
417 for line in INPUT:
418 line_number += 1
420 if line.startswith('#'):
421 continue
423 logging.info("section file data: %d: %s", line_number, line)
425 m1 = re.search(r'^<SUBSECTION\s*(.*)>', line, re.I)
426 m2 = re.search(r'^<TITLE>(.*)<\/TITLE', line)
427 m3 = re.search(r'^<FILE>(.*)<\/FILE>', line)
428 m4 = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
429 m5 = re.search(r'^(\S+)', line)
431 if line.startswith('<SECTION>'):
432 num_symbols = 0
433 in_section = False
434 file_objects = []
435 symbol_def_line = {}
437 elif m1:
438 other_synop += "\n"
439 functions_synop += "\n"
440 subsection = m1.group(1)
442 elif line.startswith('<SUBSECTION>'):
443 continue
444 elif m2:
445 title = m2.group(1)
446 logging.info("Section: %s", title)
448 # We don't want warnings if object & class structs aren't used.
449 DeclarationOutput[title] = 1
450 DeclarationOutput["%sClass" % title] = 1
451 DeclarationOutput["%sIface" % title] = 1
452 DeclarationOutput["%sInterface" % title] = 1
454 elif m3:
455 filename = m3.group(1)
456 if not filename in file_def_line:
457 file_def_line[filename] = line_number
458 else:
459 common.LogWarning(file, line_number, "Double <FILE>%s</FILE> entry. Previous occurrence on line %s." %
460 (filename, file_def_line[filename]))
461 if title == '':
462 key = filename + ":Title"
463 if key in SourceSymbolDocs:
464 title = SourceSymbolDocs[key].rstrip()
466 elif m4:
467 if in_section:
468 section_includes = m4.group(1)
469 else:
470 if options.default_includes:
471 common.LogWarning(file, line_number, "Default <INCLUDE> being overridden by command line option.")
472 else:
473 includes = m4.group(1)
475 elif re.search(r'^<\/SECTION>', line):
476 logging.info("End of section: %s", title)
477 if num_symbols > 0:
478 # collect documents
479 book_bottom += " <xi:include href=\"xml/%s.xml\"/>\n" % filename
481 key = filename + ":Include"
482 if key in SourceSymbolDocs:
483 if section_includes:
484 common.LogWarning(file, line_number, "Section <INCLUDE> being overridden by inline comments.")
485 section_includes = SourceSymbolDocs[key]
487 if section_includes == '':
488 section_includes = includes
490 signals_synop = re.sub(r'^\n*', '', signals_synop)
491 signals_synop = re.sub(r'\n+$', '\n', signals_synop)
493 if signals_synop != '':
494 signals_synop = '''<refsect1 id="%s.signals" role="signal_proto">
495 <title role="signal_proto.title">Signals</title>
496 <informaltable frame="none">
497 <tgroup cols="3">
498 <colspec colname="signals_return" colwidth="150px"/>
499 <colspec colname="signals_name" colwidth="300px"/>
500 <colspec colname="signals_flags" colwidth="200px"/>
501 <tbody>
503 </tbody>
504 </tgroup>
505 </informaltable>
506 </refsect1>
507 ''' % (section_id, signals_synop)
508 signals_desc = TrimTextBlock(signals_desc)
509 signals_desc = '''<refsect1 id="%s.signal-details" role="signals">
510 <title role="signals.title">Signal Details</title>
512 </refsect1>
513 ''' % (section_id, signals_desc)
515 args_synop = re.sub(r'^\n*', '', args_synop)
516 args_synop = re.sub(r'\n+$', '\n', args_synop)
517 if args_synop != '':
518 args_synop = '''<refsect1 id="%s.properties" role="properties">
519 <title role="properties.title">Properties</title>
520 <informaltable frame="none">
521 <tgroup cols="3">
522 <colspec colname="properties_type" colwidth="150px"/>
523 <colspec colname="properties_name" colwidth="300px"/>
524 <colspec colname="properties_flags" colwidth="200px"/>
525 <tbody>
527 </tbody>
528 </tgroup>
529 </informaltable>
530 </refsect1>
531 ''' % (section_id, args_synop)
532 args_desc = TrimTextBlock(args_desc)
533 args_desc = '''<refsect1 id="%s.property-details" role="property_details">
534 <title role="property_details.title">Property Details</title>
536 </refsect1>
537 ''' % (section_id, args_desc)
539 child_args_synop = re.sub(r'^\n*', '', child_args_synop)
540 child_args_synop = re.sub(r'\n+$', '\n', child_args_synop)
541 if child_args_synop != '':
542 args_synop += '''<refsect1 id="%s.child-properties" role="child_properties">
543 <title role="child_properties.title">Child Properties</title>
544 <informaltable frame="none">
545 <tgroup cols="3">
546 <colspec colname="child_properties_type" colwidth="150px"/>
547 <colspec colname="child_properties_name" colwidth="300px"/>
548 <colspec colname="child_properties_flags" colwidth="200px"/>
549 <tbody>
551 </tbody>
552 </tgroup>
553 </informaltable>
554 </refsect1>
555 ''' % (section_id, child_args_synop)
556 child_args_desc = TrimTextBlock(child_args_desc)
557 args_desc += '''<refsect1 id="%s.child-property-details" role="child_property_details">
558 <title role="child_property_details.title">Child Property Details</title>
560 </refsect1>
561 ''' % (section_id, child_args_desc)
563 style_args_synop = re.sub(r'^\n*', '', style_args_synop)
564 style_args_synop = re.sub(r'\n+$', '\n', style_args_synop)
565 if style_args_synop != '':
566 args_synop += '''<refsect1 id="%s.style-properties" role="style_properties">
567 <title role="style_properties.title">Style Properties</title>
568 <informaltable frame="none">
569 <tgroup cols="3">
570 <colspec colname="style_properties_type" colwidth="150px"/>
571 <colspec colname="style_properties_name" colwidth="300px"/>
572 <colspec colname="style_properties_flags" colwidth="200px"/>
573 <tbody>
575 </tbody>
576 </tgroup>
577 </informaltable>
578 </refsect1>
579 ''' % (section_id, style_args_synop)
580 style_args_desc = TrimTextBlock(style_args_desc)
581 args_desc += '''<refsect1 id="%s.style-property-details" role="style_properties_details">
582 <title role="style_properties_details.title">Style Property Details</title>
584 </refsect1>
585 ''' % (section_id, style_args_desc)
587 hierarchy_str = AddTreeLineArt(hierarchy)
588 if hierarchy_str != '':
589 hierarchy_str = '''<refsect1 id="%s.object-hierarchy" role="object_hierarchy">
590 <title role="object_hierarchy.title">Object Hierarchy</title>
591 <screen>%s
592 </screen>
593 </refsect1>
594 ''' % (section_id, hierarchy_str)
596 interfaces = TrimTextBlock(interfaces)
597 if interfaces != '':
598 interfaces = '''<refsect1 id="%s.implemented-interfaces" role="impl_interfaces">
599 <title role="impl_interfaces.title">Implemented Interfaces</title>
601 </refsect1>
602 ''' % (section_id, interfaces)
604 implementations = TrimTextBlock(implementations)
605 if implementations != '':
606 implementations = '''<refsect1 id="%s.implementations" role="implementations">
607 <title role="implementations.title">Known Implementations</title>
609 </refsect1>
610 ''' % (section_id, implementations)
612 prerequisites = TrimTextBlock(prerequisites)
613 if prerequisites != '':
614 prerequisites = '''<refsect1 id="%s.prerequisites" role="prerequisites">
615 <title role="prerequisites.title">Prerequisites</title>
617 </refsect1>
618 ''' % (section_id, prerequisites)
620 derived = TrimTextBlock(derived)
621 if derived != '':
622 derived = '''<refsect1 id="%s.derived-interfaces" role="derived_interfaces">
623 <title role="derived_interfaces.title">Known Derived Interfaces</title>
625 </refsect1>
626 ''' % (section_id, derived)
628 functions_synop = re.sub(r'^\n*', '', functions_synop)
629 functions_synop = re.sub(r'\n+$', '\n', functions_synop)
630 if functions_synop != '':
631 functions_synop = '''<refsect1 id="%s.functions" role="functions_proto">
632 <title role="functions_proto.title">Functions</title>
633 <informaltable pgwide="1" frame="none">
634 <tgroup cols="2">
635 <colspec colname="functions_return" colwidth="150px"/>
636 <colspec colname="functions_name"/>
637 <tbody>
639 </tbody>
640 </tgroup>
641 </informaltable>
642 </refsect1>
643 ''' % (section_id, functions_synop)
645 other_synop = re.sub(r'^\n*', '', other_synop)
646 other_synop = re.sub(r'\n+$', '\n', other_synop)
647 if other_synop != '':
648 other_synop = '''<refsect1 id="%s.other" role="other_proto">
649 <title role="other_proto.title">Types and Values</title>
650 <informaltable role="enum_members_table" pgwide="1" frame="none">
651 <tgroup cols="2">
652 <colspec colname="name" colwidth="150px"/>
653 <colspec colname="description"/>
654 <tbody>
656 </tbody>
657 </tgroup>
658 </informaltable>
659 </refsect1>
660 ''' % (section_id, other_synop)
662 file_changed = OutputDBFile(filename, title, section_id,
663 section_includes,
664 functions_synop, other_synop,
665 functions_details, other_details,
666 signals_synop, signals_desc,
667 args_synop, args_desc,
668 hierarchy_str, interfaces,
669 implementations,
670 prerequisites, derived,
671 file_objects)
672 if file_changed:
673 changed = True
675 title = ''
676 section_id = ''
677 subsection = ''
678 in_section = 0
679 section_includes = ''
680 functions_synop = ''
681 other_synop = ''
682 functions_details = ''
683 other_details = ''
684 signals_synop = ''
685 signals_desc = ''
686 args_synop = ''
687 child_args_synop = ''
688 style_args_synop = ''
689 args_desc = ''
690 child_args_desc = ''
691 style_args_desc = ''
692 hierarchy_str = ''
693 hierarchy = []
694 interfaces = ''
695 implementations = ''
696 prerequisites = ''
697 derived = ''
699 elif m5:
700 symbol = m5.group(1)
701 logging.info(' Symbol: "%s" in subsection: "%s"', symbol, subsection)
703 # check for duplicate entries
704 if symbol not in symbol_def_line:
705 declaration = Declarations.get(symbol)
706 # FIXME: with this we'll output empty declaration
707 if declaration is not None:
708 if CheckIsObject(symbol):
709 file_objects.append(symbol)
711 # We don't want standard macros/functions of GObjects,
712 # or private declarations.
713 if subsection != "Standard" and subsection != "Private":
714 synop, desc = OutputDeclaration(symbol, declaration)
715 type = DeclarationTypes[symbol]
717 if type == 'FUNCTION' or type == 'USER_FUNCTION':
718 functions_synop += synop
719 functions_details += desc
720 elif type == 'MACRO' and re.search(symbol + r'\(', declaration):
721 functions_synop += synop
722 functions_details += desc
723 else:
724 other_synop += synop
725 other_details += desc
727 sig_synop, sig_desc = GetSignals(symbol)
728 arg_synop, child_arg_synop, style_arg_synop, arg_desc, child_arg_desc, style_arg_desc = GetArgs(
729 symbol)
730 ifaces = GetInterfaces(symbol)
731 impls = GetImplementations(symbol)
732 prereqs = GetPrerequisites(symbol)
733 der = GetDerived(symbol)
734 hierarchy = GetHierarchy(symbol, hierarchy)
736 signals_synop += sig_synop
737 signals_desc += sig_desc
738 args_synop += arg_synop
739 child_args_synop += child_arg_synop
740 style_args_synop += style_arg_synop
741 args_desc += arg_desc
742 child_args_desc += child_arg_desc
743 style_args_desc += style_arg_desc
744 interfaces += ifaces
745 implementations += impls
746 prerequisites += prereqs
747 derived += der
749 # Note that the declaration has been output.
750 DeclarationOutput[symbol] = True
751 elif subsection != "Standard" and subsection != "Private":
752 UndeclaredSymbols[symbol] = True
753 common.LogWarning(file, line_number, "No declaration found for %s." % symbol)
755 num_symbols += 1
756 symbol_def_line[symbol] = line_number
758 if section_id == '':
759 if title == '' and filename == '':
760 common.LogWarning(file, line_number, "Section has no title and no file.")
762 # FIXME: one of those would be enough
763 # filename should be an internal detail for gtk-doc
764 if title == '':
765 title = filename
766 elif filename == '':
767 filename = title
769 filename = filename.replace(' ', '_')
771 section_id = SourceSymbolDocs.get(filename + ":Section_Id")
772 if section_id and section_id.strip() != '':
773 # Remove trailing blanks and use as is
774 section_id = section_id.rstrip()
775 elif CheckIsObject(title):
776 # GObjects use their class name as the ID.
777 section_id = common.CreateValidSGMLID(title)
778 else:
779 section_id = common.CreateValidSGMLID(MODULE + '-' + title)
781 SymbolSection[symbol] = title
782 SymbolSectionId[symbol] = section_id
784 else:
785 common.LogWarning(file, line_number, "Double symbol entry for %s. "
786 "Previous occurrence on line %d." % (symbol, symbol_def_line[symbol]))
787 INPUT.close()
789 OutputMissingDocumentation()
790 OutputUndeclaredSymbols()
791 OutputUnusedSymbols()
793 if options.outputallsymbols:
794 OutputAllSymbols()
796 if options.outputsymbolswithoutsince:
797 OutputSymbolsWithoutSince()
799 for filename in options.expand_content_files.split():
800 file_changed = OutputExtraFile(filename)
801 if file_changed:
802 changed = True
804 return (changed, book_top, book_bottom)
807 def DetermineNamespace():
808 """Find common set of characters."""
809 name_space = ''
810 pos = 0
811 ratio = 0.0
812 while True:
813 prefix = {}
814 letter = ''
815 for symbol in IndexEntriesFull.keys():
816 if name_space == '' or name_space.lower() in symbol.lower():
817 if len(symbol) > pos:
818 letter = symbol[pos:pos + 1]
819 # stop prefix scanning
820 if letter == "_":
821 # stop on "_"
822 break
823 # Should we also stop on a uppercase char, if last was lowercase
824 # GtkWidget, if we have the 'W' and had the 't' before
825 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
826 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
827 # need to recound each time as this is per symbol
828 ul = letter.upper()
829 if ul in prefix:
830 prefix[ul] += 1
831 else:
832 prefix[ul] = 1
834 if letter != '' and letter != "_":
835 maxletter = ''
836 maxsymbols = 0
837 for letter in prefix.keys():
838 logging.debug("ns prefix: %s: %s", letter, prefix[letter])
839 if prefix[letter] > maxsymbols:
840 maxletter = letter
841 maxsymbols = prefix[letter]
843 ratio = float(len(IndexEntriesFull)) / prefix[maxletter]
844 logging.debug('most symbols start with %s, that is %f', maxletter, (100 * ratio))
845 if ratio > 0.9:
846 # do another round
847 name_space += maxletter
849 pos += 1
851 else:
852 ratio = 0.0
854 if ratio < 0.9:
855 break
856 return name_space
859 def OutputIndex(basename, apiindex):
860 """Writes an index that can be included into the main-document into an <index> tag.
862 Args:
863 basename (str): name of the index file without extension
864 apiindex (dict): the index data
866 old_index = os.path.join(DB_OUTPUT_DIR, basename + '.xml')
867 new_index = os.path.join(DB_OUTPUT_DIR, basename + '.new')
868 lastletter = " "
869 divopen = 0
870 symbol = None
871 short_symbol = None
873 OUTPUT = open(new_index, 'w')
875 OUTPUT.write(MakeDocHeader("indexdiv") + "\n<indexdiv id=\"%s\">\n" % basename)
877 logging.info("generate %s index (%d entries) with namespace %s", basename, len(apiindex), NAME_SPACE)
879 # do a case insensitive sort while chopping off the prefix
880 mapped_keys = [
882 'original': x,
883 'short': re.sub(r'^' + NAME_SPACE + r'\_?(.*)', r'\1', x.upper(), flags=re.I),
884 } for x in apiindex.keys()]
885 sorted_keys = sorted(mapped_keys, key=lambda d: (d['short'], d['original']))
887 for key in sorted_keys:
888 symbol = key['original']
889 short = key['short']
890 if short != '':
891 short_symbol = short
892 else:
893 short_symbol = symbol
895 # generate a short symbol description
896 symbol_desc = ''
897 symbol_section = ''
898 symbol_section_id = ''
899 symbol_type = ''
900 if symbol in DeclarationTypes:
901 symbol_type = DeclarationTypes[symbol].lower()
903 if symbol_type == '':
904 logging.info("trying symbol %s", symbol)
905 m1 = re.search(r'(.*)::(.*)', symbol)
906 m2 = re.search(r'(.*):(.*)', symbol)
907 if m1:
908 oname = m1.group(1)
909 osym = m1.group(2)
910 logging.info(" trying object signal %s:%s in %d signals", oname, osym, len(SignalNames))
911 for name in SignalNames:
912 logging.info(" " + name)
913 if name == osym:
914 symbol_type = "object signal"
915 if oname in SymbolSection:
916 symbol_section = SymbolSection[oname]
917 symbol_section_id = SymbolSectionId[oname]
918 break
919 elif m2:
920 oname = m2.group(1)
921 osym = m2.group(2)
922 logging.info(" trying object property %s::%s in %d properties", oname, osym, len(ArgNames))
923 for name in ArgNames:
924 logging.info(" " + name)
925 if name == osym:
926 symbol_type = "object property"
927 if oname in SymbolSection:
928 symbol_section = SymbolSection[oname]
929 symbol_section_id = SymbolSectionId[oname]
930 break
931 else:
932 if symbol in SymbolSection:
933 symbol_section = SymbolSection[symbol]
934 symbol_section_id = SymbolSectionId[symbol]
936 if symbol_type != '':
937 symbol_desc = ", " + symbol_type
938 if symbol_section != '':
939 symbol_desc += " in <link linkend=\"%s\">%s</link>" % (symbol_section_id, symbol_section)
940 # symbol_desc +=" in " + ExpandAbbreviations(symbol, "#symbol_section")
942 curletter = short_symbol[0].upper()
943 ixid = apiindex[symbol]
945 logging.info(" add symbol %s with %s to index in section '%s' (derived from %s)",
946 symbol, ixid, curletter, short_symbol)
948 if curletter != lastletter:
949 lastletter = curletter
951 if divopen:
952 OUTPUT.write("</indexdiv>\n")
954 OUTPUT.write("<indexdiv><title>%s</title>\n" % curletter)
955 divopen = True
957 OUTPUT.write('<indexentry><primaryie linkends="%s"><link linkend="%s">%s</link>%s</primaryie></indexentry>\n' %
958 (ixid, ixid, symbol, symbol_desc))
960 if divopen:
961 OUTPUT.write("</indexdiv>\n")
963 OUTPUT.write("</indexdiv>\n")
964 OUTPUT.close()
966 common.UpdateFileIfChanged(old_index, new_index, 0)
969 def OutputSinceIndexes():
970 """Generate the 'since' api index files."""
971 for version in set(Since.values()):
972 logging.info("Since : [%s]", version)
973 index = {x: IndexEntriesSince[x] for x in IndexEntriesSince.keys() if Since[x] == version}
974 OutputIndex("api-index-" + version, index)
977 def OutputAnnotationGlossary():
978 """Writes a glossary of the used annotation terms.
980 The glossary file can be included into the main document.
982 # if there are no annotations used return
983 if not AnnotationsUsed:
984 return
986 old_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.xml")
987 new_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.new")
988 lastletter = " "
989 divopen = False
991 # add acronyms that are referenced from acronym text
992 rerun = True
993 while rerun:
994 rerun = False
995 for annotation in AnnotationsUsed:
996 if annotation not in AnnotationDefinition:
997 continue
998 m = re.search(r'<acronym>([\w ]+)<\/acronym>', AnnotationDefinition[annotation])
999 if m and m.group(1) not in AnnotationsUsed:
1000 AnnotationsUsed[m.group(1)] = 1
1001 rerun = True
1002 break
1004 OUTPUT = open(new_glossary, 'w')
1006 OUTPUT.write('''%s
1007 <glossary id="annotation-glossary">
1008 <title>Annotation Glossary</title>
1009 ''' % MakeDocHeader("glossary"))
1011 for annotation in sorted(AnnotationsUsed.keys(), key=str.lower):
1012 if annotation in AnnotationDefinition:
1013 definition = AnnotationDefinition[annotation]
1014 curletter = annotation[0].upper()
1016 if curletter != lastletter:
1017 lastletter = curletter
1019 if divopen:
1020 OUTPUT.write("</glossdiv>\n")
1022 OUTPUT.write("<glossdiv><title>%s</title>\n" % curletter)
1023 divopen = True
1025 OUTPUT.write(''' <glossentry>
1026 <glossterm><anchor id="annotation-glossterm-%s"/>%s</glossterm>
1027 <glossdef>
1028 <para>%s</para>
1029 </glossdef>
1030 </glossentry>
1031 ''' % (annotation, annotation, definition))
1033 if divopen:
1034 OUTPUT.write("</glossdiv>\n")
1036 OUTPUT.write("</glossary>\n")
1037 OUTPUT.close()
1039 common.UpdateFileIfChanged(old_glossary, new_glossary, 0)
1042 def ReadKnownSymbols(file):
1043 """Collect the names of non-private symbols from the $MODULE-sections.txt file.
1045 Args:
1046 file: the $MODULE-sections.txt file
1049 subsection = ''
1051 logging.info("Reading: %s", file)
1052 INPUT = open(file)
1053 for line in INPUT:
1054 if line.startswith('#'):
1055 continue
1057 if line.startswith('<SECTION>'):
1058 subsection = ''
1059 continue
1061 m = re.search(r'^<SUBSECTION\s*(.*)>', line, flags=re.I)
1062 if m:
1063 subsection = m.group(1)
1064 continue
1066 if line.startswith('<SUBSECTION>'):
1067 continue
1069 if re.search(r'^<TITLE>(.*)<\/TITLE>', line):
1070 continue
1072 m = re.search(r'^<FILE>(.*)<\/FILE>', line)
1073 if m:
1074 KnownSymbols[m.group(1) + ":Long_Description"] = 1
1075 KnownSymbols[m.group(1) + ":Short_Description"] = 1
1076 continue
1078 m = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
1079 if m:
1080 continue
1082 m = re.search(r'^<\/SECTION>', line)
1083 if m:
1084 continue
1086 m = re.search(r'^(\S+)', line)
1087 if m:
1088 symbol = m.group(1)
1089 if subsection != "Standard" and subsection != "Private":
1090 KnownSymbols[symbol] = 1
1091 else:
1092 KnownSymbols[symbol] = 0
1093 INPUT.close()
1096 def OutputDeclaration(symbol, declaration):
1097 """Returns the formatted documentation block for a symbol.
1099 Args:
1100 symbol (str): the name of the function/macro/...
1101 declaration (str): the declaration of the function/macro.
1103 Returns:
1104 str: the formatted documentation
1107 dtype = DeclarationTypes[symbol]
1108 if dtype == 'MACRO':
1109 return OutputMacro(symbol, declaration)
1110 elif dtype == 'TYPEDEF':
1111 return OutputTypedef(symbol, declaration)
1112 elif dtype == 'STRUCT':
1113 return OutputStruct(symbol, declaration)
1114 elif dtype == 'ENUM':
1115 return OutputEnum(symbol, declaration)
1116 elif dtype == 'UNION':
1117 return OutputUnion(symbol, declaration)
1118 elif dtype == 'VARIABLE':
1119 return OutputVariable(symbol, declaration)
1120 elif dtype == 'FUNCTION':
1121 return OutputFunction(symbol, declaration, dtype)
1122 elif dtype == 'USER_FUNCTION':
1123 return OutputFunction(symbol, declaration, dtype)
1124 else:
1125 sys.exit("Unknown symbol type " + dtype)
1128 def OutputSymbolTraits(symbol):
1129 """Returns the Since and StabilityLevel paragraphs for a symbol.
1131 Args:
1132 symbol (str): the name to describe
1134 Returns:
1135 str: paragraph or empty string
1138 desc = ''
1140 if symbol in Since:
1141 link_id = "api-index-" + Since[symbol]
1142 desc += "<para role=\"since\">Since: <link linkend=\"%s\">%s</link></para>" % (link_id, Since[symbol])
1144 if symbol in StabilityLevel:
1145 stability = StabilityLevel[symbol]
1146 AnnotationsUsed[stability] = True
1147 desc += "<para role=\"stability\">Stability Level: <acronym>%s</acronym></para>" % stability
1148 return desc
1151 def uri_escape(text):
1152 if text is None:
1153 return None
1155 # Build a char to hex map
1156 escapes = {chr(i): ("%%%02X" % i) for i in range(256)}
1158 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1159 def do_escape(char):
1160 return escapes[char]
1161 return re.sub(r"([^A-Za-z0-9\-_.!~*'()]", do_escape, text)
1164 def OutputSymbolExtraLinks(symbol):
1165 """Returns extralinks for the symbol (if enabled).
1167 Args:
1168 symbol (str): the name to describe
1170 Returns:
1171 str: paragraph or empty string
1173 desc = ''
1175 if False: # NEW FEATURE: needs configurability
1176 sstr = uri_escape(symbol)
1177 mstr = uri_escape(MODULE)
1178 desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink>
1179 <ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s">edit documentation</ulink>
1180 ''' % (sstr, mstr, sstr)
1182 return desc
1185 def OutputSectionExtraLinks(symbol, docsymbol):
1186 desc = ''
1188 if False: # NEW FEATURE: needs configurability
1189 sstr = uri_escape(symbol)
1190 mstr = uri_escape(MODULE)
1191 dsstr = uri_escape(docsymbol)
1192 desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink>
1193 <ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s">edit documentation</ulink>
1194 ''' % (sstr, mstr, dsstr)
1195 return desc
1198 def OutputMacro(symbol, declaration):
1199 """Returns the synopsis and detailed description of a macro.
1201 Args:
1202 symbol (str): the macro name.
1203 declaration (str): the declaration of the macro.
1205 Returns:
1206 str: the formated docs
1208 sid = common.CreateValidSGMLID(symbol)
1209 condition = MakeConditionDescription(symbol)
1210 synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link>" % (
1211 sid, symbol)
1213 fields = common.ParseMacroDeclaration(declaration, CreateValidSGML)
1214 title = symbol
1215 if len(fields) > 0:
1216 title += '()'
1218 desc = '<refsect2 id="%s" role="macro"%s>\n<title>%s</title>\n' % (sid, condition, title)
1219 desc += MakeIndexterms(symbol, sid)
1220 desc += "\n"
1221 desc += OutputSymbolExtraLinks(symbol)
1223 if len(fields) > 0:
1224 synop += "<phrase role=\"c_punctuation\">()</phrase>"
1226 synop += "</entry></row>\n"
1228 # Don't output the macro definition if is is a conditional macro or it
1229 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1230 # longer than 2 lines, otherwise we get lots of complicated macros like
1231 # g_assert.
1232 if symbol not in DeclarationConditional and not symbol.startswith('g_') \
1233 and not re.search(r'^_?gnome_', symbol) and declaration.count('\n') < 2:
1234 decl_out = CreateValidSGML(declaration)
1235 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1236 else:
1237 desc += "<programlisting language=\"C\">" + "#define".ljust(RETURN_TYPE_FIELD_WIDTH) + symbol
1238 m = re.search(r'^\s*#\s*define\s+\w+(\([^\)]*\))', declaration)
1239 if m:
1240 args = m.group(1)
1241 pad = ' ' * (RETURN_TYPE_FIELD_WIDTH - len("#define "))
1242 # Align each line so that if should all line up OK.
1243 args = args.replace('\n', '\n' + pad)
1244 desc += CreateValidSGML(args)
1246 desc += "</programlisting>\n"
1248 desc += MakeDeprecationNote(symbol)
1250 parameters = OutputParamDescriptions("MACRO", symbol, fields)
1252 if symbol in SymbolDocs:
1253 symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
1254 desc += symbol_docs
1256 desc += parameters
1257 desc += OutputSymbolTraits(symbol)
1258 desc += "</refsect2>\n"
1259 return (synop, desc)
1262 def OutputTypedef(symbol, declaration):
1263 """Returns the synopsis and detailed description of a typedef.
1265 Args:
1266 symbol (str): the typedef.
1267 declaration (str): the declaration of the typedef,
1268 e.g. 'typedef unsigned int guint;'
1270 Returns:
1271 str: the formated docs
1273 sid = common.CreateValidSGMLID(symbol)
1274 condition = MakeConditionDescription(symbol)
1275 desc = "<refsect2 id=\"%s\" role=\"typedef\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
1276 synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1277 sid, symbol)
1279 desc += MakeIndexterms(symbol, sid)
1280 desc += "\n"
1281 desc += OutputSymbolExtraLinks(symbol)
1283 if symbol in DeclarationConditional:
1284 decl_out = CreateValidSGML(declaration)
1285 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1287 desc += MakeDeprecationNote(symbol)
1289 if symbol in SymbolDocs:
1290 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1292 desc += OutputSymbolTraits(symbol)
1293 desc += "</refsect2>\n"
1294 return (synop, desc)
1297 def OutputStruct(symbol, declaration):
1298 """Returns the synopsis and detailed description of a struct.
1300 We check if it is a object struct, and if so we only output parts of it that
1301 are noted as public fields. We also use a different IDs for object structs,
1302 since the original ID is used for the entire RefEntry.
1304 Args:
1305 symbol (str): the struct.
1306 declaration (str): the declaration of the struct.
1308 Returns:
1309 str: the formated docs
1311 is_gtype = False
1312 default_to_public = True
1313 if CheckIsObject(symbol):
1314 logging.info("Found struct gtype: %s", symbol)
1315 is_gtype = True
1316 default_to_public = ObjectRoots[symbol] == 'GBoxed'
1318 sid = None
1319 condition = None
1320 if is_gtype:
1321 sid = common.CreateValidSGMLID(symbol + "_struct")
1322 condition = MakeConditionDescription(symbol + "_struct")
1323 else:
1324 sid = common.CreateValidSGMLID(symbol)
1325 condition = MakeConditionDescription(symbol)
1327 # Determine if it is a simple struct or it also has a typedef.
1328 has_typedef = False
1329 if symbol in StructHasTypedef or re.search(r'^\s*typedef\s+', declaration):
1330 has_typedef = True
1332 type_output = None
1333 desc = None
1334 if has_typedef:
1335 # For structs with typedefs we just output the struct name.
1336 type_output = ''
1337 desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
1338 else:
1339 type_output = "struct"
1340 desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>struct %s</title>\n" % (sid, condition, symbol)
1342 synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1343 type_output, sid, symbol)
1345 desc += MakeIndexterms(symbol, sid)
1346 desc += "\n"
1347 desc += OutputSymbolExtraLinks(symbol)
1349 # Form a pretty-printed, private-data-removed form of the declaration
1351 decl_out = ''
1352 if re.search(r'^\s*$', declaration):
1353 logging.info("Found opaque struct: %s", symbol)
1354 decl_out = "typedef struct _%s %s;" % (symbol, symbol)
1355 elif re.search(r'^\s*struct\s+\w+\s*;\s*$', declaration):
1356 logging.info("Found opaque struct: %s", symbol)
1357 decl_out = "struct %s;" % symbol
1358 else:
1359 m = re.search(
1360 r'^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$', declaration, flags=re.S)
1361 if m:
1362 struct_contents = m.group(2)
1364 public = default_to_public
1365 new_declaration = ''
1367 for decl_line in struct_contents.splitlines():
1368 logging.info("Struct line: %s", decl_line)
1369 m2 = re.search(r'/\*\s*<\s*public\s*>\s*\*/', decl_line)
1370 m3 = re.search(r'/\*\s*<\s*(private|protected)\s*>\s*\*/', decl_line)
1371 if m2:
1372 public = True
1373 elif m3:
1374 public = False
1375 elif public:
1376 new_declaration += decl_line + "\n"
1378 if new_declaration:
1379 # Strip any blank lines off the ends.
1380 new_declaration = re.sub(r'^\s*\n', '', new_declaration)
1381 new_declaration = re.sub(r'\n\s*$', r'\n', new_declaration)
1383 if has_typedef:
1384 decl_out = "typedef struct {\n%s} %s;\n" % (new_declaration, symbol)
1385 else:
1386 decl_out = "struct %s {\n%s};\n" % (symbol, new_declaration)
1388 else:
1389 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1390 "Couldn't parse struct:\n%s" % declaration)
1392 # If we couldn't parse the struct or it was all private, output an
1393 # empty struct declaration.
1394 if decl_out == '':
1395 if has_typedef:
1396 decl_out = "typedef struct _%s %s;" % (symbol, symbol)
1397 else:
1398 decl_out = "struct %s;" % symbol
1400 decl_out = CreateValidSGML(decl_out)
1401 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1403 desc += MakeDeprecationNote(symbol)
1405 if symbol in SymbolDocs:
1406 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1408 # Create a table of fields and descriptions
1410 # FIXME: Inserting &#160's into the produced type declarations here would
1411 # improve the output in most situations ... except for function
1412 # members of structs!
1413 def pfunc(*args):
1414 return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), args[0])
1415 fields = common.ParseStructDeclaration(declaration, not default_to_public, 0, MakeXRef, pfunc)
1416 params = SymbolParams.get(symbol)
1418 # If no parameters are filled in, we don't generate the description
1419 # table, for backwards compatibility.
1420 found = False
1421 if params:
1422 found = next((True for p in params.values() if p.strip() != ''), False)
1424 if found:
1425 field_descrs = params
1426 missing_parameters = ''
1427 unused_parameters = ''
1428 sid = common.CreateValidSGMLID(symbol + ".members")
1430 desc += '''<refsect3 id="%s" role="struct_members">\n<title>Members</title>
1431 <informaltable role="struct_members_table" pgwide="1" frame="none">
1432 <tgroup cols="3">
1433 <colspec colname="struct_members_name" colwidth="300px"/>
1434 <colspec colname="struct_members_description"/>
1435 <colspec colname="struct_members_annotations" colwidth="200px"/>
1436 <tbody>
1437 ''' % sid
1439 for field_name, text in iteritems(fields):
1440 param_annotations = ''
1442 desc += "<row role=\"member\"><entry role=\"struct_member_name\"><para>%s</para></entry>\n" % text
1443 if field_name in field_descrs:
1444 (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descrs[field_name])
1445 field_descr = ConvertMarkDown(symbol, field_descr)
1446 # trim
1447 field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M | re.S)
1448 field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M | re.S)
1449 desc += "<entry role=\"struct_member_description\">%s</entry>\n<entry role=\"struct_member_annotations\">%s</entry>\n" % (
1450 field_descr, param_annotations)
1451 del field_descrs[field_name]
1452 else:
1453 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1454 "Field description for %s::%s is missing in source code comment block." % (symbol, field_name))
1455 if missing_parameters != '':
1456 missing_parameters += ", " + field_name
1457 else:
1458 missing_parameters = field_name
1460 desc += "<entry /><entry />\n"
1462 desc += "</row>\n"
1464 desc += "</tbody></tgroup></informaltable>\n</refsect3>\n"
1465 for field_name in field_descrs:
1466 # Documenting those standard fields is not required anymore, but
1467 # we don't want to warn if they are documented anyway.
1468 m = re.search(r'(g_iface|parent_instance|parent_class)', field_name)
1469 if m:
1470 continue
1472 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1473 "Field description for %s::%s is not used from source code comment block." % (symbol, field_name))
1474 if unused_parameters != '':
1475 unused_parameters += ", " + field_name
1476 else:
1477 unused_parameters = field_name
1479 # remember missing/unused parameters (needed in tmpl-free build)
1480 if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
1481 AllIncompleteSymbols[symbol] = missing_parameters
1483 if unused_parameters != '' and (symbol not in AllUnusedSymbols):
1484 AllUnusedSymbols[symbol] = unused_parameters
1485 else:
1486 if fields:
1487 if symbol not in AllIncompleteSymbols:
1488 AllIncompleteSymbols[symbol] = "<items>"
1489 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1490 "Field descriptions for struct %s are missing in source code comment block." % symbol)
1491 logging.info("Remaining structs fields: " + ':'.join(fields) + "\n")
1493 desc += OutputSymbolTraits(symbol)
1494 desc += "</refsect2>\n"
1495 return (synop, desc)
1498 def OutputUnion(symbol, declaration):
1499 """Returns the synopsis and detailed description of a union.
1501 Args:
1502 symbol (str): the union.
1503 declaration (str): the declaration of the union.
1505 Returns:
1506 str: the formated docs
1508 is_gtype = False
1509 if CheckIsObject(symbol):
1510 logging.info("Found union gtype: %s", symbol)
1511 is_gtype = True
1513 sid = None
1514 condition = None
1515 if is_gtype:
1516 sid = common.CreateValidSGMLID(symbol + "_union")
1517 condition = MakeConditionDescription(symbol + "_union")
1518 else:
1519 sid = common.CreateValidSGMLID(symbol)
1520 condition = MakeConditionDescription(symbol)
1522 # Determine if it is a simple struct or it also has a typedef.
1523 has_typedef = False
1524 if symbol in StructHasTypedef or re.search(r'^\s*typedef\s+', declaration):
1525 has_typedef = True
1527 type_output = None
1528 desc = None
1529 if has_typedef:
1530 # For unions with typedefs we just output the union name.
1531 type_output = ''
1532 desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
1533 else:
1534 type_output = "union"
1535 desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>union %s</title>\n" % (sid, condition, symbol)
1537 synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1538 type_output, sid, symbol)
1540 desc += MakeIndexterms(symbol, sid)
1541 desc += "\n"
1542 desc += OutputSymbolExtraLinks(symbol)
1543 desc += MakeDeprecationNote(symbol)
1545 if symbol in SymbolDocs:
1546 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1548 # Create a table of fields and descriptions
1550 # FIXME: Inserting &#160's into the produced type declarations here would
1551 # improve the output in most situations ... except for function
1552 # members of structs!
1553 def pfunc(*args):
1554 return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), args[0])
1555 fields = common.ParseStructDeclaration(declaration, 0, 0, MakeXRef, pfunc)
1556 params = SymbolParams.get(symbol)
1558 # If no parameters are filled in, we don't generate the description
1559 # table, for backwards compatibility
1560 found = False
1561 if params:
1562 found = next((True for p in params.values() if p.strip() != ''), False)
1564 logging.debug('Union %s has %d entries, found=%d, has_typedef=%d', symbol, len(fields), found, has_typedef)
1566 if found:
1567 field_descrs = params
1568 missing_parameters = ''
1569 unused_parameters = ''
1570 sid = common.CreateValidSGMLID('%s.members' % symbol)
1572 desc += '''<refsect3 id="%s" role="union_members">\n<title>Members</title>
1573 <informaltable role="union_members_table" pgwide="1" frame="none">
1574 <tgroup cols="3">
1575 <colspec colname="union_members_name" colwidth="300px"/>
1576 <colspec colname="union_members_description"/>
1577 <colspec colname="union_members_annotations" colwidth="200px"/>
1578 <tbody>
1579 ''' % sid
1581 for field_name, text in iteritems(fields):
1582 param_annotations = ''
1584 desc += "<row><entry role=\"union_member_name\"><para>%s</para></entry>\n" % text
1585 if field_name in field_descrs:
1586 (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descrs[field_name])
1587 field_descr = ConvertMarkDown(symbol, field_descr)
1589 # trim
1590 field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M | re.S)
1591 field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M | re.S)
1592 desc += "<entry role=\"union_member_description\">%s</entry>\n<entry role=\"union_member_annotations\">%s</entry>\n" % (
1593 field_descr, param_annotations)
1594 del field_descrs[field_name]
1595 else:
1596 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1597 "Field description for %s::%s is missing in source code comment block." % (symbol, field_name))
1598 if missing_parameters != '':
1599 missing_parameters += ", " + field_name
1600 else:
1601 missing_parameters = field_name
1603 desc += "<entry /><entry />\n"
1605 desc += "</row>\n"
1607 desc += "</tbody></tgroup></informaltable>\n</refsect3>"
1608 for field_name in field_descrs:
1609 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1610 "Field description for %s::%s is not used from source code comment block." % (symbol, field_name))
1611 if unused_parameters != '':
1612 unused_parameters += ", " + field_name
1613 else:
1614 unused_parameters = field_name
1616 # remember missing/unused parameters (needed in tmpl-free build)
1617 if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
1618 AllIncompleteSymbols[symbol] = missing_parameters
1620 if unused_parameters != '' and (symbol not in AllUnusedSymbols):
1621 AllUnusedSymbols[symbol] = unused_parameters
1622 else:
1623 if len(fields) > 0:
1624 if symbol not in AllIncompleteSymbols:
1625 AllIncompleteSymbols[symbol] = "<items>"
1626 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1627 "Field descriptions for union %s are missing in source code comment block." % symbol)
1628 logging.info("Remaining union fields: " + ':'.join(fields) + "\n")
1630 desc += OutputSymbolTraits(symbol)
1631 desc += "</refsect2>\n"
1632 return (synop, desc)
1635 def OutputEnum(symbol, declaration):
1636 """Returns the synopsis and detailed description of a enum.
1638 Args:
1639 symbol (str): the enum.
1640 declaration (str): the declaration of the enum.
1642 Returns:
1643 str: the formated docs
1645 is_gtype = False
1646 if CheckIsObject(symbol):
1647 logging.info("Found enum gtype: %s", symbol)
1648 is_gtype = True
1650 sid = None
1651 condition = None
1652 if is_gtype:
1653 sid = common.CreateValidSGMLID(symbol + "_enum")
1654 condition = MakeConditionDescription(symbol + "_enum")
1655 else:
1656 sid = common.CreateValidSGMLID(symbol)
1657 condition = MakeConditionDescription(symbol)
1659 synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1660 sid, symbol)
1661 desc = "<refsect2 id=\"%s\" role=\"enum\"%s>\n<title>enum %s</title>\n" % (sid, condition, symbol)
1663 desc += MakeIndexterms(symbol, sid)
1664 desc += "\n"
1665 desc += OutputSymbolExtraLinks(symbol)
1666 desc += MakeDeprecationNote(symbol)
1668 if symbol in SymbolDocs:
1669 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1671 # Create a table of fields and descriptions
1673 fields = common.ParseEnumDeclaration(declaration)
1674 params = SymbolParams.get(symbol)
1676 # If nothing at all is documented log a single summary warning at the end.
1677 # Otherwise, warn about each undocumented item.
1679 found = False
1680 if params:
1681 found = next((True for p in params.values() if p.strip() != ''), False)
1682 field_descrs = params
1683 else:
1684 field_descrs = {}
1686 missing_parameters = ''
1687 unused_parameters = ''
1689 sid = common.CreateValidSGMLID("%s.members" % symbol)
1690 desc += '''<refsect3 id="%s" role="enum_members">\n<title>Members</title>
1691 <informaltable role="enum_members_table" pgwide="1" frame="none">
1692 <tgroup cols="3">
1693 <colspec colname="enum_members_name" colwidth="300px"/>
1694 <colspec colname="enum_members_description"/>
1695 <colspec colname="enum_members_annotations" colwidth="200px"/>
1696 <tbody>
1697 ''' % sid
1699 for field_name in fields:
1700 field_descr = field_descrs.get(field_name)
1701 param_annotations = ''
1703 sid = common.CreateValidSGMLID(field_name)
1704 condition = MakeConditionDescription(field_name)
1705 desc += "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"%s\">%s</para></entry>\n" % (
1706 sid, field_name)
1707 if field_descr:
1708 field_descr, param_annotations = ExpandAnnotation(symbol, field_descr)
1709 field_descr = ConvertMarkDown(symbol, field_descr)
1710 desc += "<entry role=\"enum_member_description\">%s</entry>\n<entry role=\"enum_member_annotations\">%s</entry>\n" % (
1711 field_descr, param_annotations)
1712 del field_descrs[field_name]
1713 else:
1714 if found:
1715 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1716 "Value description for %s::%s is missing in source code comment block." % (symbol, field_name))
1717 if missing_parameters != '':
1718 missing_parameters += ", " + field_name
1719 else:
1720 missing_parameters = field_name
1721 desc += "<entry /><entry />\n"
1722 desc += "</row>\n"
1724 desc += "</tbody></tgroup></informaltable>\n</refsect3>"
1725 for field_name in field_descrs:
1726 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1727 "Value description for %s::%s is not used from source code comment block." % (symbol, field_name))
1728 if unused_parameters != '':
1729 unused_parameters += ", " + field_name
1730 else:
1731 unused_parameters = field_name
1733 # remember missing/unused parameters (needed in tmpl-free build)
1734 if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
1735 AllIncompleteSymbols[symbol] = missing_parameters
1737 if unused_parameters != '' and (symbol not in AllUnusedSymbols):
1738 AllUnusedSymbols[symbol] = unused_parameters
1740 if not found:
1741 if len(fields) > 0:
1742 if symbol not in AllIncompleteSymbols:
1743 AllIncompleteSymbols[symbol] = "<items>"
1744 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1745 "Value descriptions for %s are missing in source code comment block." % symbol)
1747 desc += OutputSymbolTraits(symbol)
1748 desc += "</refsect2>\n"
1749 return (synop, desc)
1752 def OutputVariable(symbol, declaration):
1753 """Returns the synopsis and detailed description of a variable.
1755 Args:
1756 symbol (str): the extern'ed variable.
1757 declaration (str): the declaration of the variable.
1759 Returns:
1760 str: the formated docs
1762 sid = common.CreateValidSGMLID(symbol)
1763 condition = MakeConditionDescription(symbol)
1765 logging.info("ouputing variable: '%s' '%s'", symbol, declaration)
1767 type_output = None
1768 m1 = re.search(
1769 r'^\s*extern\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*;', declaration)
1770 m2 = re.search(
1771 r'\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=', declaration)
1772 if m1:
1773 mod1 = m1.group(1) or ''
1774 ptr = m1.group(3) or ''
1775 space = m1.group(4) or ''
1776 mod2 = m1.group(5) or ''
1777 type_output = "extern %s%s%s%s" % (mod1, ptr, space, mod2)
1778 elif m2:
1779 mod1 = m2.group(1) or ''
1780 ptr = m2.group(3) or ''
1781 space = m2.group(4) or ''
1782 mod2 = m2.group(5) or ''
1783 type_output = '%s%s%s%s' % (mod1, ptr, space, mod2)
1784 else:
1785 type_output = "extern"
1787 synop = "<row><entry role=\"variable_type\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1788 type_output, sid, symbol)
1790 desc = "<refsect2 id=\"%s\" role=\"variable\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
1792 desc += MakeIndexterms(symbol, sid)
1793 desc += "\n"
1794 desc += OutputSymbolExtraLinks(symbol)
1796 decl_out = CreateValidSGML(declaration)
1797 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1799 desc += MakeDeprecationNote(symbol)
1801 if symbol in SymbolDocs:
1802 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1804 if symbol in SymbolAnnotations:
1805 param_desc = SymbolAnnotations[symbol]
1806 param_annotations = ''
1807 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
1808 if param_annotations != '':
1809 desc += "\n<para>%s</para>" % param_annotations
1811 desc += OutputSymbolTraits(symbol)
1812 desc += "</refsect2>\n"
1813 return (synop, desc)
1816 def OutputFunction(symbol, declaration, symbol_type):
1817 """Returns the synopsis and detailed description of a function.
1819 Args:
1820 symbol (str): the function.
1821 declaration (str): the declaration of the function.
1823 Returns:
1824 str: the formated docs
1826 sid = common.CreateValidSGMLID(symbol)
1827 condition = MakeConditionDescription(symbol)
1829 # Take out the return type
1830 # $1 $2 $3
1831 regex = r'<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n'
1832 m = re.search(regex, declaration)
1833 declaration = re.sub(regex, '', declaration)
1834 type_modifier = m.group(1) or ''
1835 type = m.group(2)
1836 pointer = m.group(3)
1837 pointer = pointer.rstrip()
1838 xref = MakeXRef(type, tagify(type, "returnvalue"))
1839 start = ''
1840 # if (symbol_type == 'USER_FUNCTION')
1841 # start = "typedef "
1844 # We output const rather than G_CONST_RETURN.
1845 type_modifier = re.sub(r'G_CONST_RETURN', 'const', type_modifier)
1846 pointer = re.sub(r'G_CONST_RETURN', 'const', pointer)
1847 pointer = re.sub(r'^\s+', '&#160;', pointer)
1849 ret_type_output = "%s%s%s%s\n" % (start, type_modifier, xref, pointer)
1851 indent_len = len(symbol) + 2
1852 char1 = char2 = char3 = ''
1853 if symbol_type == 'USER_FUNCTION':
1854 indent_len += 3
1855 char1 = "<phrase role=\"c_punctuation\">(</phrase>"
1856 char2 = "*"
1857 char3 = "<phrase role=\"c_punctuation\">)</phrase>"
1859 symbol_output = "%s<link linkend=\"%s\">%s%s</link>%s" % (char1, sid, char2, symbol, char3)
1860 if indent_len < MAX_SYMBOL_FIELD_WIDTH:
1861 symbol_desc_output = "%s%s%s%s " % (char1, char2, symbol, char3)
1862 else:
1863 indent_len = MAX_SYMBOL_FIELD_WIDTH - 8
1864 symbol_desc_output = ('%s%s%s%s\n' % (char1, char2, symbol, char3)) + (' ' * (indent_len - 1))
1866 synop = "<row><entry role=\"function_type\">%s</entry><entry role=\"function_name\">%s&#160;<phrase role=\"c_punctuation\">()</phrase></entry></row>\n" % (
1867 ret_type_output, symbol_output)
1869 desc = "<refsect2 id=\"%s\" role=\"function\"%s>\n<title>%s&#160;()</title>\n" % (sid, condition, symbol)
1871 desc += MakeIndexterms(symbol, sid)
1872 desc += "\n"
1873 desc += OutputSymbolExtraLinks(symbol)
1875 desc += "<programlisting language=\"C\">%s%s(" % (ret_type_output, symbol_desc_output)
1877 def tagfun(*args):
1878 return tagify(args[0], "parameter")
1880 fields = common.ParseFunctionDeclaration(declaration, MakeXRef, tagfun)
1882 first = True
1883 for field_name in fields.values():
1884 if first:
1885 desc += field_name
1886 first = False
1887 else:
1888 desc += ",\n" + (' ' * indent_len) + field_name
1890 desc += ");</programlisting>\n"
1892 desc += MakeDeprecationNote(symbol)
1894 if symbol in SymbolDocs:
1895 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1897 if symbol in SymbolAnnotations:
1898 param_desc = SymbolAnnotations[symbol]
1899 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
1900 if param_annotations != '':
1901 desc += "\n<para>%s</para>" % param_annotations
1903 desc += OutputParamDescriptions("FUNCTION", symbol, fields.keys())
1904 desc += OutputSymbolTraits(symbol)
1905 desc += "</refsect2>\n"
1906 return (synop, desc)
1909 def OutputParamDescriptions(symbol_type, symbol, fields):
1910 """Returns the DocBook output describing the parameters of a symbol.
1912 This can be used for functions, macros or signal handlers.
1914 Args:
1915 symbol_type (str): 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
1916 handlers have an implicit user_data parameter last.
1917 symbol (str): the name of the symbol being described.
1918 fields (list): parsed fields from the declaration, used to determine
1919 undocumented/unused entries
1921 Returns:
1922 str: the formated parameter docs
1924 output = ''
1925 num_params = 0
1926 field_descrs = None
1928 if fields:
1929 field_descrs = [f for f in fields if f not in ['void', 'Returns']]
1930 else:
1931 field_descrs = []
1933 params = SymbolParams.get(symbol)
1934 logging.info("param_desc(%s, %s) = %s", symbol_type, symbol, str(params))
1935 # This might be an empty dict, but for SIGNALS we append the user_data docs.
1936 # TODO(ensonic): maybe create that docstring in GetSignals()
1937 if params is not None:
1938 returns = ''
1939 params_desc = ''
1940 missing_parameters = ''
1941 unused_parameters = ''
1943 for param_name, param_desc in iteritems(params):
1944 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
1945 param_desc = ConvertMarkDown(symbol, param_desc)
1946 # trim
1947 param_desc = re.sub(r'^(\s|\n)+', '', param_desc, flags=re.M | re.S)
1948 param_desc = re.sub(r'(\s|\n)+$', '', param_desc, flags=re.M | re.S)
1949 if param_name == "Returns":
1950 returns = param_desc
1951 if param_annotations != '':
1952 returns += "\n<para>%s</para>" % param_annotations
1954 elif param_name == "void":
1955 # FIXME: &common.LogWarning()?
1956 logging.info("!!!! void in params for %s?\n", symbol)
1957 else:
1958 if fields:
1959 if param_name not in field_descrs:
1960 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1961 "Parameter description for %s::%s is not used from source code comment block." % (symbol, param_name))
1962 if unused_parameters != '':
1963 unused_parameters += ", " + param_name
1964 else:
1965 unused_parameters = param_name
1966 else:
1967 field_descrs.remove(param_name)
1969 if param_desc != '':
1970 params_desc += "<row><entry role=\"parameter_name\"><para>%s</para></entry>\n<entry role=\"parameter_description\">%s</entry>\n<entry role=\"parameter_annotations\">%s</entry></row>\n" % (
1971 param_name, param_desc, param_annotations)
1972 num_params += 1
1974 for param_name in field_descrs:
1975 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1976 "Parameter description for %s::%s is missing in source code comment block." % (symbol, param_name))
1977 if missing_parameters != '':
1978 missing_parameters += ", " + param_name
1979 else:
1980 missing_parameters = param_name
1982 # Signals have an implicit user_data parameter which we describe.
1983 if symbol_type == "SIGNAL":
1984 params_desc += "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry role=\"parameter_description\"><simpara>user data set when the signal handler was connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n"
1986 # Start a table if we need one.
1987 if params_desc != '':
1988 sid = common.CreateValidSGMLID("%s.parameters" % symbol)
1990 output += '''<refsect3 id="%s" role="parameters">\n<title>Parameters</title>
1991 <informaltable role="parameters_table" pgwide="1" frame="none">
1992 <tgroup cols="3">
1993 <colspec colname="parameters_name" colwidth="150px"/>
1994 <colspec colname="parameters_description"/>
1995 <colspec colname="parameters_annotations" colwidth="200px"/>
1996 <tbody>
1997 ''' % sid
1998 output += params_desc
1999 output += "</tbody></tgroup></informaltable>\n</refsect3>"
2001 # Output the returns info last
2002 if returns != '':
2003 sid = common.CreateValidSGMLID("%s.returns" % symbol)
2005 output += '''<refsect3 id="%s" role=\"returns\">\n<title>Returns</title>
2006 ''' % sid
2007 output += returns
2008 output += "\n</refsect3>"
2010 # remember missing/unused parameters (needed in tmpl-free build)
2011 if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
2012 AllIncompleteSymbols[symbol] = missing_parameters
2014 if unused_parameters != '' and (symbol not in AllUnusedSymbols):
2015 AllUnusedSymbols[symbol] = unused_parameters
2017 if num_params == 0 and fields and field_descrs:
2018 if symbol not in AllIncompleteSymbols:
2019 AllIncompleteSymbols[symbol] = "<parameters>"
2020 return output
2023 def ParseStabilityLevel(stability, file, line, message):
2024 """Parses a stability level and outputs a warning if it isn't valid.
2025 Args:
2026 stability (str): the stability text.
2027 file, line: context for error message
2028 message: description of where the level is from, to use in any error message.
2029 Returns:
2030 str: the parsed stability level string.
2032 sl = stability.strip().lower()
2033 if sl == 'stable':
2034 stability = "Stable"
2035 elif sl == 'unstable':
2036 stability = "Unstable"
2037 elif sl == 'private':
2038 stability = "Private"
2039 else:
2040 common.LogWarning(file, line, "%s is %s." % (message, stability) +
2041 "It should be one of these: Stable, Unstable, or Private.")
2042 return stability
2045 def OutputDBFile(file, title, section_id, includes, functions_synop, other_synop, functions_details, other_details, signals_synop, signals_desc, args_synop, args_desc, hierarchy, interfaces, implementations, prerequisites, derived, file_objects):
2046 """Outputs the final DocBook file for one section.
2048 Args:
2049 file (str): the name of the file.
2050 title (str): the title from the $MODULE-sections.txt file
2051 section_id (str): the id to use for the toplevel tag.
2052 includes (str): comma-separates list of include files added at top of
2053 synopsis, with '<' '>' around them (if not already enclosed in '').
2054 functions_synop (str): the DocBook for the Functions Synopsis part.
2055 other_synop (str): the DocBook for the Types and Values Synopsis part.
2056 functions_details (str): the DocBook for the Functions Details part.
2057 other_details (str): the DocBook for the Types and Values Details part.
2058 signal_synop (str): the DocBook for the Signal Synopsis part
2059 signal_desc (str): the DocBook for the Signal Description part
2060 args_synop (str): the DocBook for the Arg Synopsis part
2061 args_desc (str): the DocBook for the Arg Description part
2062 hierarchy (str): the DocBook for the Object Hierarchy part
2063 interfaces (str): the DocBook for the Interfaces part
2064 implementations (str): the DocBook for the Known Implementations part
2065 prerequisites (str): the DocBook for the Prerequisites part
2066 derived (str): the DocBook for the Derived Interfaces part
2067 file_objects (list): objects in this file
2069 Returns:
2070 bool: True if the docs where updated
2073 logging.info("Output docbook for file %s with title '%s'", file, title)
2075 # The edited title overrides the one from the sections file.
2076 new_title = SymbolDocs.get(file + ":Title")
2077 if new_title and not new_title.strip() == '':
2078 title = new_title
2079 logging.info("Found title: %s", title)
2081 short_desc = SymbolDocs.get(file + ":Short_Description")
2082 if not short_desc or short_desc.strip() == '':
2083 short_desc = ''
2084 else:
2085 # Don't use ConvertMarkDown here for now since we don't want blocks
2086 short_desc = ExpandAbbreviations(title + ":Short_description", short_desc)
2087 logging.info("Found short_desc: %s", short_desc)
2089 long_desc = SymbolDocs.get(file + ":Long_Description")
2090 if not long_desc or long_desc.strip() == '':
2091 long_desc = ''
2092 else:
2093 long_desc = ConvertMarkDown(title + ":Long_description", long_desc)
2094 logging.info("Found long_desc: %s", long_desc)
2096 see_also = SymbolDocs.get(file + ":See_Also")
2097 if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
2098 see_also = ''
2099 else:
2100 see_also = ConvertMarkDown(title + ":See_Also", see_also)
2101 logging.info("Found see_also: %s", see_also)
2103 if see_also:
2104 see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id, see_also)
2106 stability = SymbolDocs.get(file + ":Stability_Level")
2107 if not stability or re.search(r'^\s*$', stability):
2108 stability = ''
2109 else:
2110 line_number = GetSymbolSourceLine(file + ":Stability_Level")
2111 stability = ParseStabilityLevel(stability, file, line_number, "Section stability level")
2112 logging.info("Found stability: %s", stability)
2114 if stability:
2115 AnnotationsUsed[stability] = 1
2116 stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (
2117 section_id, stability)
2118 elif DEFAULT_STABILITY:
2119 AnnotationsUsed[DEFAULT_STABILITY] = 1
2120 stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (
2121 section_id, DEFAULT_STABILITY)
2123 image = SymbolDocs.get(file + ":Image")
2124 if not image or re.search(r'^\s*$', image):
2125 image = ''
2126 else:
2127 image = image.strip()
2129 format = None
2131 il = image.lower()
2132 if re.search(r'jpe?g$', il):
2133 format = "format='JPEG'"
2134 elif il.endswith('png'):
2135 format = "format='PNG'"
2136 elif il.endswith('svg'):
2137 format = "format='SVG'"
2138 else:
2139 format = ''
2141 image = " <inlinegraphic fileref='%s' %s/>\n" % (image, format)
2143 include_output = ''
2144 if includes:
2145 include_output += "<refsect1 id=\"%s.includes\"><title>Includes</title><synopsis>" % section_id
2146 for include in includes.split(','):
2147 if re.search(r'^\".+\"$', include):
2148 include_output += "#include %s\n" % include
2149 else:
2150 include = re.sub(r'^\s+|\s+$', '', include, flags=re.S)
2151 include_output += "#include &lt;%s&gt;\n" % include
2153 include_output += "</synopsis></refsect1>\n"
2155 extralinks = OutputSectionExtraLinks(title, "Section:%s" % file)
2157 old_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml')
2158 new_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml.new')
2160 OUTPUT = open(new_db_file, 'w')
2162 object_anchors = ''
2163 for fobject in file_objects:
2164 if fobject == section_id:
2165 continue
2166 sid = common.CreateValidSGMLID(fobject)
2167 logging.info("Adding anchor for %s\n", fobject)
2168 object_anchors += "<anchor id=\"%s\"/>" % sid
2170 # Make sure we produce valid docbook
2171 if not functions_details:
2172 functions_details = "<para />"
2174 # We used to output this, but is messes up our common.UpdateFileIfChanged code
2175 # since it changes every day (and it is only used in the man pages):
2176 # "<refentry id="$section_id" revision="$mday $month $year">"
2178 OUTPUT.write(REFENTRY.substitute({
2179 'args_desc': args_desc,
2180 'args_synop': args_synop,
2181 'derived': derived,
2182 'extralinks': extralinks,
2183 'functions_details': functions_details,
2184 'functions_synop': functions_synop,
2185 'header': MakeDocHeader('refentry'),
2186 'hierarchy': hierarchy,
2187 'image': image,
2188 'include_output': include_output,
2189 'interfaces': interfaces,
2190 'implementations': implementations,
2191 'long_desc': long_desc,
2192 'object_anchors': object_anchors,
2193 'other_details': other_details,
2194 'other_synop': other_synop,
2195 'prerequisites': prerequisites,
2196 'section_id': section_id,
2197 'see_also': see_also,
2198 'signals_desc': signals_desc,
2199 'signals_synop': signals_synop,
2200 'short_desc': short_desc,
2201 'stability': stability,
2202 'title': title,
2203 'MODULE': MODULE.upper(),
2205 OUTPUT.close()
2207 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
2210 def OutputProgramDBFile(program, section_id):
2211 """Outputs the final DocBook file for one program.
2213 Args:
2214 file (str): the name of the file.
2215 section_id (str): the id to use for the toplevel tag.
2217 Returns:
2218 bool: True if the docs where updated
2220 logging.info("Output program docbook for %s", program)
2222 short_desc = SourceSymbolDocs.get(program + ":Short_Description")
2223 if not short_desc or short_desc.strip() == '':
2224 short_desc = ''
2225 else:
2226 # Don't use ConvertMarkDown here for now since we don't want blocks
2227 short_desc = ExpandAbbreviations(program, short_desc)
2228 logging.info("Found short_desc: %s", short_desc)
2230 synopsis = SourceSymbolDocs.get(program + ":Synopsis")
2231 if synopsis and synopsis.strip() != '':
2232 items = synopsis.split(' ')
2233 for i in range(0, len(items)):
2234 parameter = items[i]
2235 choice = "plain"
2236 rep = ''
2238 # first parameter is the command name
2239 if i == 0:
2240 synopsis = "<command>%s</command>\n" % parameter
2241 continue
2243 # square brackets indicate optional parameters, curly brackets
2244 # indicate required parameters ("plain" parameters are also
2245 # mandatory, but do not get extra decoration)
2246 m1 = re.search(r'^\[(.+?)\]$', parameter)
2247 m2 = re.search(r'^\{(.+?)\}$', parameter)
2248 if m1:
2249 choice = "opt"
2250 parameter = m1.group(1)
2251 elif m2:
2252 choice = "req"
2253 parameter = m2.group(1)
2255 # parameters ending in "..." are repeatable
2256 if parameter.endswith('...'):
2257 rep = ' rep=\"repeat\"'
2258 parameter = parameter[:-3]
2260 # italic parameters are replaceable parameters
2261 parameter = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', parameter)
2263 synopsis += "<arg choice=\"%s\"%s>" % (choice, rep)
2264 synopsis += parameter
2265 synopsis += "</arg>\n"
2267 logging.info("Found synopsis: %s", synopsis)
2268 else:
2269 synopsis = "<command>%s</command>" % program
2271 long_desc = SourceSymbolDocs.get(program + ":Long_Description")
2272 if not long_desc or long_desc.strip() == '':
2273 long_desc = ''
2274 else:
2275 long_desc = ConvertMarkDown("%s:Long_description" % program, long_desc)
2276 logging.info("Found long_desc: %s", long_desc)
2278 options = ''
2279 o = program + ":Options"
2280 if o in SourceSymbolDocs:
2281 opts = SourceSymbolDocs[o].split('\t')
2283 logging.info('options: %d, %s', len(opts), str(opts))
2285 options = "<refsect1>\n<title>Options</title>\n<variablelist>\n"
2286 for k in range(0, len(opts), 2):
2287 opt_desc = opts[k + 1]
2289 opt_desc = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', opt_desc)
2291 options += "<varlistentry>\n<term>"
2292 opt_names = opts[k].split(',')
2293 for i in range(len(opt_names)):
2294 prefix = ', ' if i > 0 else ''
2295 # italic parameters are replaceable parameters
2296 opt_name = re.sub(r'\*(.+?)\*', r'<replaceable>\1</replaceable>', opt_names[i])
2298 options += "%s<option>%s</option>\n" % (prefix, opt_name)
2300 options += "</term>\n"
2301 options += "<listitem><para>%s</para></listitem>\n" % opt_desc
2302 options += "</varlistentry>\n"
2304 options += "</variablelist></refsect1>\n"
2306 exit_status = SourceSymbolDocs.get(program + ":Returns")
2307 if exit_status and exit_status != '':
2308 exit_status = ConvertMarkDown("%s:Returns" % program, exit_status)
2309 exit_status = "<refsect1 id=\"%s.exit-status\">\n<title>Exit Status</title>\n%s\n</refsect1>\n" % (
2310 section_id, exit_status)
2311 else:
2312 exit_status = ''
2314 see_also = SourceSymbolDocs.get(program + ":See_Also")
2315 if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
2316 see_also = ''
2317 else:
2318 see_also = ConvertMarkDown("%s:See_Also" % program, see_also)
2319 logging.info("Found see_also: %s", see_also)
2321 if see_also:
2322 see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id, see_also)
2324 old_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml")
2325 new_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml.new")
2327 OUTPUT = open(new_db_file, 'w')
2329 OUTPUT.write('''%s
2330 <refentry id="%s">
2331 <refmeta>
2332 <refentrytitle role="top_of_page" id="%s.top_of_page">%s</refentrytitle>
2333 <manvolnum>1</manvolnum>
2334 <refmiscinfo>User Commands</refmiscinfo>
2335 </refmeta>
2336 <refnamediv>
2337 <refname>%s</refname>
2338 <refpurpose>%s</refpurpose>
2339 </refnamediv>
2340 <refsynopsisdiv>
2341 <cmdsynopsis>%s</cmdsynopsis>
2342 </refsynopsisdiv>
2343 <refsect1 id="%s.description" role="desc">
2344 <title role="desc.title">Description</title>
2346 </refsect1>
2347 %s%s%s
2348 </refentry>
2349 ''' % (MakeDocHeader("refentry"), section_id, section_id, program, program, short_desc, synopsis, section_id, long_desc, options, exit_status, see_also))
2350 OUTPUT.close()
2352 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
2355 def OutputExtraFile(file):
2356 """Copies an "extra" DocBook file into the output directory, expanding abbreviations.
2358 Args:
2359 file (str): the source file.
2361 Returns:
2362 bool: True if the docs where updated
2365 basename = os.path.basename(file)
2367 old_db_file = os.path.join(DB_OUTPUT_DIR, basename)
2368 new_db_file = os.path.join(DB_OUTPUT_DIR, basename + ".new")
2370 contents = open(file).read()
2372 OUTPUT = open(new_db_file, 'w')
2373 OUTPUT.write(ExpandAbbreviations(basename + " file", contents))
2374 OUTPUT.close()
2376 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
2379 def GetDocbookHeader(main_file):
2380 if os.path.exists(main_file):
2381 INPUT = open(main_file)
2382 header = ''
2383 for line in INPUT:
2384 if re.search(r'^\s*<(book|chapter|article)', line):
2385 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
2386 if not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', line) and \
2387 not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', header, flags=re.MULTILINE):
2388 header = ''
2389 break
2391 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
2392 # FIXME: not sure if we can do this now, as people already work-around the problem
2393 # r'#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">', r'<!ENTITY % \1 SYSTEM \"../\2\">';
2394 line = re.sub(
2395 r'<!ENTITY % gtkdocentities SYSTEM "([^"]*)">', r'<!ENTITY % gtkdocentities SYSTEM "../\1">', line)
2396 header += line
2397 INPUT.close()
2398 header = header.strip()
2399 else:
2400 header = '''<?xml version="1.0"?>
2401 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2402 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2404 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
2405 <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
2406 %gtkdocentities;
2407 ]>'''
2408 return header
2411 def OutputBook(main_file, book_top, book_bottom):
2412 """Outputs the entities that need to be included into the main docbook file for the module.
2414 Args:
2415 book_top (str): the declarations of the entities, which are added
2416 at the top of the main docbook file.
2417 book_bottom (str): the entities, which are added in the main docbook
2418 file at the desired position.
2421 old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top")
2422 new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top.new")
2424 OUTPUT = open(new_file, 'w')
2425 OUTPUT.write(book_top)
2426 OUTPUT.close()
2428 common.UpdateFileIfChanged(old_file, new_file, 0)
2430 old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom")
2431 new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom.new")
2433 OUTPUT = open(new_file, 'w')
2434 OUTPUT.write(book_bottom)
2435 OUTPUT.close()
2437 common.UpdateFileIfChanged(old_file, new_file, 0)
2439 # If the main docbook file hasn't been created yet, we create it here.
2440 # The user can tweak it later.
2441 if main_file and not os.path.exists(main_file):
2442 OUTPUT = open(main_file, 'w')
2444 OUTPUT.write('''%s
2445 <book id="index">
2446 <bookinfo>
2447 <title>&package_name; Reference Manual</title>
2448 <releaseinfo>
2449 for &package_string;.
2450 The latest version of this documentation can be found on-line at
2451 <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2452 </releaseinfo>
2453 </bookinfo>
2455 <chapter>
2456 <title>[Insert title here]</title>
2458 </chapter>
2459 ''' % (MakeDocHeader("book"), book_bottom))
2460 if os.path.exists('xml/tree_index.sgml'):
2461 OUTPUT.write(''' <chapter id="object-tree">
2462 <title>Object Hierarchy</title>
2463 <xi:include href="xml/tree_index.sgml"/>
2464 </chapter>
2465 ''')
2466 else:
2467 OUTPUT.write(''' <!-- enable this when you use gobject types
2468 <chapter id="object-tree">
2469 <title>Object Hierarchy</title>
2470 <xi:include href="xml/tree_index.sgml"/>
2471 </chapter>
2473 ''')
2475 OUTPUT.write(''' <index id="api-index-full">
2476 <title>API Index</title>
2477 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2478 </index>
2479 <index id="deprecated-api-index" role="deprecated">
2480 <title>Index of deprecated API</title>
2481 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2482 </index>
2483 ''')
2484 if AnnotationsUsed:
2485 OUTPUT.write(''' <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2486 ''')
2487 else:
2488 OUTPUT.write(''' <!-- enable this when you use gobject introspection annotations
2489 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2491 ''')
2493 OUTPUT.write('''</book>
2494 ''')
2496 OUTPUT.close()
2499 def CreateValidSGML(text):
2500 """Turn any chars which are used in XML into entities.
2502 e.g. '<' into '&lt;'
2504 Args:
2505 text (str): the text to turn into proper XML.
2507 Returns:
2508 str: escaped input
2511 text = re.sub(r'&', r'&amp;', text) # Do this first, or the others get messed up.
2512 text = re.sub(r'<', r'&lt;', text)
2513 text = re.sub(r'>', r'&gt;', text)
2514 # browsers render single tabs inconsistently
2515 text = re.sub(r'([^\s])\t([^\s])', r'\1&#160;\2', text)
2516 return text
2519 def ConvertSGMLChars(symbol, text):
2520 """Escape XML chars.
2522 This is used for text in source code comment blocks, to turn
2523 chars which are used in XML into entities, e.g. '<' into
2524 &lt;'. Depending on INLINE_MARKUP_MODE, this is done
2525 unconditionally or only if the character doesn't seem to be
2526 part of an XML construct (tag or entity reference).
2527 Args:
2528 text (str): the text to turn into proper XML.
2530 Returns:
2531 str: escaped input
2534 if INLINE_MARKUP_MODE:
2535 # For the XML/SGML mode only convert to entities outside CDATA sections.
2536 return ModifyXMLElements(text, symbol,
2537 "<!\\[CDATA\\[|<programlisting[^>]*>",
2538 ConvertSGMLCharsEndTag,
2539 ConvertSGMLCharsCallback)
2540 # For the simple non-sgml mode, convert to entities everywhere.
2542 # First, convert freestanding & to &amp
2543 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)
2544 text = re.sub(r'<', r'&lt;', text)
2545 # Allow '>' at beginning of string for blockquote markdown
2546 text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text)
2548 return text
2551 def ConvertSGMLCharsEndTag(t):
2552 if t == '<![CDATA[':
2553 return "]]>"
2554 return "</programlisting>"
2557 def ConvertSGMLCharsCallback(text, symbol, tag):
2558 if re.search(r'^<programlisting', tag):
2559 # We can handle <programlisting> specially here.
2560 return ModifyXMLElements(text, symbol,
2561 "<!\\[CDATA\\[",
2562 ConvertSGMLCharsEndTag,
2563 ConvertSGMLCharsCallback2)
2564 elif tag == '':
2565 # If we're not in CDATA convert to entities.
2566 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text) # Do this first, or the others get messed up.
2567 text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text)
2568 # Allow '>' at beginning of string for blockquote markdown
2569 text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text)
2571 # Handle "#include <xxxxx>"
2572 text = re.sub(r'#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text)
2574 return text
2577 def ConvertSGMLCharsCallback2(text, symbol, tag):
2578 # If we're not in CDATA convert to entities.
2579 # We could handle <programlisting> differently, though I'm not sure it helps.
2580 if tag == '':
2581 # replace only if its not a tag
2582 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text) # Do this first, or the others get messed up.
2583 text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text)
2584 text = re.sub(r'''(?<![a-zA-Z0-9"'\/-])>''', r'&gt;', text)
2585 # Handle "#include <xxxxx>"
2586 text = re.sub(r'/#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text)
2588 return text
2591 def ExpandAnnotation(symbol, param_desc):
2592 """This turns annotations into acronym tags.
2593 Args:
2594 symbol (str): the symbol being documented, for error messages.
2595 param_desc (str): the text to expand.
2597 Returns:
2598 str: the remaining param_desc
2599 str: the formatted annotations
2601 param_annotations = ''
2603 # look for annotations at the start of the comment part
2604 # function level annotations don't end with a colon ':'
2605 m = re.search(r'^\s*\((.*?)\)(:|$)', param_desc)
2606 if m:
2607 param_desc = param_desc[m.end():]
2609 annotations = re.split(r'\)\s*\(', m.group(1))
2610 logging.info("annotations for %s: '%s'\n", symbol, m.group(1))
2611 for annotation in annotations:
2612 # need to search for the longest key-match in %AnnotationDefinition
2613 match_length = 0
2614 match_annotation = ''
2616 for annotationdef in AnnotationDefinition:
2617 if annotation.startswith(annotationdef):
2618 if len(annotationdef) > match_length:
2619 match_length = len(annotationdef)
2620 match_annotation = annotationdef
2622 annotation_extra = ''
2623 if match_annotation != '':
2624 m = re.search(match_annotation + r'\s+(.*)', annotation)
2625 if m:
2626 annotation_extra = " " + m.group(1)
2628 AnnotationsUsed[match_annotation] = 1
2629 param_annotations += "[<acronym>%s</acronym>%s]" % (match_annotation, annotation_extra)
2630 else:
2631 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
2632 "unknown annotation \"%s\" in documentation for %s." % (annotation, symbol))
2633 param_annotations += "[%s]" % annotation
2635 param_desc = param_desc.strip()
2636 m = re.search(r'^(.*?)\.*\s*$', param_desc, flags=re.S)
2637 param_desc = m.group(1) + '. '
2639 if param_annotations != '':
2640 param_annotations = "<emphasis role=\"annotation\">%s</emphasis>" % param_annotations
2642 return (param_desc, param_annotations)
2645 def ExpandAbbreviations(symbol, text):
2646 """Expand the shortcut notation for symbol references.
2648 This turns the abbreviations function(), macro(), @param, %constant, and #symbol
2649 into appropriate DocBook markup. CDATA sections and <programlisting> parts
2650 are skipped.
2652 Args:
2653 symbol (str): the symbol being documented, for error messages.
2654 text (str): the text to expand.
2656 Returns:
2657 str: the expanded text
2659 # Note: This is a fallback and normally done in the markdown parser
2661 # Convert "|[" and "]|" into the start and end of program listing examples.
2662 # Support \[<!-- language="C" --> modifiers
2663 text = re.sub(r'\|\[<!-- language="([^"]+)" -->', r'<informalexample><programlisting language="\1"><![CDATA[', text)
2664 text = re.sub(r'\|\[', r'<informalexample><programlisting><![CDATA[', text)
2665 text = re.sub(r'\]\|', r']]></programlisting></informalexample>', text)
2667 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2668 # as such)
2669 return ModifyXMLElements(text, symbol,
2670 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2671 ExpandAbbreviationsEndTag,
2672 ExpandAbbreviationsCallback)
2675 def ExpandAbbreviationsEndTag(start_tag):
2676 # Returns the end tag (as a regexp) corresponding to the given start tag.
2677 if start_tag == r'<!\[CDATA\[':
2678 return "]]>"
2679 if start_tag == "<!DOCTYPE":
2680 return '>'
2681 m = re.search(r'<(\w+)', start_tag)
2682 if m:
2683 return "</%s>" % m.group(1)
2685 logging.warning('no end tag for "%s"', start_tag)
2686 return ''
2689 def ExpandAbbreviationsCallback(text, symbol, tag):
2690 # Called inside or outside each CDATA or <programlisting> section.
2691 if tag.startswith(r'^<programlisting'):
2692 # Handle any embedded CDATA sections.
2693 return ModifyXMLElements(text, symbol,
2694 "<!\\[CDATA\\[",
2695 ExpandAbbreviationsEndTag,
2696 ExpandAbbreviationsCallback2)
2697 elif tag == '':
2698 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2699 # but is also used for OutputExtraFile
2701 # We are outside any CDATA or <programlisting> sections, so we expand
2702 # any gtk-doc abbreviations.
2704 # Convert '@param()'
2705 # FIXME: we could make those also links ($symbol.$2), but that would be less
2706 # useful as the link target is a few lines up or down
2707 text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r'\1<parameter>\2()</parameter>', text)
2709 # Convert 'function()' or 'macro()'.
2710 # if there is abc_*_def() we don't want to make a link to _def()
2711 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2712 def f1(m):
2713 return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function"))
2714 text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f1, text)
2715 # handle #Object.func()
2716 text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1, text)
2718 # Convert '@param', but not '\@param'.
2719 text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r'\1<parameter>\2</parameter>', text)
2720 text = re.sub(r'/\\\@', r'\@', text)
2722 # Convert '%constant', but not '\%constant'.
2723 # Also allow negative numbers, e.g. %-1.
2724 def f2(m):
2725 return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2), "literal"))
2726 text = re.sub(r'(\A|[^\\])\%(-?\w+)', f2, text)
2727 text = re.sub(r'\\\%', r'\%', text)
2729 # Convert '#symbol', but not '\#symbol'.
2730 def f3(m):
2731 return m.group(1) + MakeHashXRef(m.group(2), "type")
2732 text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3, text)
2733 text = re.sub(r'\\#', '#', text)
2735 return text
2738 def ExpandAbbreviationsCallback2(text, symbol, tag):
2739 # This is called inside a <programlisting>
2740 if tag == '':
2741 # We are inside a <programlisting> but outside any CDATA sections,
2742 # so we expand any gtk-doc abbreviations.
2743 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2744 # why not just call it
2745 text = re.sub(r'#(\w+)', lambda m: '%s;' % MakeHashXRef(m.group(1), ''), text)
2746 elif tag == "<![CDATA[":
2747 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2748 text = ReplaceEntities(text, symbol)
2750 return text
2753 def MakeHashXRef(symbol, tag):
2754 text = symbol
2756 # Check for things like '#include', '#define', and skip them.
2757 if symbol in PreProcessorDirectives:
2758 return "#%s" % symbol
2760 # Get rid of special suffixes ('-struct','-enum').
2761 text = re.sub(r'-struct$', '', text)
2762 text = re.sub(r'-enum$', '', text)
2764 # If the symbol is in the form "Object::signal", then change the symbol to
2765 # "Object-signal" and use "signal" as the text.
2766 if '::' in symbol:
2767 o, s = symbol.split('::', 1)
2768 symbol = '%s-%s' % (o, s)
2769 text = '“' + s + '”'
2771 # If the symbol is in the form "Object:property", then change the symbol to
2772 # "Object--property" and use "property" as the text.
2773 if ':' in symbol:
2774 o, p = symbol.split(':', 1)
2775 symbol = '%s--%s' % (o, p)
2776 text = '“' + p + '”'
2778 if tag != '':
2779 text = tagify(text, tag)
2781 return MakeXRef(symbol, text)
2784 def ModifyXMLElements(text, symbol, start_tag_regexp, end_tag_func, callback):
2785 """Rewrite XML blocks.
2787 Looks for given XML element tags within the text, and calls
2788 the callback on pieces of text inside & outside those elements.
2789 Used for special handling of text inside things like CDATA
2790 and <programlisting>.
2792 Args:
2793 text (str): the text.
2794 symbol (str): the symbol currently being documented (only used for
2795 error messages).
2796 start_tag_regexp (str): the regular expression to match start tags.
2797 e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to
2798 match CDATA sections or programlisting elements.
2799 end_tag_func (func): function which is passed the matched start tag
2800 and should return the appropriate end tag string
2801 regexp.
2802 callback - callback called with each part of the text. It is
2803 called with a piece of text, the symbol being
2804 documented, and the matched start tag or '' if the text
2805 is outside the XML elements being matched.
2807 Returns:
2808 str: modified text
2810 before_tag = start_tag = end_tag_regexp = end_tag = None
2811 result = ''
2813 logging.debug('symbol: %s text: [%s]', symbol, text)
2815 m = re.search(start_tag_regexp, text, flags=re.S)
2816 while m:
2817 before_tag = text[:m.start()] # Prematch for last successful match string
2818 start_tag = m.group(0) # Last successful match
2819 text = text[m.end():] # Postmatch for last successful match string
2821 logging.debug('symbol: %s matched start %s: text: [%s]', symbol, start_tag, text)
2823 result += callback(before_tag, symbol, '')
2824 result += start_tag
2826 # get the matching end-tag for current tag
2827 end_tag_regexp = end_tag_func(start_tag)
2829 m2 = re.search(end_tag_regexp, text, flags=re.S)
2830 if m2:
2831 before_tag = text[:m2.start()]
2832 end_tag = m2.group(0)
2833 text = text[m2.end():]
2835 logging.debug('symbol: %s matched end %s: text: [%s]', symbol, end_tag, text)
2837 result += callback(before_tag, symbol, start_tag)
2838 result += end_tag
2839 else:
2840 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
2841 "Can't find tag end: %s in docs for: %s." % (end_tag_regexp, symbol))
2842 # Just assume it is all inside the tag.
2843 result += callback(text, symbol, start_tag)
2844 text = ''
2845 m = re.search(start_tag_regexp, text, flags=re.S)
2847 # Handle any remaining text outside the tags.
2848 result += callback(text, symbol, '')
2850 return result
2853 def tagify(text, elem):
2854 # Adds a tag around some text.
2855 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
2856 return '<' + elem + '>' + text + '</' + elem + '>'
2859 def MakeDocHeader(tag):
2860 """Builds a docbook header for the given tag.
2862 Args:
2863 tag (str): doctype tag
2865 Returns:
2866 str: the docbook header
2868 header = re.sub(r'<!DOCTYPE \w+', r'<!DOCTYPE ' + tag, doctype_header)
2869 # fix the path for book since this is one level up
2870 if tag == 'book':
2871 header = re.sub(
2872 r'<!ENTITY % gtkdocentities SYSTEM "../([a-zA-Z./]+)">', r'<!ENTITY % gtkdocentities SYSTEM "\1">', header)
2873 return header
2876 def MakeXRef(symbol, text=None):
2877 """This returns a cross-reference link to the given symbol.
2879 Though it doesn't try to do this for a few standard C types that it knows
2880 won't be in the documentation.
2882 Args:
2883 symbol (str): the symbol to try to create a XRef to.
2884 text (str): text to put inside the XRef, defaults to symbol
2886 Returns:
2887 str: a docbook link
2889 symbol = symbol.strip()
2890 if not text:
2891 text = symbol
2893 # Get rid of special suffixes ('-struct','-enum').
2894 text = re.sub(r'-struct$', '', text)
2895 text = re.sub(r'-enum$', '', text)
2897 if ' ' in symbol:
2898 return text
2900 logging.info("Getting type link for %s -> %s", symbol, text)
2902 symbol_id = common.CreateValidSGMLID(symbol)
2903 return "<link linkend=\"%s\">%s</link>" % (symbol_id, text)
2906 def MakeIndexterms(symbol, sid):
2907 """This returns a indexterm elements for the given symbol
2909 Args:
2910 symbol (str): the symbol to create indexterms for
2912 Returns:
2913 str: doxbook index terms
2915 terms = ''
2916 sortas = ''
2918 # make the index useful, by ommiting the namespace when sorting
2919 if NAME_SPACE != '':
2920 m = re.search(r'^%s\_?(.*)' % NAME_SPACE, symbol, flags=re.I)
2921 if m:
2922 sortas = ' sortas="%s"' % m.group(1)
2924 if symbol in Deprecated:
2925 terms += "<indexterm zone=\"%s\" role=\"deprecated\"><primary%s>%s</primary></indexterm>" % (
2926 sid, sortas, symbol)
2927 IndexEntriesDeprecated[symbol] = sid
2928 IndexEntriesFull[symbol] = sid
2929 if symbol in Since:
2930 since = Since[symbol].strip()
2931 if since != '':
2932 terms += "<indexterm zone=\"%s\" role=\"%s\"><primary%s>%s</primary></indexterm>" % (
2933 sid, since, sortas, symbol)
2934 IndexEntriesSince[symbol] = sid
2935 IndexEntriesFull[symbol] = sid
2936 if terms == '':
2937 terms += "<indexterm zone=\"%s\"><primary%s>%s</primary></indexterm>" % (sid, sortas, symbol)
2938 IndexEntriesFull[symbol] = sid
2939 return terms
2942 def MakeDeprecationNote(symbol):
2943 """This returns a deprecation warning for the given symbol.
2945 Args:
2946 symbol (str): the symbol to try to create a warning for.
2948 Returns:
2949 str: formatted warning or empty string if symbol is not deprecated
2951 desc = ''
2952 if symbol in Deprecated:
2953 desc += "<warning><para><literal>%s</literal> " % symbol
2954 note = Deprecated[symbol]
2956 m = re.search(r'^\s*([0-9\.]+)\s*:?', note)
2957 if m:
2958 desc += "has been deprecated since version %s and should not be used in newly-written code.</para>" % m.group(
2960 else:
2961 desc += "is deprecated and should not be used in newly-written code.</para>"
2963 note = re.sub(r'^\s*([0-9\.]+)\s*:?\s*', '', note)
2964 note = note.strip()
2966 if note != '':
2967 note = ConvertMarkDown(symbol, note)
2968 desc += " " + note
2970 desc += "</warning>\n"
2972 return desc
2975 def MakeConditionDescription(symbol):
2976 """This returns a sumary of conditions for the given symbol.
2978 Args:
2979 symbol (str): the symbol to create the sumary for.
2981 Returns:
2982 str: formatted text or empty string if no special conditions apply.
2984 desc = ''
2985 if symbol in Deprecated:
2986 if desc != '':
2987 desc += "|"
2988 m = re.search(r'^\s*(.*?)\s*$', Deprecated[symbol])
2989 if m:
2990 desc += "deprecated:%s" % m.group(1)
2991 else:
2992 desc += "deprecated"
2994 if symbol in Since:
2995 if desc != '':
2996 desc += "|"
2997 m = re.search(r'^\s*(.*?)\s*$', Since[symbol])
2998 if m:
2999 desc += "since:%s" % m.group(1)
3000 else:
3001 desc += "since"
3003 if symbol in StabilityLevel:
3004 if desc != '':
3005 desc += "|"
3007 desc += "stability:" + StabilityLevel[symbol]
3009 if desc != '':
3010 cond = re.sub(r'"', r'&quot;', desc)
3011 desc = ' condition=\"%s\"' % cond
3012 logging.info("condition for '%s' = '%s'", symbol, desc)
3014 return desc
3017 def GetHierarchy(gobject, hierarchy):
3018 """Generate the object inheritance graph.
3020 Returns the DocBook output describing the ancestors and
3021 immediate children of a GObject subclass. It uses the
3022 global Objects and ObjectLevels arrays to walk the tree.
3024 Args:
3025 object (str): the GtkObject subclass.
3026 hierarchy (list) - previous hierarchy
3028 Returns:
3029 list: lines of docbook describing the hierarchy
3031 # Find object in the objects array.
3032 found = False
3033 children = []
3034 level = 0
3035 j = 0
3036 for i in range(len(Objects)):
3037 if found:
3038 if ObjectLevels[i] <= level:
3039 break
3041 elif ObjectLevels[i] == level + 1:
3042 children.append(Objects[i])
3044 elif Objects[i] == gobject:
3045 found = True
3046 j = i
3047 level = ObjectLevels[i]
3049 if not found:
3050 return hierarchy
3052 logging.info("=== Hierachy for: %s (%d existing entries) ===", gobject, len(hierarchy))
3054 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3055 ancestors = [gobject]
3056 logging.info("Level: %s", level)
3057 while level > 1:
3058 j -= 1
3059 if ObjectLevels[j] < level:
3060 ancestors.append(Objects[j])
3061 level = ObjectLevels[j]
3062 logging.info("Level: %s", level)
3064 # Output the ancestors, indented and with links.
3065 logging.info('%d ancestors', len(ancestors))
3066 last_index = 0
3067 level = 1
3068 for i in range(len(ancestors) - 1, -1, -1):
3069 ancestor = ancestors[i]
3070 ancestor_id = common.CreateValidSGMLID(ancestor)
3071 indent = ' ' * (level * 4)
3072 # Don't add a link to the current object, i.e. when i == 0.
3073 if i > 0:
3074 entry_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
3075 alt_text = indent + ancestor
3076 else:
3077 entry_text = indent + ancestor
3078 alt_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
3080 logging.info("Checking for '%s' or '%s'", entry_text, alt_text)
3081 # Check if we already have this object
3082 index = -1
3083 for j in range(len(hierarchy)):
3084 if hierarchy[j] == entry_text or (hierarchy[j] == alt_text):
3085 index = j
3086 break
3087 if index == -1:
3088 # We have a new entry, find insert position in alphabetical order
3089 found = False
3090 for j in range(last_index, len(hierarchy)):
3091 if not re.search(r'^' + indent, hierarchy[j]):
3092 last_index = j
3093 found = True
3094 break
3095 elif re.search(r'^%s[^ ]' % indent, hierarchy[j]):
3096 stripped_text = hierarchy[j]
3097 if r'<link linkend' not in entry_text:
3098 stripped_text = re.sub(r'<link linkend="[A-Za-z]*">', '', stripped_text)
3099 stripped_text = re.sub(r'</link>', '', stripped_text)
3101 if entry_text < stripped_text:
3102 last_index = j
3103 found = True
3104 break
3106 # Append to bottom
3107 if not found:
3108 last_index = len(hierarchy)
3110 logging.debug('insert at %d: %s', last_index, entry_text)
3111 hierarchy.insert(last_index, entry_text)
3112 last_index += 1
3113 else:
3114 # Already have this one, make sure we use the not linked version
3115 if r'<link linkend' not in entry_text:
3116 hierarchy[j] = entry_text
3118 # Remember index as base insert point
3119 last_index = index + 1
3121 level += 1
3123 # Output the children, indented and with links.
3124 logging.info('%d children', len(children))
3125 for i in range(len(children)):
3126 sid = common.CreateValidSGMLID(children[i])
3127 indented_text = ' ' * (level * 4) + "<link linkend=\"%s\">%s</link>" % (sid, children[i])
3128 logging.debug('insert at %d: %s', last_index, indented_text)
3129 hierarchy.insert(last_index, indented_text)
3130 last_index += 1
3131 return hierarchy
3134 def GetInterfaces(gobject):
3135 """Generate interface implementation graph.
3137 Returns the DocBook output describing the interfaces
3138 implemented by a class. It uses the global Interfaces hash.
3140 Args:
3141 object (str): the GObject subclass.
3143 Returns:
3144 str: implemented interfaces
3146 text = ''
3147 # Find object in the objects array.
3148 if gobject in Interfaces:
3149 ifaces = Interfaces[gobject].split()
3150 text = '''<para>
3151 %s implements
3152 ''' % gobject
3153 count = len(ifaces)
3154 for i in range(count):
3155 sid = common.CreateValidSGMLID(ifaces[i])
3156 text += " <link linkend=\"%s\">%s</link>" % (sid, ifaces[i])
3157 if i < count - 2:
3158 text += ', '
3159 elif i < count - 1:
3160 text += ' and '
3161 else:
3162 text += '.'
3163 text += '</para>\n'
3164 return text
3167 def GetImplementations(gobject):
3168 """Generate interface usage graph.
3170 Returns the DocBook output describing the implementations
3171 of an interface. It uses the global Interfaces hash.
3173 Args:
3174 object (str): the GObject subclass.
3176 Returns:
3177 str: interface implementations
3179 text = ''
3180 impls = []
3181 for key in Interfaces:
3182 if re.search(r'\b%s\b' % gobject, Interfaces[key]):
3183 impls.append(key)
3185 count = len(impls)
3186 if count > 0:
3187 impls.sort()
3188 text = '''<para>
3189 %s is implemented by
3190 ''' % gobject
3191 for i in range(count):
3192 sid = common.CreateValidSGMLID(impls[i])
3193 text += " <link linkend=\"%s\">%s</link>" % (sid, impls[i])
3194 if i < count - 2:
3195 text += ', '
3196 elif i < count - 1:
3197 text += ' and '
3198 else:
3199 text += '.'
3200 text += '</para>\n'
3201 return text
3204 def GetPrerequisites(iface):
3205 """Generates interface requirements.
3207 Returns the DocBook output describing the prerequisites
3208 of an interface. It uses the global Prerequisites hash.
3209 Args:
3210 iface (str): the interface.
3212 Returns:
3213 str: required interfaces
3216 text = ''
3217 if iface in Prerequisites:
3218 text = '''<para>
3219 %s requires
3220 ''' % iface
3221 prereqs = Prerequisites[iface].split()
3222 count = len(prereqs)
3223 for i in range(count):
3224 sid = common.CreateValidSGMLID(prereqs[i])
3225 text += " <link linkend=\"%s\">%s</link>" % (sid, prereqs[i])
3226 if i < count - 2:
3227 text += ', '
3228 elif i < count - 1:
3229 text += ' and '
3230 else:
3231 text += '.'
3232 text += '</para>\n'
3233 return text
3236 def GetDerived(iface):
3238 Returns the DocBook output describing the derived interfaces
3239 of an interface. It uses the global %Prerequisites hash.
3241 Args:
3242 iface (str): the interface.
3244 Returns:
3245 str: derived interfaces
3247 text = ''
3248 derived = []
3249 for key in Prerequisites:
3250 if re.search(r'\b%s\b' % iface, Prerequisites[key]):
3251 derived.append(key)
3253 count = len(derived)
3254 if count > 0:
3255 derived.sort()
3256 text = '''<para>
3257 %s is required by
3258 ''' % iface
3259 for i in range(count):
3260 sid = common.CreateValidSGMLID(derived[i])
3261 text += " <link linkend=\"%s\">%s</link>" % (sid, derived[i])
3262 if i < count - 2:
3263 text += ', '
3264 elif i < count - 1:
3265 text += ' and '
3266 else:
3267 text += '.'
3268 text += '</para>\n'
3269 return text
3272 def GetSignals(gobject):
3273 """Generate signal docs.
3275 Returns the synopsis and detailed description DocBook output
3276 for the signal handlers of a given GObject subclass.
3278 Args:
3279 object (str): the GObject subclass, e.g. 'GtkButton'.
3281 Returns:
3282 str: signal docs
3284 synop = ''
3285 desc = ''
3287 for i in range(len(SignalObjects)):
3288 if SignalObjects[i] == gobject:
3289 logging.info("Found signal: %s", SignalNames[i])
3290 name = SignalNames[i]
3291 symbol = '%s::%s' % (gobject, name)
3292 sid = common.CreateValidSGMLID('%s-%s' % (gobject, name))
3294 desc += "<refsect2 id=\"%s\" role=\"signal\"><title>The <literal>“%s”</literal> signal</title>\n" % (
3295 sid, name)
3296 desc += MakeIndexterms(symbol, sid)
3297 desc += "\n"
3298 desc += OutputSymbolExtraLinks(symbol)
3300 desc += "<programlisting language=\"C\">"
3302 m = re.search(r'\s*(const\s+)?(\w+)\s*(\**)', SignalReturns[i])
3303 type_modifier = m.group(1) or ''
3304 gtype = m.group(2)
3305 pointer = m.group(3)
3306 xref = MakeXRef(gtype, tagify(gtype, "returnvalue"))
3308 ret_type_output = '%s%s%s' % (type_modifier, xref, pointer)
3309 callback_name = "user_function"
3310 desc += '%s\n%s (' % (ret_type_output, callback_name)
3312 indentation = ' ' * (len(callback_name) + 2)
3314 sourceparams = SourceSymbolParams.get(symbol)
3315 params = SignalPrototypes[i].splitlines()
3316 type_len = len("gpointer")
3317 name_len = len("user_data")
3318 # do two passes, the first one is to calculate padding
3319 for l in range(2):
3320 for j in range(len(params)):
3321 param_name = None
3322 # allow alphanumerics, '_', '[' & ']' in param names
3323 m = re.search(r'^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$', params[j])
3324 if m:
3325 gtype = m.group(1)
3326 pointer = m.group(2)
3327 if sourceparams:
3328 param_name = sourceparams.keys()[j]
3329 logging.info('from sourceparams: "%s" (%d: %s)', param_name, j, params[j])
3330 else:
3331 param_name = m.group(3)
3332 logging.info('from params: "%s" (%d: %s)', param_name, j, params[j])
3334 if not param_name:
3335 param_name = "arg%d" % j
3337 if l == 0:
3338 if len(gtype) + len(pointer) > type_len:
3339 type_len = len(gtype) + len(pointer)
3340 if len(param_name) > name_len:
3341 name_len = len(param_name)
3342 else:
3343 logging.info("signal arg[%d]: '%s'", j, param_name)
3344 xref = MakeXRef(gtype, tagify(gtype, "type"))
3345 pad = ' ' * (type_len - len(gtype) - len(pointer))
3346 desc += '%s%s %s%s,\n' % (xref, pad, pointer, param_name)
3347 desc += indentation
3349 else:
3350 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
3351 "Can't parse arg: %s\nArgs:%s" % (params[j], SignalPrototypes[i]))
3353 xref = MakeXRef("gpointer", tagify("gpointer", "type"))
3354 pad = ' ' * (type_len - len("gpointer"))
3355 desc += '%s%s user_data)' % (xref, pad)
3356 desc += "</programlisting>\n"
3358 flags = SignalFlags[i]
3359 flags_string = ''
3360 if flags:
3361 if 'f' in flags:
3362 flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>"
3364 elif 'l' in flags:
3365 flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>"
3367 elif 'c' in flags:
3368 flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>"
3369 flags_string = "Cleanup"
3371 if 'r' in flags:
3372 if flags_string:
3373 flags_string += " / "
3374 flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>"
3376 if 'd' in flags:
3377 if flags_string:
3378 flags_string += " / "
3379 flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>"
3381 if 'a' in flags:
3382 if flags_string:
3383 flags_string += " / "
3384 flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>"
3386 if 'h' in flags:
3387 if flags_string:
3388 flags_string += " / "
3389 flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>"
3391 synop += "<row><entry role=\"signal_type\">%s</entry><entry role=\"signal_name\"><link linkend=\"%s\">%s</link></entry><entry role=\"signal_flags\">%s</entry></row>\n" % (
3392 ret_type_output, sid, name, flags_string)
3394 parameters = OutputParamDescriptions("SIGNAL", symbol, None)
3395 logging.info("formatted signal params: '%s' -> '%s'", symbol, parameters)
3397 AllSymbols[symbol] = 1
3398 if symbol in SymbolDocs:
3399 symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
3401 desc += symbol_docs
3403 if not IsEmptyDoc(SymbolDocs[symbol]):
3404 AllDocumentedSymbols[symbol] = 1
3406 if symbol in SymbolAnnotations:
3407 param_desc = SymbolAnnotations[symbol]
3408 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
3409 if param_annotations != '':
3410 desc += "\n<para>%s</para>" % param_annotations
3412 desc += MakeDeprecationNote(symbol)
3414 desc += parameters
3415 if flags_string:
3416 desc += "<para>Flags: %s</para>\n" % flags_string
3418 desc += OutputSymbolTraits(symbol)
3419 desc += "</refsect2>"
3421 return (synop, desc)
3424 def GetArgs(gobject):
3425 """Generate property docs.
3427 Returns the synopsis and detailed description DocBook output
3428 for the Args of a given GtkObject subclass.
3430 Args:
3431 object (str): the GObject subclass, e.g. 'GtkButton'.
3433 Returns:
3434 str: property docs
3436 synop = ''
3437 desc = ''
3438 child_synop = ''
3439 child_desc = ''
3440 style_synop = ''
3441 style_desc = ''
3443 for i in range(len(ArgObjects)):
3444 if ArgObjects[i] == gobject:
3445 logging.info("Found arg: %s", ArgNames[i])
3446 name = ArgNames[i]
3447 flags = ArgFlags[i]
3448 flags_string = ''
3449 kind = ''
3450 id_sep = ''
3452 if 'c' in flags:
3453 kind = "child property"
3454 id_sep = "c-"
3455 elif 's' in flags:
3456 kind = "style property"
3457 id_sep = "s-"
3458 else:
3459 kind = "property"
3461 # Remember only one colon so we don't clash with signals.
3462 symbol = '%s:%s' % (gobject, name)
3463 # use two dashes and ev. an extra separator here for the same reason.
3464 sid = common.CreateValidSGMLID('%s--%s%s' % (gobject, id_sep, name))
3466 atype = ArgTypes[i]
3467 type_output = None
3468 arange = ArgRanges[i]
3469 range_output = CreateValidSGML(arange)
3470 default = ArgDefaults[i]
3471 default_output = CreateValidSGML(default)
3473 if atype == "GtkString":
3474 atype = "char&#160;*"
3476 if atype == "GtkSignal":
3477 atype = "GtkSignalFunc, gpointer"
3478 type_output = MakeXRef("GtkSignalFunc") + ", " + MakeXRef("gpointer")
3479 elif re.search(r'^(\w+)\*$', atype):
3480 m = re.search(r'^(\w+)\*$', atype)
3481 type_output = MakeXRef(m.group(1), tagify(m.group(1), "type")) + "&#160;*"
3482 else:
3483 type_output = MakeXRef(atype, tagify(atype, "type"))
3485 if 'r' in flags:
3486 flags_string = "Read"
3488 if 'w' in flags:
3489 if flags_string:
3490 flags_string += " / "
3491 flags_string += "Write"
3493 if 'x' in flags:
3494 if flags_string:
3495 flags_string += " / "
3496 flags_string += "Construct"
3498 if 'X' in flags:
3499 if flags_string:
3500 flags_string += " / "
3501 flags_string += "Construct Only"
3503 AllSymbols[symbol] = 1
3504 blurb = ''
3505 if symbol in SymbolDocs and not IsEmptyDoc(SymbolDocs[symbol]):
3506 blurb = ConvertMarkDown(symbol, SymbolDocs[symbol])
3507 logging.info(".. [%s][%s]", SymbolDocs[symbol], blurb)
3508 AllDocumentedSymbols[symbol] = 1
3510 else:
3511 if ArgBlurbs[i] != '':
3512 blurb = "<para>" + CreateValidSGML(ArgBlurbs[i]) + "</para>"
3513 AllDocumentedSymbols[symbol] = 1
3514 else:
3515 # FIXME: print a warning?
3516 logging.info(".. no description")
3518 pad1 = ''
3519 if len(name) < 24:
3520 pad1 = " " * (24 - len(name))
3522 arg_synop = "<row><entry role=\"property_type\">%s</entry><entry role=\"property_name\"><link linkend=\"%s\">%s</link></entry><entry role=\"property_flags\">%s</entry></row>\n" % (
3523 type_output, sid, name, flags_string)
3524 arg_desc = "<refsect2 id=\"%s\" role=\"property\"><title>The <literal>“%s”</literal> %s</title>\n" % (
3525 sid, name, kind)
3526 arg_desc += MakeIndexterms(symbol, sid)
3527 arg_desc += "\n"
3528 arg_desc += OutputSymbolExtraLinks(symbol)
3530 arg_desc += "<programlisting> “%s%s %s</programlisting>\n" % (name, pad1, type_output)
3531 arg_desc += blurb
3532 if symbol in SymbolAnnotations:
3533 param_desc = SymbolAnnotations[symbol]
3534 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
3535 if param_annotations != '':
3536 arg_desc += "\n<para>%s</para>" % param_annotations
3538 arg_desc += MakeDeprecationNote(symbol)
3540 if flags_string:
3541 arg_desc += "<para>Flags: %s</para>\n" % flags_string
3543 if arange != '':
3544 arg_desc += "<para>Allowed values: %s</para>\n" % range_output
3546 if default != '':
3547 arg_desc += "<para>Default value: %s</para>\n" % default_output
3549 arg_desc += OutputSymbolTraits(symbol)
3550 arg_desc += "</refsect2>\n"
3552 if 'c' in flags:
3553 child_synop += arg_synop
3554 child_desc += arg_desc
3556 elif 's' in flags:
3557 style_synop += arg_synop
3558 style_desc += arg_desc
3560 else:
3561 synop += arg_synop
3562 desc += arg_desc
3564 return (synop, child_synop, style_synop, desc, child_desc, style_desc)
3567 def IgnorePath(path, source_dirs, ignore_files):
3568 for sdir in source_dirs:
3569 # Cut off base directory
3570 m1 = re.search(r'^%s/(.*)$' % re.escape(sdir), path)
3571 if m1:
3572 # Check if the filename is in the ignore list.
3573 m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), ignore_files)
3574 if m2:
3575 logging.info("Skipping path: %s", path)
3576 return True
3577 else:
3578 logging.info("No match for: %s", m1.group(1))
3579 else:
3580 logging.info("No match for: %s", path)
3581 return False
3584 def ReadSourceDocumentation(source_dir, suffix_list, source_dirs, ignore_files):
3585 """Read the documentation embedded in comment blocks in the source code.
3587 It recursively descends the source directory looking for source files and
3588 scans them looking for specially-formatted comment blocks.
3590 Args:
3591 source_dir (str): the directory to scan.
3592 suffix_list (list): extensions to check
3594 if IgnorePath(source_dir, source_dirs, ignore_files):
3595 return
3597 logging.info("Scanning source directory: %s", source_dir)
3599 # This array holds any subdirectories found.
3600 subdirs = []
3602 for ifile in os.listdir(source_dir):
3603 logging.debug("... : %s", ifile)
3604 if ifile.startswith('.'):
3605 continue
3606 fname = os.path.join(source_dir, ifile)
3607 if os.path.isdir(fname):
3608 subdirs.append(fname)
3609 else:
3610 for suffix in suffix_list:
3611 if ifile.endswith(suffix):
3612 if not IgnorePath(fname, source_dirs, ignore_files):
3613 ScanSourceFile(fname, ignore_files)
3614 break
3616 # Now recursively scan the subdirectories.
3617 for sdir in subdirs:
3618 ReadSourceDocumentation(sdir, suffix_list, source_dirs, ignore_files)
3621 def ScanSourceFile(ifile, ignore_files):
3622 """Scans one source file looking for specially-formatted comment blocks.
3624 Later MergeSourceDocumentation() is copying over the doc blobs that are not
3625 suppressed/ignored.
3627 Args:
3628 file (str): the file to scan.
3630 m = re.search(r'^.*[\/\\]([^\/\\]*)$', ifile)
3631 if m:
3632 basename = m.group(1)
3633 else:
3634 common.LogWarning(ifile, 1, "Can't find basename for this filename.")
3635 basename = ifile
3637 # Check if the basename is in the list of files to ignore.
3638 if re.search(r'(\s|^)%s(\s|$)' % re.escape(basename), ignore_files):
3639 logging.info("Skipping source file: %s", ifile)
3640 return
3642 logging.info("Scanning source file: %s", ifile)
3644 SRCFILE = open(ifile)
3645 in_comment_block = False
3646 symbol = None
3647 in_part = ''
3648 description = ''
3649 return_desc = ''
3650 since_desc = stability_desc = deprecated_desc = ''
3651 params = OrderedDict()
3652 param_name = None
3653 line_number = 0
3654 for line in SRCFILE:
3655 line_number += 1
3656 # Look for the start of a comment block.
3657 if not in_comment_block:
3658 if re.search(r'^\s*/\*.*\*/', line):
3659 # one-line comment - not gtkdoc
3660 pass
3661 elif re.search(r'^\s*/\*\*\s', line):
3662 logging.info("Found comment block start")
3664 in_comment_block = True
3666 # Reset all the symbol data.
3667 symbol = ''
3668 in_part = ''
3669 description = ''
3670 return_desc = ''
3671 since_desc = ''
3672 deprecated_desc = ''
3673 stability_desc = ''
3674 params = OrderedDict()
3675 param_name = None
3677 continue
3679 # We're in a comment block. Check if we've found the end of it.
3680 if re.search(r'^\s*\*+/', line):
3681 if not symbol:
3682 # maybe its not even meant to be a gtk-doc comment?
3683 common.LogWarning(ifile, line_number, "Symbol name not found at the start of the comment block.")
3684 else:
3685 # Add the return value description onto the end of the params.
3686 if return_desc:
3687 # TODO(ensonic): check for duplicated Return docs
3688 # common.LogWarning(file, line_number, "Multiple Returns for %s." % symbol)
3689 params['Returns'] = return_desc
3691 # Convert special characters
3692 description = ConvertSGMLChars(symbol, description)
3693 for (param_name, param_desc) in iteritems(params):
3694 params[param_name] = ConvertSGMLChars(symbol, param_desc)
3696 # Handle Section docs
3697 m = re.search(r'SECTION:\s*(.*)', symbol)
3698 m2 = re.search(r'PROGRAM:\s*(.*)', symbol)
3699 if m:
3700 real_symbol = m.group(1)
3701 long_descr = real_symbol + ":Long_Description"
3703 if long_descr not in KnownSymbols or KnownSymbols[long_descr] != 1:
3704 common.LogWarning(
3705 ifile, line_number, "Section %s is not defined in the %s-sections.txt file." % (real_symbol, MODULE))
3707 logging.info("SECTION DOCS found in source for : '%s'", real_symbol)
3708 for param_name, param_desc in iteritems(params):
3709 logging.info(" '" + param_name + "'")
3710 param_name = param_name.lower()
3711 key = None
3712 if param_name == "short_description":
3713 key = real_symbol + ":Short_Description"
3714 elif param_name == "see_also":
3715 key = real_symbol + ":See_Also"
3716 elif param_name == "title":
3717 key = real_symbol + ":Title"
3718 elif param_name == "stability":
3719 key = real_symbol + ":Stability_Level"
3720 elif param_name == "section_id":
3721 key = real_symbol + ":Section_Id"
3722 elif param_name == "include":
3723 key = real_symbol + ":Include"
3724 elif param_name == "image":
3725 key = real_symbol + ":Image"
3727 if key:
3728 SourceSymbolDocs[key] = param_desc
3729 SourceSymbolSourceFile[key] = ifile
3730 SourceSymbolSourceLine[key] = line_number
3732 SourceSymbolDocs[long_descr] = description
3733 SourceSymbolSourceFile[long_descr] = ifile
3734 SourceSymbolSourceLine[long_descr] = line_number
3735 elif m2:
3736 real_symbol = m2.group(1)
3737 key = None
3738 section_id = None
3740 logging.info("PROGRAM DOCS found in source for '%s'", real_symbol)
3741 for param_name, param_desc in iteritems(params):
3742 logging.info("PROGRAM key %s: '%s'", real_symbol, param_name)
3743 param_name = param_name.lower()
3744 key = None
3745 if param_name == "short_description":
3746 key = real_symbol + ":Short_Description"
3747 elif param_name == "see_also":
3748 key = real_symbol + ":See_Also"
3749 elif param_name == "section_id":
3750 key = real_symbol + ":Section_Id"
3751 elif param_name == "synopsis":
3752 key = real_symbol + ":Synopsis"
3753 elif param_name == "returns":
3754 key = real_symbol + ":Returns"
3755 elif re.search(r'^(-.*)', param_name):
3756 logging.info("PROGRAM opts: '%s': '%s'", param_name, param_desc)
3757 key = real_symbol + ":Options"
3758 opts = []
3759 opts_str = SourceSymbolDocs.get(key)
3760 if opts_str:
3761 opts = opts_str.split('\t')
3762 opts.append(param_name)
3763 opts.append(param_desc)
3765 logging.info("Setting options for symbol: %s: '%s'", real_symbol, '\t'.join(opts))
3766 SourceSymbolDocs[key] = '\t'.join(opts)
3767 continue
3769 if key:
3770 logging.info("PROGRAM value %s: '%s'", real_symbol, param_desc.rstrip())
3771 SourceSymbolDocs[key] = param_desc.rstrip()
3772 SourceSymbolSourceFile[key] = ifile
3773 SourceSymbolSourceLine[key] = line_number
3775 long_descr = real_symbol + ":Long_Description"
3776 SourceSymbolDocs[long_descr] = description
3777 SourceSymbolSourceFile[long_descr] = ifile
3778 SourceSymbolSourceLine[long_descr] = line_number
3780 section_id = SourceSymbolDocs.get(real_symbol + ":Section_Id")
3781 if section_id and section_id.strip() != '':
3782 # Remove trailing blanks and use as is
3783 section_id = section_id.rstrip()
3784 else:
3785 section_id = common.CreateValidSGMLID('%s-%s' % (MODULE, real_symbol))
3786 OutputProgramDBFile(real_symbol, section_id)
3788 else:
3789 logging.info("SYMBOL DOCS found in source for : '%s'", symbol)
3790 SourceSymbolDocs[symbol] = description
3791 SourceSymbolParams[symbol] = params
3792 SourceSymbolSourceFile[symbol] = ifile
3793 SourceSymbolSourceLine[symbol] = line_number
3795 if since_desc:
3796 arr = since_desc.splitlines()
3797 since_desc = arr[0].strip()
3798 extra_lines = arr[1:]
3799 logging.info("Since(%s) : [%s]", symbol, since_desc)
3800 Since[symbol] = ConvertSGMLChars(symbol, since_desc)
3801 if len(extra_lines) > 1:
3802 common.LogWarning(ifile, line_number, "multi-line since docs found")
3804 if stability_desc:
3805 stability_desc = ParseStabilityLevel(
3806 stability_desc, ifile, line_number, "Stability level for %s" % symbol)
3807 StabilityLevel[symbol] = ConvertSGMLChars(symbol, stability_desc)
3809 if deprecated_desc:
3810 if symbol not in Deprecated:
3811 # don't warn for signals and properties
3812 # if ($symbol !~ m/::?(.*)/)
3813 if symbol in DeclarationTypes:
3814 common.LogWarning(ifile, line_number,
3815 "%s is deprecated in the inline comments, but no deprecation guards were found around the declaration. (See the --deprecated-guards option for gtkdoc-scan.)" % symbol)
3817 Deprecated[symbol] = ConvertSGMLChars(symbol, deprecated_desc)
3819 in_comment_block = False
3820 continue
3822 # Get rid of ' * ' at start of every line in the comment block.
3823 line = re.sub(r'^\s*\*\s?', '', line)
3824 # But make sure we don't get rid of the newline at the end.
3825 if not line.endswith('\n'):
3826 line = line + "\n"
3828 logging.info("scanning :%s", line)
3830 # If we haven't found the symbol name yet, look for it.
3831 if not symbol:
3832 m1 = re.search(r'^\s*(SECTION:\s*\S+)', line)
3833 m2 = re.search(r'^\s*(PROGRAM:\s*\S+)', line)
3834 m3 = re.search(r'^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$', line)
3835 if m1:
3836 symbol = m1.group(1)
3837 logging.info("SECTION DOCS found in source for : '%s'", symbol)
3838 elif m2:
3839 symbol = m2.group(1)
3840 logging.info("PROGRAM DOCS found in source for : '%s'", symbol)
3841 elif m3:
3842 symbol = m3.group(1)
3843 annotation = m3.group(2)
3844 logging.info("SYMBOL DOCS found in source for : '%s'", symbol)
3845 if annotation:
3846 annotation = annotation.strip()
3847 if annotation != '':
3848 SymbolAnnotations[symbol] = annotation
3849 logging.info("remaining text for %s: '%s'", symbol, annotation)
3851 continue
3853 if in_part == "description":
3854 # Get rid of 'Description:'
3855 line = re.sub(r'^\s*Description:', '', line)
3857 m1 = re.search(r'^\s*(returns|return\s+value):', line, flags=re.I)
3858 m2 = re.search(r'^\s*since:', line, flags=re.I)
3859 m3 = re.search(r'^\s*deprecated:', line, flags=re.I)
3860 m4 = re.search(r'^\s*stability:', line, flags=re.I)
3862 if m1:
3863 # we're in param section and have not seen the blank line
3864 if in_part != '':
3865 return_desc = line[m1.end():]
3866 in_part = "return"
3867 continue
3869 if m2:
3870 # we're in param section and have not seen the blank line
3871 if in_part != "param":
3872 since_desc = line[m2.end():]
3873 in_part = "since"
3874 continue
3876 elif m3:
3877 # we're in param section and have not seen the blank line
3878 if in_part != "param":
3879 deprecated_desc = line[m3.end():]
3880 in_part = "deprecated"
3881 continue
3883 elif m4:
3884 stability_desc = line[m4.end():]
3885 in_part = "stability"
3886 continue
3888 if in_part == "description":
3889 description += line
3890 continue
3891 elif in_part == "return":
3892 return_desc += line
3893 continue
3894 elif in_part == "since":
3895 since_desc += line
3896 continue
3897 elif in_part == "stability":
3898 stability_desc += line
3899 continue
3900 elif in_part == "deprecated":
3901 deprecated_desc += line
3902 continue
3904 # We must be in the parameters. Check for the empty line below them.
3905 if re.search(r'^\s*$', line):
3906 in_part = "description"
3907 continue
3909 # Look for a parameter name.
3910 m = re.search(r'^\s*@(.+?)\s*:\s*', line)
3911 if m:
3912 param_name = m.group(1)
3913 param_desc = line[m.end():]
3915 logging.info("Found parameter: %s", param_name)
3916 # Allow varargs variations
3917 if re.search(r'^\.\.\.$', param_name):
3918 param_name = "..."
3920 logging.info("Found param for symbol %s : '%s'= '%s'", symbol, param_name, line)
3922 params[param_name] = param_desc
3923 in_part = "param"
3924 continue
3925 elif in_part == '':
3926 logging.info("continuation for %s annotation '%s'", symbol, line)
3927 annotation = re.sub(r'^\s+|\s+$', '', line)
3928 if symbol in SymbolAnnotations:
3929 SymbolAnnotations[symbol] += annotation
3930 else:
3931 SymbolAnnotations[symbol] = annotation
3932 continue
3934 # We must be in the middle of a parameter description, so add it on
3935 # to the last element in @params.
3936 if not param_name:
3937 common.LogWarning(file, line_number, "Parsing comment block file : parameter expected, but got '%s'" % line)
3938 else:
3939 params[param_name] += line
3941 SRCFILE.close()
3944 def OutputMissingDocumentation():
3945 """Outputs report of documentation coverage to a file.
3947 Returns:
3948 bool: True if the report was updated
3950 old_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.txt")
3951 new_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.new")
3953 n_documented = 0
3954 n_incomplete = 0
3955 total = 0
3956 symbol = None
3957 percent = None
3958 buffer = ''
3959 buffer_deprecated = ''
3960 buffer_descriptions = ''
3962 UNDOCUMENTED = open(new_undocumented_file, 'w')
3964 for symbol in sorted(AllSymbols.keys()):
3965 # FIXME: should we print common.LogWarnings for undocumented stuff?
3966 # DEBUG
3967 # location = "defined at " + GetSymbolSourceFile(symbol) + ":" + GetSymbolSourceLine(symbol) + "\n"
3968 # DEBUG
3969 m = re.search(
3970 r':(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)', symbol)
3971 m2 = re.search(r':(Long_Description|Short_Description)', symbol)
3972 if not m:
3973 total += 1
3974 if symbol in AllDocumentedSymbols:
3975 n_documented += 1
3976 if symbol in AllIncompleteSymbols:
3977 n_incomplete += 1
3978 buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
3979 #$buffer += "\t0: ".$location
3981 elif symbol in Deprecated:
3982 if symbol in AllIncompleteSymbols:
3983 n_incomplete += 1
3984 buffer_deprecated += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
3985 #$buffer += "\t1a: ".$location
3986 else:
3987 buffer_deprecated += symbol + "\n"
3988 #$buffer += "\t1b: ".$location
3990 else:
3991 if symbol in AllIncompleteSymbols:
3992 n_incomplete += 1
3993 buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
3994 #$buffer += "\t2a: ".$location
3995 else:
3996 buffer += symbol + "\n"
3997 #$buffer += "\t2b: ".$location
3999 elif m2:
4000 total += 1
4001 if (symbol in SymbolDocs and len(SymbolDocs[symbol]) > 0)\
4002 or (symbol in AllDocumentedSymbols and AllDocumentedSymbols[symbol] > 0):
4003 n_documented += 1
4004 else:
4005 buffer_descriptions += symbol + "\n"
4007 if total == 0:
4008 percent = 100
4009 else:
4010 percent = (n_documented / total) * 100.0
4012 UNDOCUMENTED.write("%.0f%% symbol docs coverage.\n" % percent)
4013 UNDOCUMENTED.write("%s symbols documented.\n" % n_documented)
4014 UNDOCUMENTED.write("%s symbols incomplete.\n" % n_incomplete)
4015 UNDOCUMENTED.write("%d not documented.\n" % (total - n_documented))
4017 if buffer_deprecated != '':
4018 buffer += "\n" + buffer_deprecated
4020 if buffer_descriptions != '':
4021 buffer += "\n" + buffer_descriptions
4023 if buffer != '':
4024 UNDOCUMENTED.write("\n\n" + buffer)
4026 UNDOCUMENTED.close()
4028 return common.UpdateFileIfChanged(old_undocumented_file, new_undocumented_file, 0)
4031 def OutputUndeclaredSymbols():
4032 """Reports undeclared symbols.
4034 Outputs symbols that are listed in the section file, but have no declaration
4035 in the sources.
4037 Returns:
4038 bool: True if the report was updated
4040 old_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.txt")
4041 new_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.new")
4043 UNDECLARED = open(new_undeclared_file, 'w')
4045 if UndeclaredSymbols:
4046 UNDECLARED.write("\n".join(sorted(UndeclaredSymbols.keys())))
4047 UNDECLARED.write("\n")
4048 print("See %s-undeclared.txt for the list of undeclared symbols." % MODULE)
4050 UNDECLARED.close()
4052 return common.UpdateFileIfChanged(old_undeclared_file, new_undeclared_file, 0)
4055 def OutputUnusedSymbols():
4056 """Reports unused documentation.
4058 Outputs symbols that are documented in comments, but not declared in the
4059 sources.
4061 Returns:
4062 bool: True if the report was updated
4064 num_unused = 0
4065 old_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.txt")
4066 new_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.new")
4068 UNUSED = open(new_unused_file, 'w')
4070 for symbol in sorted(Declarations.keys()):
4071 if not symbol in DeclarationOutput:
4072 UNUSED.write("%s\n" % symbol)
4073 num_unused += 1
4075 for symbol in sorted(AllUnusedSymbols.keys()):
4076 UNUSED.write(symbol + "(" + AllUnusedSymbols[symbol] + ")\n")
4077 num_unused += 1
4079 UNUSED.close()
4080 if num_unused != 0:
4081 common.LogWarning(
4082 old_unused_file, 1, "%d unused declarations. They should be added to %s-sections.txt in the appropriate place." % (num_unused, MODULE))
4084 return common.UpdateFileIfChanged(old_unused_file, new_unused_file, 0)
4087 def OutputAllSymbols():
4088 """Outputs list of all symbols to a file."""
4089 SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-symbols.txt"), 'w')
4091 for symbol in sorted(AllSymbols.keys()):
4092 SYMBOLS.write(symbol + "\n")
4093 SYMBOLS.close()
4096 def OutputSymbolsWithoutSince():
4097 """Outputs list of all symbols without a since tag to a file."""
4098 SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-nosince.txt"), 'w')
4100 for symbol in sorted(SourceSymbolDocs.keys()):
4101 if symbol in Since:
4102 SYMBOLS.write(symbol + "\n")
4103 SYMBOLS.close()
4106 def CheckParamsDocumented(symbol, params):
4107 stype = DeclarationTypes.get(symbol)
4109 item = "Parameter"
4110 if stype:
4111 if stype == 'STRUCT':
4112 item = "Field"
4113 elif stype == 'ENUM':
4114 item = "Value"
4115 elif stype == 'UNION':
4116 item = "Field"
4117 else:
4118 stype = "SIGNAL"
4119 logging.info("Check param docs for %s, params: %s entries, type=%s", symbol, len(params), stype)
4121 if len(params) > 0:
4122 logging.info("params: %s", str(params))
4123 for (param_name, param_desc) in iteritems(params):
4124 # Output a warning if the parameter is empty and remember for stats.
4125 if param_name != "void" and not re.search(r'\S', param_desc):
4126 if symbol in AllIncompleteSymbols:
4127 AllIncompleteSymbols[symbol] += ", " + param_name
4128 else:
4129 AllIncompleteSymbols[symbol] = param_name
4131 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
4132 "%s description for %s::%s is missing in source code comment block." % (item, symbol, param_name))
4134 elif len(params) == 0:
4135 AllIncompleteSymbols[symbol] = "<items>"
4136 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
4137 "%s descriptions for %s are missing in source code comment block." % (item, symbol))
4140 def MergeSourceDocumentation():
4141 """Merges documentation read from a source file.
4143 Parameter descriptions override any in the template files.
4144 Function descriptions are placed before any description from
4145 the template files.
4148 # add whats found in the source
4149 symbols = set(SourceSymbolDocs.keys())
4151 # and add known symbols from -sections.txt
4152 for symbol in KnownSymbols.keys():
4153 if KnownSymbols[symbol] == 1:
4154 symbols.add(symbol)
4156 logging.info("num source entries: %d", len(symbols))
4158 for symbol in symbols:
4159 AllSymbols[symbol] = 1
4161 if symbol in SourceSymbolDocs:
4162 logging.info("merging [%s] from source", symbol)
4164 # remove leading and training whitespaces
4165 src_docs = SourceSymbolDocs[symbol].strip()
4166 if src_docs != '':
4167 AllDocumentedSymbols[symbol] = 1
4169 SymbolDocs[symbol] = src_docs
4171 # merge parameters
4172 if symbol in SourceSymbolParams:
4173 param_docs = SourceSymbolParams[symbol]
4174 SymbolParams[symbol] = param_docs
4175 # if this symbol is documented, check if docs are complete
4176 # remove all xml-tags and whitespaces
4177 check_docs = re.sub(r'\s', '', re.sub(r'<.*?>', '', src_docs))
4178 if check_docs != '' and param_docs:
4179 CheckParamsDocumented(symbol, param_docs)
4180 else:
4181 logging.info("[%s] undocumented", symbol)
4183 logging.info("num doc entries: %d", len(SymbolDocs))
4186 def IsEmptyDoc(doc):
4187 """Check if a doc-string is empty.
4189 It is also regarded as empty if it only consist of whitespace or e.g. FIXME.
4191 Args:
4192 doc (str): the doc-string
4194 Returns:
4195 bool: True if empty
4197 if re.search(r'^\s*$', doc):
4198 return True
4199 if re.search(r'^\s*<para>\s*(FIXME)?\s*<\/para>\s*$', doc):
4200 return True
4201 return False
4204 def ConvertMarkDown(symbol, text):
4205 md_to_db.Init()
4206 return md_to_db.MarkDownParse(text, symbol)
4209 def ReadDeclarationsFile(ifile, override):
4210 """Reads in a file containing the function/macro/enum etc. declarations.
4212 Note that in some cases there are several declarations with
4213 the same name, e.g. for conditional macros. In this case we
4214 set a flag in the DeclarationConditional hash so the
4215 declaration is not shown in the docs.
4217 If a macro and a function have the same name, e.g. for
4218 g_object_ref, the function declaration takes precedence.
4220 Some opaque structs are just declared with 'typedef struct
4221 _name name;' in which case the declaration may be empty.
4222 The structure may have been found later in the header, so
4223 that overrides the empty declaration.
4225 Args:
4226 file (str): the declarations file to read
4227 override (bool): if declarations in this file should override
4228 any current declaration.
4230 if override == 0:
4231 Declarations.clear()
4232 DeclarationTypes.clear()
4233 DeclarationConditional.clear()
4234 DeclarationOutput.clear()
4236 INPUT = open(ifile)
4237 declaration_type = ''
4238 declaration_name = None
4239 declaration = None
4240 is_deprecated = 0
4241 line_number = 0
4242 for line in INPUT:
4243 line_number += 1
4244 if not declaration_type:
4245 m1 = re.search(r'^<([^>]+)>', line)
4246 if m1:
4247 declaration_type = m1.group(1)
4248 declaration_name = ''
4249 logging.info("Found declaration: %s", declaration_type)
4250 declaration = ''
4251 else:
4252 m2 = re.search(r'^<NAME>(.*)</NAME>', line)
4253 m3 = re.search(r'^<DEPRECATED/>', line)
4254 m4 = re.search(r'^</%s>' % declaration_type, line)
4255 if m2:
4256 declaration_name = m2.group(1)
4257 elif m3:
4258 is_deprecated = True
4259 elif m4:
4260 logging.info("Found end of declaration: %s, %s", declaration_type, declaration_name)
4261 # Check that the declaration has a name
4262 if declaration_name == '':
4263 common.LogWarning(ifile, line_number, declaration_type + " has no name.\n")
4265 # If the declaration is an empty typedef struct _XXX XXX
4266 # set the flag to indicate the struct has a typedef.
4267 if (declaration_type == 'STRUCT' or declaration_type == 'UNION') \
4268 and re.search(r'^\s*$', declaration):
4269 logging.info("Struct has typedef: %s", declaration_name)
4270 StructHasTypedef[declaration_name] = 1
4272 # Check if the symbol is already defined.
4273 if declaration_name in Declarations and override == 0:
4274 # Function declarations take precedence.
4275 if DeclarationTypes[declaration_name] == 'FUNCTION':
4276 # Ignore it.
4277 pass
4278 elif declaration_type == 'FUNCTION':
4279 if is_deprecated:
4280 Deprecated[declaration_name] = ''
4282 Declarations[declaration_name] = declaration
4283 DeclarationTypes[declaration_name] = declaration_type
4284 elif DeclarationTypes[declaration_name] == declaration_type:
4285 # If the existing declaration is empty, or is just a
4286 # forward declaration of a struct, override it.
4287 if declaration_type == 'STRUCT' or declaration_type == 'UNION':
4288 if re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', Declarations[declaration_name]):
4289 if is_deprecated:
4290 Deprecated[declaration_name] = ''
4291 Declarations[declaration_name] = declaration
4292 elif re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', declaration):
4293 # Ignore an empty or forward declaration.
4294 pass
4295 else:
4296 common.LogWarning(
4297 ifile, line_number, "Structure %s has multiple definitions." % declaration_name)
4299 else:
4300 # set flag in %DeclarationConditional hash for
4301 # multiply defined macros/typedefs.
4302 DeclarationConditional[declaration_name] = 1
4304 else:
4305 common.LogWarning(ifile, line_number, declaration_name + " has multiple definitions.")
4307 else:
4308 if is_deprecated:
4309 Deprecated[declaration_name] = ''
4311 Declarations[declaration_name] = declaration
4312 DeclarationTypes[declaration_name] = declaration_type
4313 logging.debug("added declaration: %s, %s, [%s]", declaration_type, declaration_name, declaration)
4315 declaration_type = ''
4316 is_deprecated = False
4317 else:
4318 declaration += line
4319 INPUT.close()
4322 def ReadSignalsFile(ifile):
4323 """Reads information about object signals.
4325 It creates the arrays @SignalNames and @SignalPrototypes containing details
4326 about the signals. The first line of the SignalPrototype is the return type
4327 of the signal handler. The remaining lines are the parameters passed to it.
4328 The last parameter, "gpointer user_data" is always the same so is not included.
4330 Args:
4331 ifile (str): the file containing the signal handler prototype information.
4333 in_signal = 0
4334 signal_object = None
4335 signal_name = None
4336 signal_returns = None
4337 signal_flags = None
4338 signal_prototype = None
4340 # Reset the signal info.
4341 SignalObjects[:] = []
4342 SignalNames[:] = []
4343 SignalReturns[:] = []
4344 SignalFlags[:] = []
4345 SignalPrototypes[:] = []
4347 if not os.path.isfile(ifile):
4348 return
4350 INPUT = open(ifile)
4351 line_number = 0
4352 for line in INPUT:
4353 line_number += 1
4354 if not in_signal:
4355 if re.search(r'^<SIGNAL>', line):
4356 in_signal = 1
4357 signal_object = ''
4358 signal_name = ''
4359 signal_returns = ''
4360 signal_prototype = ''
4362 else:
4363 m = re.search(r'^<NAME>(.*)<\/NAME>', line)
4364 m2 = re.search(r'^<RETURNS>(.*)<\/RETURNS>', line)
4365 m3 = re.search(r'^<FLAGS>(.*)<\/FLAGS>', line)
4366 if m:
4367 signal_name = m.group(1)
4368 m1_2 = re.search(r'^(.*)::(.*)$', signal_name)
4369 if m1_2:
4370 signal_object = m1_2.group(1)
4371 signal_name = m1_2.group(2).replace('_', '-')
4372 logging.info("Found signal: %s", signal_name)
4373 else:
4374 common.LogWarning(ifile, line_number, "Invalid signal name: %s." % signal_name)
4376 elif m2:
4377 signal_returns = m2.group(1)
4378 elif m3:
4379 signal_flags = m3.group(1)
4380 elif re.search(r'^</SIGNAL>', line):
4381 logging.info("Found end of signal: %s::%s\nReturns: %s\n%s",
4382 signal_object, signal_name, signal_returns, signal_prototype)
4383 SignalObjects.append(signal_object)
4384 SignalNames.append(signal_name)
4385 SignalReturns.append(signal_returns)
4386 SignalFlags.append(signal_flags)
4387 SignalPrototypes.append(signal_prototype)
4388 in_signal = False
4389 else:
4390 signal_prototype += line
4391 INPUT.close()
4394 def ReadObjectHierarchy(ifile):
4395 """Reads the $MODULE-hierarchy.txt file.
4397 This contains all the GObject subclasses described in this module (and their
4398 ancestors).
4399 It places them in the Objects array, and places their level
4400 in the object hierarchy in the ObjectLevels array, at the
4401 same index. GObject, the root object, has a level of 1.
4403 This also generates tree_index.sgml as it goes along.
4405 Args:
4406 ifile (str): the input filename.
4409 Objects[:] = []
4410 ObjectLevels[:] = []
4412 if not os.path.isfile(ifile):
4413 return
4415 INPUT = open(ifile)
4417 # Only emit objects if they are supposed to be documented, or if
4418 # they have documented children. To implement this, we maintain a
4419 # stack of pending objects which will be emitted if a documented
4420 # child turns up.
4421 pending_objects = []
4422 pending_levels = []
4423 root = None
4424 tree = []
4425 for line in INPUT:
4426 m1 = re.search(r'\S+', line)
4427 if not m1:
4428 continue
4430 gobject = m1.group(0)
4431 level = len(line[:m1.start()]) // 2 + 1
4433 if level == 1:
4434 root = gobject
4436 while pending_levels and pending_levels[-1] >= level:
4437 pending_objects.pop()
4438 pending_levels.pop()
4440 pending_objects.append(gobject)
4441 pending_levels.append(level)
4443 if gobject in KnownSymbols:
4444 while len(pending_levels) > 0:
4445 gobject = pending_objects.pop(0)
4446 level = pending_levels.pop(0)
4447 xref = MakeXRef(gobject)
4449 tree.append(' ' * (level * 4) + xref)
4450 Objects.append(gobject)
4451 ObjectLevels.append(level)
4452 ObjectRoots[gobject] = root
4453 # else
4454 # common.LogWarning(ifile, line_number, "unknown type %s" % object)
4457 INPUT.close()
4459 # FIXME: use xml
4460 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml"
4461 old_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.sgml")
4462 new_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.new")
4464 logging.debug('got %d entries for hierarchy', len(tree))
4466 OUTPUT = open(new_tree_index, 'w')
4467 OUTPUT.write(MakeDocHeader("screen") + "\n<screen>\n" + AddTreeLineArt(tree) + "\n</screen>\n")
4468 OUTPUT.close()
4470 common.UpdateFileIfChanged(old_tree_index, new_tree_index, 0)
4472 OutputObjectList()
4475 def ReadInterfaces(ifile):
4476 """Reads the $MODULE.interfaces file.
4478 Args:
4479 ifile (str): the input filename.
4482 Interfaces.clear()
4484 if not os.path.isfile(ifile):
4485 return
4487 INPUT = open(ifile)
4489 for line in INPUT:
4490 line = line.strip()
4491 ifaces = line.split()
4492 gobject = ifaces.pop(0)
4493 if gobject in KnownSymbols and KnownSymbols[gobject] == 1:
4494 knownIfaces = []
4496 # filter out private interfaces, but leave foreign interfaces
4497 for iface in ifaces:
4498 if iface not in KnownSymbols or KnownSymbols[iface] == 1:
4499 knownIfaces.append(iface)
4501 Interfaces[gobject] = ' '.join(knownIfaces)
4502 logging.info("Interfaces for %s: %s", gobject, Interfaces[gobject])
4503 else:
4504 logging.info("skipping interfaces for unknown symbol: %s", gobject)
4506 INPUT.close()
4509 def ReadPrerequisites(ifile):
4510 """This reads in the $MODULE.prerequisites file.
4512 Args:
4513 ifile (str): the input filename.
4515 Prerequisites.clear()
4517 if not os.path.isfile(ifile):
4518 return
4520 INPUT = open(ifile)
4522 for line in INPUT:
4523 line = line.strip()
4524 prereqs = line.split()
4525 iface = prereqs.pop(0)
4526 if iface in KnownSymbols and KnownSymbols[iface] == 1:
4527 knownPrereqs = []
4529 # filter out private prerequisites, but leave foreign prerequisites
4530 for prereq in prereqs:
4531 if prereq not in KnownSymbols or KnownSymbols[prereq] == 1:
4532 knownPrereqs.append(prereq)
4534 Prerequisites[iface] = ' '.join(knownPrereqs)
4536 INPUT.close()
4539 def ReadArgsFile(ifile):
4540 """Reads information about object properties
4542 It creates the arrays ArgObjects, ArgNames, ArgTypes, ArgFlags, ArgNicks and
4543 ArgBlurbs containing info on the args.
4545 Args:
4546 ifile (str): the input filename.
4548 in_arg = False
4549 arg_object = None
4550 arg_name = None
4551 arg_type = None
4552 arg_flags = None
4553 arg_nick = None
4554 arg_blurb = None
4555 arg_default = None
4556 arg_range = None
4558 # Reset the args info.
4559 ArgObjects[:] = []
4560 ArgNames[:] = []
4561 ArgTypes[:] = []
4562 ArgFlags[:] = []
4563 ArgNicks[:] = []
4564 ArgBlurbs[:] = []
4565 ArgDefaults[:] = []
4566 ArgRanges[:] = []
4568 if not os.path.isfile(ifile):
4569 return
4571 INPUT = open(ifile)
4572 line_number = 0
4573 for line in INPUT:
4574 line_number += 1
4575 if not in_arg:
4576 if line.startswith('<ARG>'):
4577 in_arg = True
4578 arg_object = ''
4579 arg_name = ''
4580 arg_type = ''
4581 arg_flags = ''
4582 arg_nick = ''
4583 arg_blurb = ''
4584 arg_default = ''
4585 arg_range = ''
4587 else:
4588 m1 = re.search(r'^<NAME>(.*)</NAME>', line)
4589 m2 = re.search(r'^<TYPE>(.*)</TYPE>', line)
4590 m3 = re.search(r'^<RANGE>(.*)</RANGE>', line)
4591 m4 = re.search(r'^<FLAGS>(.*)</FLAGS>', line)
4592 m5 = re.search(r'^<NICK>(.*)</NICK>', line)
4593 m6 = re.search(r'^<BLURB>(.*)</BLURB>', line)
4594 m7 = re.search(r'^<DEFAULT>(.*)</DEFAULT>', line)
4595 if m1:
4596 arg_name = m1.group(1)
4597 m1_1 = re.search(r'^(.*)::(.*)$', arg_name)
4598 if m1_1:
4599 arg_object = m1_1.group(1)
4600 arg_name = m1_1.group(2).replace('_', '-')
4601 logging.info("Found arg: %s", arg_name)
4602 else:
4603 common.LogWarning(ifile, line_number, "Invalid argument name: " + arg_name)
4605 elif m2:
4606 arg_type = m2.group(1)
4607 elif m3:
4608 arg_range = m3.group(1)
4609 elif m4:
4610 arg_flags = m4.group(1)
4611 elif m5:
4612 arg_nick = m5.group(1)
4613 elif m6:
4614 arg_blurb = m6.group(1)
4615 if arg_blurb == "(null)":
4616 arg_blurb = ''
4617 common.LogWarning(
4618 ifile, line_number, "Property %s:%s has no documentation." % (arg_object, arg_name))
4620 elif m7:
4621 arg_default = m7.group(1)
4622 elif re.search(r'^</ARG>', line):
4623 logging.info("Found end of arg: %s::%s\n%s : %s", arg_object, arg_name, arg_type, arg_flags)
4624 ArgObjects.append(arg_object)
4625 ArgNames.append(arg_name)
4626 ArgTypes.append(arg_type)
4627 ArgRanges.append(arg_range)
4628 ArgFlags.append(arg_flags)
4629 ArgNicks.append(arg_nick)
4630 ArgBlurbs.append(arg_blurb)
4631 ArgDefaults.append(arg_default)
4632 in_arg = False
4634 INPUT.close()
4637 def AddTreeLineArt(tree):
4638 """Generate a line art tree.
4640 Add unicode lineart to a pre-indented string array and returns
4641 it as as multiline string.
4643 Args:
4644 tree (list): of indented strings.
4646 Returns:
4647 str: multiline string with tree line art
4649 # iterate bottom up over the tree
4650 for i in range(len(tree) - 1, -1, -1):
4651 # count leading spaces
4652 m = re.search(r'^([^<A-Za-z]*)', tree[i])
4653 indent = len(m.group(1))
4654 # replace with ╰───, if place of ╰ is not space insert ├
4655 if indent > 4:
4656 if tree[i][indent - 4] == " ":
4657 tree[i] = tree[i][:indent - 4] + "--- " + tree[i][indent:]
4658 else:
4659 tree[i] = tree[i][:indent - 4] + "+-- " + tree[i][indent:]
4661 # go lines up while space and insert |
4662 j = i - 1
4663 while j >= 0 and tree[j][indent - 4] == ' ':
4664 tree[j] = tree[j][:indent - 4] + '|' + tree[j][indent - 3:]
4665 j -= 1
4667 res = "\n".join(tree)
4668 # unicode chars for: ╰──
4669 res = re.sub(r'---', '<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>', res)
4670 # unicde chars for: ├──
4671 res = re.sub(r'\+--', '<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>', res)
4672 # unicode char for: │
4673 res = re.sub(r'\|', '<phrase role=\"lineart\">&#9474;</phrase>', res)
4675 return res
4678 def CheckIsObject(name):
4679 """Check if symbols is an object.
4681 It uses the global Objects array. Note that the Objects array only contains
4682 classes in the current module and their ancestors - not all GObject classes.
4684 Args:
4685 name (str): the object name to check.
4687 Returns:
4688 bool: True if the given name is a GObject or a subclass.
4690 root = ObjectRoots.get(name)
4691 # Let GBoxed pass as an object here to get -struct appended to the id
4692 # and prevent conflicts with sections.
4693 return root and root != 'GEnum' and root != 'GFlags'
4696 def GetSymbolSourceFile(symbol):
4697 """Get the filename where the symbol docs where taken from."""
4698 return SourceSymbolSourceFile.get(symbol, '')
4701 def GetSymbolSourceLine(symbol):
4702 """Get the file line where the symbol docs where taken from."""
4703 return SourceSymbolSourceLine.get(symbol, 0)