1 # -*- python; coding: utf-8 -*-
3 # gtk-doc - GTK DocBook documentation generator.
4 # Copyright (C) 1998 Damon Chaplin
5 # 2007-2016 Stefan Sauer
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 Creates the DocBook files from the source comments.
26 from __future__
import print_function
27 from six
import iteritems
, iterkeys
29 from collections
import OrderedDict
35 from . import common
, md_to_db
40 INLINE_MARKUP_MODE
= None
41 DEFAULT_STABILITY
= None
45 # These global arrays store information on signals. Each signal has an entry
46 # in each of these arrays at the same index, like a multi-dimensional array.
47 SignalObjects
= [] # The GtkObject which emits the signal.
48 SignalNames
= [] # The signal name.
49 SignalReturns
= [] # The return type.
50 SignalFlags
= [] # Flags for the signal
51 SignalPrototypes
= [] # The rest of the prototype of the signal handler.
53 # These global arrays store information on Args. Each Arg has an entry
54 # in each of these arrays at the same index, like a multi-dimensional array.
55 ArgObjects
= [] # The GtkObject which has the Arg.
56 ArgNames
= [] # The Arg name.
57 ArgTypes
= [] # The Arg type - gint, GtkArrowType etc.
58 ArgFlags
= [] # How the Arg can be used - readable/writable etc.
59 ArgNicks
= [] # The nickname of the Arg.
60 ArgBlurbs
= [] # Docstring of the Arg.
61 ArgDefaults
= [] # Default value of the Arg.
62 ArgRanges
= [] # The range of the Arg type
64 # These global hashes store declaration info keyed on a symbol name.
67 DeclarationConditional
= {}
68 DeclarationOutput
= {}
74 # These global hashes store the existing documentation.
77 SymbolAnnotations
= {}
79 # These global hashes store documentation scanned from the source files.
81 SourceSymbolParams
= {}
82 SourceSymbolSourceFile
= {}
83 SourceSymbolSourceLine
= {}
85 # all documentation goes in here, so we can do coverage analysis
87 AllIncompleteSymbols
= {}
89 AllDocumentedSymbols
= {}
91 # Undeclared yet documented symbols
92 UndeclaredSymbols
= {}
94 # These global arrays store GObject, subclasses and the hierarchy (also of
95 # non-object derived types).
103 # holds the symbols which are mentioned in <MODULE>-sections.txt and in which
104 # section they are defined
109 # collects index entries
110 IndexEntriesFull
= {}
111 IndexEntriesSince
= {}
112 IndexEntriesDeprecated
= {}
114 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
115 PreProcessorDirectives
= {
116 'assert', 'define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef',
117 'include', 'line', 'pragma', 'unassert', 'undef', 'warning'
120 # remember used annotation (to write minimal glossary)
123 # the regexp that parses the annotation is in ScanSourceFile()
124 AnnotationDefinition
= {
125 # the GObjectIntrospection annotations are defined at:
126 # https://live.gnome.org/GObjectIntrospection/Annotations
127 'allow-none': "NULL is OK, both for passing and for returning.",
128 'nullable': "NULL may be passed as the value in, out, in-out; or as a return value.",
129 'not nullable': "NULL must not be passed as the value in, out, in-out; or as a return value.",
130 'optional': "NULL may be passed instead of a pointer to a location.",
131 'not optional': "NULL must not be passed as the pointer to a location.",
132 'array': "Parameter points to an array of items.",
133 'attribute': "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
134 'attributes': "Free-form key-value pairs.",
135 'closure': "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
136 'constructor': "This symbol is a constructor, not a static method.",
137 'destroy': "This parameter is a 'destroy_data', for callbacks.",
138 'default': "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
139 'element-type': "Generics and defining elements of containers and arrays.",
140 'error-domains': "Typed errors. Similar to throws in Java.",
141 'foreign': "This is a foreign struct.",
142 'get-value-func': "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
143 'in': "Parameter for input. Default is <acronym>transfer none</acronym>.",
144 'inout': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
145 'in-out': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
146 'method': "This is a method",
147 'not-error': "A GError parameter is not to be handled like a normal GError.",
148 'out': "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
149 'out caller-allocates': "Out parameter, where caller must allocate storage.",
150 'out callee-allocates': "Out parameter, where caller must allocate storage.",
151 'ref-func': "The specified function is used to ref a struct, must be a GTypeInstance.",
152 'rename-to': "Rename the original symbol's name to SYMBOL.",
153 'scope call': "The callback is valid only during the call to the method.",
154 'scope async': "The callback is valid until first called.",
155 'scope notified': "The callback is valid until the GDestroyNotify argument is called.",
156 'set-value-func': "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
157 'skip': "Exposed in C code, not necessarily available in other languages.",
158 'transfer container': "Free data container after the code is done.",
159 'transfer floating': "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
160 'transfer full': "Free data after the code is done.",
161 'transfer none': "Don't free data after the code is done.",
162 'type': "Override the parsed C type with given type.",
163 'unref-func': "The specified function is used to unref a struct, must be a GTypeInstance.",
164 'virtual': "This is the invoker for a virtual method.",
165 'value': "The specified value overrides the evaluated value of the constant.",
166 # Stability Level definition
167 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
168 'Stable': '''The intention of a Stable interface is to enable arbitrary third parties to
169 develop applications to these interfaces, release them, and have confidence that
170 they will run on all minor releases of the product (after the one in which the
171 interface was introduced, and within the same major release). Even at a major
172 release, incompatible changes are expected to be rare, and to have strong
175 'Unstable': '''Unstable interfaces are experimental or transitional. They are typically used to
176 give outside developers early access to new or rapidly changing technology, or
177 to provide an interim solution to a problem where a more general solution is
178 anticipated. No claims are made about either source or binary compatibility from
179 one minor release to the next.
181 The Unstable interface level is a warning that these interfaces are subject to
182 change without warning and should not be used in unbundled products.
184 Given such caveats, customer impact need not be a factor when considering
185 incompatible changes to an Unstable interface in a major or minor release.
186 Nonetheless, when such changes are introduced, the changes should still be
187 mentioned in the release notes for the affected release.
189 'Private': '''An interface that can be used within the GNOME stack itself, but that is not
190 documented for end-users. Such functions should only be used in specified and
195 # Function and other declaration output settings.
196 RETURN_TYPE_FIELD_WIDTH
= 20
197 MAX_SYMBOL_FIELD_WIDTH
= 40
200 doctype_header
= None
203 REFENTRY
= string
.Template('''${header}
204 <refentry id="${section_id}">
206 <refentrytitle role="top_of_page" id="${section_id}.top_of_page">${title}</refentrytitle>
207 <manvolnum>3</manvolnum>
208 <refmiscinfo>${MODULE} Library${image}</refmiscinfo>
211 <refname>${title}</refname>
212 <refpurpose>${short_desc}</refpurpose>
215 ${functions_synop}${args_synop}${signals_synop}${object_anchors}${other_synop}${hierarchy}${prerequisites}${derived}${interfaces}${implementations}
217 <refsect1 id="${section_id}.description" role="desc">
218 <title role="desc.title">Description</title>
219 ${extralinks}${long_desc}
221 <refsect1 id="${section_id}.functions_details" role="details">
222 <title role="details.title">Functions</title>
225 <refsect1 id="${section_id}.other_details" role="details">
226 <title role="details.title">Types and Values</title>
229 ${args_desc}${signals_desc}${see_also}
235 global MODULE
, INLINE_MARKUP_MODE
, DEFAULT_STABILITY
, NAME_SPACE
, \
236 DB_OUTPUT_DIR
, doctype_header
238 # We should pass the options variable around instead of this global variable horror
239 # but too much of the code expects these to be around. Fix this once the transition is done.
240 MODULE
= options
.module
241 INLINE_MARKUP_MODE
= options
.xml_mode
or options
.sgml_mode
242 DEFAULT_STABILITY
= options
.default_stability
243 NAME_SPACE
= options
.name_space
245 main_sgml_file
= options
.main_sgml_file
246 if not main_sgml_file
:
247 # backwards compatibility
248 if os
.path
.exists(MODULE
+ "-docs.sgml"):
249 main_sgml_file
= MODULE
+ "-docs.sgml"
251 main_sgml_file
= MODULE
+ "-docs.xml"
253 # extract docbook header or define default
254 doctype_header
= GetDocbookHeader(main_sgml_file
)
256 # This is where we put all the DocBook output.
257 DB_OUTPUT_DIR
= DB_OUTPUT_DIR
if DB_OUTPUT_DIR
else os
.path
.join(ROOT_DIR
, "xml")
258 if not os
.path
.isdir(DB_OUTPUT_DIR
):
259 os
.mkdir(DB_OUTPUT_DIR
)
261 ReadKnownSymbols(os
.path
.join(ROOT_DIR
, MODULE
+ "-sections.txt"))
262 ReadSignalsFile(os
.path
.join(ROOT_DIR
, MODULE
+ ".signals"))
263 ReadArgsFile(os
.path
.join(ROOT_DIR
, MODULE
+ ".args"))
264 ReadObjectHierarchy(os
.path
.join(ROOT_DIR
, MODULE
+ ".hierarchy"))
265 ReadInterfaces(os
.path
.join(ROOT_DIR
, MODULE
+ ".interfaces"))
266 ReadPrerequisites(os
.path
.join(ROOT_DIR
, MODULE
+ ".prerequisites"))
268 ReadDeclarationsFile(os
.path
.join(ROOT_DIR
, MODULE
+ "-decl.txt"), 0)
269 if os
.path
.isfile(os
.path
.join(ROOT_DIR
, MODULE
+ "-overrides.txt")):
270 ReadDeclarationsFile(os
.path
.join(ROOT_DIR
, MODULE
+ "-overrides.txt"), 1)
273 if options
.source_suffixes
:
274 suffix_list
= ['.' + ext
for ext
in options
.source_suffixes
.split(',')]
276 suffix_list
= ['.c', '.h']
278 source_dirs
= options
.source_dir
279 ignore_files
= options
.ignore_files
280 logging
.info(" ignore files: " + ignore_files
)
281 for sdir
in source_dirs
:
282 ReadSourceDocumentation(sdir
, suffix_list
, source_dirs
, ignore_files
)
284 changed
, book_top
, book_bottom
= OutputDB(os
.path
.join(ROOT_DIR
, MODULE
+ "-sections.txt"), options
)
285 OutputBook(main_sgml_file
, book_top
, book_bottom
)
287 logging
.info("All files created: %d", changed
)
289 # If any of the DocBook files have changed, update the timestamp file (so
290 # it can be used for Makefile dependencies).
291 if changed
or not os
.path
.exists(os
.path
.join(ROOT_DIR
, "sgml.stamp")):
293 # try to detect the common prefix
294 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
296 NAME_SPACE
= DetermineNamespace()
298 logging
.info('namespace prefix ="%s"', NAME_SPACE
)
300 OutputIndex("api-index-full", IndexEntriesFull
)
301 OutputIndex("api-index-deprecated", IndexEntriesDeprecated
)
303 OutputAnnotationGlossary()
305 with
open(os
.path
.join(ROOT_DIR
, 'sgml.stamp'), 'w') as h
:
309 def OutputObjectList():
310 """This outputs the alphabetical list of objects, in a columned table."""
311 # FIXME: Currently this also outputs ancestor objects which may not actually
316 old_object_index
= os
.path
.join(DB_OUTPUT_DIR
, "object_index.sgml")
317 new_object_index
= os
.path
.join(DB_OUTPUT_DIR
, "object_index.new")
319 OUTPUT
= common
.open_text(new_object_index
, 'w')
322 <informaltable pgwide="1" frame="none">
324 <colspec colwidth="1*"/>
325 <colspec colwidth="1*"/>
326 <colspec colwidth="1*"/>
328 ''' % (MakeDocHeader("informaltable"), cols
))
332 for object in sorted(Objects
):
333 xref
= MakeXRef(object)
334 if count
% cols
== 0:
335 OUTPUT
.write("<row>\n")
336 OUTPUT
.write("<entry>%s</entry>\n" % xref
)
337 if count
% cols
== cols
- 1:
338 OUTPUT
.write("</row>\n")
342 # emit an empty row, since empty tables are invalid
343 OUTPUT
.write("<row><entry> </entry></row>\n")
347 OUTPUT
.write("</row>\n")
349 OUTPUT
.write('''</tbody></tgroup></informaltable>\n''')
352 common
.UpdateFileIfChanged(old_object_index
, new_object_index
, 0)
355 def TrimTextBlock(desc
):
356 """Trims extra whitespace.
358 Empty lines inside a block are preserved.
360 desc (str): the text block to trim. May contain newlines.
363 # strip trailing spaces on every line
364 return re
.sub(r
'\s+$', '\n', desc
.lstrip(), flags
=re
.MULTILINE
)
367 def OutputDB(file, options
):
368 """Generate docbook files.
370 This collects the output for each section of the docs, and outputs each file
371 when the end of the section is found.
374 file (str): the $MODULE-sections.txt file which contains all of the
375 functions/macros/structs etc. being documented, organised
376 into sections and subsections.
377 options: commandline options
380 logging
.info("Reading: %s", file)
381 INPUT
= common
.open_text(file)
385 includes
= options
.default_includes
or ''
386 section_includes
= ''
395 functions_details
= ''
400 child_args_synop
= ''
401 style_args_synop
= ''
415 MergeSourceDocumentation()
421 if line
.startswith('#'):
424 logging
.info("section file data: %d: %s", line_number
, line
)
426 m1
= re
.search(r
'^<SUBSECTION\s*(.*)>', line
, re
.I
)
427 m2
= re
.search(r
'^<TITLE>(.*)<\/TITLE', line
)
428 m3
= re
.search(r
'^<FILE>(.*)<\/FILE>', line
)
429 m4
= re
.search(r
'^<INCLUDE>(.*)<\/INCLUDE>', line
)
430 m5
= re
.search(r
'^(\S+)', line
)
432 if line
.startswith('<SECTION>'):
440 functions_synop
+= "\n"
441 subsection
= m1
.group(1)
443 elif line
.startswith('<SUBSECTION>'):
447 logging
.info("Section: %s", title
)
449 # We don't want warnings if object & class structs aren't used.
450 DeclarationOutput
[title
] = 1
451 DeclarationOutput
["%sClass" % title
] = 1
452 DeclarationOutput
["%sIface" % title
] = 1
453 DeclarationOutput
["%sInterface" % title
] = 1
456 filename
= m3
.group(1)
457 if filename
not in file_def_line
:
458 file_def_line
[filename
] = line_number
460 common
.LogWarning(file, line_number
, "Double <FILE>%s</FILE> entry. Previous occurrence on line %s." %
461 (filename
, file_def_line
[filename
]))
463 key
= filename
+ ":Title"
464 if key
in SourceSymbolDocs
:
465 title
= SourceSymbolDocs
[key
].rstrip()
469 section_includes
= m4
.group(1)
471 if options
.default_includes
:
472 common
.LogWarning(file, line_number
, "Default <INCLUDE> being overridden by command line option.")
474 includes
= m4
.group(1)
476 elif re
.search(r
'^<\/SECTION>', line
):
477 logging
.info("End of section: %s", title
)
478 # TODO: also output if we have sections docs?
479 # long_desc = SymbolDocs.get(filename + ":Long_Description")
482 book_bottom
+= " <xi:include href=\"xml/%s.xml\"/>\n" % filename
484 key
= filename
+ ":Include"
485 if key
in SourceSymbolDocs
:
487 common
.LogWarning(file, line_number
, "Section <INCLUDE> being overridden by inline comments.")
488 section_includes
= SourceSymbolDocs
[key
]
490 if section_includes
== '':
491 section_includes
= includes
493 signals_synop
= re
.sub(r
'^\n*', '', signals_synop
)
494 signals_synop
= re
.sub(r
'\n+$', '\n', signals_synop
)
496 if signals_synop
!= '':
497 signals_synop
= '''<refsect1 id="%s.signals" role="signal_proto">
498 <title role="signal_proto.title">Signals</title>
499 <informaltable frame="none">
501 <colspec colname="signals_return" colwidth="150px"/>
502 <colspec colname="signals_name" colwidth="300px"/>
503 <colspec colname="signals_flags" colwidth="200px"/>
510 ''' % (section_id
, signals_synop
)
511 signals_desc
= TrimTextBlock(signals_desc
)
512 signals_desc
= '''<refsect1 id="%s.signal-details" role="signals">
513 <title role="signals.title">Signal Details</title>
516 ''' % (section_id
, signals_desc
)
518 args_synop
= re
.sub(r
'^\n*', '', args_synop
)
519 args_synop
= re
.sub(r
'\n+$', '\n', args_synop
)
521 args_synop
= '''<refsect1 id="%s.properties" role="properties">
522 <title role="properties.title">Properties</title>
523 <informaltable frame="none">
525 <colspec colname="properties_type" colwidth="150px"/>
526 <colspec colname="properties_name" colwidth="300px"/>
527 <colspec colname="properties_flags" colwidth="200px"/>
534 ''' % (section_id
, args_synop
)
535 args_desc
= TrimTextBlock(args_desc
)
536 args_desc
= '''<refsect1 id="%s.property-details" role="property_details">
537 <title role="property_details.title">Property Details</title>
540 ''' % (section_id
, args_desc
)
542 child_args_synop
= re
.sub(r
'^\n*', '', child_args_synop
)
543 child_args_synop
= re
.sub(r
'\n+$', '\n', child_args_synop
)
544 if child_args_synop
!= '':
545 args_synop
+= '''<refsect1 id="%s.child-properties" role="child_properties">
546 <title role="child_properties.title">Child Properties</title>
547 <informaltable frame="none">
549 <colspec colname="child_properties_type" colwidth="150px"/>
550 <colspec colname="child_properties_name" colwidth="300px"/>
551 <colspec colname="child_properties_flags" colwidth="200px"/>
558 ''' % (section_id
, child_args_synop
)
559 child_args_desc
= TrimTextBlock(child_args_desc
)
560 args_desc
+= '''<refsect1 id="%s.child-property-details" role="child_property_details">
561 <title role="child_property_details.title">Child Property Details</title>
564 ''' % (section_id
, child_args_desc
)
566 style_args_synop
= re
.sub(r
'^\n*', '', style_args_synop
)
567 style_args_synop
= re
.sub(r
'\n+$', '\n', style_args_synop
)
568 if style_args_synop
!= '':
569 args_synop
+= '''<refsect1 id="%s.style-properties" role="style_properties">
570 <title role="style_properties.title">Style Properties</title>
571 <informaltable frame="none">
573 <colspec colname="style_properties_type" colwidth="150px"/>
574 <colspec colname="style_properties_name" colwidth="300px"/>
575 <colspec colname="style_properties_flags" colwidth="200px"/>
582 ''' % (section_id
, style_args_synop
)
583 style_args_desc
= TrimTextBlock(style_args_desc
)
584 args_desc
+= '''<refsect1 id="%s.style-property-details" role="style_properties_details">
585 <title role="style_properties_details.title">Style Property Details</title>
588 ''' % (section_id
, style_args_desc
)
590 hierarchy_str
= AddTreeLineArt(hierarchy
)
591 if hierarchy_str
!= '':
592 hierarchy_str
= '''<refsect1 id="%s.object-hierarchy" role="object_hierarchy">
593 <title role="object_hierarchy.title">Object Hierarchy</title>
597 ''' % (section_id
, hierarchy_str
)
599 interfaces
= TrimTextBlock(interfaces
)
601 interfaces
= '''<refsect1 id="%s.implemented-interfaces" role="impl_interfaces">
602 <title role="impl_interfaces.title">Implemented Interfaces</title>
605 ''' % (section_id
, interfaces
)
607 implementations
= TrimTextBlock(implementations
)
608 if implementations
!= '':
609 implementations
= '''<refsect1 id="%s.implementations" role="implementations">
610 <title role="implementations.title">Known Implementations</title>
613 ''' % (section_id
, implementations
)
615 prerequisites
= TrimTextBlock(prerequisites
)
616 if prerequisites
!= '':
617 prerequisites
= '''<refsect1 id="%s.prerequisites" role="prerequisites">
618 <title role="prerequisites.title">Prerequisites</title>
621 ''' % (section_id
, prerequisites
)
623 derived
= TrimTextBlock(derived
)
625 derived
= '''<refsect1 id="%s.derived-interfaces" role="derived_interfaces">
626 <title role="derived_interfaces.title">Known Derived Interfaces</title>
629 ''' % (section_id
, derived
)
631 functions_synop
= re
.sub(r
'^\n*', '', functions_synop
)
632 functions_synop
= re
.sub(r
'\n+$', '\n', functions_synop
)
633 if functions_synop
!= '':
634 functions_synop
= '''<refsect1 id="%s.functions" role="functions_proto">
635 <title role="functions_proto.title">Functions</title>
636 <informaltable pgwide="1" frame="none">
638 <colspec colname="functions_return" colwidth="150px"/>
639 <colspec colname="functions_name"/>
646 ''' % (section_id
, functions_synop
)
648 other_synop
= re
.sub(r
'^\n*', '', other_synop
)
649 other_synop
= re
.sub(r
'\n+$', '\n', other_synop
)
650 if other_synop
!= '':
651 other_synop
= '''<refsect1 id="%s.other" role="other_proto">
652 <title role="other_proto.title">Types and Values</title>
653 <informaltable role="enum_members_table" pgwide="1" frame="none">
655 <colspec colname="name" colwidth="150px"/>
656 <colspec colname="description"/>
663 ''' % (section_id
, other_synop
)
665 file_changed
= OutputDBFile(filename
, title
, section_id
,
667 functions_synop
, other_synop
,
668 functions_details
, other_details
,
669 signals_synop
, signals_desc
,
670 args_synop
, args_desc
,
671 hierarchy_str
, interfaces
,
673 prerequisites
, derived
,
682 section_includes
= ''
685 functions_details
= ''
690 child_args_synop
= ''
691 style_args_synop
= ''
704 logging
.info(' Symbol: "%s" in subsection: "%s"', symbol
, subsection
)
706 # check for duplicate entries
707 if symbol
not in symbol_def_line
:
708 declaration
= Declarations
.get(symbol
)
709 # FIXME: with this we'll output empty declaration
710 if declaration
is not None:
711 if CheckIsObject(symbol
):
712 file_objects
.append(symbol
)
714 # We don't want standard macros/functions of GObjects,
715 # or private declarations.
716 if subsection
!= "Standard" and subsection
!= "Private":
717 synop
, desc
= OutputDeclaration(symbol
, declaration
)
718 type = DeclarationTypes
[symbol
]
720 if type == 'FUNCTION' or type == 'USER_FUNCTION':
721 functions_synop
+= synop
722 functions_details
+= desc
723 elif type == 'MACRO' and re
.search(symbol
+ r
'\(', declaration
):
724 functions_synop
+= synop
725 functions_details
+= desc
728 other_details
+= desc
730 sig_synop
, sig_desc
= GetSignals(symbol
)
731 arg_synop
, child_arg_synop
, style_arg_synop
, arg_desc
, child_arg_desc
, style_arg_desc
= GetArgs(
733 ifaces
= GetInterfaces(symbol
)
734 impls
= GetImplementations(symbol
)
735 prereqs
= GetPrerequisites(symbol
)
736 der
= GetDerived(symbol
)
737 hierarchy
= GetHierarchy(symbol
, hierarchy
)
739 signals_synop
+= sig_synop
740 signals_desc
+= sig_desc
741 args_synop
+= arg_synop
742 child_args_synop
+= child_arg_synop
743 style_args_synop
+= style_arg_synop
744 args_desc
+= arg_desc
745 child_args_desc
+= child_arg_desc
746 style_args_desc
+= style_arg_desc
748 implementations
+= impls
749 prerequisites
+= prereqs
752 # Note that the declaration has been output.
753 DeclarationOutput
[symbol
] = True
754 elif subsection
!= "Standard" and subsection
!= "Private":
755 UndeclaredSymbols
[symbol
] = True
756 common
.LogWarning(file, line_number
, "No declaration found for %s." % symbol
)
759 symbol_def_line
[symbol
] = line_number
762 if title
== '' and filename
== '':
763 common
.LogWarning(file, line_number
, "Section has no title and no file.")
765 # FIXME: one of those would be enough
766 # filename should be an internal detail for gtk-doc
772 filename
= filename
.replace(' ', '_')
774 section_id
= SourceSymbolDocs
.get(filename
+ ":Section_Id")
775 if section_id
and section_id
.strip() != '':
776 # Remove trailing blanks and use as is
777 section_id
= section_id
.rstrip()
778 elif CheckIsObject(title
):
779 # GObjects use their class name as the ID.
780 section_id
= common
.CreateValidSGMLID(title
)
782 section_id
= common
.CreateValidSGMLID(MODULE
+ '-' + title
)
784 SymbolSection
[symbol
] = title
785 SymbolSectionId
[symbol
] = section_id
788 common
.LogWarning(file, line_number
, "Double symbol entry for %s. "
789 "Previous occurrence on line %d." % (symbol
, symbol_def_line
[symbol
]))
792 OutputMissingDocumentation()
793 OutputUndeclaredSymbols()
794 OutputUnusedSymbols()
796 if options
.outputallsymbols
:
799 if options
.outputsymbolswithoutsince
:
800 OutputSymbolsWithoutSince()
802 for filename
in options
.expand_content_files
.split():
803 file_changed
= OutputExtraFile(filename
)
807 return (changed
, book_top
, book_bottom
)
810 def DetermineNamespace():
811 """Find common set of characters."""
818 for symbol
in iterkeys(IndexEntriesFull
):
819 if name_space
== '' or name_space
.lower() in symbol
.lower():
820 if len(symbol
) > pos
:
821 letter
= symbol
[pos
:pos
+ 1]
822 # stop prefix scanning
826 # Should we also stop on a uppercase char, if last was lowercase
827 # GtkWidget, if we have the 'W' and had the 't' before
828 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
829 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
830 # need to recound each time as this is per symbol
837 if letter
!= '' and letter
!= "_":
840 for letter
in iterkeys(prefix
):
841 logging
.debug("ns prefix: %s: %s", letter
, prefix
[letter
])
842 if prefix
[letter
] > maxsymbols
:
844 maxsymbols
= prefix
[letter
]
846 ratio
= float(len(IndexEntriesFull
)) / prefix
[maxletter
]
847 logging
.debug('most symbols start with %s, that is %f', maxletter
, (100 * ratio
))
850 name_space
+= maxletter
862 def OutputIndex(basename
, apiindex
):
863 """Writes an index that can be included into the main-document into an <index> tag.
866 basename (str): name of the index file without extension
867 apiindex (dict): the index data
869 old_index
= os
.path
.join(DB_OUTPUT_DIR
, basename
+ '.xml')
870 new_index
= os
.path
.join(DB_OUTPUT_DIR
, basename
+ '.new')
876 OUTPUT
= open(new_index
, 'w')
878 OUTPUT
.write(MakeDocHeader("indexdiv") + "\n<indexdiv id=\"%s\">\n" % basename
)
880 logging
.info("generate %s index (%d entries) with namespace %s", basename
, len(apiindex
), NAME_SPACE
)
882 # do a case insensitive sort while chopping off the prefix
886 'short': re
.sub(r
'^' + NAME_SPACE
+ r
'\_?(.*)', r
'\1', x
.upper(), flags
=re
.I
),
887 } for x
in iterkeys(apiindex
)]
888 sorted_keys
= sorted(mapped_keys
, key
=lambda d
: (d
['short'], d
['original']))
890 for key
in sorted_keys
:
891 symbol
= key
['original']
896 short_symbol
= symbol
898 # generate a short symbol description
901 symbol_section_id
= ''
903 if symbol
in DeclarationTypes
:
904 symbol_type
= DeclarationTypes
[symbol
].lower()
906 if symbol_type
== '':
907 logging
.info("trying symbol %s", symbol
)
908 m1
= re
.search(r
'(.*)::(.*)', symbol
)
909 m2
= re
.search(r
'(.*):(.*)', symbol
)
913 logging
.info(" trying object signal %s:%s in %d signals", oname
, osym
, len(SignalNames
))
914 for name
in SignalNames
:
915 logging
.info(" " + name
)
917 symbol_type
= "object signal"
918 if oname
in SymbolSection
:
919 symbol_section
= SymbolSection
[oname
]
920 symbol_section_id
= SymbolSectionId
[oname
]
925 logging
.info(" trying object property %s::%s in %d properties", oname
, osym
, len(ArgNames
))
926 for name
in ArgNames
:
927 logging
.info(" " + name
)
929 symbol_type
= "object property"
930 if oname
in SymbolSection
:
931 symbol_section
= SymbolSection
[oname
]
932 symbol_section_id
= SymbolSectionId
[oname
]
935 if symbol
in SymbolSection
:
936 symbol_section
= SymbolSection
[symbol
]
937 symbol_section_id
= SymbolSectionId
[symbol
]
939 if symbol_type
!= '':
940 symbol_desc
= ", " + symbol_type
941 if symbol_section
!= '':
942 symbol_desc
+= " in <link linkend=\"%s\">%s</link>" % (symbol_section_id
, symbol_section
)
943 # symbol_desc +=" in " + ExpandAbbreviations(symbol, "#symbol_section")
945 curletter
= short_symbol
[0].upper()
946 ixid
= apiindex
[symbol
]
948 logging
.info(" add symbol %s with %s to index in section '%s' (derived from %s)",
949 symbol
, ixid
, curletter
, short_symbol
)
951 if curletter
!= lastletter
:
952 lastletter
= curletter
955 OUTPUT
.write("</indexdiv>\n")
957 OUTPUT
.write("<indexdiv><title>%s</title>\n" % curletter
)
960 OUTPUT
.write('<indexentry><primaryie linkends="%s"><link linkend="%s">%s</link>%s</primaryie></indexentry>\n' %
961 (ixid
, ixid
, symbol
, symbol_desc
))
964 OUTPUT
.write("</indexdiv>\n")
966 OUTPUT
.write("</indexdiv>\n")
969 common
.UpdateFileIfChanged(old_index
, new_index
, 0)
972 def OutputSinceIndexes():
973 """Generate the 'since' api index files."""
974 for version
in set(Since
.values()):
975 logging
.info("Since : [%s]", version
)
976 index
= {x
: IndexEntriesSince
[x
] for x
in iterkeys(IndexEntriesSince
) if Since
[x
] == version
}
977 OutputIndex("api-index-" + version
, index
)
980 def OutputAnnotationGlossary():
981 """Writes a glossary of the used annotation terms.
983 The glossary file can be included into the main document.
985 # if there are no annotations used return
986 if not AnnotationsUsed
:
989 old_glossary
= os
.path
.join(DB_OUTPUT_DIR
, "annotation-glossary.xml")
990 new_glossary
= os
.path
.join(DB_OUTPUT_DIR
, "annotation-glossary.new")
994 # add acronyms that are referenced from acronym text
998 for annotation
in AnnotationsUsed
:
999 if annotation
not in AnnotationDefinition
:
1001 m
= re
.search(r
'<acronym>([\w ]+)<\/acronym>', AnnotationDefinition
[annotation
])
1002 if m
and m
.group(1) not in AnnotationsUsed
:
1003 AnnotationsUsed
[m
.group(1)] = 1
1007 OUTPUT
= common
.open_text(new_glossary
, 'w')
1010 <glossary id="annotation-glossary">
1011 <title>Annotation Glossary</title>
1012 ''' % MakeDocHeader("glossary"))
1014 for annotation
in sorted(iterkeys(AnnotationsUsed
), key
=str.lower
):
1015 if annotation
in AnnotationDefinition
:
1016 definition
= AnnotationDefinition
[annotation
]
1017 curletter
= annotation
[0].upper()
1019 if curletter
!= lastletter
:
1020 lastletter
= curletter
1023 OUTPUT
.write("</glossdiv>\n")
1025 OUTPUT
.write("<glossdiv><title>%s</title>\n" % curletter
)
1028 OUTPUT
.write(''' <glossentry>
1029 <glossterm><anchor id="annotation-glossterm-%s"/>%s</glossterm>
1034 ''' % (annotation
, annotation
, definition
))
1037 OUTPUT
.write("</glossdiv>\n")
1039 OUTPUT
.write("</glossary>\n")
1042 common
.UpdateFileIfChanged(old_glossary
, new_glossary
, 0)
1045 def ReadKnownSymbols(file):
1046 """Collect the names of non-private symbols from the $MODULE-sections.txt file.
1049 file: the $MODULE-sections.txt file
1054 logging
.info("Reading: %s", file)
1055 INPUT
= common
.open_text(file)
1057 if line
.startswith('#'):
1060 if line
.startswith('<SECTION>'):
1064 m
= re
.search(r
'^<SUBSECTION\s*(.*)>', line
, flags
=re
.I
)
1066 subsection
= m
.group(1)
1069 if line
.startswith('<SUBSECTION>'):
1072 if re
.search(r
'^<TITLE>(.*)<\/TITLE>', line
):
1075 m
= re
.search(r
'^<FILE>(.*)<\/FILE>', line
)
1077 KnownSymbols
[m
.group(1) + ":Long_Description"] = 1
1078 KnownSymbols
[m
.group(1) + ":Short_Description"] = 1
1081 m
= re
.search(r
'^<INCLUDE>(.*)<\/INCLUDE>', line
)
1085 m
= re
.search(r
'^<\/SECTION>', line
)
1089 m
= re
.search(r
'^(\S+)', line
)
1092 if subsection
!= "Standard" and subsection
!= "Private":
1093 KnownSymbols
[symbol
] = 1
1095 KnownSymbols
[symbol
] = 0
1099 def OutputDeclaration(symbol
, declaration
):
1100 """Returns the formatted documentation block for a symbol.
1103 symbol (str): the name of the function/macro/...
1104 declaration (str): the declaration of the function/macro.
1107 str: the formatted documentation
1110 dtype
= DeclarationTypes
[symbol
]
1111 if dtype
== 'MACRO':
1112 return OutputMacro(symbol
, declaration
)
1113 elif dtype
== 'TYPEDEF':
1114 return OutputTypedef(symbol
, declaration
)
1115 elif dtype
== 'STRUCT':
1116 return OutputStruct(symbol
, declaration
)
1117 elif dtype
== 'ENUM':
1118 return OutputEnum(symbol
, declaration
)
1119 elif dtype
== 'UNION':
1120 return OutputUnion(symbol
, declaration
)
1121 elif dtype
== 'VARIABLE':
1122 return OutputVariable(symbol
, declaration
)
1123 elif dtype
== 'FUNCTION':
1124 return OutputFunction(symbol
, declaration
, dtype
)
1125 elif dtype
== 'USER_FUNCTION':
1126 return OutputFunction(symbol
, declaration
, dtype
)
1128 logging
.warning("Unknown symbol type %s for symbol %s", dtype
, symbol
)
1132 def OutputSymbolTraits(symbol
):
1133 """Returns the Since and StabilityLevel paragraphs for a symbol.
1136 symbol (str): the name to describe
1139 str: paragraph or empty string
1145 link_id
= "api-index-" + Since
[symbol
]
1146 desc
+= "<para role=\"since\">Since: <link linkend=\"%s\">%s</link></para>" % (link_id
, Since
[symbol
])
1148 if symbol
in StabilityLevel
:
1149 stability
= StabilityLevel
[symbol
]
1150 if stability
in AnnotationDefinition
:
1151 AnnotationsUsed
[stability
] = True
1152 stability
= "<acronym>%s</acronym>" % stability
1153 desc
+= "<para role=\"stability\">Stability Level: %s</para>" % stability
1157 def uri_escape(text
):
1161 # Build a char to hex map
1162 escapes
= {chr(i
): ("%%%02X" % i
) for i
in range(256)}
1164 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1165 def do_escape(char
):
1166 return escapes
[char
]
1167 return re
.sub(r
"([^A-Za-z0-9\-_.!~*'()]", do_escape
, text
)
1170 def OutputSymbolExtraLinks(symbol
):
1171 """Returns extralinks for the symbol (if enabled).
1174 symbol (str): the name to describe
1177 str: paragraph or empty string
1181 if False: # NEW FEATURE: needs configurability
1182 sstr
= uri_escape(symbol
)
1183 mstr
= uri_escape(MODULE
)
1184 desc
+= '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink>
1185 <ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&symbol=%s">edit documentation</ulink>
1186 ''' % (sstr
, mstr
, sstr
)
1191 def OutputSectionExtraLinks(symbol
, docsymbol
):
1194 if False: # NEW FEATURE: needs configurability
1195 sstr
= uri_escape(symbol
)
1196 mstr
= uri_escape(MODULE
)
1197 dsstr
= uri_escape(docsymbol
)
1198 desc
+= '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink>
1199 <ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&symbol=%s">edit documentation</ulink>
1200 ''' % (sstr
, mstr
, dsstr
)
1204 def OutputMacro(symbol
, declaration
):
1205 """Returns the synopsis and detailed description of a macro.
1208 symbol (str): the macro name.
1209 declaration (str): the declaration of the macro.
1212 str: the formated docs
1214 sid
= common
.CreateValidSGMLID(symbol
)
1215 condition
= MakeConditionDescription(symbol
)
1216 synop
= "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link>" % (
1219 fields
= common
.ParseMacroDeclaration(declaration
, CreateValidSGML
)
1224 desc
= '<refsect2 id="%s" role="macro"%s>\n<title>%s</title>\n' % (sid
, condition
, title
)
1225 desc
+= MakeIndexterms(symbol
, sid
)
1227 desc
+= OutputSymbolExtraLinks(symbol
)
1230 synop
+= "<phrase role=\"c_punctuation\">()</phrase>"
1232 synop
+= "</entry></row>\n"
1234 # Don't output the macro definition if is is a conditional macro or it
1235 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1236 # longer than 2 lines, otherwise we get lots of complicated macros like
1238 if symbol
not in DeclarationConditional
and not symbol
.startswith('g_') \
1239 and not re
.search(r
'^_?gnome_', symbol
) and declaration
.count('\n') < 2:
1240 decl_out
= CreateValidSGML(declaration
)
1241 desc
+= "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1243 desc
+= "<programlisting language=\"C\">" + "#define".ljust(RETURN_TYPE_FIELD_WIDTH
) + symbol
1244 m
= re
.search(r
'^\s*#\s*define\s+\w+(\([^\)]*\))', declaration
)
1247 pad
= ' ' * (RETURN_TYPE_FIELD_WIDTH
- len("#define "))
1248 # Align each line so that if should all line up OK.
1249 args
= args
.replace('\n', '\n' + pad
)
1250 desc
+= CreateValidSGML(args
)
1252 desc
+= "</programlisting>\n"
1254 desc
+= MakeDeprecationNote(symbol
)
1256 parameters
= OutputParamDescriptions("MACRO", symbol
, fields
)
1258 if symbol
in SymbolDocs
:
1259 symbol_docs
= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1263 desc
+= OutputSymbolTraits(symbol
)
1264 desc
+= "</refsect2>\n"
1265 return (synop
, desc
)
1268 def OutputTypedef(symbol
, declaration
):
1269 """Returns the synopsis and detailed description of a typedef.
1272 symbol (str): the typedef.
1273 declaration (str): the declaration of the typedef,
1274 e.g. 'typedef unsigned int guint;'
1277 str: the formated docs
1279 sid
= common
.CreateValidSGMLID(symbol
)
1280 condition
= MakeConditionDescription(symbol
)
1281 desc
= "<refsect2 id=\"%s\" role=\"typedef\"%s>\n<title>%s</title>\n" % (sid
, condition
, symbol
)
1282 synop
= "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1285 desc
+= MakeIndexterms(symbol
, sid
)
1287 desc
+= OutputSymbolExtraLinks(symbol
)
1289 if symbol
not in DeclarationConditional
:
1290 decl_out
= CreateValidSGML(declaration
)
1291 desc
+= "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1293 desc
+= MakeDeprecationNote(symbol
)
1295 if symbol
in SymbolDocs
:
1296 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1298 desc
+= OutputSymbolTraits(symbol
)
1299 desc
+= "</refsect2>\n"
1300 return (synop
, desc
)
1303 def OutputStruct(symbol
, declaration
):
1304 """Returns the synopsis and detailed description of a struct.
1306 We check if it is a object struct, and if so we only output parts of it that
1307 are noted as public fields. We also use a different IDs for object structs,
1308 since the original ID is used for the entire RefEntry.
1311 symbol (str): the struct.
1312 declaration (str): the declaration of the struct.
1315 str: the formated docs
1318 default_to_public
= True
1319 if CheckIsObject(symbol
):
1320 logging
.info("Found struct gtype: %s", symbol
)
1322 default_to_public
= ObjectRoots
[symbol
] == 'GBoxed'
1327 sid
= common
.CreateValidSGMLID(symbol
+ "_struct")
1328 condition
= MakeConditionDescription(symbol
+ "_struct")
1330 sid
= common
.CreateValidSGMLID(symbol
)
1331 condition
= MakeConditionDescription(symbol
)
1333 # Determine if it is a simple struct or it also has a typedef.
1335 if symbol
in StructHasTypedef
or re
.search(r
'^\s*typedef\s+', declaration
):
1341 # For structs with typedefs we just output the struct name.
1343 desc
= "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>%s</title>\n" % (sid
, condition
, symbol
)
1345 type_output
= "struct"
1346 desc
= "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>struct %s</title>\n" % (sid
, condition
, symbol
)
1348 synop
= "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1349 type_output
, sid
, symbol
)
1351 desc
+= MakeIndexterms(symbol
, sid
)
1353 desc
+= OutputSymbolExtraLinks(symbol
)
1355 # Form a pretty-printed, private-data-removed form of the declaration
1358 if re
.search(r
'^\s*$', declaration
):
1359 logging
.info("Found opaque struct: %s", symbol
)
1360 decl_out
= "typedef struct _%s %s;" % (symbol
, symbol
)
1361 elif re
.search(r
'^\s*struct\s+\w+\s*;\s*$', declaration
):
1362 logging
.info("Found opaque struct: %s", symbol
)
1363 decl_out
= "struct %s;" % symbol
1366 r
'^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$', declaration
, flags
=re
.S
)
1368 struct_contents
= m
.group(2)
1370 public
= default_to_public
1371 new_declaration
= ''
1373 for decl_line
in struct_contents
.splitlines():
1374 logging
.info("Struct line: %s", decl_line
)
1375 m2
= re
.search(r
'/\*\s*<\s*public\s*>\s*\*/', decl_line
)
1376 m3
= re
.search(r
'/\*\s*<\s*(private|protected)\s*>\s*\*/', decl_line
)
1382 new_declaration
+= decl_line
+ "\n"
1385 # Strip any blank lines off the ends.
1386 new_declaration
= re
.sub(r
'^\s*\n', '', new_declaration
)
1387 new_declaration
= re
.sub(r
'\n\s*$', r
'\n', new_declaration
)
1390 decl_out
= "typedef struct {\n%s} %s;\n" % (new_declaration
, symbol
)
1392 decl_out
= "struct %s {\n%s};\n" % (symbol
, new_declaration
)
1395 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1396 "Couldn't parse struct:\n%s" % declaration
)
1398 # If we couldn't parse the struct or it was all private, output an
1399 # empty struct declaration.
1402 decl_out
= "typedef struct _%s %s;" % (symbol
, symbol
)
1404 decl_out
= "struct %s;" % symbol
1406 decl_out
= CreateValidSGML(decl_out
)
1407 desc
+= "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1409 desc
+= MakeDeprecationNote(symbol
)
1411 if symbol
in SymbolDocs
:
1412 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1414 # Create a table of fields and descriptions
1416 # FIXME: Inserting  's into the produced type declarations here would
1417 # improve the output in most situations ... except for function
1418 # members of structs!
1420 return '<structfield id="%s">%s</structfield>' % (common
.CreateValidSGMLID(sid
+ '.' + args
[0]), args
[0])
1421 fields
= common
.ParseStructDeclaration(declaration
, not default_to_public
, 0, MakeXRef
, pfunc
)
1422 params
= SymbolParams
.get(symbol
)
1424 # If no parameters are filled in, we don't generate the description
1425 # table, for backwards compatibility.
1428 found
= next((True for p
in params
.values() if p
.strip() != ''), False)
1431 field_descrs
= params
1432 missing_parameters
= ''
1433 unused_parameters
= ''
1434 sid
= common
.CreateValidSGMLID(symbol
+ ".members")
1436 desc
+= '''<refsect3 id="%s" role="struct_members">\n<title>Members</title>
1437 <informaltable role="struct_members_table" pgwide="1" frame="none">
1439 <colspec colname="struct_members_name" colwidth="300px"/>
1440 <colspec colname="struct_members_description"/>
1441 <colspec colname="struct_members_annotations" colwidth="200px"/>
1445 for field_name
, text
in iteritems(fields
):
1446 param_annotations
= ''
1448 desc
+= "<row role=\"member\"><entry role=\"struct_member_name\"><para>%s</para></entry>\n" % text
1449 if field_name
in field_descrs
:
1450 (field_descr
, param_annotations
) = ExpandAnnotation(symbol
, field_descrs
[field_name
])
1451 field_descr
= ConvertMarkDown(symbol
, field_descr
)
1453 field_descr
= re
.sub(r
'^(\s|\n)+', '', field_descr
, flags
=re
.M | re
.S
)
1454 field_descr
= re
.sub(r
'(\s|\n)+$', '', field_descr
, flags
=re
.M | re
.S
)
1455 desc
+= "<entry role=\"struct_member_description\">%s</entry>\n<entry role=\"struct_member_annotations\">%s</entry>\n" % (
1456 field_descr
, param_annotations
)
1457 del field_descrs
[field_name
]
1459 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1460 "Field description for %s::%s is missing in source code comment block." % (symbol
, field_name
))
1461 if missing_parameters
!= '':
1462 missing_parameters
+= ", " + field_name
1464 missing_parameters
= field_name
1466 desc
+= "<entry /><entry />\n"
1470 desc
+= "</tbody></tgroup></informaltable>\n</refsect3>\n"
1471 for field_name
in field_descrs
:
1472 # Documenting those standard fields is not required anymore, but
1473 # we don't want to warn if they are documented anyway.
1474 m
= re
.search(r
'(g_iface|parent_instance|parent_class)', field_name
)
1478 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1479 "Field description for %s::%s is not used from source code comment block." % (symbol
, field_name
))
1480 if unused_parameters
!= '':
1481 unused_parameters
+= ", " + field_name
1483 unused_parameters
= field_name
1485 # remember missing/unused parameters (needed in tmpl-free build)
1486 if missing_parameters
!= '' and (symbol
not in AllIncompleteSymbols
):
1487 AllIncompleteSymbols
[symbol
] = missing_parameters
1489 if unused_parameters
!= '' and (symbol
not in AllUnusedSymbols
):
1490 AllUnusedSymbols
[symbol
] = unused_parameters
1493 if symbol
not in AllIncompleteSymbols
:
1494 AllIncompleteSymbols
[symbol
] = "<items>"
1495 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1496 "Field descriptions for struct %s are missing in source code comment block." % symbol
)
1497 logging
.info("Remaining structs fields: " + ':'.join(fields
) + "\n")
1499 desc
+= OutputSymbolTraits(symbol
)
1500 desc
+= "</refsect2>\n"
1501 return (synop
, desc
)
1504 def OutputUnion(symbol
, declaration
):
1505 """Returns the synopsis and detailed description of a union.
1508 symbol (str): the union.
1509 declaration (str): the declaration of the union.
1512 str: the formated docs
1515 if CheckIsObject(symbol
):
1516 logging
.info("Found union gtype: %s", symbol
)
1522 sid
= common
.CreateValidSGMLID(symbol
+ "_union")
1523 condition
= MakeConditionDescription(symbol
+ "_union")
1525 sid
= common
.CreateValidSGMLID(symbol
)
1526 condition
= MakeConditionDescription(symbol
)
1528 # Determine if it is a simple struct or it also has a typedef.
1530 if symbol
in StructHasTypedef
or re
.search(r
'^\s*typedef\s+', declaration
):
1536 # For unions with typedefs we just output the union name.
1538 desc
= "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>%s</title>\n" % (sid
, condition
, symbol
)
1540 type_output
= "union"
1541 desc
= "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>union %s</title>\n" % (sid
, condition
, symbol
)
1543 synop
= "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1544 type_output
, sid
, symbol
)
1546 desc
+= MakeIndexterms(symbol
, sid
)
1548 desc
+= OutputSymbolExtraLinks(symbol
)
1549 desc
+= MakeDeprecationNote(symbol
)
1551 if symbol
in SymbolDocs
:
1552 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1554 # Create a table of fields and descriptions
1556 # FIXME: Inserting  's into the produced type declarations here would
1557 # improve the output in most situations ... except for function
1558 # members of structs!
1560 return '<structfield id="%s">%s</structfield>' % (common
.CreateValidSGMLID(sid
+ '.' + args
[0]), args
[0])
1561 fields
= common
.ParseStructDeclaration(declaration
, 0, 0, MakeXRef
, pfunc
)
1562 params
= SymbolParams
.get(symbol
)
1564 # If no parameters are filled in, we don't generate the description
1565 # table, for backwards compatibility
1568 found
= next((True for p
in params
.values() if p
.strip() != ''), False)
1570 logging
.debug('Union %s has %d entries, found=%d, has_typedef=%d', symbol
, len(fields
), found
, has_typedef
)
1573 field_descrs
= params
1574 missing_parameters
= ''
1575 unused_parameters
= ''
1576 sid
= common
.CreateValidSGMLID('%s.members' % symbol
)
1578 desc
+= '''<refsect3 id="%s" role="union_members">\n<title>Members</title>
1579 <informaltable role="union_members_table" pgwide="1" frame="none">
1581 <colspec colname="union_members_name" colwidth="300px"/>
1582 <colspec colname="union_members_description"/>
1583 <colspec colname="union_members_annotations" colwidth="200px"/>
1587 for field_name
, text
in iteritems(fields
):
1588 param_annotations
= ''
1590 desc
+= "<row><entry role=\"union_member_name\"><para>%s</para></entry>\n" % text
1591 if field_name
in field_descrs
:
1592 (field_descr
, param_annotations
) = ExpandAnnotation(symbol
, field_descrs
[field_name
])
1593 field_descr
= ConvertMarkDown(symbol
, field_descr
)
1596 field_descr
= re
.sub(r
'^(\s|\n)+', '', field_descr
, flags
=re
.M | re
.S
)
1597 field_descr
= re
.sub(r
'(\s|\n)+$', '', field_descr
, flags
=re
.M | re
.S
)
1598 desc
+= "<entry role=\"union_member_description\">%s</entry>\n<entry role=\"union_member_annotations\">%s</entry>\n" % (
1599 field_descr
, param_annotations
)
1600 del field_descrs
[field_name
]
1602 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1603 "Field description for %s::%s is missing in source code comment block." % (symbol
, field_name
))
1604 if missing_parameters
!= '':
1605 missing_parameters
+= ", " + field_name
1607 missing_parameters
= field_name
1609 desc
+= "<entry /><entry />\n"
1613 desc
+= "</tbody></tgroup></informaltable>\n</refsect3>"
1614 for field_name
in field_descrs
:
1615 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1616 "Field description for %s::%s is not used from source code comment block." % (symbol
, field_name
))
1617 if unused_parameters
!= '':
1618 unused_parameters
+= ", " + field_name
1620 unused_parameters
= field_name
1622 # remember missing/unused parameters (needed in tmpl-free build)
1623 if missing_parameters
!= '' and (symbol
not in AllIncompleteSymbols
):
1624 AllIncompleteSymbols
[symbol
] = missing_parameters
1626 if unused_parameters
!= '' and (symbol
not in AllUnusedSymbols
):
1627 AllUnusedSymbols
[symbol
] = unused_parameters
1630 if symbol
not in AllIncompleteSymbols
:
1631 AllIncompleteSymbols
[symbol
] = "<items>"
1632 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1633 "Field descriptions for union %s are missing in source code comment block." % symbol
)
1634 logging
.info("Remaining union fields: " + ':'.join(fields
) + "\n")
1636 desc
+= OutputSymbolTraits(symbol
)
1637 desc
+= "</refsect2>\n"
1638 return (synop
, desc
)
1641 def OutputEnum(symbol
, declaration
):
1642 """Returns the synopsis and detailed description of a enum.
1645 symbol (str): the enum.
1646 declaration (str): the declaration of the enum.
1649 str: the formated docs
1652 if CheckIsObject(symbol
):
1653 logging
.info("Found enum gtype: %s", symbol
)
1659 sid
= common
.CreateValidSGMLID(symbol
+ "_enum")
1660 condition
= MakeConditionDescription(symbol
+ "_enum")
1662 sid
= common
.CreateValidSGMLID(symbol
)
1663 condition
= MakeConditionDescription(symbol
)
1665 synop
= "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1667 desc
= "<refsect2 id=\"%s\" role=\"enum\"%s>\n<title>enum %s</title>\n" % (sid
, condition
, symbol
)
1669 desc
+= MakeIndexterms(symbol
, sid
)
1671 desc
+= OutputSymbolExtraLinks(symbol
)
1672 desc
+= MakeDeprecationNote(symbol
)
1674 if symbol
in SymbolDocs
:
1675 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1677 # Create a table of fields and descriptions
1679 fields
= common
.ParseEnumDeclaration(declaration
)
1680 params
= SymbolParams
.get(symbol
)
1682 # If nothing at all is documented log a single summary warning at the end.
1683 # Otherwise, warn about each undocumented item.
1687 found
= next((True for p
in params
.values() if p
.strip() != ''), False)
1688 field_descrs
= params
1692 missing_parameters
= ''
1693 unused_parameters
= ''
1695 sid
= common
.CreateValidSGMLID("%s.members" % symbol
)
1696 desc
+= '''<refsect3 id="%s" role="enum_members">\n<title>Members</title>
1697 <informaltable role="enum_members_table" pgwide="1" frame="none">
1699 <colspec colname="enum_members_name" colwidth="300px"/>
1700 <colspec colname="enum_members_description"/>
1701 <colspec colname="enum_members_annotations" colwidth="200px"/>
1705 for field_name
in fields
:
1706 field_descr
= field_descrs
.get(field_name
)
1707 param_annotations
= ''
1709 sid
= common
.CreateValidSGMLID(field_name
)
1710 condition
= MakeConditionDescription(field_name
)
1711 desc
+= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"%s\">%s</para></entry>\n" % (
1714 field_descr
, param_annotations
= ExpandAnnotation(symbol
, field_descr
)
1715 field_descr
= ConvertMarkDown(symbol
, field_descr
)
1716 desc
+= "<entry role=\"enum_member_description\">%s</entry>\n<entry role=\"enum_member_annotations\">%s</entry>\n" % (
1717 field_descr
, param_annotations
)
1718 del field_descrs
[field_name
]
1721 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1722 "Value description for %s::%s is missing in source code comment block." % (symbol
, field_name
))
1723 if missing_parameters
!= '':
1724 missing_parameters
+= ", " + field_name
1726 missing_parameters
= field_name
1727 desc
+= "<entry /><entry />\n"
1730 desc
+= "</tbody></tgroup></informaltable>\n</refsect3>"
1731 for field_name
in field_descrs
:
1732 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1733 "Value description for %s::%s is not used from source code comment block." % (symbol
, field_name
))
1734 if unused_parameters
!= '':
1735 unused_parameters
+= ", " + field_name
1737 unused_parameters
= field_name
1739 # remember missing/unused parameters (needed in tmpl-free build)
1740 if missing_parameters
!= '' and (symbol
not in AllIncompleteSymbols
):
1741 AllIncompleteSymbols
[symbol
] = missing_parameters
1743 if unused_parameters
!= '' and (symbol
not in AllUnusedSymbols
):
1744 AllUnusedSymbols
[symbol
] = unused_parameters
1748 if symbol
not in AllIncompleteSymbols
:
1749 AllIncompleteSymbols
[symbol
] = "<items>"
1750 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1751 "Value descriptions for %s are missing in source code comment block." % symbol
)
1753 desc
+= OutputSymbolTraits(symbol
)
1754 desc
+= "</refsect2>\n"
1755 return (synop
, desc
)
1758 def OutputVariable(symbol
, declaration
):
1759 """Returns the synopsis and detailed description of a variable.
1762 symbol (str): the extern'ed variable.
1763 declaration (str): the declaration of the variable.
1766 str: the formated docs
1768 sid
= common
.CreateValidSGMLID(symbol
)
1769 condition
= MakeConditionDescription(symbol
)
1771 logging
.info("ouputing variable: '%s' '%s'", symbol
, declaration
)
1775 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
)
1777 r
'\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=', declaration
)
1779 mod1
= m1
.group(1) or ''
1780 ptr
= m1
.group(3) or ''
1781 space
= m1
.group(4) or ''
1782 mod2
= m1
.group(5) or ''
1783 type_output
= "extern %s%s%s%s" % (mod1
, ptr
, space
, mod2
)
1785 mod1
= m2
.group(1) or ''
1786 ptr
= m2
.group(3) or ''
1787 space
= m2
.group(4) or ''
1788 mod2
= m2
.group(5) or ''
1789 type_output
= '%s%s%s%s' % (mod1
, ptr
, space
, mod2
)
1791 type_output
= "extern"
1793 synop
= "<row><entry role=\"variable_type\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1794 type_output
, sid
, symbol
)
1796 desc
= "<refsect2 id=\"%s\" role=\"variable\"%s>\n<title>%s</title>\n" % (sid
, condition
, symbol
)
1798 desc
+= MakeIndexterms(symbol
, sid
)
1800 desc
+= OutputSymbolExtraLinks(symbol
)
1802 decl_out
= CreateValidSGML(declaration
)
1803 desc
+= "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1805 desc
+= MakeDeprecationNote(symbol
)
1807 if symbol
in SymbolDocs
:
1808 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1810 if symbol
in SymbolAnnotations
:
1811 param_desc
= SymbolAnnotations
[symbol
]
1812 param_annotations
= ''
1813 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
1814 if param_annotations
!= '':
1815 desc
+= "\n<para>%s</para>" % param_annotations
1817 desc
+= OutputSymbolTraits(symbol
)
1818 desc
+= "</refsect2>\n"
1819 return (synop
, desc
)
1822 def OutputFunction(symbol
, declaration
, symbol_type
):
1823 """Returns the synopsis and detailed description of a function.
1826 symbol (str): the function.
1827 declaration (str): the declaration of the function.
1830 str: the formated docs
1832 sid
= common
.CreateValidSGMLID(symbol
)
1833 condition
= MakeConditionDescription(symbol
)
1835 # Take out the return type
1837 regex
= r
'<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n'
1838 m
= re
.search(regex
, declaration
)
1839 declaration
= re
.sub(regex
, '', declaration
)
1840 type_modifier
= m
.group(1) or ''
1842 pointer
= m
.group(3)
1843 pointer
= pointer
.rstrip()
1844 xref
= MakeXRef(type, tagify(type, "returnvalue"))
1846 # if (symbol_type == 'USER_FUNCTION')
1847 # start = "typedef "
1850 # We output const rather than G_CONST_RETURN.
1851 type_modifier
= re
.sub(r
'G_CONST_RETURN', 'const', type_modifier
)
1852 pointer
= re
.sub(r
'G_CONST_RETURN', 'const', pointer
)
1853 pointer
= re
.sub(r
'^\s+', ' ', pointer
)
1855 ret_type_output
= "%s%s%s%s\n" % (start
, type_modifier
, xref
, pointer
)
1857 indent_len
= len(symbol
) + 2
1858 char1
= char2
= char3
= ''
1859 if symbol_type
== 'USER_FUNCTION':
1861 char1
= "<phrase role=\"c_punctuation\">(</phrase>"
1863 char3
= "<phrase role=\"c_punctuation\">)</phrase>"
1865 symbol_output
= "%s<link linkend=\"%s\">%s%s</link>%s" % (char1
, sid
, char2
, symbol
, char3
)
1866 if indent_len
< MAX_SYMBOL_FIELD_WIDTH
:
1867 symbol_desc_output
= "%s%s%s%s " % (char1
, char2
, symbol
, char3
)
1869 indent_len
= MAX_SYMBOL_FIELD_WIDTH
- 8
1870 symbol_desc_output
= ('%s%s%s%s\n' % (char1
, char2
, symbol
, char3
)) + (' ' * (indent_len
- 1))
1872 synop
= "<row><entry role=\"function_type\">%s</entry><entry role=\"function_name\">%s <phrase role=\"c_punctuation\">()</phrase></entry></row>\n" % (
1873 ret_type_output
, symbol_output
)
1875 desc
= "<refsect2 id=\"%s\" role=\"function\"%s>\n<title>%s ()</title>\n" % (sid
, condition
, symbol
)
1877 desc
+= MakeIndexterms(symbol
, sid
)
1879 desc
+= OutputSymbolExtraLinks(symbol
)
1881 desc
+= "<programlisting language=\"C\">%s%s(" % (ret_type_output
, symbol_desc_output
)
1884 return tagify(args
[0], "parameter")
1886 fields
= common
.ParseFunctionDeclaration(declaration
, MakeXRef
, tagfun
)
1889 for field_name
in fields
.values():
1894 desc
+= ",\n" + (' ' * indent_len
) + field_name
1896 desc
+= ");</programlisting>\n"
1898 desc
+= MakeDeprecationNote(symbol
)
1900 if symbol
in SymbolDocs
:
1901 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1903 if symbol
in SymbolAnnotations
:
1904 param_desc
= SymbolAnnotations
[symbol
]
1905 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
1906 if param_annotations
!= '':
1907 desc
+= "\n<para>%s</para>" % param_annotations
1909 desc
+= OutputParamDescriptions("FUNCTION", symbol
, iterkeys(fields
))
1910 desc
+= OutputSymbolTraits(symbol
)
1911 desc
+= "</refsect2>\n"
1912 return (synop
, desc
)
1915 def OutputParamDescriptions(symbol_type
, symbol
, fields
):
1916 """Returns the DocBook output describing the parameters of a symbol.
1918 This can be used for functions, macros or signal handlers.
1921 symbol_type (str): 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
1922 handlers have an implicit user_data parameter last.
1923 symbol (str): the name of the symbol being described.
1924 fields (list): parsed fields from the declaration, used to determine
1925 undocumented/unused entries
1928 str: the formated parameter docs
1935 field_descrs
= [f
for f
in fields
if f
not in ['void', 'Returns']]
1939 params
= SymbolParams
.get(symbol
)
1940 logging
.info("param_desc(%s, %s) = %s", symbol_type
, symbol
, str(params
))
1941 # This might be an empty dict, but for SIGNALS we append the user_data docs.
1942 # TODO(ensonic): maybe create that docstring in GetSignals()
1943 if params
is not None:
1946 missing_parameters
= ''
1947 unused_parameters
= ''
1949 for param_name
, param_desc
in iteritems(params
):
1950 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
1951 param_desc
= ConvertMarkDown(symbol
, param_desc
)
1953 param_desc
= re
.sub(r
'^(\s|\n)+', '', param_desc
, flags
=re
.M | re
.S
)
1954 param_desc
= re
.sub(r
'(\s|\n)+$', '', param_desc
, flags
=re
.M | re
.S
)
1955 if param_name
== "Returns":
1956 returns
= param_desc
1957 if param_annotations
!= '':
1958 returns
+= "\n<para>%s</para>" % param_annotations
1960 elif param_name
== "void":
1961 # FIXME: &common.LogWarning()?
1962 logging
.info("!!!! void in params for %s?\n", symbol
)
1965 if param_name
not in field_descrs
:
1966 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1967 "Parameter description for %s::%s is not used from source code comment block." % (symbol
, param_name
))
1968 if unused_parameters
!= '':
1969 unused_parameters
+= ", " + param_name
1971 unused_parameters
= param_name
1973 field_descrs
.remove(param_name
)
1975 if param_desc
!= '':
1976 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" % (
1977 param_name
, param_desc
, param_annotations
)
1980 for param_name
in field_descrs
:
1981 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1982 "Parameter description for %s::%s is missing in source code comment block." % (symbol
, param_name
))
1983 if missing_parameters
!= '':
1984 missing_parameters
+= ", " + param_name
1986 missing_parameters
= param_name
1988 # Signals have an implicit user_data parameter which we describe.
1989 if symbol_type
== "SIGNAL":
1990 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"
1992 # Start a table if we need one.
1993 if params_desc
!= '':
1994 sid
= common
.CreateValidSGMLID("%s.parameters" % symbol
)
1996 output
+= '''<refsect3 id="%s" role="parameters">\n<title>Parameters</title>
1997 <informaltable role="parameters_table" pgwide="1" frame="none">
1999 <colspec colname="parameters_name" colwidth="150px"/>
2000 <colspec colname="parameters_description"/>
2001 <colspec colname="parameters_annotations" colwidth="200px"/>
2004 output
+= params_desc
2005 output
+= "</tbody></tgroup></informaltable>\n</refsect3>"
2007 # Output the returns info last
2009 sid
= common
.CreateValidSGMLID("%s.returns" % symbol
)
2011 output
+= '''<refsect3 id="%s" role=\"returns\">\n<title>Returns</title>
2014 output
+= "\n</refsect3>"
2016 # remember missing/unused parameters (needed in tmpl-free build)
2017 if missing_parameters
!= '' and (symbol
not in AllIncompleteSymbols
):
2018 AllIncompleteSymbols
[symbol
] = missing_parameters
2020 if unused_parameters
!= '' and (symbol
not in AllUnusedSymbols
):
2021 AllUnusedSymbols
[symbol
] = unused_parameters
2023 if num_params
== 0 and fields
and field_descrs
:
2024 if symbol
not in AllIncompleteSymbols
:
2025 AllIncompleteSymbols
[symbol
] = "<parameters>"
2029 def ParseStabilityLevel(stability
, file, line
, message
):
2030 """Parses a stability level and outputs a warning if it isn't valid.
2032 stability (str): the stability text.
2033 file, line: context for error message
2034 message: description of where the level is from, to use in any error message.
2036 str: the parsed stability level string.
2038 stability
= stability
.strip()
2039 sl
= stability
.strip().lower()
2041 stability
= "Stable"
2042 elif sl
== 'unstable':
2043 stability
= "Unstable"
2044 elif sl
== 'private':
2045 stability
= "Private"
2047 common
.LogWarning(file, line
,
2048 "%s is %s. It should be one of these: Stable, "
2049 "Unstable, or Private." % (
2050 message
, stability
))
2051 return str(stability
)
2054 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
):
2055 """Outputs the final DocBook file for one section.
2058 file (str): the name of the file.
2059 title (str): the title from the $MODULE-sections.txt file
2060 section_id (str): the id to use for the toplevel tag.
2061 includes (str): comma-separates list of include files added at top of
2062 synopsis, with '<' '>' around them (if not already enclosed in '').
2063 functions_synop (str): the DocBook for the Functions Synopsis part.
2064 other_synop (str): the DocBook for the Types and Values Synopsis part.
2065 functions_details (str): the DocBook for the Functions Details part.
2066 other_details (str): the DocBook for the Types and Values Details part.
2067 signal_synop (str): the DocBook for the Signal Synopsis part
2068 signal_desc (str): the DocBook for the Signal Description part
2069 args_synop (str): the DocBook for the Arg Synopsis part
2070 args_desc (str): the DocBook for the Arg Description part
2071 hierarchy (str): the DocBook for the Object Hierarchy part
2072 interfaces (str): the DocBook for the Interfaces part
2073 implementations (str): the DocBook for the Known Implementations part
2074 prerequisites (str): the DocBook for the Prerequisites part
2075 derived (str): the DocBook for the Derived Interfaces part
2076 file_objects (list): objects in this file
2079 bool: True if the docs where updated
2082 logging
.info("Output docbook for file %s with title '%s'", file, title
)
2084 # The edited title overrides the one from the sections file.
2085 new_title
= SymbolDocs
.get(file + ":Title")
2086 if new_title
and not new_title
.strip() == '':
2088 logging
.info("Found title: %s", title
)
2090 short_desc
= SymbolDocs
.get(file + ":Short_Description")
2091 if not short_desc
or short_desc
.strip() == '':
2094 # Don't use ConvertMarkDown here for now since we don't want blocks
2095 short_desc
= ExpandAbbreviations(title
+ ":Short_description", short_desc
)
2096 logging
.info("Found short_desc: %s", short_desc
)
2098 long_desc
= SymbolDocs
.get(file + ":Long_Description")
2099 if not long_desc
or long_desc
.strip() == '':
2102 long_desc
= ConvertMarkDown(title
+ ":Long_description", long_desc
)
2103 logging
.info("Found long_desc: %s", long_desc
)
2105 see_also
= SymbolDocs
.get(file + ":See_Also")
2106 if not see_also
or re
.search(r
'^\s*(<para>)?\s*(</para>)?\s*$', see_also
):
2109 see_also
= ConvertMarkDown(title
+ ":See_Also", see_also
)
2110 logging
.info("Found see_also: %s", see_also
)
2113 see_also
= "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id
, see_also
)
2115 stability
= SymbolDocs
.get(file + ":Stability_Level")
2116 if not stability
or re
.search(r
'^\s*$', stability
):
2119 line_number
= GetSymbolSourceLine(file + ":Stability_Level")
2120 stability
= ParseStabilityLevel(stability
, file, line_number
, "Section stability level")
2121 logging
.info("Found stability: %s", stability
)
2124 stability
= DEFAULT_STABILITY
or ''
2127 if stability
in AnnotationDefinition
:
2128 AnnotationsUsed
[stability
] = True
2129 stability
= "<acronym>%s</acronym>" % stability
2130 stability
= "<refsect1 id=\"%s.stability-level\">\n<title>Stability Level</title>\n%s, unless otherwise indicated\n</refsect1>\n" % (
2131 section_id
, stability
)
2133 image
= SymbolDocs
.get(file + ":Image")
2134 if not image
or re
.search(r
'^\s*$', image
):
2137 image
= image
.strip()
2142 if re
.search(r
'jpe?g$', il
):
2143 format
= "format='JPEG'"
2144 elif il
.endswith('png'):
2145 format
= "format='PNG'"
2146 elif il
.endswith('svg'):
2147 format
= "format='SVG'"
2151 image
= " <inlinegraphic fileref='%s' %s/>\n" % (image
, format
)
2155 include_output
+= "<refsect1 id=\"%s.includes\"><title>Includes</title><synopsis>" % section_id
2156 for include
in includes
.split(','):
2157 if re
.search(r
'^\".+\"$', include
):
2158 include_output
+= "#include %s\n" % include
2160 include
= re
.sub(r
'^\s+|\s+$', '', include
, flags
=re
.S
)
2161 include_output
+= "#include <%s>\n" % include
2163 include_output
+= "</synopsis></refsect1>\n"
2165 extralinks
= OutputSectionExtraLinks(title
, "Section:%s" % file)
2167 old_db_file
= os
.path
.join(DB_OUTPUT_DIR
, file + '.xml')
2168 new_db_file
= os
.path
.join(DB_OUTPUT_DIR
, file + '.xml.new')
2170 OUTPUT
= common
.open_text(new_db_file
, 'w')
2173 for fobject
in file_objects
:
2174 if fobject
== section_id
:
2176 sid
= common
.CreateValidSGMLID(fobject
)
2177 logging
.info("Adding anchor for %s\n", fobject
)
2178 object_anchors
+= "<anchor id=\"%s\"/>" % sid
2180 # Make sure we produce valid docbook
2181 if not functions_details
:
2182 functions_details
= "<para />"
2184 # We used to output this, but is messes up our common.UpdateFileIfChanged code
2185 # since it changes every day (and it is only used in the man pages):
2186 # "<refentry id="$section_id" revision="$mday $month $year">"
2188 OUTPUT
.write(REFENTRY
.substitute({
2189 'args_desc': args_desc
,
2190 'args_synop': args_synop
,
2192 'extralinks': extralinks
,
2193 'functions_details': functions_details
,
2194 'functions_synop': functions_synop
,
2195 'header': MakeDocHeader('refentry'),
2196 'hierarchy': hierarchy
,
2198 'include_output': include_output
,
2199 'interfaces': interfaces
,
2200 'implementations': implementations
,
2201 'long_desc': long_desc
,
2202 'object_anchors': object_anchors
,
2203 'other_details': other_details
,
2204 'other_synop': other_synop
,
2205 'prerequisites': prerequisites
,
2206 'section_id': section_id
,
2207 'see_also': see_also
,
2208 'signals_desc': signals_desc
,
2209 'signals_synop': signals_synop
,
2210 'short_desc': short_desc
,
2211 'stability': stability
,
2213 'MODULE': MODULE
.upper(),
2217 return common
.UpdateFileIfChanged(old_db_file
, new_db_file
, 0)
2220 def OutputProgramDBFile(program
, section_id
):
2221 """Outputs the final DocBook file for one program.
2224 file (str): the name of the file.
2225 section_id (str): the id to use for the toplevel tag.
2228 bool: True if the docs where updated
2230 logging
.info("Output program docbook for %s", program
)
2232 short_desc
= SourceSymbolDocs
.get(program
+ ":Short_Description")
2233 if not short_desc
or short_desc
.strip() == '':
2236 # Don't use ConvertMarkDown here for now since we don't want blocks
2237 short_desc
= ExpandAbbreviations(program
, short_desc
)
2238 logging
.info("Found short_desc: %s", short_desc
)
2240 synopsis
= SourceSymbolDocs
.get(program
+ ":Synopsis")
2241 if synopsis
and synopsis
.strip() != '':
2242 items
= synopsis
.split(' ')
2243 for i
in range(0, len(items
)):
2244 parameter
= items
[i
]
2248 # first parameter is the command name
2250 synopsis
= "<command>%s</command>\n" % parameter
2253 # square brackets indicate optional parameters, curly brackets
2254 # indicate required parameters ("plain" parameters are also
2255 # mandatory, but do not get extra decoration)
2256 m1
= re
.search(r
'^\[(.+?)\]$', parameter
)
2257 m2
= re
.search(r
'^\{(.+?)\}$', parameter
)
2260 parameter
= m1
.group(1)
2263 parameter
= m2
.group(1)
2265 # parameters ending in "..." are repeatable
2266 if parameter
.endswith('...'):
2267 rep
= ' rep=\"repeat\"'
2268 parameter
= parameter
[:-3]
2270 # italic parameters are replaceable parameters
2271 parameter
= re
.sub(r
'\*(.+?)\*', r
'<replaceable>\1</replaceable>', parameter
)
2273 synopsis
+= "<arg choice=\"%s\"%s>" % (choice
, rep
)
2274 synopsis
+= parameter
2275 synopsis
+= "</arg>\n"
2277 logging
.info("Found synopsis: %s", synopsis
)
2279 synopsis
= "<command>%s</command>" % program
2281 long_desc
= SourceSymbolDocs
.get(program
+ ":Long_Description")
2282 if not long_desc
or long_desc
.strip() == '':
2285 long_desc
= ConvertMarkDown("%s:Long_description" % program
, long_desc
)
2286 logging
.info("Found long_desc: %s", long_desc
)
2289 o
= program
+ ":Options"
2290 if o
in SourceSymbolDocs
:
2291 opts
= SourceSymbolDocs
[o
].split('\t')
2293 logging
.info('options: %d, %s', len(opts
), str(opts
))
2295 options
= "<refsect1>\n<title>Options</title>\n<variablelist>\n"
2296 for k
in range(0, len(opts
), 2):
2297 opt_desc
= opts
[k
+ 1]
2299 opt_desc
= re
.sub(r
'\*(.+?)\*', r
'<replaceable>\1</replaceable>', opt_desc
)
2301 options
+= "<varlistentry>\n<term>"
2302 opt_names
= opts
[k
].split(',')
2303 for i
in range(len(opt_names
)):
2304 prefix
= ', ' if i
> 0 else ''
2305 # italic parameters are replaceable parameters
2306 opt_name
= re
.sub(r
'\*(.+?)\*', r
'<replaceable>\1</replaceable>', opt_names
[i
])
2308 options
+= "%s<option>%s</option>\n" % (prefix
, opt_name
)
2310 options
+= "</term>\n"
2311 options
+= "<listitem><para>%s</para></listitem>\n" % opt_desc
2312 options
+= "</varlistentry>\n"
2314 options
+= "</variablelist></refsect1>\n"
2316 exit_status
= SourceSymbolDocs
.get(program
+ ":Returns")
2317 if exit_status
and exit_status
!= '':
2318 exit_status
= ConvertMarkDown("%s:Returns" % program
, exit_status
)
2319 exit_status
= "<refsect1 id=\"%s.exit-status\">\n<title>Exit Status</title>\n%s\n</refsect1>\n" % (
2320 section_id
, exit_status
)
2324 see_also
= SourceSymbolDocs
.get(program
+ ":See_Also")
2325 if not see_also
or re
.search(r
'^\s*(<para>)?\s*(</para>)?\s*$', see_also
):
2328 see_also
= ConvertMarkDown("%s:See_Also" % program
, see_also
)
2329 logging
.info("Found see_also: %s", see_also
)
2332 see_also
= "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id
, see_also
)
2334 old_db_file
= os
.path
.join(DB_OUTPUT_DIR
, program
+ ".xml")
2335 new_db_file
= os
.path
.join(DB_OUTPUT_DIR
, program
+ ".xml.new")
2337 OUTPUT
= common
.open_text(new_db_file
, 'w')
2342 <refentrytitle role="top_of_page" id="%s.top_of_page">%s</refentrytitle>
2343 <manvolnum>1</manvolnum>
2344 <refmiscinfo>User Commands</refmiscinfo>
2347 <refname>%s</refname>
2348 <refpurpose>%s</refpurpose>
2351 <cmdsynopsis>%s</cmdsynopsis>
2353 <refsect1 id="%s.description" role="desc">
2354 <title role="desc.title">Description</title>
2359 ''' % (MakeDocHeader("refentry"), section_id
, section_id
, program
, program
, short_desc
, synopsis
, section_id
, long_desc
, options
, exit_status
, see_also
))
2362 return common
.UpdateFileIfChanged(old_db_file
, new_db_file
, 0)
2365 def OutputExtraFile(file):
2366 """Copies an "extra" DocBook file into the output directory, expanding abbreviations.
2369 file (str): the source file.
2372 bool: True if the docs where updated
2375 basename
= os
.path
.basename(file)
2377 old_db_file
= os
.path
.join(DB_OUTPUT_DIR
, basename
)
2378 new_db_file
= os
.path
.join(DB_OUTPUT_DIR
, basename
+ ".new")
2380 contents
= common
.open_text(file).read()
2382 OUTPUT
= common
.open_text(new_db_file
, 'w')
2383 OUTPUT
.write(ExpandAbbreviations(basename
+ " file", contents
))
2386 return common
.UpdateFileIfChanged(old_db_file
, new_db_file
, 0)
2389 def GetDocbookHeader(main_file
):
2390 if os
.path
.exists(main_file
):
2391 INPUT
= common
.open_text(main_file
)
2394 if re
.search(r
'^\s*<(book|chapter|article)', line
):
2395 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
2396 if not re
.search(r
'http:\/\/www.w3.org\/200[13]\/XInclude', line
) and \
2397 not re
.search(r
'http:\/\/www.w3.org\/200[13]\/XInclude', header
, flags
=re
.MULTILINE
):
2401 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
2402 # FIXME: not sure if we can do this now, as people already work-around the problem
2403 # r'#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">', r'<!ENTITY % \1 SYSTEM \"../\2\">';
2405 r
'<!ENTITY % gtkdocentities SYSTEM "([^"]*)">', r
'<!ENTITY % gtkdocentities SYSTEM "../\1">', line
)
2408 header
= header
.strip()
2410 header
= '''<?xml version="1.0"?>
2411 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2412 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2414 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
2415 <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
2421 def OutputBook(main_file
, book_top
, book_bottom
):
2422 """Outputs the entities that need to be included into the main docbook file for the module.
2425 book_top (str): the declarations of the entities, which are added
2426 at the top of the main docbook file.
2427 book_bottom (str): the entities, which are added in the main docbook
2428 file at the desired position.
2431 old_file
= os
.path
.join(DB_OUTPUT_DIR
, MODULE
+ "-doc.top")
2432 new_file
= os
.path
.join(DB_OUTPUT_DIR
, MODULE
+ "-doc.top.new")
2434 OUTPUT
= common
.open_text(new_file
, 'w')
2435 OUTPUT
.write(book_top
)
2438 common
.UpdateFileIfChanged(old_file
, new_file
, 0)
2440 old_file
= os
.path
.join(DB_OUTPUT_DIR
, MODULE
+ "-doc.bottom")
2441 new_file
= os
.path
.join(DB_OUTPUT_DIR
, MODULE
+ "-doc.bottom.new")
2443 OUTPUT
= common
.open_text(new_file
, 'w')
2444 OUTPUT
.write(book_bottom
)
2447 common
.UpdateFileIfChanged(old_file
, new_file
, 0)
2449 # If the main docbook file hasn't been created yet, we create it here.
2450 # The user can tweak it later.
2451 if main_file
and not os
.path
.exists(main_file
):
2452 OUTPUT
= common
.open_text(main_file
, 'w')
2454 logging
.info("no master doc, create default one at: " + main_file
)
2459 <title>&package_name; Reference Manual</title>
2461 for &package_string;.
2462 The latest version of this documentation can be found on-line at
2463 <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2468 <title>[Insert title here]</title>
2471 ''' % (MakeDocHeader("book"), book_bottom
))
2472 if os
.path
.exists('xml/tree_index.sgml'):
2473 OUTPUT
.write(''' <chapter id="object-tree">
2474 <title>Object Hierarchy</title>
2475 <xi:include href="xml/tree_index.sgml"/>
2479 OUTPUT
.write(''' <!-- enable this when you use gobject types
2480 <chapter id="object-tree">
2481 <title>Object Hierarchy</title>
2482 <xi:include href="xml/tree_index.sgml"/>
2487 OUTPUT
.write(''' <index id="api-index-full">
2488 <title>API Index</title>
2489 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2491 <index id="deprecated-api-index" role="deprecated">
2492 <title>Index of deprecated API</title>
2493 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2496 for version
in set(Since
.values()):
2497 dash_version
= version
.replace('.', '-')
2498 OUTPUT
.write(''' <index id="api-index-%s" role="%s">
2499 <title>Index of new API in %s</title>
2500 <xi:include href="xml/api-index-%s.xml"><xi:fallback /></xi:include>
2502 ''' % (dash_version
, version
, version
, version
))
2505 OUTPUT
.write(''' <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2508 OUTPUT
.write(''' <!-- enable this when you use gobject introspection annotations
2509 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2513 OUTPUT
.write('''</book>
2519 def CreateValidSGML(text
):
2520 """Turn any chars which are used in XML into entities.
2522 e.g. '<' into '<'
2525 text (str): the text to turn into proper XML.
2531 text
= text
.replace('&', '&') # Do this first, or the others get messed up.
2532 text
= text
.replace('<', '<')
2533 text
= text
.replace('>', '>')
2534 # browsers render single tabs inconsistently
2535 text
= re
.sub(r
'([^\s])\t([^\s])', r
'\1 \2', text
)
2539 def ConvertSGMLChars(symbol
, text
):
2540 """Escape XML chars.
2542 This is used for text in source code comment blocks, to turn
2543 chars which are used in XML into entities, e.g. '<' into
2544 <'. Depending on INLINE_MARKUP_MODE, this is done
2545 unconditionally or only if the character doesn't seem to be
2546 part of an XML construct (tag or entity reference).
2548 text (str): the text to turn into proper XML.
2554 if INLINE_MARKUP_MODE
:
2555 # For the XML/SGML mode only convert to entities outside CDATA sections.
2556 return ModifyXMLElements(text
, symbol
,
2557 "<!\\[CDATA\\[|<programlisting[^>]*>",
2558 ConvertSGMLCharsEndTag
,
2559 ConvertSGMLCharsCallback
)
2560 # For the simple non-sgml mode, convert to entities everywhere.
2562 text
= re
.sub(r
'&(?![a-zA-Z#]+;)', r
'&', text
) # Do this first, or the others get messed up.
2563 text
= re
.sub(r
'<', r
'<', text
)
2564 # Allow '>' at beginning of string for blockquote markdown
2565 text
= re
.sub(r
'''(?<=[^\w\n"'\/-])>''', r
'>', text
)
2570 def ConvertSGMLCharsEndTag(start_tag
):
2571 if start_tag
== '<![CDATA[':
2573 return "</programlisting>"
2576 def ConvertSGMLCharsCallback(text
, symbol
, tag
):
2577 if re
.search(r
'^<programlisting', tag
):
2578 logging
.debug('call modifyXML')
2579 # We can handle <programlisting> specially here.
2580 return ModifyXMLElements(text
, symbol
,
2582 ConvertSGMLCharsEndTag
,
2583 ConvertSGMLCharsCallback2
)
2585 logging
.debug('replace entities')
2586 # If we're not in CDATA convert to entities.
2587 text
= re
.sub(r
'&(?![a-zA-Z#]+;)', r
'&', text
) # Do this first, or the others get messed up.
2588 text
= re
.sub(r
'<(?![a-zA-Z\/!])', r
'<', text
)
2589 # Allow '>' at beginning of string for blockquote markdown
2590 text
= re
.sub(r
'''(?<=[^\w\n"'\/-])>''', r
'>', text
)
2592 # Handle "#include <xxxxx>"
2593 text
= re
.sub(r
'#include(\s+)<([^>]+)>', r
'#include\1<\2>', text
)
2598 def ConvertSGMLCharsCallback2(text
, symbol
, tag
):
2599 # If we're not in CDATA convert to entities.
2600 # We could handle <programlisting> differently, though I'm not sure it helps.
2602 # replace only if its not a tag
2603 text
= re
.sub(r
'&(?![a-zA-Z#]+;)', r
'&', text
) # Do this first, or the others get messed up.
2604 text
= re
.sub(r
'<(?![a-zA-Z\/!])', r
'<', text
)
2605 text
= re
.sub(r
'''(?<![a-zA-Z0-9"'\/-])>''', r
'>', text
)
2606 # Handle "#include <xxxxx>"
2607 text
= re
.sub(r
'/#include(\s+)<([^>]+)>', r
'#include\1<\2>', text
)
2612 def ExpandAnnotation(symbol
, param_desc
):
2613 """This turns annotations into acronym tags.
2615 symbol (str): the symbol being documented, for error messages.
2616 param_desc (str): the text to expand.
2619 str: the remaining param_desc
2620 str: the formatted annotations
2622 param_annotations
= ''
2624 # look for annotations at the start of the comment part
2625 # function level annotations don't end with a colon ':'
2626 m
= re
.search(r
'^\s*\((.*?)\)(:|$)', param_desc
)
2628 param_desc
= param_desc
[m
.end():]
2630 annotations
= re
.split(r
'\)\s*\(', m
.group(1))
2631 logging
.info("annotations for %s: '%s'\n", symbol
, m
.group(1))
2632 for annotation
in annotations
:
2633 # need to search for the longest key-match in %AnnotationDefinition
2635 match_annotation
= ''
2637 for annotationdef
in AnnotationDefinition
:
2638 if annotation
.startswith(annotationdef
):
2639 if len(annotationdef
) > match_length
:
2640 match_length
= len(annotationdef
)
2641 match_annotation
= annotationdef
2643 annotation_extra
= ''
2644 if match_annotation
!= '':
2645 m
= re
.search(match_annotation
+ r
'\s+(.*)', annotation
)
2647 annotation_extra
= " " + m
.group(1)
2649 AnnotationsUsed
[match_annotation
] = 1
2650 param_annotations
+= "[<acronym>%s</acronym>%s]" % (match_annotation
, annotation_extra
)
2652 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
2653 "unknown annotation \"%s\" in documentation for %s." % (annotation
, symbol
))
2654 param_annotations
+= "[%s]" % annotation
2656 param_desc
= param_desc
.strip()
2657 m
= re
.search(r
'^(.*?)\.*\s*$', param_desc
, flags
=re
.S
)
2658 param_desc
= m
.group(1) + '. '
2660 if param_annotations
!= '':
2661 param_annotations
= "<emphasis role=\"annotation\">%s</emphasis>" % param_annotations
2663 return (param_desc
, param_annotations
)
2666 def ExpandAbbreviations(symbol
, text
):
2667 """Expand the shortcut notation for symbol references.
2669 This turns the abbreviations function(), macro(), @param, %constant, and #symbol
2670 into appropriate DocBook markup. CDATA sections and <programlisting> parts
2674 symbol (str): the symbol being documented, for error messages.
2675 text (str): the text to expand.
2678 str: the expanded text
2680 # Note: This is a fallback and normally done in the markdown parser
2682 logging
.debug('expand abbreviations for "%s", text: [%s]', symbol
, text
)
2683 m
= re
.search(r
'\|\[[^\n]*\n(.*)\]\|', text
, flags
=re
.M | re
.S
)
2685 logging
.debug('replaced entities in code block')
2686 text
= text
[:m
.start(1)] + md_to_db
.ReplaceEntities(m
.group(1)) + text
[m
.end(1):]
2688 # Convert "|[" and "]|" into the start and end of program listing examples.
2689 # Support \[<!-- language="C" --> modifiers
2690 text
= re
.sub(r
'\|\[<!-- language="([^"]+)" -->', r
'<informalexample><programlisting language="\1"><![CDATA[', text
)
2691 text
= re
.sub(r
'\|\[', r
'<informalexample><programlisting><![CDATA[', text
)
2692 text
= re
.sub(r
'\]\|', r
']]></programlisting></informalexample>', text
)
2694 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2696 return ModifyXMLElements(text
, symbol
,
2697 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2698 ExpandAbbreviationsEndTag
,
2699 ExpandAbbreviationsCallback
)
2702 def ExpandAbbreviationsEndTag(start_tag
):
2703 # Returns the end tag (as a regexp) corresponding to the given start tag.
2704 if start_tag
== r
'<!\[CDATA\[':
2706 if start_tag
== "<!DOCTYPE":
2708 m
= re
.search(r
'<(\w+)', start_tag
)
2710 return "</%s>" % m
.group(1)
2712 logging
.warning('no end tag for "%s"', start_tag
)
2716 def ExpandAbbreviationsCallback(text
, symbol
, tag
):
2717 # Called inside or outside each CDATA or <programlisting> section.
2718 if tag
.startswith(r
'^<programlisting'):
2719 # Handle any embedded CDATA sections.
2720 return ModifyXMLElements(text
, symbol
,
2722 ExpandAbbreviationsEndTag
,
2723 ExpandAbbreviationsCallback2
)
2725 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2726 # but is also used for OutputExtraFile
2728 # We are outside any CDATA or <programlisting> sections, so we expand
2729 # any gtk-doc abbreviations.
2731 # Convert '@param()'
2732 # FIXME: we could make those also links ($symbol.$2), but that would be less
2733 # useful as the link target is a few lines up or down
2734 text
= re
.sub(r
'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r
'\1<parameter>\2()</parameter>', text
)
2736 # Convert 'function()' or 'macro()'.
2737 # if there is abc_*_def() we don't want to make a link to _def()
2738 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2740 return m
.group(1) + MakeXRef(m
.group(2), tagify(m
.group(2) + "()", "function"))
2741 text
= re
.sub(r
'([^\*.\w])(\w+)\s*\(\)', f1
, text
)
2742 # handle #Object.func()
2743 text
= re
.sub(r
'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1
, text
)
2745 # Convert '@param', but not '\@param'.
2746 text
= re
.sub(r
'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r
'\1<parameter>\2</parameter>', text
)
2747 text
= re
.sub(r
'/\\\@', r
'\@', text
)
2749 # Convert '%constant', but not '\%constant'.
2750 # Also allow negative numbers, e.g. %-1.
2752 return m
.group(1) + MakeXRef(m
.group(2), tagify(m
.group(2), "literal"))
2753 text
= re
.sub(r
'(\A|[^\\])\%(-?\w+)', f2
, text
)
2754 text
= re
.sub(r
'\\\%', r
'\%', text
)
2756 # Convert '#symbol', but not '\#symbol'.
2758 return m
.group(1) + MakeHashXRef(m
.group(2), "type")
2759 text
= re
.sub(r
'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3
, text
)
2760 text
= re
.sub(r
'\\#', '#', text
)
2765 def ExpandAbbreviationsCallback2(text
, symbol
, tag
):
2766 # This is called inside a <programlisting>
2768 # We are inside a <programlisting> but outside any CDATA sections,
2769 # so we expand any gtk-doc abbreviations.
2770 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2771 # why not just call it
2772 text
= re
.sub(r
'#(\w+)', lambda m
: '%s;' % MakeHashXRef(m
.group(1), ''), text
)
2773 elif tag
== "<![CDATA[":
2774 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2775 text
= ReplaceEntities(text
, symbol
)
2780 def MakeHashXRef(symbol
, tag
):
2783 # Check for things like '#include', '#define', and skip them.
2784 if symbol
in PreProcessorDirectives
:
2785 return "#%s" % symbol
2787 # Get rid of special suffixes ('-struct','-enum').
2788 text
= re
.sub(r
'-struct$', '', text
)
2789 text
= re
.sub(r
'-enum$', '', text
)
2791 # If the symbol is in the form "Object::signal", then change the symbol to
2792 # "Object-signal" and use "signal" as the text.
2794 o
, s
= symbol
.split('::', 1)
2795 symbol
= '%s-%s' % (o
, s
)
2796 text
= u
'“' + s
+ u
'”'
2798 # If the symbol is in the form "Object:property", then change the symbol to
2799 # "Object--property" and use "property" as the text.
2801 o
, p
= symbol
.split(':', 1)
2802 symbol
= '%s--%s' % (o
, p
)
2803 text
= u
'“' + p
+ u
'”'
2806 text
= tagify(text
, tag
)
2808 return MakeXRef(symbol
, text
)
2811 def ModifyXMLElements(text
, symbol
, start_tag_regexp
, end_tag_func
, callback
):
2812 """Rewrite XML blocks.
2814 Looks for given XML element tags within the text, and calls
2815 the callback on pieces of text inside & outside those elements.
2816 Used for special handling of text inside things like CDATA
2817 and <programlisting>.
2820 text (str): the text.
2821 symbol (str): the symbol currently being documented (only used for
2823 start_tag_regexp (str): the regular expression to match start tags.
2824 e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to
2825 match CDATA sections or programlisting elements.
2826 end_tag_func (func): function which is passed the matched start tag
2827 and should return the appropriate end tag string
2829 callback - callback called with each part of the text. It is
2830 called with a piece of text, the symbol being
2831 documented, and the matched start tag or '' if the text
2832 is outside the XML elements being matched.
2837 before_tag
= start_tag
= end_tag_regexp
= end_tag
= None
2840 logging
.debug('modify xml for symbol: %s, regex: %s, text: [%s]', symbol
, start_tag_regexp
, text
)
2842 m
= re
.search(start_tag_regexp
, text
, flags
=re
.S
)
2844 before_tag
= text
[:m
.start()] # Prematch for last successful match string
2845 start_tag
= m
.group(0) # Last successful match
2846 text
= text
[m
.end():] # Postmatch for last successful match string
2847 # get the matching end-tag for current tag
2848 end_tag_regexp
= end_tag_func(start_tag
)
2850 logging
.debug('symbol: %s matched start: %s, end_tag: %s, text: [%s]', symbol
, start_tag
, end_tag_regexp
, text
)
2852 logging
.debug('converting before tag: [%s]', before_tag
)
2853 result
+= callback(before_tag
, symbol
, '')
2856 m2
= re
.search(end_tag_regexp
, text
, flags
=re
.S
)
2858 before_tag
= text
[:m2
.start()]
2859 end_tag
= m2
.group(0)
2860 text
= text
[m2
.end():]
2862 logging
.debug('symbol: %s matched end %s: text: [%s]', symbol
, end_tag
, text
)
2864 result
+= callback(before_tag
, symbol
, start_tag
)
2867 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
2868 "Can't find tag end: %s in docs for: %s." % (end_tag_regexp
, symbol
))
2869 # Just assume it is all inside the tag.
2870 result
+= callback(text
, symbol
, start_tag
)
2872 m
= re
.search(start_tag_regexp
, text
, flags
=re
.S
)
2874 # Handle any remaining text outside the tags.
2875 logging
.debug('converting after tag: [%s]', text
)
2876 result
+= callback(text
, symbol
, '')
2877 logging
.debug('results for symbol: %s, text: [%s]', symbol
, result
)
2882 def tagify(text
, elem
):
2883 # Adds a tag around some text.
2884 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
2885 return '<' + elem
+ '>' + text
+ '</' + elem
+ '>'
2888 def MakeDocHeader(tag
):
2889 """Builds a docbook header for the given tag.
2892 tag (str): doctype tag
2895 str: the docbook header
2897 header
= re
.sub(r
'<!DOCTYPE \w+', r
'<!DOCTYPE ' + tag
, doctype_header
)
2898 # fix the path for book since this is one level up
2901 r
'<!ENTITY % gtkdocentities SYSTEM "../([a-zA-Z./]+)">', r
'<!ENTITY % gtkdocentities SYSTEM "\1">', header
)
2905 def MakeXRef(symbol
, text
=None):
2906 """This returns a cross-reference link to the given symbol.
2908 Though it doesn't try to do this for a few standard C types that it knows
2909 won't be in the documentation.
2912 symbol (str): the symbol to try to create a XRef to.
2913 text (str): text to put inside the XRef, defaults to symbol
2918 symbol
= symbol
.strip()
2922 # Get rid of special suffixes ('-struct','-enum').
2923 text
= re
.sub(r
'-struct$', '', text
)
2924 text
= re
.sub(r
'-enum$', '', text
)
2929 logging
.info("Getting type link for %s -> %s", symbol
, text
)
2931 symbol_id
= common
.CreateValidSGMLID(symbol
)
2932 return "<link linkend=\"%s\">%s</link>" % (symbol_id
, text
)
2935 def MakeIndexterms(symbol
, sid
):
2936 """This returns a indexterm elements for the given symbol
2939 symbol (str): the symbol to create indexterms for
2942 str: doxbook index terms
2947 # make the index useful, by ommiting the namespace when sorting
2948 if NAME_SPACE
!= '':
2949 m
= re
.search(r
'^%s\_?(.*)' % NAME_SPACE
, symbol
, flags
=re
.I
)
2951 sortas
= ' sortas="%s"' % m
.group(1)
2953 if symbol
in Deprecated
:
2954 terms
+= "<indexterm zone=\"%s\" role=\"deprecated\"><primary%s>%s</primary></indexterm>" % (
2955 sid
, sortas
, symbol
)
2956 IndexEntriesDeprecated
[symbol
] = sid
2957 IndexEntriesFull
[symbol
] = sid
2959 since
= Since
[symbol
].strip()
2961 terms
+= "<indexterm zone=\"%s\" role=\"%s\"><primary%s>%s</primary></indexterm>" % (
2962 sid
, since
, sortas
, symbol
)
2963 IndexEntriesSince
[symbol
] = sid
2964 IndexEntriesFull
[symbol
] = sid
2966 terms
+= "<indexterm zone=\"%s\"><primary%s>%s</primary></indexterm>" % (sid
, sortas
, symbol
)
2967 IndexEntriesFull
[symbol
] = sid
2971 def MakeDeprecationNote(symbol
):
2972 """This returns a deprecation warning for the given symbol.
2975 symbol (str): the symbol to try to create a warning for.
2978 str: formatted warning or empty string if symbol is not deprecated
2981 if symbol
in Deprecated
:
2982 desc
+= "<warning><para><literal>%s</literal> " % symbol
2983 note
= Deprecated
[symbol
]
2985 m
= re
.search(r
'^\s*([0-9\.]+)\s*:?', note
)
2987 desc
+= "has been deprecated since version %s and should not be used in newly-written code.</para>" % m
.group(
2990 desc
+= "is deprecated and should not be used in newly-written code.</para>"
2992 note
= re
.sub(r
'^\s*([0-9\.]+)\s*:?\s*', '', note
)
2996 note
= ConvertMarkDown(symbol
, note
)
2999 desc
+= "</warning>\n"
3004 def MakeConditionDescription(symbol
):
3005 """This returns a sumary of conditions for the given symbol.
3008 symbol (str): the symbol to create the sumary for.
3011 str: formatted text or empty string if no special conditions apply.
3014 if symbol
in Deprecated
:
3017 m
= re
.search(r
'^\s*(.*?)\s*$', Deprecated
[symbol
])
3019 desc
+= "deprecated:%s" % m
.group(1)
3021 desc
+= "deprecated"
3026 m
= re
.search(r
'^\s*(.*?)\s*$', Since
[symbol
])
3028 desc
+= "since:%s" % m
.group(1)
3032 if symbol
in StabilityLevel
:
3036 desc
+= "stability:" + StabilityLevel
[symbol
]
3039 cond
= re
.sub(r
'"', r
'"', desc
)
3040 desc
= ' condition=\"%s\"' % cond
3041 logging
.info("condition for '%s' = '%s'", symbol
, desc
)
3046 def GetHierarchy(gobject
, hierarchy
):
3047 """Generate the object inheritance graph.
3049 Returns the DocBook output describing the ancestors and
3050 immediate children of a GObject subclass. It uses the
3051 global Objects and ObjectLevels arrays to walk the tree.
3054 object (str): the GtkObject subclass.
3055 hierarchy (list) - previous hierarchy
3058 list: lines of docbook describing the hierarchy
3060 # Find object in the objects array.
3065 for i
in range(len(Objects
)):
3067 if ObjectLevels
[i
] <= level
:
3070 elif ObjectLevels
[i
] == level
+ 1:
3071 children
.append(Objects
[i
])
3073 elif Objects
[i
] == gobject
:
3076 level
= ObjectLevels
[i
]
3081 logging
.info("=== Hierachy for: %s (%d existing entries) ===", gobject
, len(hierarchy
))
3083 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3084 ancestors
= [gobject
]
3085 logging
.info("Level: %s", level
)
3088 if ObjectLevels
[j
] < level
:
3089 ancestors
.append(Objects
[j
])
3090 level
= ObjectLevels
[j
]
3091 logging
.info("Level: %s", level
)
3093 # Output the ancestors, indented and with links.
3094 logging
.info('%d ancestors', len(ancestors
))
3097 for i
in range(len(ancestors
) - 1, -1, -1):
3098 ancestor
= ancestors
[i
]
3099 ancestor_id
= common
.CreateValidSGMLID(ancestor
)
3100 indent
= ' ' * (level
* 4)
3101 # Don't add a link to the current object, i.e. when i == 0.
3103 entry_text
= indent
+ "<link linkend=\"%s\">%s</link>" % (ancestor_id
, ancestor
)
3104 alt_text
= indent
+ ancestor
3106 entry_text
= indent
+ ancestor
3107 alt_text
= indent
+ "<link linkend=\"%s\">%s</link>" % (ancestor_id
, ancestor
)
3109 logging
.info("Checking for '%s' or '%s'", entry_text
, alt_text
)
3110 # Check if we already have this object
3112 for j
in range(len(hierarchy
)):
3113 if hierarchy
[j
] == entry_text
or (hierarchy
[j
] == alt_text
):
3117 # We have a new entry, find insert position in alphabetical order
3119 for j
in range(last_index
, len(hierarchy
)):
3120 if not re
.search(r
'^' + indent
, hierarchy
[j
]):
3124 elif re
.search(r
'^%s[^ ]' % indent
, hierarchy
[j
]):
3125 stripped_text
= hierarchy
[j
]
3126 if r
'<link linkend' not in entry_text
:
3127 stripped_text
= re
.sub(r
'<link linkend="[A-Za-z]*">', '', stripped_text
)
3128 stripped_text
= re
.sub(r
'</link>', '', stripped_text
)
3130 if entry_text
< stripped_text
:
3137 last_index
= len(hierarchy
)
3139 logging
.debug('insert at %d: %s', last_index
, entry_text
)
3140 hierarchy
.insert(last_index
, entry_text
)
3143 # Already have this one, make sure we use the not linked version
3144 if r
'<link linkend' not in entry_text
:
3145 hierarchy
[j
] = entry_text
3147 # Remember index as base insert point
3148 last_index
= index
+ 1
3152 # Output the children, indented and with links.
3153 logging
.info('%d children', len(children
))
3154 for i
in range(len(children
)):
3155 sid
= common
.CreateValidSGMLID(children
[i
])
3156 indented_text
= ' ' * (level
* 4) + "<link linkend=\"%s\">%s</link>" % (sid
, children
[i
])
3157 logging
.debug('insert at %d: %s', last_index
, indented_text
)
3158 hierarchy
.insert(last_index
, indented_text
)
3163 def GetInterfaces(gobject
):
3164 """Generate interface implementation graph.
3166 Returns the DocBook output describing the interfaces
3167 implemented by a class. It uses the global Interfaces hash.
3170 object (str): the GObject subclass.
3173 str: implemented interfaces
3176 # Find object in the objects array.
3177 if gobject
in Interfaces
:
3178 ifaces
= Interfaces
[gobject
].split()
3183 for i
in range(count
):
3184 sid
= common
.CreateValidSGMLID(ifaces
[i
])
3185 text
+= " <link linkend=\"%s\">%s</link>" % (sid
, ifaces
[i
])
3196 def GetImplementations(gobject
):
3197 """Generate interface usage graph.
3199 Returns the DocBook output describing the implementations
3200 of an interface. It uses the global Interfaces hash.
3203 object (str): the GObject subclass.
3206 str: interface implementations
3210 for key
in Interfaces
:
3211 if re
.search(r
'\b%s\b' % gobject
, Interfaces
[key
]):
3218 %s is implemented by
3220 for i
in range(count
):
3221 sid
= common
.CreateValidSGMLID(impls
[i
])
3222 text
+= " <link linkend=\"%s\">%s</link>" % (sid
, impls
[i
])
3233 def GetPrerequisites(iface
):
3234 """Generates interface requirements.
3236 Returns the DocBook output describing the prerequisites
3237 of an interface. It uses the global Prerequisites hash.
3239 iface (str): the interface.
3242 str: required interfaces
3246 if iface
in Prerequisites
:
3250 prereqs
= Prerequisites
[iface
].split()
3251 count
= len(prereqs
)
3252 for i
in range(count
):
3253 sid
= common
.CreateValidSGMLID(prereqs
[i
])
3254 text
+= " <link linkend=\"%s\">%s</link>" % (sid
, prereqs
[i
])
3265 def GetDerived(iface
):
3267 Returns the DocBook output describing the derived interfaces
3268 of an interface. It uses the global %Prerequisites hash.
3271 iface (str): the interface.
3274 str: derived interfaces
3278 for key
in Prerequisites
:
3279 if re
.search(r
'\b%s\b' % iface
, Prerequisites
[key
]):
3282 count
= len(derived
)
3288 for i
in range(count
):
3289 sid
= common
.CreateValidSGMLID(derived
[i
])
3290 text
+= " <link linkend=\"%s\">%s</link>" % (sid
, derived
[i
])
3301 def GetSignals(gobject
):
3302 """Generate signal docs.
3304 Returns the synopsis and detailed description DocBook output
3305 for the signal handlers of a given GObject subclass.
3308 object (str): the GObject subclass, e.g. 'GtkButton'.
3316 for i
in range(len(SignalObjects
)):
3317 if SignalObjects
[i
] == gobject
:
3318 logging
.info("Found signal: %s", SignalNames
[i
])
3319 name
= SignalNames
[i
]
3320 symbol
= '%s::%s' % (gobject
, name
)
3321 sid
= common
.CreateValidSGMLID('%s-%s' % (gobject
, name
))
3323 desc
+= u
"<refsect2 id=\"%s\" role=\"signal\"><title>The <literal>“%s”</literal> signal</title>\n" % (
3325 desc
+= MakeIndexterms(symbol
, sid
)
3327 desc
+= OutputSymbolExtraLinks(symbol
)
3329 desc
+= "<programlisting language=\"C\">"
3331 m
= re
.search(r
'\s*(const\s+)?(\w+)\s*(\**)', SignalReturns
[i
])
3332 type_modifier
= m
.group(1) or ''
3334 pointer
= m
.group(3)
3335 xref
= MakeXRef(gtype
, tagify(gtype
, "returnvalue"))
3337 ret_type_output
= '%s%s%s' % (type_modifier
, xref
, pointer
)
3338 callback_name
= "user_function"
3339 desc
+= '%s\n%s (' % (ret_type_output
, callback_name
)
3341 indentation
= ' ' * (len(callback_name
) + 2)
3343 sourceparams
= SourceSymbolParams
.get(symbol
)
3344 sourceparam_names
= None
3346 sourceparam_names
= list(sourceparams
) # keys as list
3347 params
= SignalPrototypes
[i
].splitlines()
3348 type_len
= len("gpointer")
3349 name_len
= len("user_data")
3350 # do two passes, the first one is to calculate padding
3352 for j
in range(len(params
)):
3354 # allow alphanumerics, '_', '[' & ']' in param names
3355 m
= re
.search(r
'^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$', params
[j
])
3358 pointer
= m
.group(2)
3359 if sourceparam_names
:
3360 if j
< len(sourceparam_names
):
3361 param_name
= sourceparam_names
[j
]
3362 logging
.info('from sourceparams: "%s" (%d: %s)', param_name
, j
, params
[j
])
3363 # we're mssing the docs for this param, don't warn here though
3365 param_name
= m
.group(3)
3366 logging
.info('from params: "%s" (%d: %s)', param_name
, j
, params
[j
])
3369 param_name
= "arg%d" % j
3372 if len(gtype
) + len(pointer
) > type_len
:
3373 type_len
= len(gtype
) + len(pointer
)
3374 if len(param_name
) > name_len
:
3375 name_len
= len(param_name
)
3377 logging
.info("signal arg[%d]: '%s'", j
, param_name
)
3378 xref
= MakeXRef(gtype
, tagify(gtype
, "type"))
3379 pad
= ' ' * (type_len
- len(gtype
) - len(pointer
))
3380 desc
+= '%s%s %s%s,\n' % (xref
, pad
, pointer
, param_name
)
3384 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
3385 "Can't parse arg: %s\nArgs:%s" % (params
[j
], SignalPrototypes
[i
]))
3387 xref
= MakeXRef("gpointer", tagify("gpointer", "type"))
3388 pad
= ' ' * (type_len
- len("gpointer"))
3389 desc
+= '%s%s user_data)' % (xref
, pad
)
3390 desc
+= "</programlisting>\n"
3392 flags
= SignalFlags
[i
]
3396 flags_string
= "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>"
3399 flags_string
= "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>"
3402 flags_string
= "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>"
3403 flags_string
= "Cleanup"
3407 flags_string
+= " / "
3408 flags_string
= "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>"
3412 flags_string
+= " / "
3413 flags_string
= "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>"
3417 flags_string
+= " / "
3418 flags_string
= "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>"
3422 flags_string
+= " / "
3423 flags_string
= "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>"
3425 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" % (
3426 ret_type_output
, sid
, name
, flags_string
)
3428 parameters
= OutputParamDescriptions("SIGNAL", symbol
, None)
3429 logging
.info("formatted signal params: '%s' -> '%s'", symbol
, parameters
)
3431 AllSymbols
[symbol
] = 1
3432 if symbol
in SymbolDocs
:
3433 symbol_docs
= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
3437 if not IsEmptyDoc(SymbolDocs
[symbol
]):
3438 AllDocumentedSymbols
[symbol
] = 1
3440 if symbol
in SymbolAnnotations
:
3441 param_desc
= SymbolAnnotations
[symbol
]
3442 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
3443 if param_annotations
!= '':
3444 desc
+= "\n<para>%s</para>" % param_annotations
3446 desc
+= MakeDeprecationNote(symbol
)
3450 desc
+= "<para>Flags: %s</para>\n" % flags_string
3452 desc
+= OutputSymbolTraits(symbol
)
3453 desc
+= "</refsect2>"
3455 return (synop
, desc
)
3458 def GetArgs(gobject
):
3459 """Generate property docs.
3461 Returns the synopsis and detailed description DocBook output
3462 for the Args of a given GtkObject subclass.
3465 object (str): the GObject subclass, e.g. 'GtkButton'.
3477 for i
in range(len(ArgObjects
)):
3478 if ArgObjects
[i
] == gobject
:
3479 logging
.info("Found arg: %s", ArgNames
[i
])
3487 kind
= "child property"
3490 kind
= "style property"
3495 # Remember only one colon so we don't clash with signals.
3496 symbol
= '%s:%s' % (gobject
, name
)
3497 # use two dashes and ev. an extra separator here for the same reason.
3498 sid
= common
.CreateValidSGMLID('%s--%s%s' % (gobject
, id_sep
, name
))
3502 arange
= ArgRanges
[i
]
3503 range_output
= CreateValidSGML(arange
)
3504 default
= ArgDefaults
[i
]
3505 default_output
= CreateValidSGML(default
)
3507 if atype
== "GtkString":
3508 atype
= "char *"
3510 if atype
== "GtkSignal":
3511 atype
= "GtkSignalFunc, gpointer"
3512 type_output
= MakeXRef("GtkSignalFunc") + ", " + MakeXRef("gpointer")
3513 elif re
.search(r
'^(\w+)\*$', atype
):
3514 m
= re
.search(r
'^(\w+)\*$', atype
)
3515 type_output
= MakeXRef(m
.group(1), tagify(m
.group(1), "type")) + " *"
3517 type_output
= MakeXRef(atype
, tagify(atype
, "type"))
3520 flags_string
= "Read"
3524 flags_string
+= " / "
3525 flags_string
+= "Write"
3529 flags_string
+= " / "
3530 flags_string
+= "Construct"
3534 flags_string
+= " / "
3535 flags_string
+= "Construct Only"
3537 AllSymbols
[symbol
] = 1
3539 if symbol
in SymbolDocs
and not IsEmptyDoc(SymbolDocs
[symbol
]):
3540 blurb
= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
3541 logging
.info(".. [%s][%s]", SymbolDocs
[symbol
], blurb
)
3542 AllDocumentedSymbols
[symbol
] = 1
3545 if ArgBlurbs
[i
] != '':
3546 blurb
= "<para>" + CreateValidSGML(ArgBlurbs
[i
]) + "</para>"
3547 AllDocumentedSymbols
[symbol
] = 1
3549 # FIXME: print a warning?
3550 logging
.info(".. no description")
3554 pad1
= " " * (24 - len(name
))
3556 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" % (
3557 type_output
, sid
, name
, flags_string
)
3558 arg_desc
= u
"<refsect2 id=\"%s\" role=\"property\"><title>The <literal>“%s”</literal> %s</title>\n" % (
3560 arg_desc
+= MakeIndexterms(symbol
, sid
)
3562 arg_desc
+= OutputSymbolExtraLinks(symbol
)
3564 arg_desc
+= u
"<programlisting> “%s”%s %s</programlisting>\n" % (name
, pad1
, type_output
)
3566 if symbol
in SymbolAnnotations
:
3567 param_desc
= SymbolAnnotations
[symbol
]
3568 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
3569 if param_annotations
!= '':
3570 arg_desc
+= "\n<para>%s</para>" % param_annotations
3572 arg_desc
+= MakeDeprecationNote(symbol
)
3575 arg_desc
+= "<para>Flags: %s</para>\n" % flags_string
3578 arg_desc
+= "<para>Allowed values: %s</para>\n" % range_output
3581 arg_desc
+= "<para>Default value: %s</para>\n" % default_output
3583 arg_desc
+= OutputSymbolTraits(symbol
)
3584 arg_desc
+= "</refsect2>\n"
3587 child_synop
+= arg_synop
3588 child_desc
+= arg_desc
3591 style_synop
+= arg_synop
3592 style_desc
+= arg_desc
3598 return (synop
, child_synop
, style_synop
, desc
, child_desc
, style_desc
)
3601 def IgnorePath(path
, source_dirs
, ignore_files
):
3602 for sdir
in source_dirs
:
3603 # Cut off base directory
3604 m1
= re
.search(r
'^%s/(.*)$' % re
.escape(sdir
), path
)
3606 # Check if the filename is in the ignore list.
3607 m2
= re
.search(r
'(\s|^)%s(\s|$)' % re
.escape(m1
.group(1)), ignore_files
)
3609 logging
.info("Skipping path: %s", path
)
3612 logging
.info("No match for: %s", m1
.group(1))
3614 logging
.info("No match for: %s", path
)
3618 def ReadSourceDocumentation(source_dir
, suffix_list
, source_dirs
, ignore_files
):
3619 """Read the documentation embedded in comment blocks in the source code.
3621 It recursively descends the source directory looking for source files and
3622 scans them looking for specially-formatted comment blocks.
3625 source_dir (str): the directory to scan.
3626 suffix_list (list): extensions to check
3628 if IgnorePath(source_dir
, source_dirs
, ignore_files
):
3631 logging
.info("Scanning source directory: %s", source_dir
)
3633 # This array holds any subdirectories found.
3636 for ifile
in sorted(os
.listdir(source_dir
)):
3637 logging
.debug("... : %s", ifile
)
3638 if ifile
.startswith('.'):
3640 fname
= os
.path
.join(source_dir
, ifile
)
3641 if os
.path
.isdir(fname
):
3642 subdirs
.append(fname
)
3644 for suffix
in suffix_list
:
3645 if ifile
.endswith(suffix
):
3646 if not IgnorePath(fname
, source_dirs
, ignore_files
):
3647 ScanSourceFile(fname
, ignore_files
)
3650 # Now recursively scan the subdirectories.
3651 for sdir
in subdirs
:
3652 ReadSourceDocumentation(sdir
, suffix_list
, source_dirs
, ignore_files
)
3655 def ScanSourceFile(ifile
, ignore_files
):
3656 """Scans one source file looking for specially-formatted comment blocks.
3658 Later MergeSourceDocumentation() is copying over the doc blobs that are not
3662 file (str): the file to scan.
3664 m
= re
.search(r
'^.*[\/\\]([^\/\\]*)$', ifile
)
3666 basename
= m
.group(1)
3668 common
.LogWarning(ifile
, 1, "Can't find basename for this filename.")
3671 # Check if the basename is in the list of files to ignore.
3672 if re
.search(r
'(\s|^)%s(\s|$)' % re
.escape(basename
), ignore_files
):
3673 logging
.info("Skipping source file: %s", ifile
)
3676 logging
.info("Scanning source file: %s", ifile
)
3678 SRCFILE
= common
.open_text(ifile
)
3679 in_comment_block
= False
3684 since_desc
= stability_desc
= deprecated_desc
= ''
3685 params
= OrderedDict()
3688 for line
in SRCFILE
:
3690 # Look for the start of a comment block.
3691 if not in_comment_block
:
3692 if re
.search(r
'^\s*/\*.*\*/', line
):
3693 # one-line comment - not gtkdoc
3695 elif re
.search(r
'^\s*/\*\*\s', line
):
3696 logging
.info("Found comment block start")
3698 in_comment_block
= True
3700 # Reset all the symbol data.
3706 deprecated_desc
= ''
3708 params
= OrderedDict()
3713 # We're in a comment block. Check if we've found the end of it.
3714 if re
.search(r
'^\s*\*+/', line
):
3716 # maybe its not even meant to be a gtk-doc comment?
3717 common
.LogWarning(ifile
, line_number
, "Symbol name not found at the start of the comment block.")
3719 # Add the return value description onto the end of the params.
3721 # TODO(ensonic): check for duplicated Return docs
3722 # common.LogWarning(file, line_number, "Multiple Returns for %s." % symbol)
3723 params
['Returns'] = return_desc
3725 # Convert special characters
3726 description
= ConvertSGMLChars(symbol
, description
)
3727 for (param_name
, param_desc
) in iteritems(params
):
3728 params
[param_name
] = ConvertSGMLChars(symbol
, param_desc
)
3730 # Handle Section docs
3731 m
= re
.search(r
'SECTION:\s*(.*)', symbol
)
3732 m2
= re
.search(r
'PROGRAM:\s*(.*)', symbol
)
3734 real_symbol
= m
.group(1)
3735 long_descr
= real_symbol
+ ":Long_Description"
3737 if long_descr
not in KnownSymbols
or KnownSymbols
[long_descr
] != 1:
3739 ifile
, line_number
, "Section %s is not defined in the %s-sections.txt file." % (real_symbol
, MODULE
))
3741 logging
.info("SECTION DOCS found in source for : '%s'", real_symbol
)
3742 for param_name
, param_desc
in iteritems(params
):
3743 logging
.info(" '" + param_name
+ "'")
3744 param_name
= param_name
.lower()
3746 if param_name
== "short_description":
3747 key
= real_symbol
+ ":Short_Description"
3748 elif param_name
== "see_also":
3749 key
= real_symbol
+ ":See_Also"
3750 elif param_name
== "title":
3751 key
= real_symbol
+ ":Title"
3752 elif param_name
== "stability":
3753 key
= real_symbol
+ ":Stability_Level"
3754 elif param_name
== "section_id":
3755 key
= real_symbol
+ ":Section_Id"
3756 elif param_name
== "include":
3757 key
= real_symbol
+ ":Include"
3758 elif param_name
== "image":
3759 key
= real_symbol
+ ":Image"
3762 SourceSymbolDocs
[key
] = param_desc
3763 SourceSymbolSourceFile
[key
] = ifile
3764 SourceSymbolSourceLine
[key
] = line_number
3766 SourceSymbolDocs
[long_descr
] = description
3767 SourceSymbolSourceFile
[long_descr
] = ifile
3768 SourceSymbolSourceLine
[long_descr
] = line_number
3770 real_symbol
= m2
.group(1)
3774 logging
.info("PROGRAM DOCS found in source for '%s'", real_symbol
)
3775 for param_name
, param_desc
in iteritems(params
):
3776 logging
.info("PROGRAM key %s: '%s'", real_symbol
, param_name
)
3777 param_name
= param_name
.lower()
3779 if param_name
== "short_description":
3780 key
= real_symbol
+ ":Short_Description"
3781 elif param_name
== "see_also":
3782 key
= real_symbol
+ ":See_Also"
3783 elif param_name
== "section_id":
3784 key
= real_symbol
+ ":Section_Id"
3785 elif param_name
== "synopsis":
3786 key
= real_symbol
+ ":Synopsis"
3787 elif param_name
== "returns":
3788 key
= real_symbol
+ ":Returns"
3789 elif re
.search(r
'^(-.*)', param_name
):
3790 logging
.info("PROGRAM opts: '%s': '%s'", param_name
, param_desc
)
3791 key
= real_symbol
+ ":Options"
3793 opts_str
= SourceSymbolDocs
.get(key
)
3795 opts
= opts_str
.split('\t')
3796 opts
.append(param_name
)
3797 opts
.append(param_desc
)
3799 logging
.info("Setting options for symbol: %s: '%s'", real_symbol
, '\t'.join(opts
))
3800 SourceSymbolDocs
[key
] = '\t'.join(opts
)
3804 logging
.info("PROGRAM value %s: '%s'", real_symbol
, param_desc
.rstrip())
3805 SourceSymbolDocs
[key
] = param_desc
.rstrip()
3806 SourceSymbolSourceFile
[key
] = ifile
3807 SourceSymbolSourceLine
[key
] = line_number
3809 long_descr
= real_symbol
+ ":Long_Description"
3810 SourceSymbolDocs
[long_descr
] = description
3811 SourceSymbolSourceFile
[long_descr
] = ifile
3812 SourceSymbolSourceLine
[long_descr
] = line_number
3814 section_id
= SourceSymbolDocs
.get(real_symbol
+ ":Section_Id")
3815 if section_id
and section_id
.strip() != '':
3816 # Remove trailing blanks and use as is
3817 section_id
= section_id
.rstrip()
3819 section_id
= common
.CreateValidSGMLID('%s-%s' % (MODULE
, real_symbol
))
3820 OutputProgramDBFile(real_symbol
, section_id
)
3823 logging
.info("SYMBOL DOCS found in source for : '%s'", symbol
)
3824 SourceSymbolDocs
[symbol
] = description
3825 SourceSymbolParams
[symbol
] = params
3826 SourceSymbolSourceFile
[symbol
] = ifile
3827 SourceSymbolSourceLine
[symbol
] = line_number
3830 arr
= since_desc
.splitlines()
3831 since_desc
= arr
[0].strip()
3832 extra_lines
= arr
[1:]
3833 logging
.info("Since(%s) : [%s]", symbol
, since_desc
)
3834 Since
[symbol
] = ConvertSGMLChars(symbol
, since_desc
)
3835 if len(extra_lines
) > 1:
3836 common
.LogWarning(ifile
, line_number
, "multi-line since docs found")
3839 stability_desc
= ParseStabilityLevel(
3840 stability_desc
, ifile
, line_number
, "Stability level for %s" % symbol
)
3841 StabilityLevel
[symbol
] = ConvertSGMLChars(symbol
, stability_desc
)
3844 if symbol
not in Deprecated
:
3845 # don't warn for signals and properties
3846 # if ($symbol !~ m/::?(.*)/)
3847 if symbol
in DeclarationTypes
:
3848 common
.LogWarning(ifile
, line_number
,
3849 "%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
)
3851 Deprecated
[symbol
] = ConvertSGMLChars(symbol
, deprecated_desc
)
3853 in_comment_block
= False
3856 # Get rid of ' * ' at start of every line in the comment block.
3857 line
= re
.sub(r
'^\s*\*\s?', '', line
)
3858 # But make sure we don't get rid of the newline at the end.
3859 if not line
.endswith('\n'):
3862 logging
.info("scanning :%s", line
.strip())
3864 # If we haven't found the symbol name yet, look for it.
3866 m1
= re
.search(r
'^\s*(SECTION:\s*\S+)', line
)
3867 m2
= re
.search(r
'^\s*(PROGRAM:\s*\S+)', line
)
3868 m3
= re
.search(r
'^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$', line
)
3870 symbol
= m1
.group(1)
3871 logging
.info("SECTION DOCS found in source for : '%s'", symbol
)
3873 symbol
= m2
.group(1)
3874 logging
.info("PROGRAM DOCS found in source for : '%s'", symbol
)
3876 symbol
= m3
.group(1)
3877 annotation
= m3
.group(2)
3878 logging
.info("SYMBOL DOCS found in source for : '%s'", symbol
)
3880 annotation
= annotation
.strip()
3881 if annotation
!= '':
3882 SymbolAnnotations
[symbol
] = annotation
3883 logging
.info("remaining text for %s: '%s'", symbol
, annotation
)
3887 if in_part
== "description":
3888 # Get rid of 'Description:'
3889 line
= re
.sub(r
'^\s*Description:', '', line
)
3891 m1
= re
.search(r
'^\s*(returns|return\s+value):', line
, flags
=re
.I
)
3892 m2
= re
.search(r
'^\s*since:', line
, flags
=re
.I
)
3893 m3
= re
.search(r
'^\s*deprecated:', line
, flags
=re
.I
)
3894 m4
= re
.search(r
'^\s*stability:', line
, flags
=re
.I
)
3897 # we're in param section and have not seen the blank line
3899 return_desc
= line
[m1
.end():]
3904 # we're in param section and have not seen the blank line
3905 if in_part
!= "param":
3906 since_desc
= line
[m2
.end():]
3911 # we're in param section and have not seen the blank line
3912 if in_part
!= "param":
3913 deprecated_desc
= line
[m3
.end():]
3914 in_part
= "deprecated"
3918 stability_desc
= line
[m4
.end():]
3919 in_part
= "stability"
3922 if in_part
== "description":
3925 elif in_part
== "return":
3928 elif in_part
== "since":
3931 elif in_part
== "stability":
3932 stability_desc
+= line
3934 elif in_part
== "deprecated":
3935 deprecated_desc
+= line
3938 # We must be in the parameters. Check for the empty line below them.
3939 if re
.search(r
'^\s*$', line
):
3940 in_part
= "description"
3943 # Look for a parameter name.
3944 m
= re
.search(r
'^\s*@(.+?)\s*:\s*', line
)
3946 param_name
= m
.group(1)
3947 param_desc
= line
[m
.end():]
3949 logging
.info("Found parameter: %s", param_name
)
3950 # Allow varargs variations
3951 if re
.search(r
'^\.\.\.$', param_name
):
3954 logging
.info("Found param for symbol %s : '%s'= '%s'", symbol
, param_name
, line
)
3956 params
[param_name
] = param_desc
3960 logging
.info("continuation for %s annotation '%s'", symbol
, line
)
3961 annotation
= re
.sub(r
'^\s+|\s+$', '', line
)
3962 if symbol
in SymbolAnnotations
:
3963 SymbolAnnotations
[symbol
] += annotation
3965 SymbolAnnotations
[symbol
] = annotation
3968 # We must be in the middle of a parameter description, so add it on
3969 # to the last element in @params.
3971 common
.LogWarning(file, line_number
, "Parsing comment block file : parameter expected, but got '%s'" % line
)
3973 params
[param_name
] += line
3978 def OutputMissingDocumentation():
3979 """Outputs report of documentation coverage to a file.
3982 bool: True if the report was updated
3984 old_undocumented_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-undocumented.txt")
3985 new_undocumented_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-undocumented.new")
3993 buffer_deprecated
= ''
3994 buffer_descriptions
= ''
3996 UNDOCUMENTED
= common
.open_text(new_undocumented_file
, 'w')
3998 for symbol
in sorted(iterkeys(AllSymbols
)):
3999 # FIXME: should we print common.LogWarnings for undocumented stuff?
4001 # location = "defined at " + GetSymbolSourceFile(symbol) + ":" + GetSymbolSourceLine(symbol) + "\n"
4004 r
':(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)', symbol
)
4005 m2
= re
.search(r
':(Long_Description|Short_Description)', symbol
)
4008 if symbol
in AllDocumentedSymbols
:
4010 if symbol
in AllIncompleteSymbols
:
4012 buffer += symbol
+ " (" + AllIncompleteSymbols
[symbol
] + ")\n"
4014 elif symbol
in Deprecated
:
4015 if symbol
in AllIncompleteSymbols
:
4017 buffer_deprecated
+= symbol
+ " (" + AllIncompleteSymbols
[symbol
] + ")\n"
4019 buffer_deprecated
+= symbol
+ "\n"
4022 if symbol
in AllIncompleteSymbols
:
4024 buffer += symbol
+ " (" + AllIncompleteSymbols
[symbol
] + ")\n"
4026 buffer += symbol
+ "\n"
4030 if (symbol
in SymbolDocs
and len(SymbolDocs
[symbol
]) > 0)\
4031 or (symbol
in AllDocumentedSymbols
and AllDocumentedSymbols
[symbol
] > 0):
4034 buffer_descriptions
+= symbol
+ "\n"
4039 percent
= (n_documented
/ total
) * 100.0
4041 UNDOCUMENTED
.write("%.0f%% symbol docs coverage.\n" % percent
)
4042 UNDOCUMENTED
.write("%s symbols documented.\n" % n_documented
)
4043 UNDOCUMENTED
.write("%s symbols incomplete.\n" % n_incomplete
)
4044 UNDOCUMENTED
.write("%d not documented.\n" % (total
- n_documented
))
4046 if buffer_deprecated
!= '':
4047 buffer += "\n" + buffer_deprecated
4049 if buffer_descriptions
!= '':
4050 buffer += "\n" + buffer_descriptions
4053 UNDOCUMENTED
.write("\n\n" + buffer)
4055 UNDOCUMENTED
.close()
4057 return common
.UpdateFileIfChanged(old_undocumented_file
, new_undocumented_file
, 0)
4060 def OutputUndeclaredSymbols():
4061 """Reports undeclared symbols.
4063 Outputs symbols that are listed in the section file, but have no declaration
4067 bool: True if the report was updated
4069 old_undeclared_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-undeclared.txt")
4070 new_undeclared_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-undeclared.new")
4072 UNDECLARED
= common
.open_text(new_undeclared_file
, 'w')
4074 if UndeclaredSymbols
:
4075 UNDECLARED
.write("\n".join(sorted(iterkeys(UndeclaredSymbols
))))
4076 UNDECLARED
.write("\n")
4077 print("See %s-undeclared.txt for the list of undeclared symbols." % MODULE
)
4081 return common
.UpdateFileIfChanged(old_undeclared_file
, new_undeclared_file
, 0)
4084 def OutputUnusedSymbols():
4085 """Reports unused documentation.
4087 Outputs symbols that are documented in comments, but not declared in the
4091 bool: True if the report was updated
4094 old_unused_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-unused.txt")
4095 new_unused_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-unused.new")
4097 UNUSED
= common
.open_text(new_unused_file
, 'w')
4099 for symbol
in sorted(iterkeys(Declarations
)):
4100 if symbol
not in DeclarationOutput
:
4101 UNUSED
.write("%s\n" % symbol
)
4104 for symbol
in sorted(iterkeys(AllUnusedSymbols
)):
4105 UNUSED
.write(symbol
+ "(" + AllUnusedSymbols
[symbol
] + ")\n")
4111 old_unused_file
, 1, "%d unused declarations. They should be added to %s-sections.txt in the appropriate place." % (num_unused
, MODULE
))
4113 return common
.UpdateFileIfChanged(old_unused_file
, new_unused_file
, 0)
4116 def OutputAllSymbols():
4117 """Outputs list of all symbols to a file."""
4118 SYMBOLS
= common
.open_text(os
.path
.join(ROOT_DIR
, MODULE
+ "-symbols.txt"), 'w')
4120 for symbol
in sorted(iterkeys(AllSymbols
)):
4121 SYMBOLS
.write(symbol
+ "\n")
4125 def OutputSymbolsWithoutSince():
4126 """Outputs list of all symbols without a since tag to a file."""
4127 SYMBOLS
= common
.open_text(os
.path
.join(ROOT_DIR
, MODULE
+ "-nosince.txt"), 'w')
4129 for symbol
in sorted(iterkeys(SourceSymbolDocs
)):
4131 SYMBOLS
.write(symbol
+ "\n")
4135 def CheckParamsDocumented(symbol
, params
):
4136 stype
= DeclarationTypes
.get(symbol
)
4140 if stype
== 'STRUCT':
4142 elif stype
== 'ENUM':
4144 elif stype
== 'UNION':
4148 logging
.info("Check param docs for %s, params: %s entries, type=%s", symbol
, len(params
), stype
)
4151 logging
.info("params: %s", str(params
))
4152 for (param_name
, param_desc
) in iteritems(params
):
4153 # Output a warning if the parameter is empty and remember for stats.
4154 if param_name
!= "void" and not re
.search(r
'\S', param_desc
):
4155 if symbol
in AllIncompleteSymbols
:
4156 AllIncompleteSymbols
[symbol
] += ", " + param_name
4158 AllIncompleteSymbols
[symbol
] = param_name
4160 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
4161 "%s description for %s::%s is missing in source code comment block." % (item
, symbol
, param_name
))
4163 elif len(params
) == 0:
4164 AllIncompleteSymbols
[symbol
] = "<items>"
4165 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
4166 "%s descriptions for %s are missing in source code comment block." % (item
, symbol
))
4169 def MergeSourceDocumentation():
4170 """Merges documentation read from a source file.
4172 Parameter descriptions override any in the template files.
4173 Function descriptions are placed before any description from
4177 # add whats found in the source
4178 symbols
= set(iterkeys(SourceSymbolDocs
))
4180 # and add known symbols from -sections.txt
4181 for symbol
in iterkeys(KnownSymbols
):
4182 if KnownSymbols
[symbol
] == 1:
4185 logging
.info("num source entries: %d", len(symbols
))
4187 for symbol
in symbols
:
4188 AllSymbols
[symbol
] = 1
4190 if symbol
in SourceSymbolDocs
:
4191 logging
.info("merging [%s] from source", symbol
)
4193 # remove leading and training whitespaces
4194 src_docs
= SourceSymbolDocs
[symbol
].strip()
4196 AllDocumentedSymbols
[symbol
] = 1
4198 SymbolDocs
[symbol
] = src_docs
4201 if symbol
in SourceSymbolParams
:
4202 param_docs
= SourceSymbolParams
[symbol
]
4203 SymbolParams
[symbol
] = param_docs
4204 # if this symbol is documented, check if docs are complete
4205 # remove all xml-tags and whitespaces
4206 check_docs
= re
.sub(r
'\s', '', re
.sub(r
'<.*?>', '', src_docs
))
4207 if check_docs
!= '' and param_docs
:
4208 CheckParamsDocumented(symbol
, param_docs
)
4210 logging
.info("[%s] undocumented", symbol
)
4212 logging
.info("num doc entries: %d", len(SymbolDocs
))
4215 def IsEmptyDoc(doc
):
4216 """Check if a doc-string is empty.
4218 It is also regarded as empty if it only consist of whitespace or e.g. FIXME.
4221 doc (str): the doc-string
4226 if re
.search(r
'^\s*$', doc
):
4228 if re
.search(r
'^\s*<para>\s*(FIXME)?\s*<\/para>\s*$', doc
):
4233 def ConvertMarkDown(symbol
, text
):
4235 return md_to_db
.MarkDownParse(text
, symbol
)
4238 def ReadDeclarationsFile(ifile
, override
):
4239 """Reads in a file containing the function/macro/enum etc. declarations.
4241 Note that in some cases there are several declarations with
4242 the same name, e.g. for conditional macros. In this case we
4243 set a flag in the DeclarationConditional hash so the
4244 declaration is not shown in the docs.
4246 If a macro and a function have the same name, e.g. for
4247 g_object_ref, the function declaration takes precedence.
4249 Some opaque structs are just declared with 'typedef struct
4250 _name name;' in which case the declaration may be empty.
4251 The structure may have been found later in the header, so
4252 that overrides the empty declaration.
4255 file (str): the declarations file to read
4256 override (bool): if declarations in this file should override
4257 any current declaration.
4260 Declarations
.clear()
4261 DeclarationTypes
.clear()
4262 DeclarationConditional
.clear()
4263 DeclarationOutput
.clear()
4265 INPUT
= common
.open_text(ifile
)
4266 declaration_type
= ''
4267 declaration_name
= None
4273 # logging.debug("%s:%d: %s", ifile, line_number, line)
4274 if not declaration_type
:
4275 m1
= re
.search(r
'^<([^>]+)>', line
)
4277 declaration_type
= m1
.group(1)
4278 declaration_name
= ''
4279 logging
.info("Found declaration: %s", declaration_type
)
4282 m2
= re
.search(r
'^<NAME>(.*)</NAME>', line
)
4283 m3
= re
.search(r
'^<DEPRECATED/>', line
)
4284 m4
= re
.search(r
'^</%s>' % declaration_type
, line
)
4286 declaration_name
= m2
.group(1)
4288 is_deprecated
= True
4290 logging
.info("Found end of declaration: %s, %s", declaration_type
, declaration_name
)
4291 # Check that the declaration has a name
4292 if declaration_name
== '':
4293 common
.LogWarning(ifile
, line_number
, declaration_type
+ " has no name.\n")
4295 # If the declaration is an empty typedef struct _XXX XXX
4296 # set the flag to indicate the struct has a typedef.
4297 if (declaration_type
== 'STRUCT' or declaration_type
== 'UNION') \
4298 and re
.search(r
'^\s*$', declaration
):
4299 logging
.info("Struct has typedef: %s", declaration_name
)
4300 StructHasTypedef
[declaration_name
] = 1
4302 # Check if the symbol is already defined.
4303 if declaration_name
in Declarations
and override
== 0:
4304 # Function declarations take precedence.
4305 if DeclarationTypes
[declaration_name
] == 'FUNCTION':
4308 elif declaration_type
== 'FUNCTION':
4310 Deprecated
[declaration_name
] = ''
4312 Declarations
[declaration_name
] = declaration
4313 DeclarationTypes
[declaration_name
] = declaration_type
4314 elif DeclarationTypes
[declaration_name
] == declaration_type
:
4315 # If the existing declaration is empty, or is just a
4316 # forward declaration of a struct, override it.
4317 if declaration_type
== 'STRUCT' or declaration_type
== 'UNION':
4318 if re
.search(r
'^\s*((struct|union)\s+\w+\s*;)?\s*$', Declarations
[declaration_name
]):
4320 Deprecated
[declaration_name
] = ''
4321 Declarations
[declaration_name
] = declaration
4322 elif re
.search(r
'^\s*((struct|union)\s+\w+\s*;)?\s*$', declaration
):
4323 # Ignore an empty or forward declaration.
4327 ifile
, line_number
, "Structure %s has multiple definitions." % declaration_name
)
4330 # set flag in %DeclarationConditional hash for
4331 # multiply defined macros/typedefs.
4332 DeclarationConditional
[declaration_name
] = 1
4335 common
.LogWarning(ifile
, line_number
, declaration_name
+ " has multiple definitions.")
4339 Deprecated
[declaration_name
] = ''
4341 Declarations
[declaration_name
] = declaration
4342 DeclarationTypes
[declaration_name
] = declaration_type
4343 logging
.debug("added declaration: %s, %s, [%s]", declaration_type
, declaration_name
, declaration
)
4345 declaration_type
= ''
4346 is_deprecated
= False
4352 def ReadSignalsFile(ifile
):
4353 """Reads information about object signals.
4355 It creates the arrays @SignalNames and @SignalPrototypes containing details
4356 about the signals. The first line of the SignalPrototype is the return type
4357 of the signal handler. The remaining lines are the parameters passed to it.
4358 The last parameter, "gpointer user_data" is always the same so is not included.
4361 ifile (str): the file containing the signal handler prototype information.
4364 signal_object
= None
4366 signal_returns
= None
4368 signal_prototype
= None
4370 # Reset the signal info.
4371 SignalObjects
[:] = []
4373 SignalReturns
[:] = []
4375 SignalPrototypes
[:] = []
4377 if not os
.path
.isfile(ifile
):
4380 INPUT
= common
.open_text(ifile
)
4385 if re
.search(r
'^<SIGNAL>', line
):
4390 signal_prototype
= ''
4393 m
= re
.search(r
'^<NAME>(.*)<\/NAME>', line
)
4394 m2
= re
.search(r
'^<RETURNS>(.*)<\/RETURNS>', line
)
4395 m3
= re
.search(r
'^<FLAGS>(.*)<\/FLAGS>', line
)
4397 signal_name
= m
.group(1)
4398 m1_2
= re
.search(r
'^(.*)::(.*)$', signal_name
)
4400 signal_object
= m1_2
.group(1)
4401 signal_name
= m1_2
.group(2).replace('_', '-')
4402 logging
.info("Found signal: %s", signal_name
)
4404 common
.LogWarning(ifile
, line_number
, "Invalid signal name: %s." % signal_name
)
4407 signal_returns
= m2
.group(1)
4409 signal_flags
= m3
.group(1)
4410 elif re
.search(r
'^</SIGNAL>', line
):
4411 logging
.info("Found end of signal: %s::%s\nReturns: %s\n%s",
4412 signal_object
, signal_name
, signal_returns
, signal_prototype
)
4413 SignalObjects
.append(signal_object
)
4414 SignalNames
.append(signal_name
)
4415 SignalReturns
.append(signal_returns
)
4416 SignalFlags
.append(signal_flags
)
4417 SignalPrototypes
.append(signal_prototype
)
4420 signal_prototype
+= line
4424 def ReadObjectHierarchy(ifile
):
4425 """Reads the $MODULE-hierarchy.txt file.
4427 This contains all the GObject subclasses described in this module (and their
4429 It places them in the Objects array, and places their level
4430 in the object hierarchy in the ObjectLevels array, at the
4431 same index. GObject, the root object, has a level of 1.
4433 This also generates tree_index.sgml as it goes along.
4436 ifile (str): the input filename.
4440 ObjectLevels
[:] = []
4442 if not os
.path
.isfile(ifile
):
4443 logging
.debug('no *-hierarchy.tx')
4446 INPUT
= common
.open_text(ifile
)
4448 # Only emit objects if they are supposed to be documented, or if
4449 # they have documented children. To implement this, we maintain a
4450 # stack of pending objects which will be emitted if a documented
4452 pending_objects
= []
4457 m1
= re
.search(r
'\S+', line
)
4461 gobject
= m1
.group(0)
4462 level
= len(line
[:m1
.start()]) // 2 + 1
4467 while pending_levels
and pending_levels
[-1] >= level
:
4468 pending_objects
.pop()
4469 pending_levels
.pop()
4471 pending_objects
.append(gobject
)
4472 pending_levels
.append(level
)
4474 if gobject
in KnownSymbols
:
4475 while len(pending_levels
) > 0:
4476 gobject
= pending_objects
.pop(0)
4477 level
= pending_levels
.pop(0)
4478 xref
= MakeXRef(gobject
)
4480 tree
.append(' ' * (level
* 4) + xref
)
4481 Objects
.append(gobject
)
4482 ObjectLevels
.append(level
)
4483 ObjectRoots
[gobject
] = root
4485 # common.LogWarning(ifile, line_number, "unknown type %s" % object)
4491 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml"
4492 old_tree_index
= os
.path
.join(DB_OUTPUT_DIR
, "tree_index.sgml")
4493 new_tree_index
= os
.path
.join(DB_OUTPUT_DIR
, "tree_index.new")
4495 logging
.debug('got %d entries for hierarchy', len(tree
))
4497 OUTPUT
= common
.open_text(new_tree_index
, 'w')
4498 OUTPUT
.write(MakeDocHeader("screen") + "\n<screen>\n" + AddTreeLineArt(tree
) + "\n</screen>\n")
4501 common
.UpdateFileIfChanged(old_tree_index
, new_tree_index
, 0)
4506 def ReadInterfaces(ifile
):
4507 """Reads the $MODULE.interfaces file.
4510 ifile (str): the input filename.
4515 if not os
.path
.isfile(ifile
):
4518 INPUT
= common
.open_text(ifile
)
4522 ifaces
= line
.split()
4523 gobject
= ifaces
.pop(0)
4524 if gobject
in KnownSymbols
and KnownSymbols
[gobject
] == 1:
4527 # filter out private interfaces, but leave foreign interfaces
4528 for iface
in ifaces
:
4529 if iface
not in KnownSymbols
or KnownSymbols
[iface
] == 1:
4530 knownIfaces
.append(iface
)
4532 Interfaces
[gobject
] = ' '.join(knownIfaces
)
4533 logging
.info("Interfaces for %s: %s", gobject
, Interfaces
[gobject
])
4535 logging
.info("skipping interfaces for unknown symbol: %s", gobject
)
4540 def ReadPrerequisites(ifile
):
4541 """This reads in the $MODULE.prerequisites file.
4544 ifile (str): the input filename.
4546 Prerequisites
.clear()
4548 if not os
.path
.isfile(ifile
):
4551 INPUT
= common
.open_text(ifile
)
4555 prereqs
= line
.split()
4556 iface
= prereqs
.pop(0)
4557 if iface
in KnownSymbols
and KnownSymbols
[iface
] == 1:
4560 # filter out private prerequisites, but leave foreign prerequisites
4561 for prereq
in prereqs
:
4562 if prereq
not in KnownSymbols
or KnownSymbols
[prereq
] == 1:
4563 knownPrereqs
.append(prereq
)
4565 Prerequisites
[iface
] = ' '.join(knownPrereqs
)
4570 def ReadArgsFile(ifile
):
4571 """Reads information about object properties
4573 It creates the arrays ArgObjects, ArgNames, ArgTypes, ArgFlags, ArgNicks and
4574 ArgBlurbs containing info on the args.
4577 ifile (str): the input filename.
4589 # Reset the args info.
4599 if not os
.path
.isfile(ifile
):
4602 INPUT
= common
.open_text(ifile
)
4607 if line
.startswith('<ARG>'):
4619 m1
= re
.search(r
'^<NAME>(.*)</NAME>', line
)
4620 m2
= re
.search(r
'^<TYPE>(.*)</TYPE>', line
)
4621 m3
= re
.search(r
'^<RANGE>(.*)</RANGE>', line
)
4622 m4
= re
.search(r
'^<FLAGS>(.*)</FLAGS>', line
)
4623 m5
= re
.search(r
'^<NICK>(.*)</NICK>', line
)
4624 m6
= re
.search(r
'^<BLURB>(.*)</BLURB>', line
)
4625 m7
= re
.search(r
'^<DEFAULT>(.*)</DEFAULT>', line
)
4627 arg_name
= m1
.group(1)
4628 m1_1
= re
.search(r
'^(.*)::(.*)$', arg_name
)
4630 arg_object
= m1_1
.group(1)
4631 arg_name
= m1_1
.group(2).replace('_', '-')
4632 logging
.info("Found arg: %s", arg_name
)
4634 common
.LogWarning(ifile
, line_number
, "Invalid argument name: " + arg_name
)
4637 arg_type
= m2
.group(1)
4639 arg_range
= m3
.group(1)
4641 arg_flags
= m4
.group(1)
4643 arg_nick
= m5
.group(1)
4645 arg_blurb
= m6
.group(1)
4646 if arg_blurb
== "(null)":
4649 ifile
, line_number
, "Property %s:%s has no documentation." % (arg_object
, arg_name
))
4652 arg_default
= m7
.group(1)
4653 elif re
.search(r
'^</ARG>', line
):
4654 logging
.info("Found end of arg: %s::%s\n%s : %s", arg_object
, arg_name
, arg_type
, arg_flags
)
4655 ArgObjects
.append(arg_object
)
4656 ArgNames
.append(arg_name
)
4657 ArgTypes
.append(arg_type
)
4658 ArgRanges
.append(arg_range
)
4659 ArgFlags
.append(arg_flags
)
4660 ArgNicks
.append(arg_nick
)
4661 ArgBlurbs
.append(arg_blurb
)
4662 ArgDefaults
.append(arg_default
)
4668 def AddTreeLineArt(tree
):
4669 """Generate a line art tree.
4671 Add unicode lineart to a pre-indented string array and returns
4672 it as as multiline string.
4675 tree (list): of indented strings.
4678 str: multiline string with tree line art
4680 # iterate bottom up over the tree
4681 for i
in range(len(tree
) - 1, -1, -1):
4682 # count leading spaces
4683 m
= re
.search(r
'^([^<A-Za-z]*)', tree
[i
])
4684 indent
= len(m
.group(1))
4685 # replace with ╰───, if place of ╰ is not space insert ├
4687 if tree
[i
][indent
- 4] == " ":
4688 tree
[i
] = tree
[i
][:indent
- 4] + "--- " + tree
[i
][indent
:]
4690 tree
[i
] = tree
[i
][:indent
- 4] + "+-- " + tree
[i
][indent
:]
4692 # go lines up while space and insert |
4694 while j
>= 0 and tree
[j
][indent
- 4] == ' ':
4695 tree
[j
] = tree
[j
][:indent
- 4] + '|' + tree
[j
][indent
- 3:]
4698 res
= "\n".join(tree
)
4699 # unicode chars for: ╰──
4700 res
= re
.sub(r
'---', '<phrase role=\"lineart\">╰──</phrase>', res
)
4701 # unicde chars for: ├──
4702 res
= re
.sub(r
'\+--', '<phrase role=\"lineart\">├──</phrase>', res
)
4703 # unicode char for: │
4704 res
= re
.sub(r
'\|', '<phrase role=\"lineart\">│</phrase>', res
)
4709 def CheckIsObject(name
):
4710 """Check if symbols is an object.
4712 It uses the global Objects array. Note that the Objects array only contains
4713 classes in the current module and their ancestors - not all GObject classes.
4716 name (str): the object name to check.
4719 bool: True if the given name is a GObject or a subclass.
4721 root
= ObjectRoots
.get(name
)
4722 # Let GBoxed pass as an object here to get -struct appended to the id
4723 # and prevent conflicts with sections.
4724 return root
and root
!= 'GEnum' and root
!= 'GFlags'
4727 def GetSymbolSourceFile(symbol
):
4728 """Get the filename where the symbol docs where taken from."""
4729 return SourceSymbolSourceFile
.get(symbol
, '')
4732 def GetSymbolSourceLine(symbol
):
4733 """Get the file line where the symbol docs where taken from."""
4734 return SourceSymbolSourceLine
.get(symbol
, 0)