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.
42 # do not read files twice; checking it here permits to give both srcdir and
43 # builddir as --source-dir without fear of duplicities
48 # logging.basicConfig(level=logging.INFO)
50 if not os
.path
.isdir(options
.output_dir
):
51 os
.mkdir(options
.output_dir
)
53 base_filename
= os
.path
.join(options
.output_dir
, options
.module
)
54 old_decl_list
= base_filename
+ '-decl-list.txt'
55 new_decl_list
= base_filename
+ '-decl-list.new'
56 old_decl
= base_filename
+ '-decl.txt'
57 new_decl
= base_filename
+ '-decl.new'
58 old_types
= base_filename
+ '.types'
59 new_types
= base_filename
+ '.types.new'
60 sections_file
= base_filename
+ '-sections.txt'
62 # If this is the very first run then we create the .types file automatically.
63 if not os
.path
.exists(sections_file
) and not os
.path
.exists(old_types
):
64 options
.rebuild_types
= True
70 for file in options
.headers
:
71 ScanHeader(file, section_list
, decl_list
, get_types
, options
)
73 for dir in options
.source_dir
:
74 ScanHeaders(dir, section_list
, decl_list
, get_types
, options
)
76 with
open(new_decl_list
, 'w') as f
:
77 for section
in sorted(section_list
.keys()):
78 f
.write(section_list
[section
])
80 with
open(new_decl
, 'w') as f
:
81 for decl
in decl_list
:
84 if options
.rebuild_types
:
85 with
open(new_types
, 'w') as f
:
86 for func
in sorted(get_types
):
89 # remove the file if empty
90 if len(get_types
) == 0:
92 if os
.path
.exists(old_types
):
93 os
.rename(old_types
, old_types
+ '.bak')
95 common
.UpdateFileIfChanged(old_types
, new_types
, True)
97 common
.UpdateFileIfChanged(old_decl_list
, new_decl_list
, True)
98 common
.UpdateFileIfChanged(old_decl
, new_decl
, True)
100 # If there is no MODULE-sections.txt file yet or we are asked to rebuild it,
101 # we copy the MODULE-decl-list.txt file into its place. The user can tweak it
102 # later if they want.
103 if options
.rebuild_sections
or not os
.path
.exists(sections_file
):
104 common
.UpdateFileIfChanged(sections_file
, old_decl_list
, False)
106 # If there is no MODULE-overrides.txt file we create an empty one
107 # because EXTRA_DIST in gtk-doc.make requires it.
108 overrides_file
= base_filename
+ '-overrides.txt'
109 if not os
.path
.exists(overrides_file
):
110 open(overrides_file
, 'w').close()
114 # Function : ScanHeaders
115 # Description : This scans a directory tree looking for header files.
117 # Arguments : $source_dir - the directory to scan.
118 # $section_list - a reference to the hashmap of sections.
121 def ScanHeaders(source_dir
, section_list
, decl_list
, get_types
, options
):
122 logging
.info('Scanning source directory: %s', source_dir
)
124 # This array holds any subdirectories found.
127 for file in os
.listdir(source_dir
):
128 if file.startswith('.'):
130 fullname
= os
.path
.join(source_dir
, file)
131 if os
.path
.isdir(fullname
):
133 elif file.endswith('.h'):
134 ScanHeader(fullname
, section_list
, decl_list
, get_types
, options
)
136 # Now recursively scan the subdirectories.
138 matchstr
= r
'(\s|^)' + re
.escape(dir) + r
'(\s|$)'
139 if re
.search(matchstr
, options
.ignore_headers
):
141 ScanHeaders(os
.path
.join(source_dir
, dir), section_list
, decl_list
,
146 # Function : ScanHeader
147 # Description : This scans a header file, looking for declarations of
148 # functions, macros, typedefs, structs and unions, which it
149 # outputs to the decl_list.
150 # Arguments : $input_file - the header file to scan.
151 # $section_list - a map of sections.
152 # $decl_list - a list of declarations
153 # Returns : it adds declarations to the appropriate list.
156 def ScanHeader(input_file
, section_list
, decl_list
, get_types
, options
):
158 slist
= [] # Holds the resulting list of declarations.
159 title
= '' # Holds the title of the section
160 in_comment
= 0 # True if we are in a comment.
161 in_declaration
= '' # The type of declaration we are in, e.g.
162 # 'function' or 'macro'.
163 skip_block
= 0 # True if we should skip a block.
164 symbol
= None # The current symbol being declared.
165 decl
= '' # Holds the declaration of the current symbol.
166 ret_type
= None # For functions and function typedefs this
167 # holds the function's return type.
168 pre_previous_line
= '' # The pre-previous line read in - some Gnome
169 # functions have the return type on one
170 # line, the function name on the next,
171 # and the rest of the declaration after.
172 previous_line
= '' # The previous line read in - some Gnome
173 # functions have the return type on one line
174 # and the rest of the declaration after.
175 first_macro
= 1 # Used to try to skip the standard #ifdef XXX
176 # define XXX at the start of headers.
177 level
= None # Used to handle structs/unions which contain
178 # nested structs or unions.
179 internal
= 0 # Set to 1 for internal symbols, we need to
180 # fully parse, but don't add them to docs
181 forward_decls
= {} # hashtable of forward declarations, we skip
182 # them if we find the real declaration
184 doc_comments
= {} # hastable of doc-comment we found. We can
185 # use that to put element to the right
186 # sction in the generated section-file
190 deprecated_conditional_nest
= 0
191 ignore_conditional_nest
= 0
196 # Don't scan headers twice
197 canonical_input_file
= os
.path
.realpath(input_file
)
198 if canonical_input_file
in seen_headers
:
199 logging
.info('File already scanned: %s', input_file
)
202 seen_headers
[canonical_input_file
] = 1
204 file_basename
= os
.path
.split(input_file
)[1][:-2] # filename ends in .h
206 # Check if the basename is in the list of headers to ignore.
207 matchstr
= r
'(\s|^)' + re
.escape(file_basename
) + r
'\.h(\s|$)'
208 if re
.search(matchstr
, options
.ignore_headers
):
209 logging
.info('File ignored: %s', input_file
)
212 # Check if the full name is in the list of headers to ignore.
213 matchstr
= r
'(\s|^)' + re
.escape(input_file
) + r
'(\s|$)'
214 if re
.search(matchstr
, options
.ignore_headers
):
215 logging
.info('File ignored: %s', input_file
)
218 if not os
.path
.exists(input_file
):
219 logging
.warning('File does not exist: %s', input_file
)
222 logging
.info('Scanning %s', input_file
)
224 for line
in open(input_file
):
225 # If this is a private header, skip it.
226 if re
.search(r
'%^\s*/\*\s*<\s*private_header\s*>\s*\*/', line
):
229 # Skip to the end of the current comment.
231 logging
.info('Comment: %s', line
)
233 if re
.search(r
'\*/', line
):
234 m
= re
.search(r
'\* ([a-zA-Z][a-zA-Z0-9_]+):/', doc_comment
)
236 doc_comments
[m
.group(1).lower()] = 1
241 # Keep a count of #if, #ifdef, #ifndef nesting,
242 # and if we enter a deprecation-symbol-bracketed
244 m
= re
.search(r
'^\s*#\s*if(?:n?def\b|\s+!?\s*defined\s*\()\s*(\w+)', line
)
246 define_name
= m
.group(1)
247 if deprecated_conditional_nest
< 1 and re
.search(options
.deprecated_guards
, define_name
):
248 deprecated_conditional_nest
= 1
249 elif deprecated_conditional_nest
>= 1:
250 deprecated_conditional_nest
+= 1
251 if ignore_conditional_nest
== 0 and '__GTK_DOC_IGNORE__' in define_name
:
252 ignore_conditional_nest
= 1
253 elif ignore_conditional_nest
> 0:
254 ignore_conditional_nest
= 1
256 elif re
.search(r
'^\s*#\sif', line
):
257 if deprecated_conditional_nest
>= 1:
258 deprecated_conditional_nest
+= 1
260 if ignore_conditional_nest
> 0:
261 ignore_conditional_nest
+= 1
262 elif re
.search(r
'^\s*#endif', line
):
263 if deprecated_conditional_nest
>= 1:
264 deprecated_conditional_nest
-= 1
266 if ignore_conditional_nest
> 0:
267 ignore_conditional_nest
-= 1
269 # If we find a line containing _DEPRECATED, we hope that this is
270 # attribute based deprecation and also treat this as a deprecation
271 # guard, unless it's a macro definition.
272 if deprecated_conditional_nest
== 0 and '_DEPRECATED' in line
:
273 m
= re
.search(r
'^\s*#\s*(if*|define)', line
)
274 if not (m
or in_declaration
== 'enum'):
275 logging
.info('Found deprecation annotation (decl: "%s"): "%s"', in_declaration
, line
)
276 deprecated_conditional_nest
+= 0.1
278 # set flag that is used later when we do AddSymbolToList
279 if deprecated_conditional_nest
> 0:
280 deprecated
= '<DEPRECATED/>\n'
284 if ignore_conditional_nest
:
287 if not in_declaration
:
288 # Skip top-level comments.
289 m
= re
.search(r
'^\s*/\*', line
)
291 re
.sub(r
'^\s*/\*', '', line
)
292 if re
.search(r
'\*/', line
):
293 logging
.info('Found one-line comment: %s', line
)
297 logging
.info('Found start of comment: %s', line
)
300 logging
.info('no decl: %s', line
.strip())
302 # avoid generating regex with |'' (matching no string)
303 ignore_decorators
= ''
304 if options
.ignore_decorators
:
305 ignore_decorators
= '|' + options
.ignore_decorators
307 m
= re
.search(r
'^\s*#\s*define\s+(\w+)', line
)
310 r
'^\s*typedef\s+((const\s+|G_CONST_RETURN\s+)?\w+)(\s+const)?\s*(\**)\s*\(\*\s*(\w+)\)\s*\(', line
)
312 m3
= re
.search(r
'^\s*((const\s+|G_CONST_RETURN\s+)?\w+)(\s+const)?\s*(\**)\s*\(\*\s*(\w+)\)\s*\(', line
)
314 m4
= re
.search(r
'^\s*(\**)\s*\(\*\s*(\w+)\)\s*\(', line
)
316 m5
= re
.search(r
'^\s*typedef\s*((const\s+|G_CONST_RETURN\s+)?\w+)(\s+const)?\s*', previous_line
)
319 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
)
320 m7
= re
.search(r
'^\s*enum\s+_?(\w+)\s+\{', line
)
321 m8
= re
.search(r
'^\s*typedef\s+enum', line
)
322 m9
= re
.search(r
'^\s*typedef\s+(struct|union)\s+_(\w+)\s+\2\s*;', line
)
323 m10
= re
.search(r
'^\s*(struct|union)\s+(\w+)\s*;', line
)
324 m11
= re
.search(r
'^\s*typedef\s+(struct|union)\s*\w*\s*{', line
)
325 m12
= re
.search(r
'^\s*typedef\s+(?:struct|union)\s+\w+[\s\*]+(\w+)\s*;', line
)
326 m13
= re
.search(r
'^\s*(G_GNUC_EXTENSION\s+)?typedef\s+(.+[\s\*])(\w+)(\s*\[[^\]]+\])*\s*;', line
)
328 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
)
330 r
'^\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)\s*(const\s+)*([A-Za-z]\w*)\s*\=', line
)
331 m16
= re
.search(r
'.*G_DECLARE_(FINAL_TYPE|DERIVABLE_TYPE|INTERFACE)\s*\(', line
)
334 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
)
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
)
338 m19
= re
.search(r
'^\s*([A-Za-z]\w*)\s*\(', line
)
339 m20
= re
.search(r
'^\s*\(', line
)
340 m21
= re
.search(r
'^\s*struct\s+_?(\w+)', line
)
341 m22
= re
.search(r
'^\s*union\s+_(\w+)', line
)
348 # We assume all macros which start with '_' are private.
349 # We also try to skip the first macro if it looks like the
350 # standard #ifndef HEADER_FILE #define HEADER_FILE etc.
351 # And we only want TRUE & FALSE defined in GLib.
352 if not symbol
.startswith('_') \
353 and (not re
.search(r
'#ifndef\s+' + symbol
, previous_line
)
354 or first_macro
== 0) \
355 and ((symbol
!= 'TRUE' and symbol
!= 'FALSE')
356 or options
.module
== 'glib'):
357 in_declaration
= 'macro'
358 logging
.info('Macro: "%s"', symbol
)
360 logging
.info('skipping Macro: "%s"', symbol
)
361 in_declaration
= 'macro'
365 # TYPEDEF'D FUNCTIONS (i.e. user functions)
367 p3
= m2
.group(3) or ''
368 ret_type
= "%s%s %s" % (m2
.group(1), p3
, m2
.group(4))
370 decl
= line
[m2
.end():]
371 in_declaration
= 'user_function'
372 logging
.info('user function (1): "%s", Returns: "%s"', symbol
, ret_type
)
374 elif re
.search(r
'^\s*typedef\s*', previous_line
) and m3
:
375 p3
= m3
.group(3) or ''
376 ret_type
= '%s%s %s' % (m3
.group(1), p3
, m3
.group(4))
378 decl
= line
[m3
.end():]
379 in_declaration
= 'user_function'
380 logging
.info('user function (2): "%s", Returns: "%s"', symbol
, ret_type
)
382 elif re
.search(r
'^\s*typedef\s*', previous_line
) and m4
:
383 ret_type
= m4
.group(1)
385 decl
= line
[m4
.end():]
387 p3
= m5
.group(3) or ''
388 ret_type
= "%s%s %s" % (m5
.group(1), p3
, ret_type
)
389 in_declaration
= 'user_function'
390 logging
.info('user function (3): "%s", Returns: "%s"', symbol
, ret_type
)
392 # FUNCTION POINTER VARIABLES
394 p3
= m6
.group(3) or ''
395 ret_type
= '%s%s %s' % (m6
.group(1), p3
, m6
.group(4))
397 decl
= line
[m6
.end():]
398 in_declaration
= 'user_function'
399 logging
.info('function pointer variable: "%s", Returns: "%s"', symbol
, ret_type
)
404 re
.sub(r
'^\s*enum\s+_?(\w+)\s+\{', r
'enum \1 {', line
)
405 # We assume that 'enum _<enum_name> {' is really the
406 # declaration of enum <enum_name>.
409 in_declaration
= 'enum'
410 logging
.info('plain enum: "%s"', symbol
)
412 elif re
.search(r
'^\s*typedef\s+enum\s+_?(\w+)\s+\1\s*;', line
):
413 # We skip 'typedef enum <enum_name> _<enum_name>;' as the enum will
414 # be declared elsewhere.
415 logging
.info('skipping enum typedef: "%s"', line
)
419 in_declaration
= 'enum'
420 logging
.info('typedef enum: -')
425 # We've found a 'typedef struct _<name> <name>;'
426 # This could be an opaque data structure, so we output an
427 # empty declaration. If the structure is actually found that
428 # will override this.
429 structsym
= m9
.group(1).upper()
430 logging
.info('%s typedef: "%s"', structsym
, m9
.group(2))
431 forward_decls
[m9
.group(2)] = '<%s>\n<NAME>%s</NAME>\n%s</%s>\n' % (
432 structsym
, m9
.group(2), deprecated
, structsym
)
434 elif re
.search(r
'^\s*(?:struct|union)\s+_(\w+)\s*;', line
):
435 # Skip private structs/unions.
436 logging
.info('private struct/union')
439 # Do a similar thing for normal structs as for typedefs above.
440 # But we output the declaration as well in this case, so we
441 # can differentiate it from a typedef.
442 structsym
= m10
.group(1).upper()
443 logging
.info('%s:%s', structsym
, m10
.group(2))
444 forward_decls
[m10
.group(2)] = '<%s>\n<NAME>%s</NAME>\n%s%s</%s>\n' % (
445 structsym
, m10
.group(2), line
, deprecated
, structsym
)
451 in_declaration
= m11
.group(1)
452 logging
.info('typedef struct/union "%s"', in_declaration
)
457 logging
.info('Found struct/union(*) typedef "%s": "%s"', m12
.group(1), line
)
458 if AddSymbolToList(slist
, m12
.group(1)):
459 decl_list
.append('<TYPEDEF>\n<NAME>%s</NAME>\n%s%s</TYPEDEF>\n' % (m12
.group(1), deprecated
, line
))
462 if m13
.group(2).split()[0] not in ('struct', 'union'):
463 logging
.info('Found typedef: "%s"', line
)
464 if AddSymbolToList(slist
, m13
.group(3)):
466 '<TYPEDEF>\n<NAME>%s</NAME>\n%s%s</TYPEDEF>\n' % (m13
.group(3), deprecated
, line
))
467 elif re
.search(r
'^\s*typedef\s+', line
):
468 logging
.info('Skipping typedef: "%s"', line
)
470 # VARIABLES (extern'ed variables)
473 symbol
= m14
.group(6)
474 line
= re
.sub(r
'^\s*([A-Za-z_]+VAR)\b', r
'extern', line
)
476 logging
.info('Possible extern var "%s": "%s"', symbol
, decl
)
477 if AddSymbolToList(slist
, symbol
):
478 decl_list
.append('<VARIABLE>\n<NAME>%s</NAME>\n%s%s</VARIABLE>\n' % (symbol
, deprecated
, decl
))
483 symbol
= m15
.group(5)
485 logging
.info('Possible global var" %s": "%s"', symbol
, decl
)
486 if AddSymbolToList(slist
, symbol
):
487 decl_list
.append('<VARIABLE>\n<NAME>%s</NAME>\n%s%s</VARIABLE>\n' % (symbol
, deprecated
, decl
))
492 in_declaration
= 'g-declare'
493 symbol
= 'G_DECLARE_' + m16
.group(1)
494 decl
= line
[m16
.end():]
498 # We assume that functions which start with '_' are private, so
501 ret_type
= m17
.group(1)
503 ret_type
+= ' ' + m17
.group(2)
504 symbol
= m17
.group(3)
505 decl
= line
[m17
.end():]
506 logging
.info('internal Function: "%s", Returns: "%s""%s"', symbol
, m17
.group(1), m17
.group(2))
507 in_declaration
= 'function'
509 if line
.strip().startswith('G_INLINE_FUNC'):
510 logging
.info('skip block after inline function')
511 # now we we need to skip a whole { } block
515 ret_type
= m18
.group(1)
517 ret_type
+= ' ' + m18
.group(2)
518 symbol
= m18
.group(3)
519 decl
= line
[m18
.end():]
520 logging
.info('Function (1): "%s", Returns: "%s""%s"', symbol
, m18
.group(1), m18
.group(2))
521 in_declaration
= 'function'
522 if line
.strip().startswith('G_INLINE_FUNC'):
523 logging
.info('skip block after inline function')
524 # now we we need to skip a whole { } block
527 # Try to catch function declarations which have the return type on
528 # the previous line. But we don't want to catch complete functions
529 # which have been declared G_INLINE_FUNC, e.g. g_bit_nth_lsf in
530 # glib, or 'static inline' functions.
532 symbol
= m19
.group(1)
533 decl
= line
[m19
.end():]
535 previous_line_words
= previous_line
.strip().split()
537 if not previous_line
.strip().startswith('G_INLINE_FUNC'):
538 if not previous_line_words
or previous_line_words
[0] != 'static':
540 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*$' %
541 ignore_decorators
, previous_line
)
543 ret_type
= pm
.group(1)
545 ret_type
+= ' ' + pm
.group(2)
546 logging
.info('Function (2): "%s", Returns: "%s"', symbol
, ret_type
)
547 in_declaration
= 'function'
549 logging
.info('skip block after inline function')
550 # now we we need to skip a whole { } block
553 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*$' %
554 ignore_decorators
, previous_line
)
556 ret_type
= pm
.group(1)
558 ret_type
+= ' ' + pm
.group(2)
559 logging
.info('Function (3): "%s", Returns: "%s"', symbol
, ret_type
)
560 in_declaration
= 'function'
562 if not previous_line_words
or previous_line_words
[0] != 'static':
563 logging
.info('skip block after inline function')
564 # now we we need to skip a whole { } block
567 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*$' %
568 ignore_decorators
, previous_line
)
570 ret_type
= pm
.group(1)
572 ret_type
+= ' ' + pm
.group(2)
573 logging
.info('Function (4): "%s", Returns: "%s"', symbol
, ret_type
)
574 in_declaration
= 'function'
576 # Try to catch function declarations with the return type and name
577 # on the previous line(s), and the start of the parameters on this.
579 decl
= line
[m20
.end():]
581 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
)
582 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*$' %
583 ignore_decorators
, pre_previous_line
)
585 ret_type
= pm
.group(1) + ' ' + pm
.group(2)
587 in_declaration
= 'function'
588 logging
.info('Function (5): "%s", Returns: "%s"', symbol
, ret_type
)
590 elif re
.search(r
'^\s*\w+\s*$', previous_line
) and ppm
:
591 ret_type
= ppm
.group(1)
592 ret_type
= re
.sub(r
'\s*\n', '', ret_type
, flags
=re
.MULTILINE
)
593 in_declaration
= 'function'
595 symbol
= previous_line
596 symbol
= re
.sub(r
'^\s+', '', symbol
)
597 symbol
= re
.sub(r
'\s*\n', '', symbol
, flags
=re
.MULTILINE
)
598 logging
.info('Function (6): "%s", Returns: "%s"', symbol
, ret_type
)
600 #} elsif (m/^extern\s+/) {
601 # print "DEBUG: Skipping extern: $_"
604 elif re
.search(r
'^\s*struct\s+_?(\w+)\s*\*', line
):
605 # Skip 'struct _<struct_name> *', since it could be a
606 # return type on its own line.
609 # We assume that 'struct _<struct_name>' is really the
610 # declaration of struct <struct_name>.
611 symbol
= m21
.group(1)
613 # we will find the correct level as below we do $level += tr/{//
615 in_declaration
= 'struct'
616 logging
.info('Struct(_): "%s"', symbol
)
619 elif re
.search(r
'^\s*union\s+_(\w+)\s*\*', line
):
620 # Skip 'union _<union_name> *' (see above)
623 symbol
= m22
.group(1)
626 in_declaration
= 'union'
627 logging
.info('Union(_): "%s"', symbol
)
629 logging
.info('in decl: skip=%s %s', skip_block
, line
.strip())
630 # If we were already in the middle of a declaration, we simply add
631 # the current line onto the end of it.
635 # Remove all nested pairs of curly braces.
636 brace_remover
= r
'{[^{]*}'
637 bm
= re
.search(brace_remover
, line
)
639 line
= re
.sub(brace_remover
, '', line
)
640 bm
= re
.search(brace_remover
, line
)
641 # Then hope at most one remains in the line...
642 bm
= re
.search(r
'(.*?){', line
)
650 # this is a hack to detect the end of declaration
653 logging
.info('2: ---')
659 if in_declaration
== "g-declare":
660 dm
= re
.search(r
'\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\).*$', decl
)
661 # FIXME the original code does s// stuff here and we don't. Is it necessary?
663 ModuleObjName
= dm
.group(1)
664 module_obj_name
= dm
.group(2)
665 if options
.rebuild_types
:
666 get_types
.append(module_obj_name
+ '_get_type')
667 forward_decls
[ModuleObjName
] = '<STRUCT>\n<NAME>%s</NAME>\n%s</STRUCT>\n' % (ModuleObjName
, deprecated
)
668 if symbol
.startswith('G_DECLARE_DERIVABLE'):
669 forward_decls
[ModuleObjName
+ 'Class'] = '<STRUCT>\n<NAME>%sClass</NAME>\n%s</STRUCT>\n' % (
670 ModuleObjName
, deprecated
)
671 if symbol
.startswith('G_DECLARE_INTERFACE'):
672 forward_decls
[ModuleObjName
+ 'Interface'] = '<STRUCT>\n<NAME>%sInterface</NAME>\n%s</STRUCT>\n' % (
673 ModuleObjName
, deprecated
)
676 # Note that sometimes functions end in ') G_GNUC_PRINTF (2, 3);' or
677 # ') __attribute__ (...);'.
678 if in_declaration
== 'function':
679 regex
= r
'\)\s*(G_GNUC_.*|.*DEPRECATED.*%s\s*|__attribute__\s*\(.*\)\s*)*;.*$' % ignore_decorators
680 pm
= re
.search(regex
, decl
, flags
=re
.MULTILINE
)
682 logging
.info('scrubbing:[%s]', decl
)
683 decl
= re
.sub(regex
, '', decl
, flags
=re
.MULTILINE
)
684 logging
.info('scrubbed:[%s]', decl
)
686 decl
= re
.sub(r
'/\*.*?\*/', '', decl
, flags
=re
.MULTILINE
) # remove comments.
687 decl
= re
.sub(r
'\s*\n\s*(?!$)', ' ', decl
, flags
=re
.MULTILINE
)
688 # consolidate whitespace at start/end of lines.
690 ret_type
= re
.sub(r
'/\*.*?\*/', '', ret_type
) # remove comments in ret type.
691 if AddSymbolToList(slist
, symbol
):
692 decl_list
.append('<FUNCTION>\n<NAME>%s</NAME>\n%s<RETURNS>%s</RETURNS>\n%s\n</FUNCTION>\n' %
693 (symbol
, deprecated
, ret_type
, decl
))
694 if options
.rebuild_types
:
695 # check if this looks like a get_type function and if so remember
696 if symbol
.endswith('_get_type') and 'GType' in ret_type
and re
.search(r
'^(void|)$', decl
):
698 "Adding get-type: [%s] [%s] [%s]\tfrom %s", ret_type
, symbol
, decl
, input_file
)
699 get_types
.append(symbol
)
702 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
706 if in_declaration
== 'user_function':
707 if re
.search(r
'\).*$', decl
):
708 decl
= re
.sub(r
'\).*$', '', decl
)
709 if AddSymbolToList(slist
, symbol
):
710 decl_list
.append('<USER_FUNCTION>\n<NAME>%s</NAME>\n%s<RETURNS>%s</RETURNS>\n%s</USER_FUNCTION>\n' %
711 (symbol
, deprecated
, ret_type
, decl
))
712 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
715 if in_declaration
== 'macro':
716 if not re
.search(r
'\\\s*$', decl
):
718 if AddSymbolToList(slist
, symbol
):
719 decl_list
.append('<MACRO>\n<NAME>%s</NAME>\n%s%s</MACRO>\n' % (symbol
, deprecated
, decl
))
722 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
725 if in_declaration
== 'enum':
726 em
= re
.search(r
'\}\s*(\w+)?;\s*$', decl
)
730 if AddSymbolToList(slist
, symbol
):
731 decl_list
.append('<ENUM>\n<NAME>%s</NAME>\n%s%s</ENUM>\n' % (symbol
, deprecated
, decl
))
732 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
735 # We try to handle nested stucts/unions, but unmatched brackets in
736 # comments will cause problems.
737 if in_declaration
== 'struct' or in_declaration
== 'union':
738 sm
= re
.search(r
'\n\}\s*(\w*);\s*$', decl
)
739 if level
<= 1 and sm
:
743 bm
= re
.search(r
'^(\S+)(Class|Iface|Interface)\b', symbol
)
745 objectname
= bm
.group(1)
746 logging
.info('Found object: "%s"', objectname
)
747 title
= '<TITLE>%s</TITLE>' % objectname
749 logging
.info('Store struct: "%s"', symbol
)
750 if AddSymbolToList(slist
, symbol
):
751 structsym
= in_declaration
.upper()
752 decl_list
.append('<%s>\n<NAME>%s</NAME>\n%s%s</%s>\n' %
753 (structsym
, symbol
, deprecated
, decl
, structsym
))
754 if symbol
in forward_decls
:
755 del forward_decls
[symbol
]
756 deprecated_conditional_nest
= int(deprecated_conditional_nest
)
759 # We use tr to count the brackets in the line, and adjust
760 # $level accordingly.
761 level
+= line
.count('{')
762 level
-= line
.count('}')
763 logging
.info('struct/union level : %d', level
)
765 pre_previous_line
= previous_line
768 # print remaining forward declarations
769 for symbol
in sorted(forward_decls
.keys()):
770 if forward_decls
[symbol
]:
771 AddSymbolToList(slist
, symbol
)
772 decl_list
.append(forward_decls
[symbol
])
775 slist
= [title
] + slist
777 logging
.info("Scanning %s done", input_file
)
779 # Try to separate the standard macros and functions, placing them at the
780 # end of the current section, in a subsection named 'Standard'.
781 # do this in a loop to catch object, enums and flags
782 klass
= lclass
= prefix
= lprefix
= None
784 liststr
= '\n'.join(s
for s
in slist
if s
) + '\n'
786 m
= re
.search(r
'^(\S+)_IS_(\S*)_CLASS\n', liststr
, flags
=re
.MULTILINE
)
787 m2
= re
.search(r
'^(\S+)_IS_(\S*)\n', liststr
, flags
=re
.MULTILINE
)
788 m3
= re
.search(r
'^(\S+?)_(\S*)_get_type\n', liststr
, flags
=re
.MULTILINE
)
791 lprefix
= prefix
.lower()
793 lclass
= klass
.lower()
794 logging
.info("Found gobject type '%s_%s' from is_class macro", prefix
, klass
)
797 lprefix
= prefix
.lower()
799 lclass
= klass
.lower()
800 logging
.info("Found gobject type '%s_%s' from is_ macro", prefix
, klass
)
802 lprefix
= m3
.group(1)
803 prefix
= lprefix
.upper()
805 klass
= lclass
.upper()
806 logging
.info("Found gobject type '%s_%s' from get_type function", prefix
, klass
)
811 cclass
= cclass
.replace('_', '')
812 mtype
= lprefix
+ cclass
814 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'^%sPrivate\n' % mtype
)
816 # We only leave XxYy* in the normal section if they have docs
817 if mtype
not in doc_comments
:
818 logging
.info(" Hide instance docs for %s", mtype
)
819 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'^%s\n' % mtype
)
821 if mtype
+ 'class' not in doc_comments
:
822 logging
.info(" Hide class docs for %s", mtype
)
823 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'^%sClass\n' % mtype
)
825 if mtype
+ 'interface' not in doc_comments
:
826 logging
.info(" Hide iface docs for %s", mtype
)
827 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'%sInterface\n' % mtype
)
829 if mtype
+ 'iface' not in doc_comments
:
830 logging
.info(" Hide iface docs for " + mtype
)
831 liststr
, standard_decl
= replace_once(liststr
, standard_decl
, r
'%sIface\n' % mtype
)
833 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_IS_%s\n' % klass
)
834 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_TYPE_%s\n' % klass
)
835 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_get_type\n' % lclass
)
836 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_CLASS\n' % klass
)
837 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_IS_%s_CLASS\n' % klass
)
838 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_GET_CLASS\n' % klass
)
839 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_GET_IFACE\n' % klass
)
840 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s_GET_INTERFACE\n' % klass
)
841 # We do this one last, otherwise it tends to be caught by the IS_$class macro
842 liststr
, standard_decl
= replace_all(liststr
, standard_decl
, r
'^\S+_%s\n' % klass
)
844 logging
.info('Decl:%s---', liststr
)
845 logging
.info('Std :%s---', ''.join(sorted(standard_decl
)))
846 if len(standard_decl
):
848 liststr
+= '<SUBSECTION Standard>\n' + ''.join(sorted(standard_decl
))
851 if file_basename
not in section_list
:
852 section_list
[file_basename
] = ''
853 section_list
[file_basename
] += "<SECTION>\n<FILE>%s</FILE>\n%s</SECTION>\n\n" % (file_basename
, liststr
)
856 def replace_once(liststr
, standard_decl
, regex
):
857 mre
= re
.search(regex
, liststr
, flags
=re
.IGNORECASE | re
.MULTILINE
)
859 standard_decl
.append(mre
.group(0))
860 liststr
= re
.sub(regex
, '', liststr
, flags
=re
.IGNORECASE | re
.MULTILINE
)
861 return liststr
, standard_decl
864 def replace_all(liststr
, standard_decl
, regex
):
865 mre
= re
.search(regex
, liststr
, flags
=re
.MULTILINE
)
867 standard_decl
.append(mre
.group(0))
868 liststr
= re
.sub(regex
, '', liststr
, flags
=re
.MULTILINE
)
869 mre
= re
.search(regex
, liststr
, flags
=re
.MULTILINE
)
870 return liststr
, standard_decl
873 def AddSymbolToList(slist
, symbol
):
874 """ Adds symbol to list of declaration if not already present.
877 slist: The list of symbols.
878 symbol: The symbol to add to the list.
881 # logging.info('Symbol %s already in list. skipping', symbol)
882 # we return False to skip outputting another entry to -decl.txt
883 # this is to avoid redeclarations (e.g. in conditional sections).