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 collections
import OrderedDict
32 from . import common
, md_to_db
37 INLINE_MARKUP_MODE
= None
38 DEFAULT_STABILITY
= None
42 # These global arrays store information on signals. Each signal has an entry
43 # in each of these arrays at the same index, like a multi-dimensional array.
44 SignalObjects
= [] # The GtkObject which emits the signal.
45 SignalNames
= [] # The signal name.
46 SignalReturns
= [] # The return type.
47 SignalFlags
= [] # Flags for the signal
48 SignalPrototypes
= [] # The rest of the prototype of the signal handler.
50 # These global arrays store information on Args. Each Arg has an entry
51 # in each of these arrays at the same index, like a multi-dimensional array.
52 ArgObjects
= [] # The GtkObject which has the Arg.
53 ArgNames
= [] # The Arg name.
54 ArgTypes
= [] # The Arg type - gint, GtkArrowType etc.
55 ArgFlags
= [] # How the Arg can be used - readable/writable etc.
56 ArgNicks
= [] # The nickname of the Arg.
57 ArgBlurbs
= [] # Docstring of the Arg.
58 ArgDefaults
= [] # Default value of the Arg.
59 ArgRanges
= [] # The range of the Arg type
61 # These global hashes store declaration info keyed on a symbol name.
64 DeclarationConditional
= {}
65 DeclarationOutput
= {}
71 # These global hashes store the existing documentation.
74 SymbolAnnotations
= {}
76 # These global hashes store documentation scanned from the source files.
78 SourceSymbolParams
= {}
79 SourceSymbolSourceFile
= {}
80 SourceSymbolSourceLine
= {}
82 # all documentation goes in here, so we can do coverage analysis
84 AllIncompleteSymbols
= {}
86 AllDocumentedSymbols
= {}
88 # Undeclared yet documented symbols
89 UndeclaredSymbols
= {}
91 # These global arrays store GObject, subclasses and the hierarchy (also of
92 # non-object derived types).
100 # holds the symbols which are mentioned in <MODULE>-sections.txt and in which
101 # section they are defined
106 # collects index entries
107 IndexEntriesFull
= {}
108 IndexEntriesSince
= {}
109 IndexEntriesDeprecated
= {}
111 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
112 PreProcessorDirectives
= {
113 'assert', 'define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef',
114 'include', 'line', 'pragma', 'unassert', 'undef', 'warning'
117 # remember used annotation (to write minimal glossary)
120 # the regexp that parses the annotation is in ScanSourceFile()
121 AnnotationDefinition
= {
122 # the GObjectIntrospection annotations are defined at:
123 # https://live.gnome.org/GObjectIntrospection/Annotations
124 'allow-none': "NULL is OK, both for passing and for returning.",
125 'nullable': "NULL may be passed as the value in, out, in-out; or as a return value.",
126 'not nullable': "NULL must not be passed as the value in, out, in-out; or as a return value.",
127 'optional': "NULL may be passed instead of a pointer to a location.",
128 'not optional': "NULL must not be passed as the pointer to a location.",
129 'array': "Parameter points to an array of items.",
130 'attribute': "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
131 'attributes': "Free-form key-value pairs.",
132 'closure': "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
133 'constructor': "This symbol is a constructor, not a static method.",
134 'destroy': "This parameter is a 'destroy_data', for callbacks.",
135 'default': "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less parameters).",
136 'element-type': "Generics and defining elements of containers and arrays.",
137 'error-domains': "Typed errors. Similar to throws in Java.",
138 'foreign': "This is a foreign struct.",
139 'get-value-func': "The specified function is used to convert a struct from a GValue, must be a GTypeInstance.",
140 'in': "Parameter for input. Default is <acronym>transfer none</acronym>.",
141 'inout': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
142 'in-out': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
143 'method': "This is a method",
144 'not-error': "A GError parameter is not to be handled like a normal GError.",
145 'out': "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
146 'out caller-allocates': "Out parameter, where caller must allocate storage.",
147 'out callee-allocates': "Out parameter, where caller must allocate storage.",
148 'ref-func': "The specified function is used to ref a struct, must be a GTypeInstance.",
149 'rename-to': "Rename the original symbol's name to SYMBOL.",
150 'scope call': "The callback is valid only during the call to the method.",
151 'scope async': "The callback is valid until first called.",
152 'scope notified': "The callback is valid until the GDestroyNotify argument is called.",
153 'set-value-func': "The specified function is used to convert from a struct to a GValue, must be a GTypeInstance.",
154 'skip': "Exposed in C code, not necessarily available in other languages.",
155 'transfer container': "Free data container after the code is done.",
156 'transfer floating': "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
157 'transfer full': "Free data after the code is done.",
158 'transfer none': "Don't free data after the code is done.",
159 'type': "Override the parsed C type with given type.",
160 'unref-func': "The specified function is used to unref a struct, must be a GTypeInstance.",
161 'virtual': "This is the invoker for a virtual method.",
162 'value': "The specified value overrides the evaluated value of the constant.",
163 # Stability Level definition
164 # https://bugzilla.gnome.org/show_bug.cgi?id=170860
165 'Stable': '''The intention of a Stable interface is to enable arbitrary third parties to
166 develop applications to these interfaces, release them, and have confidence that
167 they will run on all minor releases of the product (after the one in which the
168 interface was introduced, and within the same major release). Even at a major
169 release, incompatible changes are expected to be rare, and to have strong
172 'Unstable': '''Unstable interfaces are experimental or transitional. They are typically used to
173 give outside developers early access to new or rapidly changing technology, or
174 to provide an interim solution to a problem where a more general solution is
175 anticipated. No claims are made about either source or binary compatibility from
176 one minor release to the next.
178 The Unstable interface level is a warning that these interfaces are subject to
179 change without warning and should not be used in unbundled products.
181 Given such caveats, customer impact need not be a factor when considering
182 incompatible changes to an Unstable interface in a major or minor release.
183 Nonetheless, when such changes are introduced, the changes should still be
184 mentioned in the release notes for the affected release.
186 'Private': '''An interface that can be used within the GNOME stack itself, but that is not
187 documented for end-users. Such functions should only be used in specified and
192 # Function and other declaration output settings.
193 RETURN_TYPE_FIELD_WIDTH
= 20
194 MAX_SYMBOL_FIELD_WIDTH
= 40
197 doctype_header
= None
200 REFENTRY
= string
.Template('''${header}
201 <refentry id="${section_id}">
203 <refentrytitle role="top_of_page" id="${section_id}.top_of_page">${title}</refentrytitle>
204 <manvolnum>3</manvolnum>
205 <refmiscinfo>${MODULE} Library${image}</refmiscinfo>
208 <refname>${title}</refname>
209 <refpurpose>${short_desc}</refpurpose>
212 ${functions_synop}${args_synop}${signals_synop}${object_anchors}${other_synop}${hierarchy}${prerequisites}${derived}${interfaces}${implementations}
214 <refsect1 id="${section_id}.description" role="desc">
215 <title role="desc.title">Description</title>
216 ${extralinks}${long_desc}
218 <refsect1 id="${section_id}.functions_details" role="details">
219 <title role="details.title">Functions</title>
222 <refsect1 id="${section_id}.other_details" role="details">
223 <title role="details.title">Types and Values</title>
226 ${args_desc}${signals_desc}${see_also}
232 global MODULE
, INLINE_MARKUP_MODE
, DEFAULT_STABILITY
, NAME_SPACE
, \
233 DB_OUTPUT_DIR
, doctype_header
235 logging
.info('options: %s', str(options
.__dict
__))
237 # We should pass the options variable around instead of this global variable horror
238 # but too much of the code expects these to be around. Fix this once the transition is done.
239 MODULE
= options
.module
240 INLINE_MARKUP_MODE
= options
.xml_mode
or options
.sgml_mode
241 DEFAULT_STABILITY
= options
.default_stability
242 NAME_SPACE
= options
.name_space
244 main_sgml_file
= options
.main_sgml_file
245 if not main_sgml_file
:
246 # backwards compatibility
247 if os
.path
.exists(MODULE
+ "-docs.sgml"):
248 main_sgml_file
= MODULE
+ "-docs.sgml"
250 main_sgml_file
= MODULE
+ "-docs.xml"
252 # extract docbook header or define default
253 doctype_header
= GetDocbookHeader(main_sgml_file
)
255 # This is where we put all the DocBook output.
256 DB_OUTPUT_DIR
= DB_OUTPUT_DIR
if DB_OUTPUT_DIR
else os
.path
.join(ROOT_DIR
, "xml")
257 if not os
.path
.isdir(DB_OUTPUT_DIR
):
258 os
.mkdir(DB_OUTPUT_DIR
)
260 ReadKnownSymbols(os
.path
.join(ROOT_DIR
, MODULE
+ "-sections.txt"))
261 ReadSignalsFile(os
.path
.join(ROOT_DIR
, MODULE
+ ".signals"))
262 ReadArgsFile(os
.path
.join(ROOT_DIR
, MODULE
+ ".args"))
263 ReadObjectHierarchy(os
.path
.join(ROOT_DIR
, MODULE
+ ".hierarchy"))
264 ReadInterfaces(os
.path
.join(ROOT_DIR
, MODULE
+ ".interfaces"))
265 ReadPrerequisites(os
.path
.join(ROOT_DIR
, MODULE
+ ".prerequisites"))
267 ReadDeclarationsFile(os
.path
.join(ROOT_DIR
, MODULE
+ "-decl.txt"), 0)
268 if os
.path
.isfile(os
.path
.join(ROOT_DIR
, MODULE
+ "-overrides.txt")):
269 ReadDeclarationsFile(os
.path
.join(ROOT_DIR
, MODULE
+ "-overrides.txt"), 1)
272 if options
.source_suffixes
:
273 suffix_list
= ['.' + ext
for ext
in options
.source_suffixes
.split(',')]
275 suffix_list
= ['.c', '.h']
277 source_dirs
= options
.source_dir
278 ignore_files
= options
.ignore_files
279 logging
.info(" ignore files: " + ignore_files
)
280 for sdir
in source_dirs
:
281 ReadSourceDocumentation(sdir
, suffix_list
, source_dirs
, ignore_files
)
283 changed
, book_top
, book_bottom
= OutputDB(os
.path
.join(ROOT_DIR
, MODULE
+ "-sections.txt"), options
)
284 OutputBook(main_sgml_file
, book_top
, book_bottom
)
286 logging
.info("All files created: %d", changed
)
288 # If any of the DocBook files have changed, update the timestamp file (so
289 # it can be used for Makefile dependencies).
290 if changed
or not os
.path
.exists(os
.path
.join(ROOT_DIR
, "sgml.stamp")):
292 # try to detect the common prefix
293 # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
295 NAME_SPACE
= DetermineNamespace()
297 logging
.info('namespace prefix ="%s"', NAME_SPACE
)
299 OutputIndex("api-index-full", IndexEntriesFull
)
300 OutputIndex("api-index-deprecated", IndexEntriesDeprecated
)
302 OutputAnnotationGlossary()
304 with
open(os
.path
.join(ROOT_DIR
, 'sgml.stamp'), 'w') as h
:
308 def OutputObjectList():
309 """This outputs the alphabetical list of objects, in a columned table."""
310 # FIXME: Currently this also outputs ancestor objects which may not actually
315 old_object_index
= os
.path
.join(DB_OUTPUT_DIR
, "object_index.sgml")
316 new_object_index
= os
.path
.join(DB_OUTPUT_DIR
, "object_index.new")
318 OUTPUT
= open(new_object_index
, 'w', encoding
='utf-8')
321 <informaltable pgwide="1" frame="none">
323 <colspec colwidth="1*"/>
324 <colspec colwidth="1*"/>
325 <colspec colwidth="1*"/>
327 ''' % (MakeDocHeader("informaltable"), cols
))
331 for object in sorted(Objects
):
332 xref
= MakeXRef(object)
333 if count
% cols
== 0:
334 OUTPUT
.write("<row>\n")
335 OUTPUT
.write("<entry>%s</entry>\n" % xref
)
336 if count
% cols
== cols
- 1:
337 OUTPUT
.write("</row>\n")
341 # emit an empty row, since empty tables are invalid
342 OUTPUT
.write("<row><entry> </entry></row>\n")
346 OUTPUT
.write("</row>\n")
348 OUTPUT
.write('''</tbody></tgroup></informaltable>\n''')
351 common
.UpdateFileIfChanged(old_object_index
, new_object_index
, 0)
354 def TrimTextBlock(desc
):
355 """Trims extra whitespace.
357 Empty lines inside a block are preserved.
359 desc (str): the text block to trim. May contain newlines.
362 # strip trailing spaces on every line
363 return re
.sub(r
'\s+$', '\n', desc
.lstrip(), flags
=re
.MULTILINE
)
366 def OutputDB(file, options
):
367 """Generate docbook files.
369 This collects the output for each section of the docs, and outputs each file
370 when the end of the section is found.
373 file (str): the $MODULE-sections.txt file which contains all of the
374 functions/macros/structs etc. being documented, organised
375 into sections and subsections.
376 options: commandline options
379 logging
.info("Reading: %s", file)
380 INPUT
= open(file, 'r', encoding
='utf-8')
384 includes
= options
.default_includes
or ''
385 section_includes
= ''
394 functions_details
= ''
399 child_args_synop
= ''
400 style_args_synop
= ''
414 MergeSourceDocumentation()
420 if line
.startswith('#'):
423 logging
.info("section file data: %d: %s", line_number
, line
)
425 m1
= re
.search(r
'^<SUBSECTION\s*(.*)>', line
, re
.I
)
426 m2
= re
.search(r
'^<TITLE>(.*)<\/TITLE', line
)
427 m3
= re
.search(r
'^<FILE>(.*)<\/FILE>', line
)
428 m4
= re
.search(r
'^<INCLUDE>(.*)<\/INCLUDE>', line
)
429 m5
= re
.search(r
'^(\S+)', line
)
431 if line
.startswith('<SECTION>'):
439 functions_synop
+= "\n"
440 subsection
= m1
.group(1)
442 elif line
.startswith('<SUBSECTION>'):
446 logging
.info("Section: %s", title
)
448 # We don't want warnings if object & class structs aren't used.
449 DeclarationOutput
[title
] = 1
450 DeclarationOutput
["%sClass" % title
] = 1
451 DeclarationOutput
["%sIface" % title
] = 1
452 DeclarationOutput
["%sInterface" % title
] = 1
455 filename
= m3
.group(1)
456 if filename
not in file_def_line
:
457 file_def_line
[filename
] = line_number
459 common
.LogWarning(file, line_number
, "Double <FILE>%s</FILE> entry. Previous occurrence on line %s." %
460 (filename
, file_def_line
[filename
]))
462 key
= filename
+ ":Title"
463 if key
in SourceSymbolDocs
:
464 title
= SourceSymbolDocs
[key
].rstrip()
468 section_includes
= m4
.group(1)
470 if options
.default_includes
:
471 common
.LogWarning(file, line_number
, "Default <INCLUDE> being overridden by command line option.")
473 includes
= m4
.group(1)
475 elif re
.search(r
'^<\/SECTION>', line
):
476 logging
.info("End of section: %s", title
)
477 # TODO: also output if we have sections docs?
478 # long_desc = SymbolDocs.get(filename + ":Long_Description")
481 book_bottom
+= " <xi:include href=\"xml/%s.xml\"/>\n" % filename
483 key
= filename
+ ":Include"
484 if key
in SourceSymbolDocs
:
486 common
.LogWarning(file, line_number
, "Section <INCLUDE> being overridden by inline comments.")
487 section_includes
= SourceSymbolDocs
[key
]
489 if section_includes
== '':
490 section_includes
= includes
492 signals_synop
= re
.sub(r
'^\n*', '', signals_synop
)
493 signals_synop
= re
.sub(r
'\n+$', '\n', signals_synop
)
495 if signals_synop
!= '':
496 signals_synop
= '''<refsect1 id="%s.signals" role="signal_proto">
497 <title role="signal_proto.title">Signals</title>
498 <informaltable frame="none">
500 <colspec colname="signals_return" colwidth="150px"/>
501 <colspec colname="signals_name" colwidth="300px"/>
502 <colspec colname="signals_flags" colwidth="200px"/>
509 ''' % (section_id
, signals_synop
)
510 signals_desc
= TrimTextBlock(signals_desc
)
511 signals_desc
= '''<refsect1 id="%s.signal-details" role="signals">
512 <title role="signals.title">Signal Details</title>
515 ''' % (section_id
, signals_desc
)
517 args_synop
= re
.sub(r
'^\n*', '', args_synop
)
518 args_synop
= re
.sub(r
'\n+$', '\n', args_synop
)
520 args_synop
= '''<refsect1 id="%s.properties" role="properties">
521 <title role="properties.title">Properties</title>
522 <informaltable frame="none">
524 <colspec colname="properties_type" colwidth="150px"/>
525 <colspec colname="properties_name" colwidth="300px"/>
526 <colspec colname="properties_flags" colwidth="200px"/>
533 ''' % (section_id
, args_synop
)
534 args_desc
= TrimTextBlock(args_desc
)
535 args_desc
= '''<refsect1 id="%s.property-details" role="property_details">
536 <title role="property_details.title">Property Details</title>
539 ''' % (section_id
, args_desc
)
541 child_args_synop
= re
.sub(r
'^\n*', '', child_args_synop
)
542 child_args_synop
= re
.sub(r
'\n+$', '\n', child_args_synop
)
543 if child_args_synop
!= '':
544 args_synop
+= '''<refsect1 id="%s.child-properties" role="child_properties">
545 <title role="child_properties.title">Child Properties</title>
546 <informaltable frame="none">
548 <colspec colname="child_properties_type" colwidth="150px"/>
549 <colspec colname="child_properties_name" colwidth="300px"/>
550 <colspec colname="child_properties_flags" colwidth="200px"/>
557 ''' % (section_id
, child_args_synop
)
558 child_args_desc
= TrimTextBlock(child_args_desc
)
559 args_desc
+= '''<refsect1 id="%s.child-property-details" role="child_property_details">
560 <title role="child_property_details.title">Child Property Details</title>
563 ''' % (section_id
, child_args_desc
)
565 style_args_synop
= re
.sub(r
'^\n*', '', style_args_synop
)
566 style_args_synop
= re
.sub(r
'\n+$', '\n', style_args_synop
)
567 if style_args_synop
!= '':
568 args_synop
+= '''<refsect1 id="%s.style-properties" role="style_properties">
569 <title role="style_properties.title">Style Properties</title>
570 <informaltable frame="none">
572 <colspec colname="style_properties_type" colwidth="150px"/>
573 <colspec colname="style_properties_name" colwidth="300px"/>
574 <colspec colname="style_properties_flags" colwidth="200px"/>
581 ''' % (section_id
, style_args_synop
)
582 style_args_desc
= TrimTextBlock(style_args_desc
)
583 args_desc
+= '''<refsect1 id="%s.style-property-details" role="style_properties_details">
584 <title role="style_properties_details.title">Style Property Details</title>
587 ''' % (section_id
, style_args_desc
)
589 hierarchy_str
= AddTreeLineArt(hierarchy
)
590 if hierarchy_str
!= '':
591 hierarchy_str
= '''<refsect1 id="%s.object-hierarchy" role="object_hierarchy">
592 <title role="object_hierarchy.title">Object Hierarchy</title>
596 ''' % (section_id
, hierarchy_str
)
598 interfaces
= TrimTextBlock(interfaces
)
600 interfaces
= '''<refsect1 id="%s.implemented-interfaces" role="impl_interfaces">
601 <title role="impl_interfaces.title">Implemented Interfaces</title>
604 ''' % (section_id
, interfaces
)
606 implementations
= TrimTextBlock(implementations
)
607 if implementations
!= '':
608 implementations
= '''<refsect1 id="%s.implementations" role="implementations">
609 <title role="implementations.title">Known Implementations</title>
612 ''' % (section_id
, implementations
)
614 prerequisites
= TrimTextBlock(prerequisites
)
615 if prerequisites
!= '':
616 prerequisites
= '''<refsect1 id="%s.prerequisites" role="prerequisites">
617 <title role="prerequisites.title">Prerequisites</title>
620 ''' % (section_id
, prerequisites
)
622 derived
= TrimTextBlock(derived
)
624 derived
= '''<refsect1 id="%s.derived-interfaces" role="derived_interfaces">
625 <title role="derived_interfaces.title">Known Derived Interfaces</title>
628 ''' % (section_id
, derived
)
630 functions_synop
= re
.sub(r
'^\n*', '', functions_synop
)
631 functions_synop
= re
.sub(r
'\n+$', '\n', functions_synop
)
632 if functions_synop
!= '':
633 functions_synop
= '''<refsect1 id="%s.functions" role="functions_proto">
634 <title role="functions_proto.title">Functions</title>
635 <informaltable pgwide="1" frame="none">
637 <colspec colname="functions_return" colwidth="150px"/>
638 <colspec colname="functions_name"/>
645 ''' % (section_id
, functions_synop
)
647 other_synop
= re
.sub(r
'^\n*', '', other_synop
)
648 other_synop
= re
.sub(r
'\n+$', '\n', other_synop
)
649 if other_synop
!= '':
650 other_synop
= '''<refsect1 id="%s.other" role="other_proto">
651 <title role="other_proto.title">Types and Values</title>
652 <informaltable role="enum_members_table" pgwide="1" frame="none">
654 <colspec colname="name" colwidth="150px"/>
655 <colspec colname="description"/>
662 ''' % (section_id
, other_synop
)
664 file_changed
= OutputDBFile(filename
, title
, section_id
,
666 functions_synop
, other_synop
,
667 functions_details
, other_details
,
668 signals_synop
, signals_desc
,
669 args_synop
, args_desc
,
670 hierarchy_str
, interfaces
,
672 prerequisites
, derived
,
681 section_includes
= ''
684 functions_details
= ''
689 child_args_synop
= ''
690 style_args_synop
= ''
703 logging
.info(' Symbol: "%s" in subsection: "%s"', symbol
, subsection
)
705 # check for duplicate entries
706 if symbol
not in symbol_def_line
:
707 declaration
= Declarations
.get(symbol
)
708 # FIXME: with this we'll output empty declaration
709 if declaration
is not None:
710 if CheckIsObject(symbol
):
711 file_objects
.append(symbol
)
713 # We don't want standard macros/functions of GObjects,
714 # or private declarations.
715 if subsection
!= "Standard" and subsection
!= "Private":
716 synop
, desc
= OutputDeclaration(symbol
, declaration
)
717 type = DeclarationTypes
[symbol
]
719 if type == 'FUNCTION' or type == 'USER_FUNCTION':
720 functions_synop
+= synop
721 functions_details
+= desc
722 elif type == 'MACRO' and re
.search(symbol
+ r
'\(', declaration
):
723 functions_synop
+= synop
724 functions_details
+= desc
727 other_details
+= desc
729 sig_synop
, sig_desc
= GetSignals(symbol
)
730 arg_synop
, child_arg_synop
, style_arg_synop
, arg_desc
, child_arg_desc
, style_arg_desc
= GetArgs(
732 ifaces
= GetInterfaces(symbol
)
733 impls
= GetImplementations(symbol
)
734 prereqs
= GetPrerequisites(symbol
)
735 der
= GetDerived(symbol
)
736 hierarchy
= GetHierarchy(symbol
, hierarchy
)
738 signals_synop
+= sig_synop
739 signals_desc
+= sig_desc
740 args_synop
+= arg_synop
741 child_args_synop
+= child_arg_synop
742 style_args_synop
+= style_arg_synop
743 args_desc
+= arg_desc
744 child_args_desc
+= child_arg_desc
745 style_args_desc
+= style_arg_desc
747 implementations
+= impls
748 prerequisites
+= prereqs
751 # Note that the declaration has been output.
752 DeclarationOutput
[symbol
] = True
753 elif subsection
!= "Standard" and subsection
!= "Private":
754 UndeclaredSymbols
[symbol
] = True
755 common
.LogWarning(file, line_number
, "No declaration found for %s." % symbol
)
758 symbol_def_line
[symbol
] = line_number
761 if title
== '' and filename
== '':
762 common
.LogWarning(file, line_number
, "Section has no title and no file.")
764 # FIXME: one of those would be enough
765 # filename should be an internal detail for gtk-doc
771 filename
= filename
.replace(' ', '_')
773 section_id
= SourceSymbolDocs
.get(filename
+ ":Section_Id")
774 if section_id
and section_id
.strip() != '':
775 # Remove trailing blanks and use as is
776 section_id
= section_id
.rstrip()
777 elif CheckIsObject(title
):
778 # GObjects use their class name as the ID.
779 section_id
= common
.CreateValidSGMLID(title
)
781 section_id
= common
.CreateValidSGMLID(MODULE
+ '-' + title
)
783 SymbolSection
[symbol
] = title
784 SymbolSectionId
[symbol
] = section_id
787 common
.LogWarning(file, line_number
, "Double symbol entry for %s. "
788 "Previous occurrence on line %d." % (symbol
, symbol_def_line
[symbol
]))
791 OutputMissingDocumentation()
792 OutputUndeclaredSymbols()
793 OutputUnusedSymbols()
795 if options
.outputallsymbols
:
798 if options
.outputsymbolswithoutsince
:
799 OutputSymbolsWithoutSince()
801 for filename
in options
.expand_content_files
.split():
802 file_changed
= OutputExtraFile(filename
)
806 return (changed
, book_top
, book_bottom
)
809 def DetermineNamespace():
810 """Find common set of characters."""
817 for symbol
in IndexEntriesFull
.keys():
818 if name_space
== '' or name_space
.lower() in symbol
.lower():
819 if len(symbol
) > pos
:
820 letter
= symbol
[pos
:pos
+ 1]
821 # stop prefix scanning
825 # Should we also stop on a uppercase char, if last was lowercase
826 # GtkWidget, if we have the 'W' and had the 't' before
827 # or should we count upper and lowercase, and stop one 2nd uppercase, if we already had a lowercase
828 # GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had lowercase chars before
829 # need to recound each time as this is per symbol
836 if letter
!= '' and letter
!= "_":
839 for letter
in prefix
.keys():
840 logging
.debug("ns prefix: %s: %s", letter
, prefix
[letter
])
841 if prefix
[letter
] > maxsymbols
:
843 maxsymbols
= prefix
[letter
]
845 ratio
= float(len(IndexEntriesFull
)) / prefix
[maxletter
]
846 logging
.debug('most symbols start with %s, that is %f', maxletter
, (100 * ratio
))
849 name_space
+= maxletter
861 def OutputIndex(basename
, apiindex
):
862 """Writes an index that can be included into the main-document into an <index> tag.
865 basename (str): name of the index file without extension
866 apiindex (dict): the index data
868 old_index
= os
.path
.join(DB_OUTPUT_DIR
, basename
+ '.xml')
869 new_index
= os
.path
.join(DB_OUTPUT_DIR
, basename
+ '.new')
875 OUTPUT
= open(new_index
, 'w')
877 OUTPUT
.write(MakeDocHeader("indexdiv") + "\n<indexdiv id=\"%s\">\n" % basename
)
879 logging
.info("generate %s index (%d entries) with namespace %s", basename
, len(apiindex
), NAME_SPACE
)
881 # do a case insensitive sort while chopping off the prefix
885 'short': re
.sub(r
'^' + NAME_SPACE
+ r
'\_?(.*)', r
'\1', x
.upper(), flags
=re
.I
),
886 } for x
in apiindex
.keys()]
887 sorted_keys
= sorted(mapped_keys
, key
=lambda d
: (d
['short'], d
['original']))
889 for key
in sorted_keys
:
890 symbol
= key
['original']
895 short_symbol
= symbol
897 # generate a short symbol description
900 symbol_section_id
= ''
902 if symbol
in DeclarationTypes
:
903 symbol_type
= DeclarationTypes
[symbol
].lower()
905 if symbol_type
== '':
906 logging
.info("trying symbol %s", symbol
)
907 m1
= re
.search(r
'(.*)::(.*)', symbol
)
908 m2
= re
.search(r
'(.*):(.*)', symbol
)
912 logging
.info(" trying object signal %s:%s in %d signals", oname
, osym
, len(SignalNames
))
913 for name
in SignalNames
:
914 logging
.info(" " + name
)
916 symbol_type
= "object signal"
917 if oname
in SymbolSection
:
918 symbol_section
= SymbolSection
[oname
]
919 symbol_section_id
= SymbolSectionId
[oname
]
924 logging
.info(" trying object property %s::%s in %d properties", oname
, osym
, len(ArgNames
))
925 for name
in ArgNames
:
926 logging
.info(" " + name
)
928 symbol_type
= "object property"
929 if oname
in SymbolSection
:
930 symbol_section
= SymbolSection
[oname
]
931 symbol_section_id
= SymbolSectionId
[oname
]
934 if symbol
in SymbolSection
:
935 symbol_section
= SymbolSection
[symbol
]
936 symbol_section_id
= SymbolSectionId
[symbol
]
938 if symbol_type
!= '':
939 symbol_desc
= ", " + symbol_type
940 if symbol_section
!= '':
941 symbol_desc
+= " in <link linkend=\"%s\">%s</link>" % (symbol_section_id
, symbol_section
)
942 # symbol_desc +=" in " + ExpandAbbreviations(symbol, "#symbol_section")
944 curletter
= short_symbol
[0].upper()
945 ixid
= apiindex
[symbol
]
947 logging
.info(" add symbol %s with %s to index in section '%s' (derived from %s)",
948 symbol
, ixid
, curletter
, short_symbol
)
950 if curletter
!= lastletter
:
951 lastletter
= curletter
954 OUTPUT
.write("</indexdiv>\n")
956 OUTPUT
.write("<indexdiv><title>%s</title>\n" % curletter
)
959 OUTPUT
.write('<indexentry><primaryie linkends="%s"><link linkend="%s">%s</link>%s</primaryie></indexentry>\n' %
960 (ixid
, ixid
, symbol
, symbol_desc
))
963 OUTPUT
.write("</indexdiv>\n")
965 OUTPUT
.write("</indexdiv>\n")
968 common
.UpdateFileIfChanged(old_index
, new_index
, 0)
971 def OutputSinceIndexes():
972 """Generate the 'since' api index files."""
973 for version
in set(Since
.values()):
974 logging
.info("Since : [%s]", version
)
975 index
= {x
: IndexEntriesSince
[x
] for x
in IndexEntriesSince
.keys() if Since
[x
] == version
}
976 OutputIndex("api-index-" + version
, index
)
979 def OutputAnnotationGlossary():
980 """Writes a glossary of the used annotation terms.
982 The glossary file can be included into the main document.
984 # if there are no annotations used return
985 if not AnnotationsUsed
:
988 old_glossary
= os
.path
.join(DB_OUTPUT_DIR
, "annotation-glossary.xml")
989 new_glossary
= os
.path
.join(DB_OUTPUT_DIR
, "annotation-glossary.new")
993 # add acronyms that are referenced from acronym text
997 for annotation
in AnnotationsUsed
:
998 if annotation
not in AnnotationDefinition
:
1000 m
= re
.search(r
'<acronym>([\w ]+)<\/acronym>', AnnotationDefinition
[annotation
])
1001 if m
and m
.group(1) not in AnnotationsUsed
:
1002 AnnotationsUsed
[m
.group(1)] = 1
1006 OUTPUT
= open(new_glossary
, 'w', encoding
='utf-8')
1009 <glossary id="annotation-glossary">
1010 <title>Annotation Glossary</title>
1011 ''' % MakeDocHeader("glossary"))
1013 for annotation
in sorted(AnnotationsUsed
.keys(), key
=str.lower
):
1014 if annotation
in AnnotationDefinition
:
1015 definition
= AnnotationDefinition
[annotation
]
1016 curletter
= annotation
[0].upper()
1018 if curletter
!= lastletter
:
1019 lastletter
= curletter
1022 OUTPUT
.write("</glossdiv>\n")
1024 OUTPUT
.write("<glossdiv><title>%s</title>\n" % curletter
)
1027 OUTPUT
.write(''' <glossentry>
1028 <glossterm><anchor id="annotation-glossterm-%s"/>%s</glossterm>
1033 ''' % (annotation
, annotation
, definition
))
1036 OUTPUT
.write("</glossdiv>\n")
1038 OUTPUT
.write("</glossary>\n")
1041 common
.UpdateFileIfChanged(old_glossary
, new_glossary
, 0)
1044 def ReadKnownSymbols(file):
1045 """Collect the names of non-private symbols from the $MODULE-sections.txt file.
1048 file: the $MODULE-sections.txt file
1053 logging
.info("Reading: %s", file)
1054 INPUT
= open(file, 'r', encoding
='utf-8')
1056 if line
.startswith('#'):
1059 if line
.startswith('<SECTION>'):
1063 m
= re
.search(r
'^<SUBSECTION\s*(.*)>', line
, flags
=re
.I
)
1065 subsection
= m
.group(1)
1068 if line
.startswith('<SUBSECTION>'):
1071 if re
.search(r
'^<TITLE>(.*)<\/TITLE>', line
):
1074 m
= re
.search(r
'^<FILE>(.*)<\/FILE>', line
)
1076 KnownSymbols
[m
.group(1) + ":Long_Description"] = 1
1077 KnownSymbols
[m
.group(1) + ":Short_Description"] = 1
1080 m
= re
.search(r
'^<INCLUDE>(.*)<\/INCLUDE>', line
)
1084 m
= re
.search(r
'^<\/SECTION>', line
)
1088 m
= re
.search(r
'^(\S+)', line
)
1091 if subsection
!= "Standard" and subsection
!= "Private":
1092 KnownSymbols
[symbol
] = 1
1094 KnownSymbols
[symbol
] = 0
1098 def OutputDeclaration(symbol
, declaration
):
1099 """Returns the formatted documentation block for a symbol.
1102 symbol (str): the name of the function/macro/...
1103 declaration (str): the declaration of the function/macro.
1106 str: the formatted documentation
1109 dtype
= DeclarationTypes
[symbol
]
1110 if dtype
== 'MACRO':
1111 return OutputMacro(symbol
, declaration
)
1112 elif dtype
== 'TYPEDEF':
1113 return OutputTypedef(symbol
, declaration
)
1114 elif dtype
== 'STRUCT':
1115 return OutputStruct(symbol
, declaration
)
1116 elif dtype
== 'ENUM':
1117 return OutputEnum(symbol
, declaration
)
1118 elif dtype
== 'UNION':
1119 return OutputUnion(symbol
, declaration
)
1120 elif dtype
== 'VARIABLE':
1121 return OutputVariable(symbol
, declaration
)
1122 elif dtype
== 'FUNCTION':
1123 return OutputFunction(symbol
, declaration
, dtype
)
1124 elif dtype
== 'USER_FUNCTION':
1125 return OutputFunction(symbol
, declaration
, dtype
)
1127 logging
.warning("Unknown symbol type %s for symbol %s", dtype
, symbol
)
1131 def OutputSymbolTraits(symbol
):
1132 """Returns the Since and StabilityLevel paragraphs for a symbol.
1135 symbol (str): the name to describe
1138 str: paragraph or empty string
1144 link_id
= "api-index-" + Since
[symbol
]
1145 desc
+= "<para role=\"since\">Since: <link linkend=\"%s\">%s</link></para>" % (link_id
, Since
[symbol
])
1147 if symbol
in StabilityLevel
:
1148 stability
= StabilityLevel
[symbol
]
1149 if stability
in AnnotationDefinition
:
1150 AnnotationsUsed
[stability
] = True
1151 stability
= "<acronym>%s</acronym>" % stability
1152 desc
+= "<para role=\"stability\">Stability Level: %s</para>" % stability
1156 def uri_escape(text
):
1160 # Build a char to hex map
1161 escapes
= {chr(i
): ("%%%02X" % i
) for i
in range(256)}
1163 # Default unsafe characters. RFC 2732 ^(uric - reserved)
1164 def do_escape(char
):
1165 return escapes
[char
]
1166 return re
.sub(r
"([^A-Za-z0-9\-_.!~*'()]", do_escape
, text
)
1169 def OutputSymbolExtraLinks(symbol
):
1170 """Returns extralinks for the symbol (if enabled).
1173 symbol (str): the name to describe
1176 str: paragraph or empty string
1180 if False: # NEW FEATURE: needs configurability
1181 sstr
= uri_escape(symbol
)
1182 mstr
= uri_escape(MODULE
)
1183 desc
+= '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink>
1184 <ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&symbol=%s">edit documentation</ulink>
1185 ''' % (sstr
, mstr
, sstr
)
1190 def OutputSectionExtraLinks(symbol
, docsymbol
):
1193 if False: # NEW FEATURE: needs configurability
1194 sstr
= uri_escape(symbol
)
1195 mstr
= uri_escape(MODULE
)
1196 dsstr
= uri_escape(docsymbol
)
1197 desc
+= '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s">code search</ulink>
1198 <ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&symbol=%s">edit documentation</ulink>
1199 ''' % (sstr
, mstr
, dsstr
)
1203 def OutputMacro(symbol
, declaration
):
1204 """Returns the synopsis and detailed description of a macro.
1207 symbol (str): the macro name.
1208 declaration (str): the declaration of the macro.
1211 str: the formated docs
1213 sid
= common
.CreateValidSGMLID(symbol
)
1214 condition
= MakeConditionDescription(symbol
)
1215 synop
= "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link>" % (
1218 fields
= common
.ParseMacroDeclaration(declaration
, CreateValidSGML
)
1223 desc
= '<refsect2 id="%s" role="macro"%s>\n<title>%s</title>\n' % (sid
, condition
, title
)
1224 desc
+= MakeIndexterms(symbol
, sid
)
1226 desc
+= OutputSymbolExtraLinks(symbol
)
1229 synop
+= "<phrase role=\"c_punctuation\">()</phrase>"
1231 synop
+= "</entry></row>\n"
1233 # Don't output the macro definition if is is a conditional macro or it
1234 # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
1235 # longer than 2 lines, otherwise we get lots of complicated macros like
1237 if symbol
not in DeclarationConditional
and not symbol
.startswith('g_') \
1238 and not re
.search(r
'^_?gnome_', symbol
) and declaration
.count('\n') < 2:
1239 decl_out
= CreateValidSGML(declaration
)
1240 desc
+= "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1242 desc
+= "<programlisting language=\"C\">" + "#define".ljust(RETURN_TYPE_FIELD_WIDTH
) + symbol
1243 m
= re
.search(r
'^\s*#\s*define\s+\w+(\([^\)]*\))', declaration
)
1246 pad
= ' ' * (RETURN_TYPE_FIELD_WIDTH
- len("#define "))
1247 # Align each line so that if should all line up OK.
1248 args
= args
.replace('\n', '\n' + pad
)
1249 desc
+= CreateValidSGML(args
)
1251 desc
+= "</programlisting>\n"
1253 desc
+= MakeDeprecationNote(symbol
)
1255 parameters
= OutputParamDescriptions("MACRO", symbol
, fields
)
1257 if symbol
in SymbolDocs
:
1258 symbol_docs
= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1262 desc
+= OutputSymbolTraits(symbol
)
1263 desc
+= "</refsect2>\n"
1264 return (synop
, desc
)
1267 def OutputTypedef(symbol
, declaration
):
1268 """Returns the synopsis and detailed description of a typedef.
1271 symbol (str): the typedef.
1272 declaration (str): the declaration of the typedef,
1273 e.g. 'typedef unsigned int guint;'
1276 str: the formated docs
1278 sid
= common
.CreateValidSGMLID(symbol
)
1279 condition
= MakeConditionDescription(symbol
)
1280 desc
= "<refsect2 id=\"%s\" role=\"typedef\"%s>\n<title>%s</title>\n" % (sid
, condition
, symbol
)
1281 synop
= "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1284 desc
+= MakeIndexterms(symbol
, sid
)
1286 desc
+= OutputSymbolExtraLinks(symbol
)
1288 if symbol
not in DeclarationConditional
:
1289 decl_out
= CreateValidSGML(declaration
)
1290 desc
+= "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1292 desc
+= MakeDeprecationNote(symbol
)
1294 if symbol
in SymbolDocs
:
1295 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1297 desc
+= OutputSymbolTraits(symbol
)
1298 desc
+= "</refsect2>\n"
1299 return (synop
, desc
)
1302 def OutputStruct(symbol
, declaration
):
1303 """Returns the synopsis and detailed description of a struct.
1305 We check if it is a object struct, and if so we only output parts of it that
1306 are noted as public fields. We also use a different IDs for object structs,
1307 since the original ID is used for the entire RefEntry.
1310 symbol (str): the struct.
1311 declaration (str): the declaration of the struct.
1314 str: the formated docs
1317 default_to_public
= True
1318 if CheckIsObject(symbol
):
1319 logging
.info("Found struct gtype: %s", symbol
)
1321 default_to_public
= ObjectRoots
[symbol
] == 'GBoxed'
1326 sid
= common
.CreateValidSGMLID(symbol
+ "_struct")
1327 condition
= MakeConditionDescription(symbol
+ "_struct")
1329 sid
= common
.CreateValidSGMLID(symbol
)
1330 condition
= MakeConditionDescription(symbol
)
1332 # Determine if it is a simple struct or it also has a typedef.
1334 if symbol
in StructHasTypedef
or re
.search(r
'^\s*typedef\s+', declaration
):
1340 # For structs with typedefs we just output the struct name.
1342 desc
= "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>%s</title>\n" % (sid
, condition
, symbol
)
1344 type_output
= "struct"
1345 desc
= "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>struct %s</title>\n" % (sid
, condition
, symbol
)
1347 synop
= "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1348 type_output
, sid
, symbol
)
1350 desc
+= MakeIndexterms(symbol
, sid
)
1352 desc
+= OutputSymbolExtraLinks(symbol
)
1354 # Form a pretty-printed, private-data-removed form of the declaration
1357 if re
.search(r
'^\s*$', declaration
):
1358 logging
.info("Found opaque struct: %s", symbol
)
1359 decl_out
= "typedef struct _%s %s;" % (symbol
, symbol
)
1360 elif re
.search(r
'^\s*struct\s+\w+\s*;\s*$', declaration
):
1361 logging
.info("Found opaque struct: %s", symbol
)
1362 decl_out
= "struct %s;" % symbol
1365 r
'^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$', declaration
, flags
=re
.S
)
1367 struct_contents
= m
.group(2)
1369 public
= default_to_public
1370 new_declaration
= ''
1372 for decl_line
in struct_contents
.splitlines():
1373 logging
.info("Struct line: %s", decl_line
)
1374 m2
= re
.search(r
'/\*\s*<\s*public\s*>\s*\*/', decl_line
)
1375 m3
= re
.search(r
'/\*\s*<\s*(private|protected)\s*>\s*\*/', decl_line
)
1381 new_declaration
+= decl_line
+ "\n"
1384 # Strip any blank lines off the ends.
1385 new_declaration
= re
.sub(r
'^\s*\n', '', new_declaration
)
1386 new_declaration
= re
.sub(r
'\n\s*$', r
'\n', new_declaration
)
1389 decl_out
= "typedef struct {\n%s} %s;\n" % (new_declaration
, symbol
)
1391 decl_out
= "struct %s {\n%s};\n" % (symbol
, new_declaration
)
1394 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1395 "Couldn't parse struct:\n%s" % declaration
)
1397 # If we couldn't parse the struct or it was all private, output an
1398 # empty struct declaration.
1401 decl_out
= "typedef struct _%s %s;" % (symbol
, symbol
)
1403 decl_out
= "struct %s;" % symbol
1405 decl_out
= CreateValidSGML(decl_out
)
1406 desc
+= "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1408 desc
+= MakeDeprecationNote(symbol
)
1410 if symbol
in SymbolDocs
:
1411 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1413 # Create a table of fields and descriptions
1415 # FIXME: Inserting  's into the produced type declarations here would
1416 # improve the output in most situations ... except for function
1417 # members of structs!
1419 return '<structfield id="%s">%s</structfield>' % (common
.CreateValidSGMLID(sid
+ '.' + args
[0]), args
[0])
1420 fields
= common
.ParseStructDeclaration(declaration
, not default_to_public
, 0, MakeXRef
, pfunc
)
1421 params
= SymbolParams
.get(symbol
)
1423 # If no parameters are filled in, we don't generate the description
1424 # table, for backwards compatibility.
1427 found
= next((True for p
in params
.values() if p
.strip() != ''), False)
1430 field_descrs
= params
1431 missing_parameters
= ''
1432 unused_parameters
= ''
1433 sid
= common
.CreateValidSGMLID(symbol
+ ".members")
1435 desc
+= '''<refsect3 id="%s" role="struct_members">\n<title>Members</title>
1436 <informaltable role="struct_members_table" pgwide="1" frame="none">
1438 <colspec colname="struct_members_name" colwidth="300px"/>
1439 <colspec colname="struct_members_description"/>
1440 <colspec colname="struct_members_annotations" colwidth="200px"/>
1444 for field_name
, text
in fields
.items():
1445 param_annotations
= ''
1447 desc
+= "<row role=\"member\"><entry role=\"struct_member_name\"><para>%s</para></entry>\n" % text
1448 if field_name
in field_descrs
:
1449 (field_descr
, param_annotations
) = ExpandAnnotation(symbol
, field_descrs
[field_name
])
1450 field_descr
= ConvertMarkDown(symbol
, field_descr
)
1452 field_descr
= re
.sub(r
'^(\s|\n)+', '', field_descr
, flags
=re
.M | re
.S
)
1453 field_descr
= re
.sub(r
'(\s|\n)+$', '', field_descr
, flags
=re
.M | re
.S
)
1454 desc
+= "<entry role=\"struct_member_description\">%s</entry>\n<entry role=\"struct_member_annotations\">%s</entry>\n" % (
1455 field_descr
, param_annotations
)
1456 del field_descrs
[field_name
]
1458 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1459 "Field description for %s::%s is missing in source code comment block." % (symbol
, field_name
))
1460 if missing_parameters
!= '':
1461 missing_parameters
+= ", " + field_name
1463 missing_parameters
= field_name
1465 desc
+= "<entry /><entry />\n"
1469 desc
+= "</tbody></tgroup></informaltable>\n</refsect3>\n"
1470 for field_name
in field_descrs
:
1471 # Documenting those standard fields is not required anymore, but
1472 # we don't want to warn if they are documented anyway.
1473 m
= re
.search(r
'(g_iface|parent_instance|parent_class)', field_name
)
1477 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1478 "Field description for %s::%s is not used from source code comment block." % (symbol
, field_name
))
1479 if unused_parameters
!= '':
1480 unused_parameters
+= ", " + field_name
1482 unused_parameters
= field_name
1484 # remember missing/unused parameters (needed in tmpl-free build)
1485 if missing_parameters
!= '' and (symbol
not in AllIncompleteSymbols
):
1486 AllIncompleteSymbols
[symbol
] = missing_parameters
1488 if unused_parameters
!= '' and (symbol
not in AllUnusedSymbols
):
1489 AllUnusedSymbols
[symbol
] = unused_parameters
1492 if symbol
not in AllIncompleteSymbols
:
1493 AllIncompleteSymbols
[symbol
] = "<items>"
1494 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1495 "Field descriptions for struct %s are missing in source code comment block." % symbol
)
1496 logging
.info("Remaining structs fields: " + ':'.join(fields
) + "\n")
1498 desc
+= OutputSymbolTraits(symbol
)
1499 desc
+= "</refsect2>\n"
1500 return (synop
, desc
)
1503 def OutputUnion(symbol
, declaration
):
1504 """Returns the synopsis and detailed description of a union.
1507 symbol (str): the union.
1508 declaration (str): the declaration of the union.
1511 str: the formated docs
1514 if CheckIsObject(symbol
):
1515 logging
.info("Found union gtype: %s", symbol
)
1521 sid
= common
.CreateValidSGMLID(symbol
+ "_union")
1522 condition
= MakeConditionDescription(symbol
+ "_union")
1524 sid
= common
.CreateValidSGMLID(symbol
)
1525 condition
= MakeConditionDescription(symbol
)
1527 # Determine if it is a simple struct or it also has a typedef.
1529 if symbol
in StructHasTypedef
or re
.search(r
'^\s*typedef\s+', declaration
):
1535 # For unions with typedefs we just output the union name.
1537 desc
= "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>%s</title>\n" % (sid
, condition
, symbol
)
1539 type_output
= "union"
1540 desc
= "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>union %s</title>\n" % (sid
, condition
, symbol
)
1542 synop
= "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1543 type_output
, sid
, symbol
)
1545 desc
+= MakeIndexterms(symbol
, sid
)
1547 desc
+= OutputSymbolExtraLinks(symbol
)
1548 desc
+= MakeDeprecationNote(symbol
)
1550 if symbol
in SymbolDocs
:
1551 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1553 # Create a table of fields and descriptions
1555 # FIXME: Inserting  's into the produced type declarations here would
1556 # improve the output in most situations ... except for function
1557 # members of structs!
1559 return '<structfield id="%s">%s</structfield>' % (common
.CreateValidSGMLID(sid
+ '.' + args
[0]), args
[0])
1560 fields
= common
.ParseStructDeclaration(declaration
, 0, 0, MakeXRef
, pfunc
)
1561 params
= SymbolParams
.get(symbol
)
1563 # If no parameters are filled in, we don't generate the description
1564 # table, for backwards compatibility
1567 found
= next((True for p
in params
.values() if p
.strip() != ''), False)
1569 logging
.debug('Union %s has %d entries, found=%d, has_typedef=%d', symbol
, len(fields
), found
, has_typedef
)
1572 field_descrs
= params
1573 missing_parameters
= ''
1574 unused_parameters
= ''
1575 sid
= common
.CreateValidSGMLID('%s.members' % symbol
)
1577 desc
+= '''<refsect3 id="%s" role="union_members">\n<title>Members</title>
1578 <informaltable role="union_members_table" pgwide="1" frame="none">
1580 <colspec colname="union_members_name" colwidth="300px"/>
1581 <colspec colname="union_members_description"/>
1582 <colspec colname="union_members_annotations" colwidth="200px"/>
1586 for field_name
, text
in fields
.items():
1587 param_annotations
= ''
1589 desc
+= "<row><entry role=\"union_member_name\"><para>%s</para></entry>\n" % text
1590 if field_name
in field_descrs
:
1591 (field_descr
, param_annotations
) = ExpandAnnotation(symbol
, field_descrs
[field_name
])
1592 field_descr
= ConvertMarkDown(symbol
, field_descr
)
1595 field_descr
= re
.sub(r
'^(\s|\n)+', '', field_descr
, flags
=re
.M | re
.S
)
1596 field_descr
= re
.sub(r
'(\s|\n)+$', '', field_descr
, flags
=re
.M | re
.S
)
1597 desc
+= "<entry role=\"union_member_description\">%s</entry>\n<entry role=\"union_member_annotations\">%s</entry>\n" % (
1598 field_descr
, param_annotations
)
1599 del field_descrs
[field_name
]
1601 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1602 "Field description for %s::%s is missing in source code comment block." % (symbol
, field_name
))
1603 if missing_parameters
!= '':
1604 missing_parameters
+= ", " + field_name
1606 missing_parameters
= field_name
1608 desc
+= "<entry /><entry />\n"
1612 desc
+= "</tbody></tgroup></informaltable>\n</refsect3>"
1613 for field_name
in field_descrs
:
1614 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1615 "Field description for %s::%s is not used from source code comment block." % (symbol
, field_name
))
1616 if unused_parameters
!= '':
1617 unused_parameters
+= ", " + field_name
1619 unused_parameters
= field_name
1621 # remember missing/unused parameters (needed in tmpl-free build)
1622 if missing_parameters
!= '' and (symbol
not in AllIncompleteSymbols
):
1623 AllIncompleteSymbols
[symbol
] = missing_parameters
1625 if unused_parameters
!= '' and (symbol
not in AllUnusedSymbols
):
1626 AllUnusedSymbols
[symbol
] = unused_parameters
1629 if symbol
not in AllIncompleteSymbols
:
1630 AllIncompleteSymbols
[symbol
] = "<items>"
1631 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1632 "Field descriptions for union %s are missing in source code comment block." % symbol
)
1633 logging
.info("Remaining union fields: " + ':'.join(fields
) + "\n")
1635 desc
+= OutputSymbolTraits(symbol
)
1636 desc
+= "</refsect2>\n"
1637 return (synop
, desc
)
1640 def OutputEnum(symbol
, declaration
):
1641 """Returns the synopsis and detailed description of a enum.
1644 symbol (str): the enum.
1645 declaration (str): the declaration of the enum.
1648 str: the formated docs
1651 if CheckIsObject(symbol
):
1652 logging
.info("Found enum gtype: %s", symbol
)
1658 sid
= common
.CreateValidSGMLID(symbol
+ "_enum")
1659 condition
= MakeConditionDescription(symbol
+ "_enum")
1661 sid
= common
.CreateValidSGMLID(symbol
)
1662 condition
= MakeConditionDescription(symbol
)
1664 synop
= "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1666 desc
= "<refsect2 id=\"%s\" role=\"enum\"%s>\n<title>enum %s</title>\n" % (sid
, condition
, symbol
)
1668 desc
+= MakeIndexterms(symbol
, sid
)
1670 desc
+= OutputSymbolExtraLinks(symbol
)
1671 desc
+= MakeDeprecationNote(symbol
)
1673 if symbol
in SymbolDocs
:
1674 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1676 # Create a table of fields and descriptions
1678 fields
= common
.ParseEnumDeclaration(declaration
)
1679 params
= SymbolParams
.get(symbol
)
1681 # If nothing at all is documented log a single summary warning at the end.
1682 # Otherwise, warn about each undocumented item.
1686 found
= next((True for p
in params
.values() if p
.strip() != ''), False)
1687 field_descrs
= params
1691 missing_parameters
= ''
1692 unused_parameters
= ''
1694 sid
= common
.CreateValidSGMLID("%s.members" % symbol
)
1695 desc
+= '''<refsect3 id="%s" role="enum_members">\n<title>Members</title>
1696 <informaltable role="enum_members_table" pgwide="1" frame="none">
1698 <colspec colname="enum_members_name" colwidth="300px"/>
1699 <colspec colname="enum_members_description"/>
1700 <colspec colname="enum_members_annotations" colwidth="200px"/>
1704 for field_name
in fields
:
1705 field_descr
= field_descrs
.get(field_name
)
1706 param_annotations
= ''
1708 sid
= common
.CreateValidSGMLID(field_name
)
1709 condition
= MakeConditionDescription(field_name
)
1710 desc
+= "<row role=\"constant\"><entry role=\"enum_member_name\"><para id=\"%s\">%s</para></entry>\n" % (
1713 field_descr
, param_annotations
= ExpandAnnotation(symbol
, field_descr
)
1714 field_descr
= ConvertMarkDown(symbol
, field_descr
)
1715 desc
+= "<entry role=\"enum_member_description\">%s</entry>\n<entry role=\"enum_member_annotations\">%s</entry>\n" % (
1716 field_descr
, param_annotations
)
1717 del field_descrs
[field_name
]
1720 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1721 "Value description for %s::%s is missing in source code comment block." % (symbol
, field_name
))
1722 if missing_parameters
!= '':
1723 missing_parameters
+= ", " + field_name
1725 missing_parameters
= field_name
1726 desc
+= "<entry /><entry />\n"
1729 desc
+= "</tbody></tgroup></informaltable>\n</refsect3>"
1730 for field_name
in field_descrs
:
1731 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1732 "Value description for %s::%s is not used from source code comment block." % (symbol
, field_name
))
1733 if unused_parameters
!= '':
1734 unused_parameters
+= ", " + field_name
1736 unused_parameters
= field_name
1738 # remember missing/unused parameters (needed in tmpl-free build)
1739 if missing_parameters
!= '' and (symbol
not in AllIncompleteSymbols
):
1740 AllIncompleteSymbols
[symbol
] = missing_parameters
1742 if unused_parameters
!= '' and (symbol
not in AllUnusedSymbols
):
1743 AllUnusedSymbols
[symbol
] = unused_parameters
1747 if symbol
not in AllIncompleteSymbols
:
1748 AllIncompleteSymbols
[symbol
] = "<items>"
1749 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1750 "Value descriptions for %s are missing in source code comment block." % symbol
)
1752 desc
+= OutputSymbolTraits(symbol
)
1753 desc
+= "</refsect2>\n"
1754 return (synop
, desc
)
1757 def OutputVariable(symbol
, declaration
):
1758 """Returns the synopsis and detailed description of a variable.
1761 symbol (str): the extern'ed variable.
1762 declaration (str): the declaration of the variable.
1765 str: the formated docs
1767 sid
= common
.CreateValidSGMLID(symbol
)
1768 condition
= MakeConditionDescription(symbol
)
1770 logging
.info("ouputing variable: '%s' '%s'", symbol
, declaration
)
1774 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
)
1776 r
'\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=', declaration
)
1778 mod1
= m1
.group(1) or ''
1779 ptr
= m1
.group(3) or ''
1780 space
= m1
.group(4) or ''
1781 mod2
= m1
.group(5) or ''
1782 type_output
= "extern %s%s%s%s" % (mod1
, ptr
, space
, mod2
)
1784 mod1
= m2
.group(1) or ''
1785 ptr
= m2
.group(3) or ''
1786 space
= m2
.group(4) or ''
1787 mod2
= m2
.group(5) or ''
1788 type_output
= '%s%s%s%s' % (mod1
, ptr
, space
, mod2
)
1790 type_output
= "extern"
1792 synop
= "<row><entry role=\"variable_type\">%s</entry><entry role=\"function_name\"><link linkend=\"%s\">%s</link></entry></row>\n" % (
1793 type_output
, sid
, symbol
)
1795 desc
= "<refsect2 id=\"%s\" role=\"variable\"%s>\n<title>%s</title>\n" % (sid
, condition
, symbol
)
1797 desc
+= MakeIndexterms(symbol
, sid
)
1799 desc
+= OutputSymbolExtraLinks(symbol
)
1801 decl_out
= CreateValidSGML(declaration
)
1802 desc
+= "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
1804 desc
+= MakeDeprecationNote(symbol
)
1806 if symbol
in SymbolDocs
:
1807 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1809 if symbol
in SymbolAnnotations
:
1810 param_desc
= SymbolAnnotations
[symbol
]
1811 param_annotations
= ''
1812 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
1813 if param_annotations
!= '':
1814 desc
+= "\n<para>%s</para>" % param_annotations
1816 desc
+= OutputSymbolTraits(symbol
)
1817 desc
+= "</refsect2>\n"
1818 return (synop
, desc
)
1821 def OutputFunction(symbol
, declaration
, symbol_type
):
1822 """Returns the synopsis and detailed description of a function.
1825 symbol (str): the function.
1826 declaration (str): the declaration of the function.
1829 str: the formated docs
1831 sid
= common
.CreateValidSGMLID(symbol
)
1832 condition
= MakeConditionDescription(symbol
)
1834 # Take out the return type
1836 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'
1837 m
= re
.search(regex
, declaration
)
1838 declaration
= re
.sub(regex
, '', declaration
)
1839 type_modifier
= m
.group(1) or ''
1841 pointer
= m
.group(3)
1842 pointer
= pointer
.rstrip()
1843 xref
= MakeXRef(type, tagify(type, "returnvalue"))
1845 # if (symbol_type == 'USER_FUNCTION')
1846 # start = "typedef "
1849 # We output const rather than G_CONST_RETURN.
1850 type_modifier
= re
.sub(r
'G_CONST_RETURN', 'const', type_modifier
)
1851 pointer
= re
.sub(r
'G_CONST_RETURN', 'const', pointer
)
1852 pointer
= re
.sub(r
'^\s+', ' ', pointer
)
1854 ret_type_output
= "%s%s%s%s\n" % (start
, type_modifier
, xref
, pointer
)
1856 indent_len
= len(symbol
) + 2
1857 char1
= char2
= char3
= ''
1858 if symbol_type
== 'USER_FUNCTION':
1860 char1
= "<phrase role=\"c_punctuation\">(</phrase>"
1862 char3
= "<phrase role=\"c_punctuation\">)</phrase>"
1864 symbol_output
= "%s<link linkend=\"%s\">%s%s</link>%s" % (char1
, sid
, char2
, symbol
, char3
)
1865 if indent_len
< MAX_SYMBOL_FIELD_WIDTH
:
1866 symbol_desc_output
= "%s%s%s%s " % (char1
, char2
, symbol
, char3
)
1868 indent_len
= MAX_SYMBOL_FIELD_WIDTH
- 8
1869 symbol_desc_output
= ('%s%s%s%s\n' % (char1
, char2
, symbol
, char3
)) + (' ' * (indent_len
- 1))
1871 synop
= "<row><entry role=\"function_type\">%s</entry><entry role=\"function_name\">%s <phrase role=\"c_punctuation\">()</phrase></entry></row>\n" % (
1872 ret_type_output
, symbol_output
)
1874 desc
= "<refsect2 id=\"%s\" role=\"function\"%s>\n<title>%s ()</title>\n" % (sid
, condition
, symbol
)
1876 desc
+= MakeIndexterms(symbol
, sid
)
1878 desc
+= OutputSymbolExtraLinks(symbol
)
1880 desc
+= "<programlisting language=\"C\">%s%s(" % (ret_type_output
, symbol_desc_output
)
1883 return tagify(args
[0], "parameter")
1885 fields
= common
.ParseFunctionDeclaration(declaration
, MakeXRef
, tagfun
)
1888 for field_name
in fields
.values():
1893 desc
+= ",\n" + (' ' * indent_len
) + field_name
1895 desc
+= ");</programlisting>\n"
1897 desc
+= MakeDeprecationNote(symbol
)
1899 if symbol
in SymbolDocs
:
1900 desc
+= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
1902 if symbol
in SymbolAnnotations
:
1903 param_desc
= SymbolAnnotations
[symbol
]
1904 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
1905 if param_annotations
!= '':
1906 desc
+= "\n<para>%s</para>" % param_annotations
1908 desc
+= OutputParamDescriptions("FUNCTION", symbol
, fields
.keys())
1909 desc
+= OutputSymbolTraits(symbol
)
1910 desc
+= "</refsect2>\n"
1911 return (synop
, desc
)
1914 def OutputParamDescriptions(symbol_type
, symbol
, fields
):
1915 """Returns the DocBook output describing the parameters of a symbol.
1917 This can be used for functions, macros or signal handlers.
1920 symbol_type (str): 'FUNCTION', 'MACRO' or 'SIGNAL'. Signal
1921 handlers have an implicit user_data parameter last.
1922 symbol (str): the name of the symbol being described.
1923 fields (list): parsed fields from the declaration, used to determine
1924 undocumented/unused entries
1927 str: the formated parameter docs
1934 field_descrs
= [f
for f
in fields
if f
not in ['void', 'Returns']]
1938 params
= SymbolParams
.get(symbol
)
1939 logging
.info("param_desc(%s, %s) = %s", symbol_type
, symbol
, str(params
))
1940 # This might be an empty dict, but for SIGNALS we append the user_data docs.
1941 # TODO(ensonic): maybe create that docstring in GetSignals()
1942 if params
is not None:
1945 missing_parameters
= ''
1946 unused_parameters
= ''
1948 for param_name
, param_desc
in params
.items():
1949 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
1950 param_desc
= ConvertMarkDown(symbol
, param_desc
)
1952 param_desc
= re
.sub(r
'^(\s|\n)+', '', param_desc
, flags
=re
.M | re
.S
)
1953 param_desc
= re
.sub(r
'(\s|\n)+$', '', param_desc
, flags
=re
.M | re
.S
)
1954 if param_name
== "Returns":
1955 returns
= param_desc
1956 if param_annotations
!= '':
1957 returns
+= "\n<para>%s</para>" % param_annotations
1959 elif param_name
== "void":
1960 # FIXME: &common.LogWarning()?
1961 logging
.info("!!!! void in params for %s?\n", symbol
)
1964 if param_name
not in field_descrs
:
1965 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1966 "Parameter description for %s::%s is not used from source code comment block." % (symbol
, param_name
))
1967 if unused_parameters
!= '':
1968 unused_parameters
+= ", " + param_name
1970 unused_parameters
= param_name
1972 field_descrs
.remove(param_name
)
1974 if param_desc
!= '':
1975 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" % (
1976 param_name
, param_desc
, param_annotations
)
1979 for param_name
in field_descrs
:
1980 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
1981 "Parameter description for %s::%s is missing in source code comment block." % (symbol
, param_name
))
1982 if missing_parameters
!= '':
1983 missing_parameters
+= ", " + param_name
1985 missing_parameters
= param_name
1987 # Signals have an implicit user_data parameter which we describe.
1988 if symbol_type
== "SIGNAL":
1989 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"
1991 # Start a table if we need one.
1992 if params_desc
!= '':
1993 sid
= common
.CreateValidSGMLID("%s.parameters" % symbol
)
1995 output
+= '''<refsect3 id="%s" role="parameters">\n<title>Parameters</title>
1996 <informaltable role="parameters_table" pgwide="1" frame="none">
1998 <colspec colname="parameters_name" colwidth="150px"/>
1999 <colspec colname="parameters_description"/>
2000 <colspec colname="parameters_annotations" colwidth="200px"/>
2003 output
+= params_desc
2004 output
+= "</tbody></tgroup></informaltable>\n</refsect3>"
2006 # Output the returns info last
2008 sid
= common
.CreateValidSGMLID("%s.returns" % symbol
)
2010 output
+= '''<refsect3 id="%s" role=\"returns\">\n<title>Returns</title>
2013 output
+= "\n</refsect3>"
2015 # remember missing/unused parameters (needed in tmpl-free build)
2016 if missing_parameters
!= '' and (symbol
not in AllIncompleteSymbols
):
2017 AllIncompleteSymbols
[symbol
] = missing_parameters
2019 if unused_parameters
!= '' and (symbol
not in AllUnusedSymbols
):
2020 AllUnusedSymbols
[symbol
] = unused_parameters
2022 if num_params
== 0 and fields
and field_descrs
:
2023 if symbol
not in AllIncompleteSymbols
:
2024 AllIncompleteSymbols
[symbol
] = "<parameters>"
2028 def ParseStabilityLevel(stability
, file, line
, message
):
2029 """Parses a stability level and outputs a warning if it isn't valid.
2031 stability (str): the stability text.
2032 file, line: context for error message
2033 message: description of where the level is from, to use in any error message.
2035 str: the parsed stability level string.
2037 stability
= stability
.strip()
2038 sl
= stability
.strip().lower()
2040 stability
= "Stable"
2041 elif sl
== 'unstable':
2042 stability
= "Unstable"
2043 elif sl
== 'private':
2044 stability
= "Private"
2046 common
.LogWarning(file, line
,
2047 "%s is %s. It should be one of these: Stable, "
2048 "Unstable, or Private." % (
2049 message
, stability
))
2050 return str(stability
)
2053 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
):
2054 """Outputs the final DocBook file for one section.
2057 file (str): the name of the file.
2058 title (str): the title from the $MODULE-sections.txt file
2059 section_id (str): the id to use for the toplevel tag.
2060 includes (str): comma-separates list of include files added at top of
2061 synopsis, with '<' '>' around them (if not already enclosed in '').
2062 functions_synop (str): the DocBook for the Functions Synopsis part.
2063 other_synop (str): the DocBook for the Types and Values Synopsis part.
2064 functions_details (str): the DocBook for the Functions Details part.
2065 other_details (str): the DocBook for the Types and Values Details part.
2066 signal_synop (str): the DocBook for the Signal Synopsis part
2067 signal_desc (str): the DocBook for the Signal Description part
2068 args_synop (str): the DocBook for the Arg Synopsis part
2069 args_desc (str): the DocBook for the Arg Description part
2070 hierarchy (str): the DocBook for the Object Hierarchy part
2071 interfaces (str): the DocBook for the Interfaces part
2072 implementations (str): the DocBook for the Known Implementations part
2073 prerequisites (str): the DocBook for the Prerequisites part
2074 derived (str): the DocBook for the Derived Interfaces part
2075 file_objects (list): objects in this file
2078 bool: True if the docs where updated
2081 logging
.info("Output docbook for file %s with title '%s'", file, title
)
2083 # The edited title overrides the one from the sections file.
2084 new_title
= SymbolDocs
.get(file + ":Title")
2085 if new_title
and not new_title
.strip() == '':
2087 logging
.info("Found title: %s", title
)
2089 short_desc
= SymbolDocs
.get(file + ":Short_Description")
2090 if not short_desc
or short_desc
.strip() == '':
2093 # Don't use ConvertMarkDown here for now since we don't want blocks
2094 short_desc
= ExpandAbbreviations(title
+ ":Short_description", short_desc
)
2095 logging
.info("Found short_desc: %s", short_desc
)
2097 long_desc
= SymbolDocs
.get(file + ":Long_Description")
2098 if not long_desc
or long_desc
.strip() == '':
2101 long_desc
= ConvertMarkDown(title
+ ":Long_description", long_desc
)
2102 logging
.info("Found long_desc: %s", long_desc
)
2104 see_also
= SymbolDocs
.get(file + ":See_Also")
2105 if not see_also
or re
.search(r
'^\s*(<para>)?\s*(</para>)?\s*$', see_also
):
2108 see_also
= ConvertMarkDown(title
+ ":See_Also", see_also
)
2109 logging
.info("Found see_also: %s", see_also
)
2112 see_also
= "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id
, see_also
)
2114 stability
= SymbolDocs
.get(file + ":Stability_Level")
2115 if not stability
or re
.search(r
'^\s*$', stability
):
2118 line_number
= GetSymbolSourceLine(file + ":Stability_Level")
2119 stability
= ParseStabilityLevel(stability
, file, line_number
, "Section stability level")
2120 logging
.info("Found stability: %s", stability
)
2123 stability
= DEFAULT_STABILITY
or ''
2126 if stability
in AnnotationDefinition
:
2127 AnnotationsUsed
[stability
] = True
2128 stability
= "<acronym>%s</acronym>" % stability
2129 stability
= "<refsect1 id=\"%s.stability-level\">\n<title>Stability Level</title>\n%s, unless otherwise indicated\n</refsect1>\n" % (
2130 section_id
, stability
)
2132 image
= SymbolDocs
.get(file + ":Image")
2133 if not image
or re
.search(r
'^\s*$', image
):
2136 image
= image
.strip()
2141 if re
.search(r
'jpe?g$', il
):
2142 format
= "format='JPEG'"
2143 elif il
.endswith('png'):
2144 format
= "format='PNG'"
2145 elif il
.endswith('svg'):
2146 format
= "format='SVG'"
2150 image
= " <inlinegraphic fileref='%s' %s/>\n" % (image
, format
)
2154 include_output
+= "<refsect1 id=\"%s.includes\"><title>Includes</title><synopsis>" % section_id
2155 for include
in includes
.split(','):
2156 if re
.search(r
'^\".+\"$', include
):
2157 include_output
+= "#include %s\n" % include
2159 include
= re
.sub(r
'^\s+|\s+$', '', include
, flags
=re
.S
)
2160 include_output
+= "#include <%s>\n" % include
2162 include_output
+= "</synopsis></refsect1>\n"
2164 extralinks
= OutputSectionExtraLinks(title
, "Section:%s" % file)
2166 old_db_file
= os
.path
.join(DB_OUTPUT_DIR
, file + '.xml')
2167 new_db_file
= os
.path
.join(DB_OUTPUT_DIR
, file + '.xml.new')
2169 OUTPUT
= open(new_db_file
, 'w', encoding
='utf-8')
2172 for fobject
in file_objects
:
2173 if fobject
== section_id
:
2175 sid
= common
.CreateValidSGMLID(fobject
)
2176 logging
.info("Adding anchor for %s\n", fobject
)
2177 object_anchors
+= "<anchor id=\"%s\"/>" % sid
2179 # Make sure we produce valid docbook
2180 if not functions_details
:
2181 functions_details
= "<para />"
2183 # We used to output this, but is messes up our common.UpdateFileIfChanged code
2184 # since it changes every day (and it is only used in the man pages):
2185 # "<refentry id="$section_id" revision="$mday $month $year">"
2187 OUTPUT
.write(REFENTRY
.substitute({
2188 'args_desc': args_desc
,
2189 'args_synop': args_synop
,
2191 'extralinks': extralinks
,
2192 'functions_details': functions_details
,
2193 'functions_synop': functions_synop
,
2194 'header': MakeDocHeader('refentry'),
2195 'hierarchy': hierarchy
,
2197 'include_output': include_output
,
2198 'interfaces': interfaces
,
2199 'implementations': implementations
,
2200 'long_desc': long_desc
,
2201 'object_anchors': object_anchors
,
2202 'other_details': other_details
,
2203 'other_synop': other_synop
,
2204 'prerequisites': prerequisites
,
2205 'section_id': section_id
,
2206 'see_also': see_also
,
2207 'signals_desc': signals_desc
,
2208 'signals_synop': signals_synop
,
2209 'short_desc': short_desc
,
2210 'stability': stability
,
2212 'MODULE': MODULE
.upper(),
2216 return common
.UpdateFileIfChanged(old_db_file
, new_db_file
, 0)
2219 def OutputProgramDBFile(program
, section_id
):
2220 """Outputs the final DocBook file for one program.
2223 file (str): the name of the file.
2224 section_id (str): the id to use for the toplevel tag.
2227 bool: True if the docs where updated
2229 logging
.info("Output program docbook for %s", program
)
2231 short_desc
= SourceSymbolDocs
.get(program
+ ":Short_Description")
2232 if not short_desc
or short_desc
.strip() == '':
2235 # Don't use ConvertMarkDown here for now since we don't want blocks
2236 short_desc
= ExpandAbbreviations(program
, short_desc
)
2237 logging
.info("Found short_desc: %s", short_desc
)
2239 synopsis
= SourceSymbolDocs
.get(program
+ ":Synopsis")
2240 if synopsis
and synopsis
.strip() != '':
2241 items
= synopsis
.split(' ')
2242 for i
in range(0, len(items
)):
2243 parameter
= items
[i
]
2247 # first parameter is the command name
2249 synopsis
= "<command>%s</command>\n" % parameter
2252 # square brackets indicate optional parameters, curly brackets
2253 # indicate required parameters ("plain" parameters are also
2254 # mandatory, but do not get extra decoration)
2255 m1
= re
.search(r
'^\[(.+?)\]$', parameter
)
2256 m2
= re
.search(r
'^\{(.+?)\}$', parameter
)
2259 parameter
= m1
.group(1)
2262 parameter
= m2
.group(1)
2264 # parameters ending in "..." are repeatable
2265 if parameter
.endswith('...'):
2266 rep
= ' rep=\"repeat\"'
2267 parameter
= parameter
[:-3]
2269 # italic parameters are replaceable parameters
2270 parameter
= re
.sub(r
'\*(.+?)\*', r
'<replaceable>\1</replaceable>', parameter
)
2272 synopsis
+= "<arg choice=\"%s\"%s>" % (choice
, rep
)
2273 synopsis
+= parameter
2274 synopsis
+= "</arg>\n"
2276 logging
.info("Found synopsis: %s", synopsis
)
2278 synopsis
= "<command>%s</command>" % program
2280 long_desc
= SourceSymbolDocs
.get(program
+ ":Long_Description")
2281 if not long_desc
or long_desc
.strip() == '':
2284 long_desc
= ConvertMarkDown("%s:Long_description" % program
, long_desc
)
2285 logging
.info("Found long_desc: %s", long_desc
)
2288 o
= program
+ ":Options"
2289 if o
in SourceSymbolDocs
:
2290 opts
= SourceSymbolDocs
[o
].split('\t')
2292 logging
.info('options: %d, %s', len(opts
), str(opts
))
2294 options
= "<refsect1>\n<title>Options</title>\n<variablelist>\n"
2295 for k
in range(0, len(opts
), 2):
2296 opt_desc
= opts
[k
+ 1]
2298 opt_desc
= re
.sub(r
'\*(.+?)\*', r
'<replaceable>\1</replaceable>', opt_desc
)
2300 options
+= "<varlistentry>\n<term>"
2301 opt_names
= opts
[k
].split(',')
2302 for i
in range(len(opt_names
)):
2303 prefix
= ', ' if i
> 0 else ''
2304 # italic parameters are replaceable parameters
2305 opt_name
= re
.sub(r
'\*(.+?)\*', r
'<replaceable>\1</replaceable>', opt_names
[i
])
2307 options
+= "%s<option>%s</option>\n" % (prefix
, opt_name
)
2309 options
+= "</term>\n"
2310 options
+= "<listitem><para>%s</para></listitem>\n" % opt_desc
2311 options
+= "</varlistentry>\n"
2313 options
+= "</variablelist></refsect1>\n"
2315 exit_status
= SourceSymbolDocs
.get(program
+ ":Returns")
2316 if exit_status
and exit_status
!= '':
2317 exit_status
= ConvertMarkDown("%s:Returns" % program
, exit_status
)
2318 exit_status
= "<refsect1 id=\"%s.exit-status\">\n<title>Exit Status</title>\n%s\n</refsect1>\n" % (
2319 section_id
, exit_status
)
2323 see_also
= SourceSymbolDocs
.get(program
+ ":See_Also")
2324 if not see_also
or re
.search(r
'^\s*(<para>)?\s*(</para>)?\s*$', see_also
):
2327 see_also
= ConvertMarkDown("%s:See_Also" % program
, see_also
)
2328 logging
.info("Found see_also: %s", see_also
)
2331 see_also
= "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % (section_id
, see_also
)
2333 old_db_file
= os
.path
.join(DB_OUTPUT_DIR
, program
+ ".xml")
2334 new_db_file
= os
.path
.join(DB_OUTPUT_DIR
, program
+ ".xml.new")
2336 OUTPUT
= open(new_db_file
, 'w', encoding
='utf-8')
2341 <refentrytitle role="top_of_page" id="%s.top_of_page">%s</refentrytitle>
2342 <manvolnum>1</manvolnum>
2343 <refmiscinfo>User Commands</refmiscinfo>
2346 <refname>%s</refname>
2347 <refpurpose>%s</refpurpose>
2350 <cmdsynopsis>%s</cmdsynopsis>
2352 <refsect1 id="%s.description" role="desc">
2353 <title role="desc.title">Description</title>
2358 ''' % (MakeDocHeader("refentry"), section_id
, section_id
, program
, program
, short_desc
, synopsis
, section_id
, long_desc
, options
, exit_status
, see_also
))
2361 return common
.UpdateFileIfChanged(old_db_file
, new_db_file
, 0)
2364 def OutputExtraFile(file):
2365 """Copies an "extra" DocBook file into the output directory, expanding abbreviations.
2368 file (str): the source file.
2371 bool: True if the docs where updated
2374 basename
= os
.path
.basename(file)
2376 old_db_file
= os
.path
.join(DB_OUTPUT_DIR
, basename
)
2377 new_db_file
= os
.path
.join(DB_OUTPUT_DIR
, basename
+ ".new")
2379 contents
= open(file, 'r', encoding
='utf-8').read()
2381 with
open(new_db_file
, 'w', encoding
='utf-8') as out
:
2382 out
.write(ExpandAbbreviations(basename
+ " file", contents
))
2384 return common
.UpdateFileIfChanged(old_db_file
, new_db_file
, 0)
2387 def GetDocbookHeader(main_file
):
2388 if os
.path
.exists(main_file
):
2389 INPUT
= open(main_file
, 'r', encoding
='utf-8')
2392 if re
.search(r
'^\s*<(book|chapter|article)', line
):
2393 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
2394 if not re
.search(r
'http:\/\/www.w3.org\/200[13]\/XInclude', line
) and \
2395 not re
.search(r
'http:\/\/www.w3.org\/200[13]\/XInclude', header
, flags
=re
.MULTILINE
):
2399 # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
2400 # FIXME: not sure if we can do this now, as people already work-around the problem
2401 # r'#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">', r'<!ENTITY % \1 SYSTEM \"../\2\">';
2403 r
'<!ENTITY % gtkdocentities SYSTEM "([^"]*)">', r
'<!ENTITY % gtkdocentities SYSTEM "../\1">', line
)
2406 header
= header
.strip()
2408 header
= '''<?xml version="1.0"?>
2409 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
2410 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
2412 <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
2413 <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
2419 def OutputBook(main_file
, book_top
, book_bottom
):
2420 """Outputs the entities that need to be included into the main docbook file for the module.
2423 book_top (str): the declarations of the entities, which are added
2424 at the top of the main docbook file.
2425 book_bottom (str): the entities, which are added in the main docbook
2426 file at the desired position.
2429 old_file
= os
.path
.join(DB_OUTPUT_DIR
, MODULE
+ "-doc.top")
2430 new_file
= os
.path
.join(DB_OUTPUT_DIR
, MODULE
+ "-doc.top.new")
2432 with
open(new_file
, 'w', encoding
='utf-8') as out
:
2435 common
.UpdateFileIfChanged(old_file
, new_file
, 0)
2437 old_file
= os
.path
.join(DB_OUTPUT_DIR
, MODULE
+ "-doc.bottom")
2438 new_file
= os
.path
.join(DB_OUTPUT_DIR
, MODULE
+ "-doc.bottom.new")
2440 with
open(new_file
, 'w', encoding
='utf-8') as out
:
2441 out
.write(book_bottom
)
2443 common
.UpdateFileIfChanged(old_file
, new_file
, 0)
2445 # If the main docbook file hasn't been created yet, we create it here.
2446 # The user can tweak it later.
2447 if main_file
and not os
.path
.exists(main_file
):
2448 OUTPUT
= open(main_file
, 'w', encoding
='utf-8')
2450 logging
.info("no master doc, create default one at: " + main_file
)
2453 <book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
2455 <title>&package_name; Reference Manual</title>
2457 for &package_string;.
2458 The latest version of this documentation can be found on-line at
2459 <ulink role="online-location" url="http://[SERVER]/&package_name;/index.html">http://[SERVER]/&package_name;/</ulink>.
2464 <title>[Insert title here]</title>
2466 ''' % (MakeDocHeader("book"), book_bottom
))
2467 if os
.path
.exists('xml/tree_index.sgml'):
2468 OUTPUT
.write(''' <chapter id="object-tree">
2469 <title>Object Hierarchy</title>
2470 <xi:include href="xml/tree_index.sgml"/>
2474 OUTPUT
.write(''' <!-- enable this when you use gobject types
2475 <chapter id="object-tree">
2476 <title>Object Hierarchy</title>
2477 <xi:include href="xml/tree_index.sgml"/>
2482 OUTPUT
.write(''' <index id="api-index-full">
2483 <title>API Index</title>
2484 <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
2486 <index id="deprecated-api-index" role="deprecated">
2487 <title>Index of deprecated API</title>
2488 <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
2491 for version
in set(Since
.values()):
2492 dash_version
= version
.replace('.', '-')
2493 OUTPUT
.write(''' <index id="api-index-%s" role="%s">
2494 <title>Index of new API in %s</title>
2495 <xi:include href="xml/api-index-%s.xml"><xi:fallback /></xi:include>
2497 ''' % (dash_version
, version
, version
, version
))
2500 OUTPUT
.write(''' <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2503 OUTPUT
.write(''' <!-- enable this when you use gobject introspection annotations
2504 <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
2508 OUTPUT
.write('''</book>
2514 def CreateValidSGML(text
):
2515 """Turn any chars which are used in XML into entities.
2517 e.g. '<' into '<'
2520 text (str): the text to turn into proper XML.
2526 text
= text
.replace('&', '&') # Do this first, or the others get messed up.
2527 text
= text
.replace('<', '<')
2528 text
= text
.replace('>', '>')
2529 # browsers render single tabs inconsistently
2530 text
= re
.sub(r
'([^\s])\t([^\s])', r
'\1 \2', text
)
2534 def ConvertSGMLChars(symbol
, text
):
2535 """Escape XML chars.
2537 This is used for text in source code comment blocks, to turn
2538 chars which are used in XML into entities, e.g. '<' into
2539 <'. Depending on INLINE_MARKUP_MODE, this is done
2540 unconditionally or only if the character doesn't seem to be
2541 part of an XML construct (tag or entity reference).
2543 text (str): the text to turn into proper XML.
2549 if INLINE_MARKUP_MODE
:
2550 # For the XML/SGML mode only convert to entities outside CDATA sections.
2551 return ModifyXMLElements(text
, symbol
,
2552 "<!\\[CDATA\\[|<programlisting[^>]*>",
2553 ConvertSGMLCharsEndTag
,
2554 ConvertSGMLCharsCallback
)
2555 # For the simple non-sgml mode, convert to entities everywhere.
2557 text
= re
.sub(r
'&(?![a-zA-Z#]+;)', r
'&', text
) # Do this first, or the others get messed up.
2558 # Allow '<' in verbatim markdown
2559 # TODO: we don't want to convert any of them between ``
2560 text
= re
.sub(r
'(?<!`)<', r
'<', text
)
2561 # Allow '>' at beginning of string for blockquote markdown
2562 text
= re
.sub(r
'''(?<=[^\w\n"'\/-])>''', r
'>', text
)
2567 def ConvertSGMLCharsEndTag(start_tag
):
2568 if start_tag
== '<![CDATA[':
2570 return "</programlisting>"
2573 def ConvertSGMLCharsCallback(text
, symbol
, tag
):
2574 if re
.search(r
'^<programlisting', tag
):
2575 logging
.debug('call modifyXML')
2576 # We can handle <programlisting> specially here.
2577 return ModifyXMLElements(text
, symbol
,
2579 ConvertSGMLCharsEndTag
,
2580 ConvertSGMLCharsCallback2
)
2582 logging
.debug('replace entities')
2583 # If we're not in CDATA convert to entities.
2584 text
= re
.sub(r
'&(?![a-zA-Z#]+;)', r
'&', text
) # Do this first, or the others get messed up.
2585 text
= re
.sub(r
'<(?![a-zA-Z\/!])', r
'<', text
)
2586 # Allow '>' at beginning of string for blockquote markdown
2587 text
= re
.sub(r
'''(?<=[^\w\n"'\/-])>''', r
'>', text
)
2589 # Handle "#include <xxxxx>"
2590 text
= re
.sub(r
'#include(\s+)<([^>]+)>', r
'#include\1<\2>', text
)
2595 def ConvertSGMLCharsCallback2(text
, symbol
, tag
):
2596 # If we're not in CDATA convert to entities.
2597 # We could handle <programlisting> differently, though I'm not sure it helps.
2599 # replace only if its not a tag
2600 text
= re
.sub(r
'&(?![a-zA-Z#]+;)', r
'&', text
) # Do this first, or the others get messed up.
2601 text
= re
.sub(r
'<(?![a-zA-Z\/!])', r
'<', text
)
2602 text
= re
.sub(r
'''(?<![a-zA-Z0-9"'\/-])>''', r
'>', text
)
2603 # Handle "#include <xxxxx>"
2604 text
= re
.sub(r
'/#include(\s+)<([^>]+)>', r
'#include\1<\2>', text
)
2609 def ExpandAnnotation(symbol
, param_desc
):
2610 """This turns annotations into acronym tags.
2612 symbol (str): the symbol being documented, for error messages.
2613 param_desc (str): the text to expand.
2616 str: the remaining param_desc
2617 str: the formatted annotations
2619 param_annotations
= ''
2621 # look for annotations at the start of the comment part
2622 # function level annotations don't end with a colon ':'
2623 m
= re
.search(r
'^\s*\((.*?)\)(:|$)', param_desc
)
2625 param_desc
= param_desc
[m
.end():]
2627 annotations
= re
.split(r
'\)\s*\(', m
.group(1))
2628 logging
.info("annotations for %s: '%s'\n", symbol
, m
.group(1))
2629 for annotation
in annotations
:
2630 # need to search for the longest key-match in %AnnotationDefinition
2632 match_annotation
= ''
2634 for annotationdef
in AnnotationDefinition
:
2635 if annotation
.startswith(annotationdef
):
2636 if len(annotationdef
) > match_length
:
2637 match_length
= len(annotationdef
)
2638 match_annotation
= annotationdef
2640 annotation_extra
= ''
2641 if match_annotation
!= '':
2642 m
= re
.search(match_annotation
+ r
'\s+(.*)', annotation
)
2644 annotation_extra
= " " + m
.group(1)
2646 AnnotationsUsed
[match_annotation
] = 1
2647 param_annotations
+= "[<acronym>%s</acronym>%s]" % (match_annotation
, annotation_extra
)
2649 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
2650 "unknown annotation \"%s\" in documentation for %s." % (annotation
, symbol
))
2651 param_annotations
+= "[%s]" % annotation
2653 param_desc
= param_desc
.strip()
2654 m
= re
.search(r
'^(.*?)\.*\s*$', param_desc
, flags
=re
.S
)
2655 param_desc
= m
.group(1) + '. '
2657 if param_annotations
!= '':
2658 param_annotations
= "<emphasis role=\"annotation\">%s</emphasis>" % param_annotations
2660 return (param_desc
, param_annotations
)
2663 def ExpandAbbreviations(symbol
, text
):
2664 """Expand the shortcut notation for symbol references.
2666 This turns the abbreviations function(), macro(), @param, %constant, and #symbol
2667 into appropriate DocBook markup. CDATA sections and <programlisting> parts
2671 symbol (str): the symbol being documented, for error messages.
2672 text (str): the text to expand.
2675 str: the expanded text
2677 # Note: This is a fallback and normally done in the markdown parser
2679 logging
.debug('expand abbreviations for "%s", text: [%s]', symbol
, text
)
2680 m
= re
.search(r
'\|\[[^\n]*\n(.*)\]\|', text
, flags
=re
.M | re
.S
)
2682 logging
.debug('replaced entities in code block')
2683 text
= text
[:m
.start(1)] + md_to_db
.ReplaceEntities(m
.group(1)) + text
[m
.end(1):]
2685 # Convert "|[" and "]|" into the start and end of program listing examples.
2686 # Support \[<!-- language="C" --> modifiers
2687 text
= re
.sub(r
'\|\[<!-- language="([^"]+)" -->',
2688 r
'<informalexample><programlisting role="example" language="\1"><![CDATA[', text
)
2689 text
= re
.sub(r
'\|\[', r
'<informalexample><programlisting role="example"><![CDATA[', text
)
2690 text
= re
.sub(r
'\]\|', r
']]></programlisting></informalexample>', text
)
2692 # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
2694 return ModifyXMLElements(text
, symbol
,
2695 "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
2696 ExpandAbbreviationsEndTag
,
2697 ExpandAbbreviationsCallback
)
2700 def ExpandAbbreviationsEndTag(start_tag
):
2701 # Returns the end tag (as a regexp) corresponding to the given start tag.
2702 if start_tag
== r
'<!\[CDATA\[':
2704 if start_tag
== "<!DOCTYPE":
2706 m
= re
.search(r
'<(\w+)', start_tag
)
2708 return "</%s>" % m
.group(1)
2710 logging
.warning('no end tag for "%s"', start_tag
)
2714 def ExpandAbbreviationsCallback(text
, symbol
, tag
):
2715 # Called inside or outside each CDATA or <programlisting> section.
2716 if tag
.startswith(r
'^<programlisting'):
2717 # Handle any embedded CDATA sections.
2718 return ModifyXMLElements(text
, symbol
,
2720 ExpandAbbreviationsEndTag
,
2721 ExpandAbbreviationsCallback2
)
2723 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2724 # but is also used for OutputExtraFile
2726 # We are outside any CDATA or <programlisting> sections, so we expand
2727 # any gtk-doc abbreviations.
2729 # Convert '@param()'
2730 # FIXME: we could make those also links ($symbol.$2), but that would be less
2731 # useful as the link target is a few lines up or down
2732 text
= re
.sub(r
'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r
'\1<parameter>\2()</parameter>', text
)
2734 # Convert 'function()' or 'macro()'.
2735 # if there is abc_*_def() we don't want to make a link to _def()
2736 # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
2738 return m
.group(1) + MakeXRef(m
.group(2), tagify(m
.group(2) + "()", "function"))
2739 text
= re
.sub(r
'([^\*.\w])(\w+)\s*\(\)', f1
, text
)
2740 # handle #Object.func()
2741 text
= re
.sub(r
'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1
, text
)
2743 # Convert '@param', but not '\@param'.
2744 text
= re
.sub(r
'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r
'\1<parameter>\2</parameter>', text
)
2745 text
= re
.sub(r
'/\\\@', r
'\@', text
)
2747 # Convert '%constant', but not '\%constant'.
2748 # Also allow negative numbers, e.g. %-1.
2750 return m
.group(1) + MakeXRef(m
.group(2), tagify(m
.group(2), "literal"))
2751 text
= re
.sub(r
'(\A|[^\\])\%(-?\w+)', f2
, text
)
2752 text
= re
.sub(r
'\\\%', r
'\%', text
)
2754 # Convert '#symbol', but not '\#symbol'.
2756 return m
.group(1) + MakeHashXRef(m
.group(2), "type")
2757 text
= re
.sub(r
'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3
, text
)
2758 text
= re
.sub(r
'\\#', '#', text
)
2763 def ExpandAbbreviationsCallback2(text
, symbol
, tag
):
2764 # This is called inside a <programlisting>
2766 # We are inside a <programlisting> but outside any CDATA sections,
2767 # so we expand any gtk-doc abbreviations.
2768 # FIXME: why is this different from &ExpandAbbreviationsCallback(),
2769 # why not just call it
2770 text
= re
.sub(r
'#(\w+)', lambda m
: '%s;' % MakeHashXRef(m
.group(1), ''), text
)
2771 elif tag
== "<![CDATA[":
2772 # NOTE: this is a fallback. It is normally done by the Markdown parser.
2773 text
= ReplaceEntities(text
, symbol
)
2778 def MakeHashXRef(symbol
, tag
):
2781 # Check for things like '#include', '#define', and skip them.
2782 if symbol
in PreProcessorDirectives
:
2783 return "#%s" % symbol
2785 # Get rid of special suffixes ('-struct','-enum').
2786 text
= re
.sub(r
'-struct$', '', text
)
2787 text
= re
.sub(r
'-enum$', '', text
)
2789 # If the symbol is in the form "Object::signal", then change the symbol to
2790 # "Object-signal" and use "signal" as the text.
2792 o
, s
= symbol
.split('::', 1)
2793 symbol
= '%s-%s' % (o
, s
)
2794 text
= u
'“' + s
+ u
'”'
2796 # If the symbol is in the form "Object:property", then change the symbol to
2797 # "Object--property" and use "property" as the text.
2799 o
, p
= symbol
.split(':', 1)
2800 symbol
= '%s--%s' % (o
, p
)
2801 text
= u
'“' + p
+ u
'”'
2804 text
= tagify(text
, tag
)
2806 return MakeXRef(symbol
, text
)
2809 def ModifyXMLElements(text
, symbol
, start_tag_regexp
, end_tag_func
, callback
):
2810 """Rewrite XML blocks.
2812 Looks for given XML element tags within the text, and calls
2813 the callback on pieces of text inside & outside those elements.
2814 Used for special handling of text inside things like CDATA
2815 and <programlisting>.
2818 text (str): the text.
2819 symbol (str): the symbol currently being documented (only used for
2821 start_tag_regexp (str): the regular expression to match start tags.
2822 e.g. "<!\\[CDATA\\[|<programlisting[^>]*>" to
2823 match CDATA sections or programlisting elements.
2824 end_tag_func (func): function which is passed the matched start tag
2825 and should return the appropriate end tag string
2827 callback - callback called with each part of the text. It is
2828 called with a piece of text, the symbol being
2829 documented, and the matched start tag or '' if the text
2830 is outside the XML elements being matched.
2835 before_tag
= start_tag
= end_tag_regexp
= end_tag
= None
2838 logging
.debug('modify xml for symbol: %s, regex: %s, text: [%s]', symbol
, start_tag_regexp
, text
)
2840 m
= re
.search(start_tag_regexp
, text
, flags
=re
.S
)
2842 before_tag
= text
[:m
.start()] # Prematch for last successful match string
2843 start_tag
= m
.group(0) # Last successful match
2844 text
= text
[m
.end():] # Postmatch for last successful match string
2845 # get the matching end-tag for current tag
2846 end_tag_regexp
= end_tag_func(start_tag
)
2848 logging
.debug('symbol: %s matched start: %s, end_tag: %s, text: [%s]', symbol
, start_tag
, end_tag_regexp
, text
)
2850 logging
.debug('converting before tag: [%s]', before_tag
)
2851 result
+= callback(before_tag
, symbol
, '')
2854 m2
= re
.search(end_tag_regexp
, text
, flags
=re
.S
)
2856 before_tag
= text
[:m2
.start()]
2857 end_tag
= m2
.group(0)
2858 text
= text
[m2
.end():]
2860 logging
.debug('symbol: %s matched end %s: text: [%s]', symbol
, end_tag
, text
)
2862 result
+= callback(before_tag
, symbol
, start_tag
)
2865 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
2866 "Can't find tag end: %s in docs for: %s." % (end_tag_regexp
, symbol
))
2867 # Just assume it is all inside the tag.
2868 result
+= callback(text
, symbol
, start_tag
)
2870 m
= re
.search(start_tag_regexp
, text
, flags
=re
.S
)
2872 # Handle any remaining text outside the tags.
2873 logging
.debug('converting after tag: [%s]', text
)
2874 result
+= callback(text
, symbol
, '')
2875 logging
.debug('results for symbol: %s, text: [%s]', symbol
, result
)
2880 def tagify(text
, elem
):
2881 # Adds a tag around some text.
2882 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
2883 return '<' + elem
+ '>' + text
+ '</' + elem
+ '>'
2886 def MakeDocHeader(tag
):
2887 """Builds a docbook header for the given tag.
2890 tag (str): doctype tag
2893 str: the docbook header
2895 header
= re
.sub(r
'<!DOCTYPE \w+', r
'<!DOCTYPE ' + tag
, doctype_header
)
2896 # fix the path for book since this is one level up
2899 r
'<!ENTITY % gtkdocentities SYSTEM "../([a-zA-Z./]+)">', r
'<!ENTITY % gtkdocentities SYSTEM "\1">', header
)
2903 def MakeXRef(symbol
, text
=None):
2904 """This returns a cross-reference link to the given symbol.
2906 Though it doesn't try to do this for a few standard C types that it knows
2907 won't be in the documentation.
2910 symbol (str): the symbol to try to create a XRef to.
2911 text (str): text to put inside the XRef, defaults to symbol
2916 symbol
= symbol
.strip()
2920 # Get rid of special suffixes ('-struct','-enum').
2921 text
= re
.sub(r
'-struct$', '', text
)
2922 text
= re
.sub(r
'-enum$', '', text
)
2927 logging
.info("Getting type link for %s -> %s", symbol
, text
)
2929 symbol_id
= common
.CreateValidSGMLID(symbol
)
2930 return "<link linkend=\"%s\">%s</link>" % (symbol_id
, text
)
2933 def MakeIndexterms(symbol
, sid
):
2934 """This returns a indexterm elements for the given symbol
2937 symbol (str): the symbol to create indexterms for
2940 str: doxbook index terms
2945 # make the index useful, by ommiting the namespace when sorting
2946 if NAME_SPACE
!= '':
2947 m
= re
.search(r
'^%s\_?(.*)' % NAME_SPACE
, symbol
, flags
=re
.I
)
2949 sortas
= ' sortas="%s"' % m
.group(1)
2951 if symbol
in Deprecated
:
2952 terms
+= "<indexterm zone=\"%s\" role=\"deprecated\"><primary%s>%s</primary></indexterm>" % (
2953 sid
, sortas
, symbol
)
2954 IndexEntriesDeprecated
[symbol
] = sid
2955 IndexEntriesFull
[symbol
] = sid
2957 since
= Since
[symbol
].strip()
2959 terms
+= "<indexterm zone=\"%s\" role=\"%s\"><primary%s>%s</primary></indexterm>" % (
2960 sid
, since
, sortas
, symbol
)
2961 IndexEntriesSince
[symbol
] = sid
2962 IndexEntriesFull
[symbol
] = sid
2964 terms
+= "<indexterm zone=\"%s\"><primary%s>%s</primary></indexterm>" % (sid
, sortas
, symbol
)
2965 IndexEntriesFull
[symbol
] = sid
2969 def MakeDeprecationNote(symbol
):
2970 """This returns a deprecation warning for the given symbol.
2973 symbol (str): the symbol to try to create a warning for.
2976 str: formatted warning or empty string if symbol is not deprecated
2979 if symbol
in Deprecated
:
2980 desc
+= "<warning><para><literal>%s</literal> " % symbol
2981 note
= Deprecated
[symbol
]
2983 m
= re
.search(r
'^\s*([0-9\.]+)\s*:?', note
)
2985 desc
+= "has been deprecated since version %s and should not be used in newly-written code.</para>" % m
.group(
2988 desc
+= "is deprecated and should not be used in newly-written code.</para>"
2990 note
= re
.sub(r
'^\s*([0-9\.]+)\s*:?\s*', '', note
)
2994 note
= ConvertMarkDown(symbol
, note
)
2997 desc
+= "</warning>\n"
3002 def MakeConditionDescription(symbol
):
3003 """This returns a sumary of conditions for the given symbol.
3006 symbol (str): the symbol to create the sumary for.
3009 str: formatted text or empty string if no special conditions apply.
3012 if symbol
in Deprecated
:
3015 m
= re
.search(r
'^\s*(.*?)\s*$', Deprecated
[symbol
])
3017 desc
+= "deprecated:%s" % m
.group(1)
3019 desc
+= "deprecated"
3024 m
= re
.search(r
'^\s*(.*?)\s*$', Since
[symbol
])
3026 desc
+= "since:%s" % m
.group(1)
3030 if symbol
in StabilityLevel
:
3034 desc
+= "stability:" + StabilityLevel
[symbol
]
3037 cond
= re
.sub(r
'"', r
'"', desc
)
3038 desc
= ' condition=\"%s\"' % cond
3039 logging
.info("condition for '%s' = '%s'", symbol
, desc
)
3044 def GetHierarchy(gobject
, hierarchy
):
3045 """Generate the object inheritance graph.
3047 Returns the DocBook output describing the ancestors and
3048 immediate children of a GObject subclass. It uses the
3049 global Objects and ObjectLevels arrays to walk the tree.
3052 object (str): the GtkObject subclass.
3053 hierarchy (list) - previous hierarchy
3056 list: lines of docbook describing the hierarchy
3058 # Find object in the objects array.
3063 for i
in range(len(Objects
)):
3065 if ObjectLevels
[i
] <= level
:
3068 elif ObjectLevels
[i
] == level
+ 1:
3069 children
.append(Objects
[i
])
3071 elif Objects
[i
] == gobject
:
3074 level
= ObjectLevels
[i
]
3079 logging
.info("=== Hierachy for: %s (%d existing entries) ===", gobject
, len(hierarchy
))
3081 # Walk up the hierarchy, pushing ancestors onto the ancestors array.
3082 ancestors
= [gobject
]
3083 logging
.info("Level: %s", level
)
3086 if ObjectLevels
[j
] < level
:
3087 ancestors
.append(Objects
[j
])
3088 level
= ObjectLevels
[j
]
3089 logging
.info("Level: %s", level
)
3091 # Output the ancestors, indented and with links.
3092 logging
.info('%d ancestors', len(ancestors
))
3095 for i
in range(len(ancestors
) - 1, -1, -1):
3096 ancestor
= ancestors
[i
]
3097 ancestor_id
= common
.CreateValidSGMLID(ancestor
)
3098 indent
= ' ' * (level
* 4)
3099 # Don't add a link to the current object, i.e. when i == 0.
3101 entry_text
= indent
+ "<link linkend=\"%s\">%s</link>" % (ancestor_id
, ancestor
)
3102 alt_text
= indent
+ ancestor
3104 entry_text
= indent
+ ancestor
3105 alt_text
= indent
+ "<link linkend=\"%s\">%s</link>" % (ancestor_id
, ancestor
)
3107 logging
.info("Checking for '%s' or '%s'", entry_text
, alt_text
)
3108 # Check if we already have this object
3110 for j
in range(len(hierarchy
)):
3111 if hierarchy
[j
] == entry_text
or (hierarchy
[j
] == alt_text
):
3115 # We have a new entry, find insert position in alphabetical order
3117 for j
in range(last_index
, len(hierarchy
)):
3118 if not re
.search(r
'^' + indent
, hierarchy
[j
]):
3122 elif re
.search(r
'^%s[^ ]' % indent
, hierarchy
[j
]):
3123 stripped_text
= hierarchy
[j
]
3124 if r
'<link linkend' not in entry_text
:
3125 stripped_text
= re
.sub(r
'<link linkend="[A-Za-z]*">', '', stripped_text
)
3126 stripped_text
= re
.sub(r
'</link>', '', stripped_text
)
3128 if entry_text
< stripped_text
:
3135 last_index
= len(hierarchy
)
3137 logging
.debug('insert at %d: %s', last_index
, entry_text
)
3138 hierarchy
.insert(last_index
, entry_text
)
3141 # Already have this one, make sure we use the not linked version
3142 if r
'<link linkend' not in entry_text
:
3143 hierarchy
[j
] = entry_text
3145 # Remember index as base insert point
3146 last_index
= index
+ 1
3150 # Output the children, indented and with links.
3151 logging
.info('%d children', len(children
))
3152 for i
in range(len(children
)):
3153 sid
= common
.CreateValidSGMLID(children
[i
])
3154 indented_text
= ' ' * (level
* 4) + "<link linkend=\"%s\">%s</link>" % (sid
, children
[i
])
3155 logging
.debug('insert at %d: %s', last_index
, indented_text
)
3156 hierarchy
.insert(last_index
, indented_text
)
3161 def GetInterfaces(gobject
):
3162 """Generate interface implementation graph.
3164 Returns the DocBook output describing the interfaces
3165 implemented by a class. It uses the global Interfaces hash.
3168 object (str): the GObject subclass.
3171 str: implemented interfaces
3174 # Find object in the objects array.
3175 if gobject
in Interfaces
:
3176 ifaces
= Interfaces
[gobject
].split()
3181 for i
in range(count
):
3182 sid
= common
.CreateValidSGMLID(ifaces
[i
])
3183 text
+= " <link linkend=\"%s\">%s</link>" % (sid
, ifaces
[i
])
3194 def GetImplementations(gobject
):
3195 """Generate interface usage graph.
3197 Returns the DocBook output describing the implementations
3198 of an interface. It uses the global Interfaces hash.
3201 object (str): the GObject subclass.
3204 str: interface implementations
3208 for key
in Interfaces
:
3209 if re
.search(r
'\b%s\b' % gobject
, Interfaces
[key
]):
3216 %s is implemented by
3218 for i
in range(count
):
3219 sid
= common
.CreateValidSGMLID(impls
[i
])
3220 text
+= " <link linkend=\"%s\">%s</link>" % (sid
, impls
[i
])
3231 def GetPrerequisites(iface
):
3232 """Generates interface requirements.
3234 Returns the DocBook output describing the prerequisites
3235 of an interface. It uses the global Prerequisites hash.
3237 iface (str): the interface.
3240 str: required interfaces
3244 if iface
in Prerequisites
:
3248 prereqs
= Prerequisites
[iface
].split()
3249 count
= len(prereqs
)
3250 for i
in range(count
):
3251 sid
= common
.CreateValidSGMLID(prereqs
[i
])
3252 text
+= " <link linkend=\"%s\">%s</link>" % (sid
, prereqs
[i
])
3263 def GetDerived(iface
):
3265 Returns the DocBook output describing the derived interfaces
3266 of an interface. It uses the global %Prerequisites hash.
3269 iface (str): the interface.
3272 str: derived interfaces
3276 for key
in Prerequisites
:
3277 if re
.search(r
'\b%s\b' % iface
, Prerequisites
[key
]):
3280 count
= len(derived
)
3286 for i
in range(count
):
3287 sid
= common
.CreateValidSGMLID(derived
[i
])
3288 text
+= " <link linkend=\"%s\">%s</link>" % (sid
, derived
[i
])
3299 def GetSignals(gobject
):
3300 """Generate signal docs.
3302 Returns the synopsis and detailed description DocBook output
3303 for the signal handlers of a given GObject subclass.
3306 object (str): the GObject subclass, e.g. 'GtkButton'.
3314 for i
in range(len(SignalObjects
)):
3315 if SignalObjects
[i
] == gobject
:
3316 logging
.info("Found signal: %s", SignalNames
[i
])
3317 name
= SignalNames
[i
]
3318 symbol
= '%s::%s' % (gobject
, name
)
3319 sid
= common
.CreateValidSGMLID('%s-%s' % (gobject
, name
))
3321 desc
+= u
"<refsect2 id=\"%s\" role=\"signal\"><title>The <literal>“%s”</literal> signal</title>\n" % (
3323 desc
+= MakeIndexterms(symbol
, sid
)
3325 desc
+= OutputSymbolExtraLinks(symbol
)
3327 desc
+= "<programlisting language=\"C\">"
3329 m
= re
.search(r
'\s*(const\s+)?(\w+)\s*(\**)', SignalReturns
[i
])
3330 type_modifier
= m
.group(1) or ''
3332 pointer
= m
.group(3)
3333 xref
= MakeXRef(gtype
, tagify(gtype
, "returnvalue"))
3335 ret_type_output
= '%s%s%s' % (type_modifier
, xref
, pointer
)
3336 callback_name
= "user_function"
3337 desc
+= '%s\n%s (' % (ret_type_output
, callback_name
)
3339 indentation
= ' ' * (len(callback_name
) + 2)
3341 sourceparams
= SourceSymbolParams
.get(symbol
)
3342 sourceparam_names
= None
3344 sourceparam_names
= list(sourceparams
) # keys as list
3345 params
= SignalPrototypes
[i
].splitlines()
3346 type_len
= len("gpointer")
3347 name_len
= len("user_data")
3348 # do two passes, the first one is to calculate padding
3350 for j
in range(len(params
)):
3352 # allow alphanumerics, '_', '[' & ']' in param names
3353 m
= re
.search(r
'^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$', params
[j
])
3356 pointer
= m
.group(2)
3357 if sourceparam_names
:
3358 if j
< len(sourceparam_names
):
3359 param_name
= sourceparam_names
[j
]
3360 logging
.info('from sourceparams: "%s" (%d: %s)', param_name
, j
, params
[j
])
3361 # we're mssing the docs for this param, don't warn here though
3363 param_name
= m
.group(3)
3364 logging
.info('from params: "%s" (%d: %s)', param_name
, j
, params
[j
])
3367 param_name
= "arg%d" % j
3370 if len(gtype
) + len(pointer
) > type_len
:
3371 type_len
= len(gtype
) + len(pointer
)
3372 if len(param_name
) > name_len
:
3373 name_len
= len(param_name
)
3375 logging
.info("signal arg[%d]: '%s'", j
, param_name
)
3376 xref
= MakeXRef(gtype
, tagify(gtype
, "type"))
3377 pad
= ' ' * (type_len
- len(gtype
) - len(pointer
))
3378 desc
+= '%s%s %s%s,\n' % (xref
, pad
, pointer
, param_name
)
3382 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
3383 "Can't parse arg: %s\nArgs:%s" % (params
[j
], SignalPrototypes
[i
]))
3385 xref
= MakeXRef("gpointer", tagify("gpointer", "type"))
3386 pad
= ' ' * (type_len
- len("gpointer"))
3387 desc
+= '%s%s user_data)' % (xref
, pad
)
3388 desc
+= "</programlisting>\n"
3390 flags
= SignalFlags
[i
]
3394 flags_string
= "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>"
3397 flags_string
= "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>"
3400 flags_string
= "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>"
3401 flags_string
= "Cleanup"
3405 flags_string
+= " / "
3406 flags_string
= "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>"
3410 flags_string
+= " / "
3411 flags_string
= "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>"
3415 flags_string
+= " / "
3416 flags_string
= "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>"
3420 flags_string
+= " / "
3421 flags_string
= "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>"
3423 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" % (
3424 ret_type_output
, sid
, name
, flags_string
)
3426 parameters
= OutputParamDescriptions("SIGNAL", symbol
, None)
3427 logging
.info("formatted signal params: '%s' -> '%s'", symbol
, parameters
)
3429 AllSymbols
[symbol
] = 1
3430 if symbol
in SymbolDocs
:
3431 symbol_docs
= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
3435 if not IsEmptyDoc(SymbolDocs
[symbol
]):
3436 AllDocumentedSymbols
[symbol
] = 1
3438 if symbol
in SymbolAnnotations
:
3439 param_desc
= SymbolAnnotations
[symbol
]
3440 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
3441 if param_annotations
!= '':
3442 desc
+= "\n<para>%s</para>" % param_annotations
3444 desc
+= MakeDeprecationNote(symbol
)
3448 desc
+= "<para>Flags: %s</para>\n" % flags_string
3450 desc
+= OutputSymbolTraits(symbol
)
3451 desc
+= "</refsect2>"
3453 return (synop
, desc
)
3456 def GetArgs(gobject
):
3457 """Generate property docs.
3459 Returns the synopsis and detailed description DocBook output
3460 for the Args of a given GtkObject subclass.
3463 object (str): the GObject subclass, e.g. 'GtkButton'.
3475 for i
in range(len(ArgObjects
)):
3476 if ArgObjects
[i
] == gobject
:
3477 logging
.info("Found arg: %s", ArgNames
[i
])
3485 kind
= "child property"
3488 kind
= "style property"
3493 # Remember only one colon so we don't clash with signals.
3494 symbol
= '%s:%s' % (gobject
, name
)
3495 # use two dashes and ev. an extra separator here for the same reason.
3496 sid
= common
.CreateValidSGMLID('%s--%s%s' % (gobject
, id_sep
, name
))
3500 arange
= ArgRanges
[i
]
3501 range_output
= CreateValidSGML(arange
)
3502 default
= ArgDefaults
[i
]
3503 default_output
= CreateValidSGML(default
)
3505 if atype
== "GtkString":
3506 atype
= "char *"
3508 if atype
== "GtkSignal":
3509 atype
= "GtkSignalFunc, gpointer"
3510 type_output
= MakeXRef("GtkSignalFunc") + ", " + MakeXRef("gpointer")
3511 elif re
.search(r
'^(\w+)\*$', atype
):
3512 m
= re
.search(r
'^(\w+)\*$', atype
)
3513 type_output
= MakeXRef(m
.group(1), tagify(m
.group(1), "type")) + " *"
3515 type_output
= MakeXRef(atype
, tagify(atype
, "type"))
3518 flags_string
= "Read"
3522 flags_string
+= " / "
3523 flags_string
+= "Write"
3527 flags_string
+= " / "
3528 flags_string
+= "Construct"
3532 flags_string
+= " / "
3533 flags_string
+= "Construct Only"
3535 AllSymbols
[symbol
] = 1
3537 if symbol
in SymbolDocs
and not IsEmptyDoc(SymbolDocs
[symbol
]):
3538 blurb
= ConvertMarkDown(symbol
, SymbolDocs
[symbol
])
3539 logging
.info(".. [%s][%s]", SymbolDocs
[symbol
], blurb
)
3540 AllDocumentedSymbols
[symbol
] = 1
3543 if ArgBlurbs
[i
] != '':
3544 blurb
= "<para>" + CreateValidSGML(ArgBlurbs
[i
]) + "</para>"
3545 AllDocumentedSymbols
[symbol
] = 1
3547 # FIXME: print a warning?
3548 logging
.info(".. no description")
3552 pad1
= " " * (24 - len(name
))
3554 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" % (
3555 type_output
, sid
, name
, flags_string
)
3556 arg_desc
= u
"<refsect2 id=\"%s\" role=\"property\"><title>The <literal>“%s”</literal> %s</title>\n" % (
3558 arg_desc
+= MakeIndexterms(symbol
, sid
)
3560 arg_desc
+= OutputSymbolExtraLinks(symbol
)
3562 arg_desc
+= u
"<programlisting> “%s”%s %s</programlisting>\n" % (name
, pad1
, type_output
)
3564 if symbol
in SymbolAnnotations
:
3565 param_desc
= SymbolAnnotations
[symbol
]
3566 (param_desc
, param_annotations
) = ExpandAnnotation(symbol
, param_desc
)
3567 if param_annotations
!= '':
3568 arg_desc
+= "\n<para>%s</para>" % param_annotations
3570 arg_desc
+= MakeDeprecationNote(symbol
)
3573 arg_desc
+= "<para>Flags: %s</para>\n" % flags_string
3576 arg_desc
+= "<para>Allowed values: %s</para>\n" % range_output
3579 arg_desc
+= "<para>Default value: %s</para>\n" % default_output
3581 arg_desc
+= OutputSymbolTraits(symbol
)
3582 arg_desc
+= "</refsect2>\n"
3585 child_synop
+= arg_synop
3586 child_desc
+= arg_desc
3589 style_synop
+= arg_synop
3590 style_desc
+= arg_desc
3596 return (synop
, child_synop
, style_synop
, desc
, child_desc
, style_desc
)
3599 def IgnorePath(path
, source_dirs
, ignore_files
):
3600 for sdir
in source_dirs
:
3601 # Cut off base directory
3602 m1
= re
.search(r
'^%s/(.*)$' % re
.escape(sdir
), path
)
3604 # Check if the filename is in the ignore list.
3605 m2
= re
.search(r
'(\s|^)%s(\s|$)' % re
.escape(m1
.group(1)), ignore_files
)
3607 logging
.info("Skipping path: %s", path
)
3610 logging
.info("No match for: %s", m1
.group(1))
3612 logging
.info("No match for: %s", path
)
3616 def ReadSourceDocumentation(source_dir
, suffix_list
, source_dirs
, ignore_files
):
3617 """Read the documentation embedded in comment blocks in the source code.
3619 It recursively descends the source directory looking for source files and
3620 scans them looking for specially-formatted comment blocks.
3623 source_dir (str): the directory to scan.
3624 suffix_list (list): extensions to check
3626 if IgnorePath(source_dir
, source_dirs
, ignore_files
):
3629 logging
.info("Scanning source directory: %s", source_dir
)
3631 # This array holds any subdirectories found.
3634 for ifile
in sorted(os
.listdir(source_dir
)):
3635 logging
.debug("... : %s", ifile
)
3636 if ifile
.startswith('.'):
3638 fname
= os
.path
.join(source_dir
, ifile
)
3639 if os
.path
.isdir(fname
):
3640 subdirs
.append(fname
)
3642 for suffix
in suffix_list
:
3643 if ifile
.endswith(suffix
):
3644 if not IgnorePath(fname
, source_dirs
, ignore_files
):
3645 ScanSourceFile(fname
, ignore_files
)
3648 # Now recursively scan the subdirectories.
3649 for sdir
in subdirs
:
3650 ReadSourceDocumentation(sdir
, suffix_list
, source_dirs
, ignore_files
)
3653 def ScanSourceFile(ifile
, ignore_files
):
3654 """Scans one source file looking for specially-formatted comment blocks.
3656 Later MergeSourceDocumentation() is copying over the doc blobs that are not
3660 file (str): the file to scan.
3662 m
= re
.search(r
'^.*[\/\\]([^\/\\]*)$', ifile
)
3664 basename
= m
.group(1)
3666 common
.LogWarning(ifile
, 1, "Can't find basename for this filename.")
3669 # Check if the basename is in the list of files to ignore.
3670 if re
.search(r
'(\s|^)%s(\s|$)' % re
.escape(basename
), ignore_files
):
3671 logging
.info("Skipping source file: %s", ifile
)
3674 logging
.info("Scanning source file: %s", ifile
)
3676 SRCFILE
= open(ifile
, 'r', encoding
='utf-8')
3677 in_comment_block
= False
3682 since_desc
= stability_desc
= deprecated_desc
= ''
3683 params
= OrderedDict()
3686 for line
in SRCFILE
:
3688 # Look for the start of a comment block.
3689 if not in_comment_block
:
3690 if re
.search(r
'^\s*/\*.*\*/', line
):
3691 # one-line comment - not gtkdoc
3693 elif re
.search(r
'^\s*/\*\*\s', line
):
3694 logging
.info("Found comment block start")
3696 in_comment_block
= True
3698 # Reset all the symbol data.
3704 deprecated_desc
= ''
3706 params
= OrderedDict()
3711 # We're in a comment block. Check if we've found the end of it.
3712 if re
.search(r
'^\s*\*+/', line
):
3714 # maybe its not even meant to be a gtk-doc comment?
3715 common
.LogWarning(ifile
, line_number
, "Symbol name not found at the start of the comment block.")
3717 # Add the return value description onto the end of the params.
3719 # TODO(ensonic): check for duplicated Return docs
3720 # common.LogWarning(file, line_number, "Multiple Returns for %s." % symbol)
3721 params
['Returns'] = return_desc
3723 # Convert special characters
3724 description
= ConvertSGMLChars(symbol
, description
)
3725 for (param_name
, param_desc
) in params
.items():
3726 params
[param_name
] = ConvertSGMLChars(symbol
, param_desc
)
3728 # Handle Section docs
3729 m
= re
.search(r
'SECTION:\s*(.*)', symbol
)
3730 m2
= re
.search(r
'PROGRAM:\s*(.*)', symbol
)
3732 real_symbol
= m
.group(1)
3733 long_descr
= real_symbol
+ ":Long_Description"
3735 if long_descr
not in KnownSymbols
or KnownSymbols
[long_descr
] != 1:
3737 ifile
, line_number
, "Section %s is not defined in the %s-sections.txt file." % (real_symbol
, MODULE
))
3739 logging
.info("SECTION DOCS found in source for : '%s'", real_symbol
)
3740 for param_name
, param_desc
in params
.items():
3741 logging
.info(" '" + param_name
+ "'")
3742 param_name
= param_name
.lower()
3744 if param_name
== "short_description":
3745 key
= real_symbol
+ ":Short_Description"
3746 elif param_name
== "see_also":
3747 key
= real_symbol
+ ":See_Also"
3748 elif param_name
== "title":
3749 key
= real_symbol
+ ":Title"
3750 elif param_name
== "stability":
3751 key
= real_symbol
+ ":Stability_Level"
3752 elif param_name
== "section_id":
3753 key
= real_symbol
+ ":Section_Id"
3754 elif param_name
== "include":
3755 key
= real_symbol
+ ":Include"
3756 elif param_name
== "image":
3757 key
= real_symbol
+ ":Image"
3760 SourceSymbolDocs
[key
] = param_desc
3761 SourceSymbolSourceFile
[key
] = ifile
3762 SourceSymbolSourceLine
[key
] = line_number
3764 SourceSymbolDocs
[long_descr
] = description
3765 SourceSymbolSourceFile
[long_descr
] = ifile
3766 SourceSymbolSourceLine
[long_descr
] = line_number
3768 real_symbol
= m2
.group(1)
3772 logging
.info("PROGRAM DOCS found in source for '%s'", real_symbol
)
3773 for param_name
, param_desc
in params
.items():
3774 logging
.info("PROGRAM key %s: '%s'", real_symbol
, param_name
)
3775 param_name
= param_name
.lower()
3777 if param_name
== "short_description":
3778 key
= real_symbol
+ ":Short_Description"
3779 elif param_name
== "see_also":
3780 key
= real_symbol
+ ":See_Also"
3781 elif param_name
== "section_id":
3782 key
= real_symbol
+ ":Section_Id"
3783 elif param_name
== "synopsis":
3784 key
= real_symbol
+ ":Synopsis"
3785 elif param_name
== "returns":
3786 key
= real_symbol
+ ":Returns"
3787 elif re
.search(r
'^(-.*)', param_name
):
3788 logging
.info("PROGRAM opts: '%s': '%s'", param_name
, param_desc
)
3789 key
= real_symbol
+ ":Options"
3791 opts_str
= SourceSymbolDocs
.get(key
)
3793 opts
= opts_str
.split('\t')
3794 opts
.append(param_name
)
3795 opts
.append(param_desc
)
3797 logging
.info("Setting options for symbol: %s: '%s'", real_symbol
, '\t'.join(opts
))
3798 SourceSymbolDocs
[key
] = '\t'.join(opts
)
3802 logging
.info("PROGRAM value %s: '%s'", real_symbol
, param_desc
.rstrip())
3803 SourceSymbolDocs
[key
] = param_desc
.rstrip()
3804 SourceSymbolSourceFile
[key
] = ifile
3805 SourceSymbolSourceLine
[key
] = line_number
3807 long_descr
= real_symbol
+ ":Long_Description"
3808 SourceSymbolDocs
[long_descr
] = description
3809 SourceSymbolSourceFile
[long_descr
] = ifile
3810 SourceSymbolSourceLine
[long_descr
] = line_number
3812 section_id
= SourceSymbolDocs
.get(real_symbol
+ ":Section_Id")
3813 if section_id
and section_id
.strip() != '':
3814 # Remove trailing blanks and use as is
3815 section_id
= section_id
.rstrip()
3817 section_id
= common
.CreateValidSGMLID('%s-%s' % (MODULE
, real_symbol
))
3818 OutputProgramDBFile(real_symbol
, section_id
)
3821 logging
.info("SYMBOL DOCS found in source for : '%s'", symbol
)
3822 SourceSymbolDocs
[symbol
] = description
3823 SourceSymbolParams
[symbol
] = params
3824 SourceSymbolSourceFile
[symbol
] = ifile
3825 SourceSymbolSourceLine
[symbol
] = line_number
3828 arr
= since_desc
.splitlines()
3829 since_desc
= arr
[0].strip()
3830 extra_lines
= arr
[1:]
3831 logging
.info("Since(%s) : [%s]", symbol
, since_desc
)
3832 Since
[symbol
] = ConvertSGMLChars(symbol
, since_desc
)
3833 if len(extra_lines
) > 1:
3834 common
.LogWarning(ifile
, line_number
, "multi-line since docs found")
3837 stability_desc
= ParseStabilityLevel(
3838 stability_desc
, ifile
, line_number
, "Stability level for %s" % symbol
)
3839 StabilityLevel
[symbol
] = ConvertSGMLChars(symbol
, stability_desc
)
3842 if symbol
not in Deprecated
:
3843 # don't warn for signals and properties
3844 # if ($symbol !~ m/::?(.*)/)
3845 if symbol
in DeclarationTypes
:
3846 common
.LogWarning(ifile
, line_number
,
3847 "%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
)
3849 Deprecated
[symbol
] = ConvertSGMLChars(symbol
, deprecated_desc
)
3851 in_comment_block
= False
3854 # Get rid of ' * ' at start of every line in the comment block.
3855 line
= re
.sub(r
'^\s*\*\s?', '', line
)
3856 # But make sure we don't get rid of the newline at the end.
3857 if not line
.endswith('\n'):
3860 logging
.info("scanning :%s", line
.strip())
3862 # If we haven't found the symbol name yet, look for it.
3864 m1
= re
.search(r
'^\s*(SECTION:\s*\S+)', line
)
3865 m2
= re
.search(r
'^\s*(PROGRAM:\s*\S+)', line
)
3866 m3
= re
.search(r
'^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$', line
)
3868 symbol
= m1
.group(1)
3869 logging
.info("SECTION DOCS found in source for : '%s'", symbol
)
3871 symbol
= m2
.group(1)
3872 logging
.info("PROGRAM DOCS found in source for : '%s'", symbol
)
3874 symbol
= m3
.group(1)
3875 annotation
= m3
.group(2)
3876 logging
.info("SYMBOL DOCS found in source for : '%s'", symbol
)
3878 annotation
= annotation
.strip()
3879 if annotation
!= '':
3880 SymbolAnnotations
[symbol
] = annotation
3881 logging
.info("remaining text for %s: '%s'", symbol
, annotation
)
3885 if in_part
== "description":
3886 # Get rid of 'Description:'
3887 line
= re
.sub(r
'^\s*Description:', '', line
)
3889 m1
= re
.search(r
'^\s*(returns|return\s+value):', line
, flags
=re
.I
)
3890 m2
= re
.search(r
'^\s*since:', line
, flags
=re
.I
)
3891 m3
= re
.search(r
'^\s*deprecated:', line
, flags
=re
.I
)
3892 m4
= re
.search(r
'^\s*stability:', line
, flags
=re
.I
)
3895 # we're in param section and have not seen the blank line
3897 return_desc
= line
[m1
.end():]
3902 # we're in param section and have not seen the blank line
3903 if in_part
!= "param":
3904 since_desc
= line
[m2
.end():]
3909 # we're in param section and have not seen the blank line
3910 if in_part
!= "param":
3911 deprecated_desc
= line
[m3
.end():]
3912 in_part
= "deprecated"
3916 stability_desc
= line
[m4
.end():]
3917 in_part
= "stability"
3920 if in_part
== "description":
3923 elif in_part
== "return":
3926 elif in_part
== "since":
3929 elif in_part
== "stability":
3930 stability_desc
+= line
3932 elif in_part
== "deprecated":
3933 deprecated_desc
+= line
3936 # We must be in the parameters. Check for the empty line below them.
3937 if re
.search(r
'^\s*$', line
):
3938 in_part
= "description"
3941 # Look for a parameter name.
3942 m
= re
.search(r
'^\s*@(.+?)\s*:\s*', line
)
3944 param_name
= m
.group(1)
3945 param_desc
= line
[m
.end():]
3947 logging
.info("Found parameter: %s", param_name
)
3948 # Allow varargs variations
3949 if re
.search(r
'^\.\.\.$', param_name
):
3952 logging
.info("Found param for symbol %s : '%s'= '%s'", symbol
, param_name
, line
)
3954 params
[param_name
] = param_desc
3958 logging
.info("continuation for %s annotation '%s'", symbol
, line
)
3959 annotation
= re
.sub(r
'^\s+|\s+$', '', line
)
3960 if symbol
in SymbolAnnotations
:
3961 SymbolAnnotations
[symbol
] += annotation
3963 SymbolAnnotations
[symbol
] = annotation
3966 # We must be in the middle of a parameter description, so add it on
3967 # to the last element in @params.
3969 common
.LogWarning(file, line_number
, "Parsing comment block file : parameter expected, but got '%s'" % line
)
3971 params
[param_name
] += line
3976 def OutputMissingDocumentation():
3977 """Outputs report of documentation coverage to a file.
3980 bool: True if the report was updated
3982 old_undocumented_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-undocumented.txt")
3983 new_undocumented_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-undocumented.new")
3991 buffer_deprecated
= ''
3992 buffer_descriptions
= ''
3994 UNDOCUMENTED
= open(new_undocumented_file
, 'w', encoding
='utf-8')
3996 for symbol
in sorted(AllSymbols
.keys()):
3997 # FIXME: should we print common.LogWarnings for undocumented stuff?
3999 # location = "defined at " + GetSymbolSourceFile(symbol) + ":" + GetSymbolSourceLine(symbol) + "\n"
4002 r
':(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)', symbol
)
4003 m2
= re
.search(r
':(Long_Description|Short_Description)', symbol
)
4006 if symbol
in AllDocumentedSymbols
:
4008 if symbol
in AllIncompleteSymbols
:
4010 buffer += symbol
+ " (" + AllIncompleteSymbols
[symbol
] + ")\n"
4012 elif symbol
in Deprecated
:
4013 if symbol
in AllIncompleteSymbols
:
4015 buffer_deprecated
+= symbol
+ " (" + AllIncompleteSymbols
[symbol
] + ")\n"
4017 buffer_deprecated
+= symbol
+ "\n"
4020 if symbol
in AllIncompleteSymbols
:
4022 buffer += symbol
+ " (" + AllIncompleteSymbols
[symbol
] + ")\n"
4024 buffer += symbol
+ "\n"
4028 if (symbol
in SymbolDocs
and len(SymbolDocs
[symbol
]) > 0)\
4029 or (symbol
in AllDocumentedSymbols
and AllDocumentedSymbols
[symbol
] > 0):
4032 buffer_descriptions
+= symbol
+ "\n"
4037 percent
= (n_documented
/ total
) * 100.0
4039 UNDOCUMENTED
.write("%.0f%% symbol docs coverage.\n" % percent
)
4040 UNDOCUMENTED
.write("%s symbols documented.\n" % n_documented
)
4041 UNDOCUMENTED
.write("%s symbols incomplete.\n" % n_incomplete
)
4042 UNDOCUMENTED
.write("%d not documented.\n" % (total
- n_documented
))
4044 if buffer_deprecated
!= '':
4045 buffer += "\n" + buffer_deprecated
4047 if buffer_descriptions
!= '':
4048 buffer += "\n" + buffer_descriptions
4051 UNDOCUMENTED
.write("\n\n" + buffer)
4053 UNDOCUMENTED
.close()
4055 return common
.UpdateFileIfChanged(old_undocumented_file
, new_undocumented_file
, 0)
4058 def OutputUndeclaredSymbols():
4059 """Reports undeclared symbols.
4061 Outputs symbols that are listed in the section file, but have no declaration
4065 bool: True if the report was updated
4067 old_undeclared_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-undeclared.txt")
4068 new_undeclared_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-undeclared.new")
4070 with
open(new_undeclared_file
, 'w', encoding
='utf-8') as out
:
4071 if UndeclaredSymbols
:
4072 out
.write("\n".join(sorted(UndeclaredSymbols
.keys())))
4074 print("See %s-undeclared.txt for the list of undeclared symbols." % MODULE
)
4076 return common
.UpdateFileIfChanged(old_undeclared_file
, new_undeclared_file
, 0)
4079 def OutputUnusedSymbols():
4080 """Reports unused documentation.
4082 Outputs symbols that are documented in comments, but not declared in the
4086 bool: True if the report was updated
4089 old_unused_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-unused.txt")
4090 new_unused_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-unused.new")
4092 with
open(new_unused_file
, 'w', encoding
='utf-8') as out
:
4094 for symbol
in sorted(Declarations
.keys()):
4095 if symbol
not in DeclarationOutput
:
4096 out
.write("%s\n" % symbol
)
4099 for symbol
in sorted(AllUnusedSymbols
.keys()):
4100 out
.write(symbol
+ "(" + AllUnusedSymbols
[symbol
] + ")\n")
4105 old_unused_file
, 1, "%d unused declarations. They should be added to %s-sections.txt in the appropriate place." % (num_unused
, MODULE
))
4107 return common
.UpdateFileIfChanged(old_unused_file
, new_unused_file
, 0)
4110 def OutputAllSymbols():
4111 """Outputs list of all symbols to a file."""
4112 new_symbols_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-symbols.txt")
4113 with
open(new_symbols_file
, 'w', encoding
='utf-8') as out
:
4114 for symbol
in sorted(AllSymbols
.keys()):
4115 out
.write(symbol
+ "\n")
4118 def OutputSymbolsWithoutSince():
4119 """Outputs list of all symbols without a since tag to a file."""
4120 new_nosince_file
= os
.path
.join(ROOT_DIR
, MODULE
+ "-nosince.txt")
4121 with
open(new_nosince_file
, 'w', encoding
='utf-8') as out
:
4122 for symbol
in sorted(SourceSymbolDocs
.keys()):
4124 out
.write(symbol
+ "\n")
4127 def CheckParamsDocumented(symbol
, params
):
4128 stype
= DeclarationTypes
.get(symbol
)
4132 if stype
== 'STRUCT':
4134 elif stype
== 'ENUM':
4136 elif stype
== 'UNION':
4140 logging
.info("Check param docs for %s, params: %s entries, type=%s", symbol
, len(params
), stype
)
4143 logging
.info("params: %s", str(params
))
4144 for (param_name
, param_desc
) in params
.items():
4145 # Output a warning if the parameter is empty and remember for stats.
4146 if param_name
!= "void" and not re
.search(r
'\S', param_desc
):
4147 if symbol
in AllIncompleteSymbols
:
4148 AllIncompleteSymbols
[symbol
] += ", " + param_name
4150 AllIncompleteSymbols
[symbol
] = param_name
4152 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
4153 "%s description for %s::%s is missing in source code comment block." % (item
, symbol
, param_name
))
4155 elif len(params
) == 0:
4156 AllIncompleteSymbols
[symbol
] = "<items>"
4157 common
.LogWarning(GetSymbolSourceFile(symbol
), GetSymbolSourceLine(symbol
),
4158 "%s descriptions for %s are missing in source code comment block." % (item
, symbol
))
4161 def MergeSourceDocumentation():
4162 """Merges documentation read from a source file.
4164 Parameter descriptions override any in the template files.
4165 Function descriptions are placed before any description from
4169 # add whats found in the source
4170 symbols
= set(SourceSymbolDocs
.keys())
4172 # and add known symbols from -sections.txt
4173 for symbol
in KnownSymbols
.keys():
4174 if KnownSymbols
[symbol
] == 1:
4177 logging
.info("num source entries: %d", len(symbols
))
4179 for symbol
in symbols
:
4180 AllSymbols
[symbol
] = 1
4182 if symbol
in SourceSymbolDocs
:
4183 logging
.info("merging [%s] from source", symbol
)
4185 # remove leading and training whitespaces
4186 src_docs
= SourceSymbolDocs
[symbol
].strip()
4188 AllDocumentedSymbols
[symbol
] = 1
4190 SymbolDocs
[symbol
] = src_docs
4193 if symbol
in SourceSymbolParams
:
4194 param_docs
= SourceSymbolParams
[symbol
]
4195 SymbolParams
[symbol
] = param_docs
4196 # if this symbol is documented, check if docs are complete
4197 # remove all xml-tags and whitespaces
4198 check_docs
= re
.sub(r
'\s', '', re
.sub(r
'<.*?>', '', src_docs
))
4199 if check_docs
!= '' and param_docs
:
4200 CheckParamsDocumented(symbol
, param_docs
)
4202 logging
.info("[%s] undocumented", symbol
)
4204 logging
.info("num doc entries: %d", len(SymbolDocs
))
4207 def IsEmptyDoc(doc
):
4208 """Check if a doc-string is empty.
4210 It is also regarded as empty if it only consist of whitespace or e.g. FIXME.
4213 doc (str): the doc-string
4218 if re
.search(r
'^\s*$', doc
):
4220 if re
.search(r
'^\s*<para>\s*(FIXME)?\s*<\/para>\s*$', doc
):
4225 def ConvertMarkDown(symbol
, text
):
4227 return md_to_db
.MarkDownParse(text
, symbol
)
4230 def ReadDeclarationsFile(ifile
, override
):
4231 """Reads in a file containing the function/macro/enum etc. declarations.
4233 Note that in some cases there are several declarations with
4234 the same name, e.g. for conditional macros. In this case we
4235 set a flag in the DeclarationConditional hash so the
4236 declaration is not shown in the docs.
4238 If a macro and a function have the same name, e.g. for
4239 g_object_ref, the function declaration takes precedence.
4241 Some opaque structs are just declared with 'typedef struct
4242 _name name;' in which case the declaration may be empty.
4243 The structure may have been found later in the header, so
4244 that overrides the empty declaration.
4247 file (str): the declarations file to read
4248 override (bool): if declarations in this file should override
4249 any current declaration.
4252 Declarations
.clear()
4253 DeclarationTypes
.clear()
4254 DeclarationConditional
.clear()
4255 DeclarationOutput
.clear()
4257 INPUT
= open(ifile
, 'r', encoding
='utf-8')
4258 declaration_type
= ''
4259 declaration_name
= None
4265 # logging.debug("%s:%d: %s", ifile, line_number, line)
4266 if not declaration_type
:
4267 m1
= re
.search(r
'^<([^>]+)>', line
)
4269 declaration_type
= m1
.group(1)
4270 declaration_name
= ''
4271 logging
.info("Found declaration: %s", declaration_type
)
4274 m2
= re
.search(r
'^<NAME>(.*)</NAME>', line
)
4275 m3
= re
.search(r
'^<DEPRECATED/>', line
)
4276 m4
= re
.search(r
'^</%s>' % declaration_type
, line
)
4278 declaration_name
= m2
.group(1)
4280 is_deprecated
= True
4282 logging
.info("Found end of declaration: %s, %s", declaration_type
, declaration_name
)
4283 # Check that the declaration has a name
4284 if declaration_name
== '':
4285 common
.LogWarning(ifile
, line_number
, declaration_type
+ " has no name.\n")
4287 # If the declaration is an empty typedef struct _XXX XXX
4288 # set the flag to indicate the struct has a typedef.
4289 if (declaration_type
== 'STRUCT' or declaration_type
== 'UNION') \
4290 and re
.search(r
'^\s*$', declaration
):
4291 logging
.info("Struct has typedef: %s", declaration_name
)
4292 StructHasTypedef
[declaration_name
] = 1
4294 # Check if the symbol is already defined.
4295 if declaration_name
in Declarations
and override
== 0:
4296 # Function declarations take precedence.
4297 if DeclarationTypes
[declaration_name
] == 'FUNCTION':
4300 elif declaration_type
== 'FUNCTION':
4302 Deprecated
[declaration_name
] = ''
4304 Declarations
[declaration_name
] = declaration
4305 DeclarationTypes
[declaration_name
] = declaration_type
4306 elif DeclarationTypes
[declaration_name
] == declaration_type
:
4307 # If the existing declaration is empty, or is just a
4308 # forward declaration of a struct, override it.
4309 if declaration_type
== 'STRUCT' or declaration_type
== 'UNION':
4310 if re
.search(r
'^\s*((struct|union)\s+\w+\s*;)?\s*$', Declarations
[declaration_name
]):
4312 Deprecated
[declaration_name
] = ''
4313 Declarations
[declaration_name
] = declaration
4314 elif re
.search(r
'^\s*((struct|union)\s+\w+\s*;)?\s*$', declaration
):
4315 # Ignore an empty or forward declaration.
4319 ifile
, line_number
, "Structure %s has multiple definitions." % declaration_name
)
4322 # set flag in %DeclarationConditional hash for
4323 # multiply defined macros/typedefs.
4324 DeclarationConditional
[declaration_name
] = 1
4327 common
.LogWarning(ifile
, line_number
, declaration_name
+ " has multiple definitions.")
4331 Deprecated
[declaration_name
] = ''
4333 Declarations
[declaration_name
] = declaration
4334 DeclarationTypes
[declaration_name
] = declaration_type
4335 logging
.debug("added declaration: %s, %s, [%s]", declaration_type
, declaration_name
, declaration
)
4337 declaration_type
= ''
4338 is_deprecated
= False
4344 def ReadSignalsFile(ifile
):
4345 """Reads information about object signals.
4347 It creates the arrays @SignalNames and @SignalPrototypes containing details
4348 about the signals. The first line of the SignalPrototype is the return type
4349 of the signal handler. The remaining lines are the parameters passed to it.
4350 The last parameter, "gpointer user_data" is always the same so is not included.
4353 ifile (str): the file containing the signal handler prototype information.
4356 signal_object
= None
4358 signal_returns
= None
4360 signal_prototype
= None
4362 # Reset the signal info.
4363 SignalObjects
[:] = []
4365 SignalReturns
[:] = []
4367 SignalPrototypes
[:] = []
4369 if not os
.path
.isfile(ifile
):
4372 INPUT
= open(ifile
, 'r', encoding
='utf-8')
4377 if re
.search(r
'^<SIGNAL>', line
):
4382 signal_prototype
= ''
4385 m
= re
.search(r
'^<NAME>(.*)<\/NAME>', line
)
4386 m2
= re
.search(r
'^<RETURNS>(.*)<\/RETURNS>', line
)
4387 m3
= re
.search(r
'^<FLAGS>(.*)<\/FLAGS>', line
)
4389 signal_name
= m
.group(1)
4390 m1_2
= re
.search(r
'^(.*)::(.*)$', signal_name
)
4392 signal_object
= m1_2
.group(1)
4393 signal_name
= m1_2
.group(2).replace('_', '-')
4394 logging
.info("Found signal: %s", signal_name
)
4396 common
.LogWarning(ifile
, line_number
, "Invalid signal name: %s." % signal_name
)
4399 signal_returns
= m2
.group(1)
4401 signal_flags
= m3
.group(1)
4402 elif re
.search(r
'^</SIGNAL>', line
):
4403 logging
.info("Found end of signal: %s::%s\nReturns: %s\n%s",
4404 signal_object
, signal_name
, signal_returns
, signal_prototype
)
4405 SignalObjects
.append(signal_object
)
4406 SignalNames
.append(signal_name
)
4407 SignalReturns
.append(signal_returns
)
4408 SignalFlags
.append(signal_flags
)
4409 SignalPrototypes
.append(signal_prototype
)
4412 signal_prototype
+= line
4416 def ReadObjectHierarchy(ifile
):
4417 """Reads the $MODULE-hierarchy.txt file.
4419 This contains all the GObject subclasses described in this module (and their
4421 It places them in the Objects array, and places their level
4422 in the object hierarchy in the ObjectLevels array, at the
4423 same index. GObject, the root object, has a level of 1.
4425 This also generates tree_index.sgml as it goes along.
4428 ifile (str): the input filename.
4432 ObjectLevels
[:] = []
4434 if not os
.path
.isfile(ifile
):
4435 logging
.debug('no *-hierarchy.tx')
4438 INPUT
= open(ifile
, 'r', encoding
='utf-8')
4440 # Only emit objects if they are supposed to be documented, or if
4441 # they have documented children. To implement this, we maintain a
4442 # stack of pending objects which will be emitted if a documented
4444 pending_objects
= []
4449 m1
= re
.search(r
'\S+', line
)
4453 gobject
= m1
.group(0)
4454 level
= len(line
[:m1
.start()]) // 2 + 1
4459 while pending_levels
and pending_levels
[-1] >= level
:
4460 pending_objects
.pop()
4461 pending_levels
.pop()
4463 pending_objects
.append(gobject
)
4464 pending_levels
.append(level
)
4466 if gobject
in KnownSymbols
:
4467 while len(pending_levels
) > 0:
4468 gobject
= pending_objects
.pop(0)
4469 level
= pending_levels
.pop(0)
4470 xref
= MakeXRef(gobject
)
4472 tree
.append(' ' * (level
* 4) + xref
)
4473 Objects
.append(gobject
)
4474 ObjectLevels
.append(level
)
4475 ObjectRoots
[gobject
] = root
4477 # common.LogWarning(ifile, line_number, "unknown type %s" % object)
4483 # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml"
4484 old_tree_index
= os
.path
.join(DB_OUTPUT_DIR
, "tree_index.sgml")
4485 new_tree_index
= os
.path
.join(DB_OUTPUT_DIR
, "tree_index.new")
4487 logging
.debug('got %d entries for hierarchy', len(tree
))
4489 with
open(new_tree_index
, 'w', encoding
='utf-8') as out
:
4490 out
.write(MakeDocHeader("screen"))
4491 out
.write("\n<screen>\n")
4492 out
.write(AddTreeLineArt(tree
))
4493 out
.write("\n</screen>\n")
4495 common
.UpdateFileIfChanged(old_tree_index
, new_tree_index
, 0)
4500 def ReadInterfaces(ifile
):
4501 """Reads the $MODULE.interfaces file.
4504 ifile (str): the input filename.
4509 if not os
.path
.isfile(ifile
):
4512 INPUT
= open(ifile
, 'r', encoding
='utf-8')
4516 ifaces
= line
.split()
4517 gobject
= ifaces
.pop(0)
4518 if gobject
in KnownSymbols
and KnownSymbols
[gobject
] == 1:
4521 # filter out private interfaces, but leave foreign interfaces
4522 for iface
in ifaces
:
4523 if iface
not in KnownSymbols
or KnownSymbols
[iface
] == 1:
4524 knownIfaces
.append(iface
)
4526 Interfaces
[gobject
] = ' '.join(knownIfaces
)
4527 logging
.info("Interfaces for %s: %s", gobject
, Interfaces
[gobject
])
4529 logging
.info("skipping interfaces for unknown symbol: %s", gobject
)
4534 def ReadPrerequisites(ifile
):
4535 """This reads in the $MODULE.prerequisites file.
4538 ifile (str): the input filename.
4540 Prerequisites
.clear()
4542 if not os
.path
.isfile(ifile
):
4545 INPUT
= open(ifile
, 'r', encoding
='utf-8')
4549 prereqs
= line
.split()
4550 iface
= prereqs
.pop(0)
4551 if iface
in KnownSymbols
and KnownSymbols
[iface
] == 1:
4554 # filter out private prerequisites, but leave foreign prerequisites
4555 for prereq
in prereqs
:
4556 if prereq
not in KnownSymbols
or KnownSymbols
[prereq
] == 1:
4557 knownPrereqs
.append(prereq
)
4559 Prerequisites
[iface
] = ' '.join(knownPrereqs
)
4564 def ReadArgsFile(ifile
):
4565 """Reads information about object properties
4567 It creates the arrays ArgObjects, ArgNames, ArgTypes, ArgFlags, ArgNicks and
4568 ArgBlurbs containing info on the args.
4571 ifile (str): the input filename.
4583 # Reset the args info.
4593 if not os
.path
.isfile(ifile
):
4596 INPUT
= open(ifile
, 'r', encoding
='utf-8')
4601 if line
.startswith('<ARG>'):
4613 m1
= re
.search(r
'^<NAME>(.*)</NAME>', line
)
4614 m2
= re
.search(r
'^<TYPE>(.*)</TYPE>', line
)
4615 m3
= re
.search(r
'^<RANGE>(.*)</RANGE>', line
)
4616 m4
= re
.search(r
'^<FLAGS>(.*)</FLAGS>', line
)
4617 m5
= re
.search(r
'^<NICK>(.*)</NICK>', line
)
4618 m6
= re
.search(r
'^<BLURB>(.*)</BLURB>', line
)
4619 m7
= re
.search(r
'^<DEFAULT>(.*)</DEFAULT>', line
)
4621 arg_name
= m1
.group(1)
4622 m1_1
= re
.search(r
'^(.*)::(.*)$', arg_name
)
4624 arg_object
= m1_1
.group(1)
4625 arg_name
= m1_1
.group(2).replace('_', '-')
4626 logging
.info("Found arg: %s", arg_name
)
4628 common
.LogWarning(ifile
, line_number
, "Invalid argument name: " + arg_name
)
4631 arg_type
= m2
.group(1)
4633 arg_range
= m3
.group(1)
4635 arg_flags
= m4
.group(1)
4637 arg_nick
= m5
.group(1)
4639 arg_blurb
= m6
.group(1)
4640 if arg_blurb
== "(null)":
4643 ifile
, line_number
, "Property %s:%s has no documentation." % (arg_object
, arg_name
))
4646 arg_default
= m7
.group(1)
4647 elif re
.search(r
'^</ARG>', line
):
4648 logging
.info("Found end of arg: %s::%s\n%s : %s", arg_object
, arg_name
, arg_type
, arg_flags
)
4649 ArgObjects
.append(arg_object
)
4650 ArgNames
.append(arg_name
)
4651 ArgTypes
.append(arg_type
)
4652 ArgRanges
.append(arg_range
)
4653 ArgFlags
.append(arg_flags
)
4654 ArgNicks
.append(arg_nick
)
4655 ArgBlurbs
.append(arg_blurb
)
4656 ArgDefaults
.append(arg_default
)
4662 def AddTreeLineArt(tree
):
4663 """Generate a line art tree.
4665 Add unicode lineart to a pre-indented string array and returns
4666 it as as multiline string.
4669 tree (list): of indented strings.
4672 str: multiline string with tree line art
4674 # iterate bottom up over the tree
4675 for i
in range(len(tree
) - 1, -1, -1):
4676 # count leading spaces
4677 m
= re
.search(r
'^([^<A-Za-z]*)', tree
[i
])
4678 indent
= len(m
.group(1))
4679 # replace with ╰───, if place of ╰ is not space insert ├
4681 if tree
[i
][indent
- 4] == " ":
4682 tree
[i
] = tree
[i
][:indent
- 4] + "--- " + tree
[i
][indent
:]
4684 tree
[i
] = tree
[i
][:indent
- 4] + "+-- " + tree
[i
][indent
:]
4686 # go lines up while space and insert |
4688 while j
>= 0 and tree
[j
][indent
- 4] == ' ':
4689 tree
[j
] = tree
[j
][:indent
- 4] + '|' + tree
[j
][indent
- 3:]
4692 res
= "\n".join(tree
)
4693 # unicode chars for: ╰──
4694 res
= re
.sub(r
'---', '<phrase role=\"lineart\">╰──</phrase>', res
)
4695 # unicde chars for: ├──
4696 res
= re
.sub(r
'\+--', '<phrase role=\"lineart\">├──</phrase>', res
)
4697 # unicode char for: │
4698 res
= re
.sub(r
'\|', '<phrase role=\"lineart\">│</phrase>', res
)
4703 def CheckIsObject(name
):
4704 """Check if symbols is an object.
4706 It uses the global Objects array. Note that the Objects array only contains
4707 classes in the current module and their ancestors - not all GObject classes.
4710 name (str): the object name to check.
4713 bool: True if the given name is a GObject or a subclass.
4715 root
= ObjectRoots
.get(name
)
4716 # Let GBoxed pass as an object here to get -struct appended to the id
4717 # and prevent conflicts with sections.
4718 return root
and root
!= 'GEnum' and root
!= 'GFlags'
4721 def GetSymbolSourceFile(symbol
):
4722 """Get the filename where the symbol docs where taken from."""
4723 return SourceSymbolSourceFile
.get(symbol
, '')
4726 def GetSymbolSourceLine(symbol
):
4727 """Get the file line where the symbol docs where taken from."""
4728 return SourceSymbolSourceLine
.get(symbol
, 0)