2 # -*- python; coding: utf-8 -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998 Damon Chaplin
6 # 2007-2016 Stefan Sauer
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #############################################################################
24 # Script : gtkdoc-mkdb
25 # Description : This creates the DocBook files from the edited templates.
26 #############################################################################
28 from __future__ import print_function
36 from gtkdoc import common
40 # name of documentation module
48 EXPAND_CONTENT_FILES = ''
49 INLINE_MARKUP_MODE = None
50 DEFAULT_STABILITY = None
51 DEFAULT_INCLUDES = None
54 OUTPUT_ALL_SYMBOLS = None
55 OUTPUT_SYMBOLS_WITHOUT_SINCE = None
57 OBJECT_TREE_FILE = None
58 INTERFACES_FILE = None
59 PREREQUISITES_FILE = None
63 # These global arrays store information on signals. Each signal has an entry
64 # in each of these arrays at the same index, like a multi-dimensional array.
65 SignalObjects = [] # The GtkObject which emits the signal.
66 SignalNames = [] # The signal name.
67 SignalReturns = [] # The return type.
68 SignalFlags = [] # Flags for the signal
69 SignalPrototypes = [] # The rest of the prototype of the signal handler.
71 # These global arrays store information on Args. Each Arg has an entry
72 # in each of these arrays at the same index, like a multi-dimensional array.
73 ArgObjects = [] # The GtkObject which has the Arg.
74 ArgNames = [] # The Arg name.
75 ArgTypes = [] # The Arg type - gint, GtkArrowType etc.
76 ArgFlags = [] # How the Arg can be used - readable/writable etc.
77 ArgNicks = [] # The nickname of the Arg.
78 ArgBlurbs = [] # Docstring of the Arg.
79 ArgDefaults = [] # Default value of the Arg.
80 ArgRanges = [] # The range of the Arg type
82 # These global hashes store declaration info keyed on a symbol name.
85 DeclarationConditional = {}
86 DeclarationOutput = {}
92 # These global hashes store the existing documentation.
98 SymbolAnnotations = {}
100 # These global hashes store documentation scanned from the source files.
101 SourceSymbolDocs = {}
102 SourceSymbolParams = {}
103 SourceSymbolSourceFile = {}
104 SourceSymbolSourceLine = {}
106 # all documentation goes in here, so we can do coverage analysis
108 AllIncompleteSymbols = {}
109 AllUnusedSymbols = {}
110 AllDocumentedSymbols = {}
112 # Undeclared yet documented symbols
113 UndeclaredSymbols = {}
115 # These global arrays store GObject, subclasses and the hierarchy (also of
116 # non-object derived types).
124 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
125 # section they are defined
130 # collects index entries
131 IndexEntriesFull = {}
132 IndexEntriesSince = {}
133 IndexEntriesDeprecated = {}
135 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
136 PreProcessorDirectives = {
154 # remember used annotation (to write minimal glossary)
157 # the regexp that parses the annotation is in ScanSourceFile()
158 AnnotationDefinition = {
159 # the GObjectIntrospection annotations are defined at:
160 # https://live.gnome.org/GObjectIntrospection/Annotations
161 'allow-none': "NULL is OK, both for passing and for returning.",
162 'nullable': "NULL may be passed as the value in, out, in-out; or as a return value.",
163 'not nullable': "NULL must not be passed as the value in, out, in-out; or as a return value.",
164 'optional': "NULL may be passed instead of a pointer to a location.",
165 'array': "Parameter points to an array of items.",
166 'attribute': "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
167 'attributes': "Free-form key-value pairs.",
168 'closure': "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
169 'constructor': "This symbol is a constructor, not a static method.",
170 'destroy': "This parameter is a 'destroy_data', for callbacks.",
171 'default': "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
172 'element-type': "Generics and defining elements of containers and arrays.",
173 'error-domains': "Typed errors. Similar to throws in Java.",
174 'foreign': "This is a foreign struct.",
175 'get-value-func': "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
176 'in': "Parameter for input. Default is <acronym>transfer none</acronym>.",
177 'inout': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
178 'in-out': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
179 'method': "This is a method",
180 'not-error': "A GError parameter is not to be handled like a normal GError.",
181 'out': "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
182 'out caller-allocates': "Out parameter, where caller must allocate storage.",
183 'out callee-allocates': "Out parameter, where caller must allocate storage.",
184 'ref-func': "The specified function is used to ref a struct, must be a GTypeInstance.",
185 'rename-to': "Rename the original symbol's name to SYMBOL.",
186 'scope call': "The callback is valid only during the call to the method.",
187 'scope async': "The callback is valid until first called.",
188 'scope notified': "The callback is valid until the GDestroyNotify argument is called.",
189 'set-value-func': "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
190 'skip': "Exposed in C code, not necessarily available in other languages.",
191 'transfer container': "Free data container after the code is done.",
192 'transfer floating': "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
193 'transfer full': "Free data after the code is done.",
194 'transfer none': "Don't free data after the code is done.",
195 'type': "Override the parsed C type with given type.",
196 'unref-func': "The specified function is used to unref a struct, must be a GTypeInstance.",
197 'virtual': "This is the invoker for a virtual method.",
198 'value': "The specified value overrides the evaluated value of the constant.",
199 # Stability Level definition
200 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
202 The intention of a Stable interface is to enable arbitrary third parties to
203 develop applications to these interfaces, release them, and have confidence that
204 they will run on all minor releases of the product (after the one in which the
205 interface was introduced, and within the same major release). Even at a major
206 release, incompatible changes are expected to be rare, and to have strong
210 Unstable interfaces are experimental or transitional. They are typically used to
211 give outside developers early access to new or rapidly changing technology, or
212 to provide an interim solution to a problem where a more general solution is
213 anticipated. No claims are made about either source or binary compatibility from
214 one minor release to the next.
216 The Unstable interface level is a warning that these interfaces are subject to
217 change without warning and should not be used in unbundled products.
219 Given such caveats, customer impact need not be a factor when considering
220 incompatible changes to an Unstable interface in a major or minor release.
221 Nonetheless, when such changes are introduced, the changes should still be
222 mentioned in the release notes for the affected release.
225 An interface that can be used within the GNOME stack itself, but that is not
226 documented for end-users. Such functions should only be used in specified and
231 # Elements to consider non-block items in MarkDown parsing
232 MD_TEXT_LEVEL_ELEMENTS = {"literal": 1,
247 MD_ESCAPABLE_CHARS = {"\\": 1,
264 MD_GTK_ESCAPABLE_CHARS = {"@": 1,
268 # Function and other declaration output settings.
269 RETURN_TYPE_FIELD_WIDTH = 20
270 SYMBOL_FIELD_WIDTH = 36
271 MAX_SYMBOL_FIELD_WIDTH = 40
272 SIGNAL_FIELD_WIDTH = 16
273 PARAM_FIELD_COUNT = 2
275 # XML, SGML formatting helper
276 doctype_header = None
279 parser = argparse.ArgumentParser()
280 parser.add_argument('--module', default='')
281 parser.add_argument('--source-dir', dest='source_dir', default='')
282 parser.add_argument('--source-suffixes', dest='source_suffixes', default='')
283 parser.add_argument('--ignore-files', dest='ignore_files', default='')
284 parser.add_argument('--output-dir', dest='output_dir', default='')
285 parser.add_argument('--tmpl-dir', dest='tmpl_dir', default='')
286 parser.add_argument('--main-sgml-file', dest='main_sgml_file', default='')
287 parser.add_argument('--expand-content-files', dest='expand_content_files', default='')
288 group = parser.add_mutually_exclusive_group()
289 group.add_argument('--sgml-mode', action='store_true', default=False, dest='sgml_mode')
290 group.add_argument('--xml-mode', action='store_true', default=False, dest='xml_mode')
291 parser.add_argument('--default-stability', dest='default_stability', choices=['Stable', 'Private', 'Unstable'], default='Stable')
292 parser.add_argument('--default-includes', dest='default_includes', default='')
293 parser.add_argument('--output-format', dest='default_format', default='')
294 parser.add_argument('--name-space', dest='name_space', default='')
295 parser.add_argument('--outputallsymbols', default=False, action='store_true')
296 parser.add_argument('--outputsymbolswithoutsince', default=False, action='store_true')
300 global MODULE, TMPL_DIR, SOURCE_DIRS, SOURCE_SUFFIXES, IGNORE_FILES, MAIN_SGML_FILE, EXPAND_CONTENT_FILES, \
301 INLINE_MARKUP_MODE, DEFAULT_STABILITY, DEFAULT_INCLUDES, OUTPUT_FORMAT, NAME_SPACE, OUTPUT_ALL_SYMBOLS, \
302 OUTPUT_SYMBOLS_WITHOUT_SINCE, DB_OUTPUT_DIR, OBJECT_TREE_FILE, INTERFACES_FILE, PREREQUISITES_FILE, \
303 SIGNALS_FILE, ARGS_FILE, doctype_header
305 options = parser.parse_args()
307 # We should pass the options variable around instead of this global variable horror
308 # but too much of the code expects these to be around. Fix this once the transition is done.
309 MODULE = options.module
310 TMPL_DIR = options.tmpl_dir
311 SOURCE_DIRS = options.source_dir
312 SOURCE_SUFFIXES = options.source_suffixes
313 IGNORE_FILES = options.ignore_files
314 MAIN_SGML_FILE = options.main_sgml_file
315 EXPAND_CONTENT_FILES = options.expand_content_files
316 INLINE_MARKUP_MODE = options.xml_mode or options.sgml_mode
317 DEFAULT_STABILITY = options.default_stability
318 DEFAULT_INCLUDES = options.default_includes
319 OUTPUT_FORMAT = options.default_format
320 NAME_SPACE = options.name_space
321 OUTPUT_ALL_SYMBOLS = options.outputallsymbols
322 OUTPUT_SYMBOLS_WITHOUT_SINCE = options.outputsymbolswithoutsince
324 logging.info(" ignore files: " + IGNORE_FILES)
326 # check output format
327 if OUTPUT_FORMAT is None or OUTPUT_FORMAT == '':
328 OUTPUT_FORMAT = "xml"
330 OUTPUT_FORMAT = OUTPUT_FORMAT.lower()
331 if OUTPUT_FORMAT != "xml":
332 sys.exit("Invalid format '%s' passed to --output.format" % OUTPUT_FORMAT)
334 if not MAIN_SGML_FILE:
335 # backwards compatibility
336 if os.path.exists(MODULE + "-docs.sgml"):
337 MAIN_SGML_FILE = MODULE + "-docs.sgml"
339 MAIN_SGML_FILE = MODULE + "-docs.xml"
341 # extract docbook header or define default
342 if os.path.exists(MAIN_SGML_FILE):
343 INPUT = open(MAIN_SGML_FILE)
346 if re.search(r'^\s*<(book|chapter|article)', line):
347 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
348 if not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', line) and not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', doctype_header, flags=re.MULTILINE):
352 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
353 # FIXME: not sure if we can do this now, as people already work-around the problem
354 # s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#;
355 #<!ENTITY % gtkdocentities SYSTEM \"([^"]*)\">#<!ENTITY % gtkdocentities SYSTEM \"../$1\">#;
356 doctype_header += line
359 doctype_header = '''<?xml version="1.0"?>
360 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
361 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
363 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
364 <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
369 doctype_header = doctype_header.strip()
371 # All the files are written in subdirectories beneath here.
372 TMPL_DIR = TMPL_DIR if TMPL_DIR else os.path.join(ROOT_DIR, "tmpl")
374 # This is where we put all the DocBook output.
375 DB_OUTPUT_DIR = DB_OUTPUT_DIR if DB_OUTPUT_DIR else os.path.join(ROOT_DIR, "xml")
377 # This file contains the object hierarchy.
378 OBJECT_TREE_FILE = os.path.join(ROOT_DIR, MODULE + ".hierarchy")
380 # This file contains the interfaces.
381 INTERFACES_FILE = os.path.join(ROOT_DIR, MODULE + ".interfaces")
383 # This file contains the prerequisites.
384 PREREQUISITES_FILE = os.path.join(ROOT_DIR, MODULE + ".prerequisites")
386 # This file contains signal arguments and names.
387 SIGNALS_FILE = os.path.join(ROOT_DIR, MODULE + ".signals")
389 # The file containing Arg information.
390 ARGS_FILE = os.path.join(ROOT_DIR, MODULE + ".args")
392 # Create the root DocBook output directory if it doens't exist.
393 if not os.path.isdir(DB_OUTPUT_DIR):
394 os.mkdir(DB_OUTPUT_DIR)
396 ReadKnownSymbols(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
397 ReadSignalsFile(SIGNALS_FILE)
398 ReadArgsFile(ARGS_FILE)
399 ReadObjectHierarchy()
403 ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-decl.txt"), 0)
404 if os.path.isfile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt")):
405 ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt"), 1)
407 for sdir in SOURCE_DIRS:
408 ReadSourceDocumentation(sdir)
410 changed = OutputDB(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
412 # If any of the DocBook files have changed, update the timestamp file (so
413 # it can be used for Makefile dependencies).
414 if changed or not os.path.exists(os.path.join(ROOT_DIR, "sgml.stamp")):
416 # try to detect the common prefix
417 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
425 for symbol in IndexEntriesFull.keys():
426 if NAME_SPACE == '' or NAME_SPACE.lower() in symbol.lower():
427 if len(symbol) > pos:
428 letter = symbol[pos:pos+1]
429 # stop prefix scanning
433 # Should we also stop on a uppercase char, if last was lowercase
434 # GtkWidget, if we have the 'W' and had the 't' before
435 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
436 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
437 # need to recound each time as this is per symbol
444 if letter != '' and letter != "_":
447 for letter in prefix.keys():
448 #print "$letter: $prefix{$letter}.\n";
449 if prefix[letter] > maxsymbols:
451 maxsymbols = prefix[letter]
453 ratio = float(len(IndexEntriesFull)) / prefix[maxletter]
454 #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
457 NAME_SPACE += maxletter
468 OutputDeprecatedIndex()
470 OutputAnnotationGlossary()
472 open(os.path.join(ROOT_DIR, 'sgml.stamp'), 'w').write('timestamp')
474 #############################################################################
475 # Function : OutputObjectList
476 # Description : This outputs the alphabetical list of objects, in a columned
478 # FIXME: Currently this also outputs ancestor objects
479 # which may not actually be in this module.
481 #############################################################################
483 def OutputObjectList():
487 # my $old_object_index = "$DB_OUTPUT_DIR/object_index.xml";
488 old_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.sgml")
489 new_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.new")
491 OUTPUT = open(new_object_index, 'w')
495 <informaltable pgwide="1" frame="none">
496 <tgroup cols="$cols">
497 <colspec colwidth="1*"/>
498 <colspec colwidth="1*"/>
499 <colspec colwidth="1*"/>
501 ''' % MakeDocHeader("informaltable"))
505 for object in sorted(Objects):
506 xref = MakeXRef(object)
507 if count % cols == 0:
508 OUTPUT.write("<row>\n")
509 OUTPUT.write("<entry>%s</entry>\n" % xref)
510 if count % cols == cols - 1:
511 OUTPUT.write("</row>\n")
515 # emit an empty row, since empty tables are invalid
516 OUTPUT.write("<row><entry> </entry></row>\n")
520 OUTPUT.write("</row>\n")
522 OUTPUT.write('''</tbody></tgroup></informaltable>\n''')
525 common.UpdateFileIfChanged(old_object_index, new_object_index, 0)
528 #############################################################################
529 # Function : TrimTextBlock
530 # Description : Trims extra whitespace. Empty lines inside a block are
532 # Arguments : $desc - the text block to trim. May contain newlines.
533 #############################################################################
535 def TrimTextBlock(desc):
536 # strip leading spaces on the block
537 desc = re.sub(r'^\s+', '', desc)
538 # strip trailing spaces on every line
539 desc = re.sub(r'\s+$', '\n', desc, flags=re.MULTILINE)
544 #############################################################################
545 # Function : OutputDB
546 # Description : This collects the output for each section of the docs, and
547 # outputs each file when the end of the section is found.
548 # Arguments : $file - the $MODULE-sections.txt file which contains all of
549 # the functions/macros/structs etc. being documented, organised
550 # into sections and subsections.
551 #############################################################################
555 logging.info("Reading: %s", file)
560 includes = DEFAULT_INCLUDES if DEFAULT_INCLUDES else ''
561 section_includes = ''
570 functions_details = ''
575 child_args_synop = ''
576 style_args_synop = ''
590 # merge the source docs, in case there are no templates
591 MergeSourceDocumentation()
597 if line.strip() == '':
600 m1 = re.search(r'^<SUBSECTION\s*(.*)', line)
601 m2 = re.search(r'^<TITLE>(.*)<\/TITLE', line)
602 m3 = re.search(r'^<FILE>(.*)<\/FILE>', line)
603 m4 = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
604 m5 = re.search(r'^(\S+)', line)
606 if line.startswith('<SECTION>'):
614 functions_synop += "\n"
615 subsection = m1.group(1)
617 elif re.search(r'^<SUBSECTION>', line):
621 logging.info("Section: %s", title)
623 # We don't want warnings if object & class structs aren't used.
624 DeclarationOutput[title] = 1
625 DeclarationOutput["%sClass" % title] = 1
626 DeclarationOutput["%sIface" % title] = 1
627 DeclarationOutput["%sInterface" % title] = 1
630 filename = m3.group(1)
631 if filename in templates:
632 if ReadTemplateFile(os.path.join(TMPL_DIR, filename), 1):
633 MergeSourceDocumentation()
634 templates[filename] = line_number
636 common.LogWarning(file, line_number, ("Double <FILE>%s</FILE> entry. " % file) + "Previous occurrence on line " + templates[filename] + ".")
637 if title == '' and ("%s/%s:Title" % (TMPL_DIR, filename)) & SourceSymbolDocs:
638 title = SourceSymbolDocs["%s/%s:Title" % (TMPL_DIR, filename)]
639 # Remove trailing blanks
640 title = title.rstrip()
644 section_includes = m4.group(1)
647 common.LogWarning(file, line_number, "Default <INCLUDE> being overridden by command line option.")
649 includes = m4.group(1)
651 elif re.search(r'^<\/SECTION>', line):
652 logging.info("End of section: $title\n")
655 book_bottom += " <xi:include href=\"xml/%s.xml\"/>\n" % filename
657 i = os.path.join(TMPL_DIR, filename + ":Include")
659 if i in SourceSymbolDocs:
661 common.LogWarning(file, line_number, "Section <INCLUDE> being overridden by inline comments.")
662 section_includes = SourceSymbolDocs[i]
664 if section_includes == '':
665 section_includes = includes
667 signals_synop = re.sub(r'^\n*', '', signals_synop)
668 signals_synop = re.sub(r'\n+$', '\n', signals_synop)
670 if signals_synop != '':
671 signals_synop = '''<refsect1 id="%s.signals" role="signal_proto">
672 <title role="signal_proto.title">Signals</title>
673 <informaltable frame="none">
675 <colspec colname="signals_return" colwidth="150px"/>
676 <colspec colname="signals_name" colwidth="300px"/>
677 <colspec colname="signals_flags" colwidth="200px"/>
684 ''' % (section_id, signals_synop)
685 signals_desc = TrimTextBlock(signals_desc)
686 signals_desc = '''<refsect1 id="%s.signal-details" role="signals">
687 <title role="signals.title">Signal Details</title>
690 ''' % (section_id, signals_desc)
692 args_synop = re.sub(r'^\n*', '', args_synop)
693 args_synop = re.sub(r'\n+$', '\n', args_synop)
695 args_synop = '''<refsect1 id="%s.properties" role="properties">
696 <title role="properties.title">Properties</title>
697 <informaltable frame="none">
699 <colspec colname="properties_type" colwidth="150px"/>
700 <colspec colname="properties_name" colwidth="300px"/>
701 <colspec colname="properties_flags" colwidth="200px"/>
708 ''' %(section_id, args_synop)
709 args_desc = TrimTextBlock(args_desc)
710 args_desc = '''<refsect1 id="%s.property-details" role="property_details">
711 <title role="property_details.title">Property Details</title>
714 ''' % (section_id, args_desc)
716 child_args_synop = re.sub(r'^\n*', '', child_args_synop)
717 child_args_synop = re.sub(r'\n+$', '\n', child_args_synop)
718 if child_args_synop != '':
719 args_synop += '''<refsect1 id="%s.child-properties" role="child_properties">
720 <title role="child_properties.title">Child Properties</title>
721 <informaltable frame="none">
723 <colspec colname="child_properties_type" colwidth="150px"/>
724 <colspec colname="child_properties_name" colwidth="300px"/>
725 <colspec colname="child_properties_flags" colwidth="200px"/>
732 ''' % (section_id, child_args_synop)
733 child_args_desc = TrimTextBlock(child_args_desc)
734 args_desc += '''<refsect1 id="%s.child-property-details" role="child_property_details">
735 <title role="child_property_details.title">Child Property Details</title>
738 ''' % (section_id, child_args_desc)
741 style_args_synop = re.sub(r'^\n*', '', style_args_synop)
742 style_args_synop = re.sub(r'\n+$', '\n', style_args_synop)
743 if style_args_synop != '':
744 args_synop += '''<refsect1 id="%s.style-properties" role="style_properties">
745 <title role="style_properties.title">Style Properties</title>
746 <informaltable frame="none">
748 <colspec colname="style_properties_type" colwidth="150px"/>
749 <colspec colname="style_properties_name" colwidth="300px"/>
750 <colspec colname="style_properties_flags" colwidth="200px"/>
757 ''' % (section_id, style_args_synop)
758 style_args_desc = TrimTextBlock(style_args_desc)
759 args_desc += '''<refsect1 id="%s.style-property-details" role="style_properties_details">
760 <title role="style_properties_details.title">Style Property Details</title>
763 ''' % (section_id, style_args_desc)
765 hierarchy_str = AddTreeLineArt(hierarchy)
766 if hierarchy_str != '':
767 hierarchy_str = '''<refsect1 id="%s.object-hierarchy" role="object_hierarchy">
768 <title role="object_hierarchy.title">Object Hierarchy</title>
772 ''' % (section_id, hierarchy_str)
774 interfaces = TrimTextBlock(interfaces)
776 interfaces = '''<refsect1 id="%s.implemented-interfaces" role="impl_interfaces">
777 <title role="impl_interfaces.title">Implemented Interfaces</title>
780 ''' % (section_id, interfaces)
782 implementations = TrimTextBlock(implementations)
783 if implementations != '':
784 implementations = '''<refsect1 id="%s.implementations" role="implementations">
785 <title role="implementations.title">Known Implementations</title>
788 ''' % (section_id, implementations)
790 prerequisites = TrimTextBlock(prerequisites)
791 if prerequisites != '':
792 prerequisites = '''<refsect1 id="%s.prerequisites" role="prerequisites">
793 <title role="prerequisites.title">Prerequisites</title>
796 ''' % (section_id, prerequisites)
799 derived = TrimTextBlock(derived)
801 derived = '''<refsect1 id="%s.derived-interfaces" role="derived_interfaces">
802 <title role="derived_interfaces.title">Known Derived Interfaces</title>
805 ''' % (section_id, derived)
807 functions_synop = re.sub(r'^\n*', '', functions_synop)
808 functions_synop = re.sub(r'\n+$', '\n', functions_synop)
809 if functions_synop != '':
810 functions_synop = '''<refsect1 id="%s.functions" role="functions_proto">
811 <title role="functions_proto.title">Functions</title>
812 <informaltable pgwide="1" frame="none">
814 <colspec colname="functions_return" colwidth="150px"/>
815 <colspec colname="functions_name"/>
822 ''' % (section_id, functions_synop)
824 other_synop = re.sub(r'^\n*', '', other_synop)
825 other_synop = re.sub(r'\n+$', '\n', other_synop)
826 if other_synop != '':
827 other_synop = '''<refsect1 id="%s.other" role="other_proto">
828 <title role="other_proto.title">Types and Values</title>
829 <informaltable role="enum_members_table" pgwide="1" frame="none">
831 <colspec colname="name" colwidth="150px"/>
832 <colspec colname="description"/>
839 ''' % (section_id, other_synop)
841 file_changed = OutputDBFile(filename, title, section_id,
843 functions_synop, other_synop,
844 functions_details, other_details,
845 signals_synop, signals_desc,
846 args_synop, args_desc,
847 hierarchy_str, interfaces,
849 prerequisites, derived,
858 section_includes = ''
861 functions_details = ''
866 child_args_synop = ''
867 style_args_synop = ''
880 logging.info(" Symbol: %s in subsection: %s", symbol, subsection)
882 # check for duplicate entries
883 if symbol in symbol_def_line:
884 declaration = Declarations.get(symbol, None)
886 if CheckIsObject(symbol):
887 file_objects.append(symbol)
889 # We don't want standard macros/functions of GObjects,
890 # or private declarations.
891 if subsection != "Standard" and subsection != "Private":
892 synop, desc = OutputDeclaration(symbol, declaration)
893 type = DeclarationTypes[symbol]
895 if type == 'FUNCTION' or type == 'USER_FUNCTION':
896 functions_synop += synop
897 functions_details += desc
898 elif type == 'MACRO' and re.search(symbol + r'\(', declaration):
899 functions_synop += synop
900 functions_details += desc
903 other_details += +desc
905 sig_synop, sig_desc = GetSignals(symbol)
906 arg_synop, child_arg_synop, style_arg_synop, arg_desc, child_arg_desc, style_arg_desc = GetArgs(symbol)
907 ifaces = GetInterfaces(symbol)
908 impls = GetImplementations(symbol)
909 prereqs = GetPrerequisites(symbol)
910 der = GetDerived(symbol)
911 hierarchy = GetHierarchy(symbol, hierarchy)
913 signals_synop += sig_synop
914 signals_desc += sig_desc
915 args_synop += arg_synop
916 child_args_synop += child_arg_synop
917 style_args_synop += style_arg_synop
918 args_desc += arg_desc
919 child_args_desc += child_arg_desc
920 style_args_desc += style_arg_desc
922 implementations += impls
923 prerequisites += prereqs
926 # Note that the declaration has been output.
927 DeclarationOutput[symbol] = True
928 elif subsection != "Standard" and subsection != "Private":
929 UndeclaredSymbols[symbol] = True
930 common.LogWarning(file, line_number, "No declaration found for %s." % symbol)
933 symbol_def_line[symbol] = line_number
936 if title == '' and filename == '':
937 common.LogWarning(file, line_number, "Section has no title and no file.")
939 # FIXME: one of those would be enough
940 # filename should be an internal detail for gtk-doc
946 filename = filename.replace(' ', '_')
948 section_id = SourceSymbolDocs[os.path.join(TMPL_DIR, filename + ":Section_Id")]
949 if section_id and section_id.strip() != '':
950 # Remove trailing blanks and use as is
951 section_id = section_id.rstrip()
952 elif CheckIsObject(title):
953 # GObjects use their class name as the ID.
954 section_id = common.CreateValidSGMLID(title)
956 section_id = common.CreateValidSGMLID(MODULE + '-' + title)
958 SymbolSection[symbol] = title
959 SymbolSectionId[symbol] = section_id
962 common.LogWarning(file, line_number, "Double symbol entry for %s. "
963 "Previous occurrence on line %d." % (symbol, symbol_def_line[symbol]))
966 OutputMissingDocumentation()
967 OutputUndeclaredSymbols()
968 OutputUnusedSymbols()
970 if OUTPUT_ALL_SYMBOLS:
973 if OUTPUT_SYMBOLS_WITHOUT_SINCE:
974 OutputSymbolsWithoutSince()
977 for filename in EXPAND_CONTENT_FILES.split():
978 file_changed = OutputExtraFile(filename)
982 OutputBook(book_top, book_bottom)
986 #############################################################################
987 # Function : OutputIndex
988 # Description : This writes an indexlist that can be included into the main-
989 # document into an <index> tag.
990 #############################################################################
992 def OutputIndex(basename, apiindexref):
993 apiindex = apiindexref # FIXME should we copy here?
994 old_index = os.path.join(DB_OUTPUT_DIR, basename + '.xml')
995 new_index = os.path.join(DB_OUTPUT_DIR, basename + '.new')
1001 OUTPUT = open(new_index, 'w')
1003 OUTPUT.write(MakeDocHeader("indexdiv") + "\n<indexdiv id=\"%s\">\n" % basename)
1005 logging.info("generate %s index (%d entries)\n", basename, len(apiindex))
1007 # do a case insensitive sort while chopping off the prefix
1010 criteria = re.sub(r'^' + NAME_SPACE + r'\_?(.*)', r'\1', uc, flags=re.I)
1011 return (criteria, intxt)
1013 sorted_keys = sorted(apiindex.keys(), key=cpmfunc)
1015 for original in sorted_keys:
1016 symbol = apiindex[original]
1017 match = re.search(r'^' + NAME_SPACE + r'\_?(.*)', original, flags=re.I)
1018 short = match.group(1)
1019 if short and short != '':
1020 short_symbol = short
1022 short_symbol = symbol
1024 # generate a short symbol description
1027 symbol_section_id = ''
1029 if symbol in DeclarationTypes:
1030 symbol_type = DeclarationTypes[symbol].lower()
1032 if symbol_type == '':
1033 logging.info("trying symbol %s\n", symbol)
1034 m = re.search(r'(.*)::(.*)', symbol)
1035 m2 = re.search(r'(.*):(.*)/', symbol)
1040 logging.info(" trying object signal %s:%s in %d signals\n", oname, osym, len(SignalNames))
1041 for i in range(len(SignalNames)):
1042 if SignalNames[i] == osym:
1043 symbol_type = "object signal"
1044 if oname in SymbolSection:
1045 symbol_section = SymbolSection[oname]
1046 symbol_section_id = SymbolSectionId[oname]
1052 logging.info(" trying object property %s::%s in %d properties\n", oname, osym, len(ArgNames))
1053 for i in range(len(ArgNames)):
1054 logging.info(" " + ArgNames[i] + "\n")
1055 if ArgNames[i] == osym:
1056 symbol_type = "object property"
1057 if oname in SymbolSection:
1058 symbol_section = SymbolSection[oname]
1059 symbol_section_id = SymbolSectionId[oname]
1062 if symbol in SymbolSection:
1063 symbol_section = SymbolSection[symbol]
1064 symbol_section_id = SymbolSectionId[symbol]
1066 if symbol_type != '':
1067 symbol_desc = ", " + symbol_type
1068 if symbol_section != '':
1069 symbol_desc += " in <link linkend=\"%s\">%s</link>" % (symbol_section_id, symbol_section)
1070 #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section")
1072 curletter = short_symbol[0].upper()
1073 id = apiindex[symbol]
1075 logging.info(" add symbol %s with %d to index in section '%s' (derived from %s)\n", symbol, id, curletter, short_symbol)
1077 if curletter != lastletter:
1078 lastletter = curletter
1081 OUTPUT.write("</indexdiv>\n")
1083 OUTPUT.write("<indexdiv><title>$curletter</title>\n")
1086 OUTPUT.write('<indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>\n')
1089 OUTPUT.write("</indexdiv>\n")
1091 OUTPUT.write("</indexdiv>\n")
1094 common.UpdateFileIfChanged(old_index, new_index, 0)
1098 #############################################################################
1099 # Function : OutputIndexFull
1100 # Description : This writes the full api indexlist that can be included into the
1101 # main document into an <index> tag.
1102 #############################################################################
1104 def OutputIndexFull():
1105 OutputIndex("api-index-full", IndexEntriesFull)
1108 #############################################################################
1109 # Function : OutputDeprecatedIndex
1110 # Description : This writes the deprecated api indexlist that can be included
1111 # into the main document into an <index> tag.
1112 #############################################################################
1114 def OutputDeprecatedIndex():
1115 OutputIndex("api-index-deprecated", IndexEntriesDeprecated)
1118 #############################################################################
1119 # Function : OutputSinceIndexes
1120 # Description : This writes the 'since' api indexlists that can be included into
1121 # the main document into an <index> tag.
1122 #############################################################################
1124 def OutputSinceIndexes():
1125 raise RuntimeError('I have no idea what this does.')
1126 # my @sinces = keys %{{ map { $_ => 1 } values %Since }
1128 # foreach my $version (@sinces)
1129 # logging.info("Since : [$version]\n")
1130 # # TODO make filtered hash
1131 # #my %index = grep { $Since{$_} == $version } %IndexEntriesSince
1132 # my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} == $version } keys %IndexEntriesSince
1134 # &OutputIndex ("api-index-$version", \%index)
1138 #############################################################################
1139 # Function : OutputAnnotationGlossary
1140 # Description : This writes a glossary of the used annotation terms into a
1141 # separate glossary file that can be included into the main
1143 #############################################################################
1145 def OutputAnnotationGlossary():
1146 old_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.xml")
1147 new_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.new")
1151 # if there are no annotations used return
1152 if len(AnnotationsUsed) == 0:
1155 # add acronyms that are referenced from acronym text
1157 for annotation in AnnotationsUsed:
1158 if annotation in AnnotationDefinition:
1159 m = re.search(r'<acronym>([\w ]+)<\/acronym>', AnnotationDefinition[annotation])
1161 if m.group(1) in AnnotationsUsed:
1162 AnnotationsUsed[m.group(1)] = 1
1163 return OutputAnnotationGlossary()
1165 OUTPUT = open(new_glossary, 'w')
1168 <glossary id="annotation-glossary">
1169 <title>Annotation Glossary</title>
1170 ''' % MakeDocHeader("glossary"))
1172 for annotation in sorted(AnnotationsUsed.keys(), keys=str.lower()):
1173 if annotation in AnnotationDefinition:
1174 defi = AnnotationDefinition[annotation]
1175 curletter = annotation[0].upper()
1177 if curletter != lastletter:
1178 lastletter = curletter
1181 OUTPUT.write("</glossdiv>\n")
1183 OUTPUT.write("<glossdiv><title>%s</title>\n" % curletter)
1188 <glossterm><anchor id="annotation-glossterm-%s"/>%s</glossterm>
1193 ''' % (annotation, annotation, defi))
1196 OUTPUT.write("</glossdiv>\n")
1198 OUTPUT.write("</glossary>\n")
1201 common.UpdateFileIfChanged(old_glossary, new_glossary, 0)
1204 #############################################################################
1205 # Function : ReadKnownSymbols
1206 # Description : This collects the names of non-private symbols from the
1207 # $MODULE-sections.txt file.
1208 # Arguments : $file - the $MODULE-sections.txt file which contains all of
1209 # the functions/macros/structs etc. being documented, organised
1210 # into sections and subsections.
1211 #############################################################################
1213 def ReadKnownSymbols(file):
1217 logging.info("Reading: %s", file)
1221 if line.strip() == '':
1224 m = re.search(r'^<SECTION>', line)
1229 m = re.search(r'^<SUBSECTION\s*(.*)', line, flags=re.I)
1231 subsection = m.group(1)
1234 if re.search(r'^<SUBSECTION>', line):
1237 if re.search(r'^<TITLE>(.*)<\/TITLE>', line):
1240 m = re.search(r'^<FILE>(.*)<\/FILE>', line)
1242 KnownSymbols[os.path.join(TMPL_DIR, m.group(1) + ":Long_Description")] = 1
1243 KnownSymbols[os.path.join(TMPL_DIR, m.group(1) + ":Short_Description")] = 1
1246 m = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
1250 m = re.search(r'^<\/SECTION>', line)
1254 m = re.search(r'^(\S+)', line)
1258 if subsection != "Standard" and subsection != "Private":
1259 KnownSymbols[symbol] = 1
1261 KnownSymbols[symbol] = 0
1264 #############################################################################
1265 # Function : OutputDeclaration
1266 # Description : Returns the synopsis and detailed description DocBook
1267 # describing one function/macro etc.
1268 # Arguments : $symbol - the name of the function/macro begin described.
1269 # $declaration - the declaration of the function/macro.
1270 #############################################################################
1272 def OutputDeclaration(symbol, declaration):
1273 dtype = DeclarationTypes[symbol]
1274 if dtype == 'MACRO':
1275 return OutputMacro(symbol, declaration)
1276 elif dtype == 'TYPEDEF':
1277 return OutputTypedef(symbol, declaration)
1278 elif dtype == 'STRUCT':
1279 return OutputStruct(symbol, declaration)
1280 elif dtype == 'ENUM':
1281 return OutputEnum(symbol, declaration)
1282 elif dtype == 'UNION':
1283 return OutputUnion(symbol, declaration)
1284 elif dtype == 'VARIABLE':
1285 return OutputVariable(symbol, declaration)
1286 elif dtype == 'FUNCTION':
1287 return OutputFunction(symbol, declaration, dtype)
1288 elif dtype == 'USER_FUNCTION':
1289 return OutputFunction(symbol, declaration, dtype)
1291 sys.exit("Unknown symbol type " + dtype)
1294 #############################################################################
1295 # Function : OutputSymbolTraits
1296 # Description : Returns the Since and StabilityLevel paragraphs for a symbol.
1297 # Arguments : $symbol - the name of the function/macro begin described.
1298 #############################################################################
1300 def OutputSymbolTraits(symbol):
1304 link_id = "api-index-" + Since[symbol]
1305 desc += "<para role=\"since\">Since: <link linkend=\"%s\">%s</link></para>" % (link_id, Since[symbol])
1307 if symbol in StabilityLevel:
1308 stability = StabilityLevel[symbol]
1309 AnnotationsUsed[stability] = True
1310 desc += "<para role=\"stability\">Stability Level: <acronym>%s</acronym></para>" % stability
1314 #############################################################################
1315 # Function : Output{Symbol,Section}ExtraLinks
1316 # Description : Returns extralinks for the symbol (if enabled).
1317 # Arguments : $symbol - the name of the function/macro begin described.
1318 #############################################################################
1320 def uri_escape(text):
1324 # Build a char to hex map
1326 for i in range(256):
1327 escapes[chr(i)] = "%%%02X" % i
1329 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1330 def do_escape(char):
1331 return escapes[char]
1332 text = re.sub(r"([^A-Za-z0-9\-_.!~*'()]", do_escape, text)
1336 def OutputSymbolExtraLinks(symbol):
1339 if False: # NEW FEATURE: needs configurability
1340 sstr = uri_escape(symbol)
1341 mstr = uri_escape(MODULE)
1342 desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink>
1343 <ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&symbol=%s">edit documentation</ulink>
1344 ''' % (sstr, mstr, sstr)
1349 def OutputSectionExtraLinks(symbol, docsymbol):
1352 if False: # NEW FEATURE: needs configurability
1353 sstr = uri_escape(symbol)
1354 mstr = uri_escape(MODULE)
1355 dsstr = uri_escape(docsymbol)
1356 desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink>
1357 <ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&symbol=%s">edit documentation</ulink>
1358 ''' % (sstr, mstr, dsstr)
1362 #############################################################################
1363 # Function : OutputMacro
1364 # Description : Returns the synopsis and detailed description of a macro.
1365 # Arguments : $symbol - the macro.
1366 # $declaration - the declaration of the macro.
1367 #############################################################################
1369 def OutputMacro(symbol, declaration):
1370 sid = common.CreateValidSGMLID(symbol)
1371 condition = MakeConditionDescription(symbol)
1372 synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link>" % (sid, symbol)
1375 fields = common.ParseMacroDeclaration(declaration, CreateValidSGML)
1376 title = symbol +'()' if len(fields) > 0 else ''
1378 desc = "<refsect2 id=\"%s\" role=\"macro\"condition>\n<title>title</title>\n" % (sid, condition, title)
1379 desc += MakeIndexterms(symbol, sid)
1381 desc += OutputSymbolExtraLinks(symbol)
1384 synop += "<phrase role=\"c_punctuation\">()</phrase>"
1386 synop += "</entry></row>\n"
1388 # Don't output the macro definition if is is a conditional macro or it
1389 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1390 # longer than 2 lines, otherwise we get lots of complicated macros like
1392 if symbol in DeclarationConditional and not symbol.startswith('g_') \
1393 and not re.search(r'^_?gnome_', symbol) and declaration.count('\n/') < 2:
1394 decl_out = CreateValidSGML(declaration)
1395 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1397 desc += "<programlisting language=\"C\">" + MakeReturnField("#define") + symbol
1398 m = re.search(r'^\s*#\s*define\s+\w+(\([^\)]*\))', declaration)
1401 pad = ' ' * RETURN_TYPE_FIELD_WIDTH - len("#define ")
1402 # Align each line so that if should all line up OK.
1403 args = args.replace('\n', '\n' + pad)
1404 desc += CreateValidSGML(args)
1406 desc += "</programlisting>\n"
1409 desc += MakeDeprecationNote(symbol)
1411 parameters = OutputParamDescriptions("MACRO", symbol, fields)
1413 if symbol in SymbolDocs:
1414 symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
1419 desc += OutputSymbolTraits(symbol)
1420 desc += "</refsect2>\n"
1421 return (synop, desc)
1424 #############################################################################
1425 # Function : OutputTypedef
1426 # Description : Returns the synopsis and detailed description of a typedef.
1427 # Arguments : $symbol - the typedef.
1428 # $declaration - the declaration of the typedef,
1429 # e.g. 'typedef unsigned int guint;'
1430 #############################################################################
1432 def OutputTypedef(symbol, declaration):
1433 sid = common.CreateValidSGMLID(symbol)
1434 condition = MakeConditionDescription(symbol)
1435 desc = "<refsect2 id=\"%s\" role=\"typedef\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
1436 synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (sid, symbol)
1438 desc += MakeIndexterms(symbol, sid)
1440 desc += OutputSymbolExtraLinks(symbol)
1442 if symbol in DeclarationConditional:
1443 decl_out = CreateValidSGML(declaration)
1444 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1447 desc += MakeDeprecationNote(symbol)
1449 if symbol in SymbolDocs:
1450 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1452 desc += OutputSymbolTraits(symbol)
1453 desc += "</refsect2>\n"
1454 return (synop, desc)
1458 #############################################################################
1459 # Function : OutputStruct
1460 # Description : Returns the synopsis and detailed description of a struct.
1461 # We check if it is a object struct, and if so we only output
1462 # parts of it that are noted as public fields.
1463 # We also use a different IDs for object structs, since the
1464 # original ID is used for the entire RefEntry.
1465 # Arguments : $symbol - the struct.
1466 # $declaration - the declaration of the struct.
1467 #############################################################################
1469 def OutputStruct(symbol, declaration):
1472 default_to_public = True
1473 if CheckIsObject(symbol):
1474 logging.info("Found struct gtype: %s", symbol)
1476 default_to_public = ObjectRoots[symbol] == 'GBoxed'
1482 sid = common.CreateValidSGMLID(symbol + "_struct")
1483 condition = MakeConditionDescription(symbol + "_struct")
1485 sid = common.CreateValidSGMLID(symbol)
1486 condition = MakeConditionDescription(symbol)
1489 # Determine if it is a simple struct or it also has a typedef.
1491 if StructHasTypedef[symbol] or re.search(r'^\s*typedef\s+', declaration):
1497 # For structs with typedefs we just output the struct name.
1499 desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
1501 type_output = "struct"
1502 desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>struct %s</title>\n" % (sid, condition, symbol)
1504 synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (type_output, sid, symbol)
1506 desc += MakeIndexterms(symbol, sid)
1508 desc += OutputSymbolExtraLinks(symbol)
1510 # Form a pretty-printed, private-data-removed form of the declaration
1513 if re.search(r'^\s*$', declaration):
1514 logging.info("Found opaque struct: %s", symbol)
1515 decl_out = "typedef struct _%s %s;" % (symbol, symbol)
1516 elif re.search(r'^\s*struct\s+\w+\s*;\s*', declaration):
1517 logging.info("Found opaque struct: %s", symbol)
1518 decl_out = "struct %s;" % symbol
1520 public = default_to_public
1521 new_declaration = ''
1525 m = re.search(r'^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$', decl, flags=re.S)
1527 struct_contents = m.group(2)
1529 for decl_line in struct_contents.split('\n'):
1530 logging.info("Struct line: %s", decl_line)
1531 m2 = re.search(r'/\*\s*<\s*public\s*>\s*\*/', decl_line)
1532 m3 = re.search(r'/\*\s*<\s*(private|protected)\s*>\s*\*/', decl_line)
1538 new_declaration += decl_line + "\n"
1541 # Strip any blank lines off the ends.
1542 new_declaration = '\n'.join([x.strip() for x in new_declaration.split('\n')])
1545 decl_out = "typedef struct {\n" + new_declaration + "} $symbol;\n"
1547 decl_out = "struct %s {\n%s };\n" % (symbol, new_declaration)
1550 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1551 "Couldn't parse struct:\n%s" % declaration)
1553 # If we couldn't parse the struct or it was all private, output an
1554 # empty struct declaration.
1557 decl_out = "typedef struct _%s %s;" % (symbol, symbol)
1559 decl_out = "struct %s;" % symbol
1561 decl_out = CreateValidSGML(decl_out)
1562 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1564 desc += MakeDeprecationNote(symbol)
1566 if symbol in SymbolDocs:
1567 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1570 # Create a table of fields and descriptions
1572 # FIXME: Inserting  's into the produced type declarations here would
1573 # improve the output in most situations ... except for function
1574 # members of structs!
1576 return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), args[0])
1578 fields = common.ParseStructDeclaration(declaration, not default_to_public,
1579 0, MakeXRef, depfunc)
1580 params = SymbolParams[symbol]
1582 # If no parameters are filled in, we don't generate the description
1583 # table, for backwards compatibility.
1587 for i in range(1, len(params)+1, PARAM_FIELD_COUNT):
1588 if re.search(r'\S', params[i]):
1593 field_descrs = {} # FIXME, don't know what this does, original was: @$params
1594 missing_parameters = ''
1595 unused_parameters = ''
1596 sid = common.CreateValidSGMLID(symbol + ".members")
1598 desc += '''<refsect3 id="%s" role="struct_members">\n<title>Members</title>
1599 <informaltable role="struct_members_table" pgwide="1" frame="none">
1601 <colspec colname="struct_members_name" colwidth="300px"/>
1602 <colspec colname="struct_members_description"/>
1603 <colspec colname="struct_members_annotations" colwidth="200px"/>
1607 while len(fields) > 0:
1608 field_name = fields.pop(0)
1609 text = fields.pop(0)
1610 field_descr = field_descrs[field_name]
1611 param_annotations = ''
1613 desc += "<row role=\"member\"><entry role=\"struct_member_name\"><para>%s</para></entry>\n" % text
1615 (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descr)
1616 field_descr = ConvertMarkDown(symbol, field_descr)
1618 field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M|re.S)
1619 field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M|re.S)
1620 desc += "<entry role=\"struct_member_description\">%s</entry>\n<entry role=\"struct_member_annotations\">%s</entry>\n" % (field_descr, param_annotations)
1621 del field_descrs[field_name]
1623 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1624 "Field description for %s::%s is missing in source code comment block." % (symbol, field_name))
1625 if missing_parameters != '':
1626 missing_parameters += ", " + field_name
1628 missing_parameters = field_name
1630 desc += "<entry /><entry />\n"
1634 desc += "</tbody></tgroup></informaltable>\n</refsect3>\n"
1635 for field_name in field_descrs:
1636 # Documenting those standard fields is not required anymore, but
1637 # we don't want to warn if they are documented anyway.
1638 m = re.search(r'(g_iface|parent_instance|parent_class)', field_name)
1642 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1643 "Field description for %s::%s is not used from source code comment block." % (symbol, field_name))
1644 if unused_parameters != '':
1645 unused_parameters += ", " + field_name
1647 unused_parameters = field_name
1649 # remember missing/unused parameters (needed in tmpl-free build)
1650 if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
1651 AllIncompleteSymbols[symbol] = missing_parameters
1653 if unused_parameters != '' and (symbol not in AllUnusedSymbols):
1654 AllUnusedSymbols[symbol] = unused_parameters
1657 if symbol not in AllIncompleteSymbols:
1658 AllIncompleteSymbols[symbol] = "<items>"
1659 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1660 "Field descriptions for struct %s are missing in source code comment block." % symbol)
1661 logging.info("Remaining structs fields: " + ':'.join(fields) + "\n")
1663 desc += OutputSymbolTraits(symbol)
1664 desc += "</refsect2>\n"
1665 return (synop, desc)
1669 #############################################################################
1670 # Function : OutputUnion
1671 # Description : Returns the synopsis and detailed description of a union.
1672 # Arguments : $symbol - the union.
1673 # $declaration - the declaration of the union.
1674 #############################################################################
1676 def OutputUnion(symbol, declaration):
1679 if CheckIsObject(symbol):
1680 logging.info("Found union gtype: %s\n", symbol)
1687 sid = common.CreateValidSGMLID(symbol + "_union")
1688 condition = MakeConditionDescription(symbol + "_union")
1690 sid = common.CreateValidSGMLID(symbol)
1691 condition = MakeConditionDescription(symbol)
1694 # Determine if it is a simple struct or it also has a typedef.
1696 if StructHasTypedef[symbol] or re.search(r'^\s*typedef\s+', declaration):
1703 # For unions with typedefs we just output the union name.
1705 desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
1707 type_output = "union"
1708 desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>union %s</title>\n" % (sid, condition, symbol)
1710 synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s$id\">%s</link></entry></row>\n" % (type_output, sid, symbol)
1712 desc += MakeIndexterms(symbol, sid)
1714 desc += OutputSymbolExtraLinks(symbol)
1715 desc += MakeDeprecationNote(symbol)
1717 if symbol in SymbolDocs:
1718 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1721 # Create a table of fields and descriptions
1723 # FIXME: Inserting  's into the produced type declarations here would
1724 # improve the output in most situations ... except for function
1725 # members of structs!
1727 return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), args[0])
1728 fields = common.ParseStructDeclaration(declaration, 0, 0, MakeXRef, pfunc)
1729 params = SymbolParams[symbol]
1731 # If no parameters are filled in, we don't generate the description
1732 # table, for backwards compatibility
1736 for i in range(1, len(params)+1, PARAM_FIELD_COUNT):
1737 if re.search(r'\S', params[i]):
1742 field_descrs = {} # FIXME same as above: @$params
1743 missing_parameters = ''
1744 unused_parameters = ''
1745 sid = common.CreateValidSGMLID('%s.members' % symbol)
1747 desc += '''<refsect3 id="%s" role="union_members">\n<title>Members</title>
1748 <informaltable role="union_members_table" pgwide="1" frame="none">
1750 <colspec colname="union_members_name" colwidth="300px"/>
1751 <colspec colname="union_members_description"/>
1752 <colspec colname="union_members_annotations" colwidth="200px"/>
1756 while len(fields) > 0:
1757 field_name = fields.pop(0)
1758 text = fields.pop(0)
1759 field_descr = field_descrs[field_name]
1760 param_annotations = ''
1762 desc += "<row><entry role=\"union_member_name\"><para>%s</para></entry>\n" % text
1764 (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descr)
1765 field_descr = ConvertMarkDown(symbol, field_descr)
1768 field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M|re.S)
1769 field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M|re.S)
1770 desc += "<entry role=\"union_member_description\">%s</entry>\n<entry role=\"union_member_annotations\">%s</entry>\n" % (field_descr, param_annotations)
1771 del field_descrs[field_name]
1773 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1774 "Field description for %s::%s is missing in source code comment block." % (symbol, field_name))
1775 if missing_parameters != '':
1776 missing_parameters += ", " + field_name
1778 missing_parameters = field_name
1780 desc += "<entry /><entry />\n"
1784 desc += "</tbody></tgroup></informaltable>\n</refsect3>"
1785 for field_name in field_descrs:
1786 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1787 "Field description for %s::%s is not used from source code comment block." % (symbol, field_name))
1788 if unused_parameters != '':
1789 unused_parameters += ", " + field_name
1791 unused_parameters = field_name
1793 # remember missing/unused parameters (needed in tmpl-free build)
1794 if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
1795 AllIncompleteSymbols[symbol] = missing_parameters
1797 if unused_parameters != '' and (symbol not in AllUnusedSymbols):
1798 AllUnusedSymbols[symbol] = unused_parameters
1801 if symbol not in AllIncompleteSymbols:
1802 AllIncompleteSymbols[symbol] = "<items>"
1803 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1804 "Field descriptions for union %s are missing in source code comment block." % symbol)
1805 logging.info("Remaining union fields: " + ':'.join(fields) + "\n")
1807 desc += OutputSymbolTraits(symbol)
1808 desc += "</refsect2>\n"
1809 return (synop, desc)
1811 #############################################################################
1812 # Function : OutputEnum
1813 # Description : Returns the synopsis and detailed description of a enum.
1814 # Arguments : $symbol - the enum.
1815 # $declaration - the declaration of the enum.
1816 #############################################################################
1818 def OutputEnum(symbol, declaration):
1820 if CheckIsObject(symbol):
1821 logging.info("Found enum gtype: %s", symbol)
1827 sid = common.CreateValidSGMLID(symbol + "_enum")
1828 condition = MakeConditionDescription(symbol + "_enum")
1830 sid = common.CreateValidSGMLID(symbol)
1831 condition = MakeConditionDescription(symbol)
1834 synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"\">$symbol</link></entry></row>\n" % sid
1835 desc = "<refsect2 id=\"%s\" role=\"enum\"%s>\n<title>enum %s</title>\n" % (sid, condition, symbol)
1837 desc += MakeIndexterms(symbol, sid)
1839 desc += OutputSymbolExtraLinks(symbol)
1840 desc += MakeDeprecationNote(symbol)
1842 if symbol in SymbolDocs:
1843 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1845 # Create a table of fields and descriptions
1847 fields = common.ParseEnumDeclaration(declaration)
1848 params = SymbolParams[symbol]
1850 # If nothing at all is documented log a single summary warning at the end.
1851 # Otherwise, warn about each undocumented item.
1855 for i in range(1, len(params), PARAM_FIELD_COUNT):
1856 if re.search(r'\S', params[i]):
1860 field_descrs = params if params else {}
1861 missing_parameters = ''
1862 unused_parameters = ''
1864 sid = common.CreateValidSGMLID("%s.members" % symbol)
1865 desc += '''<refsect3 id="%s" role="enum_members">\n<title>Members</title>
1866 <informaltable role="enum_members_table" pgwide="1" frame="none">
1868 <colspec colname="enum_members_name" colwidth="300px"/>
1869 <colspec colname="enum_members_description"/>
1870 <colspec colname="enum_members_annotations" colwidth="200px"/>
1874 for field_name in fields:
1875 field_descr = field_descrs[field_name]
1876 param_annotations = ''
1878 sid = common.CreateValidSGMLID(field_name)
1879 condition = MakeConditionDescription(field_name)
1880 desc += "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"%s\">$field_name</para></entry>\n" % sid
1882 field_descr, param_annotations = ExpandAnnotation(symbol, field_descr)
1883 field_descr = ConvertMarkDown(symbol, field_descr)
1884 desc += "<entry role=\"enum_member_description\">%s</entry>\n<entry role=\"enum_member_annotations\">%s</entry>\n" % (field_descr, param_annotations)
1885 del field_descrs[field_name]
1888 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1889 "Value description for %s::%s is missing in source code comment block." % (symbol, field_name))
1890 if missing_parameters != '':
1891 missing_parameters += ", " + field_name
1893 missing_parameters = field_name
1894 desc += "<entry /><entry />\n"
1897 desc += "</tbody></tgroup></informaltable>\n</refsect3>"
1898 for field_name in field_descrs:
1899 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1900 "Value description for %s::%s is not used from source code comment block." % (symbol, field_name))
1901 if unused_parameters != '':
1902 unused_parameters += ", " + field_name
1904 unused_parameters = field_name
1906 # remember missing/unused parameters (needed in tmpl-free build)
1907 if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
1908 AllIncompleteSymbols[symbol] = missing_parameters
1910 if unused_parameters != '' and (symbol not in AllUnusedSymbols):
1911 AllUnusedSymbols[symbol] = unused_parameters
1915 if symbol not in AllIncompleteSymbols:
1916 AllIncompleteSymbols[symbol] = "<items>"
1917 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
1918 "Value descriptions for %s are missing in source code comment block." % symbol)
1920 desc += OutputSymbolTraits(symbol)
1921 desc += "</refsect2>\n"
1922 return (synop, desc)
1926 #############################################################################
1927 # Function : OutputVariable
1928 # Description : Returns the synopsis and detailed description of a variable.
1929 # Arguments : $symbol - the extern'ed variable.
1930 # $declaration - the declaration of the variable.
1931 #############################################################################
1933 def OutputVariable(symbol, declaration):
1934 sid = common.CreateValidSGMLID(symbol)
1935 condition = MakeConditionDescription(symbol)
1937 logging.info("ouputing variable: '%s' '%s'", symbol, declaration)
1940 m1 = re.search(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)
1941 m2 = re.search(r'\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=', declaration)
1948 mod1 = g1 if g1 else ''
1949 ptr = g3 if g2 else ''
1950 space = g4 if g4 else ''
1951 mod2 = g5 if g5 else ''
1952 type_output = "extern %s%s%s%s" % (mod1, ptr, space, mod2)
1959 mod1 = g1 if g1 else ''
1960 ptr = g3 if g3 else ''
1961 space = g4 if g4 else ''
1962 mod2 = g5 if g5 else ''
1963 type_output = '%s%s%s%s' % (mod1, ptr, space, mod2)
1965 type_output = "extern"
1967 synop = "<row><entry role=\"variable_type\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (type_output, sid, symbol)
1969 desc = "<refsect2 id=\"%s\" role=\"variable\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
1971 desc += MakeIndexterms(symbol, sid)
1973 desc += OutputSymbolExtraLinks(symbol)
1975 decl_out = CreateValidSGML(declaration)
1976 desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1978 desc += MakeDeprecationNote(symbol)
1980 if symbol in SymbolDocs:
1981 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
1983 if symbol in SymbolAnnotations:
1984 param_desc = SymbolAnnotations[symbol]
1985 param_annotations = ''
1986 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
1987 if param_annotations != '':
1988 desc += "\n<para>$param_annotations</para>"
1992 desc += OutputSymbolTraits(symbol)
1993 desc += "</refsect2>\n"
1994 return (synop, desc)
1998 #############################################################################
1999 # Function : OutputFunction
2000 # Description : Returns the synopsis and detailed description of a function.
2001 # Arguments : $symbol - the function.
2002 # $declaration - the declaration of the function.
2003 #############################################################################
2005 def OutputFunction(symbol, declaration, symbol_type):
2006 sid = common.CreateValidSGMLID(symbol)
2007 condition = MakeConditionDescription(symbol)
2009 # Take out the return type $1 $2 $3
2010 m = re.search(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', declaration)
2011 type_modifier = m.group(1) if m.group(1) else ''
2013 pointer = m.group(3)
2014 # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
2015 pointer = pointer.rstrip()
2016 xref = MakeXRef(type, tagify(type, "returnvalue"))
2018 #if ($symbol_type == 'USER_FUNCTION')
2019 # $start = "typedef "
2022 # We output const rather than G_CONST_RETURN.
2023 type_modifier = re.sub(r'G_CONST_RETURN', 'const', type_modifier)
2024 pointer = re.sub(r'G_CONST_RETURN', 'const', pointer)
2025 pointer = re.sub(r'^\s+', ' ', pointer)
2027 ret_type_output = None
2028 ret_type_output = "%s%s%s%s\n" % (start, type_modifier, xref, pointer)
2030 indent_len = len(symbol) + 2
2031 char1 = char2 = char3 = ''
2032 if symbol_type == 'USER_FUNCTION':
2034 char1 = "<phrase role=\"c_punctuation\">(</phrase>"
2036 char3 = "<phrase role=\"c_punctuation\">)</phrase>"
2039 symbol_output = "%s<link linkend=\"%s\">%s%s</link>%s" % (char1, sid, char2, symbol, char3)
2040 if indent_len < MAX_SYMBOL_FIELD_WIDTH:
2041 symbol_desc_output = "%s%s%s%s" % (char1, char2, symbol, char3)
2043 indent_len = MAX_SYMBOL_FIELD_WIDTH - 8
2044 symbol_desc_output = ('%s%s%s%s\n' % (char1, char2, symbol, char3)) + (' ' * (indent_len - 1))
2046 synop = "<row><entry role=\"function_type\">%s</entry><entry role=\"function_name\">%s <phrase role=\"c_punctuation\">()</phrase></entry></row>\n" % (ret_type_output, symbol_output)
2048 desc = "<refsect2 id=\"%s\" role=\"function\"%s>\n<title>%s ()</title>\n" % (sid, condition, symbol)
2050 desc += MakeIndexterms(symbol, sid)
2052 desc += OutputSymbolExtraLinks(symbol)
2054 desc += "<programlisting language=\"C\">%s%s(" % (ret_type_output, symbol_desc_output)
2057 return tagify(args[0], "parameter")
2059 fields = common.ParseFunctionDeclaration(declaration, MakeXRef, tagfun)
2061 for i in range(1, len(fields) +1, 2):
2062 field_name = fields[i]
2067 desc += ",\n" + (' ' * indent_len) + field_name
2069 desc += ");</programlisting>\n"
2071 desc += MakeDeprecationNote(symbol)
2073 if symbol in SymbolDocs:
2074 desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
2076 if symbol in SymbolAnnotations:
2077 param_desc = SymbolAnnotations[symbol]
2078 param_annotations = ''
2079 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
2080 if param_annotations != '':
2081 desc += "\n<para>%s</para>" % param_annotations
2083 desc += OutputParamDescriptions("FUNCTION", symbol, fields)
2084 desc += OutputSymbolTraits(symbol)
2085 desc += "</refsect2>\n"
2086 return (synop, desc)
2088 #############################################################################
2089 # Function : OutputParamDescriptions
2090 # Description : Returns the DocBook output describing the parameters of a
2091 # function, macro or signal handler.
2092 # Arguments : $symbol_type - 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
2093 # handlers have an implicit user_data parameter last.
2094 # $symbol - the name of the function/macro being described.
2095 # @fields - parsed fields from the declaration, used to determine
2096 # undocumented/unused entries
2097 #############################################################################
2099 def OutputParamDescriptions(symbol_type, symbol, fields):
2101 params = SymbolParams[symbol]
2106 field_descrs = {} # FIXME convert @fields
2107 del field_descrs["void"]
2108 del field_descrs["Returns"]
2114 missing_parameters = ''
2115 unused_parameters = ''
2117 for j in range(0, len(params), PARAM_FIELD_COUNT):
2118 param_name = params[j]
2119 param_desc = params[j + 1]
2120 param_annotations = ''
2122 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
2123 param_desc = ConvertMarkDown(symbol, param_desc)
2125 param_desc = re.sub(r'^(\s|\n)+', '', param_desc, flags=re.M|re.S)
2126 param_desc = re.sub(r'(\s|\n)+', '', param_desc, flags=re.M|re.S)
2127 if param_name == "Returns":
2128 returns = param_desc
2129 if param_annotations != '':
2130 returns += "\n<para>%s</para>" % param_annotations
2132 elif param_name == "void":
2133 # FIXME: &common.LogWarning()?
2134 logging.info("!!!! void in params for %s?\n", symbol)
2137 if param_name in field_descrs:
2138 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
2139 "Parameter description for %s::%s is not used from source code comment block." % (symbol, param_name))
2140 if unused_parameters != '':
2141 unused_parameters += ", " + param_name
2143 unused_parameters = param_name
2145 del field_descrs[param_name]
2148 if param_desc != '':
2149 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" % (param_name, param_desc, param_annotations)
2152 for param_name in field_descrs:
2153 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
2154 "Parameter description for %s::%s is missing in source code comment block." % (symbol, param_name))
2155 if missing_parameters != '':
2156 missing_parameters += ", " + param_name
2158 missing_parameters = param_name
2160 # Signals have an implicit user_data parameter which we describe.
2161 if symbol_type == "SIGNAL":
2162 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"
2165 # Start a table if we need one.
2166 if params_desc != '':
2167 sid = common.CreateValidSGMLID("%s.parameters" % symbol)
2169 output += '''<refsect3 id="%s" role="parameters">\n<title>Parameters</title>
2170 <informaltable role="parameters_table" pgwide="1" frame="none">
2172 <colspec colname="parameters_name" colwidth="150px"/>
2173 <colspec colname="parameters_description"/>
2174 <colspec colname="parameters_annotations" colwidth="200px"/>
2177 output += params_desc
2178 output += "</tbody></tgroup></informaltable>\n</refsect3>"
2180 # Output the returns info last
2182 sid = common.CreateValidSGMLID("%s.returns" % symbol)
2185 <refsect3 id="%s" role=\"returns\">\n<title>Returns</title>
2188 output += "\n</refsect3>"
2191 # remember missing/unused parameters (needed in tmpl-free build)
2192 if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
2193 AllIncompleteSymbols[symbol] = missing_parameters
2195 if unused_parameters != '' and (symbol not in AllUnusedSymbols):
2196 AllUnusedSymbols[symbol] = unused_parameters
2198 if num_params == 0 and len(fields) > 0 and len(field_descrs) > 0:
2199 if symbol not in AllIncompleteSymbols:
2200 AllIncompleteSymbols[symbol] = "<parameters>"
2205 #############################################################################
2206 # Function : ParseStabilityLevel
2207 # Description : Parses a stability level and outputs a warning if it isn't
2209 # Arguments : $stability - the stability text.
2210 # $file, $line - context for error message
2211 # $message - description of where the level is from, to use in
2212 # any error message.
2213 # Returns : The parsed stability level string.
2214 #############################################################################
2216 def ParseStabilityLevel(stability, file, line, message):
2218 stability = stability.strip()
2219 sl = stability.lower()
2222 stability = "Stable"
2223 elif stability == 'unstable':
2224 stability = "Unstable"
2225 elif stability == 'private':
2226 stability = "Private"
2228 common.LogWarning(file, line, "%s is %s." % (message, stability) +\
2229 "It should be one of these: Stable, Unstable, or Private.")
2234 #############################################################################
2235 # Function : OutputDBFile
2236 # Description : Outputs the final DocBook file for one section.
2237 # Arguments : $file - the name of the file.
2238 # $title - the title from the $MODULE-sections.txt file, which
2239 # will be overridden by the title in the template file.
2240 # $section_id - the id to use for the toplevel tag.
2241 # $includes - comma-separates list of include files added at top of
2242 # synopsis, with '<' '>' around them (if not already enclosed in '').
2243 # $functions_synop - reference to the DocBook for the Functions Synopsis part.
2244 # $other_synop - reference to the DocBook for the Types and Values Synopsis part.
2245 # $functions_details - reference to the DocBook for the Functions Details part.
2246 # $other_details - reference to the DocBook for the Types and Values Details part.
2247 # $signal_synop - reference to the DocBook for the Signal Synopsis part
2248 # $signal_desc - reference to the DocBook for the Signal Description part
2249 # $args_synop - reference to the DocBook for the Arg Synopsis part
2250 # $args_desc - reference to the DocBook for the Arg Description part
2251 # $hierarchy - reference to the DocBook for the Object Hierarchy part
2252 # $interfaces - reference to the DocBook for the Interfaces part
2253 # $implementations - reference to the DocBook for the Known Implementations part
2254 # $prerequisites - reference to the DocBook for the Prerequisites part
2255 # $derived - reference to the DocBook for the Derived Interfaces part
2256 # $file_objects - reference to an array of objects in this file
2257 #############################################################################
2259 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):
2261 logging.info("Output docbook for file %s with title '%s'\n", file, title)
2263 # The edited title overrides the one from the sections file.
2264 new_title = SymbolDocs[os.path.join(TMPL_DIR, file + ":Title")]
2265 if new_title and not re.search(r'^\s*$', new_title):
2267 logging.info("Found title: %s", title)
2269 short_desc = SymbolDocs[os.path.join(TMPL_DIR, file + ":Short_Description")]
2270 if not short_desc or re.search(r'^\s*$', short_desc):
2273 # Don't use ConvertMarkDown here for now since we don't want blocks
2274 short_desc = ExpandAbbreviations(":Short_description" % title,
2276 logging.info("Found short_desc: %s", short_desc)
2278 long_desc = SymbolDocs[os.path.join(TMPL_DIR, file + ":Long_Description")]
2279 if not long_desc or re.search(r'^\s*$', long_desc):
2282 long_desc = ConvertMarkDown(":Long_description" % title,
2284 logging.info("Found long_desc: ", long_desc)
2286 see_also = SymbolDocs[os.path.join(TMPL_DIR, file + ":See_Also")]
2287 if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
2290 see_also = ConvertMarkDown("%s:See_Also" % title, see_also)
2291 logging.info("Found see_also: %s", see_also)
2294 see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id, see_also)
2296 stability = SymbolDocs[os.path.join(TMPL_DIR, file + ":Stability_Level")]
2297 if not stability or re.search(r'^\s*$', stability):
2300 stability = ParseStabilityLevel(stability, file, line_number, "Section stability level")
2301 logging.info("Found stability: %s", stability)
2304 AnnotationsUsed[stability] = 1
2305 stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (section_id, stability)
2306 elif DEFAULT_STABILITY:
2307 AnnotationsUsed[DEFAULT_STABILITY] = 1
2308 stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (section_id, DEFAULT_STABILITY)
2311 image = SymbolDocs[os.path.join(TMPL_DIR, file + ":Image")]
2312 if image or re.search(r'^\s*$', image):
2315 image = image.strip()
2320 if re.search(r'jpe?g$', il):
2321 format = "format='JPEG'"
2322 elif il.endswith('png'):
2323 format = "format='PNG'"
2324 elif il.endswith('svg'):
2325 format = "format='SVG'"
2330 image = " <inlinegraphic fileref='%s' %s/>\n" % (image, format)
2334 include_output += "<refsect1 id=\"%s.includes\"><title>Includes</title><synopsis>" % section_id
2335 for include in includes.split(','):
2336 if re.search(r'^\".+\"$', include):
2337 include_output += "#include %s\n" % include
2339 include = re.sub(r'^\s+|\s+$', '', include, flags=re.S)
2340 include_output += "#include <%s>\n" % include
2343 include_output += "</synopsis></refsect1>\n"
2346 extralinks = OutputSectionExtraLinks(title, "Section:%s" % file)
2348 old_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml')
2349 new_db_file = os.path.join(DB_OUTPUT_DIR, file, '.xml.new')
2351 OUTPUT = open(new_db_file, 'w')
2354 for fobject in file_objects:
2355 if fobject == section_id:
2357 sid = common.CreateValidSGMLID(fobject)
2358 logging.info("Adding anchor for %s\n", fobject)
2359 object_anchors += "<anchor id=\"%s\"/>" % sid
2361 # Make sure we produce valid docbook
2362 if not functions_details:
2363 functions_details = "<para />"
2365 # We used to output this, but is messes up our common.UpdateFileIfChanged code
2366 # since it changes every day (and it is only used in the man pages):
2367 # "<refentry id="$section_id" revision="$mday $month $year">"
2369 OUTPUT.write(r'''${\( MakeDocHeader ("refentry") )
2370 <refentry id="$section_id">
2372 <refentrytitle role="top_of_page" id="%s.top_of_page">%s</refentrytitle>
2373 <manvolnum>3</manvolnum>
2374 <refmiscinfo>\U%s\E Library%s</refmiscinfo>
2377 <refname>%s</refname>
2378 <refpurpose>%s</refpurpose>
2381 %s%s%s%s%s%s%s%s%s%s
2383 <refsect1 id="%s.description" role="desc">
2384 <title role="desc.title">Description</title>
2387 <refsect1 id="%s.functions_details" role="details">
2388 <title role="details.title">Functions</title>
2391 <refsect1 id="%s.other_details" role="details">
2392 <title role="details.title">Types and Values</title>
2397 ''' % (section_id, title, MODULE, image, title, short_desc, stability, functions_synop, args_synop, signals_synop, object_anchors, other_synop, hierarchy, prerequisites, derived, interfaces, implementations, section_id, extralinks, long_desc, section_id, functions_details, section_id, other_details, args_desc, signals_desc, see_also))
2400 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
2403 #############################################################################
2404 # Function : OutputProgramDBFile
2405 # Description : Outputs the final DocBook file for one program.
2406 # Arguments : $file - the name of the file.
2407 # $section_id - the id to use for the toplevel tag.
2408 #############################################################################
2410 def OutputProgramDBFile(program, section_id):
2411 logging.info("Output program docbook for %s", program)
2413 short_desc = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":Short_Description")]
2414 if not short_desc or re.search(r'^\s*$', short_desc):
2417 # Don't use ConvertMarkDown here for now since we don't want blocks
2418 short_desc = ExpandAbbreviations(program, short_desc)
2419 logging.info("Found short_desc: %s", short_desc)
2421 synopsis = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":Synopsis")]
2422 if synopsis and re.search(r'~\s*$', synopsis):
2423 items = synopsis.split(' ')
2424 for i in range(0, len(items)):
2425 parameter = items[i]
2429 # first parameter is the command name
2431 synopsis = "<command>%s</command>\n" % parameter
2434 # square brackets indicate optional parameters, curly brackets
2435 # indicate required parameters ("plain" parameters are also
2436 # mandatory, but do not get extra decoration)
2437 m1 = re.search(r'^\[(.+?)\]$', parameter)
2438 m2 = re.search(r'^\{(.+?)\}$', parameter)
2439 m3 = re.search(r'\.\.\.$', parameter)
2440 m4 = re.search(r'\*(.+?)\*', parameter)
2446 # parameters ending in "..." are repeatable
2448 rep = ' rep=\"repeat\"'
2450 # italic parameters are replaceable parameters
2452 parameter = "<replaceable>%s</replaceable>" % parameter
2455 synopsis += "<arg choice=\"%s\"%s>" % (choice, rep)
2456 synopsis += parameter
2457 synopsis += "</arg>\n"
2459 logging.info("Found synopsis: %s", synopsis)
2461 synopsis = "<command>%s</command>" % program
2464 long_desc = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":Long_Description")]
2465 if not long_desc or re.search(r'^\s*$', long_desc):
2468 long_desc = ConvertMarkDown("%s:Long_description" % program, long_desc)
2469 logging.info("Found long_desc: %s", long_desc)
2472 o = os.path.join(TMPL_DIR, program + ":Options")
2473 if o in SourceSymbolDocs:
2474 opts = SourceSymbolDocs[o]
2476 options = "<refsect1>\n<title>Options</title>\n<variablelist>\n"
2477 for k in range(0, len(opts), 2):
2478 opt_desc = opts[k+1]
2480 opt_desc = re.sub(r'\*(.+?)\*', r'<replaceable>\1<\/replaceable>', opt_desc)
2482 options += "<varlistentry>\n<term>"
2483 opt_names = opts[k].split(' ')
2484 for i in range(len(opt_names)):
2485 prefix = ', ' if i > 0 else ''
2486 opt_names[i] = re.sub(r'\*(.+?)\*', r'<replaceable>\1<\/replaceable>', opt_names[i])
2488 options += "%s<option>%s</option>\n" % (prefix, opt_names[i])
2490 options += "</term>\n"
2491 options += "<listitem><para>%s</para></listitem>\n" % opt_desc
2492 options += "</varlistentry>\n"
2494 options += "</variablelist></refsect1>\n"
2497 exit_status = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":Returns")]
2498 if exit_status and exit_status != '':
2499 exit_status = ConvertMarkDown("%s:Returns" % program, exit_status)
2500 exit_status = "<refsect1 id=\"%s.exit-status\">\n<title>Exit Status</title>\n%s\n</refsect1>\n" % (section_id, exit_status)
2505 see_also = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":See_Also")]
2506 if see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
2509 see_also = ConvertMarkDown("%s:See_Also" % program, see_also)
2510 logging.info("Found see_also: %s", see_also)
2513 see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id, see_also)
2516 old_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml")
2517 new_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml.new")
2519 OUTPUT = open(new_db_file, 'w')
2524 <refentrytitle role="top_of_page" id="%s.top_of_page">$program</refentrytitle>
2525 <manvolnum>1</manvolnum>
2526 <refmiscinfo>User Commands</refmiscinfo>
2529 <refname>%s</refname>
2530 <refpurpose>%s</refpurpose>
2533 <cmdsynopsis>%s</cmdsynopsis>
2535 <refsect1 id="%s.description" role="desc">
2536 <title role="desc.title">Description</title>
2541 ''' % (MakeDocHeader("refentry"), section_id, section_id, program, short_desc, synopsis, section_id, long_desc, options, exit_status, see_also))
2544 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
2547 #############################################################################
2548 # Function : OutputExtraFile
2549 # Description : Copies an "extra" DocBook file into the output directory,
2550 # expanding abbreviations
2551 # Arguments : $file - the source file.
2552 #############################################################################
2553 def OutputExtraFile(file):
2555 basename = re.sub(r'^.*/', '', file)
2557 old_db_file = os.path.join(DB_OUTPUT_DIR, basename)
2558 new_db_file = os.path.join(DB_OUTPUT_DIR, basename + ".new")
2560 contents = open(file).read()
2562 OUTPUT = open(new_db_file, 'w')
2564 OUTPUT.write(ExpandAbbreviations(basename + " file", contents))
2567 return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
2569 #############################################################################
2570 # Function : OutputBook
2571 # Description : Outputs the entities that need to be included into the
2572 # main docbook file for the module.
2573 # Arguments : $book_top - the declarations of the entities, which are added
2574 # at the top of the main docbook file.
2575 # $book_bottom - the references to the entities, which are
2576 # added in the main docbook file at the desired position.
2577 #############################################################################
2579 def OutputBook(book_top, book_bottom):
2581 old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top")
2582 new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top.new")
2584 OUTPUT = open(new_file, 'w')
2585 OUTPUT.write(book_top)
2588 common.UpdateFileIfChanged(old_file, new_file, 0)
2591 old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom")
2592 new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom.new")
2594 OUTPUT = open(new_file, 'w')
2595 OUTPUT.write(book_bottom)
2598 common.UpdateFileIfChanged(old_file, new_file, 0)
2601 # If the main docbook file hasn't been created yet, we create it here.
2602 # The user can tweak it later.
2603 if MAIN_SGML_FILE and not os.path.exists(MAIN_SGML_FILE):
2604 OUTPUT = open(MAIN_SGML_FILE, 'w')
2609 <title>&package_name; Reference Manual</title>
2611 for &package_string;.
2612 The latest version of this documentation can be found on-line at
2613 <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2618 <title>[Insert title here]</title>
2621 ''' % MakeDocHeader("book"))
2622 if os.path.exists(OBJECT_TREE_FILE):
2623 OUTPUT.write(''' <chapter id="object-tree">
2624 <title>Object Hierarchy</title>
2625 <xi:include href="xml/tree_index.sgml"/>
2629 OUTPUT.write(''' <!-- enable this when you use gobject types
2630 <chapter id="object-tree">
2631 <title>Object Hierarchy</title>
2632 <xi:include href="xml/tree_index.sgml"/>
2637 OUTPUT.write(''' <index id="api-index-full">
2638 <title>API Index</title>
2639 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2641 <index id="deprecated-api-index" role="deprecated">
2642 <title>Index of deprecated API</title>
2643 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2646 if len(AnnotationsUsed) > 0:
2647 OUTPUT.write(''' <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2650 OUTPUT.write(''' <!-- enable this when you use gobject introspection annotations
2651 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2661 #############################################################################
2662 # Function : CreateValidSGML
2663 # Description : This turns any chars which are used in SGML into entities,
2664 # e.g. '<' into '<'
2665 # Arguments : $text - the text to turn into proper SGML.
2666 #############################################################################
2668 def CreateValidSGML(text):
2669 text = re.sub(r'&', r'&', text) # Do this first, or the others get messed up.
2670 text = re.sub(r'<', r'<', text)
2671 text = re.sub(r'>', r'>', text)
2672 # browsers render single tabs inconsistently
2673 text = re.sub(r'([^\s])\t([^\s])', r'\1 \2', text)
2677 #############################################################################
2678 # Function : ConvertSGMLChars
2679 # Description : This is used for text in source code comment blocks, to turn
2680 # chars which are used in SGML into entities, e.g. '<' into
2681 # '<'. Depending on $INLINE_MARKUP_MODE, this is done
2682 # unconditionally or only if the character doesn't seem to be
2683 # part of an SGML construct (tag or entity reference).
2684 # Arguments : $text - the text to turn into proper SGML.
2685 #############################################################################
2687 def ConvertSGMLChars(symbol, text):
2689 if INLINE_MARKUP_MODE:
2690 # For the XML/SGML mode only convert to entities outside CDATA sections.
2691 return ModifyXMLElements(text, symbol,
2692 "<!\\[CDATA\\[|<programlisting[^>]*>",
2693 ConvertSGMLCharsEndTag,
2694 ConvertSGMLCharsCallback)
2695 # For the simple non-sgml mode, convert to entities everywhere.
2697 # First, convert freestanding & to &
2698 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&', text)
2699 text = re.sub(r'<', r'/<', text)
2700 # Allow ">" at beginning of string for blockquote markdown
2701 text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'>', text)
2705 def ConvertSGMLCharsEndTag(t):
2706 if t == r'<!\[CDATA\[':
2708 return "</programlisting>"
2710 def ConvertSGMLCharsCallback(text, symbol, tag):
2711 if re.search(r'^<programlisting', tag):
2712 # We can handle <programlisting> specially here.
2713 return ModifyXMLElements(text, symbol,
2715 ConvertSGMLCharsEndTag,
2716 ConvertSGMLCharsCallback2)
2718 # If we're not in CDATA convert to entities.
2719 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&', text) # Do this first, or the others get messed up.
2720 text = re.sub(r'<(?![a-zA-Z\/!])', r'<', text)
2721 # Allow ">" at beginning of string for blockquote markdown
2722 text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'>', text)
2724 # Handle "#include <xxxxx>"
2725 text = re.sub(r'#include(\s+)<([^>]+)>', r'#include\1<\2>', text)
2730 def ConvertSGMLCharsCallback2(text, symbol, tag):
2732 # If we're not in CDATA convert to entities.
2733 # We could handle <programlisting> differently, though I'm not sure it helps.
2735 # replace only if its not a tag
2736 text = re.sub(r'&(?![a-zA-Z#]+;)', r'&', text) # Do this first, or the others get messed up.
2737 text = re.sub(r'<(?![a-zA-Z\/!])', r'<', text)
2738 text = re.sub(r'''(?<![a-zA-Z0-9"'\/-])>''', r'>', text)
2739 # Handle "#include <xxxxx>"
2740 text = re.sub(r'/#include(\s+)<([^>]+)>', r'#include\1<\2>', text)
2745 #############################################################################
2746 # Function : ExpandAnnotation
2747 # Description : This turns annotations into acronym tags.
2748 # Arguments : $symbol - the symbol being documented, for error messages.
2749 # $text - the text to expand.
2750 #############################################################################
2751 def ExpandAnnotation(symbol, param_desc):
2752 param_annotations = ''
2754 # look for annotations at the start of the comment part
2755 # function level annotations don't end with a colon ':'
2756 m = re.search(r'^\s*\((.*?)\)(:|$)', param_desc)
2758 param_desc = param_desc[m.end():]
2760 annotations = re.split(r'\)\s*\(', m.group(1))
2761 logging.info("annotations for %s: '%s'\n", symbol, m.group(1))
2762 for annotation in annotations:
2763 # need to search for the longest key-match in %AnnotationDefinition
2765 match_annotation = ''
2767 for annotationdef in AnnotationDefinition:
2768 if annotation.startswith(annotationdef):
2769 if len(annotationdef) > match_length:
2770 match_length = len(annotationdef)
2771 match_annotation = annotationdef
2773 annotation_extra = ''
2774 if match_annotation != '':
2775 m = re.search(match_annotation + r'\s+(.*)', annotation)
2777 annotation_extra = " " + m.group(1)
2779 AnnotationsUsed[match_annotation] = 1
2780 param_annotations += "[<acronym>%s</acronym>%s]" % (match_annotation, annotation_extra)
2782 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
2783 "unknown annotation \"%s\" in documentation for %s." % (annotation, symbol))
2784 param_annotations += "[%s]" % annotation
2787 param_desc = param_desc.strip()
2788 m = re.search(r'^(.*?)\.*\s*$', param_desc)
2789 param_desc = "%s. " % m.group(1)
2791 if param_annotations != '':
2792 param_annotations = "<emphasis role=\"annotation\">%s</emphasis>" % param_annotations
2794 return (param_desc, param_annotations)
2797 #############################################################################
2798 # Function : ExpandAbbreviations
2799 # Description : This turns the abbreviations function(), macro(), @param,
2800 # %constant, and #symbol into appropriate DocBook markup.
2801 # CDATA sections and <programlisting> parts are skipped.
2802 # Arguments : $symbol - the symbol being documented, for error messages.
2803 # $text - the text to expand.
2804 #############################################################################
2806 def ExpandAbbreviations(symbol, text):
2807 # Note: This is a fallback and normally done in the markdown parser
2809 # Convert "|[" and "]|" into the start and end of program listing examples.
2810 # Support \[<!-- language="C" --> modifiers
2811 text = re.sub(r'\|\[<!-- language="([^"]+)" -->', r'<informalexample><programlisting language="\1"><![CDATA[', text)
2812 text = re.sub(r'\|\[', r'<informalexample><programlisting><![CDATA[', text)
2813 text = re.sub(r'\]\|', r']]></programlisting></informalexample>', text)
2815 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2817 return ModifyXMLElements(text, symbol,
2818 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2819 ExpandAbbreviationsEndTag,
2820 ExpandAbbreviationsCallback)
2823 # Returns the end tag (as a regexp) corresponding to the given start tag.
2824 def ExpandAbbreviationsEndTag(start_tag):
2825 if start_tag == r'<!\[CDATA\[':
2827 if start_tag == "<!DOCTYPE":
2829 m = re.search(r'<(\w+)', start_tag)
2830 return "</%s>" % m.group(1)
2832 # Called inside or outside each CDATA or <programlisting> section.
2833 def ExpandAbbreviationsCallback(text, symbol, tag):
2835 if tag.startswith(r'^<programlisting'):
2836 # Handle any embedded CDATA sections.
2837 return ModifyXMLElements(text, symbol,
2839 ExpandAbbreviationsEndTag,
2840 ExpandAbbreviationsCallback2)
2842 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2844 # We are outside any CDATA or <programlisting> sections, so we expand
2845 # any gtk-doc abbreviations.
2847 # Convert '@param()'
2848 # FIXME: we could make those also links ($symbol.$2), but that would be less
2849 # useful as the link target is a few lines up or down
2850 text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r'\1<parameter>\2()', text)
2852 # Convert 'function()' or 'macro()'.
2853 # if there is abc_*_def() we don't want to make a link to _def()
2854 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2856 return '%s.%s;' % (m.group(1), MakeXRef(m.group(2), tagify(m.group(2) + "()", "function")))
2857 text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f1, text)
2858 # handle #Object.func()
2859 text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1, text)
2861 # Convert '@param', but not '\@param'.
2862 text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r'\1<parameter>\2<\/parameter>', text)
2863 text = re.sub(r'/\\\@', r'\@', text)
2865 # Convert '%constant', but not '\%constant'.
2866 # Also allow negative numbers, e.g. %-1.
2868 return '%s.%s;' % (m.group(1), MakeXRef(m.group(2), tagify(m.group(2), "literal")))
2869 text = re.sub(r'(\A|[^\\])\%(-?\w+)', f2, text)
2870 text = re.sub(r'\\\%', r'\%', text)
2872 # Convert '#symbol', but not '\#symbol'.
2874 return '%s.%s;' % (m.group(1), MakeHashXRef(m.group(2), "type"))
2875 text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3, text)
2876 text = re.sub(r'\\#', '#', text)
2882 # This is called inside a <programlisting>
2883 def ExpandAbbreviationsCallback2(text, symbol, tag):
2885 # We are inside a <programlisting> but outside any CDATA sections,
2886 # so we expand any gtk-doc abbreviations.
2887 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2888 # why not just call it
2889 text = re.sub(r'#(\w+)', lambda m: '%s;' % MakeHashXRef(m.group(1), ''), text)
2890 elif tag == "<![CDATA[":
2891 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2892 text = ReplaceEntities(text, symbol)
2898 def MakeHashXRef(symbol, tag):
2901 # Check for things like '#include', '#define', and skip them.
2902 if PreProcessorDirectives[symbol]:
2903 return "#%s" % symbol
2905 # Get rid of special suffixes ('-struct','-enum').
2906 text = re.sub(r'-struct$', '', text)
2907 text = re.sub(r'-enum$', '', text)
2909 # If the symbol is in the form "Object::signal", then change the symbol to
2910 # "Object-signal" and use "signal" as the text.
2912 o, s = symbol.split('::', 1)
2913 symbol = '%s-%s' % (o, s)
2916 # If the symbol is in the form "Object:property", then change the symbol to
2917 # "Object--property" and use "property" as the text.
2919 o, p = symbol.split(':', 1)
2920 symbol = '%s--%s' % (o, p)
2925 text = tagify(text, tag)
2928 return MakeXRef(symbol, text)
2932 #############################################################################
2933 # Function : ModifyXMLElements
2934 # Description : Looks for given XML element tags within the text, and calls
2935 # the callback on pieces of text inside & outside those elements.
2936 # Used for special handling of text inside things like CDATA
2937 # and <programlisting>.
2938 # Arguments : $text - the text.
2939 # $symbol - the symbol currently being documented (only used for
2941 # $start_tag_regexp - the regular expression to match start tags.
2942 # e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to match
2943 # CDATA sections or programlisting elements.
2944 # $end_tag_func - function which is passed the matched start tag
2945 # and should return the appropriate end tag string regexp.
2946 # $callback - callback called with each part of the text. It is
2947 # called with a piece of text, the symbol being
2948 # documented, and the matched start tag or '' if the text
2949 # is outside the XML elements being matched.
2950 #############################################################################
2951 def ModifyXMLElements(text, symbol, start_tag_regexp, end_tag_func, callback):
2952 before_tag = start_tag = end_tag_regexp = end_tag = None
2955 m = re.search(start_tag_regexp, text, flags=re.S)
2957 before_tag = text[0:m.begin()] # Prematch for last successful match string
2958 start_tag = m.group(0) # Last successful match
2959 text = text[m.end():] # Postmatch for last successful match string
2961 result += callback(before_tag, symbol, '')
2964 # get the matching end-tag for current tag
2965 end_tag_regexp = end_tag_func(start_tag)
2967 m2 = re.search(end_tag_regexp, text, flags=re.S)
2969 before_tag = text[0:m2.begin()]
2970 end_tag = m2.group(0)
2971 text = text[m2.end():]
2973 result += callback(before_tag, symbol, start_tag)
2976 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
2977 "Can't find tag end: %s in docs for: %s." % (end_tag_regexp, symbol))
2978 # Just assume it is all inside the tag.
2979 result += callback(text, symbol, start_tag)
2981 m = re.search(start_tag_regexp, text, flags=re.S)
2983 # Handle any remaining text outside the tags.
2984 result += callback(text, symbol, '')
2993 # Adds a tag around some text.
2994 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
2995 def tagify(text, elem):
2996 return "<" + elem + ">" + +text + "</" + +elem + ">"
2998 #############################################################################
2999 # Function : MakeDocHeader
3000 # Description : Builds a docbook header for the given tag
3001 # Arguments : $tag - doctype tag
3002 #############################################################################
3004 def MakeDocHeader(tag):
3005 header = doctype_header
3006 header = re.sub(r'<!DOCTYPE \w+', r'<!DOCTYPE ' + tag, header)
3008 # fix the path for book since this is one level up
3010 header = re.sub(r'<!ENTITY % gtkdocentities SYSTEM \"../([a-zA-Z./]+)\">', r'<!ENTITY % gtkdocentities SYSTEM \"\1\">', header)
3015 #############################################################################
3016 # Function : MakeXRef
3017 # Description : This returns a cross-reference link to the given symbol.
3018 # Though it doesn't try to do this for a few standard C types
3019 # that it knows won't be in the documentation.
3020 # Arguments : $symbol - the symbol to try to create a XRef to.
3021 # $text - text text to put inside the XRef, defaults to $symbol
3022 #############################################################################
3024 def MakeXRef(symbol, text=None):
3025 symbol = symbol.strip()
3030 # Get rid of special suffixes ('-struct','-enum').
3031 text = re.sub(r'-struct$', '', text)
3032 text = re.sub(r'-enum$', '', text)
3037 logging.info("Getting type link for %s -> %s\n", symbol, text)
3039 symbol_id = common.CreateValidSGMLID(symbol)
3040 return "<link linkend=\"%s\">%s</link>" % (symbol_id, text)
3042 #############################################################################
3043 # Function : MakeIndexterms
3044 # Description : This returns a indexterm elements for the given symbol
3045 # Arguments : $symbol - the symbol to create indexterms for
3046 #############################################################################
3048 def MakeIndexterms(symbol, sid):
3052 # make the index useful, by ommiting the namespace when sorting
3053 if NAME_SPACE != '':
3054 m = re.search(r'^$NAME_SPACE\_?(.*)', symbol, flags=re.I)
3056 sortas = ' sortas=\"%s\"' % m.group(1)
3057 if symbol in Deprecated:
3058 terms += "<indexterm zone=\"%s\" role=\"deprecated\"><primary%s>%s</primary></indexterm>" % (sid, sortas, symbol)
3059 IndexEntriesDeprecated[symbol] = sid
3060 IndexEntriesFull[symbol] = sid
3062 since = Since[symbol].strip()
3064 terms += "<indexterm zone=\"%s\" role=\"%s\"><primary%s>%s</primary></indexterm>" % (sid, since, sortas, symbol)
3065 IndexEntriesSince[symbol] = sid
3066 IndexEntriesFull[symbol] = sid
3068 terms += "<indexterm zone=\"%s\"><primary%s>%s</primary></indexterm>" % (sid, sortas, symbol)
3069 IndexEntriesFull[symbol] = sid
3073 #############################################################################
3074 # Function : MakeDeprecationNote
3075 # Description : This returns a deprecation warning for the given symbol.
3076 # Arguments : $symbol - the symbol to try to create a warning for.
3077 #############################################################################
3079 def MakeDeprecationNote(symbol):
3081 if symbol in Deprecated:
3082 desc += "<warning><para><literal>%s</literal> " % symbol
3084 note = Deprecated[symbol]
3086 m = re.search(r'^\s*([0-9\.]+)\s*:?', note)
3088 desc += "has been deprecated since version %s and should not be used in newly-written code.</para>" % m.group(1)
3090 desc += "is deprecated and should not be used in newly-written code.</para>"
3092 note = re.sub(r'^\s*([0-9\.]+)\s*:?\s*', '', note)
3096 note = ConvertMarkDown(symbol, note)
3099 desc += "</warning>\n"
3104 #############################################################################
3105 # Function : MakeConditionDescription
3106 # Description : This returns a sumary of conditions for the given symbol.
3107 # Arguments : $symbol - the symbol to try to create the sumary.
3108 #############################################################################
3110 def MakeConditionDescription(symbol):
3112 if symbol in Deprecated:
3115 m = re.search(r'^\s*(.*?)\s*$', Deprecated[symbol])
3117 desc += "deprecated:%s" % m.group(1)
3119 desc += "deprecated"
3124 m = re.search(r'^\s*(.*?)\s*$', Since[symbol])
3126 desc += "since:%s" % m.group(1)
3132 if symbol in StabilityLevel:
3136 desc += "stability:" + StabilityLevel[symbol]
3141 cond = re.sub(r'"', r'"', cond)
3142 desc = ' condition=\"%s\"' % cond
3143 logging.info("condition for '%s' = '%s'\n", symbol, desc)
3148 #############################################################################
3149 # Function : GetHierarchy
3150 # Description : Returns the DocBook output describing the ancestors and
3151 # immediate children of a GObject subclass. It uses the
3152 # global @Objects and @ObjectLevels arrays to walk the tree.
3154 # Arguments : $object - the GtkObject subclass.
3155 # @hierarchy - previous hierarchy
3156 #############################################################################
3158 def GetHierarchy(gobject, hierarchy_ref):
3159 hierarchy = hierarchy_ref
3161 # Find object in the objects array.
3165 for i in range(len(Objects)):
3167 if ObjectLevels[i] <= level:
3170 elif ObjectLevels[i] == level + 1:
3171 children.append(Objects[i])
3173 elif Objects[i] == gobject:
3176 level = ObjectLevels[i]
3181 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3182 ancestors = [gobject]
3183 logging.info("Level: %s\n", level)
3186 if ObjectLevels[j] < level:
3187 ancestors.append(Objects[j])
3188 level = ObjectLevels[j]
3189 logging.info("Level: %s", level)
3191 # Output the ancestors, indented and with links.
3194 for i in range(len(ancestors), -1, -1):
3195 ancestor = ancestors[i]
3196 ancestor_id = common.CreateValidSGMLID(ancestor)
3197 indent = ' ' * (level * 4)
3198 # Don't add a link to the current object, i.e. when i == 0.
3200 entry_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
3201 alt_text = indent + ancestor
3203 entry_text = indent + ancestor
3204 alt_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
3206 logging.info("Checking for '%s' or '%s'", entry_text, alt_text)
3207 # Check if we already have this object
3209 for j in range(len(hierarchy)):
3210 if hierarchy[j] == entry_text or (hierarchy[j] == alt_text):
3214 # We have a new entry, find insert position in alphabetical order
3216 for j in range(last_index, len(hierarchy)):
3217 if re.search(r'^$%s' % indent, hierarchy[j]):
3221 elif re.search(r'^%s[^ ]' % indent, hierarchy[j]):
3222 stripped_text = hierarchy[j]
3223 if r'<link linkend' not in entry_text:
3224 stripped_text = re.sub(r'<link linkend="[A-Za-z]*">', '', stripped_text)
3225 stripped_text = re.sub(r'</link>', '', stripped_text)
3227 if entry_text < stripped_text:
3234 last_index = len(hierarchy)
3236 hierarchy.insert(last_index, entry_text)
3239 # Already have this one, make sure we use the not linked version
3240 if re.search(r'<link linkend=', entry_text):
3241 hierarchy[j] = entry_text
3243 # Remember index as base insert point
3244 last_index = index + 1
3248 # Output the children, indented and with links.
3249 for i in range(len(children)):
3250 sid = common.CreateValidSGMLID(children[i])
3251 indented_text = ' ' * (level * 4) + "<link linkend=\"%s\">%s</link>" % (sid, children[i])
3252 hierarchy.insert(last_index, indented_text)
3257 #############################################################################
3258 # Function : GetInterfaces
3259 # Description : Returns the DocBook output describing the interfaces
3260 # implemented by a class. It uses the global %Interfaces hash.
3261 # Arguments : $object - the GtkObject subclass.
3262 #############################################################################
3264 def GetInterfaces(gobject):
3267 # Find object in the objects array.
3268 if gobject in Interfaces:
3269 ifaces = Interfaces[gobject].split()
3273 for i in range(len(ifaces)):
3274 sid = common.CreateValidSGMLID(ifaces[i])
3275 text += " <link linkend=\"%s\">%s</link>" % (sid, ifaces[i])
3276 if i < len(ifaces) - 1:
3278 elif i < len(ifaces):
3287 #############################################################################
3288 # Function : GetImplementations
3289 # Description : Returns the DocBook output describing the implementations
3290 # of an interface. It uses the global %Interfaces hash.
3291 # Arguments : $object - the GtkObject subclass.
3292 #############################################################################
3294 def GetImplementations(gobject):
3298 for key in Interfaces:
3299 if re.search(r'\b%s\b' % gobject, Interfaces[key]):
3305 %s is implemented by
3307 for i in range(len(impls)):
3308 sid = common.CreateValidSGMLID(impls[i])
3309 text += " <link linkend=\"%s\">%s</link>" % (sid, impls[i])
3310 if i < len(impls) - 1:
3313 elif i < len(impls):
3324 #############################################################################
3325 # Function : GetPrerequisites
3326 # Description : Returns the DocBook output describing the prerequisites
3327 # of an interface. It uses the global %Prerequisites hash.
3328 # Arguments : $iface - the interface.
3329 #############################################################################
3331 def GetPrerequisites(iface):
3334 if iface in Prerequisites:
3339 prereqs = Prerequisites[iface].split()
3340 for i in range(len(prereqs)):
3341 sid = common.CreateValidSGMLID(prereqs[i])
3342 text += " <link linkend=\"%s\">%s</link>" % (sid, prereqs[i])
3343 if i < len(prereqs) - 1:
3345 elif i < len(prereqs):
3356 #############################################################################
3357 # Function : GetDerived
3358 # Description : Returns the DocBook output describing the derived interfaces
3359 # of an interface. It uses the global %Prerequisites hash.
3360 # Arguments : $iface - the interface.
3361 #############################################################################
3363 def GetDerived(iface):
3367 for key in Prerequisites:
3368 if re.search(r'\b%s\b' % iface, Prerequisites[key]):
3370 if len(derived) > 0:
3375 for i in range(len(derived)):
3376 sid = common.CreateValidSGMLID(derived[i])
3377 text += " <link linkend=\"%s\">%s</link>" % (sid, derived[i])
3378 if i < len(derived) - 1:
3380 elif i < len(derived):
3390 #############################################################################
3391 # Function : GetSignals
3392 # Description : Returns the synopsis and detailed description DocBook output
3393 # for the signal handlers of a given GtkObject subclass.
3394 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3395 #############################################################################
3397 def GetSignals(gobject):
3401 for i in range(len(SignalObjects)):
3402 if SignalObjects[i] == gobject:
3403 logging.info("Found signal: %s\n", SignalNames[i])
3404 name = SignalNames[i]
3405 symbol = '%s::%s' % (gobject, name)
3406 sid = common.CreateValidSGMLID('%s-%s' % (gobject, name))
3408 desc += "<refsect2 id=\"%s\" role=\"signal\"><title>The <literal>“%s”</literal> signal</title>\n" % (sid, name)
3409 desc += MakeIndexterms(symbol, sid)
3411 desc += OutputSymbolExtraLinks(symbol)
3413 desc += "<programlisting language=\"C\">"
3415 m = re.search(r'\s*(const\s+)?(\w+)\s*(\**)', SignalReturns[i])
3416 type_modifier = m.group(1) if m.group(1) else ''
3418 pointer = m.group(3)
3419 xref = MakeXRef(gtype, tagify(gtype, "returnvalue"))
3421 ret_type_output = '%s%s%s' % (type_modifier, xref, pointer)
3422 callback_name = "user_function"
3423 desc += '%s\n%s (' % (ret_type_output, callback_name)
3425 indentation = ' ' * len(callback_name) + 2
3428 sourceparams = SourceSymbolParams[symbol]
3429 params = SignalPrototypes[i].split('\n')
3430 type_len = len("gpointer")
3431 name_len = len("user_data")
3432 # do two passes, the first one is to calculate padding
3434 for j in range(len(params)):
3436 # allow alphanumerics, '_', '[' & ']' in param names
3437 m = re.search(r'^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$', params[j])
3440 pointer = m.group(2)
3442 param_name = sourceparams[PARAM_FIELD_COUNT * j]
3445 param_name = m.group(3)
3448 param_name = "arg%d" % j
3451 if len(gtype) + len(pointer) > type_len:
3452 type_len = len(gtype) + len(pointer)
3453 if len(param_name) > name_len:
3454 name_len = len(param_name)
3456 xref = MakeXRef(gtype, tagify(gtype, "type"))
3457 pad = ' ' * (type_len - len(type) - len(pointer))
3458 desc += '%s%s %s%s,\n' % (xref, pad, pointer, param_name)
3462 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
3463 "Can't parse arg: %s\nArgs:%s" % (params[j], SignalPrototypes[i]))
3465 xref = MakeXRef("gpointer", tagify("gpointer", "type"))
3466 pad = ' ' * (type_len - len("gpointer"))
3467 desc += '%s%s user data)' % (xref, pad)
3468 desc += "</programlisting>\n"
3470 flags = SignalFlags[i]
3475 flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>"
3478 flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>"
3481 flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>"
3482 flags_string = "Cleanup"
3486 flags_string += " / "
3487 flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>"
3491 flags_string += " / "
3492 flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>"
3496 flags_string += " / "
3497 flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>"
3501 flags_string += " / "
3502 flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>"
3504 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" % (ret_type_output, sid, name, flags_string)
3506 parameters = OutputParamDescriptions("SIGNAL", symbol)
3508 AllSymbols[symbol] = 1
3509 if SymbolDocs[symbol]:
3510 symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
3514 if not IsEmptyDoc(SymbolDocs[symbol]):
3515 AllDocumentedSymbols[symbol] = 1
3517 if SymbolAnnotations[symbol]:
3518 param_desc = SymbolAnnotations[symbol]
3519 param_annotations = ''
3520 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
3521 if param_annotations != '':
3522 desc += "\n<para>%s</para>" % param_annotations
3524 desc += MakeDeprecationNote(symbol)
3528 desc += "<para>Flags: %s</para>\n" % flags_string
3530 desc += OutputSymbolTraits(symbol)
3531 desc += "</refsect2>"
3533 return (synop, desc)
3535 #############################################################################
3536 # Function : GetArgs
3537 # Description : Returns the synopsis and detailed description DocBook output
3538 # for the Args of a given GtkObject subclass.
3539 # Arguments : $object - the GtkObject subclass, e.g. 'GtkButton'.
3540 #############################################################################
3542 def GetArgs(gobject):
3550 for i in range(len(ArgObjects)):
3551 if ArgObjects[i] == gobject:
3552 logging.info("Found arg: %s", ArgNames[i])
3560 kind = "child property"
3563 kind = "style property"
3569 # Remember only one colon so we don't clash with signals.
3570 symbol = '%s:%s' % (gobject, name)
3571 # use two dashes and ev. an extra separator here for the same reason.
3572 sid = common.CreateValidSGMLID('%s--%s%s' % (gobject, id_sep, name))
3576 arange = ArgRanges[i]
3577 range_output = CreateValidSGML(arange)
3578 default = ArgDefaults[i]
3579 default_output = CreateValidSGML(default)
3581 if type == "GtkString":
3582 type = "char *"
3584 if type == "GtkSignal":
3585 type = "GtkSignalFunc, gpointer"
3586 type_output = MakeXRef("GtkSignalFunc") + ", " + MakeXRef("gpointer")
3587 elif re.search(r'^(\w+)\*$', type):
3588 m = re.search(r'^(\w+)\*$', type)
3589 type_output = MakeXRef(m.group(1), tagify(m.group(1), "type")) + " *"
3591 type_output = MakeXRef(atype, tagify(atype, "type"))
3594 flags_string = "Read"
3598 flags_string += " / "
3599 flags_string += "Write"
3603 flags_string += " / "
3604 flags_string += "Construct"
3608 flags_string += " / "
3609 flags_string += "Construct Only"
3612 AllSymbols[symbol] = 1
3614 if symbol in SymbolDocs and not IsEmptyDoc(SymbolDocs[symbol]):
3615 blurb = ConvertMarkDown(symbol, SymbolDocs[symbol])
3616 logging.info(".. [%s][%s]\n", SymbolDocs[symbol], blurb)
3617 AllDocumentedSymbols[symbol] = 1
3620 if ArgBlurbs[i] != '':
3621 blurb = "<para>" + CreateValidSGML(ArgBlurbs[i]) + "</para>"
3622 AllDocumentedSymbols[symbol] = 1
3624 # FIXME: print a warning?
3625 logging.info(".. no description\n")
3629 pad1 = " " * (24 - len(name))
3632 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" % (type_output, sid, name, flags_string)
3633 arg_desc = "<refsect2 id=\"%s\" role=\"property\"><title>The <literal>“%s”</literal> %s</title>\n" % (sid, name, kind)
3634 arg_desc += MakeIndexterms(symbol, sid)
3636 arg_desc += OutputSymbolExtraLinks(symbol)
3638 arg_desc += "<programlisting> “%s”%s %s</programlisting>\n" % (name, pad1, type_output)
3640 if symbol in SymbolAnnotations:
3641 param_desc = SymbolAnnotations[symbol]
3642 param_annotations = ''
3643 (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
3644 if param_annotations != '':
3645 arg_desc += "\n<para>%s</para>" % param_annotations
3647 arg_desc += MakeDeprecationNote(symbol)
3650 arg_desc += "<para>Flags: %s</para>\n" % flags_string
3653 arg_desc += "<para>Allowed values: %s</para>\n" % range_output
3656 arg_desc += "<para>Default value: %s</para>\n" % default_output
3658 arg_desc += OutputSymbolTraits(symbol)
3659 arg_desc += "</refsect2>\n"
3662 child_synop += arg_synop
3663 child_desc += arg_desc
3666 style_synop += arg_synop
3667 style_desc += arg_desc
3673 return (synop, child_synop, style_synop, desc, child_desc, style_desc)
3677 #############################################################################
3678 # Function : ReadSourceDocumentation
3679 # Description : This reads in the documentation embedded in comment blocks
3680 # in the source code (for Gnome).
3682 # Parameter descriptions override any in the template files.
3683 # Function descriptions are placed before any description from
3684 # the template files.
3686 # It recursively descends the source directory looking for .c
3687 # files and scans them looking for specially-formatted comment
3690 # Arguments : $source_dir - the directory to scan.
3691 #############m###############################################################
3693 def ReadSourceDocumentation(source_dir):
3695 # prepend entries from @SOURCE_DIR
3696 for sdir in SOURCE_DIRS:
3697 # Check if the filename is in the ignore list.
3698 m1 = re.search(r'^%s/(.*)$' % re.escape(sdir), source_dir)
3700 m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), IGNORE_FILES)
3702 logging.info("Skipping source directory: %s", source_dir)
3705 logging.info("No match for: %s", (m2.group(1) or source_dir))
3707 logging.info("Scanning source directory: %s", source_dir)
3709 # This array holds any subdirectories found.
3712 suffix_list = SOURCE_SUFFIXES.split(',')
3714 for ifile in os.listdir(source_dir):
3715 fname = os.path.join(source_dir, ifile)
3716 if ifile.startswith('.'):
3718 elif os.path.isdir(fname):
3719 subdirs.append(ifile)
3721 for suffix in suffix_list:
3722 if re.search(r'\.%s$' % re.escape(suffix), ifile):
3723 ScanSourceFile(fname)
3724 elif re.search(r'\.[ch]$', ifile):
3725 ScanSourceFile(fname)
3727 # Now recursively scan the subdirectories.
3728 for sdir in subdirs:
3729 ReadSourceDocumentation(os.path.join(source_dir, sdir))
3731 #############################################################################
3732 # Function : ScanSourceFile
3733 # Description : Scans one source file looking for specially-formatted comment
3734 # blocks. Later &MergeSourceDocumentation is used to merge any
3735 # documentation found with the documentation already read in
3736 # from the template files.
3738 # Arguments : $file - the file to scan.
3739 #############################################################################
3741 def ScanSourceFile(ifile):
3743 # prepend entries from @SOURCE_DIR
3744 for idir in SOURCE_DIRS:
3745 # Check if the filename is in the ignore list.
3746 m1 = re.search(r'^%s/(.*)$' % re.escape(idir), ifile)
3748 m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), IGNORE_FILES)
3750 logging.info("Skipping source file: ", ifile)
3753 m = re.search(r'^.*[\/\\]([^\/\\]*)$', ifile)
3755 basename = m.group(1)
3757 common.LogWarning(ifile, 1, "Can't find basename for this filename.")
3761 # Check if the basename is in the list of files to ignore.
3762 if re.search(r'(\s|^)%s(\s|$)' % re.escape(basename), IGNORE_FILES):
3763 logging.info("Skipping source file: %s", ifile)
3767 logging.info("Scanning source file: %s", ifile)
3769 SRCFILE = open(ifile)
3770 in_comment_block = False
3775 since_desc = stability_desc = deprecated_desc = ''
3776 current_param = None
3779 for line in SRCFILE:
3781 # Look for the start of a comment block.
3782 if not in_comment_block:
3783 if re.search(r'^\s*/\*.*\*/', line):
3784 #one-line comment - not gtkdoc
3786 elif re.search(r'^\s*/\*\*\s', line):
3787 logging.info("Found comment block start\n")
3789 in_comment_block = True
3791 # Reset all the symbol data.
3797 deprecated_desc = ''
3804 # We're in a comment block. Check if we've found the end of it.
3805 if re.search(r'^\s*\*+/', line):
3807 # maybe its not even meant to be a gtk-doc comment?
3808 common.LogWarning(ifile, line_number, "Symbol name not found at the start of the comment block.")
3810 # Add the return value description onto the end of the params.
3812 # TODO(ensonic): check for duplicated Return docs
3813 # &common.LogWarning($file, line_number, "Multiple Returns for $symbol.")
3814 params.append("Returns")
3815 params.append(return_desc)
3817 # Convert special characters
3818 description = ConvertSGMLChars(symbol, description)
3819 for k in range(1, len(params), PARAM_FIELD_COUNT):
3820 params[k] = ConvertSGMLChars(symbol, params[k])
3823 # Handle Section docs
3824 m = re.search(r'SECTION:\s*(.*)', symbol)
3825 m2 = re.search(r'PROGRAM:\s*(.*)', symbol)
3827 real_symbol = m.group(1)
3828 long_descr = os.path.join(TMPL_DIR, real_symbol + ":Long_Description")
3830 if len(KnownSymbols) > 0:
3831 if long_descr in KnownSymbols or KnownSymbols[long_descr] != 1:
3832 common.LogWarning(ifile, line_number, "Section %s is not defined in the %s-sections.txt file." % (real_symbol, MODULE))
3834 logging.info("SECTION DOCS found in source for : '%s'", real_symbol)
3835 for k in range(0, len(params), PARAM_FIELD_COUNT):
3836 logging.info(" '" + params[k] + "'\n")
3837 params[k] = params[k].lower()
3839 if params[k] == "short_description":
3840 key = os.path.join(TMPL_DIR, real_symbol + ":Short_Description")
3841 elif params[k] == "see_also":
3842 key = os.path.join(TMPL_DIR, real_symbol + ":See_Also")
3843 elif params[k] == "title":
3844 key = os.path.join(TMPL_DIR, real_symbol + ":Title")
3845 elif params[k] == "stability":
3846 key = os.path.join(TMPL_DIR, real_symbol + ":Stability_Level")
3847 elif params[k] == "section_id":
3848 key = os.path.join(TMPL_DIR, real_symbol + ":Section_Id")
3849 elif params[k] == "include":
3850 key = os.path.join(TMPL_DIR, real_symbol + ":Include")
3851 elif params[k] == "image":
3852 key = os.path.join(TMPL_DIR, real_symbol + ":Image")
3855 SourceSymbolDocs[key] = params[k+1]
3856 SourceSymbolSourceFile[key] = ifile
3857 SourceSymbolSourceLine[key] = line_number
3860 SourceSymbolDocs[long_descr] = description
3861 SourceSymbolSourceFile[long_descr] = ifile
3862 SourceSymbolSourceLine[long_descr] = line_number
3863 #$SourceSymbolTypes{$symbol} = "SECTION"
3865 real_symbol = m2.group(1)
3869 logging.info("PROGRAM DOCS found in source for '%s'", real_symbol)
3870 for k in range(0, len(params), PARAM_FIELD_COUNT):
3871 logging.info(" '" + params[k] + "'\n")
3872 params[k] = params[k].lower()
3875 if params[k] == "short_description":
3876 key = os.path.join(TMPL_DIR, real_symbol, ":Short_Description")
3877 elif params[k] == "see_also":
3878 key = os.path.join(TMPL_DIR, real_symbol + ":See_Also")
3879 elif params[k] == "section_id":
3880 key = os.path.join(TMPL_DIR, real_symbol + ":Section_Id")
3881 elif params[k] == "synopsis":
3882 key = os.path.join(TMPL_DIR, real_symbol + ":Synopsis")
3883 elif params[k] == "returns":
3884 key = os.path.join(TMPL_DIR, real_symbol + ":Returns")
3885 elif re.search(r'^(-.*)', params[k]):
3886 m4 = re.search(r'^(-.*)', params[k])
3887 key = os.path.join(TMPL_DIR, real_symbol + ":Options")
3888 if key in SourceSymbolDocs:
3889 opts = SourceSymbolDocs[key]
3893 opts.append(m4.group(1))
3894 opts.append(params[k+1])
3896 SourceSymbolDocs[key] = opts
3900 SourceSymbolDocs[key] = params[k+1]
3901 SourceSymbolSourceFile[key] = ifile
3902 SourceSymbolSourceLine[key] = line_number
3905 long_descr = os.path.join(TMPL_DIR, real_symbol + ":Long_Description")
3906 SourceSymbolDocs[long_descr] = description
3907 SourceSymbolSourceFile[long_descr] = ifile
3908 SourceSymbolSourceLine[long_descr] = line_number
3910 section_id = SourceSymbolDocs[os.path.join(TMPL_DIR, real_symbol + ":Section_Id")]
3911 if section_id and not re.search(r'^\s*$', section_id):
3912 # Remove trailing blanks and use as is
3913 section_id = section_id.rstrip()
3915 section_id = common.CreateValidSGMLID('%s-%s' % (MODULE, real_symbol))
3916 OutputProgramDBFile(real_symbol, section_id)
3919 logging.info("SYMBOL DOCS found in source for : '%s' %d ", symbol, len(description))
3920 SourceSymbolDocs[symbol] = description
3921 SourceSymbolParams[symbol] = [params]
3922 # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO"
3923 #if (defined $DeclarationTypes{$symbol})
3924 # $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol
3926 SourceSymbolSourceFile[symbol] = ifile
3927 SourceSymbolSourceLine[symbol] = line_number
3931 arr = since_desc.split('\n')
3932 since_desc = arr[0].strip()
3933 extra_lines = arr[1:]
3934 logging.info("Since(%s) : [%s]\n", symbol, since_desc)
3935 Since[symbol] = ConvertSGMLChars(symbol, since_desc)
3936 if len(extra_lines) > 0:
3937 common.LogWarning(ifile, line_number, "multi-line since docs found")
3940 stability_desc = ParseStabilityLevel(stability_desc, ifile, line_number, "Stability level for %s" % symbol)
3941 StabilityLevel[symbol] = ConvertSGMLChars(symbol, stability_desc)
3945 if symbol in Deprecated:
3946 # don't warn for signals and properties
3947 #if ($symbol !~ m/::?(.*)/)
3948 if symbol in DeclarationTypes:
3949 common.LogWarning(ifile, line_number,
3950 "%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)
3953 Deprecated[symbol] = ConvertSGMLChars(symbol, deprecated_desc)
3957 in_comment_block = False
3960 # Get rid of ' * ' at start of every line in the comment block.
3961 line = re.sub(r'^\s*\*\s?', '', line)
3962 # But make sure we don't get rid of the newline at the end.
3963 if not line.endswith('\n'):
3966 logging.info("scanning : %s", line)
3968 # If we haven't found the symbol name yet, look for it.
3970 m1 = re.search(r'^\s*(SECTION:\s*\S+)', line)
3971 m2 = re.search(r'^\s*(PROGRAM:\s*\S+)', line)
3972 m3 = re.search(r'^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$', line)
3974 symbol = m1.group(1)
3975 logging.info("SECTION DOCS found in source for : '%s'", symbol)
3977 symbol = m2.group(1)
3978 logging.info("PROGRAM DOCS found in source for : '%s'", symbol)
3980 symbol = m3.group(1)
3981 annotation = m3.group(2)
3982 logging.info("SYMBOL DOCS found in source for : '%s'\n", symbol)
3984 annotation = annotation.strip()
3985 if annotation != '':
3986 SymbolAnnotations[symbol] = annotation
3987 logging.info("remaining text for $symbol: '%s'\n", annotation)
3992 if in_part == "description":
3993 # Get rid of 'Description:'
3994 line = re.sub(r'^\s*Description:', '', line)
3997 m = re.search(r'^\s*(returns|return\s+value):', line, flags=re.I)
3998 m2 = re.search(r'^\s*since:', line, flags=re.I)
3999 m3 = re.search(r'^\s*deprecated:', line, flags=re.I)
4000 m4 = re.search(r'^\s*stability:', line, flags=re.I)
4003 # we're in param section and have not seen the blank line
4005 return_desc = line[m.end():]
4010 # we're in param section and have not seen the blank line
4011 if in_part != "param":
4012 since_desc = line[m2.end():]
4017 # we're in param section and have not seen the blank line
4018 if in_part != "param":
4019 deprecated_desc = line[m.end():]
4020 in_part = "deprecated"
4024 stability_desc = line[m.end():]
4025 in_part = "stability"
4029 if in_part == "description":
4032 elif in_part == "return":
4035 elif in_part == "since":
4038 elif in_part == "stability":
4039 stability_desc += line
4041 elif in_part == "deprecated":
4042 deprecated_desc += line
4046 # We must be in the parameters. Check for the empty line below them.
4047 if re.search(r'^\s*$', line):
4048 in_part = "description"
4052 # Look for a parameter name.
4053 m = re.search(r'^\s*@(.+?)\s*:\s*', line)
4055 param_name = m.group(1)
4056 param_desc = line[m.end():]
4058 logging.info("Found parameter: %s", param_name)
4059 # Allow varargs variations
4060 if re.search(r'^\.\.\.$', param_name):
4063 logging.info("Found param for symbol $symbol : '%s'= '%s'", param_name, line)
4065 params.append(param_name)
4066 params.append(param_desc)
4067 current_param += PARAM_FIELD_COUNT
4071 logging.info("continuation for %s annotation '%s'", symbol, line)
4073 annotation = re.sub(r'^\s+|\s+$', '', annotation)
4074 SymbolAnnotations[symbol] += annotation
4078 # We must be in the middle of a parameter description, so add it on
4079 # to the last element in @params.
4080 if current_param == -1:
4081 common.LogWarning(file, line_number, "Parsing comment block file : parameter expected, but got '%s'" % line)
4083 params[len(params)] += line
4089 #############################################################################
4090 # Function : OutputMissingDocumentation
4091 # Description : Outputs report of documentation coverage to a file
4094 #############################################################################
4096 def OutputMissingDocumentation():
4097 old_undocumented_file = os.path.join(ROOT_DIR, MODULE + "undocumented.txt")
4098 new_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.new")
4106 buffer_deprecated = ''
4107 buffer_descriptions = ''
4109 UNDOCUMENTED = open(new_undocumented_file, 'w')
4111 for symbol in sorted(AllSymbols.keys()):
4112 # FIXME: should we print common.LogWarnings for undocumented stuff?
4114 #my $ssfile = &GetSymbolSourceFile($symbol)
4115 #my $ssline = &GetSymbolSourceLine($symbol)
4116 #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . (defined($ssline)?$ssline:"0") . "\n"
4118 m = re.search(r':(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)', symbol)
4119 m2 = re.search(r':(Long_Description|Short_Description)', symbol)
4122 if symbol in AllDocumentedSymbols:
4124 if symbol in AllIncompleteSymbols:
4126 buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
4127 #$buffer += "\t0: ".$location
4129 elif symbol in Deprecated:
4130 if symbol in AllIncompleteSymbols:
4132 buffer_deprecated += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
4133 #$buffer += "\t1a: ".$location
4135 buffer_deprecated += symbol + "\n"
4136 #$buffer += "\t1b: ".$location
4139 if symbol in AllIncompleteSymbols:
4141 buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
4142 #$buffer += "\t2a: ".$location
4144 buffer += symbol + "\n"
4145 #$buffer += "\t2b: ".$location
4150 if symbol in SymbolDocs and len(SymbolDocs[symbol]) > 0\
4151 or symbol in AllDocumentedSymbols and len(AllDocumentedSymbols[symbol]) > 0:
4154 # cut off the leading namespace ($TMPL_DIR)
4155 m = re.search(r'^.*\/(.*)$', symbol)
4156 buffer_descriptions += m.group(1) + "\n"
4161 percent = (n_documented / total) * 100.0
4164 UNDOCUMENTED.write("%.0f%% symbol docs coverage.\n"%percent)
4165 UNDOCUMENTED.write("%s symbols documented.\n" % n_documented)
4166 UNDOCUMENTED.write("%s symbols incomplete.\n" % n_incomplete)
4167 UNDOCUMENTED.write("%d not documented.\n" % (total - n_documented))
4169 if buffer_deprecated != '':
4170 buffer += "\n" + buffer_deprecated
4172 if buffer_descriptions != '':
4173 buffer += "\n" + buffer_descriptions
4176 UNDOCUMENTED.write("\n\n" + buffer)
4178 UNDOCUMENTED.close()
4180 return common.UpdateFileIfChanged(old_undocumented_file, new_undocumented_file, 0)
4182 # printf "%.0f%% symbol docs coverage", $percent
4183 # print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) . " not documented)\n"
4184 # print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't include intro sections.\n"
4188 #############################################################################
4189 # Function : OutputUndeclaredSymbols
4190 # Description : Outputs symbols that are listed in the section file, but not
4191 # declaration is found in the sources
4194 #############################################################################
4196 def OutputUndeclaredSymbols():
4197 old_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.txt")
4198 new_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.new")
4200 UNDECLARED = open(new_undeclared_file, 'w')
4202 if UndeclaredSymbols:
4203 UNDECLARED.write("\n".join(sorted(UndeclaredSymbols.keys())))
4204 UNDECLARED.write("\n")
4205 print("See %s-undeclared.txt for the list of undeclared symbols." % MODULE)
4209 return common.UpdateFileIfChanged(old_undeclared_file, new_undeclared_file, 0)
4212 #############################################################################
4213 # Function : OutputUnusedSymbols
4214 # Description : Outputs symbols that are documented in comments, but not
4215 # declared in the sources
4218 #############################################################################
4220 def OutputUnusedSymbols():
4222 old_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.txt")
4223 new_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.new")
4225 UNUSED = open(new_unused_file, 'w')
4227 for symbol in sorted(Declarations.keys()):
4228 if not symbol in DeclarationOutput:
4229 UNUSED.write("%s\n" % symbol)
4233 for symbol in sorted(AllUnusedSymbols.keys()):
4234 UNUSED.write(symbol + "(" + AllUnusedSymbols[symbol] + ")\n")
4239 common.LogWarning(old_unused_file, 1, "%d unused declarations. They should be added to %s-sections.txt in the appropriate place." % (num_unused, MODULE))
4242 return common.UpdateFileIfChanged(old_unused_file, new_unused_file, 0)
4246 #############################################################################
4247 # Function : OutputAllSymbols
4248 # Description : Outputs list of all symbols to a file
4251 #############################################################################
4253 def OutputAllSymbols():
4254 SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-symbols.txt"), 'w')
4256 for symbol in sorted(AllSymbols.keys()):
4257 SYMBOLS.write(symbol + "\n")
4261 #############################################################################
4262 # Function : OutputSymbolsWithoutSince
4263 # Description : Outputs list of all symbols without a since tag to a file
4266 #############################################################################
4268 def OutputSymbolsWithoutSince():
4269 SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-nosince.txt"), 'w')
4271 for symbol in sorted(SourceSymbolDocs.keys()):
4273 SYMBOLS.write(symbol + "\n")
4276 #############################################################################
4277 # Function : MergeSourceDocumentation
4278 # Description : This merges documentation read from a source file into the
4279 # documentation read in from a template file.
4281 # Parameter descriptions override any in the template files.
4282 # Function descriptions are placed before any description from
4283 # the template files.
4286 #############################################################################
4288 def MergeSourceDocumentation():
4289 if len(SymbolDocs) > 0:
4290 Symbols = SymbolDocs.keys()
4291 logging.info("num existing entries: %d\n", len(Symbols))
4294 # filter scanned declarations, with what we suppress from -sections.txt
4296 for symbol in Declarations.keys():
4297 if symbol in KnownSymbols and KnownSymbols[symbol] == 1:
4301 # , add the rest from -sections.txt
4302 for symbol in KnownSymbols.keys():
4303 if KnownSymbols[symbol] == 1:
4307 # and add whats found in the source
4308 for symbol in SourceSymbolDocs.keys():
4311 Symbols = tmp.keys()
4312 logging.info("num source entries: %d\n", len(Symbols))
4314 for symbol in Symbols:
4315 AllSymbols[symbol] = 1
4319 ## see if the symbol is documented in template
4320 tmpl_doc = SymbolDocs.get(symbol, '')
4321 check_tmpl_doc = tmpl_doc
4322 # remove all xml-tags and whitespaces
4323 check_tmpl_doc = re.sub(r'<.*?>', '', check_tmpl_doc)
4324 check_tmpl_doc = re.sub(r'\s', '', check_tmpl_doc)
4326 if check_tmpl_doc != '':
4329 # if the docs have just an empty para, don't merge that.
4330 check_tmpl_doc = re.sub(r'(\s|\n)', '', tmpl_doc, flags=re.M|re.S)
4331 if check_tmpl_doc == "<para></para>":
4334 if symbol in SourceSymbolDocs:
4335 stype = DeclarationTypes[symbol]
4337 logging.info("merging [%s] from source\n", symbol)
4341 if stype == 'STRUCT':
4343 elif stype == 'ENUM':
4345 elif stype == 'UNION':
4350 src_doc = SourceSymbolDocs[symbol]
4351 # remove leading and training whitespaces
4352 src_doc = src_doc.strip()
4354 # Don't output warnings for overridden titles as titles are
4355 # automatically generated in the -sections.txt file, and thus they
4356 # are often overridden.
4357 m = re.search(r':Title$', symbol)
4358 if have_tmpl_docs and not m:
4359 # check if content is different
4360 if tmpl_doc != src_doc:
4361 #print "[$tmpl_doc] [$src_doc]\n"
4362 common.LogWarning(SourceSymbolSourceFile[symbol], SourceSymbolSourceLine[symbol],
4363 "Documentation in template " + SymbolSourceFile[symbol] + ":" + SymbolSourceLine[symbol] + " for %s being overridden by inline comments." % symbol)
4366 AllDocumentedSymbols[symbol] = 1
4369 # Do not add <para> to nothing, it breaks missing docs checks.
4372 src_doc_para = src_doc
4375 m = re.search(r'$TMPL_DIR\/.+:Long_Description', symbol)
4376 m2 = re.search(TMPL_DIR + r'\/.+:.+', symbol)
4378 SymbolDocs[symbol] = '%s%s' % (src_doc_para, tmpl_doc)
4380 # For the title/summary/see also section docs we don't want to
4381 # add any <para> tags.
4382 SymbolDocs[symbol] = src_doc
4384 SymbolDocs[symbol] = '%s%s' % (src_doc_para, tmpl_doc)
4387 if re.search(r'.*::.*', symbol):
4388 # For signals we prefer the param names from the source docs,
4389 # since the ones from the templates are likely to contain the
4390 # artificial argn names which are generated by gtkdoc-scangobj.
4391 SymbolParams[symbol] = SourceSymbolParams[symbol]
4392 # FIXME: we need to check for empty docs here as well!
4394 # The templates contain the definitive parameter names and order,
4395 # so we will not change that. We only override the actual text.
4396 tmpl_params = SymbolParams[symbol]
4398 logging.info("No merge needed for %s\n", symbol)
4399 SymbolParams[symbol] = SourceSymbolParams[symbol]
4400 # FIXME: we still like to get the number of params and merge
4401 # 1) we would noticed that params have been removed/renamed
4402 # 2) we would catch undocumented params
4403 # params are not (yet) exported in -decl.txt so that we
4404 # could easily grab them :/
4406 params = SourceSymbolParams[symbol]
4407 logging.info("Merge needed for %s, tmpl_params: %d, source_params: %d \n", symbol, len(tmpl_params), len(params))
4408 for j in range(0, len(tmpl_params), PARAM_FIELD_COUNT):
4409 tmpl_param_name = tmpl_params[j]
4411 # Try to find the param in the source comment documentation.
4413 logging.info(" try merge param %s\n", tmpl_param_name)
4414 for k in range(0, len(params), PARAM_FIELD_COUNT):
4415 param_name = params[k]
4416 param_desc = params[k + 1]
4418 logging.info(" test param %s\n", param_name)
4419 # We accept changes in case, since the Gnome source
4420 # docs contain a lot of these.
4421 if param_name.lower() == tmpl_param_name.lower():
4424 # Override the description.
4425 tmpl_params[j + 1] = param_desc
4427 # Set the name to '' to mark it as used.
4431 # If it looks like the parameters are there, but not
4432 # in the right place, try to explain a bit better.
4433 if found and re.search(r'\@%s:' % tmpl_param_name, src_doc):
4434 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
4435 "Parameters for %s must start on the line immediately after the function or macro name." % symbol)
4437 # Now we output a warning if parameters have been described which
4439 for j in range(0, len(params), PARAM_FIELD_COUNT):
4440 param_name = params[j]
4442 # the template builder cannot detect if a macro returns
4444 if stype == "MACRO" and param_name == "Returns":
4445 # FIXME: do we need to add it then to tmpl_params[] ?
4446 num = len(tmpl_params)
4447 logging.info(" adding Returns: to macro docs for %s.", symbol)
4448 tmpl_params[num+1] = "Returns"
4449 tmpl_params[num+2] = params[j+1]
4452 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
4453 "%s described in source code comment block but does not exist. %s: %s %s: %s." % (item, stype, symbol, item, param_name))
4457 AllDocumentedSymbols[symbol] = 1
4458 logging.info("merging [%s] from template", symbol)
4461 logging.info("[%s] undocumented\n", symbol)
4463 # if this symbol is documented, check if docs are complete
4464 check_tmpl_doc = SymbolDocs.get(symbol, '')
4465 # remove all xml-tags and whitespaces
4466 check_tmpl_doc = re.sub(r'<.*?>', '', check_tmpl_doc)
4467 check_tmpl_doc = re.sub(r'\s', '', check_tmpl_doc)
4468 if check_tmpl_doc != '':
4469 tmpl_params = SymbolParams[symbol]
4471 stype = DeclarationTypes[symbol]
4475 if stype == 'STRUCT':
4477 elif stype == 'ENUM':
4479 elif stype == 'UNION':
4484 logging.info("Check param docs for %s, tmpl_params: %s entries, type=%s\n", symbol, len(tmpl_params), stype)
4486 if len(tmpl_params > 0):
4487 for j in range(0, len(tmpl_params), PARAM_FIELD_COUNT):
4488 # Output a warning if the parameter is empty and
4489 # remember for stats.
4490 tmpl_param_name = tmpl_params[j]
4491 tmpl_param_desc = tmpl_params[j + 1]
4492 if tmpl_param_name != "void" and not re.search(r'\S', tmpl_param_desc):
4493 if symbol in AllIncompleteSymbols[symbol]:
4494 AllIncompleteSymbols[symbol] += ", " + tmpl_param_name
4496 AllIncompleteSymbols[symbol] = tmpl_param_name
4498 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
4499 "%s description for %s::%s is missing in source code comment block." % (item, symbol, tmpl_param_name))
4502 if len(tmpl_params) == 0:
4503 AllIncompleteSymbols[symbol] = "<items>"
4504 common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
4505 "%s descriptions for %s are missing in source code comment block." % (item, symbol))
4507 # $#$tmpl_params==-1 means we don't know about parameters
4508 # this unfortunately does not tell if there should be some
4509 logging.info("num doc entries: %d\n", len(SymbolDocs))
4512 #############################################################################
4513 # Function : IsEmptyDoc
4514 # Description : Check if a doc-string is empty. Its also regarded as empty if
4515 # it only consist of whitespace or e.g. FIXME.
4516 # Arguments : the doc-string
4517 #############################################################################
4518 def IsEmptyDoc(doc):
4520 if re.search(r'^\s*$', doc):
4524 if re.search(r'^\s*<para>\s*(FIXME)?\s*<\/para>\s*$', doc):
4531 #############################################################################
4532 # Function : ConvertMarkDown
4533 # Description : Converts mark down syntax to the respective docbook.
4534 # http://de.wikipedia.org/wiki/Markdown
4535 # Inspired by the design of ParseDown
4536 # http://parsedown.org/
4537 # Copyright (c) 2013 Emanuil Rusev, erusev.com
4538 # Arguments : the symbol name, the doc-string
4539 #############################################################################
4541 def ConvertMarkDown(symbol, text):
4542 text = MarkDownParse(text, symbol)
4546 # SUPPORTED MARKDOWN
4547 # ==================
4556 # Setext-style Headers
4557 # --------------------
4565 # Ordered (unnested) Lists
4566 # ------------------------
4570 # 1. item 2 with loooong
4575 # Note: we require a blank line above the list items
4578 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
4580 def MarkDownParseBlocks(linesref, symbol, context):
4582 md_block = {"type": ''}
4584 # FIXME the original code had OUTER tag here but there does not seem to be inner loops.
4585 for line in linesref:
4586 first_char = line[0]
4588 logging.info("in '" + md_block["type"] + "' state, parsing '%s'" % line)
4590 if md_block["type"] == "markup":
4591 if not md_block["closed"]:
4592 if md_block["start"] in line:
4593 md_block["depth"] += 1
4595 if md_block["end"] in line:
4596 if md_block["depth"] > 0:
4597 md_block["depth"] -= 1
4599 logging.info("closing tag '%s'", line)
4600 md_block["closed"] = 1
4601 # TODO(ensonic): reparse inner text with MarkDownParseLines?
4603 md_block["text"] += "\n" + line
4604 logging.info("add to markup")
4607 deindented_line = line.lstrip()
4609 m = re.search(r'^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
4610 if md_block["type"] == "heading":
4611 # a heading is ended by any level less than or equal
4612 if md_block["level"] == 1:
4613 if re.search(r'^={4,}[ \t]*$', line):
4614 text = md_block["lines"].pop()
4615 md_block["interrupted"] = 0
4616 md_blocks.append(md_block)
4618 md_block = {'type': "heading",
4625 md_block["interrupted"] = 0
4626 md_blocks.append(md_block)
4628 md_block = {'type': "heading",
4636 # push lines into the block until the end is reached
4637 md_block["lines"].append(line)
4641 m2 = re.search(r'^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
4642 if re.search(r'^[=]{4,}[ \t]*$', line):
4643 text = md_block["lines"].pop()
4644 md_block["interrupted"] = 0
4645 md_blocks.append(md_block)
4647 md_block = {'type': "heading",
4653 elif re.search(r'^[-]{4,}[ \t]*$', line):
4654 text = md_block["lines"].pop()
4655 md_block["interrupted"] = 0
4656 md_blocks.append(md_block)
4658 md_block = {'type': "heading",
4665 md_block["interrupted"] = 0
4666 md_blocks.append(md_block)
4668 md_block = {'type': "heading",
4669 'text': m2.group(2),
4672 'level': len(m2.group(1))
4676 # push lines into the block until the end is reached
4677 md_block["lines"].append(line)
4679 elif md_block["type"] == "code":
4680 m3 = re.search(r'^[ \t]*\]\|(.*)', line)
4682 md_blocks.append(md_block)
4683 md_block = {'type': "paragraph",
4688 md_block["lines"].append(line)
4692 if deindented_line == '':
4693 md_block["interrupted"] = 1
4697 if md_block["type"] == "quote":
4698 if not md_block["interrupted"]:
4699 line = re.sub(r'^[ ]*>[ ]?', '', line)
4700 md_block["lines"].append(line)
4703 elif md_block["type"] == "li":
4704 marker = md_block["marker"]
4705 m4 = re.search(r'^([ ]{0,3})($marker)[ ](.*)', line)
4707 indentation = m.group(1)
4708 if md_block["indentation"] != indentation:
4709 md_block["lines"].append(line)
4712 ordered = md_block["ordered"]
4713 lines = re.sub(r'^[ ]{0,4}', '', lines)
4714 md_block["last"] = 0
4715 md_blocks.append(md_block)
4716 md_block = {'type': "li",
4718 'indentation': indentation,
4727 if md_block["interrupted"]:
4728 if first_char == " ":
4729 md_block["lines"].append('')
4730 line = re.sub(r'^[ ]{0,4}', '', line)
4731 md_block["lines"].append(line)
4732 md_block["interrupted"] = 0
4736 line = re.sub(r'^[ ]{0,4}', '', line)
4737 md_block["lines"].append(line)
4740 # indentation sensitive types
4741 logging.info("parsing '%s'", line)
4743 m5 = re.search(r'^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
4744 m6 = re.search(r'^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?', line)
4745 m7 = re.search(r'^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>', line)
4746 m8 = re.search(r'^([ ]*)[*+-][ ](.*)', line)
4747 m9 = re.search(r'^[ ]*>[ ]?(.*)', line)
4750 md_blocks.append(md_block)
4752 md_block = {'type': "heading",
4753 'text': m5.group(2),
4756 'level': len(m5.group(1)),
4760 elif re.search(r'^={4,}[ \t]*$', line):
4761 # setext heading (====)
4763 if md_block["type"] == "paragraph" and md_block["interrupted"]:
4764 md_blocks.append(md_block)
4765 md_block["type"] = "heading"
4766 md_block["lines"] = []
4767 md_block["level"] = 1
4769 elif re.search(r'^-{4,}[ \t]*$/', line):
4770 # setext heading (-----)
4772 if md_block["type"] == "paragraph" and md_block["interrupted"]:
4773 md_blocks.append(md_block)
4774 md_block["type"] = "heading"
4775 md_block["lines"] = []
4776 md_block["level"] = 2
4781 md_block["interrupted"] = 1
4782 md_blocks.append(md_block)
4783 md_block = {'type': "code",
4784 'language': m6.group(1),
4789 # indentation insensitive types
4790 if re.search(r'^[ ]*<!DOCTYPE/', line):
4791 md_blocks.append(md_block)
4793 md_block = {'type' : "markup",
4794 'text' : deindented_line,
4802 # markup, including <?xml version="1.0"?>
4804 is_self_closing = m7.group(2) is not None
4806 # skip link markdown
4807 # TODO(ensonic): consider adding more uri schemes (ftp, ...)
4808 if re.search(r'https?', tag):
4809 logging.info("skipping link '%s'", tag)
4811 # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
4812 # instead of creation a markdown block.
4813 scanning_for_end_of_text_level_tag = (
4814 md_block["type"] == "paragraph" and
4815 'start' in md_block and
4816 not md_block["closed"])
4817 logging.info("markup found '%s', scanning %s ?", tag, scanning_for_end_of_text_level_tag)
4818 if not MD_TEXT_LEVEL_ELEMENTS[tag] and not scanning_for_end_of_text_level_tag:
4819 md_blocks.append(md_block)
4822 logging.info("self-closing docbook '%s'", tag)
4823 md_block = {'type': "self-closing tag",
4824 'text': deindented_line,
4829 logging.info("new markup '%s'", tag)
4830 md_block = {'type' : "markup",
4831 'text' : deindented_line,
4832 'start' : "<" + tag + ">",
4833 'end' : "</" + tag + ">",
4837 if re.search(r'<\/%s>' % tag, deindented_line):
4838 md_block["closed"] = 1
4842 if MD_TEXT_LEVEL_ELEMENTS[tag]:
4843 logging.info("text level docbook '%s' in '%s' state", tag, md_block["type"])
4844 # TODO(ensonic): handle nesting
4845 if scanning_for_end_of_text_level_tag:
4846 if not re.search(r'<\/%s>' % tag, deindented_line):
4847 logging.info("new text level markup '%s'", tag)
4848 md_block["start"] = "<" + tag + ">"
4849 md_block["end"] = "</" + tag + ">"
4850 md_block["closed"] = 0
4851 logging.info("scanning for end of '%s'", tag)
4854 if re.search(md_block["end"], deindented_line):
4855 md_block["closed"] = 1
4856 logging.info("found end of '%s'", tag)
4860 md_blocks.append(md_block)
4862 indentation = m8.group(1)
4863 lines = re.sub(r'^[ ]{0,4}', '', lines)
4864 md_block = {'type': "li",
4866 'indentation': indentation,
4874 md_blocks.append(md_block)
4875 md_block = {'type': "quote",
4876 'lines': [m9.group(1)],
4882 m10 = re.search(r'^([ ]{0,4})\d+[.][ ]+(.*)', line)
4884 md_blocks.append(md_block)
4885 lines = m10.group(2)
4886 indentation = m10.group(1)
4887 lines = re.sub(r'^[ ]{0,4}', '', lines)
4889 md_block = {'type': "li",
4891 'indentation': indentation,
4892 'marker': "\\d+[.]",
4902 if md_block["type"] == "paragraph":
4903 if md_block["interrupted"]:
4904 md_blocks.append(md_block)
4905 md_block = {'type': "paragraph",
4909 logging.info("new paragraph due to interrupted")
4911 md_block["text"] += "\n" + line
4912 logging.info("add to paragraph")
4915 md_blocks.append(md_block)
4916 md_block = {'type': "paragraph",
4919 logging.info("new paragraph due to different block type")
4921 md_blocks.append(md_block)
4923 return md_blocks[1:]
4926 def MarkDownParseSpanElementsInner(text, markersref):
4929 for i in markersref:
4934 # closest_marker_index = 0
4935 closest_marker_position = -1
4941 for marker, use in markers.items():
4946 marker_position = text.find(marker)
4948 if marker_position < 0:
4953 if closest_marker == '' or marker_position < closest_marker_position:
4954 closest_marker = marker
4955 # closest_marker_index = i
4956 closest_marker_position = marker_position
4958 if closest_marker_position >= 0:
4959 text_marker = text[closest_marker_position:]
4962 if text_marker == '':
4968 markup += text[0:closest_marker_position]
4969 text = text[closest_marker_position:]
4971 for key in markers.keys():
4973 if key == closest_marker:
4974 markers_rest.append([])
4976 markers_rest.append(key)
4978 markers_rest.append([])
4980 if closest_marker == "![" or closest_marker == "[":
4983 m = re.search(r'\[((?:[^][]|(?R))*)\]', text)
4984 if ']' in text and m:
4986 element = {"!": text[0] == "!",
4990 offset = len(m.group(0))
4995 remaining_text = text[offset:]
4996 m2 = re.search(r'''^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)''', remaining_text)
4997 m3 = re.search(r'^\s*\[([^\]<]*?)\]', remaining_text)
4999 element["»"] = m2.group(1)
5001 element["#"] = m2.group(2)
5002 offset += len(m2.group(0))
5004 element["ref"] = m3.group(1)
5005 offset += len(m3.group(0))
5011 element["»"] = element["»"].replace('&', '&')
5012 element["»"] = element["»"].replace('<', '<')
5015 markup += '<inlinemediaobject><imageobject><imagedata fileref="' + element['»'] + '"></imagedata></imageobject>'
5018 markup += "<textobject><phrase>" + element["a"] + "</phrase></textobject>"
5021 markup += "</inlinemediaobject>"
5022 elif element["ref"]:
5023 element["a"] = MarkDownParseSpanElementsInner(element["a"], markers_rest)
5024 markup += '<link linkend="' + element['ref'] + '"'
5026 if '#' in element["#"]:
5027 # title attribute not supported
5030 markup += ">" + element["a"] + "</link>"
5032 element["a"] = MarkDownParseSpanElementsInner(element["a"], markers_rest)
5033 markup += '<ulink url="' + element['»'] + '"'
5036 # title attribute not supported
5037 markup += ">" + element["a"] + "</ulink>"
5040 markup += closest_marker
5041 if closest_marker == "![":
5047 elif closest_marker == "<":
5048 m4 = re.search(r'^<(https?:[\/]{2}[^\s]+?)>', text)
5049 m5 = re.search(r'^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>', text)
5050 m6 = re.search(r'^<[^>]+?>', text)
5052 element_url = m4.group(1).replace('&', '&').replace('<', '<')
5054 markup += '<ulink url="' + element_url + '">' + element_url + '</ulink>'
5055 offset = len(m4.group(0))
5057 markup += "<ulink url=\"mailto:" + m5.group(1) + "\">" + m5.group(1) + "</ulink>"
5058 offset = len(m5.group(0))
5060 markup += m6.group(0)
5061 offset = len(m6.group(0))
5066 elif closest_marker == "\\":
5067 special_char = text[1]
5068 if MD_ESCAPABLE_CHARS[special_char] or \
5069 MD_GTK_ESCAPABLE_CHARS[special_char]:
5070 markup += special_char
5076 elif closest_marker == "`":
5077 m7 = re.search(r'^(`+)([^`]+?)\1(?!`)', text)
5079 element_text = m7.group(2)
5080 markup += "<literal>" + element_text + "</literal>"
5081 offset = len(m7.group(0))
5086 elif closest_marker == "@":
5087 # Convert '@param()'
5088 # FIXME: we could make those also links ($symbol.$2), but that would be less
5089 # useful as the link target is a few lines up or down
5090 m7 = re.search(r'^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', text)
5091 m8 = re.search(r'^(\A|[^\\])\@(\w+((\.|->)\w+)*)', text)
5092 m9 = re.search(r'^\\\@', text)
5094 markup += m7.group(1) + "<parameter>" + m7.group(2) + "()</parameter>\n"
5095 offset = len(m7.group(0))
5097 # Convert '@param', but not '\@param'.
5098 markup += m8.group(1) + "<parameter>" + m8.group(2) + "</parameter>\n"
5099 offset = len(m8.group(0))
5102 offset = len(m9.group(0))
5107 elif closest_marker == "#":
5108 m10 = re.search(r'^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', text)
5109 m11 = re.search(r'^(\A|[^\\])#([\w\-:\.]+[\w]+)', text)
5110 m12 = re.search(r'^\\#', text)
5112 # handle #Object.func()
5113 markup += m10.group(1) + MakeXRef(m10.group(2), tagify(m10.group(2) + "()", "function"))
5114 offset = len(m10.group(0))
5116 # Convert '#symbol', but not '\#symbol'.
5117 markup += m11.group(1) + MakeHashXRef(m11.group(2), "type")
5118 offset = len(m11.group(0))
5121 offset = len(m12.group(0))
5126 elif closest_marker == "%":
5127 m12 = re.search(r'^(\A|[^\\])\%(-?\w+)', text)
5128 m13 = re.search(r'^\\%', text)
5130 # Convert '%constant', but not '\%constant'.
5131 # Also allow negative numbers, e.g. %-1.
5132 markup += m12.group(1) + MakeXRef(m12.group(2), tagify(m12.group(2), "literal"))
5133 offset = len(m12.group(0))
5136 offset = len(m13.group(0))
5144 text = text[offset:]
5149 def MarkDownParseSpanElements(text):
5150 markers = ["\\", "<", "![", "[", "`", "%", "#", "@"]
5152 text = MarkDownParseSpanElementsInner(text, markers)
5154 # Convert 'function()' or 'macro()'.
5155 # if there is abc_*_def() we don't want to make a link to _def()
5156 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
5158 return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function")) + ';'
5159 text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f, text)
5163 def ReplaceEntities(text, symbol):
5164 entities = [["<", "<"],
5173 ["&", "&"], # Do this last, or the others get messed up.
5177 # Expand entities in <programlisting> even inside CDATA since
5178 # we changed the definition of |[ to add CDATA
5180 text = re.sub(i[0], i[1], text)
5184 def MarkDownOutputDocBook(blocksref, symbol, context):
5188 for block in blocks:
5189 #$output += "\n<!-- beg type='" . $block->{"type"} . "'-->\n"
5191 if block["type"] == "paragraph":
5192 text = MarkDownParseSpanElements(block["text"])
5193 if context == "li" and output == '':
5194 if block["interrupted"]:
5195 output += "\n<para>%s</para>\n" % text
5197 output += "<para>%s</para>" % text
5201 output += "<para>%s</para>\n" % text
5203 elif block["type"] == "heading":
5205 title = MarkDownParseSpanElements(block["text"])
5207 if block["level"] == 1:
5213 text = MarkDownParseLines(block["lines"], symbol, "heading")
5215 output += "<%s id=\"%s\">" % (tag, block["id"])
5217 output += "<%s>" % tag
5220 output += "<title>%s</title>%s</%s>\n" % (title, text, tag)
5221 elif block["type"] == "li":
5222 tag = "itemizedlist"
5225 if block["ordered"]:
5228 output += "<%s>\n" % tag
5231 if block["interrupted"]:
5232 block["lines"].append('')
5235 text = MarkDownParseLines(block["lines"], symbol, "li")
5236 output += "<listitem>" + text + "</listitem>\n"
5238 if block["ordered"]:
5240 output += "</$tag>\n"
5242 elif block["type"] == "quote":
5243 text = MarkDownParseLines(block["lines"], symbol, "quote")
5244 output += "<blockquote>\n%s</blockquote>\n" % text
5245 elif block["type"] == "code":
5246 tag = "programlisting"
5248 if block["language"]:
5249 if block["language"] == "plain":
5250 output += "<informalexample><screen><![CDATA[\n"
5253 output += '<informalexample><programlisting language="' + block['language'] + '"><![CDATA[\n'
5256 output += "<informalexample><programlisting><![CDATA[\n"
5258 for line in block["lines"]:
5259 output += ReplaceEntities(line, symbol) + "\n"
5261 output += "]]></$tag></informalexample>\n"
5262 elif block["type"] == "markup":
5263 text = ExpandAbbreviations(symbol, block["text"])
5266 output += block["text"]+"\n"
5268 #$output += "\n<!-- end type='" . $block->{"type"} . "'-->\n"
5273 def MarkDownParseLines(linesref, symbol, context):
5276 blocks = MarkDownParseBlocks(lines, symbol, context)
5277 output = MarkDownOutputDocBook(blocks, symbol, context)
5282 def MarkDownParse(text, symbol):
5283 # take out some variability in line endings
5284 text = re.sub(r'\r\n', '\n', text)
5285 text = re.sub(r'\r', '\n', text)
5288 lines = text.split('\n')
5289 text = MarkDownParseLines(lines, symbol, '')
5294 #############################################################################
5295 # Function : ReadDeclarationsFile
5296 # Description : This reads in a file containing the function/macro/enum etc.
5299 # Note that in some cases there are several declarations with
5300 # the same name, e.g. for conditional macros. In this case we
5301 # set a flag in the %DeclarationConditional hash so the
5302 # declaration is not shown in the docs.
5304 # If a macro and a function have the same name, e.g. for
5305 # gtk_object_ref, the function declaration takes precedence.
5307 # Some opaque structs are just declared with 'typedef struct
5308 # _name name;' in which case the declaration may be empty.
5309 # The structure may have been found later in the header, so
5310 # that overrides the empty declaration.
5312 # Arguments : $file - the declarations file to read
5313 # $override - if declarations in this file should override
5314 # any current declaration.
5315 #############################################################################
5317 def ReadDeclarationsFile(ifile, override):
5320 global Declarations, DeclarationTypes, DeclarationConditional, DeclarationOutput
5322 DeclarationTypes = {}
5323 DeclarationConditional = {}
5324 DeclarationOutput = {}
5328 declaration_type = ''
5329 declaration_name = None
5335 if not declaration_type:
5336 m1 = re.search(r'^<([^>]+)>', line)
5338 declaration_type = m1.group(1)
5339 declaration_name = ''
5340 logging.info("Found declaration: %s", declaration_type)
5344 m2 = re.search(r'^<NAME>(.*)</NAME>', line)
5345 m3 = re.search(r'^<DEPRECATED/>', line)
5346 m4 = re.search(r'^</$declaration_type>', line)
5348 declaration_name = m2.group(1)
5350 is_deprecated = True
5352 logging.info("Found end of declaration: %s", declaration_name)
5353 # Check that the declaration has a name
5354 if declaration_name == '':
5355 common.LogWarning(ifile, line_number, declaration_type + " has no name.\n")
5358 # If the declaration is an empty typedef struct _XXX XXX
5359 # set the flag to indicate the struct has a typedef.
5360 if declaration_type == 'STRUCT' or declaration_type == 'UNION' \
5361 and re.search(r'^\s*$', declaration):
5362 logging.info("Struct has typedef: " + declaration_name)
5363 StructHasTypedef[declaration_name] = 1
5366 # Check if the symbol is already defined.
5367 if declaration_name in Declarations and override == 0:
5368 # Function declarations take precedence.
5369 if DeclarationTypes[declaration_name] == 'FUNCTION':
5372 elif declaration_type == 'FUNCTION':
5374 Deprecated[declaration_name] = ''
5376 Declarations[declaration_name] = declaration
5377 DeclarationTypes[declaration_name] = declaration_type
5378 elif DeclarationTypes[declaration_name] == declaration_type:
5379 # If the existing declaration is empty, or is just a
5380 # forward declaration of a struct, override it.
5381 if declaration_type == 'STRUCT' or declaration_type == 'UNION':
5382 if re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', Declarations[declaration_name]):
5384 Deprecated[declaration_name] = ''
5385 Declarations[declaration_name] = declaration
5386 elif re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', declaration):
5387 # Ignore an empty or forward declaration.
5390 common.LogWarning(ifile, line_number, "Structure %s has multiple definitions." % declaration_name)
5393 # set flag in %DeclarationConditional hash for
5394 # multiply defined macros/typedefs.
5395 DeclarationConditional[declaration_name] = 1
5398 common.LogWarning(ifile, line_number, declaration_name + " has multiple definitions.")
5402 Deprecated[declaration_name] = ''
5404 Declarations[declaration_name] = declaration
5405 DeclarationTypes[declaration_name] = declaration_type
5408 declaration_type = ''
5409 is_deprecated = False
5415 #############################################################################
5416 # Function : ReadSignalsFile
5417 # Description : This reads in an existing file which contains information on
5418 # all GTK signals. It creates the arrays @SignalNames and
5419 # @SignalPrototypes containing info on the signals. The first
5420 # line of the SignalPrototype is the return type of the signal
5421 # handler. The remaining lines are the parameters passed to it.
5422 # The last parameter, "gpointer user_data" is always the same
5423 # so is not included.
5424 # Arguments : $file - the file containing the signal handler prototype
5426 #############################################################################
5428 def ReadSignalsFile(ifile):
5431 signal_object = None
5433 signal_returns = None
5435 signal_prototype = None
5437 # Reset the signal info.
5438 global SignalObjects, SignalNames, SignalReturns, SignalFlags, SignalPrototypes
5443 SignalPrototypes = []
5445 if not os.path.isfile(ifile):
5453 if re.search(r'^<SIGNAL>', line):
5458 signal_prototype = ''
5461 m = re.search(r'^<NAME>(.*)<\/NAME>', line)
5462 m2 = re.search(r'^<RETURNS>(.*)<\/RETURNS>', line)
5463 m3 = re.search(r'^<FLAGS>(.*)<\/FLAGS>', line)
5465 signal_name = m.group(1)
5466 m1_2 = re.search(r'^(.*)::(.*)$', signal_name)
5468 signal_object = m1_2.group(1)
5469 signal_name = m1_2.group(2).replace('_', '-')
5470 logging.info("Found signal: %s", signal_name)
5472 common.LogWarning(ifile, line_number, "Invalid signal name: %s." % signal_name)
5475 signal_returns = m2.group(1)
5477 signal_flags = m3.group(1)
5478 elif re.search(r'^</SIGNAL>', line):
5479 logging.info("Found end of signal: %s::%s\nReturns: %s\n%s", signal_object, signal_name, signal_returns, signal_prototype)
5480 SignalObjects.append(signal_object)
5481 SignalNames.append(signal_name)
5482 SignalReturns.append(signal_returns)
5483 SignalFlags.append(signal_flags)
5484 SignalPrototypes.append(signal_prototype)
5487 signal_prototype += line
5492 #############################################################################
5493 # Function : ReadTemplateFile
5494 # Description : This reads in the manually-edited documentation file
5495 # corresponding to the file currently being created, so we can
5496 # insert the documentation at the appropriate places.
5497 # It outputs %SymbolTypes, %SymbolDocs and %SymbolParams, which
5498 # is a hash of arrays.
5499 # Arguments : $docsfile - the template file to read in.
5500 # $skip_unused_params - 1 if the unused parameters should be
5502 #############################################################################
5504 def ReadTemplateFile(docsfile, skip_unused_params):
5506 template = docsfile + ".sgml"
5507 if not os.path.isfile(template):
5508 logging.info("File doesn't exist: " + template)
5512 # start with empty hashes, we merge the source comment for each file
5514 global SymbolDocs, SymbolTypes, SymbolParams
5519 current_type = '' # Type of symbol being read.
5520 current_symbol = '' # Name of symbol being read.
5521 symbol_doc = '' # Description of symbol being read.
5522 params = [] # Parameter names and descriptions of current
5523 # function/macro/function typedef.
5524 current_param = -1 # Index of parameter currently being read.
5525 # Note that the param array contains pairs
5526 # of param name & description.
5527 in_unused_params = 0 # True if we are reading in the unused params.
5532 DOCS = open(template)
5534 logging.info("reading template " + template)
5539 m1 = re.search(r'^<!-- ##### ([A-Z_]+) (\S+) ##### -->', line)
5542 symbol = m1.group(2)
5543 if symbol == "Title" \
5544 or symbol == "Short_Description" \
5545 or symbol == "Long_Description" \
5546 or symbol == "See_Also" \
5547 or symbol == "Stability_Level" \
5548 or symbol == "Include" \
5549 or symbol == "Image":
5551 symbol = docsfile + ":" + symbol
5554 logging.info("Found symbol: " + symbol)
5555 # Remember file and line for the symbol
5556 SymbolSourceFile[symbol] = template
5557 SymbolSourceLine[symbol] = line_number
5559 # Store previous symbol, but remove any trailing blank lines.
5560 if current_symbol != '':
5561 symbol_doc = symbol_doc.rstrip()
5562 SymbolTypes[current_symbol] = current_type
5563 SymbolDocs[current_symbol] = symbol_doc
5565 # Check that the stability level is valid.
5566 if StabilityLevel[current_symbol]:
5567 StabilityLevel[current_symbol] = ParseStabilityLevel(StabilityLevel[current_symbol], template, line_number, "Stability level for " + current_symbol)
5569 if current_param >= 0:
5570 SymbolParams[current_symbol] = [params]
5572 # Delete any existing params in case we are overriding a
5573 # previously read template.
5574 del SymbolParams[current_symbol]
5577 current_type = stype
5578 current_symbol = symbol
5580 in_unused_params = 0
5587 elif re.search(r'^<!-- # Unused Parameters # -->', line):
5588 logging.info("Found unused parameters\n")
5589 in_unused_params = True
5592 elif in_unused_params and skip_unused_params:
5593 # When outputting the DocBook we skip unused parameters.
5594 logging.info("Skipping unused param: " + line)
5598 # Check if param found. Need to handle "..." and "format...".
5599 m2 = re.search(r'^\@([\w\.]+):\040?', line)
5601 line = re.sub(r'^\@([\w\.]+):\040?', '', line)
5602 param_name = m2.group(1)
5604 # Allow variations of 'Returns'
5605 if re.search(r'^[Rr]eturns?$', param_name):
5606 param_name = "Returns"
5608 # Allow varargs variations
5609 if re.search(r'^.*\.\.\.$', param_name):
5613 # strip trailing whitespaces and blank lines
5614 line = re.sub(r'\s+\n$', '\n', line, flags=re.M)
5615 line = re.sub(r'\n+$', '\n', line, flags=re.M|re.S)
5616 logging.info("Found param for symbol %s : '%s'= '%s'", current_symbol, param_name, line)
5618 if param_name == "Deprecated":
5619 in_deprecated = True
5620 Deprecated[current_symbol] = line
5621 elif param_name == "Since":
5623 Since[current_symbol] = line.strip()
5624 elif param_name == "Stability":
5626 StabilityLevel[current_symbol] = line
5628 params.append(param_name)
5629 params.append(param_desc)
5630 current_param += PARAM_FIELD_COUNT
5633 # strip trailing whitespaces and blank lines
5634 line = re.sub(r'\s+\n$', '\n', line, flags=re.M)
5635 line = re.sub(r'\n+$', '\n', line, flags=re.M|re.S)
5637 if re.search(r'^\s+$', line):
5639 Deprecated[current_symbol] += line
5641 common.LogWarning(template, line_number, "multi-line since docs found")
5642 #$Since{$current_symbol} += $_
5644 StabilityLevel[current_symbol] += line
5645 elif current_param >= 0:
5646 params[current_param] += line
5650 # Remember to finish the current symbol doccs.
5651 if current_symbol != '':
5653 symbol_doc = re.sub(r'\s+$', '', symbol_doc)
5654 SymbolTypes[current_symbol] = current_type
5655 SymbolDocs[current_symbol] = symbol_doc
5657 # Check that the stability level is valid.
5658 if StabilityLevel[current_symbol]:
5659 StabilityLevel[current_symbol] = ParseStabilityLevel(StabilityLevel[current_symbol], template, line_number, "Stability level for " + current_symbol)
5661 if current_param >= 0:
5662 SymbolParams[current_symbol] = [params]
5664 # Delete any existing params in case we are overriding a
5665 # previously read template.
5666 del SymbolParams[current_symbol]
5673 #############################################################################
5674 # Function : ReadObjectHierarchy
5675 # Description : This reads in the $MODULE-hierarchy.txt file containing all
5676 # the GtkObject subclasses described in this module (and their
5678 # It places them in the @Objects array, and places their level
5679 # in the object hierarchy in the @ObjectLevels array, at the
5680 # same index. GtkObject, the root object, has a level of 1.
5682 # This also generates tree_index.sgml as it goes along.
5685 #############################################################################
5687 def ReadObjectHierarchy():
5689 global Objects, ObjectLevels
5693 if not os.path.isfile(OBJECT_TREE_FILE):
5696 INPUT = open(OBJECT_TREE_FILE)
5698 # Only emit objects if they are supposed to be documented, or if
5699 # they have documented children. To implement this, we maintain a
5700 # stack of pending objects which will be emitted if a documented
5702 pending_objects = []
5707 m1 = re.search(r'\S+', line)
5709 gobject = m1.group(0)
5710 level = len(line[0:m1.start()]) / 2 + 1
5717 while pending_levels and pending_levels[-1] >= level:
5718 pending_objects.pop()
5719 pending_levels.pop()
5722 pending_objects.append(gobject)
5723 pending_levels.append(level)
5725 if gobject in KnownSymbols:
5726 while len(pending_levels) > 0:
5727 gobject = pending_objects[0]
5728 pending_objects = pending_objects[1:]
5729 level = pending_levels[0]
5730 pending_levels = pending_levels[1:]
5731 xref = MakeXRef(gobject)
5733 tree.append(' ' * (level * 4) + xref)
5734 Objects.append(gobject)
5735 ObjectLevels.append(level)
5736 ObjectRoots[gobject] = root
5740 # common.LogWarning($OBJECT_TREE_FILE, line_number, "unknown type $object")
5747 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml"
5748 old_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.sgml")
5749 new_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.new")
5751 OUTPUT = open(new_tree_index, 'w')
5753 OUTPUT.write(MakeDocHeader("screen") + "\n<screen>\n" + AddTreeLineArt(tree) + "\n</screen>\n")
5756 common.UpdateFileIfChanged(old_tree_index, new_tree_index, 0)
5761 #############################################################################
5762 # Function : ReadInterfaces
5763 # Description : This reads in the $MODULE.interfaces file.
5766 #############################################################################
5768 def ReadInterfaces():
5772 if not os.path.isfile(INTERFACES_FILE):
5775 INPUT = open(INTERFACES_FILE)
5779 (gobject, ifaces) = line.split()
5780 if gobject in KnownSymbols and KnownSymbols[gobject] == 1:
5783 # filter out private interfaces, but leave foreign interfaces
5784 for iface in ifaces:
5785 if iface not in KnownSymbols or KnownSymbols[iface] == 1:
5786 knownIfaces.append(iface)
5790 Interfaces[gobject] = ' '.join(knownIfaces)
5791 logging.info("Interfaces for %s: %s\n", gobject, Interfaces[gobject])
5793 logging.info("skipping interfaces for unknown symbol: %s", gobject)
5798 #############################################################################
5799 # Function : ReadPrerequisites
5800 # Description : This reads in the $MODULE.prerequisites file.
5803 #############################################################################
5805 def ReadPrerequisites():
5806 global Prerequisites
5809 if not os.path.isfile(PREREQUISITES_FILE):
5812 INPUT = open(PREREQUISITES_FILE)
5816 (iface, prereqs) = line.split()
5817 if iface in KnownSymbols and KnownSymbols[iface] == 1:
5820 # filter out private prerequisites, but leave foreign prerequisites
5821 for prereq in prereqs:
5822 if prereq not in KnownSymbols or KnownSymbols[prereq] == 1:
5823 knownPrereqs.append(prereq)
5825 Prerequisites[iface] = ' '.join(knownPrereqs)
5830 #############################################################################
5831 # Function : ReadArgsFile
5832 # Description : This reads in an existing file which contains information on
5833 # all GTK args. It creates the arrays @ArgObjects, @ArgNames,
5834 # @ArgTypes, @ArgFlags, @ArgNicks and @ArgBlurbs containing info
5836 # Arguments : $file - the file containing the arg information.
5837 #############################################################################
5839 def ReadArgsFile(ifile):
5850 # Reset the args info.
5851 global ArgObjects, ArgNames, ArgTypes, ArgFlags, ArgNicks, ArgBlurbs, ArgDefaults, ArgRanges
5861 if not os.path.isfile(ifile):
5869 if re.search(r'^<ARG>', line):
5881 m1 = re.search(r'^<NAME>(.*)<\/NAME>', line)
5882 m2 = re.search(r'^<TYPE>(.*)<\/TYPE>', line)
5883 m3 = re.search(r'^<RANGE>(.*)<\/RANGE>', line)
5884 m4 = re.search(r'^<FLAGS>(.*)<\/FLAGS>', line)
5885 m5 = re.search(r'^<NICK>(.*)<\/NICK>', line)
5886 m6 = re.search(r'^<BLURB>(.*)<\/BLURB>', line)
5887 m7 = re.search(r'^<DEFAULT>(.*)<\/DEFAULT>', line)
5889 arg_name = m1.group(1)
5890 m1_1 = re.search(r'^(.*)::(.*)$', arg_name)
5892 arg_object = m1_1.group(1)
5893 arg_name = m1_1.group(2).replace('_', '-')
5894 logging.info("Found arg: %s", arg_name)
5896 common.LogWarning(ifile, line_number, "Invalid argument name: " + arg_name)
5899 arg_type = m2.group(1)
5901 arg_range = m3.group(1)
5903 arg_flags = m4.group(1)
5905 arg_nick = m5.group(1)
5907 arg_blurb = m6.group(1)
5908 if arg_blurb == "(null)":
5910 common.LogWarning(ifile, line_number, "Property %s:%s has no documentation." % (arg_object, arg_name))
5913 arg_default = m7.group(1)
5914 elif re.search(r'^</ARG>', line):
5915 logging.info("Found end of arg: %s::%s\n%s : %s\n", arg_object, arg_name, arg_type, arg_flags)
5916 ArgObjects.append(arg_object)
5917 ArgNames.append(arg_name)
5918 ArgTypes.append(arg_type)
5919 ArgRanges.append(arg_range)
5920 ArgFlags.append(arg_flags)
5921 ArgNicks.append(arg_nick)
5922 ArgBlurbs.append(arg_blurb)
5923 ArgDefaults.append(arg_default)
5929 #############################################################################
5930 # Function : AddTreeLineArt
5931 # Description : Add unicode lineart to a pre-indented string array and returns
5932 # it as as multiline string.
5933 # Arguments : @tree - array of indented strings.
5934 #############################################################################
5936 def AddTreeLineArt(tree):
5937 # iterate bottom up over the tree
5938 for i in range(len(tree) - 1, -1, -1):
5939 # count leading spaces
5940 m = re.search(r'^([^<A-Za-z]*)', tree[i])
5941 tree[i] = m.group(0)
5942 indent = len(m.group(1))
5943 # replace with ╰───, if place of ╰ is not space insert ├
5945 if tree[i][indent-4] == " ":
5946 tree[i] = tree[i][:indent-4] + "--- " + tree[i][indent:]
5948 tree[i] = tree[i][:indent-4] + "+-- " + tree[i][indent:]
5950 # go lines up while space and insert |
5952 while j >= 0 and tree[j][indent-4] == ' ':
5953 tree[j][indent] = '|'
5957 res = "\n".join(tree)
5958 # unicode chars for: ╰──
5959 res = re.sub(r'---', '<phrase role=\"lineart\">╰──</phrase>', res)
5960 # unicde chars for: ├──
5961 res = re.sub(r'\+--', '<phrase role=\"lineart\">├──</phrase>', res)
5962 # unicode char for: │
5963 res = re.sub(r'\|', '<phrase role=\"lineart\">│</phrase>', res)
5969 #############################################################################
5970 # Function : CheckIsObject
5971 # Description : Returns 1 if the given name is a GObject or a subclass.
5972 # It uses the global @Objects array.
5973 # Note that the @Objects array only contains classes in the
5974 # current module and their ancestors - not all GObject classes.
5975 # Arguments : $name - the name to check.
5976 #############################################################################
5978 def CheckIsObject(name):
5979 root = ObjectRoots[name]
5980 # Let GBoxed pass as an object here to get -struct appended to the id
5981 # and prevent conflicts with sections.
5982 return root and root != 'GEnum' and root != 'GFlags'
5986 #############################################################################
5987 # Function : MakeReturnField
5988 # Description : Pads a string to $RETURN_TYPE_FIELD_WIDTH.
5989 # Arguments : $str - the string to pad.
5990 #############################################################################
5992 def MakeReturnField(s):
5994 return str + (' ' * (RETURN_TYPE_FIELD_WIDTH - len(s)))
5997 #############################################################################
5998 # Function : GetSymbolSourceFile
5999 # Description : Get the filename where the symbol docs where taken from.
6000 # Arguments : $symbol - the symbol name
6001 #############################################################################
6003 def GetSymbolSourceFile(symbol):
6004 if symbol in SourceSymbolSourceFile:
6005 return SourceSymbolSourceFile[symbol]
6006 if symbol in SymbolSourceFile:
6007 return SymbolSourceFile[symbol]
6010 #############################################################################
6011 # Function : GetSymbolSourceLine
6012 # Description : Get the file line where the symbol docs where taken from.
6013 # Arguments : $symbol - the symbol name
6014 #############################################################################
6016 def GetSymbolSourceLine(symbol):
6017 if symbol in SourceSymbolSourceLine:
6018 return SourceSymbolSourceLine[symbol]
6019 if symbol in SymbolSourceLine[symbol]:
6020 return SymbolSourceLine[symbol]
6024 if __name__ == '__main__':