File indexing completed on 2024-04-28 07:40:48

0001 #!/usr/bin/env python
0002 #
0003 # SPDX-FileCopyrightText: 2016 Shaheed Haque <srhaque@theiet.org>
0004 # SPDX-FileCopyrightText: 2016 Stephen Kelly <steveire@gmail.com>
0005 #
0006 # SPDX-License-Identifier: BSD-3-Clause
0007 
0008 """SIP file generator for PyQt."""
0009 
0010 from __future__ import print_function
0011 import argparse
0012 import gettext
0013 import inspect
0014 import logging
0015 import sys
0016 import traceback
0017 from clang import cindex
0018 from clang.cindex import AccessSpecifier, AvailabilityKind, CursorKind, SourceRange, TokenKind, TypeKind, TranslationUnit
0019 
0020 import rules_engine
0021 
0022 
0023 class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
0024     pass
0025 
0026 
0027 logger = logging.getLogger(__name__)
0028 gettext.install(__name__)
0029 
0030 EXPR_KINDS = [
0031     CursorKind.UNEXPOSED_EXPR,
0032     CursorKind.CONDITIONAL_OPERATOR, CursorKind.UNARY_OPERATOR, CursorKind.BINARY_OPERATOR,
0033     CursorKind.INTEGER_LITERAL, CursorKind.FLOATING_LITERAL, CursorKind.STRING_LITERAL,
0034     CursorKind.CXX_BOOL_LITERAL_EXPR, CursorKind.CXX_STATIC_CAST_EXPR, CursorKind.DECL_REF_EXPR
0035 ]
0036 TEMPLATE_KINDS = [
0037                      CursorKind.TYPE_REF, CursorKind.TEMPLATE_REF, CursorKind.NAMESPACE_REF
0038                  ] + EXPR_KINDS
0039 
0040 def clang_diagnostic_to_logging_diagnostic(lvl):
0041     """
0042 
0043     The diagnostic levels in cindex.py are
0044 
0045         Ignored = 0
0046         Note    = 1
0047         Warning = 2
0048         Error   = 3
0049         Fatal   = 4
0050 
0051     and the leves in the python logging module are
0052 
0053         NOTSET      0
0054         DEBUG       10
0055         INFO        20
0056         WARNING     30
0057         ERROR       40
0058         CRITICAL    50
0059 
0060     """
0061     return (logging.NOTSET,
0062         logging.INFO,
0063         logging.WARNING,
0064         logging.ERROR,
0065         logging.CRITICAL)[lvl]
0066 
0067 def diagnostic_word(lvl):
0068     return ("", "info", "warning", "error", "fatality")[lvl]
0069 
0070 class SipGenerator(object):
0071     def __init__(self, project_rules, compile_flags, verbose=False, dump_includes=False, dump_privates=False):
0072         """
0073         Constructor.
0074 
0075         :param project_rules:       The rules for the file.
0076         :param compile_flags:       The compile flags for the file.
0077         :param dump_includes:       Turn on diagnostics for include files.
0078         :param dump_privates:       Turn on diagnostics for omitted private items.
0079         """
0080         self.rules = project_rules
0081         self.compile_flags = compile_flags
0082         self.verbose = verbose
0083         self.dump_includes = dump_includes
0084         self.dump_privates = dump_privates
0085         self.diagnostics = set()
0086         self.tu = None
0087         self.unpreprocessed_source = None
0088 
0089     @staticmethod
0090     def describe(cursor, text=None):
0091         if not text:
0092             text = cursor.spelling
0093         return "{} on line {} '{}'".format(cursor.kind.name, cursor.extent.start.line, text)
0094 
0095     def create_sip(self, h_file, include_filename):
0096         """
0097         Actually convert the given source header file into its SIP equivalent.
0098 
0099         :param h_file:              The source (header) file of interest.
0100         :param include_filename:    The (header) to generate in the sip file.
0101         """
0102 
0103         #
0104         # Read in the original file.
0105         #
0106         source = h_file
0107         self.unpreprocessed_source = []
0108         with open(source, "rb") as f:
0109             for line in f:
0110                 self.unpreprocessed_source.append(line)
0111 
0112         index = cindex.Index.create()
0113         self.tu = index.parse(source, ["-x", "c++"] + self.compile_flags)
0114         for diag in self.tu.diagnostics:
0115             #
0116             # We expect to be run over hundreds of files. Any parsing issues are likely to be very repetitive.
0117             # So, to avoid bothering the user, we suppress duplicates.
0118             #
0119             loc = diag.location
0120             msg = "{}:{}[{}] {}".format(loc.file, loc.line, loc.column, diag.spelling)
0121             if (diag.spelling == "#pragma once in main file"):
0122                 continue
0123             if msg in self.diagnostics:
0124                 continue
0125             self.diagnostics.add(msg)
0126             logger.log(clang_diagnostic_to_logging_diagnostic(diag.severity),
0127                 "Parse {}: {}".format(diagnostic_word(diag.severity), msg))
0128         if self.dump_includes:
0129             for include in sorted(set(self.tu.get_includes())):
0130                 logger.debug(_("Used includes {}").format(include.include.name))
0131         #
0132         # Run through the top level children in the translation unit.
0133         #
0134         body = self._container_get(self.tu.cursor, -1, h_file, include_filename)
0135         return body, self.tu.get_includes
0136 
0137     def skippable_attribute(self, parent, member, text, sip):
0138         """
0139         We don't seem to have access to the __attribute__(())s, but at least we can look for stuff we care about.
0140 
0141         :param member:          The attribute.
0142         :param text:            The raw source corresponding to the region of member.
0143         """
0144         if member.kind != CursorKind.VISIBILITY_ATTR:
0145             return False
0146         if member.spelling == "hidden":
0147             if self.dump_privates:
0148                 logger.debug("Ignoring private {}".format(SipGenerator.describe(parent)))
0149             sip["name"] = ""
0150             return True
0151         return False
0152 
0153     def _container_get(self, container, level, h_file, include_filename):
0154         """
0155         Generate the (recursive) translation for a class or namespace.
0156 
0157         :param container:           A class or namespace.
0158         :param h_file:              Name of header file being processed.
0159         :param level:               Recursion level controls indentation.
0160         :return:                    A string.
0161         """
0162 
0163         if container.kind.is_translation_unit():
0164             #
0165             # Any module-related manual code (%ExportedHeaderCode, %ModuleCode, %ModuleHeaderCode or other
0166             # module-level directives?
0167             #
0168             sip = {
0169                 "name": include_filename,
0170                 "decl": ""
0171             }
0172             self.rules.modulecode(include_filename, sip)
0173             body = sip["code"]
0174         else:
0175             body = ""
0176 
0177         sip = {
0178             "name": container.displayname,
0179             "annotations": set()
0180         }
0181         name = container.displayname
0182         if container.access_specifier == AccessSpecifier.PRIVATE:
0183             if self.dump_privates:
0184                 logger.debug("Ignoring private {}".format(SipGenerator.describe(container)))
0185             return ""
0186         base_specifiers = []
0187         template_type_parameters = []
0188         had_copy_constructor = False
0189         had_deleted_copy_constructor = False;
0190         had_const_member = False
0191         for member in container.get_children():
0192             #
0193             # Only emit items in the translation unit.
0194             #
0195             if member.location.file.name != self.tu.spelling:
0196                 continue
0197             decl = ""
0198             if member.kind in [CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, CursorKind.FUNCTION_TEMPLATE,
0199                                CursorKind.CONSTRUCTOR, CursorKind.DESTRUCTOR, CursorKind.CONVERSION_FUNCTION]:
0200                 decl = self._fn_get(container, member, level + 1)
0201             elif member.kind == CursorKind.ENUM_DECL:
0202                 decl = self._enum_get(container, member, level + 1) + ";\n"
0203             elif member.kind == CursorKind.CXX_ACCESS_SPEC_DECL:
0204                 decl = self._get_access_specifier(member, level + 1)
0205             elif member.kind == CursorKind.TYPEDEF_DECL:
0206                 decl = self._typedef_get(container, member, level + 1)
0207             elif member.kind == CursorKind.CXX_BASE_SPECIFIER:
0208                 #
0209                 # Strip off the leading "class". Except for TypeKind.UNEXPOSED...
0210                 #
0211                 base_specifiers.append(member.displayname.split(None, 2)[-1])
0212             elif member.kind == CursorKind.TEMPLATE_TYPE_PARAMETER:
0213                 template_type_parameters.append(member.displayname)
0214             elif member.kind == CursorKind.TEMPLATE_NON_TYPE_PARAMETER:
0215                 template_type_parameters.append(member.type.spelling + " " + member.displayname)
0216             elif member.kind in [CursorKind.VAR_DECL, CursorKind.FIELD_DECL]:
0217                 had_const_member = had_const_member or member.type.is_const_qualified()
0218                 decl = self._var_get(container, member, level + 1)
0219             elif member.kind in [CursorKind.NAMESPACE, CursorKind.CLASS_DECL,
0220                                  CursorKind.CLASS_TEMPLATE, CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION,
0221                                  CursorKind.STRUCT_DECL, CursorKind.UNION_DECL]:
0222                 decl = self._container_get(member, level + 1, h_file, include_filename)
0223             elif member.kind in TEMPLATE_KINDS + [CursorKind.USING_DECLARATION, CursorKind.USING_DIRECTIVE,
0224                                                   CursorKind.CXX_FINAL_ATTR]:
0225                 #
0226                 # Ignore:
0227                 #
0228                 #   TEMPLATE_KINDS: Template type parameter.
0229                 #   CursorKind.USING_DECLARATION, CursorKind.USING_DIRECTIVE: Using? Pah!
0230                 #   CursorKind.CXX_FINAL_ATTR: Again, not much to be done with this.
0231                 #
0232                 pass
0233             else:
0234                 text = self._read_source(member.extent)
0235                 if self.skippable_attribute(container, member, text, sip):
0236                     if not sip["name"]:
0237                         return ""
0238                 else:
0239                     SipGenerator._report_ignoring(container, member)
0240 
0241             def is_copy_constructor(member):
0242                 if member.kind != CursorKind.CONSTRUCTOR:
0243                     return False
0244                 numParams = 0
0245                 hasSelfType = False
0246                 for child in member.get_children():
0247                     numParams += 1
0248                     if child.kind == CursorKind.PARM_DECL:
0249                         paramType = child.type.spelling
0250                         paramType = paramType.split("::")[-1]
0251                         paramType = paramType.replace("const", "").replace("&", "").strip()
0252                         hasSelfType = paramType == container.displayname
0253                 return numParams == 1 and hasSelfType
0254 
0255             def has_parameter_default(parameter):
0256                 for member in parameter.get_children():
0257                     if member.kind.is_expression():
0258                         return True
0259                 return False
0260 
0261             def is_default_constructor(member):
0262                 if member.kind != CursorKind.CONSTRUCTOR:
0263                     return False
0264                 numParams = 0
0265                 for parameter in member.get_children():
0266                     if (has_parameter_default(parameter)):
0267                         break
0268                     numParams += 1
0269                 return numParams == 0
0270 
0271             if is_copy_constructor(member):
0272                 had_copy_constructor = True
0273                 # We need to generate a fake private copy constructor for deleted constructors
0274                 if member.availability == AvailabilityKind.NOT_AVAILABLE and member.access_specifier != AccessSpecifier.PRIVATE:
0275                     had_deleted_copy_constructor = True
0276                     continue
0277 
0278             #
0279             # Discard almost anything which is private.
0280             #
0281             if member.access_specifier == AccessSpecifier.PRIVATE:
0282                 if member.kind == CursorKind.CXX_ACCESS_SPEC_DECL:
0283                     #
0284                     # We need these because...
0285                     #
0286                     pass
0287                 elif is_copy_constructor(member) or is_default_constructor(member):
0288                     #
0289                     # ...we need to pass private copy constructors to the SIP compiler.
0290                     #
0291                     pass
0292                 else:
0293                     if self.dump_privates:
0294                         logger.debug("Ignoring private {}".format(SipGenerator.describe(member)))
0295                     continue
0296 
0297             if decl:
0298                 if self.verbose:
0299                     pad = " " * ((level + 1) * 4)
0300                     body += pad + "// {}\n".format(SipGenerator.describe(member))
0301                 body += decl
0302 
0303 
0304         if container.kind == CursorKind.TRANSLATION_UNIT:
0305             return body
0306 
0307         if container.kind == CursorKind.NAMESPACE:
0308             container_type = "namespace " + name
0309         elif container.kind in [CursorKind.CLASS_DECL, CursorKind.CLASS_TEMPLATE,
0310                                 CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION]:
0311             container_type = "class " + name
0312         elif container.kind == CursorKind.STRUCT_DECL:
0313             container_type = "struct " + name
0314         elif container.kind == CursorKind.UNION_DECL:
0315             container_type = "union " + name
0316         else:
0317             raise AssertionError(
0318                 _("Unexpected container {}: {}[{}]").format(container.kind, name, container.extent.start.line))
0319 
0320         sip["decl"] = container_type
0321         sip["template_parameters"] = template_type_parameters
0322 
0323         pad = " " * (level * 4)
0324 
0325         #
0326         # Empty containers are still useful if they provide namespaces or forward declarations.
0327         #
0328         if not body:
0329             text = self._read_source(container.extent)
0330             if not text.endswith("}"):
0331                 #
0332                 # Forward declaration.
0333                 #
0334                 modifying_rule = self.rules.forward_declaration_rules().apply(container, sip)
0335                 if sip["name"]:
0336                     if modifying_rule:
0337                         body += "// Modified {} (by {}):\n".format(SipGenerator.describe(container), modifying_rule)
0338                     if "External" in sip["annotations"]:
0339                         body += pad + sip["decl"]
0340                         body += " /External/;\n"
0341                     else:
0342                         body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), "default forward declaration handling")
0343 
0344                 else:
0345                     body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), modifying_rule)
0346         else:
0347             #
0348             # Generate private copy constructor for non-copyable types.
0349             #
0350             if (had_deleted_copy_constructor) or (had_const_member and not had_copy_constructor):
0351                 body += "    private:\n        {}(const {} &); // Generated\n".format(name, container.type.get_canonical().spelling)
0352             #
0353             # Flesh out the SIP context for the rules engine.
0354             #
0355             sip["base_specifiers"] = base_specifiers
0356             sip["body"] = body
0357             modifying_rule = self.rules.container_rules().apply(container, sip)
0358             if sip["name"]:
0359                 decl = ""
0360                 if modifying_rule:
0361                     decl += "// Modified {} (by {}):\n".format(SipGenerator.describe(container), modifying_rule)
0362                 decl += pad + sip["decl"]
0363 
0364                 if sip["base_specifiers"]:
0365                     decl += ": " + ", ".join(sip["base_specifiers"])
0366                 if sip["annotations"]:
0367                     decl += " /" + ",".join(sip["annotations"]) + "/"
0368                 if sip["template_parameters"]:
0369                     decl = pad + "template <" + ", ".join(sip["template_parameters"]) + ">\n" + decl
0370                 decl += "\n" + pad + "{\n"
0371                 decl += "%TypeHeaderCode\n#include <{}>\n%End\n".format(include_filename)
0372                 body = decl + sip["body"] + pad + "};\n"
0373             else:
0374                 body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), modifying_rule)
0375         return body
0376 
0377     def _get_access_specifier(self, member, level):
0378         """
0379         Skip access specifiers embedded in the Q_OBJECT macro.
0380         """
0381         access_specifier_text = self._read_source(member.extent)
0382         if access_specifier_text == "Q_OBJECT":
0383             return ""
0384         pad = " " * ((level - 1) * 4)
0385         access_specifier = ""
0386         if (access_specifier_text in ("Q_SIGNALS:", "signals:",
0387                                       "public Q_SLOTS:", "public slots:",
0388                                       "protected Q_SLOTS:", "protected slots:")):
0389             access_specifier = access_specifier_text
0390         elif member.access_specifier == AccessSpecifier.PRIVATE:
0391             access_specifier = "private:"
0392         elif member.access_specifier == AccessSpecifier.PROTECTED:
0393             access_specifier = "protected:"
0394         elif member.access_specifier == AccessSpecifier.PUBLIC:
0395             access_specifier = "public:"
0396 
0397         decl = pad + access_specifier + "\n"
0398         return decl
0399 
0400     def _enum_get(self, container, enum, level):
0401         pad = " " * (level * 4)
0402         scoped = ""
0403         if enum.is_scoped_enum():
0404             scoped = "class "
0405         decl = pad + "enum {}{} {{\n".format(scoped, enum.displayname)
0406         enumerations = []
0407         for enum in enum.get_children():
0408             #
0409             # Skip visibility attributes and the like.
0410             #
0411             if enum.kind == CursorKind.ENUM_CONSTANT_DECL:
0412                 enumerations.append(pad + "    {}".format(enum.displayname))
0413         decl += ",\n".join(enumerations) + "\n"
0414         decl += pad + "}"
0415         return decl
0416 
0417     def _fn_get(self, container, function, level):
0418         """
0419         Generate the translation for a function.
0420 
0421         :param container:           A class or namespace.
0422         :param function:            The function object.
0423         :param level:               Recursion level controls indentation.
0424         :return:                    A string.
0425         """
0426         if container.kind == CursorKind.TRANSLATION_UNIT and \
0427                 (function.semantic_parent.kind == CursorKind.CLASS_DECL or
0428                  function.semantic_parent.kind == CursorKind.STRUCT_DECL) and \
0429                 function.is_definition():
0430             # Skip inline methods
0431             return
0432 
0433         sip = {
0434             "name": function.spelling,
0435         }
0436         parameters = []
0437         parameter_modifying_rules = []
0438         template_parameters = []
0439         for child in function.get_children():
0440             if child.kind == CursorKind.PARM_DECL:
0441                 parameter = child.displayname or "__{}".format(len(parameters))
0442                 theType = child.type.get_canonical()
0443                 typeSpelling = theType.spelling
0444                 if theType.kind == TypeKind.POINTER:
0445                     typeSpelling = theType.get_pointee().spelling + "* "
0446 
0447                 decl = "{} {}".format(typeSpelling, parameter)
0448                 child_sip = {
0449                     "name": parameter,
0450                     "decl": decl,
0451                     "init": self._fn_get_parameter_default(function, child),
0452                     "annotations": set()
0453                 }
0454                 modifying_rule = self.rules.parameter_rules().apply(container, function, child, child_sip)
0455                 if modifying_rule:
0456                     parameter_modifying_rules.append("// Modified {} (by {}):\n".format(SipGenerator.describe(child), modifying_rule))
0457                 decl = child_sip["decl"]
0458                 if child_sip["annotations"]:
0459                     decl += " /" + ",".join(child_sip["annotations"]) + "/"
0460                 if child_sip["init"]:
0461                     decl += " = " + child_sip["init"]
0462                 parameters.append(decl)
0463             elif child.kind in [CursorKind.COMPOUND_STMT, CursorKind.CXX_OVERRIDE_ATTR,
0464                                 CursorKind.MEMBER_REF, CursorKind.DECL_REF_EXPR, CursorKind.CALL_EXPR] + TEMPLATE_KINDS:
0465                 #
0466                 # Ignore:
0467                 #
0468                 #   CursorKind.COMPOUND_STMT: Function body.
0469                 #   CursorKind.CXX_OVERRIDE_ATTR: The "override" keyword.
0470                 #   CursorKind.MEMBER_REF, CursorKind.DECL_REF_EXPR, CursorKind.CALL_EXPR: Constructor initialisers.
0471                 #   TEMPLATE_KINDS: The result type.
0472                 #
0473                 pass
0474             elif child.kind == CursorKind.TEMPLATE_TYPE_PARAMETER:
0475                 template_parameters.append(child.displayname)
0476             elif child.kind == CursorKind.TEMPLATE_NON_TYPE_PARAMETER:
0477                 template_parameters.append(child.type.spelling + " " + child.displayname)
0478             else:
0479                 text = self._read_source(child.extent)
0480                 if self.skippable_attribute(function, child, text, sip):
0481                     if not sip["name"]:
0482                         return ""
0483                 else:
0484                     SipGenerator._report_ignoring(function, child)
0485         #
0486         # Flesh out the SIP context for the rules engine.
0487         #
0488         sip["template_parameters"] = template_parameters
0489         if function.kind in [CursorKind.CONSTRUCTOR, CursorKind.DESTRUCTOR]:
0490             sip["fn_result"] = ""
0491         else:
0492             sip["fn_result"] = function.result_type.spelling
0493         sip["parameters"] = parameters
0494         sip["prefix"], sip["suffix"] = self._fn_get_decorators(function)
0495         modifying_rule = self.rules.function_rules().apply(container, function, sip)
0496         pad = " " * (level * 4)
0497         if sip["name"]:
0498             #
0499             # Any method-related code (%MethodCode, %VirtualCatcherCode, VirtualCallCode
0500             # or other method-related directives)?
0501             #
0502             self.rules.methodcode(function, sip)
0503             decl = ""
0504             if modifying_rule:
0505                 decl += "// Modified {} (by {}):\n".format(SipGenerator.describe(function), modifying_rule) + pad
0506             decl += pad.join(parameter_modifying_rules)
0507             if parameter_modifying_rules:
0508                 decl += pad
0509 
0510             decl += sip["name"] + "(" + ", ".join(sip["parameters"]) + ")"
0511             if sip["fn_result"]:
0512                 decl = sip["fn_result"] + " " + decl
0513             decl = pad + sip["prefix"] + decl + sip["suffix"]
0514             if sip["template_parameters"]:
0515                 decl = pad + "template <" + ", ".join(sip["template_parameters"]) + ">\n" + decl
0516             decl += ";\n"
0517             decl += sip["code"]
0518         else:
0519             decl = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(function), modifying_rule)
0520         return decl
0521 
0522     def _fn_get_decorators(self, function):
0523         """
0524         The parser does not provide direct access to the complete keywords (explicit, const, static, etc) of a function
0525         in the displayname. It would be nice to get these from the AST, but I cannot find where they are hiding.
0526 
0527         Now, we could resort to using the original source. That does not bode well if you have macros (QOBJECT,
0528         xxxDEPRECATED?), inlined bodies and the like, using the rule engine could be used to patch corner cases...
0529 
0530         ...or we can try to guess what SIP cares about, i.e static and maybe const. Luckily (?), we have those to hand!
0531 
0532         :param function:                    The function object.
0533         :return: prefix, suffix             String containing any prefix or suffix keywords.
0534         """
0535         suffix = ""
0536         if function.is_const_method():
0537             suffix += " const"
0538         prefix = ""
0539         if function.is_static_method():
0540             prefix += "static "
0541         if function.is_virtual_method():
0542             prefix += "virtual "
0543             if function.is_pure_virtual_method():
0544                 suffix += " = 0"
0545         return prefix, suffix
0546 
0547     def _fn_get_parameter_default(self, function, parameter):
0548         """
0549         The parser does not seem to provide access to the complete text of a parameter.
0550         This makes it hard to find any default values, so we:
0551 
0552             1. Run the lexer from "here" to the end of the file, bailing out when we see the ","
0553             or a ")" marking the end.
0554             2. Watch for the assignment.
0555         """
0556         def _get_param_type(parameter):
0557             result = parameter.type.get_declaration().type
0558 
0559             if result.kind != TypeKind.ENUM and result.kind != TypeKind.TYPEDEF and parameter.type.kind == TypeKind.LVALUEREFERENCE:
0560                 if parameter.type.get_pointee().get_declaration().type.kind != TypeKind.INVALID:
0561                     return parameter.type.get_pointee().get_declaration().type
0562                 return parameter.type.get_pointee()
0563 
0564             if parameter.type.get_declaration().type.kind == TypeKind.INVALID:
0565                 return parameter.type
0566 
0567             if (parameter.type.get_declaration().type.kind == TypeKind.TYPEDEF):
0568                 isQFlags = False
0569                 for member in parameter.type.get_declaration().get_children():
0570                     if member.kind == CursorKind.TEMPLATE_REF and member.spelling == "QFlags":
0571                         isQFlags = True
0572                     if isQFlags and member.kind == CursorKind.TYPE_REF:
0573                         result = member.type
0574                         break
0575 
0576             return result
0577 
0578         def _get_param_value(text, parameterType):
0579             if text == "0" or text == "nullptr":
0580                 return text
0581             if text == "{}":
0582                 if parameterType.kind == TypeKind.ENUM:
0583                     return "0"
0584                 if parameterType.kind == TypeKind.POINTER:
0585                     return "nullptr"
0586                 if parameterType.spelling.startswith("const "):
0587                     return parameterType.spelling[6:] + "()"
0588                 return parameterType.spelling + "()"
0589             if not "::" in parameterType.spelling:
0590                 return text
0591             try:
0592                 typeText, typeInit = text.split("(")
0593                 typeInit = "(" + typeInit
0594             except:
0595                 typeText = text
0596                 typeInit = ""
0597 
0598             if parameterType.kind == TypeKind.ENUM and parameterType.get_declaration().is_scoped_enum():
0599                 prefix = parameterType.spelling
0600             else:
0601                 prefix = parameterType.spelling.rsplit("::", 1)[0]
0602             if "::" in typeText:
0603                 typeText = typeText.rsplit("::", 1)[1]
0604             return prefix + "::" + typeText + typeInit
0605 
0606 
0607         for member in parameter.get_children():
0608             if member.kind.is_expression():
0609 
0610                 possible_extent = SourceRange.from_locations(parameter.extent.start, function.extent.end)
0611                 text = ""
0612                 bracket_level = 0
0613                 found_start = False
0614                 found_end = False
0615                 for token in self.tu.get_tokens(extent=possible_extent):
0616                     if (token.spelling == "="):
0617                         found_start = True
0618                         continue
0619                     if token.spelling == "," and bracket_level == 0:
0620                         found_end = True
0621                         break
0622                     elif token.spelling == "(":
0623                         bracket_level += 1
0624                         text += token.spelling
0625                     elif token.spelling == ")":
0626                         if bracket_level == 0:
0627                             found_end = True
0628                             break
0629                         bracket_level -= 1
0630                         text += token.spelling
0631                         if bracket_level == 0:
0632                             found_end = True
0633                             break
0634                     elif found_start:
0635                         text += token.spelling
0636                 if not found_end and text:
0637                     RuntimeError(_("No end found for {}::{}, '{}'").format(function.spelling, parameter.spelling, text))
0638 
0639                 parameterType = _get_param_type(parameter)
0640 
0641                 return _get_param_value(text, parameterType)
0642         return ""
0643 
0644     def _typedef_get(self, container, typedef, level):
0645         """
0646         Generate the translation for a typedef.
0647 
0648         :param container:           A class or namespace.
0649         :param typedef:             The typedef object.
0650         :param level:               Recursion level controls indentation.
0651         :return:                    A string.
0652         """
0653 
0654         sip = {
0655             "name": typedef.displayname,
0656             "decl": typedef.underlying_typedef_type.spelling,
0657             "annotations": set(),
0658         }
0659 
0660         self.rules.typedef_rules().apply(container, typedef, sip)
0661 
0662         pad = " " * (level * 4)
0663         if sip["name"]:
0664             decl = pad + "typedef {} {}".format(sip["decl"], sip["name"])
0665             decl += ";\n"
0666         else:
0667             decl = pad + "// Discarded {}\n".format(SipGenerator.describe(typedef))
0668         return decl
0669 
0670     def _var_get(self, container, variable, level):
0671         """
0672         Generate the translation for a variable.
0673 
0674         :param container:           A class or namespace.
0675         :param variable:            The variable object.
0676         :param level:               Recursion level controls indentation.
0677         :return:                    A string.
0678         """
0679 
0680         sip = {
0681             "name": variable.spelling
0682         }
0683         for child in variable.get_children():
0684             if child.kind in TEMPLATE_KINDS + [CursorKind.STRUCT_DECL, CursorKind.UNION_DECL]:
0685                 #
0686                 # Ignore:
0687                 #
0688                 #   TEMPLATE_KINDS, CursorKind.STRUCT_DECL, CursorKind.UNION_DECL: : The variable type.
0689                 #
0690                 pass
0691             else:
0692                 text = self._read_source(child.extent)
0693                 if self.skippable_attribute(variable, child, text, sip):
0694                     if not sip["name"]:
0695                         return ""
0696                 else:
0697                     SipGenerator._report_ignoring(variable, child)
0698         #
0699         # Flesh out the SIP context for the rules engine.
0700         #
0701         decl = "{} {}".format(variable.type.spelling, variable.spelling)
0702         sip["decl"] = decl
0703         modifying_rule = self.rules.variable_rules().apply(container, variable, sip)
0704 
0705         pad = " " * (level * 4)
0706         if sip["name"]:
0707             decl = sip["decl"]
0708             #
0709             # SIP does not support protected variables, so we ignore them.
0710             #
0711             if variable.access_specifier == AccessSpecifier.PROTECTED:
0712                 decl = pad + "// Discarded {}\n".format(SipGenerator.describe(variable))
0713             else:
0714                 decl = pad + decl + ";\n"
0715         else:
0716             decl = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(variable), modifying_rule)
0717         return decl
0718 
0719     def _read_source(self, extent):
0720         """
0721         Read the given range from the unpre-processed source.
0722 
0723         :param extent:              The range of text required.
0724         """
0725         # Extent columns are specified in bytes
0726         extract = self.unpreprocessed_source[extent.start.line - 1:extent.end.line]
0727         if extent.start.line == extent.end.line:
0728             extract[0] = extract[0][extent.start.column - 1:extent.end.column - 1]
0729         else:
0730             extract[0] = extract[0][extent.start.column - 1:]
0731             extract[-1] = extract[-1][:extent.end.column - 1]
0732         #
0733         # Return a single line of text.
0734         # Replace all kinds of newline variants (DOS, UNIX, MAC style) by single spaces
0735         #
0736         return b''.join(extract).decode('utf-8').replace("\r\n", " ").replace("\n", " ").replace("\r", " ")
0737 
0738     @staticmethod
0739     def _report_ignoring(parent, child, text=None):
0740         if not text:
0741             text = child.displayname or child.spelling
0742         logger.debug(_("Ignoring {} {} child {}").format(parent.kind.name, parent.spelling, SipGenerator.describe(child, text)))
0743 
0744 
0745 def main(argv=None):
0746     """
0747     Take a single C++ header file and generate the corresponding SIP file.
0748     Beyond simple generation of the SIP file from the corresponding C++
0749     header file, a set of rules can be used to customize the generated
0750     SIP file.
0751 
0752     Examples:
0753 
0754         sip_generator.py /usr/include/KF5/KItemModels/kselectionproxymodel.h
0755     """
0756     if argv is None:
0757         argv = sys.argv
0758     parser = argparse.ArgumentParser(epilog=inspect.getdoc(main),
0759                                      formatter_class=HelpFormatter)
0760     parser.add_argument("-v", "--verbose", action="store_true", default=False, help=_("Enable verbose output"))
0761     parser.add_argument("--flags",
0762                         help=_("Semicolon-separated C++ compile flags to use"))
0763     parser.add_argument("--include_filename", help=_("C++ header include to compile"))
0764     parser.add_argument("libclang", help=_("libclang library to use for parsing"))
0765     parser.add_argument("project_rules", help=_("Project rules"))
0766     parser.add_argument("source", help=_("C++ header to process"))
0767     parser.add_argument("output", help=_("output filename to write"))
0768     try:
0769         args = parser.parse_args(argv[1:])
0770         if args.verbose:
0771             logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s: %(message)s')
0772         else:
0773             logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
0774         #
0775         # Generate!
0776         #
0777 
0778         cindex.Config.set_library_file(args.libclang)
0779 
0780         rules = rules_engine.rules(args.project_rules)
0781         g = SipGenerator(rules, args.flags.lstrip().split(";"), args.verbose)
0782         body, includes = g.create_sip(args.source, args.include_filename)
0783         with open(args.output, "w") as outputFile:
0784             outputFile.write(body)
0785     except Exception:
0786         tbk = traceback.format_exc()
0787         print(tbk)
0788         return -1
0789 
0790 
0791 if __name__ == "__main__":
0792     if sys.argv[1] != "--self-check":
0793         sys.exit(main())
0794     else:
0795         from PyQt5.Qt import PYQT_CONFIGURATION
0796         cindex.Config.set_library_file(sys.argv[2])