Coverage for /builds/ahmed.baizid.0/gtk-doc/gtkdoc/scan.py: 71%
595 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-11-05 19:25 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2024-11-05 19:25 +0000
1# -*- python -*-
2#
3# gtk-doc - GTK DocBook documentation generator.
4# Copyright (C) 1998 Damon Chaplin
5# 2007-2016 Stefan Sauer
6#
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.
11#
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.
16#
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.
20#
22"""
23Extracts declarations of functions, macros, enums, structs and unions from
24header files.
26It is called with a module name, an optional source directory, an optional
27output directory, and the header files to scan.
29It outputs all declarations found to a file named '$MODULE-decl.txt', and the
30list of decarations to another file '$MODULE-decl-list.txt'.
32This second list file is typically copied to '$MODULE-sections.txt' and
33organized into sections ready to output the XML pages.
34"""
36import argparse
37import logging
38import os
39import re
40import shutil
42from . import common, config
44TYPE_MODIFIERS = ['const', 'signed', 'unsigned', 'long', 'short', 'struct', 'union', 'enum']
45VAR_TYPE_MODIFIER = '(?:' + '|'.join([t + '\s+' for t in TYPE_MODIFIERS]) + ')*'
46RET_TYPE_MODIFIER = '(?:' + '|'.join([t + '\s+' for t in TYPE_MODIFIERS + ['G_CONST_RETURN']]) + ')*'
48# Matchers for current line
49CLINE_MATCHER = [
50 # 0: MACROS
51 re.compile(
52 r"""^\s*\#\s*define\s+
53 (\w+) # 1: name
54 """, re.VERBOSE),
55 # 1-4: TYPEDEF'D FUNCTIONS
56 re.compile(
57 r"""^\s*typedef\s+
58 (%s\w+) # 1: return type
59 (\s+const)?\s* # 2: 2nd const
60 (\**)\s* # 3: ptr
61 \(\*\s*
62 (\w+) # 4: name
63 \)\s*\(""" % RET_TYPE_MODIFIER, re.VERBOSE),
64 re.compile(
65 r"""^\s*
66 (%s?\w+) # 1: return type
67 (\s+const)?\s* # 2: 2nd const
68 (\**)\s* # 3: ptr
69 \(\*\s*
70 (\w+) # 4: name
71 \)\s*\(""" % RET_TYPE_MODIFIER, re.VERBOSE),
72 re.compile(
73 r"""^\s*
74 (\**)\s* # 1: ptr
75 \(\*\s*
76 (\w+) # 2: name
77 \)\s*\(""", re.VERBOSE),
78 # 4: FUNCTION POINTER VARIABLES
79 None, # in InitScanner()
80 # 5-7: ENUMS
81 re.compile(
82 r"""^\s*enum\s+
83 _?(\w+) # 1: name
84 \s+\{""", re.VERBOSE),
85 None, # in InitScanner()
86 re.compile(r'^\s*typedef\s+enum'),
87 # 8-11: STRUCTS AND UNIONS
88 None, # in InitScanner()
89 re.compile(r'^\s*(?:struct|union)\s+_(\w+)\s*;'),
90 re.compile(
91 r"""^\s*
92 (struct|union)\s+ # 1: struct/union
93 (\w+) # 2: name
94 \s*;""", re.VERBOSE),
95 re.compile(
96 r"""^\s*typedef\s+
97 (struct|union)\s*
98 \w*\s*{""", re.VERBOSE),
99 # 12-14: OTHER TYPEDEFS
100 None, # in InitScanner()
101 None, # in InitScanner()
102 re.compile(r'^\s*typedef\s+'),
103 # 15: VARIABLES (extern'ed variables)
104 None, # in InitScanner()
105 # 16: VARIABLES
106 re.compile(
107 r"""^\s*
108 (?:%s\w+)
109 (?:\s+\*+|\*+|\s)\s*
110 (?:const\s+)*
111 ([A-Za-z]\w*) # 1: name
112 \s*\=""" % VAR_TYPE_MODIFIER, re.VERBOSE),
113 # 17: G_DECLARE_*
114 re.compile(
115 r""".*(G_DECLARE_|GDK_DECLARE_)
116 (FINAL_TYPE|DERIVABLE_TYPE|INTERNAL_TYPE|INTERFACE) # 1: variant
117 \s*\(""", re.VERBOSE),
118 # 18-21: FUNCTIONS
119 None, # in InitScanner()
120 None, # in InitScanner()
121 re.compile(r'^\s*\(?([A-Za-z]\w*)\)?\s*\('),
122 re.compile(r'^\s*\('),
123 # 22-23: STRUCTS
124 re.compile(r'^\s*struct\s+_?(\w+)\s*\*'),
125 re.compile(r'^\s*struct\s+_?(\w+)'),
126 # 24-25: UNIONS
127 re.compile(r'^\s*union\s+_(\w+)\s*\*'),
128 re.compile(r'^\s*union\s+_?(\w+)'),
129]
131# Matchers for previous line
132PLINE_MATCHER = [
133 # 0-1: TYPEDEF'D FUNCTIONS
134 re.compile(
135 r"""^\s*typedef\s*
136 (%s\w+) # 1: return type
137 (\s+const)?\s* # 2: 2nd const
138 (\**)\s* # 3: ptr
139 """ % RET_TYPE_MODIFIER, re.VERBOSE),
140 re.compile(r'^\s*typedef\s*'),
141 # 2-4 :FUNCTIONS
142 None, # in InitScanner()
143 None, # in InitScanner()
144 None, # in InitScanner()
145]
147# Matchers for 2nd previous line
148PPLINE_MATCHER = None
150# Matchers for sub expressions
151SUB_MATCHER = [
152 # 0: STRUCTS AND UNIONS
153 re.compile(r'^(\S+)(Class|Iface|Interface)\b'),
154]
157def Run(options):
158 logging.info('options: %s', str(options.__dict__))
160 InitScanner(options)
162 if not os.path.isdir(options.output_dir):
163 os.mkdir(options.output_dir)
165 base_filename = os.path.join(options.output_dir, options.module)
166 old_decl_list = base_filename + '-decl-list.txt'
167 new_decl_list = base_filename + '-decl-list.new'
168 old_decl = base_filename + '-decl.txt'
169 new_decl = base_filename + '-decl.new'
170 old_types = base_filename + '.types'
171 new_types = base_filename + '.types.new'
172 sections_file = base_filename + '-sections.txt'
174 # If this is the very first run then we create the .types file automatically.
175 if not os.path.exists(sections_file) and not os.path.exists(old_types):
176 options.rebuild_types = True
178 section_list = {}
179 decl_list = []
180 get_types = []
182 # do not read files twice; checking it here permits to give both srcdir and
183 # builddir as --source-dir without fear of duplicities
184 seen_headers = {}
186 for file in options.headers:
187 ScanHeader(file, section_list, decl_list, get_types, seen_headers, options)
189 for dir in options.source_dir:
190 ScanHeaders(dir, section_list, decl_list, get_types, seen_headers, options)
192 with open(new_decl_list, 'w', encoding='utf-8') as f:
193 for section in sorted(section_list.keys()):
194 f.write(section_list[section])
195 common.UpdateFileIfChanged(old_decl_list, new_decl_list, True)
197 with open(new_decl, 'w', encoding='utf-8') as f:
198 for decl in decl_list:
199 f.write(decl)
200 common.UpdateFileIfChanged(old_decl, new_decl, True)
202 if options.rebuild_types:
203 with open(new_types, 'w', encoding='utf-8') as f:
204 for func in sorted(get_types):
205 f.write(func + '\n')
207 # remove the file if empty
208 if len(get_types) == 0:
209 os.unlink(new_types)
210 if os.path.exists(old_types):
211 os.rename(old_types, old_types + '.bak')
212 else:
213 common.UpdateFileIfChanged(old_types, new_types, True)
215 # If there is no MODULE-sections.txt file yet or we are asked to rebuild it,
216 # we copy the MODULE-decl-list.txt file into its place. The user can tweak it
217 # later if they want.
218 if options.rebuild_sections or not os.path.exists(sections_file):
219 new_sections_file = base_filename + '-sections.new'
220 shutil.copyfile(old_decl_list, new_sections_file)
221 common.UpdateFileIfChanged(sections_file, new_sections_file, False)
223 # If there is no MODULE-overrides.txt file we create an empty one
224 # because EXTRA_DIST in gtk-doc.make requires it.
225 overrides_file = base_filename + '-overrides.txt'
226 if not os.path.exists(overrides_file):
227 open(overrides_file, 'w', encoding='utf-8').close()
230def InitScanner(options):
231 """Apply options to regexps.
232 """
234 # avoid generating regex with |'' (matching no string)
235 # TODO(ensonic): keep in sync with ScanHeaderContent()
236 ignore_decorators = ''
237 optional_decorators_regex = ''
238 if options.ignore_decorators:
239 ignore_decorators = '|' + options.ignore_decorators.replace('()', '\(\w*\)')
240 optional_decorators_regex = '(?:\s+(?:%s))?' % ignore_decorators[1:]
242 # FUNCTION POINTER VARIABLES
243 CLINE_MATCHER[4] = re.compile(
244 r"""^\s*(?:\b(?:extern|static|inline|G_INLINE_FUNC%s)\s*)*
245 ((?:const\s+|G_CONST_RETURN\s+)?\w+) # 1: 1st const
246 (\s+const)?\s* # 2: 2nd const
247 (\**)\s* # 3: ptr
248 \(\*\s*
249 (\w+) # 4: name
250 \)\s*\(""" % ignore_decorators, re.VERBOSE)
252 CLINE_MATCHER[6] = re.compile(r'^\s*typedef\s+enum\s+_?(\w+)\s+\1%s\s*;' % optional_decorators_regex)
253 CLINE_MATCHER[8] = re.compile(
254 r"""^\s*typedef\s+
255 (struct|union)\s+ # 1: struct/union
256 _(\w+)\s+\2 # 2: name
257 %s # 3: optional decorator
258 \s*;""" % optional_decorators_regex, re.VERBOSE)
259 # OTHER TYPEDEFS
260 CLINE_MATCHER[12] = re.compile(
261 r"""^\s*typedef\s+
262 (?:struct|union)\s+\w+[\s\*]+
263 (\w+) # 1: name
264 %s # 2: optional decorator
265 \s*;""" % optional_decorators_regex, re.VERBOSE)
266 CLINE_MATCHER[13] = re.compile(
267 r"""^\s*
268 (?:G_GNUC_EXTENSION\s+)?
269 typedef\s+
270 (.+?[\s\*]) # 1: e.g. 'unsigned int'
271 (\w+) # 2: name
272 (?:\s*\[[^\]]+\])*
273 %s # 3: optional decorator
274 \s*;""" % optional_decorators_regex, re.VERBOSE)
275 CLINE_MATCHER[15] = re.compile(
276 r"""^\s*
277 (?:extern|[A-Za-z_]+VAR%s)\s+
278 (?:%s\w+)
279 (?:\s+\*+|\*+|\s)\s*
280 (?:const\s+)*
281 ([A-Za-z]\w*) # 1: name
282 \s*;""" % (ignore_decorators, RET_TYPE_MODIFIER), re.VERBOSE)
283 # FUNCTIONS
284 CLINE_MATCHER[18] = re.compile(
285 r"""^\s*
286 (?:\b(?:extern|static|inline|G_INLINE_FUNC%s)\s*)*
287 (%s\w+) # 1: return type
288 ([\s*]+(?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*)\s* # 2: .. cont'
289 (_[A-Za-z]\w*) # 3: name
290 \s*\(""" % (ignore_decorators, RET_TYPE_MODIFIER), re.VERBOSE)
291 CLINE_MATCHER[19] = re.compile(
292 r"""^\s*
293 (?:\b(?:extern|static|inline|G_INLINE_FUNC%s)\s*)*
294 (%s\w+) # 1: return type
295 ([\s*]+(?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*)\s* # 2: .. cont'
296 \(?([A-Za-z]\w*)\)? # 3: name
297 \s*\(""" % (ignore_decorators, RET_TYPE_MODIFIER), re.VERBOSE)
299 PLINE_MATCHER[2] = re.compile(
300 r"""^\s*
301 (?:\b(?:extern%s)\s*)*
302 (%s\w+) # 1: retun type
303 ((?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*) # 2: .. cont'
304 \s*$""" % (ignore_decorators, RET_TYPE_MODIFIER), re.VERBOSE)
306 PLINE_MATCHER[3] = re.compile(
307 r"""^\s*(?:\b(?:extern|static|inline|G_INLINE_FUNC%s)\s*)*
308 (%s\w+) # 1: return type
309 ((?:\s*(?:\*+|\bconst\b|\bG_CONST_RETURN\b))*) # 2: .. cont'
310 \s*$""" % (ignore_decorators, RET_TYPE_MODIFIER), re.VERBOSE)
312 PLINE_MATCHER[4] = re.compile(
313 r"""^\s*(?:\b(?:extern|static|inline|G_INLINE_FUNC%s)\s*)*
314 (%s\w+) # 1: return type
315 (\s+\*+|\*+|\s)\s* # 2: ptr?
316 ([A-Za-z]\w*) # 3: symbols
317 \s*$""" % (ignore_decorators, RET_TYPE_MODIFIER), re.VERBOSE)
319 # Matchers for 2nd previous line
320 global PPLINE_MATCHER
321 PPLINE_MATCHER = [
322 # 0: FUNCTIONS
323 re.compile(
324 r"""^\s*(?:\b(?:extern|static|inline|G_INLINE_FUNC%s)\s*)*
325 (
326 (?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|struct\s+|union\s+|enum\s+)*
327 \w+
328 (?:\**\s+\**(?:const|G_CONST_RETURN))?
329 (?:\s+|\s*\*+)
330 )\s*$""" % ignore_decorators, re.VERBOSE)
331 ]
334def ScanHeaders(source_dir, section_list, decl_list, get_types, seen_headers, options):
335 """Scans a directory tree looking for header files.
337 Args:
338 source_dir (str): the directory to scan.
339 section_list (dict): map of section to filenames.
340 seen_headers (set): set to avoid scanning headers twice
341 """
343 logging.info('Scanning source directory: %s', source_dir)
345 # This array holds any subdirectories found.
346 subdirs = []
348 for file in sorted(os.listdir(source_dir)):
349 if file.startswith('.'):
350 continue
351 fullname = os.path.join(source_dir, file)
352 if os.path.isdir(fullname):
353 subdirs.append(file)
354 elif file.endswith('.h'):
355 ScanHeader(fullname, section_list, decl_list, get_types,
356 seen_headers, options)
358 # Now recursively scan the subdirectories.
359 for dir in subdirs:
360 matchstr = r'(\s|^)' + re.escape(dir) + r'(\s|$)'
361 if re.search(matchstr, options.ignore_headers):
362 continue
363 ScanHeaders(os.path.join(source_dir, dir), section_list, decl_list,
364 get_types, seen_headers, options)
367def ScanHeader(input_file, section_list, decl_list, get_types, seen_headers, options):
368 """Scan a header file for doc commants.
370 Look for doc comments and extract them. Parse each doc comments and the
371 symbol declaration.
373 Args:
374 input_file (str): the header file to scan.
375 section_list (dict): a map of section per filename
376 decl_list (list): a list of declarations
377 seen_headers (set): set to avoid scanning headers twice
378 """
380 # Don't scan headers twice
381 canonical_input_file = os.path.realpath(input_file)
382 if canonical_input_file in seen_headers:
383 logging.info('File already scanned: %s', input_file)
384 return
386 seen_headers[canonical_input_file] = 1
388 file_basename = os.path.split(input_file)[1][:-2] # filename ends in .h
390 # Check if the basename is in the list of headers to ignore.
391 matchstr = r'(\s|^)' + re.escape(file_basename) + r'\.h(\s|$)'
392 if re.search(matchstr, options.ignore_headers):
393 logging.info('File ignored: %s', input_file)
394 return
396 # Check if the full name is in the list of headers to ignore.
397 matchstr = r'(\s|^)' + re.escape(input_file) + r'(\s|$)'
398 if re.search(matchstr, options.ignore_headers):
399 logging.info('File ignored: %s', input_file)
400 return
402 if not os.path.exists(input_file):
403 logging.warning('File does not exist: %s', input_file)
404 return
406 logging.info('Scanning %s', input_file)
408 with open(input_file, 'r', encoding='utf-8') as hdr:
409 input_lines = hdr.readlines()
411 try:
412 slist, doc_comments = ScanHeaderContent(input_lines, decl_list, get_types, options)
413 logging.info("Scanning %s done", input_file)
415 liststr = SeparateSubSections(slist, doc_comments)
416 if liststr != '':
417 if file_basename not in section_list:
418 section_list[file_basename] = ''
419 section_list[file_basename] += "<SECTION>\n<FILE>%s</FILE>\n%s</SECTION>\n\n" % (file_basename, liststr)
421 except RuntimeError as e:
422 common.LogWarning(input_file, 0, str(e))
425def ScanHeaderContent(input_lines, decl_list, get_types, options):
426 """Scan the the given content lines.
428 Args:
429 input_lines (list):
430 decl_list (list): symbols declarations
431 get_types (list): lst of symbols that have a get_type function
432 options: commandline options
434 Returns:
435 list: a list of symbols found and a set of symbols for which we have a
436 doc-comment
437 """
439 # Holds the resulting list of declarations.
440 slist = []
441 # Holds the title of the section
442 title = None
443 # True if we are in a comment.
444 in_comment = 0
445 # The type of declaration we are in, e.g. 'function' or 'macro'.
446 in_declaration = ''
447 # True if we should skip a block.
448 skip_block = False
449 # The current symbol being declared.
450 symbol = None
451 # Holds the declaration of the current symbol.
452 decl = ''
453 # For functions and function typedefs this holds the function's return type.
454 ret_type = None
455 # The pre-previous line read in - some Gnome functions have the return type
456 # on one line, the function name on the next, and the rest of the
457 # declaration after.
458 pre_previous_line = ''
459 # The previous line read in - some Gnome functions have the return type on
460 # one line and the rest of the declaration after.
461 previous_line = ''
462 # Used to try to skip the standard #ifdef XXX #define XXX at the start of
463 # headers.
464 first_macro = 1
465 # Used to handle structs/unions which contain nested structs or unions.
466 level = None
467 # Set to 1 for internal symbols, we need to fully parse, but don't add them
468 # to docs
469 internal = 0
470 # Dict of forward declarations, we skip them if we find the real declaration
471 # later.
472 forward_decls = {}
473 # Dict of doc-comments we found. The key is lowercase symbol name, val=1.
474 doc_comments = {}
476 deprecated_conditional_nest = 0
477 ignore_conditional_nest = 0
479 deprecated = ''
480 doc_comment = ''
482 # avoid generating regex with |'' (matching no string)
483 # TODO(ensonic): keep in sync with InitScanner()
484 # TODO(ensonic): extract the remaining regexps
485 ignore_decorators = '' # 1 uses
486 optional_decorators_regex = '' # 4 uses
487 if options.ignore_decorators:
488 ignore_decorators = '|' + options.ignore_decorators.replace('()', '\(\w*\)')
489 optional_decorators_regex = '(?:\s+(?:%s))?' % ignore_decorators[1:]
491 for line in input_lines:
492 # If this is a private header, skip it.
493 # TODO: consider scanning this first, so that we don't modify: decl_list
494 # and get_types
495 if re.search(r'^\s*/\*\s*<\s*private_header\s*>\s*\*/', line):
496 return [], {}
498 # Skip to the end of the current comment.
499 if in_comment:
500 logging.info('Comment: %s', line.strip())
501 doc_comment += line
502 if re.search(r'\*/', line):
503 m = re.search(r'\* ([a-zA-Z][a-zA-Z0-9_]+):', doc_comment)
504 if m:
505 doc_comments[m.group(1).lower()] = 1
506 in_comment = 0
507 doc_comment = ''
508 continue
510 # Keep a count of #if, #ifdef, #ifndef nesting,
511 # and if we enter a deprecation-symbol-bracketed
512 # zone, take note.
513 m = re.search(r'^\s*#\s*if(?:n?def\b|\s+!?\s*defined\s*\()\s*(\w+)', line)
514 if m:
515 define_name = m.group(1)
516 if deprecated_conditional_nest < 1 and re.search(options.deprecated_guards, define_name):
517 deprecated_conditional_nest = 1
518 elif deprecated_conditional_nest >= 1:
519 deprecated_conditional_nest += 1
520 if ignore_conditional_nest == 0 and '__GTK_DOC_IGNORE__' in define_name:
521 ignore_conditional_nest = 1
522 elif ignore_conditional_nest > 0:
523 ignore_conditional_nest = 1
525 elif re.search(r'^\s*#\sif', line):
526 if deprecated_conditional_nest >= 1:
527 deprecated_conditional_nest += 1
528 if ignore_conditional_nest > 0:
529 ignore_conditional_nest += 1
530 elif re.search(r'^\s*#endif', line):
531 if deprecated_conditional_nest >= 1:
532 deprecated_conditional_nest -= 1
533 if ignore_conditional_nest > 0:
534 ignore_conditional_nest -= 1
536 # If we find a line containing _DEPRECATED, we hope that this is
537 # attribute based deprecation and also treat this as a deprecation
538 # guard, unless it's a macro definition or the end of a deprecation
539 # section (#endif /* XXX_DEPRECATED */
540 if deprecated_conditional_nest == 0 and '_DEPRECATED' in line:
541 m = re.search(r'^\s*#\s*(if*|define|endif)', line)
542 if not (m or in_declaration == 'enum' or in_declaration == 'struct'):
543 logging.info('Found deprecation annotation (decl: "%s"): "%s"',
544 in_declaration, line.strip())
545 deprecated_conditional_nest += 0.1
547 # set flag that is used later when we do AddSymbolToList
548 if deprecated_conditional_nest > 0:
549 deprecated = '<DEPRECATED/>\n'
550 else:
551 deprecated = ''
553 if ignore_conditional_nest:
554 continue
556 if not in_declaration:
557 # Skip top-level comments.
558 m = re.search(r'^\s*/\*', line)
559 if m:
560 re.sub(r'^\s*/\*', '', line)
561 if re.search(r'\*/', line):
562 logging.info('Found one-line comment: %s', line.strip())
563 else:
564 in_comment = 1
565 doc_comment = line
566 logging.info('Found start of comment: %s', line.strip())
567 continue
569 # Skip begin/end deprecation macros.
570 m = re.search(r'^\s*G_GNUC_(BEGIN|END)_IGNORE_DEPRECATIONS', line)
571 if m:
572 continue
574 logging.info('no decl: %s', line.strip())
576 cm = [m.match(line) for m in CLINE_MATCHER]
577 pm = [m.match(previous_line) for m in PLINE_MATCHER]
578 ppm = [m.match(pre_previous_line) for m in PPLINE_MATCHER]
580 # MACROS
582 if cm[0]:
583 symbol = cm[0].group(1)
584 decl = line
585 # We assume all macros which start with '_' are private, but
586 # we accept '_' itself which is the standard gettext macro.
587 # We also try to skip the first macro if it looks like the
588 # standard #ifndef HEADER_FILE #define HEADER_FILE etc.
589 # And we only want TRUE & FALSE defined in GLib.
590 if not symbol.startswith('_') \
591 and (not re.search(r'#ifndef\s+' + symbol, previous_line)
592 or first_macro == 0) \
593 and ((symbol != 'TRUE' and symbol != 'FALSE')
594 or options.module == 'glib') \
595 or symbol == '_':
596 in_declaration = 'macro'
597 logging.info('Macro: "%s"', symbol)
598 else:
599 logging.info('skipping Macro: "%s"', symbol)
600 in_declaration = 'macro'
601 internal = 1
602 first_macro = 0
604 # TYPEDEF'D FUNCTIONS (i.e. user functions)
605 elif cm[1]:
606 ret_type = format_ret_type(cm[1].group(1), cm[1].group(2), cm[1].group(3))
607 symbol = cm[1].group(4)
608 decl = line[cm[1].end():]
609 in_declaration = 'user_function'
610 logging.info('user function (1): "%s", Returns: "%s"', symbol, ret_type)
612 elif pm[1] and cm[2]:
613 ret_type = format_ret_type(cm[2].group(1), cm[2].group(2), cm[2].group(3))
614 symbol = cm[2].group(4)
615 decl = line[cm[2].end():]
616 in_declaration = 'user_function'
617 logging.info('user function (2): "%s", Returns: "%s"', symbol, ret_type)
619 elif pm[1] and cm[3]:
620 ret_type = cm[3].group(1)
621 symbol = cm[3].group(2)
622 decl = line[cm[3].end():]
623 if pm[0]:
624 ret_type = format_ret_type(pm[0].group(1), pm[0].group(2), pm[0].group(3)) + ret_type
625 in_declaration = 'user_function'
626 logging.info('user function (3): "%s", Returns: "%s"', symbol, ret_type)
628 # FUNCTION POINTER VARIABLES
629 elif cm[4]:
630 ret_type = format_ret_type(cm[4].group(1), cm[4].group(2), cm[4].group(3))
631 symbol = cm[4].group(4)
632 decl = line[cm[4].end():]
633 in_declaration = 'user_function'
634 logging.info('function pointer variable: "%s", Returns: "%s"', symbol, ret_type)
636 # ENUMS
638 elif cm[5]:
639 re.sub(r'^\s*enum\s+_?(\w+)\s+\{', r'enum \1 {', line)
640 # We assume that 'enum _<enum_name> {' is really the
641 # declaration of enum <enum_name>.
642 symbol = cm[5].group(1)
643 decl = line
644 in_declaration = 'enum'
645 logging.info('plain enum: "%s"', symbol)
647 elif cm[6]:
648 # We skip 'typedef enum <enum_name> _<enum_name>;' as the enum will
649 # be declared elsewhere.
650 logging.info('skipping enum typedef: "%s"', line)
652 elif cm[7]:
653 symbol = ''
654 decl = line
655 in_declaration = 'enum'
656 logging.info('typedef enum: -')
658 # STRUCTS AND UNIONS
660 elif cm[8]:
661 # We've found a 'typedef struct _<name> <name>;'
662 # This could be an opaque data structure, so we output an
663 # empty declaration. If the structure is actually found that
664 # will override this (technically if will just be another entry
665 # in the output file and will be joined when reading the file).
666 structsym = cm[8].group(1).upper()
667 logging.info('%s typedef: "%s"', structsym, cm[8].group(2))
668 forward_decls[cm[8].group(2)] = '<%s>\n<NAME>%s</NAME>\n%s</%s>\n' % (
669 structsym, cm[8].group(2), deprecated, structsym)
671 m = SUB_MATCHER[0].match(cm[8].group(2))
672 if m:
673 objectname = m.group(1)
674 logging.info('Found object: "%s"', objectname)
675 title = '<TITLE>%s</TITLE>' % objectname
677 elif cm[9]:
678 # Skip private structs/unions.
679 logging.info('private struct/union')
681 elif cm[10]:
682 # Do a similar thing for normal structs as for typedefs above.
683 # But we output the declaration as well in this case, so we
684 # can differentiate it from a typedef.
685 structsym = cm[10].group(1).upper()
686 logging.info('%s:%s', structsym, cm[10].group(2))
687 forward_decls[cm[10].group(2)] = '<%s>\n<NAME>%s</NAME>\n%s%s</%s>\n' % (
688 structsym, cm[10].group(2), line, deprecated, structsym)
690 elif cm[11]:
691 symbol = ''
692 decl = line
693 level = 0
694 in_declaration = cm[11].group(1)
695 logging.info('typedef struct/union "%s"', in_declaration)
697 # OTHER TYPEDEFS
699 elif cm[12]:
700 logging.info('Found struct/union(*) typedef "%s": "%s"', cm[12].group(1), line)
701 if AddSymbolToList(slist, cm[12].group(1)):
702 decl_list.append('<TYPEDEF>\n<NAME>%s</NAME>\n%s%s</TYPEDEF>\n' %
703 (cm[12].group(1), deprecated, line))
705 elif cm[13]:
706 if cm[13].group(1).split()[0] not in ('struct', 'union'):
707 logging.info('Found typedef: "%s"', line)
708 if AddSymbolToList(slist, cm[13].group(2)):
709 decl_list.append(
710 '<TYPEDEF>\n<NAME>%s</NAME>\n%s%s</TYPEDEF>\n' % (cm[13].group(2), deprecated, line))
711 elif cm[14]:
712 logging.info('Skipping typedef: "%s"', line)
714 # VARIABLES (extern'ed variables)
716 elif cm[15]:
717 symbol = cm[15].group(1)
718 line = re.sub(r'^\s*([A-Za-z_]+VAR)\b', r'extern', line)
719 decl = line
720 logging.info('Possible extern var "%s": "%s"', symbol, decl)
721 if AddSymbolToList(slist, symbol):
722 decl_list.append('<VARIABLE>\n<NAME>%s</NAME>\n%s%s</VARIABLE>\n' % (symbol, deprecated, decl))
724 # VARIABLES
726 elif cm[16]:
727 symbol = cm[16].group(1)
728 decl = line
729 logging.info('Possible global var" %s": "%s"', symbol, decl)
730 if AddSymbolToList(slist, symbol):
731 decl_list.append('<VARIABLE>\n<NAME>%s</NAME>\n%s%s</VARIABLE>\n' % (symbol, deprecated, decl))
733 # G_DECLARE_*
735 elif cm[17]:
736 in_declaration = 'g-declare'
737 symbol = cm[17].group(1) + cm[17].group(2)
738 decl = line[cm[17].end():]
740 # FUNCTIONS
742 elif cm[18]:
743 # We assume that functions starting with '_' are private and skip them.
744 ret_type = format_ret_type(cm[18].group(1), None, cm[18].group(2))
745 symbol = cm[18].group(3)
746 decl = line[cm[18].end():]
747 logging.info('internal Function: "%s", Returns: "%s""%s"', symbol, cm[18].group(1), cm[18].group(2))
748 in_declaration = 'function'
749 internal = 1
750 skip_block |= is_inline_func(line)
752 elif cm[19]:
753 ret_type = format_ret_type(cm[19].group(1), None, cm[19].group(2))
754 symbol = cm[19].group(3)
755 decl = line[cm[19].end():]
756 logging.info('Function (1): "%s", Returns: "%s""%s"', symbol, cm[19].group(1), cm[19].group(2))
757 in_declaration = 'function'
758 skip_block |= is_inline_func(line)
760 # Try to catch function declarations which have the return type on
761 # the previous line. But we don't want to catch complete functions
762 # which have been declared G_INLINE_FUNC, e.g. g_bit_nth_lsf in
763 # glib, or 'static inline' functions.
764 elif cm[20]:
765 symbol = cm[20].group(1)
766 decl = line[cm[20].end():]
768 if is_inline_func(previous_line):
769 skip_block = True
770 if pm[3]:
771 ret_type = format_ret_type(pm[3].group(1), None, pm[3].group(2))
772 logging.info('Function (3): "%s", Returns: "%s"', symbol, ret_type)
773 in_declaration = 'function'
774 else:
775 if pm[2]:
776 ret_type = format_ret_type(pm[2].group(1), None, pm[2].group(2))
777 logging.info('Function (2): "%s", Returns: "%s"', symbol, ret_type)
778 in_declaration = 'function'
780 # Try to catch function declarations with the return type and name
781 # on the previous line(s), and the start of the parameters on this.
782 elif cm[21]:
783 decl = line[cm[21].end():]
784 if pm[4]:
785 ret_type = pm[4].group(1) + ' ' + pm[4].group(2).strip()
786 symbol = pm[4].group(3)
787 in_declaration = 'function'
788 logging.info('Function (5): "%s", Returns: "%s"', symbol, ret_type)
790 elif re.search(r'^\s*\w+\s*$', previous_line) and ppm[0]:
791 ret_type = ppm[0].group(1)
792 ret_type = re.sub(r'\s*\n', '', ret_type, flags=re.MULTILINE)
793 in_declaration = 'function'
795 symbol = previous_line
796 symbol = re.sub(r'^\s+', '', symbol)
797 symbol = re.sub(r'\s*\n', '', symbol, flags=re.MULTILINE)
798 logging.info('Function (6): "%s", Returns: "%s"', symbol, ret_type)
800 # } elsif (m/^extern\s+/) {
801 # print "DEBUG: Skipping extern: $_"
803 # STRUCTS
804 elif cm[22]:
805 # Skip 'struct _<struct_name> *', since it could be a
806 # return type on its own line.
807 pass
808 elif cm[23]:
809 # We assume that 'struct _<struct_name>' is really the
810 # declaration of struct <struct_name>.
811 symbol = cm[23].group(1)
812 decl = line
813 # we will find the correct level as below we do $level += tr/{//
814 level = 0
815 in_declaration = 'struct'
816 logging.info('Struct(_): "%s"', symbol)
818 # UNIONS
819 elif cm[24]:
820 # Skip 'union _<union_name> *' (see above)
821 pass
822 elif cm[25]:
823 symbol = cm[25].group(1)
824 decl = line
825 level = 0
826 in_declaration = 'union'
827 logging.info('Union(_): "%s"', symbol)
828 else:
829 logging.info('in decl %s: skip=%s %s', in_declaration, skip_block, line.strip())
830 decl += line
832 if skip_block and '{' in decl:
833 (skip_block, decl) = remove_braced_content(decl)
834 logging.info('in decl: skip=%s decl=[%s]', skip_block, decl)
836 pre_previous_line = previous_line
837 previous_line = line
839 if skip_block:
840 logging.info('skipping, in decl %s, decl=[%s]', in_declaration, decl)
841 continue
843 if in_declaration == "g-declare":
844 dm = re.search(r'\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\).*$', decl)
845 # FIXME the original code does s// stuff here and we don't. Is it necessary?
846 if dm:
847 ModuleObjName = dm.group(1)
848 module_obj_name = dm.group(2)
849 if options.rebuild_types:
850 get_types.append(module_obj_name + '_get_type')
851 forward_decls[ModuleObjName] = '<STRUCT>\n<NAME>%s</NAME>\n%s</STRUCT>\n' % (ModuleObjName, deprecated)
852 if symbol.startswith('G_DECLARE_DERIVABLE'):
853 forward_decls[ModuleObjName + 'Class'] = '<STRUCT>\n<NAME>%sClass</NAME>\n%s</STRUCT>\n' % (
854 ModuleObjName, deprecated)
855 if symbol.startswith('G_DECLARE_INTERFACE'):
856 forward_decls[ModuleObjName + 'Interface'] = '<STRUCT>\n<NAME>%sInterface</NAME>\n%s</STRUCT>\n' % (
857 ModuleObjName, deprecated)
858 in_declaration = ''
860 if in_declaration == 'function':
861 # Note that sometimes functions end in ') G_GNUC_PRINTF (2, 3);' or
862 # ') __attribute__ (...);'.
863 regex = r'\)\s*(G_GNUC_.*|.*DEPRECATED.*%s\s*|__attribute__\s*\(.*\)\s*)*;.*$' % ignore_decorators
864 pm = re.search(regex, decl, flags=re.MULTILINE)
865 if pm:
866 logging.info('scrubbing:[%s]', decl.strip())
867 decl = re.sub(regex, '', decl, flags=re.MULTILINE)
868 logging.info('scrubbed:[%s]', decl.strip())
869 if internal == 0:
870 decl = re.sub(r'/\*.*?\*/', '', decl, flags=re.MULTILINE) # remove comments.
871 decl = re.sub(r'\s*\n\s*(?!$)', ' ', decl, flags=re.MULTILINE) # remove newlines
872 # consolidate whitespace at start/end of lines.
873 decl = decl.strip()
874 ret_type = re.sub(r'/\*.*?\*/', '', ret_type).strip() # remove comments in ret type.
875 if AddSymbolToList(slist, symbol):
876 decl_list.append('<FUNCTION>\n<NAME>%s</NAME>\n%s<RETURNS>%s</RETURNS>\n%s\n</FUNCTION>\n' %
877 (symbol, deprecated, ret_type, decl))
878 if options.rebuild_types:
879 # check if this looks like a get_type function and if so remember
880 if symbol.endswith('_get_type') and 'GType' in ret_type and re.search(r'^(void|)$', decl):
881 logging.info(
882 "Adding get-type: [%s] [%s] [%s]", ret_type, symbol, decl)
883 get_types.append(symbol)
884 else:
885 internal = 0
886 deprecated_conditional_nest = int(deprecated_conditional_nest)
887 in_declaration = ''
888 skip_block = False
890 if in_declaration == 'user_function':
891 if re.search(r'\).*$', decl):
892 decl = re.sub(r'\).*$', '', decl)
893 # TODO: same as above
894 decl = re.sub(r'/\*.*?\*/', '', decl, flags=re.MULTILINE) # remove comments.
895 decl = re.sub(r'\s*\n\s*(?!$)', ' ', decl, flags=re.MULTILINE) # remove newlines
896 # TODO: don't stip here (it works above, but fails some test
897 # consolidate whitespace at start/end of lines.
898 # decl = decl.strip()
899 if AddSymbolToList(slist, symbol):
900 decl_list.append('<USER_FUNCTION>\n<NAME>%s</NAME>\n%s<RETURNS>%s</RETURNS>\n%s</USER_FUNCTION>\n' %
901 (symbol, deprecated, ret_type, decl))
902 deprecated_conditional_nest = int(deprecated_conditional_nest)
903 in_declaration = ''
905 if in_declaration == 'macro':
906 if not re.search(r'\\\s*$', decl):
907 if internal == 0:
908 if AddSymbolToList(slist, symbol):
909 decl_list.append('<MACRO>\n<NAME>%s</NAME>\n%s%s</MACRO>\n' % (symbol, deprecated, decl))
910 else:
911 logging.info('skip internal macro: [%s]', symbol)
912 internal = 0
913 deprecated_conditional_nest = int(deprecated_conditional_nest)
914 in_declaration = ''
915 else:
916 logging.info('skip empty macro: [%s]', symbol)
918 if in_declaration == 'enum':
919 # Examples:
920 # "};"
921 # "} MyEnum;"
922 # "} MyEnum DEPRECATED_FOR(NewEnum);"
923 # "} DEPRECATED_FOR(NewEnum);"
924 em = re.search(r'\n\s*\}\s*(?:(\w+)?%s)?;\s*$' % optional_decorators_regex, decl)
925 if em:
926 if symbol == '':
927 symbol = em.group(1)
928 # Enums could contain deprecated values and that doesn't mean
929 # the whole enum is deprecated, so they are ignored when setting
930 # deprecated_conditional_nest above. Here we can check if the
931 # _DEPRECATED is between '}' and ';' which would mean the enum
932 # as a whole is deprecated.
933 if re.search(r'\n\s*\}.*_DEPRECATED.*;\s*$', decl):
934 deprecated = '<DEPRECATED/>\n'
935 if AddSymbolToList(slist, symbol):
936 stripped_decl = re.sub(optional_decorators_regex, '', decl)
937 decl_list.append('<ENUM>\n<NAME>%s</NAME>\n%s%s</ENUM>\n' % (symbol, deprecated, stripped_decl))
938 deprecated_conditional_nest = int(deprecated_conditional_nest)
939 in_declaration = ''
941 # We try to handle nested structs/unions, but unmatched brackets in
942 # comments will cause problems.
943 if in_declaration == 'struct' or in_declaration == 'union':
944 # Same regex as for enum
945 sm = re.search(r'\n\}\s*(?:(\w+)?%s)?;\s*$' % optional_decorators_regex, decl)
946 if level <= 1 and sm:
947 if symbol == '':
948 symbol = sm.group(1)
950 bm = re.search(r'^(\S+)(Class|Iface|Interface)\b', symbol)
951 if bm:
952 objectname = bm.group(1)
953 logging.info('Found object: "%s"', objectname)
954 title = '<TITLE>%s</TITLE>' % objectname
956 logging.info('Store struct: "%s"', symbol)
957 # Structs could contain deprecated members and that doesn't
958 # mean the whole struct is deprecated, so they are ignored when
959 # setting deprecated_conditional_nest above. Here we can check
960 # if the _DEPRECATED is between '}' and ';' which would mean
961 # the struct as a whole is deprecated.
962 if re.search(r'\n\s*\}.*_DEPRECATED.*;\s*$', decl):
963 deprecated = '<DEPRECATED/>\n'
964 if AddSymbolToList(slist, symbol):
965 structsym = in_declaration.upper()
966 regex = r'(?:\s+(?:G_GNUC_\w+(?:\(\w*\))?%s))' % ignore_decorators
967 stripped_decl = re.sub(regex, '', decl)
968 decl_list.append('<%s>\n<NAME>%s</NAME>\n%s%s</%s>\n' %
969 (structsym, symbol, deprecated, stripped_decl, structsym))
970 if symbol in forward_decls:
971 del forward_decls[symbol]
972 deprecated_conditional_nest = int(deprecated_conditional_nest)
973 in_declaration = ''
974 else:
975 # We use tr to count the brackets in the line, and adjust
976 # $level accordingly.
977 level += line.count('{')
978 level -= line.count('}')
979 logging.info('struct/union level : %d', level)
981 # here we want in_declaration=='', otherwise we have a partial declaration
982 # Code commented out because it breaks some projects. More work is needed.
983 #if in_declaration != '':
984 # raise RuntimeError('partial declaration (%s) : %s ' % (in_declaration, decl))
986 # print remaining forward declarations
987 for symbol in sorted(forward_decls.keys()):
988 if forward_decls[symbol]:
989 AddSymbolToList(slist, symbol)
990 decl_list.append(forward_decls[symbol])
992 # add title
993 if title:
994 slist = [title] + slist
995 return slist, doc_comments
998def remove_braced_content(decl):
999 """Remove all nested pairs of curly braces.
1001 Args:
1002 decl (str): the decl
1004 Returns:
1005 str: a declaration stripped of braced content
1006 """
1008 skip_block = True
1009 # Remove all nested pairs of curly braces.
1010 brace_remover = r'{[^{]*?}'
1011 bm = re.search(brace_remover, decl)
1012 while bm:
1013 decl = re.sub(brace_remover, '', decl)
1014 logging.info('decl=[%s]' % decl)
1015 bm = re.search(brace_remover, decl)
1017 # If all '{' have been matched and removed, we're done
1018 bm = re.search(r'(.*?){', decl)
1019 if not bm:
1020 # this is a hack to detect the end of declaration
1021 decl = decl.rstrip() + ';'
1022 skip_block = False
1023 logging.info('skip_block done')
1025 return skip_block, decl
1028def is_inline_func(line):
1029 line = line.strip()
1030 if line.startswith('G_INLINE_FUNC'):
1031 logging.info('skip block after G_INLINE_FUNC function')
1032 return True
1033 if re.search(r'static\s+inline', line):
1034 logging.info('skip block after static inline function')
1035 return True
1036 return False
1039def format_ret_type(base_type, const, ptr):
1040 ret_type = base_type
1041 if const:
1042 ret_type += const
1043 if ptr:
1044 ret_type += ' ' + ptr.strip()
1045 return ret_type
1048def SeparateSubSections(slist, doc_comments):
1049 """Separate the standard macros and functions.
1051 Place them at the end of the current section, in a subsection named
1052 'Standard'. Do this in a loop to catch objects, enums and flags.
1054 Args:
1055 slist (list): list of symbols
1056 doc_comments (dict): comments for each symbol
1058 Returns:
1059 str: the section doc xml fomatted as string
1060 """
1062 klass = lclass = prefix = lprefix = None
1063 standard_decl = []
1064 liststr = '\n'.join(s for s in slist if s) + '\n'
1065 while True:
1066 m = re.search(r'^(\S+)_IS_(\S*)_CLASS\n', liststr, flags=re.MULTILINE)
1067 m2 = re.search(r'^(\S+)_IS_(\S*)\n', liststr, flags=re.MULTILINE)
1068 m3 = re.search(r'^(\S+?)_(\S*)_get_type\n', liststr, flags=re.MULTILINE)
1069 if m:
1070 prefix = m.group(1)
1071 lprefix = prefix.lower()
1072 klass = m.group(2)
1073 lclass = klass.lower()
1074 logging.info("Found gobject type '%s_%s' from is_class macro", prefix, klass)
1075 elif m2:
1076 prefix = m2.group(1)
1077 lprefix = prefix.lower()
1078 klass = m2.group(2)
1079 lclass = klass.lower()
1080 logging.info("Found gobject type '%s_%s' from is_ macro", prefix, klass)
1081 elif m3:
1082 lprefix = m3.group(1)
1083 prefix = lprefix.upper()
1084 lclass = m3.group(2)
1085 klass = lclass.upper()
1086 logging.info("Found gobject type '%s_%s' from get_type function", prefix, klass)
1087 else:
1088 break
1090 cclass = lclass
1091 cclass = cclass.replace('_', '')
1092 mtype = lprefix + cclass
1094 liststr, standard_decl = replace_once(liststr, standard_decl, r'^%sPrivate\n' % mtype)
1096 # We only leave XxYy* in the normal section if they have docs
1097 if mtype not in doc_comments:
1098 logging.info(" Hide instance docs for %s", mtype)
1099 liststr, standard_decl = replace_once(liststr, standard_decl, r'^%s\n' % mtype)
1101 if mtype + 'class' not in doc_comments:
1102 logging.info(" Hide class docs for %s", mtype)
1103 liststr, standard_decl = replace_once(liststr, standard_decl, r'^%sClass\n' % mtype)
1105 if mtype + 'interface' not in doc_comments:
1106 logging.info(" Hide iface docs for %s", mtype)
1107 liststr, standard_decl = replace_once(liststr, standard_decl, r'%sInterface\n' % mtype)
1109 if mtype + 'iface' not in doc_comments:
1110 logging.info(" Hide iface docs for " + mtype)
1111 liststr, standard_decl = replace_once(liststr, standard_decl, r'%sIface\n' % mtype)
1113 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_IS_%s\n' % klass)
1114 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_TYPE_%s\n' % klass)
1115 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_%s_get_type\n' % lclass)
1116 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_IS_%s_CLASS\n' % klass)
1117 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_%s_CLASS\n' % klass)
1118 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_%s_GET_CLASS\n' % klass)
1119 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_%s_GET_IFACE\n' % klass)
1120 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_%s_GET_INTERFACE\n' % klass)
1121 # We do this one last, otherwise it tends to be caught by the IS_$class macro
1122 liststr, standard_decl = replace_all(liststr, standard_decl, r'^\S+_%s\n' % klass)
1124 logging.info('Decl:%s---', liststr)
1125 logging.info('Std :%s---', ''.join(sorted(standard_decl)))
1126 if len(standard_decl):
1127 # sort the symbols
1128 liststr += '<SUBSECTION Standard>\n' + ''.join(sorted(standard_decl))
1129 return liststr
1132def replace_once(liststr, standard_decl, regex):
1133 mre = re.search(regex, liststr, flags=re.IGNORECASE | re.MULTILINE)
1134 if mre:
1135 standard_decl.append(mre.group(0))
1136 liststr = re.sub(regex, '', liststr, flags=re.IGNORECASE | re.MULTILINE)
1137 return liststr, standard_decl
1140def replace_all(liststr, standard_decl, regex):
1141 mre = re.search(regex, liststr, flags=re.MULTILINE)
1142 while mre:
1143 standard_decl.append(mre.group(0))
1144 liststr = re.sub(regex, '', liststr, flags=re.MULTILINE)
1145 mre = re.search(regex, liststr, flags=re.MULTILINE)
1146 return liststr, standard_decl
1149def AddSymbolToList(slist, symbol):
1150 """ Adds symbol to list of declaration if not already present.
1152 Args:
1153 slist: The list of symbols.
1154 symbol: The symbol to add to the list.
1155 """
1156 if symbol in slist:
1157 # logging.info('Symbol %s already in list. skipping', symbol)
1158 # we return False to skip outputting another entry to -decl.txt
1159 # this is to avoid redeclarations (e.g. in conditional sections).
1160 return False
1161 slist.append(symbol)
1162 return True
1165def new_args_parser():
1166 parser = argparse.ArgumentParser(
1167 description=f"gtkdoc-scan version {config.version} - scan header files for public symbols"
1168 )
1169 parser.add_argument("--version", action="version", version=config.version)
1170 parser.add_argument(
1171 "--module", required=True, help="Name of the doc module being processed."
1172 )
1173 parser.add_argument(
1174 "--source-dir",
1175 action="append",
1176 default=[],
1177 help="Directories containing the source files to scan",
1178 )
1179 parser.add_argument(
1180 "--ignore-headers",
1181 default="",
1182 help="A space-separated list of header files/dirs not to scan",
1183 )
1184 parser.add_argument(
1185 "--output-dir", default=".", help="The directory where the results are stored"
1186 )
1187 parser.add_argument(
1188 "--deprecated-guards",
1189 default="does_not_match_any_cpp_symbols_at_all_nope",
1190 help="A |-separated list of symbols used as deprecation guards",
1191 )
1192 parser.add_argument(
1193 "--ignore-decorators",
1194 default="(?=no)match",
1195 help="A |-separated list of additional decorators in"
1196 "declarations that should be ignored",
1197 )
1198 parser.add_argument(
1199 "--rebuild-sections",
1200 action="store_true",
1201 default=False,
1202 help="Rebuild (overwrite) the MODULE-sections.txt file",
1203 )
1204 parser.add_argument(
1205 "--rebuild-types",
1206 action="store_true",
1207 default=False,
1208 help="Automatically recreate the MODULE.types file using"
1209 "all the *_get_type() functions found",
1210 )
1211 parser.add_argument("headers", nargs="*")
1212 return parser