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 Extracts declarations of functions, macros, enums, structs and unions from
26 It is called with a module name, an optional source directory, an optional
27 output directory, and the header files to scan.
29 It outputs all declarations found to a file named '$MODULE-decl.txt', and the
30 list of decarations to another file '$MODULE-decl-list.txt'.
32 This second list file is typically copied to '$MODULE-sections.txt' and
33 organized into sections ready to output the XML pages.
43 # do not read files twice; checking it here permits to give both srcdir and
44 # builddir as --source-dir without fear of duplicities
49 logging
.info('options: %s', str(options
.__dict
__))
51 if not os
.path
.isdir(options
.output_dir
):
52 os
.mkdir(options
.output_dir
)
54 base_filename
= os
.path
.join(options
.output_dir
, options
.module
)
55 old_decl_list
= base_filename
+ '-decl-list.txt'
56 new_decl_list
= base_filename
+ '-decl-list.new'
57 old_decl
= base_filename
+ '-decl.txt'
58 new_decl
= base_filename
+ '-decl.new'
59 old_types
= base_filename
+ '.types'
60 new_types
= base_filename
+ '.types.new'
61 sections_file
= base_filename
+ '-sections.txt'
63 # If this is the very first run then we create the .types file automatically.
64 if not os
.path
.exists(sections_file
) and not os
.path
.exists(old_types
):
65 options
.rebuild_types
= True
71 for file in options
.headers
:
72 ScanHeader(file, section_list
, decl_list
, get_types
, options
)
74 for dir in options
.source_dir
:
75 ScanHeaders(dir, section_list
, decl_list
, get_types
, options
)
77 with
open(new_decl_list
, 'w', encoding
='utf-8') as f
:
78 for section
in sorted(section_list
.keys()):
79 f
.write(section_list
[section
])
81 with
open(new_decl
, 'w', encoding
='utf-8') as f
:
82 for decl
in decl_list
:
85 if options
.rebuild_types
:
86 with
open(new_types
, 'w', encoding
='utf-8') as f
:
87 for func
in sorted(get_types
):
90 # remove the file if empty
91 if len(get_types
) == 0:
93 if os
.path
.exists(old_types
):
94 os
.rename(old_types
, old_types
+ '.bak')
96 common
.UpdateFileIfChanged(old_types
, new_types
, True)
98 common
.UpdateFileIfChanged(old_decl_list
, new_decl_list
, True)
99 common
.UpdateFileIfChanged(old_decl
, new_decl
, True)
101 # If there is no MODULE-sections.txt file yet or we are asked to rebuild it,
102 # we copy the MODULE-decl-list.txt file into its place. The user can tweak it
103 # later if they want.
104 if options
.rebuild_sections
or not os
.path
.exists(sections_file
):
105 new_sections_file
= base_filename
+ '-sections.new'
106 shutil
.copyfile(old_decl_list
, new_sections_file
)
107 common
.UpdateFileIfChanged(sections_file
, new_sections_file
, False)
109 # If there is no MODULE-overrides.txt file we create an empty one
110 # because EXTRA_DIST in gtk-doc.make requires it.
111 overrides_file
= base_filename
+ '-overrides.txt'
112 if not os
.path
.exists(overrides_file
):
113 open(overrides_file
, 'w', encoding
='utf-8').close()
117 # Function : ScanHeaders
118 # Description : This scans a directory tree looking for header files.
120 # Arguments : $source_dir - the directory to scan.
121 # $section_list - a reference to the hashmap of sections.
124 def ScanHeaders(source_dir
, section_list
, decl_list
, get_types
, options
):
125 logging
.info('Scanning source directory: %s', source_dir
)
127 # This array holds any subdirectories found.
130 for file in sorted(os
.listdir(source_dir
)):
131 if file.startswith('.'):
133 fullname
= os
.path
.join(source_dir
, file)
134 if os
.path
.isdir(fullname
):
136 elif file.endswith('.h'):
137 ScanHeader(fullname
, section_list
, decl_list
, get_types
, options
)
139 # Now recursively scan the subdirectories.
141 matchstr
= r
'(\s|^)' + re
.escape(dir) + r
'(\s|$)'
142 if re
.search(matchstr
, options
.ignore_headers
):
144 ScanHeaders(os
.path
.join(source_dir
, dir), section_list
, decl_list
,
149 # Function : ScanHeader
150 # Description : This scans a header file, looking for declarations of
151 # functions, macros, typedefs, structs and unions, which it
152 # outputs to the decl_list.
153 # Arguments : $input_file - the header file to scan.
154 # $section_list - a map of sections.
155 # $decl_list - a list of declarations
156 # Returns : it adds declarations to the appropriate list.
159 def ScanHeader(input_file
, section_list
, decl_list
, get_types
, options
):
161 slist
= [] # Holds the resulting list of declarations.
162 title
= '' # Holds the title of the section
163 in_comment
= 0 # True if we are in a comment.
164 in_declaration
= '' # The type of declaration we are in, e.g.
165 # 'function' or 'macro'.
166 skip_block
= 0 # True if we should skip a block.
167 symbol
= None # The current symbol being declared.
168 decl
= '' # Holds the declaration of the current symbol.
169 ret_type
= None # For functions and function typedefs this
170 # holds the function's return type.
171 pre_previous_line
= '' # The pre-previous line read in - some Gnome
172 # functions have the return type on one
173 # line, the function name on the next,
174 # and the rest of the declaration after.
175 previous_line
= '' # The previous line read in - some Gnome
176 # functions have the return type on one line
177 # and the rest of the declaration after.
178 first_macro
= 1 # Used to try to skip the standard #ifdef XXX
179 # define XXX at the start of headers.
180 level
= None # Used to handle structs/unions which contain
181 # nested structs or unions.
182 internal
= 0 # Set to 1 for internal symbols, we need to
183 # fully parse, but don't add them to docs
184 forward_decls
= {} # Dict of forward declarations, we skip
185 # them if we find the real declaration
187 doc_comments
= {} # Dict of doc-comments we found.
188 # The key is lowercase symbol name, val=1.
192 deprecated_conditional_nest
= 0
193 ignore_conditional_nest
= 0
198 # Don't scan headers twice
199 canonical_input_file
= os
.path
.realpath(input_file
)
200 if canonical_input_file
in seen_headers
:
201 logging
.info('File already scanned: %s', input_file
)
204 seen_headers
[canonical_input_file
] = 1
206 file_basename
= os
.path
.split(input_file
)[1][:-2] # filename ends in .h
208 # Check if the basename is in the list of headers to ignore.
209 matchstr
= r
'(\s|^)' + re
.escape(file_basename
) + r
'\.h(\s|$)'
210 if re
.search(matchstr
, options
.ignore_headers
):
211 logging
.info('File ignored: %s', input_file
)
214 # Check if the full name is in the list of headers to ignore.
215 matchstr
= r
'(\s|^)' + re
.escape(input_file
) + r
'(\s|$)'
216 if re
.search(matchstr
, options
.ignore_headers
):
217 logging
.info('File ignored: %s', input_file
)
220 if not os
.path
.exists(input_file
):
221 logging
.warning('File does not exist: %s', input_file
)
224 logging
.info('Scanning %s', input_file
)
226 for line
in open(input_file
, 'r', encoding
='utf-8'):
227 # If this is a private header, skip it.
228 if re
.search(r
'^\s*/\*\s*<\s*private_header\s*>\s*\*/', line
):
231 # Skip to the end of the current comment.
233 logging
.info('Comment: %s', line
.strip())
235 if re
.search(r
'\*/', line
):
236 m
= re
.search(r
'\* ([a-zA-Z][a-zA-Z0-9_]+):', doc_comment
)
238 doc_comments
[m
.group(1).lower()] = 1
243 # Keep a count of #if, #ifdef, #ifndef nesting,
244 # and if we enter a deprecation-symbol-bracketed
246 m
= re
.search(r
'^\s*#\s*if(?:n?def\b|\s+!?\s*defined\s*\()\s*(\w+)', line
)
248 define_name
= m
.group(1)
249 if deprecated_conditional_nest
< 1 and re
.search(options
.deprecated_guards
, define_name
):
250 deprecated_conditional_nest
= 1
251 elif deprecated_conditional_nest
>= 1:
252 deprecated_conditional_nest
+= 1
253 if ignore_conditional_nest
== 0 and '__GTK_DOC_IGNORE__' in define_name
:
254 ignore_conditional_nest
= 1
255 elif ignore_conditional_nest
> 0:
256 ignore_conditional_nest
= 1
258 elif re
.search(r
'^\s*#\sif', line
):
259 if deprecated_conditional_nest
>= 1:
260 deprecated_conditional_nest
+= 1
262 if ignore_conditional_nest
> 0:
263 ignore_conditional_nest
+= 1
264 elif re
.search(r
'^\s*#endif', line
):
265 if deprecated_conditional_nest
>= 1:
266 deprecated_conditional_nest
-= 1
268 if ignore_conditional_nest
> 0:
269 ignore_conditional_nest
-= 1
271 # If we find a line containing _DEPRECATED, we hope that this is
272 # attribute based deprecation and also treat this as a deprecation
273 # guard, unless it's a macro definition.
274 if deprecated_conditional_nest
== 0 and '_DEPRECATED' in line
:
275 m
= re
.search(r
'^\s*#\s*(if*|define)', line
)
276 if not (m
or in_declaration
== 'enum'):
277 logging
.info('Found deprecation annotation (decl: "%s"): "%s"',
278 in_declaration
, line
.strip())
279 deprecated_conditional_nest
+= 0.1
281 # set flag that is used later when we do AddSymbolToList
282 if deprecated_conditional_nest
> 0:
283 deprecated
= '<DEPRECATED/>\n'
287 if ignore_conditional_nest
:
290 if not in_declaration
:
291 # Skip top-level comments.
292 m
= re
.search(r
'^\s*/\*', line
)
294 re
.sub(r
'^\s*/\*', '', line
)
295 if re
.search(r
'\*/', line
):
296 logging
.info('Found one-line comment: %s', line
.strip())
300 logging
.info('Found start of comment: %s', line
.strip())
303 logging
.info('no decl: %s', line
.strip())
305 # avoid generating regex with |'' (matching no string)
306 ignore_decorators
= ''
307 if options
.ignore_decorators
:
308 ignore_decorators
= '|' + options
.ignore_decorators
310 m
= re
.search(r
'^\s*#\s*define\s+(\w+)', line
)
313 r
'^\s*typedef\s+((const\s+|G_CONST_RETURN\s+)?\w+)(\s+const)?\s*(\**)\s*\(\*\s*(\w+)\)\s*\(', line
)
315 m3
= re
.search(r
'^\s*((const\s+|G_CONST_RETURN\s+)?\w+)(\s+const)?\s*(\**)\s*\(\*\s*(\w+)\)\s*\(', line
)
317 m4
= re
.search(r
'^\s*(\**)\s*\(\*\s*(\w+)\)\s*\(', line
)
319 m5
= re
.search(r
'^\s*typedef\s*((const\s+|G_CONST_RETURN\s+)?\w+)(\s+const)?\s*', previous_line
)
322 r
'^\s*(?:\b(?:extern|G_INLINE_FUNC%s)\s*)*((const\s+|G_CONST_RETURN\s+)?\w+)(\s+const)?\s*(\**)\s*\(\*\s*(\w+)\)\s*\(' % ignore_decorators
, line
)
323 m7
= re
.search(r
'^\s*enum\s+_?(\w+)\s+\{', line
)
324 m8
= re
.search(r
'^\s*typedef\s+enum', line
)
325 m9
= re
.search(r
'^\s*typedef\s+(struct|union)\s+_(\w+)\s+\2\s*;', line
)
326 m10
= re
.search(r
'^\s*(struct|union)\s+(\w+)\s*;', line
)
327 m11
= re
.search(r
'^\s*typedef\s+(struct|union)\s*\w*\s*{', line
)
328 m12
= re
.search(r
'^\s*typedef\s+(?:struct|union)\s+\w+[\s\*]+(\w+)\s*;', line
)
329 m13
= re
.search(r
'^\s*(G_GNUC_EXTENSION\s+)?typedef\s+(.+[\s\*])(\w+)(\s*\[[^\]]+\])*\s*;', line
)
331 r
'^\s*(extern|[A-Za-z_]+VAR%s)\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)\s*(const\s+)*([A-Za-z]\w*)\s*;' % ignore_decorators
, line
)
333 r
'^\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)\s*(const\s+)*([A-Za-z]\w*)\s*\=', line
)
334 m16
= re
.search(r
'.*G_DECLARE_(FINAL_TYPE|DERIVABLE_TYPE|INTERFACE)\s*\(', line
)
337 r
'^\s*(?:\b(?:extern|G_INLINE_FUNC%s)\s*)*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|union\s+|enum\s+)*\w+)([\s*]+(?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*)\s*(_[A-Za-z]\w*)\s*\(' % ignore_decorators
, line
)
340 r
'^\s*(?:\b(?:extern|G_INLINE_FUNC%s)\s*)*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|union\s+|enum\s+)*\w+)([\s*]+(?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*)\s*([A-Za-z]\w*)\s*\(' % ignore_decorators
, line
)
341 m19
= re
.search(r
'^\s*([A-Za-z]\w*)\s*\(', line
)
342 m20
= re
.search(r
'^\s*\(', line
)
343 m21
= re
.search(r
'^\s*struct\s+_?(\w+)', line
)
344 m22
= re
.search(r
'^\s*union\s+_(\w+)', line
)
351 # We assume all macros which start with '_' are private.
352 # We also try to skip the first macro if it looks like the
353 # standard #ifndef HEADER_FILE #define HEADER_FILE etc.
354 # And we only want TRUE & FALSE defined in GLib.
355 if not symbol
.startswith('_') \
356 and (not re
.search(r
'#ifndef\s+' + symbol
, previous_line
)
357 or first_macro
== 0) \
358 and ((symbol
!= 'TRUE' and symbol
!= 'FALSE')
359 or options
.module
== 'glib'):
360 in_declaration
= 'macro'
361 logging
.info('Macro: "%s"', symbol
)
363 logging
.info('skipping Macro: "%s"', symbol
)
364 in_declaration
= 'macro'
368 # TYPEDEF'D FUNCTIONS (i.e. user functions)
370 p3
= m2
.group(3) or ''
371 ret_type
= "%s%s %s" % (m2
.group(1), p3
, m2
.group(4))
373 decl
= line
[m2
.end():]
374 in_declaration
= 'user_function'
375 logging
.info('user function (1): "%s", Returns: "%s"', symbol
, ret_type
)
377 elif re
.search(r
'^\s*typedef\s*', previous_line
) and m3
:
378 p3
= m3
.group(3) or ''
379 ret_type
= '%s%s %s' % (m3
.group(1), p3
, m3
.group(4))
381 decl
= line
[m3
.end():]
382 in_declaration
= 'user_function'
383 logging
.info('user function (2): "%s", Returns: "%s"', symbol
, ret_type
)
385 elif re
.search(r
'^\s*typedef\s*', previous_line
) and m4
:
386 ret_type
= m4
.group(1)
388 decl
= line
[m4
.end():]
390 p3
= m5
.group(3) or ''
391 ret_type
= "%s%s %s" % (m5
.group(1), p3
, ret_type
)
392 in_declaration
= 'user_function'
393 logging
.info('user function (3): "%s", Returns: "%s"', symbol
, ret_type
)
395 # FUNCTION POINTER VARIABLES
397 p3
= m6
.group(3) or ''
398 ret_type
= '%s%s %s' % (m6
.group(1), p3
, m6
.group(4))
400 decl
= line
[m6
.end():]
401 in_declaration
= 'user_function'
402 logging
.info('function pointer variable: "%s", Returns: "%s"', symbol
, ret_type
)
407 re
.sub(r
'^\s*enum\s+_?(\w+)\s+\{', r
'enum \1 {', line
)
408 # We assume that 'enum _<enum_name> {' is really the
409 # declaration of enum <enum_name>.
412 in_declaration
= 'enum'
413 logging
.info('plain enum: "%s"', symbol
)
415 elif re
.search(r
'^\s*typedef\s+enum\s+_?(\w+)\s+\1\s*;', line
):
416 # We skip 'typedef enum <enum_name> _<enum_name>;' as the enum will
417 # be declared elsewhere.
418 logging
.info('skipping enum typedef: "%s"', line
)
422 in_declaration
= 'enum'
423 logging
.info('typedef enum: -')
428 # We've found a 'typedef struct _<name> <name>;'
429 # This could be an opaque data structure, so we output an
430 # empty declaration. If the structure is actually found that
431 # will override this.
432 structsym
= m9
.group(1).upper()
433 logging
.info('%s typedef: "%s"', structsym
, m9
.group(2))
434 forward_decls
[m9
.group(2)] = '<%s>\n<NAME>%s</NAME>\n%s</%s>\n' % (
435 structsym
, m9
.group(2), deprecated
, structsym
)
437 elif re
.search(r
'^\s*(?:struct|union)\s+_(\w+)\s*;', line
):
438 # Skip private structs/unions.
439 logging
.info('private struct/union')
442 # Do a similar thing for normal structs as for typedefs above.
443 # But we output the declaration as well in this case, so we
444 # can differentiate it from a typedef.
445 structsym
= m10
.group(1).upper()
446 logging
.info('%s:%s', structsym
, m10
.group(2))
447 forward_decls
[m10
.group(2)] = '<%s>\n<NAME>%s</NAME>\n%s%s</%s>\n' % (
448 structsym
, m10
.group(2), line
, deprecated
, structsym
)
454 in_declaration
= m11
.group(1)
455 logging
.info('typedef struct/union "%s"', in_declaration
)
460 logging
.info('Found struct/union(*) typedef "%s": "%s"', m12
.group(1), line
)
461 if AddSymbolToList(slist
, m12
.group(1)):
462 decl_list
.append('<TYPEDEF>\n<NAME>%s</NAME>\n%s%s</TYPEDEF>\n' % (m12
.group(1), deprecated
, line
))
465 if m13
.group(2).split()[0] not in ('struct', 'union'):
466 logging
.info('Found typedef: "%s"', line
)
467 if AddSymbolToList(slist
, m13
.group(3)):
469 '<TYPEDEF>\n<NAME>%s</NAME>\n%s%s</TYPEDEF>\n' % (m13
.group(3), deprecated
, line
))
470 elif re
.search(r
'^\s*typedef\s+', line
):
471 logging
.info('Skipping typedef: "%s"', line
)
473 # VARIABLES (extern'ed variables)
476 symbol
= m14
.group(6)
477 line
= re
.sub(r
'^\s*([A-Za-z_]+VAR)\b', r
'extern', line
)
479 logging
.info('Possible extern var "%s": "%s"', symbol
, decl
)
480 if AddSymbolToList(slist
, symbol
):
481 decl_list
.append('<VARIABLE>\n<NAME>%s</NAME>\n%s%s</VARIABLE>\n' % (symbol
, deprecated
, decl
))
486 symbol
= m15
.group(5)
488 logging
.info('Possible global var" %s": "%s"', symbol
, decl
)
489 if AddSymbolToList(slist
, symbol
):
490 decl_list
.append('<VARIABLE>\n<NAME>%s</NAME>\n%s%s</VARIABLE>\n' % (symbol
, deprecated
, decl
))
495 in_declaration
= 'g-declare'
496 symbol
= 'G_DECLARE_' + m16
.group(1)
497 decl
= line
[m16
.end():]
501 # We assume that functions which start with '_' are private, so
504 ret_type
= m17
.group(1)
506 ret_type
+= ' ' + m17
.group(2)
507 symbol
= m17
.group(3)
508 decl
= line
[m17
.end():]
509 logging
.info('internal Function: "%s", Returns: "%s""%s"', symbol
, m17
.group(1), m17
.group(2))
510 in_declaration
= 'function'
512 if line
.strip().startswith('G_INLINE_FUNC'):
513 logging
.info('skip block after inline function')
514 # now we we need to skip a whole { } block
518 ret_type
= m18
.group(1)
520 ret_type
+= ' ' + m18
.group(2)
521 symbol
= m18
.group(3)
522 decl
= line
[m18
.end():]
523 logging
.info('Function (1): "%s", Returns: "%s""%s"', symbol
, m18
.group(1), m18
.group(2))
524 in_declaration
= 'function'
525 if line
.strip().startswith('G_INLINE_FUNC'):
526 logging
.info('skip block after inline function')
527 # now we we need to skip a whole { } block
530 # Try to catch function declarations which have the return type on
531 # the previous line. But we don't want to catch complete functions
532 # which have been declared G_INLINE_FUNC, e.g. g_bit_nth_lsf in
533 # glib, or 'static inline' functions.
535 symbol
= m19
.group(1)
536 decl
= line
[m19
.end():]
538 previous_line_strip
= previous_line
.strip()
539 previous_line_words
= previous_line_strip
.split()
541 if not previous_line_strip
.startswith('G_INLINE_FUNC'):
542 if not previous_line_words
or previous_line_words
[0] != 'static':
544 pm
= re
.search(r
'^\s*(?:\b(?:extern%s)\s*)*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|union\s+|enum\s+)*\w+)((?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*)\s*$' %
545 ignore_decorators
, previous_line
)
547 ret_type
= pm
.group(1)
549 ret_type
+= ' ' + pm
.group(2)
550 logging
.info('Function (2): "%s", Returns: "%s"', symbol
, ret_type
)
551 in_declaration
= 'function'
553 logging
.info('skip block after inline function')
554 # now we we need to skip a whole { } block
557 pm
= re
.search(r
'^\s*(?:\b(?:extern|static|inline%s)\s*)*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|union\s+|enum\s+)*\w+)((?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*)\s*$' %
558 ignore_decorators
, previous_line
)
560 ret_type
= pm
.group(1)
562 ret_type
+= ' ' + pm
.group(2)
563 logging
.info('Function (3): "%s", Returns: "%s"', symbol
, ret_type
)
564 in_declaration
= 'function'
566 if not previous_line_words
or previous_line_words
[0] != 'static':
567 logging
.info('skip block after inline function')
568 # now we we need to skip a whole { } block
571 pm
= re
.search(r
'^\s*(?:\b(?:extern|G_INLINE_FUNC%s)\s*)*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|union\s+|enum\s+)*\w+)((?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*)\s*$' %
572 ignore_decorators
, previous_line
)
574 ret_type
= pm
.group(1)
576 ret_type
+= ' ' + pm
.group(2)
577 logging
.info('Function (4): "%s", Returns: "%s"', symbol
, ret_type
)
578 in_declaration
= 'function'
580 # Try to catch function declarations with the return type and name
581 # on the previous line(s), and the start of the parameters on this.
583 decl
= line
[m20
.end():]
585 r
'^\s*(?:\b(?:extern|G_INLINE_FUNC%s)\s*)*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|enum\s+)*\w+)(\s+\*+|\*+|\s)\s*([A-Za-z]\w*)\s*$' % ignore_decorators
, previous_line
)
586 ppm
= re
.search(r
'^\s*(?:\b(?:extern|G_INLINE_FUNC%s)\s*)*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|struct\s+|union\s+|enum\s+)*\w+(?:\**\s+\**(?:const|G_CONST_RETURN))?(?:\s+|\s*\*+))\s*$' %
587 ignore_decorators
, pre_previous_line
)
589 ret_type
= pm
.group(1) + ' ' + pm
.group(2)
591 in_declaration
= 'function'
592 logging
.info('Function (5): "%s", Returns: "%s"', symbol
, ret_type
)
594 elif re
.search(r
'^\s*\w+\s*$', previous_line
) and ppm
:
595 ret_type
= ppm
.group(1)
596 ret_type
= re
.sub(r
'\s*\n', '', ret_type
, flags
=re
.MULTILINE
)
597 in_declaration
= 'function'
599 symbol
= previous_line
600 symbol
= re
.sub(r
'^\s+', '', symbol
)
601 symbol
= re
.sub(r
'\s*\n', '', symbol
, flags
=re
.MULTILINE
)
602 logging
.info('Function (6): "%s", Returns: "%s"', symbol
, ret_type
)
604 # } elsif (m/^extern\s+/) {
605 # print "DEBUG: Skipping extern: $_"
608 elif re
.search(r
'^\s*struct\s+_?(\w+)\s*\*', line
):
609 # Skip 'struct _<struct_name> *', since it could be a
610 # return type on its own line.
613 # We assume that 'struct _<struct_name>' is really the
614 # declaration of struct <struct_name>.
615 symbol
= m21
.group(1)
617 # we will find the correct level as below we do $level += tr/{//
619 in_declaration
= 'struct'
620 logging
.info('Struct(_): "%s"', symbol
)
623 elif re
.search(r
'^\s*union\s+_(\w+)\s*\*', line
):
624 # Skip 'union _<union_name> *' (see above)
627 symbol
= m22
.group(1)
630 in_declaration
= 'union'
631 logging
.info('Union(_): "%s"', symbol
)
633 logging
.info('in decl: skip=%s %s', skip_block
, line
.strip())
634 # If we were already in the middle of a declaration, we simply add
635 # the current line onto the end of it.
639 # Remove all nested pairs of curly braces.
640 brace_remover
= r
'{[^{]*}'
641 bm
= re
.search(brace_remover
, line
)
643 line
= re
.sub(brace_remover
, '', line
)
644 bm
= re
.search(brace_remover
, line
)
645 # Then hope at most one remains in the line...
646 bm
= re
.search(r
'(.*?){', line
)
654 # this is a hack to detect the end of declaration
657 logging
.info('2: ---')
663 if in_declaration
== "g-declare":
664 dm
= re
.search(r
'\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\).*$', decl
)
665 # FIXME the original code does s// stuff here and we don't. Is it necessary?
667 ModuleObjName
= dm
.group(1)
668 module_obj_name
= dm
.group(2)
669 if options
.rebuild_types
:
670 get_types
.append(module_obj_name
+ '_get_type')
671 forward_decls
[ModuleObjName
] = '<STRUCT>\n<NAME>%s</NAME>\n%s</STRUCT>\n' % (ModuleObjName
, deprecated
)
672 if symbol
.startswith('G_DECLARE_DERIVABLE'):
673 forward_decls
[ModuleObjName
+ 'Class'] = '<STRUCT>\n<NAME>%sClass</NAME>\n%s</STRUCT>\n' % (
674 ModuleObjName
, deprecated
)
675 if symbol
.startswith('G_DECLARE_INTERFACE'):
676 forward_decls
[ModuleObjName
+ 'Interface'] = '<STRUCT>\n<NAME>%sInterface</NAME>\n%s</STRUCT>\n' % (
677 ModuleObjName
, deprecated
)
680 # Note that sometimes functions end in ') G_GNUC_PRINTF (2, 3);' or
681 # ') __attribute__ (...);'.
682 if in_declaration
== 'function':
683 regex
= r
'\)\s*(G_GNUC_.*|.*DEPRECATED.*%s\s*|__attribute__\s*\(.*\)\s*)*;.*$' % ignore_decorators
684 pm
= re
.search(regex
, decl
, flags
=re
.MULTILINE
)
686 logging
.info('scrubbing:[%s]', decl
.strip())
687 decl
= re
.sub(regex
, '', decl
, flags
=re
.MULTILINE
)
688 logging
.info('scrubbed:[%s]', decl
.strip())
690 decl
= re
.sub(r
'/\*.*?\*/', '', decl
, flags
=re
.MULTILINE
) # remove comments.
691 decl
= re
.sub(r
'\s*\n\s*(?!$)', ' ', decl
, flags
=re
.MULTILINE
)
692 # consolidate whitespace at start/end of lines.
694 ret_type
= re
.sub(r
'/\*.*?\*/', '', ret_type
) # remove comments in ret type.
695 if AddSymbolToList(slist
, symbol
):
696 decl_list
.append('<FUNCTION>\n<NAME>%s</NAME>\n%s<RETURNS>%s</RETURNS>\n%s\n</FUNCTION>\n' %
697 (symbol
, deprecated
, ret_type
, decl
))
698 if options
.rebuild_types
:
699 # check if this looks like a get_type function and if so remember
700 if symbol
.endswith('_get_type') and 'GType' in ret_type
and re
.search(r
'^(void|)$', decl
):
702 "Adding get-type: [%s] [%s] [%s]\tfrom %s", ret_type
, symbol
, decl
, input_file
)
703 get_types
.append(symbol
)
706 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
710 if in_declaration
== 'user_function':
711 if re
.search(r
'\).*$', decl
):
712 decl
= re
.sub(r
'\).*$', '', decl
)
713 if AddSymbolToList(slist
, symbol
):
714 decl_list
.append('<USER_FUNCTION>\n<NAME>%s</NAME>\n%s<RETURNS>%s</RETURNS>\n%s</USER_FUNCTION>\n' %
715 (symbol
, deprecated
, ret_type
, decl
))
716 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
719 if in_declaration
== 'macro':
720 if not re
.search(r
'\\\s*$', decl
):
722 if AddSymbolToList(slist
, symbol
):
723 decl_list
.append('<MACRO>\n<NAME>%s</NAME>\n%s%s</MACRO>\n' % (symbol
, deprecated
, decl
))
726 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
729 if in_declaration
== 'enum':
730 em
= re
.search(r
'\}\s*(\w+)?;\s*$', decl
)
734 if AddSymbolToList(slist
, symbol
):
735 decl_list
.append('<ENUM>\n<NAME>%s</NAME>\n%s%s</ENUM>\n' % (symbol
, deprecated
, decl
))
736 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
739 # We try to handle nested stucts/unions, but unmatched brackets in
740 # comments will cause problems.
741 if in_declaration
== 'struct' or in_declaration
== 'union':
742 sm
= re
.search(r
'\n\}\s*(\w*);\s*$', decl
)
743 if level
<= 1 and sm
:
747 bm
= re
.search(r
'^(\S+)(Class|Iface|Interface)\b', symbol
)
749 objectname
= bm
.group(1)
750 logging
.info('Found object: "%s"', objectname
)
751 title
= '<TITLE>%s</TITLE>' % objectname
753 logging
.info('Store struct: "%s"', symbol
)
754 if AddSymbolToList(slist
, symbol
):
755 structsym
= in_declaration
.upper()
756 decl_list
.append('<%s>\n<NAME>%s</NAME>\n%s%s</%s>\n' %
757 (structsym
, symbol
, deprecated
, decl
, structsym
))
758 if symbol
in forward_decls
:
759 del forward_decls
[symbol
]
760 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
763 # We use tr to count the brackets in the line, and adjust
764 # $level accordingly.
765 level
+= line
.count('{')
766 level
-= line
.count('}')
767 logging
.info('struct/union level : %d', level
)
769 pre_previous_line
= previous_line
772 # print remaining forward declarations
773 for symbol
in sorted(forward_decls
.keys()):
774 if forward_decls
[symbol
]:
775 AddSymbolToList(slist
, symbol
)
776 decl_list
.append(forward_decls
[symbol
])
779 slist
= [title
] + slist
781 logging
.info("Scanning %s done", input_file
)
783 # Try to separate the standard macros and functions, placing them at the
784 # end of the current section, in a subsection named 'Standard'.
785 # do this in a loop to catch object, enums and flags
786 klass
= lclass
= prefix
= lprefix
= None
788 liststr
= '\n'.join(s
for s
in slist
if s
) + '\n'
790 m
= re
.search(r
'^(\S+)_IS_(\S*)_CLASS\n', liststr
, flags
=re
.MULTILINE
)
791 m2
= re
.search(r
'^(\S+)_IS_(\S*)\n', liststr
, flags
=re
.MULTILINE
)
792 m3
= re
.search(r
'^(\S+?)_(\S*)_get_type\n', liststr
, flags
=re
.MULTILINE
)
795 lprefix
= prefix
.lower()
797 lclass
= klass
.lower()
798 logging
.info("Found gobject type '%s_%s' from is_class macro", prefix
, klass
)
801 lprefix
= prefix
.lower()
803 lclass
= klass
.lower()
804 logging
.info("Found gobject type '%s_%s' from is_ macro", prefix
, klass
)
806 lprefix
= m3
.group(1)
807 prefix
= lprefix
.upper()
809 klass
= lclass
.upper()
810 logging
.info("Found gobject type '%s_%s' from get_type function", prefix
, klass
)
815 cclass
= cclass
.replace('_', '')
816 mtype
= lprefix
+ cclass
818 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'^%sPrivate\n' % mtype
)
820 # We only leave XxYy* in the normal section if they have docs
821 if mtype
not in doc_comments
:
822 logging
.info(" Hide instance docs for %s", mtype
)
823 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'^%s\n' % mtype
)
825 if mtype
+ 'class' not in doc_comments
:
826 logging
.info(" Hide class docs for %s", mtype
)
827 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'^%sClass\n' % mtype
)
829 if mtype
+ 'interface' not in doc_comments
:
830 logging
.info(" Hide iface docs for %s", mtype
)
831 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'%sInterface\n' % mtype
)
833 if mtype
+ 'iface' not in doc_comments
:
834 logging
.info(" Hide iface docs for " + mtype
)
835 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'%sIface\n' % mtype
)
837 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_IS_%s\n' % klass
)
838 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_TYPE_%s\n' % klass
)
839 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_get_type\n' % lclass
)
840 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_CLASS\n' % klass
)
841 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_IS_%s_CLASS\n' % klass
)
842 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_GET_CLASS\n' % klass
)
843 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_GET_IFACE\n' % klass
)
844 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_GET_INTERFACE\n' % klass
)
845 # We do this one last, otherwise it tends to be caught by the IS_$class macro
846 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s\n' % klass
)
848 logging
.info('Decl:%s---', liststr
)
849 logging
.info('Std :%s---', ''.join(sorted(standard_decl
)))
850 if len(standard_decl
):
852 liststr
+= '<SUBSECTION Standard>\n' + ''.join(sorted(standard_decl
))
855 if file_basename
not in section_list
:
856 section_list
[file_basename
] = ''
857 section_list
[file_basename
] += "<SECTION>\n<FILE>%s</FILE>\n%s</SECTION>\n\n" % (file_basename
, liststr
)
860 def replace_once(liststr
, standard_decl
, regex
):
861 mre
= re
.search(regex
, liststr
, flags
=re
.IGNORECASE | re
.MULTILINE
)
863 standard_decl
.append(mre
.group(0))
864 liststr
= re
.sub(regex
, '', liststr
, flags
=re
.IGNORECASE | re
.MULTILINE
)
865 return liststr
, standard_decl
868 def replace_all(liststr
, standard_decl
, regex
):
869 mre
= re
.search(regex
, liststr
, flags
=re
.MULTILINE
)
871 standard_decl
.append(mre
.group(0))
872 liststr
= re
.sub(regex
, '', liststr
, flags
=re
.MULTILINE
)
873 mre
= re
.search(regex
, liststr
, flags
=re
.MULTILINE
)
874 return liststr
, standard_decl
877 def AddSymbolToList(slist
, symbol
):
878 """ Adds symbol to list of declaration if not already present.
881 slist: The list of symbols.
882 symbol: The symbol to add to the list.
885 # logging.info('Symbol %s already in list. skipping', symbol)
886 # we return False to skip outputting another entry to -decl.txt
887 # this is to avoid redeclarations (e.g. in conditional sections).