aboutsummaryrefslogtreecommitdiff
path: root/find-modules
diff options
context:
space:
mode:
authorStephen Kelly <steveire@gmail.com>2017-01-14 22:13:49 +0000
committerStephen Kelly <steveire@gmail.com>2017-01-15 11:18:50 +0000
commit636d6acc7adf8bf7169d38833340161dc42d3484 (patch)
tree2ff1bbefa5808aec21e902b10c9547e359067604 /find-modules
parent8c347c61abafa68e247ff4664ae658cfa15af932 (diff)
downloadextra-cmake-modules-636d6acc7adf8bf7169d38833340161dc42d3484.tar.gz
extra-cmake-modules-636d6acc7adf8bf7169d38833340161dc42d3484.tar.bz2
Bindings: Fix handling of forward declarations
It is not appropriate to decorate each forward declaration with the SIP attribute /External/. That is only needed for forward declarations of types which are defined in a different module. Local forward declarations can be omitted from the sip code. In sip code, a forward declaration followed later by a full class definition is an error. Omit forward declarations unless they are decorated with the external attribute. Introduce a rules database for consumers to decorate them with the attribute as required.
Diffstat (limited to 'find-modules')
-rw-r--r--find-modules/Qt5Ruleset.py7
-rwxr-xr-xfind-modules/rules_engine.py77
-rw-r--r--find-modules/sip_generator.py82
3 files changed, 131 insertions, 35 deletions
diff --git a/find-modules/Qt5Ruleset.py b/find-modules/Qt5Ruleset.py
index 9392ae18..2d8cfaac 100644
--- a/find-modules/Qt5Ruleset.py
+++ b/find-modules/Qt5Ruleset.py
@@ -107,6 +107,9 @@ def variable_rules():
def typedef_rules():
return []
+def forward_declaration_rules():
+ return []
+
class RuleSet(rules_engine.RuleSet):
"""
SIP file generator rules. This is a set of (short, non-public) functions
@@ -114,6 +117,7 @@ class RuleSet(rules_engine.RuleSet):
"""
def __init__(self):
self._container_db = rules_engine.ContainerRuleDb(container_rules)
+ self._forward_declaration_db = rules_engine.ForwardDeclarationRuleDb(forward_declaration_rules)
self._fn_db = rules_engine.FunctionRuleDb(function_rules)
self._param_db = rules_engine.ParameterRuleDb(parameter_rules)
self._typedef_db = rules_engine.TypedefRuleDb(typedef_rules)
@@ -124,6 +128,9 @@ class RuleSet(rules_engine.RuleSet):
def container_rules(self):
return self._container_db
+ def forward_declaration_rules(self):
+ return self._forward_declaration_db
+
def function_rules(self):
return self._fn_db
diff --git a/find-modules/rules_engine.py b/find-modules/rules_engine.py
index 34bd15dc..897d0433 100755
--- a/find-modules/rules_engine.py
+++ b/find-modules/rules_engine.py
@@ -235,6 +235,71 @@ class ContainerRuleDb(AbstractCompiledRuleDb):
return None
+class ForwardDeclarationRuleDb(AbstractCompiledRuleDb):
+ """
+ THE RULES FOR FORWARD DECLARATIONS.
+
+ These are used to customise the behaviour of the SIP generator by allowing
+ the forward declaration for any container (class, struct, union) to be
+ customised, for example to add SIP compiler annotations.
+
+ Each entry in the raw rule database must be a list with members as follows:
+
+ 0. A regular expression which matches the fully-qualified name of the
+ "container" enclosing the container.
+
+ 1. A regular expression which matches the container name.
+
+ 2. A regular expression which matches any template parameters.
+
+ 3. A function.
+
+ In use, the database is walked in order from the first entry. If the regular
+ expressions are matched, the function is called, and no further entries are
+ walked. The function is called with the following contract:
+
+ def declaration_xxx(container, sip, matcher):
+ '''
+ Return a modified declaration for the given container.
+
+ :param container: The clang.cindex.Cursor for the container.
+ :param sip: A dict with the following keys:
+
+ name The name of the container.
+ template_parameters Any template parameters.
+ annotations Any SIP annotations.
+
+ :param matcher: The re.Match object. This contains named
+ groups corresponding to the key names above
+ EXCEPT body and annotations.
+
+ :return: An updated set of sip.xxx values. Setting sip.name to the
+ empty string will cause the container to be suppressed.
+ '''
+
+ :return: The compiled form of the rules.
+ """
+ def __init__(self, db):
+ super(ForwardDeclarationRuleDb, self).__init__(db, ["parents", "container", "template_parameters"])
+
+ def apply(self, container, sip):
+ """
+ Walk over the rules database for containers, applying the first matching transformation.
+
+ :param container: The clang.cindex.Cursor for the container.
+ :param sip: The SIP dict (may be modified on return).
+ :return: Modifying rule or None (even if a rule matched, it may not modify things).
+ """
+ parents = _parents(container)
+ matcher, rule = self._match(parents, sip["name"],
+ ", ".join(sip["template_parameters"]))
+ if matcher:
+ before = deepcopy(sip)
+ rule.fn(container, sip, matcher)
+ return rule.trace_result(parents, container, before, sip)
+ return None
+
+
class FunctionRuleDb(AbstractCompiledRuleDb):
"""
THE RULES FOR FUNCTIONS.
@@ -774,6 +839,15 @@ class RuleSet(object):
raise NotImplemented(_("Missing subclass implementation"))
@abstractmethod
+ def forward_declaration_rules(self):
+ """
+ Return a compiled list of rules for containers.
+
+ :return: A ForwardDeclarationRuleDb instance
+ """
+ raise NotImplemented(_("Missing subclass implementation"))
+
+ @abstractmethod
def function_rules(self):
"""
Return a compiled list of rules for functions.
@@ -888,6 +962,9 @@ def typedef_discard(container, typedef, sip, matcher):
def discard_QSharedData_base(container, sip, matcher):
sip["base_specifiers"].remove("QSharedData")
+def mark_forward_declaration_external(container, sip, matcher):
+ sip["annotations"].add("External")
+
def rules(project_rules):
"""
Constructor.
diff --git a/find-modules/sip_generator.py b/find-modules/sip_generator.py
index 41dd6369..7df9de3d 100644
--- a/find-modules/sip_generator.py
+++ b/find-modules/sip_generator.py
@@ -313,30 +313,51 @@ class SipGenerator(object):
pad = " " * ((level + 1) * 4)
body += pad + "// {}\n".format(SipGenerator.describe(member))
body += decl
+
+
+ if container.kind == CursorKind.TRANSLATION_UNIT:
+ return body
+
+ if container.kind == CursorKind.NAMESPACE:
+ container_type = "namespace " + name
+ elif container.kind in [CursorKind.CLASS_DECL, CursorKind.CLASS_TEMPLATE,
+ CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION]:
+ container_type = "class " + name
+ elif container.kind == CursorKind.STRUCT_DECL:
+ container_type = "struct " + name
+ elif container.kind == CursorKind.UNION_DECL:
+ container_type = "union " + name
+ else:
+ raise AssertionError(
+ _("Unexpected container {}: {}[{}]").format(container.kind, name, container.extent.start.line))
+
+ sip["decl"] = container_type
+ sip["template_parameters"] = template_type_parameters
+
+ pad = " " * (level * 4)
+
#
# Empty containers are still useful if they provide namespaces or forward declarations.
#
- if not body and level >= 0:
- body = "\n"
+ if not body:
text = self._read_source(container.extent)
if not text.endswith("}"):
#
# Forward declaration.
#
- sip["annotations"].add("External")
- if body and level >= 0:
- if container.kind == CursorKind.NAMESPACE:
- container_type = "namespace " + name
- elif container.kind in [CursorKind.CLASS_DECL, CursorKind.CLASS_TEMPLATE,
- CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION]:
- container_type = "class " + name
- elif container.kind == CursorKind.STRUCT_DECL:
- container_type = "struct " + name
- elif container.kind == CursorKind.UNION_DECL:
- container_type = "union " + name
- else:
- raise AssertionError(
- _("Unexpected container {}: {}[{}]").format(container.kind, name, container.extent.start.line))
+ modifying_rule = self.rules.forward_declaration_rules().apply(container, sip)
+ if sip["name"]:
+ if modifying_rule:
+ body += "// Modified {} (by {}):\n".format(SipGenerator.describe(container), modifying_rule)
+ if "External" in sip["annotations"]:
+ body += pad + sip["decl"]
+ body += " /External/;\n"
+ else:
+ body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), "default forward declaration handling")
+
+ else:
+ body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), modifying_rule)
+ else:
#
# Generate private copy constructor for non-copyable types.
#
@@ -345,33 +366,24 @@ class SipGenerator(object):
#
# Flesh out the SIP context for the rules engine.
#
- sip["template_parameters"] = template_type_parameters
- sip["decl"] = container_type
sip["base_specifiers"] = base_specifiers
sip["body"] = body
modifying_rule = self.rules.container_rules().apply(container, sip)
- pad = " " * (level * 4)
if sip["name"]:
decl = ""
if modifying_rule:
decl += "// Modified {} (by {}):\n".format(SipGenerator.describe(container), modifying_rule)
decl += pad + sip["decl"]
- if "External" in sip["annotations"]:
- #
- # SIP /External/ does not seem to work as one might wish. Suppress.
- #
- body = decl + " /External/;\n"
- body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), "/External/ handling")
- else:
- if sip["base_specifiers"]:
- decl += ": " + ", ".join(sip["base_specifiers"])
- if sip["annotations"]:
- decl += " /" + ",".join(sip["annotations"]) + "/"
- if sip["template_parameters"]:
- decl = pad + "template <" + ", ".join(sip["template_parameters"]) + ">\n" + decl
- decl += "\n" + pad + "{\n"
- decl += "%TypeHeaderCode\n#include <{}>\n%End\n".format(include_filename)
- body = decl + sip["body"] + pad + "};\n"
+
+ if sip["base_specifiers"]:
+ decl += ": " + ", ".join(sip["base_specifiers"])
+ if sip["annotations"]:
+ decl += " /" + ",".join(sip["annotations"]) + "/"
+ if sip["template_parameters"]:
+ decl = pad + "template <" + ", ".join(sip["template_parameters"]) + ">\n" + decl
+ decl += "\n" + pad + "{\n"
+ decl += "%TypeHeaderCode\n#include <{}>\n%End\n".format(include_filename)
+ body = decl + sip["body"] + pad + "};\n"
else:
body = pad + "// Discarded {} (by {})\n".format(SipGenerator.describe(container), modifying_rule)
return body