Files
gtkada/contrib/binding.py
Anthony Leonardo Gracio 21ee71a6b8 Add pragma Obsolescent to constants
The bindings generator now detect the 'deprecated' tag
for constants too, and add a pragma Obsolescent for them.

For eng/ide/gnatstudio#330
2024-06-20 15:12:33 +00:00

3052 lines
116 KiB
Python

#!/usr/bin/env python2
"""
Parse a .gir file for any of the gtk+ libraries (gtk+, glib,...)
and generate Ada bindings.
"""
# Issues:
# - Missing handling of <field> nodes (see GtkArrow for instance)
# - Some comments contain xref like "#GtkMisc". Not sure what to do with
# those. Likewise for names of subprograms in comments.
#
# Backward incompatibility:
# - Missing documentation for some properties.
# SOLVE: we could point to the corresponding Set_* and Get_* subprograms,
# or simply ignore the missing doc
from xml.etree.cElementTree import parse, Element, QName, tostring, fromstring
from adaformat import *
import copy
from binding_gtkada import GtkAda
from data import enums, interfaces, binding, user_data_params
from data import destroy_data_params
import sys
# Unfortunately, generating the slot marshallers in a separate package
# does not work since we end up with circularities in a number of
# cases. For now, we simply duplicate them as needed
SHARED_SLOT_MARSHALLERS = False
# For parsing command line options
from optparse import OptionParser
# Python interpreter version check: this script does not work with Python
# version 3.7 or earlier!
from sys import version_info
version_string = '.'.join(map(str, version_info[0:3]))
if version_info[0] < 3:
print(('Need at least Python 3.7, got version ' + version_string))
quit(1)
if version_info[0] == 3 and version_info[1] < 7:
print(('Need at least Python 3.7, got version ' + version_string))
quit(1)
uri = "http://www.gtk.org/introspection/core/1.0"
glib_uri = "http://www.gtk.org/introspection/glib/1.0"
c_uri = "http://www.gtk.org/introspection/c/1.0"
cidentifier = QName(c_uri, "identifier").text
cidentifier_prefix = QName(c_uri, "identifier-prefixes").text
ctype_qname = QName(c_uri, "type").text
ggettype = QName(glib_uri, "get-type").text
gsignal = QName(glib_uri, "signal").text
glib_type_struct = QName(glib_uri, "type-struct").text
glib_type_name = QName(glib_uri, "type-name").text
namespace = QName(uri, "namespace").text
narray = QName(uri, "array").text
nbitfield = QName(uri, "bitfield").text
ncallback = QName(uri, "callback").text
nclass = QName(uri, "class").text
ndoc = QName(uri, "doc").text
nenumeration = QName(uri, "enumeration").text
nfield = QName(uri, "field").text
nfunction = QName(uri, "function").text
nimplements = QName(uri, "implements").text
ninterface = QName(uri, "interface").text
nmember = QName(uri, "member").text
nmethod = QName(uri, "method").text
nvirtualmethod = QName(uri, "virtual-method").text
nparam = QName(uri, "parameter").text
nparams = QName(uri, "parameters").text
nrecord = QName(uri, "record").text
nunion = QName(uri, "union").text
nreturn = QName(uri, "return-value").text
ntype = QName(uri, "type").text
nvalue = QName(uri, "value").text
nvarargs = QName(uri, "varargs").text
nconstant = QName(uri, "constant").text
ninstanceparam = QName(uri, "instance-parameter").text
class GIR(object):
def __init__(self, files):
"""Parse filename and initializes SELF"""
self.packages = dict() # Ada name (lower case) -> Package instance
self.ccode = ""
self.classes = dict() # Maps C name to a GIRClass instance
self.interfaces = dict() # Maps GIR's "name" to an interface
self.ctype_interfaces = dict() # Maps GIR's c:type to an interface
self.callbacks = dict() # Ada name to GIR XML node
self.enums = dict() # Maps C "name" to a GIR XML node
self.globals = GlobalsBinder(self) # global vars
self.records = dict() # Maps C "name" to a GIR XML node
# Maps C "name" to a GIR XML node for constants
self.constants = dict()
self.bound = set() # C names for the entities that have an Ada binding
# The marshallers that have been generated when we use a slot object.
# These can be shared among all packages, since the profiles of the
# handlers are the same.
if SHARED_SLOT_MARSHALLERS:
self.slot_marshallers = set()
self.slot_marshaller_pkg = self.get_package(
name="Gtkada.Marshallers",
ctype=None,
doc="Automatically generated, used internally by GtkAda")
self.slot_marshaller_section = self.slot_marshaller_pkg.section("")
for filename in files:
_tree = parse(filename)
root = _tree.getroot()
identifier_prefix = root.find(namespace).get(cidentifier_prefix)
k = "%s/%s" % (namespace, ncallback)
for cl in root.findall(k):
ct = cl.get(ctype_qname)
type = Callback(naming.case(ct))
naming.add_type_exception(cname=ct, type=type)
ct = naming.type(ct).ada
self.callbacks[ct] = cl
k = "%s/%s" % (namespace, ninterface)
for cl in root.findall(k):
self.ctype_interfaces[cl.get(ctype_qname)] = \
self.interfaces[cl.get("name")] = \
self._create_class(
root, cl, is_interface=True, is_gobject=False,
identifier_prefix=identifier_prefix)
k = "%s/%s" % (namespace, nclass)
for cl in root.findall(k):
if cl.get(ctype_qname) is not None:
self.classes[cl.get(ctype_qname)] = self._create_class(
root, cl, is_interface=False,
identifier_prefix=identifier_prefix)
k = "%s/%s" % (namespace, nconstant)
for cl in root.findall(k):
self.constants[cl.get(ctype_qname)] = cl
# Some <record> are defined with methods. They are bound the same
# way in GtkAda, except that they do not derive from GObject
k = "%s/%s" % (namespace, nrecord)
for cl in root.findall(k):
if cl.findall(nmethod):
self.classes[cl.get(ctype_qname)] = self._create_class(
root, cl, is_interface=False, is_gobject=False,
identifier_prefix=identifier_prefix)
self.records[cl.get(ctype_qname)] = cl
k = "%s/%s" % (namespace, nunion)
for cl in root.findall(k):
if cl.findall(nmethod):
self.classes[cl.get(ctype_qname)] = self._create_class(
root, cl, is_interface=False, is_gobject=False,
identifier_prefix=identifier_prefix)
self.records[cl.get(ctype_qname)] = cl
for enums in (nenumeration, nbitfield):
k = "%s/%s" % (namespace, enums)
for cl in root.findall(k):
self.enums[cl.get(ctype_qname)] = cl
self.globals.add(root)
def show_unbound(self):
"""Display the list of entities known in the GIR files, but that have
no Ada binding.
"""
print("Missing bindings:")
count = 0
for name in sorted(gir.interfaces.keys()):
if name not in self.bound:
sys.stdout.write("%-28s" % (name + "(intf)", ))
count += 1
if (count % 4) == 0:
sys.stdout.write("\n")
for name in sorted(gir.classes.keys()):
if name not in self.bound:
sys.stdout.write("%-28s" % name)
# print ' "--%s", # Not tested yet, from Gio' % name
count += 1
if (count % 4) == 0:
sys.stdout.write("\n")
print()
def _get_class_node(self, rootNode, girname):
"""Find the <class> node in the same XML document as node that matches
[girname].
"""
k = "{%(uri)s}namespace/{%(uri)s}class" % {"uri": uri}
for cl in rootNode.findall(k):
if cl.get("name") == girname:
return cl
return None
def _create_class(self, rootNode, node, is_interface,
identifier_prefix, is_gobject=True,
has_toplevel_type=True):
return GIRClass(self, rootNode=rootNode, node=node,
is_interface=is_interface,
is_gobject=is_gobject,
identifier_prefix=identifier_prefix,
has_toplevel_type=has_toplevel_type)
def debug(self, element):
"""A debug form of element"""
return tostring(element)
def get_package(self, name, ctype, doc=""):
"""Return a handle to an Ada package"""
if not name.lower() in self.packages:
pkg = self.packages[name.lower()] = Package(
name=name,
doc=gtkada.get_pkg(ctype).get_doc())
else:
pkg = self.packages[name.lower()]
if doc:
pkg.doc = ["<description>", doc, "</description>"] + pkg.doc
return pkg
def generate(self, out, cout):
"""Generate Ada code for all packages"""
for pkg in self.packages.values():
out.write(pkg.spec().encode('UTF-8'))
out.write(b"\n")
out.write(pkg.body().encode('UTF-8'))
out.write(b"\n")
cout.write(self.ccode.encode("UTF-8"))
class GlobalsBinder(object):
def __init__(self, gir):
self.gir = gir
self.globals = dict()
def add(self, node):
k = "{%(uri)s}namespace/{%(uri)s}function" % {"uri": uri}
all = node.findall(k)
if all is not None:
for c in all:
id = c.get(cidentifier)
self.globals[id] = c
def get_function(self, id):
"""Return the XML node corresponding to a global function"""
return self.globals[id]
def _get_clean_doc(node):
"""
Get the <doc> child node, and replace common unicode characters by their
ASCII equivalent to keep the Ada specs more readable in a terminal.
"""
doc = node.findtext(ndoc, "")
if doc:
doc = doc.replace("\u2019", "'").replace(
"\u201c", '"').replace("\u201d", '"')
return doc
def _get_type(nodeOrType, allow_access=True, allow_none=False,
transfer_ownership=False, userecord=True, pkg=None):
"""Return the type of the GIR XML node.
nodeOrType can be one of:
* a string, given the name of the C type
* an instance of CType, which is returned as is.
* An XML node from which the information is gathered.
`allow_access' should be False if "access Type" parameters should
not be allowed, and an explicit type is needed instead.
`allow_none': see doc for CType.
`pkg' is used to add with statements, if specified
"""
if isinstance(nodeOrType, CType):
return nodeOrType
elif isinstance(nodeOrType, str):
return naming.type(name=nodeOrType,
cname=nodeOrType,
userecord=userecord,
transfer_ownership=transfer_ownership,
allow_access=allow_access,
allow_none=allow_none, pkg=pkg)
else:
t = nodeOrType.find(ntype)
if t is not None:
if t.get("name") == "none":
return None
ctype_name = t.get(ctype_qname)
if ctype_name:
ctype_name = ctype_name.replace("const ", "")
return naming.type(name=t.get("name"),
cname=ctype_name,
userecord=userecord,
transfer_ownership=transfer_ownership,
allow_access=allow_access,
allow_none=allow_none, pkg=pkg)
a = nodeOrType.find(narray)
if a is not None:
t = a.find(ntype)
if a:
type = t.get(ctype_qname)
name = t.get("name") or type # Sometimes name is not set
size = a.get("fixed-size", None)
if type:
type = "array_of_%s" % (type, )
return naming.type(name="array_of_%s" % name,
cname=type,
pkg=pkg, isArray=True,
array_fixed_size=size,
transfer_ownership=transfer_ownership,
allow_none=allow_none,
userecord=userecord,
allow_access=allow_access)
a = nodeOrType.find(nvarargs)
if a is not None:
# A function with multiple arguments cannot be bound
# No need for an error message, we will already let the user know
# that the function is not bound.
return None
print("Error: XML Node has unknown type: %s (%s)" % (nodeOrType, nodeOrType.attrib))
return None
class SubprogramProfile(object):
"""A class that groups info on the parameters of a function and
its return type.
"""
def __init__(self):
self.node = None # the XML node for this profile
self.gtkmethod = None
self.params = None # list of parameters (None if we have varargs)
self.returns = None # return value (None for a procedure)
self.returns_doc = "" # documentation for returned value
self.doc = "" # documentation for the subprogram
# The following fields are used to handle callback parameters
# and generate an Ada generic
self.callback_param = [] # indexes of the callback parameter
self.user_data_param = -1 # index of the "user data" parameter
self.destroy_param = -1 # index of the parameter to destroy data
def __repr__(self):
return "<SubprogramProfile %s>" % self.node.get('name')
@staticmethod
def parse(node, gtkmethod, pkg=None, ignore_return=False):
"""Parse the parameter info and return type info from the XML
GIR node, overriding with binding.xml.
gtkmethod is the GtkAdaMethod that contains the overriding for the
various method attributes.
If pkg is specified, with statements are added as necessary.
If ignore_return is True, the return type is not parsed. This is
used for constructors, so that we do not end up adding extra 'with'
statements in the generated package.
"""
profile = SubprogramProfile()
profile.node = node
profile.gtkmethod = gtkmethod
# make sure to init the 'returns' field before the parameters, to be
# able to correctly set the parameters direction ('in out' or 'out'
# case)
if not ignore_return:
profile.returns = profile._returns(node, gtkmethod, pkg=pkg)
profile.params = profile._parameters(node, gtkmethod, pkg=pkg)
profile.doc = profile._getdoc(gtkmethod, node)
return profile
@staticmethod
def setter(node, pkg=None):
"""Create a new SubprogramProfile for a getter"""
profile = SubprogramProfile()
profile.node = node
profile.params = [Parameter("Value", _get_type(node, pkg))]
return profile
def callback_param_info(self):
"""If there is one or more callback parameters in this profile, return
them so that we can generate the appropriate function. Returns None
if there are no such parameters.
"""
if not self.callback_param:
return None
return [self.params[p] for p in self.callback_param]
def callback_destroy(self):
if self.destroy_param < 0:
return None
return self.params[self.destroy_param]
def callback_user_data(self):
"""Returns the name of the "user_data" parameter"""
if self.user_data_param == -1:
return None
return self.params[self.user_data_param].name
def has_varargs(self):
return self.params is None
def direct_c_map(self):
"""Wether all parameters and return value can be mapped directly from
C to Ada.
"""
for p in self.params:
# If a parameter is not mapped in Ada, we need an actual body
if not p.direct_c_map() or not p.ada_binding:
return False
return self.returns is None or self.returns.direct_c_map()
def c_params(self, localvars, code):
"""Returns the list of parameters for an Ada function that would be
a direct pragma Import. local variables or additional code will
be extended as needed to handle conversions.
"""
assert(isinstance(localvars, list))
assert(isinstance(code, list))
result = []
for p in self.params:
n = p.name
is_temporary = False
# Restore original mode only for arrays now
# because generator is not adopted for c_mode = p.c_mod and
# generates incorrect code for example for instance parameters
c_mode = p.mode
if p.type.isArray and p.c_mode == "out" \
and p.is_caller_allocates:
# C expects an allocated array like for "in out" mode
# so we will pass an address of buffer's first element
# and do not expect allocation in C
c_mode = "in"
# Pass the array as is, without creating temporary variable
as_array = p.type.isArray and p.is_caller_allocates \
and p.c_mode in ("in", "in out", "out")
if self.returns is not None and p.mode != "in" and p.ada_binding \
and as_array is False:
n = "Acc_%s" % p.name
var = Local_Var(
name=n,
aliased=True,
default="" if p.mode != "in out" else p.name,
type=p.type)
var.type.userecord = False
localvars.append(var)
if p.mode == "access":
if p.type.allow_none:
code.append(
"if %s /= null then %s.all := %s; end if;"
% (p.name, p.name, var.name))
else:
code.append("%s.all := %s;" % (p.name, var.name))
else:
is_temporary = p.mode != "out"
code.append("%s := %s;" % (p.name, var.name))
# If we do not bind the parameter in the Ada profile, we will need
# to substitute its default value instead. But we don't want to
# systematically put the default value, which is in Ada. We would
# end up with Interfaces.C.Strings.chars_ptr=""
result.append(Parameter(
name=n, mode=c_mode, type=p.type,
for_function=self.returns is not None,
default=p.default if not p.ada_binding else None,
is_temporary_variable=is_temporary,
ada_binding=p.ada_binding,
c_mode=p.c_mode,
ownership=p.ownership,
is_caller_allocates=p.is_caller_allocates))
return result
def set_class_wide(self):
"""This profile is not for a primitive operation, but for a class-wide
operation.
"""
if isinstance(self.params[0].type, GObject):
self.params[0].type.classwide = True
def add_param(self, pos, param):
"""Add a new parameter in the list, at the given position"""
self.params.insert(pos, param)
if self.callback_param:
self.callback_param = [p + 1 if p >= pos else p
for p in self.callback_param]
if self.user_data_param >= 0 and self.user_data_param >= pos:
self.user_data_param += 1
if self.destroy_param >= 0 and self.destroy_param >= pos:
self.destroy_param += 1
def replace_param(self, name_or_index, type):
"""Overrides the type of a parameter"""
if name_or_index is None:
return
if isinstance(name_or_index, int):
self.params[name_or_index].set_type(type)
else:
for idx, p in enumerate(self.params):
if p.name.lower() == name_or_index.lower():
self.params[idx].set_type(type)
return
def remove_param(self, names):
"""Remove the parameter with the given names from the list"""
assert(isinstance(names, list))
for n in names:
if n is not None:
n = n.lower()
for p in self.params:
if p.name.lower() == n:
self.params.remove(p)
break
def find_param(self, names):
"""Return the first name for which there is a parameter"""
for n in names:
lo = n.lower()
for p in self.params:
if p.name.lower() == lo:
return n
return None
def unset_default_values(self):
"""Remove the default values for the parameters"""
for p in self.params:
p.default = None
def subprogram(self, name, showdoc=True, local_vars=[], code=[],
convention=None, lang="ada"):
"""Return an instance of Subprogram with the corresponding profile.
lang is one of "ada", "c->ada" or "ada->c".
"""
params = self.params
if lang == "ada":
params = [p for p in self.params if p.ada_binding]
subp = Subprogram(
name=name,
plist=params,
returns=self.returns,
showdoc=showdoc,
doc=self.doc,
lang=lang,
local_vars=local_vars,
code=code)
subp.convention = convention or self.gtkmethod.convention()
if name != "":
depr = self.node.get("deprecated")
if depr is not None:
subp.mark_deprecated(
"\nDeprecated since %s, %s"
% (self.node.get("deprecated-version"), depr))
elif self.gtkmethod and self.gtkmethod.is_obsolete():
subp.mark_deprecated("Deprecated")
return subp
def _getdoc(self, gtkmethod, node):
doc = gtkmethod.get_doc(default=_get_clean_doc(node))
if node.get("version"):
doc.append("Since: gtk+ %s" % node.get("version"))
return doc
def _parameters(self, c, gtkmethod, pkg):
"""Parse the <parameters> child node of c"""
if c is None:
return []
params = c.find(nparams)
if params is None:
return []
# Check whether we'll bind to a procedure or to a function
is_function = self.returns and not gtkmethod.return_as_param()
result = []
for p_index, p in enumerate(params.findall(nparam)):
name = p.get("name")
if name == "...":
name = "varargs"
gtkparam = gtkmethod.get_param(name=name)
adan = gtkparam.ada_name()
ada_binding = adan is None or adan != ""
name = adan or name # override default computed name
default = gtkparam.get_default()
allow_access = not default
allow_none = gtkparam.allow_none(girnode=p) or default == 'null'
nodeOrType = gtkparam.get_type(pkg=pkg) or p
type = _get_type(
nodeOrType=nodeOrType,
allow_none=allow_none,
userecord=default != 'null',
allow_access=allow_access,
pkg=pkg)
if type is None:
if nodeOrType.find(nvarargs) is not None:
type = gtkmethod.get_param("varargs").get_type(pkg=pkg)
else:
type = gtkmethod.get_param(name).get_type(pkg=pkg)
if type is None:
return None
type = _get_type(type)
if not default and allow_none and isinstance(type, UTF8):
default = '""'
if (p.get("scope", "") in ("notified", "call", "async")
or p.get("closure", "") != ""):
# "async" means a callback with no closure. As a special case,
# we ignore it for destroy callbacks, since they are already
# handled specially.
if p.get("scope") != "async" \
or type.ada != "Glib.G_Destroy_Notify_Address":
self.callback_param.append(p_index)
self.user_data_param = int(p.get("closure", "-1"))
self.destroy_param = int(p.get("destroy", "-1")) - 1
direction = gtkparam.get_direction() or p.get("direction", "in")
assert direction in ("in", "out", "inout", "access"), \
"Invalid value for direction: '%s'" % direction
is_allocated = (gtkparam.get_caller_allocates() or
p.get("caller-allocates", None)) == "1"
ownership = (gtkparam.get_transfer_ownership() or
p.get("transfer-ownership", "none")) == "full"
if direction == "inout":
c_mode = "in out"
elif direction in ("out", "access"):
c_mode = direction
elif type.is_ptr:
c_mode = "in out"
else:
c_mode = "in"
if is_function and direction not in ("in", "access"):
mode = "access"
else:
mode = c_mode
doc = _get_clean_doc(p)
if doc:
doc = '"%s": %s' % (name, doc)
result.append(
Parameter(name=naming.case(name),
type=type,
mode=mode,
default=default,
ada_binding=ada_binding,
doc=doc,
c_mode=c_mode,
ownership=ownership,
is_caller_allocates=is_allocated))
return result
def _returns(self, node, gtkmethod, pkg):
"""Parse the method's return type"""
returns = gtkmethod.returned_c_type()
if returns is None:
ret = node.find(nreturn)
if ret is None:
# For a <field>, the method's return value will be the type
# of the field itself
ret = node
else:
self.returns_doc = _get_clean_doc(ret)
if self.returns_doc:
self.returns_doc = "Returns %s" % self.returns_doc
return _get_type(
ret, allow_access=False, pkg=pkg,
transfer_ownership=gtkmethod.transfer_ownership(ret))
else:
return naming.type(name=None, cname=returns, pkg=pkg)
class GIRClass(object):
"""Represents a gtk class"""
def __init__(self, gir, rootNode, node, identifier_prefix,
is_interface=False, is_gobject=True, has_toplevel_type=True):
"""If has_toplevel_type is False, no widget type is generated"""
self.gir = gir
self.node = node
self.rootNode = rootNode
self.ctype = self.node.get(ctype_qname)
if not self.ctype:
print("no c:type defined for %s" % (self.node.get(glib_type_name, )))
return
self._private = ""
self._generated = False
self.identifier_prefix = identifier_prefix
self.implements = dict() # Implemented interfaces
self.is_gobject = is_gobject
self.is_interface = is_interface
self.has_toplevel_type = has_toplevel_type
self.callbacks = set() # The callback functions
self.pkg = None # Instance of Package(), that we are generating
# List of Convert(...) functions that were implemented
self.conversions = dict()
self.__marshallers = set() # The generated marshallers
# Search for the GtkAda binding information
self.gtkpkg = gtkada.get_pkg(self.ctype)
if not self.gtkpkg.bindtype:
self.has_toplevel_type = False
self.is_gobject = False
# Is this a binding for an opaque C record (not a GObject). In this
# case, we bind it as an Ada tagged type so that we can use the
# convenient dot notation for primitive operations. This is only doable
# if there is no public field that should be user visible in the
# record. Otherwise, we'll map to a standard Ada record
# (self.is_record)
self.is_proxy = False
self.is_boxed = (self.node.tag == nrecord
and (not self.node.findall(nfield)
or self.node.get("introspectable", "1") == "0"))
self.is_record = self.node.tag == nrecord and not self.is_boxed
if (self.is_boxed
and naming.type_exceptions.get(self.ctype) is not None
and isinstance(naming.type_exceptions.get(self.ctype), Proxy)):
self.is_proxy = True
self.is_boxed = False
# Register naming exceptions for this class
n = naming.case(self.ctype)
into = self.gtkpkg.into()
ada = self.gtkpkg.ada_name()
if ada:
pkg = ada
elif into:
into = naming.case(into)
pkg = naming.protect_keywords(into.replace("_", ".", 1))
else:
pkg = naming.protect_keywords(n.replace("_", ".", 1))
pkg = "%s.%s" % (pkg, n)
naming.add_girname(girname=n, ctype=self.ctype)
if has_toplevel_type:
ctype = node.get(ctype_qname)
if is_interface:
t = Interface(pkg)
elif is_gobject:
t = GObject(pkg)
elif self.is_proxy:
t = Proxy(pkg)
elif self.is_boxed:
t = Tagged(pkg)
else:
t = Record(pkg)
naming.add_type_exception(cname=ctype, type=t)
classtype = naming.type(name=self.ctype)
typename = classtype.ada
self.name = package_name(typename)
if ada is not None:
self.name = ada
self.ada_package_name = self.name
if not self.has_toplevel_type:
# Compute the package name ignoring the type_exceptions. For
# instance, we have defined that GdkWindow is mapped to
# Gdk.Gdk_Window, but the operations should go into the package
# Gdk.Window.Gdk_Window.
self.ada_package_name = package_name(pkg)
else:
typename = ""
self.name = package_name(pkg)
self.ada_package_name = self.name
self.gtkpkg.register_types(adapkg=self.ada_package_name)
# Compute information that will be used for the binding
self._subst = { # for substitution in string templates
"name": self.name,
"typename": base_name(typename),
"cname": self.ctype or ""}
def _handle_function(self, section, c, ismethod=False, gtkmethod=None,
showdoc=True, isinherited=False):
cname = c.get(cidentifier)
if gtkmethod is None:
gtkmethod = self.gtkpkg.get_method(cname=cname)
if gtkmethod.bind():
profile = SubprogramProfile.parse(
node=c, gtkmethod=gtkmethod, pkg=self.pkg)
self._handle_function_internal(
section, node=c, cname=cname,
gtkmethod=gtkmethod,
profile=profile,
showdoc=showdoc,
ismethod=ismethod,
isinherited=isinherited)
else:
naming.add_cmethod(
cname, gtkmethod.ada_name() or cname) # Avoid warning later on
def _func_is_direct_import(self, profile):
"""Whether a function with this profile
should be implemented directly as a pragma Import, rather than
require its own body.
"""
return not self.is_gobject \
and not self.is_boxed \
and profile.direct_c_map()
def _add_self_param(self, adaname, node, gtkmethod, profile, inherited):
"""Add a Self parameter to the list of parameters in profile.
The exact type of the parameter depends on several criteria.
:param bool inherited: should be true if this is for a subprogram
inherited from an interface (in which case we force the type of
Self to be that of the child, not the interface type as described
in the gir file)
"""
# Try to extract the type of the parameter from the instance-parameter
# node.
t = None
if not inherited:
try:
ip = next(node.iter(ninstanceparam))
ipt = ip.find(ntype)
if ipt is not None:
ctype_name = ipt.get(ctype_qname)
if ctype_name:
ctype_name = ctype_name.replace('const ', '')
t = naming.type(name=ipt.get('name'),
cname=ctype_name,
useclass=gtkmethod.is_class_wide())
except StopIteration:
t = None
# There was no instance-parameter node, guess the type from the
# package name
if t is None:
t = naming.type(self._subst["cname"],
cname=self._subst["cname"],
useclass=gtkmethod.is_class_wide())
gtkparam = gtkmethod.get_param("self")
pname = gtkparam.ada_name() or "Self"
direction = gtkparam.get_direction() or "in"
if direction in ("out", "access"):
mode = direction
elif direction == "inout":
mode = "in out"
else:
mode = "in"
profile.add_param(0, Parameter(name=pname, type=t, mode=mode))
def _handle_function_internal(self, section, node, cname,
gtkmethod,
profile=None,
showdoc=True,
adaname=None,
ismethod=False,
isinherited=False):
"""Generate a binding for a function.,
This returns None if no binding was made, an instance of Subprogram
otherwise.
`adaname' is the name of the generated Ada subprograms. By default,
it is computed automatically from either binding.xml or the "name"
attribute of `node'.
`profile' is an instance of SubprogramProfile
"""
assert(profile is None or isinstance(profile, SubprogramProfile))
if profile.has_varargs() \
and gtkmethod.get_param("varargs").node is None:
naming.add_cmethod(cname, cname) # Avoid warning later on.
print("No binding for %s: varargs" % cname)
return None
is_import = self._func_is_direct_import(profile) \
and not gtkmethod.get_body() \
and not gtkmethod.return_as_param()
adaname = adaname or gtkmethod.ada_name() or node.get("name").title()
adaname = naming.protect_keywords(adaname)
if not isinherited:
naming.add_cmethod(cname, "%s.%s" % (self.pkg.name, adaname))
if ismethod:
self._add_self_param(
adaname, node, gtkmethod, profile, inherited=isinherited)
if adaname.startswith("Gtk_New"):
# Overrides the GIR file even if it reported a function or method
self._handle_constructor(
node, gtkmethod=gtkmethod, cname=cname, profile=profile)
return
local_vars = []
call = ""
body = gtkmethod.get_body()
if not is_import:
# Prepare the Internal C function
internal_call = []
internal = Subprogram(
name="Internal",
returns=profile.returns,
lang="ada->c",
plist=profile.c_params(local_vars, internal_call)
).import_c(cname)
# Should we transform the return value into a parameter ?
ret_as_param = gtkmethod.return_as_param()
if ret_as_param is not None:
profile.params.append(
Parameter(name=ret_as_param,
type=profile.returns, mode="out"))
profile.returns = None
# Is this a function that takes a callback parameter ?
cb = profile.callback_param_info()
if cb is not None:
if ret_as_param:
# ??? We would need to change _callback_support to
# have additional code to set the return value. One
# issue is that the profile has already been changed
raise Exception("Cannot bind function with callback"
+ " and return value as parameter: %s"
% cname)
return self._callback_support(adaname, cname, profile, cb)
execute = internal.call(
in_pkg=self.pkg, extra_postcall="".join(internal_call))
if ret_as_param is not None:
assert execute[1] is not None, \
"Must have a return value in %s => %s" % (cname, execute)
call = "%s%s := %s;" % (execute[0], ret_as_param, execute[1])
else:
if execute[1]: # A function, with a standard "return"
call = "%sreturn %s;" % (execute[0], execute[1])
else:
call = execute[0]
local_vars += execute[2]
subp = profile.subprogram(name=adaname, showdoc=showdoc,
local_vars=local_vars, code=call)
if is_import:
subp.import_c(cname)
else:
subp.add_nested(internal)
if body:
subp.set_body(" " + body.strip() + "\n")
section.add(subp)
return subp
def _callback_support(self, adaname, cname, profile, cb):
"""Add support for a function with a callback parameter and user data.
We generate multiple bindings for such a function:
* One version that doesn't take a user_data. This looks like:
type My_Callback is access function (Self, other_params);
procedure Gtk_Func (Self : ...; Cb : My_Callback);
since My_Callback doesn't have exactly the same profile as
required by gtk+, we in fact go through an intermediate function
in the body, to which we pass, as user_data, a pointer to the
user's callback:
function Internal_Callback (same_profile_as_c, user_data) is
User_Func : My_Callback := convert (user_data);
begin
return User_Func (...);
end Internal_Callback;
pragma Convention (C, Internal_Callback);
* Ideally we want to generate a generic package to which users
can pass their own user data type. We then need to generate the
proper Destroy callback that C will call to free that user data.
:profile: is an instance of SubprogramProfile.
:cname: is the name of the gtk+ C function.
:adaname: is the name of the corresponding Ada function.
:cb: is a list of Parameter instances representing the callback
parameters.
"""
if len(cb) > 1:
print("No binding for %s: multiple callback parameters" % cname)
return
cb = cb[0]
def call_to_c(gtk_func, values,
user_data_setup='', user_data_cleanup=''):
"""Implement the call to the C function.
If the user passes a null callback, we always want to pass null
to C rather than passing our Internal_Callback'Address.
:param values: a dictionary of the parameters to pass to call().
:return: the code for the Ada function's body
"""
values_if_null = copy.deepcopy(values)
values_if_null[cb.name.lower()] = "System.Null_Address"
if user_data is not None:
values_if_null[user_data.lower()] = "System.Null_Address"
exec1 = gtk_func.call(
in_pkg=self.pkg,
extra_postcall="".join(call), values=values_if_null)
call1 = gtk_func.call_to_string(exec1, lang="ada->c")
if not call1.endswith(";"):
call1 += ";"
exec2 = gtk_func.call(
in_pkg=self.pkg,
extra_postcall="".join(call), values=values)
call2 = gtk_func.call_to_string(exec2, lang="ada->c")
if not call2.endswith(";"):
call2 += ";"
if user_data_setup:
call2 = user_data_setup + '\n' + call2
if user_data_cleanup:
call2 += '\n' + user_data_cleanup
return ("""if %s = null then
%s
else
%s
end if;""" % (cb.name, call1, call2), exec2[2])
cbname = cb.type.param
# Compute the name of the Ada type representing the user callback
cb_type_name = naming.type(name=cb.type.ada, cname=cbname).ada
funcname = base_name(cb_type_name)
destroy = profile.find_param(destroy_data_params)
# Compute the profile of the callback (will all its arguments)
gtkmethod = self.gtkpkg.get_method(cname=cname)
try:
cb_gir_node = self.gir.callbacks[cb.type.ada]
except:
raise Exception(
"No GIR node for %s in callback %s" % (cb.type.ada, cname))
cbgtk = self.gtkpkg.get_method(cbname)
cb_profile = SubprogramProfile.parse(
cb_gir_node, gtkmethod=cbgtk, pkg=self.pkg)
user_data = profile.callback_user_data()
cb_user_data = cb_profile.find_param(user_data_params)
if user_data is None and cb_user_data is not None:
user_data = cb_user_data
# Generate the access-to-subprogram type for the user callback, unless
# we have already done so. This is the type that doesn't receive
# user data.
if cbname not in self.callbacks:
self.callbacks.add(cbname) # Prevent multiple generations
section = self.pkg.section("Callbacks")
if cb_user_data is None:
print("callback has no user data: %s" % cbname)
# If the C function has no user data, we do not know how to
# generate a high-level binding, since we cannot go through an
# intermediate C function that transforms the parameters into
# their Ada equivalent.
#
# Instead, we just generate a low-level C callback passing
# System.Address for widgets.
nouser_cb_profile = copy.deepcopy(cb_profile)
subp = nouser_cb_profile.subprogram(name="", lang="ada->c")
section.add(
"\ntype %s is %s" % (funcname, subp.spec(pkg=self.pkg)))
section.add(
"\npragma Convention (C, %s);" % funcname)
section.add(
("function To_Address is new Ada.Unchecked_Conversion\n"
+ " (%s, System.Address);\n") % (cb_type_name,),
in_spec=False)
else:
# Generate a simpler version of the callback, without
# user data, that the Ada applications can use
nouser_cb_profile = copy.deepcopy(cb_profile)
nouser_cb_profile.remove_param(
destroy_data_params + [cb_user_data])
subp = nouser_cb_profile.subprogram(name="")
section.add(
"\ntype %s is %s" % (funcname, subp.spec(pkg=self.pkg)))
# Generate a subprogram in the body to act as the C callback.
# This subprogram is responsible for calling the user's
# callback. In the call to the user's callback, we need to
# convert the parameters from the C values to the
# corresponding Ada values.
self.pkg.add_with(
"Ada.Unchecked_Conversion", do_use=False, specs=False)
section.add(
("function To_%s is new Ada.Unchecked_Conversion\n"
+ " (System.Address, %s);\n") % (funcname, funcname),
in_spec=False)
section.add(
("function To_Address is new Ada.Unchecked_Conversion\n"
+ " (%s, System.Address);\n") % (cb_type_name,),
in_spec=False)
ada_func = copy.deepcopy(subp)
if ada_func.plist:
ada_func.name = "Func"
else:
ada_func.name = "Func.all"
ada_func_call = ada_func.call(in_pkg=self.pkg, lang="c->ada")
body_cb = cb_profile.subprogram(
name="Internal_%s" % funcname,
local_vars=[Local_Var(
"Func", "constant %s" % funcname,
"To_%s (%s)" % (funcname, cb_user_data))]
+ ada_func_call[2],
lang="c->ada",
code=ada_func.call_to_string(ada_func_call, lang="c->ada"))
body_cb.convention = "C"
body_cb.doc = []
section.add(body_cb, in_spec=False)
# The gtk C function, will all parameters.
# This will be used to generate the "Internal" nested subprogram.
local_vars = []
call = []
gtk_func_profile = copy.deepcopy(profile)
if cb is not None:
gtk_func_profile.replace_param(cb.name, "System.Address")
if cb_user_data is not None:
gtk_func_profile.replace_param(destroy, "System.Address")
gtk_func = gtk_func_profile.subprogram(
name=naming.case("C_%s" % cname), lang="ada->c").import_c(cname)
# This function is shared both by the version without user_data and by
# the generic package, so we need to put it directly in the package,
# not a nested subprogram.
self.pkg.section("").add(gtk_func, in_spec=False)
# Create a version of the function without a user data.
section = self.pkg.section("Methods")
nouser_profile = copy.deepcopy(profile)
if user_data is None:
values = {destroy: "System.Null_Address",
cb.name.lower(): "To_Address (%s)" % cb.name}
elif cb_user_data is None:
values = {destroy: "System.Null_Address",
cb.name.lower(): "Internal_%s'Address" % funcname,
user_data.lower(): "To_Address (%s)" % cb.name}
else:
nouser_profile.remove_param(destroy_data_params + [user_data])
values = {destroy: "System.Null_Address",
cb.name.lower(): "Internal_%s'Address" % funcname,
user_data.lower(): "To_Address (%s)" % cb.name}
c_call = call_to_c(gtk_func, values)
subp = nouser_profile.subprogram(
name=adaname, local_vars=c_call[1],
code=c_call[0])
section.add(subp)
# Now create a generic package that will provide access to
# user_data. The function can no longer be a primitive operation of the
# object, since it is in a nested package.
# It is possible that the function doesn't accept a user data in fact
# (for instance when scope="async"). In this case, no need for a
# generic package.
user_data2 = cb_profile.find_param(user_data_params)
if user_data2 is not None:
# If we have no "destroy" callback, so any memory we allocate via
# User.Build would be leaked. Better not to provide a binding
# in such a case.
# As a special case, some functions do not need to keep the data
# after their execution, so we can still bind those.
if profile.callback_destroy() is None \
and '_for' not in cname \
and cname not in ('gtk_tree_view_map_expanded_rows',
'pango_attributes_filter',
'gdk_window_invalidate_maybe_recurse',
'gtk_menu_popup'):
pass
else:
self.pkg.add_with("Glib.Object", do_use=False, specs=False)
pkg2 = Package(name="%s_User_Data" % adaname)
section.add(pkg2)
pkg2.formal_params = """type User_Data_Type (<>) is private;
with procedure Destroy (Data : in out User_Data_Type) is null;"""
sect2 = pkg2.section("")
sect2.add("""package Users is new Glib.Object.User_Data_Closure
(User_Data_Type, Destroy);""", in_spec=False)
sect2.add(
("function To_%s is new Ada.Unchecked_Conversion\n"
+ " (System.Address, %s);\n") % (funcname, funcname),
in_spec=False)
sect2.add(
("function To_Address is new Ada.Unchecked_Conversion\n"
+ " (%s, System.Address);\n") % (funcname,),
in_spec=False)
cb_profile2 = copy.deepcopy(cb_profile)
cb_profile2.replace_param(user_data2, "User_Data_Type")
cb2 = cb_profile2.subprogram(name="")
sect2.add(
"\ntype %s is %s" % (funcname, cb2.spec(pkg=pkg2)))
values = {user_data2.lower(): "D.Data.all"}
user_cb = cb_profile2.subprogram(
name="To_%s (D.Func)" % funcname)
user_cb_call = user_cb.call(
in_pkg=self.pkg,
lang="c->ada",
extra_postcall="".join(call), values=values)
internal_cb = cb_profile.subprogram(
name="Internal_Cb",
local_vars=[
Local_Var("D", "constant Users.Internal_Data_Access",
"Users.Convert (%s)" % user_data2)]
+ user_cb_call[2],
convention="C",
lang="c->ada",
code=user_cb.call_to_string(user_cb_call, lang="c->ada"))
sect2.add(internal_cb, in_spec=False)
values = {destroy: "Users.Free_Data'Address",
cb.name.lower(): "%s'Address" % internal_cb.name,
user_data.lower(): "D"}
full_profile = copy.deepcopy(profile)
full_profile.set_class_wide()
full_profile.remove_param(destroy_data_params)
full_profile.replace_param(cb.name, funcname)
full_profile.replace_param(user_data, "User_Data_Type")
if profile.callback_destroy() is None:
c_call = call_to_c(
gtk_func, values,
user_data_setup=
"D := Users.Build (To_Address (%s), %s);" %
(cb.name, user_data),
user_data_cleanup="Users.Free_Data (D);")
else:
c_call = call_to_c(
gtk_func, values,
user_data_setup=
"D := Users.Build (To_Address (%s), %s);" %
(cb.name, user_data))
subp2 = full_profile.subprogram(
name=adaname,
local_vars=c_call[1] + [Local_Var("D", "System.Address")],
code=c_call[0])
sect2.add(subp2)
return subp
def _constructors(self):
n = QName(uri, "constructor").text
for c in self.node.findall(n):
cname = c.get(cidentifier)
gtkmethod = self.gtkpkg.get_method(cname=cname)
if not gtkmethod.bind():
naming.add_cmethod(
cname, gtkmethod.ada_name() or cname) # Avoid warning
continue
profile = SubprogramProfile.parse(
node=c, gtkmethod=gtkmethod, pkg=self.pkg,
ignore_return=True)
if profile.has_varargs() \
and gtkmethod.get_param("varargs").node is None:
naming.add_cmethod(cname, cname) # Avoid warning later on.
print("No binding for %s: varargs" % cname)
continue
self._handle_constructor(
c, gtkmethod=gtkmethod, cname=cname, profile=profile)
def _handle_constructor(self, c, cname, gtkmethod, profile=None):
assert(profile is None or isinstance(profile, SubprogramProfile))
section = self.pkg.section("Constructors")
name = c.get("name").title()
assert profile.params is not None, "No profile defined for %s" % cname
format_params = ", ".join(p.name for p in profile.params)
if format_params:
self._subst["internal_params"] = " (%s)" % format_params
format_params = ", " + format_params
self._subst["params"] = format_params
else:
self._subst["params"] = ""
self._subst["internal_params"] = ""
if self.is_gobject or self.is_boxed:
profile.returns = AdaType(
"System.Address", pkg=self.pkg, in_spec=False)
else:
profile.returns = AdaType(
"%(typename)s" % self._subst,
pkg=self.pkg, in_spec=False)
local_vars = []
code = []
internal = Subprogram(
name="Internal",
lang="ada->c",
plist=profile.c_params(local_vars, code),
returns=profile.returns).import_c(cname)
call = internal.call(in_pkg=self.pkg)
assert(call[1] is not None) # A function
gtk_new_prefix = "Gtk_New"
adaname = gtkmethod.ada_name()
if not adaname:
if cname.startswith("gdk_") or cname.startswith("pango_"):
gtk_new_prefix = "Gdk_New"
adaname = "Gdk_%s" % name # e.g. Gdk_New
elif cname.startswith("g_"):
gtk_new_prefix = "G_New"
adaname = "G_%s" % name # e.g. G_New
else:
adaname = "Gtk_%s" % name # e.g. Gtk_New
selfname = gtkmethod.get_param("self").ada_name() or "Self"
if self.is_gobject:
selftype = "%(typename)s_Record'Class" % self._subst
else:
selftype = "%(typename)s" % self._subst
if self.is_gobject:
filtered_params = [p for p in profile.params if p.ada_binding]
initialize_name=adaname.replace(
gtk_new_prefix, "%s.Initialize" % self.pkg.name)
initialize_params = [Parameter(
name=selfname,
type=AdaType(selftype, pkg=self.pkg, in_spec=True),
mode="not null access")] + filtered_params
initialize = Subprogram(
name=initialize_name,
plist=initialize_params,
local_vars=local_vars + call[2],
doc=profile.doc +
[('\n%s does nothing if the object was already' +
' created with another call to Initialize* or G_New.') % (
base_name(initialize_name), )],
code="if not %s.Is_Created then %sSet_Object (%s, %s); end if" % (
selfname, call[0], selfname, call[1]),
).add_nested(internal)
call = initialize.call(in_pkg=self.pkg)
assert(call[1] is None) # This is a procedure
naming.add_cmethod(cname, "%s.%s" % (self.pkg.name, adaname))
gtk_new = Subprogram(
name=adaname,
plist=[Parameter(
name=selfname,
type=AdaType("%(typename)s" % self._subst,
pkg=self.pkg, in_spec=True),
mode="out")] + filtered_params,
local_vars=call[2],
code=selfname + " := new %(typename)s_Record;" % self._subst
+ call[0],
doc=profile.doc)
section.add(gtk_new)
section.add(initialize)
# Gtk_New as a function
gtk_new = Subprogram(
name="%s_%s" % (self._subst["typename"], name),
returns=AdaType("%(typename)s" % self._subst,
pkg=self.pkg, in_spec=True),
plist=filtered_params,
local_vars=call[2] +
[Local_Var(selfname,
"constant %(typename)s" % self._subst,
"new %(typename)s_Record" % self._subst)],
code=call[0] + "return %s;" % selfname,
doc=profile.doc)
section.add(gtk_new)
elif self.is_boxed:
gtk_new = Subprogram(
name=adaname,
plist=[Parameter(
name=selfname,
type=AdaType("%(typename)s" % self._subst,
pkg=self.pkg, in_spec=True),
mode="out")] + profile.params,
local_vars=local_vars + call[2],
code="%s%s.Set_Object (%s)" % (call[0], selfname, call[1]),
doc=profile.doc)
gtk_new.add_nested(internal)
section.add(gtk_new)
# Now as a function
gtk_new = Subprogram(
name="%s_%s" % (self._subst["typename"], name),
returns=AdaType("%(typename)s" % self._subst,
pkg=self.pkg, in_spec=True),
plist=profile.params,
local_vars=local_vars + call[2] +
[Local_Var(selfname,
"%(typename)s" % self._subst)],
code="%s%s.Set_Object (%s); return %s" % (
call[0], selfname, call[1], selfname),
doc=profile.doc)
gtk_new.add_nested(internal)
section.add(gtk_new)
else:
# likely a Proxy
gtk_new = Subprogram(
name=adaname,
plist=[Parameter(
name=selfname,
type=AdaType("%(typename)s" % self._subst,
pkg=self.pkg, in_spec=True),
mode="out")] + profile.params,
local_vars=local_vars + call[2],
code="%s%s := %s" % (call[0], selfname, call[1]),
doc=profile.doc)
gtk_new.add_nested(internal)
section.add(gtk_new)
# Now as a function
gtk_new = Subprogram(
name="%s_%s" % (self._subst["typename"], name),
returns=AdaType("%(typename)s" % self._subst,
pkg=self.pkg, in_spec=True),
plist=profile.params,
local_vars=local_vars + call[2] +
[Local_Var(selfname,
"%(typename)s" % self._subst)],
code="%s%s := %s; return %s;" % (
call[0], selfname, call[1], selfname),
doc=profile.doc)
gtk_new.add_nested(internal)
section.add(gtk_new)
def _methods(self):
all = self.node.findall(nmethod)
if all is not None:
section = self.pkg.section("Methods")
for c in all:
self._handle_function(section, c, ismethod=True)
def _functions(self):
all = self.node.findall(nfunction)
if all is not None:
section = self.pkg.section("Functions")
for c in all:
self._handle_function(section, c)
def _virtual_methods(self):
all = self.node.findall(nvirtualmethod)
has_iface = False
if all is not None:
ifacename = base_name(self.name)
info = ''
for c in all:
if not self.gtkpkg.bind_virtual_method(
c.get('name'), default=self.is_interface):
continue
gtkmethod = self.gtkpkg.get_method(cname=c.get('name'))
basename = gtkmethod.ada_name() or c.get('name').title()
adaname = "Virtual_%s" % basename
if not has_iface:
has_iface = True
section = self.pkg.section("Virtual Methods")
info += """
procedure Set_%(basename)s
(Self : %(type)s;
Handler : %(adaname)s);
pragma Import (C, Set_%(basename)s, "gtkada_%(ifacename)s_set_%(method)s");
""" % {"basename": basename,
"type": "%s_Interface_Descr" % ifacename
if self.is_interface
else "Glib.Object.GObject_Class",
"adaname": adaname,
"ifacename": ifacename,
"method": basename.lower()}
c_iface = '%s%s' % (
self.identifier_prefix,
self.node.get(glib_type_struct))
self.gir.ccode += """
void gtkada_%(type)s_set_%(method)s(%(iface)s* iface, void* handler) {
iface->%(field)s = handler;
}
""" % {"type": ifacename,
"method": basename.lower(),
"iface": c_iface,
"field": c.get('name')}
profile = SubprogramProfile.parse(
node=c,
gtkmethod=gtkmethod,
pkg=self.pkg)
self._add_self_param(
adaname=adaname, node=c, gtkmethod=gtkmethod, profile=profile,
inherited=False)
subp = profile.subprogram(
name=adaname,
lang="ada->c",
convention="C",
showdoc=True)
section.add(
'\ntype %s is %s' % (
adaname,
subp.spec(pkg=self.pkg, as_type=True)))
subp.add_withs_for_subprogram(pkg=self.pkg, in_specs=True)
if has_iface:
info = ('subtype %s_Interface_Descr is ' % ifacename
+ 'Glib.Object.Interface_Description;\n'
+ info
+ '-- See Glib.Object.Add_Interface\n')
section.add(info)
self.pkg.add_with('Glib.Object')
def _globals(self):
funcs = self.gtkpkg.get_global_functions() # List of binding.xml nodes
if funcs:
section = self.pkg.section("Functions") # Make sure section exists
for f in funcs:
c = self.gir.globals.get_function(
f.cname()) # Node in gir file
self._handle_function(section, c, gtkmethod=f)
def _method_get_type(self):
"""Generate the Get_Type subprogram"""
n = self.node.get(ggettype)
if n is not None:
section = self.pkg.section("Constructors")
gtkmethod = self.gtkpkg.get_method(cname=n)
if not gtkmethod.bind():
return
self.pkg.add_with("Glib")
get_type_name = gtkmethod.ada_name() or "Get_Type"
section.add(
Subprogram(
name=get_type_name,
doc=gtkmethod.get_doc(default=''),
returns=AdaType("Glib.GType", pkg=self.pkg, in_spec=True))
.import_c(n))
if not self.gtktype.is_subtype() \
and not self.is_interface \
and self.is_gobject \
and self._subst["parent"] is not None:
self.pkg.add_with("Glib.Type_Conversion_Hooks", specs=False)
self._subst["get_type"] = get_type_name
section.add(
("package Type_Conversion_%(typename)s is new"
+ " Glib.Type_Conversion_Hooks.Hook_Registrator\n"
+ " (%(get_type)s'Access, %(typename)s_Record);\n"
+ "pragma Unreferenced (Type_Conversion_%(typename)s);""")
% self._subst, in_spec=False)
def _get_c_type(self, node):
t = node.find(ntype)
if t is not None:
return t.get(ctype_qname)
return None
def _fields(self):
fields = self.node.findall(nfield)
if fields:
section = self.pkg.section("Fields")
for f in fields:
name = f.get("name")
# Getter
if f.get("readable", "1") != "0":
cname = "gtkada_%(cname)s_get_%(name)s" % {
"cname": self._subst["cname"], "name": name}
gtkmethod = self.gtkpkg.get_method(cname=cname)
if gtkmethod.bind(default="false"):
profile = SubprogramProfile.parse(
node=f, gtkmethod=gtkmethod, pkg=self.pkg)
func = self._handle_function_internal(
section,
node=f,
cname=cname,
adaname="Get_%s" % name,
ismethod=True,
profile=profile,
gtkmethod=gtkmethod)
if func is not None:
ctype = self._get_c_type(f)
if ctype is None:
continue
self.gir.ccode += """
%(ctype)s %(cname)s (%(self)s* self) {
return self->%(name)s;
}
""" % {"ctype": ctype, "cname": cname, "self": self.ctype, "name": name}
# Setter
if f.get("writable", "1") != "0":
cname = "gtkada_%(cname)s_set_%(name)s" % {
"cname": self._subst["cname"], "name": name}
gtkmethod = self.gtkpkg.get_method(cname=cname)
if gtkmethod.bind("false"):
profile = SubprogramProfile.setter(
node=f, pkg=self.pkg)
func = self._handle_function_internal(
section,
node=f,
cname=cname,
adaname="Set_%s" % name,
ismethod=True,
gtkmethod=gtkmethod,
profile=profile)
if func is not None:
ctype = self._get_c_type(f)
if ctype is None:
continue
self.gir.ccode += """
void %(cname)s (%(self)s* self, %(ctype)s val) {
self->%(name)s = val;
}
""" % {"ctype": ctype, "cname": cname, "self": self.ctype, "name": name}
def _properties(self):
n = QName(uri, "property")
props = list(self.node.findall(n.text))
if props is not None:
adaprops = []
for p in props:
flags = []
if p.get("readable", "1") != "0":
flags.append("read")
if p.get("writable", "1") != "0":
flags.append("write")
# Do not provide a "pkg=self.pkg" parameter, since we do
# not want to generate a with for the actual property type
# (for instance a Gtk_Cell_Area), just for the actual type
# (Property_Object).
tp = _get_type(p)
tp.userecord = False
ptype = tp.as_property()
if ptype:
pkg = ptype[:ptype.rfind(".")]
if pkg:
self.pkg.add_with(pkg)
else:
section = self.pkg.section("Properties")
section.add(
(' %(name)s_Property : constant '
+ 'Glib.Properties.Property_String :=\n'
+ ' Glib.Properties.Build ("%(cname)s");'
+ ' -- Unknown type: %(type)s') % {
"name": naming.case(p.get("name")),
"type": (p.find(ntype).get("name")
if p.find(ntype) is not None
else "unspecified"),
"cname": p.get("name")})
self.pkg.add_with("Glib.Properties")
continue
adaprops.append({
"cname": p.get("name"),
"name": naming.case(p.get("name")),
"flags": "-".join(flags),
"doc": _get_clean_doc(p),
"pkg": pkg,
"ptype": ptype,
"type": tp.as_ada_param(self.pkg)})
if adaprops:
section = self.pkg.section("Properties")
section.add_comment(
"""The following properties are defined for this widget.
See Glib.Properties for more information on properties)""")
adaprops.sort(key=lambda x: x["name"])
for p in adaprops:
prop_str = ' %(name)s_Property : constant %(ptype)s;' % p
if not section.add(prop_str):
continue
# Only display the type if it isn't obvious from the actual
# type of the property.
if p["type"] not in ("Boolean", "UTF8_String", "Gfloat",
"Glib.Gint", "Guint") \
and not p["type"].startswith("Gtk.Enums."):
section.add(
Code("Type: %(type)s" % p, iscomment=True))
if p["flags"] != "read-write":
section.add(
Code("Flags: %(flags)s" % p, iscomment=True))
if p["doc"]:
section.add(Code("%s" % p["doc"], iscomment=True))
d = ' %(name)s_Property : constant %(ptype)s' % p
self.pkg.add_private(
d + ' :=\n %(pkg)s.Build ("%(cname)s");' % p)
def _compute_marshaller_suffix(self, selftype, profile):
"""Computes the suffix for the connect and marshallers types based on
the profile of the signal.
"""
return "_".join(
[base_name(selftype.ada)]
+ [base_name(p.type.ada) for p in profile.params]
+ [(base_name(profile.returns.ada)
if profile.returns else "Void")])
def _marshall_gvalue(self, profile):
"""Return the arguments to parse to an Ada callback, after extracting
them from a GValue. Returns a list of local variables for the
marshaller, and its body
"""
call_params = []
for index, p in enumerate(profile.params):
if isinstance(p.type, Tagged):
call_params.append(
"%s.From_Object (Unchecked_To_Address (Params, %d))" %
(package_name(p.type.ada), index + 1))
elif isinstance(p.type, Interface):
call_params.append(
"%s (Unchecked_To_Interface (Params, %d))" %
(p.type.ada, index + 1))
elif isinstance(p.type, GObject):
if p.type.ada != "Glib.Object.GObject":
call_params.append(
"%s (Unchecked_To_Object (Params, %d))" % (
p.type.ada, index + 1))
else:
call_params.append(
"Unchecked_To_Object (Params, %d)" % (index + 1, ))
else:
if p.mode != "in":
call_params.append(
"Unchecked_To_%s_Access (Params, %d)" % (
base_name(p.type.ada), index + 1))
else:
call_params.append(
"Unchecked_To_%s (Params, %d)" % (
base_name(p.type.ada), index + 1))
if call_params:
call = "H (Obj, %s)" % ", ".join(call_params)
else:
call = "H (Obj)"
if profile.returns:
marsh_local = [
Local_Var("V", profile.returns, aliased=True, default=call)]
marsh_body = "Set_Value (Return_Value, V'Address);"
else:
marsh_local = []
marsh_body = "%s;" % call
marsh_body += "exception when E : others => Process_Exception (E);"
return marsh_local, marsh_body
def _generate_slot_marshaller(self, section, selftype, node, gtkmethod):
"""Generate connect+marshaller when connect takes a slot object. These
procedure are independent of the specific widget, and can be shared.
Returns the name for the hander type.
"""
if SHARED_SLOT_MARSHALLERS:
pkg = gir.slot_marshaller_pkg
section = gir.slot_marshaller_section
existing_marshallers = gir.slot_marshallers
else:
pkg = self.pkg
section = section
existing_marshallers = self.__marshallers
profile = SubprogramProfile.parse(
node=node, gtkmethod=gtkmethod, pkg=pkg)
callback_selftype = GObject(
"Glib.Object.GObject", classwide=True, allow_none=True)
name_suffix = self._compute_marshaller_suffix(
callback_selftype, profile)
slot_name = "Cb_%s" % name_suffix
marshname = "Marsh_%s" % name_suffix
callback = Subprogram(
name="",
plist=[Parameter(name="Self", type=callback_selftype)]
+ profile.params,
code="null",
allow_none=False,
returns=profile.returns)
if slot_name not in existing_marshallers:
existing_marshallers.add(slot_name)
slot_handler_type = "type %s is %s;" % (
slot_name, callback.profile(
pkg=pkg,
maxlen=69, indent=" "))
section.add(Code(slot_handler_type), in_spec=True)
section.add(Code(
"""function Cb_To_Address is new Ada.Unchecked_Conversion
(%s, System.Address);
function Address_To_Cb is new Ada.Unchecked_Conversion
(System.Address, %s);
""" % (slot_name, slot_name)),
in_spec=False)
if isinstance(selftype, Interface):
obj_in_body = "Glib.Types.GType_Interface (Object)"
else:
obj_in_body = "Object"
connect_slot_body = """
Unchecked_Do_Signal_Connect
(Object => %s,
C_Name => C_Name,
Marshaller => %s'Access,
Handler => Cb_To_Address (Handler), -- Set in the closure
Slot_Object => Slot,
After => After)""" % (obj_in_body, marshname)
marsh_local, marsh_body = self._marshall_gvalue(profile)
connect_slot = Subprogram(
name="Connect_Slot",
plist=[Parameter("Object", selftype),
Parameter("C_Name", "Glib.Signal_Name"),
Parameter("Handler", slot_name),
Parameter("After", "Boolean"),
Parameter("Slot", GObject(
"Glib.Object.GObject", allow_none=True,
classwide=True), default="null")
],
code=connect_slot_body)
section.add(connect_slot, in_spec=False)
addr_to_obj = "Glib.Object.Convert (Get_Data (Closure))"
marsh = Subprogram(
name=marshname,
plist=[Parameter("Closure", "GClosure"),
Parameter("Return_Value", "Glib.Values.GValue"),
Parameter("N_Params", "Glib.Guint"),
Parameter("Params", "Glib.Values.C_GValues"),
Parameter("Invocation_Hint", "System.Address"),
Parameter("User_Data", "System.Address")],
local_vars=[
Local_Var("H", "constant %s" % slot_name,
"Address_To_Cb (Get_Callback (Closure))"),
Local_Var("Obj", "Glib.Object.GObject", constant=True,
default=addr_to_obj)
] + marsh_local,
convention="C",
code=marsh_body)
section.add(marsh, in_spec=False)
return slot_name, callback
def _generate_marshaller(self, section, selftype, node, gtkmethod):
"""Generate, if needed, a connect+marshaller for signals with this
profile.
Returns the name of the type that contains the subprogram profile
"""
profile = SubprogramProfile.parse(
node=node, gtkmethod=gtkmethod,
pkg=self.pkg if gtkmethod.bind() else None)
name_suffix = self._compute_marshaller_suffix(selftype, profile)
marshname = "Marsh_%s" % name_suffix
name = "Cb_%s" % name_suffix
callback = Subprogram(
name="",
plist=[Parameter(name="Self", type=selftype)] + profile.params,
code="null",
allow_none=False,
returns=profile.returns)
if gtkmethod.bind() and name not in self.__marshallers:
self.__marshallers.add(name)
handler_type = "type %s is %s;" % (
name, callback.profile(
pkg=self.pkg, maxlen=69, indent=" "))
section.add(Code(handler_type), in_spec=True)
section.add(Code(
"""function Cb_To_Address is new Ada.Unchecked_Conversion
(%s, System.Address);
function Address_To_Cb is new Ada.Unchecked_Conversion
(System.Address, %s);
""" % (name, name)),
in_spec=False)
if isinstance(selftype, Interface):
obj_in_body = "Glib.Types.GType_Interface (Object)"
else:
obj_in_body = "Object"
connect_body = """
Unchecked_Do_Signal_Connect
(Object => %s,
C_Name => C_Name,
Marshaller => %s'Access,
Handler => Cb_To_Address (Handler), -- Set in the closure
After => After)""" % (obj_in_body, marshname)
marsh_local, marsh_body = self._marshall_gvalue(profile)
connect = Subprogram(
name="Connect",
plist=[Parameter("Object", selftype),
Parameter("C_Name", "Glib.Signal_Name"),
Parameter("Handler", name),
Parameter("After", "Boolean"),
],
code=connect_body)
section.add(connect, in_spec=False)
if isinstance(selftype, Interface):
addr_to_obj = \
"%s (Unchecked_To_Interface (Params, 0))" % selftype.ada
else:
addr_to_obj = \
"%s (Unchecked_To_Object (Params, 0))" % selftype.ada
marsh = Subprogram(
name=marshname,
plist=[Parameter("Closure", "GClosure"),
Parameter("Return_Value", "Glib.Values.GValue"),
Parameter("N_Params", "Glib.Guint"),
Parameter("Params", "Glib.Values.C_GValues"),
Parameter("Invocation_Hint", "System.Address"),
Parameter("User_Data", "System.Address")],
local_vars=[
Local_Var("H", "constant %s" % name,
"Address_To_Cb (Get_Callback (Closure))"),
Local_Var("Obj", selftype.ada, constant=True,
default=addr_to_obj)
] + marsh_local,
convention="C",
code=marsh_body)
section.add(marsh, in_spec=False)
return name, callback, profile
def _signals(self):
signals = list(self.node.findall(gsignal))
if signals:
adasignals = []
section = self.pkg.section("Signals")
section.sort_objects = False # preserve insertion order
# If at least one signal gets a On_* procedure
for s in signals:
if self.gtkpkg.get_method(
cname="::%s" % s.get("name")).bind():
section.add(
Code("use type System.Address;"), in_spec=False)
self.pkg.add_with("Gtkada.Bindings", specs=False)
self.pkg.add_with("Glib.Values", specs=False)
self.pkg.add_with("Gtk.Arguments", specs=False)
if SHARED_SLOT_MARSHALLERS:
self.pkg.add_with("Gtkada.Marshallers")
self.pkg.add_with(
"Ada.Unchecked_Conversion", specs=False, do_use=False)
break
signals.sort(key=lambda x: x.get("name"))
for s in signals:
gtkmethod = self.gtkpkg.get_method(
cname="::%s" % (s.get("name")))
bind = gtkmethod.bind()
name = s.get("name")
if self.is_gobject:
selftype = GObject("%(typename)s" % self._subst,
allow_none=True, classwide=True)
on_selftype = GObject("%(typename)s" % self._subst,
allow_none=False, classwide=False)
elif self.is_interface:
on_selftype = selftype = Interface(
"%(typename)s" % self._subst)
else:
on_selftype = selftype = Proxy(
"%(typename)s" % self._subst)
# We need to parse the profile twice, so that the with
# statements are set both in self.pkg and in
# gtkada.marshallers
handler_type_name, sub, profile = \
self._generate_marshaller(
section, selftype, node=s, gtkmethod=gtkmethod)
if bind:
slot_handler_type_name, obj_sub = \
self._generate_slot_marshaller(
section, selftype, node=s, gtkmethod=gtkmethod)
section.add(
Code(' Signal_%s : constant Glib.Signal_Name := "%s";' %
(naming.case(name, protect=False), name)),
add_newline=False)
if bind:
connect = Subprogram(
name="On_%s" % naming.case(name, protect=False),
plist=[Parameter(name="Self", type=on_selftype),
Parameter(
name="Call",
type=Proxy(handler_type_name)),
Parameter(
name="After",
type="Boolean",
default="False")],
code='Connect (Self, "%s" & ASCII.NUL, Call, After);' %
name)
section.add(connect, add_newline=False)
self.pkg.add_with("Glib.Object")
obj_connect = Subprogram(
name="On_%s" % naming.case(name, protect=False),
plist=[Parameter(name="Self", type=on_selftype),
Parameter(
name="Call",
type=Proxy(slot_handler_type_name)),
Parameter(
name="Slot",
type=GObject("Glib.Object.GObject",
allow_none=False,
classwide=True)),
Parameter(
name="After",
type="Boolean",
default="False")],
code=('Connect_Slot (Self, "%s" & ASCII.NUL,'
+ ' Call, After, Slot);') % name)
section.add(obj_connect)
doc = _get_clean_doc(s)
if doc:
section.add(Code(" %s""" % doc, iscomment=True))
if not bind:
sub.name = "Handler"
section.add(
Code(
sub.profile(pkg=self.pkg, maxlen=69),
fill=False, iscomment=True))
if profile.returns_doc or len(profile.params) > 1:
doc = "\n Callback parameters:" + sub.formatted_doc()
if profile.returns_doc:
doc += "\n -- %s" % profile.returns_doc
section.add(Code(doc, fill=False, iscomment=True))
def _implements(self):
"""Bind the interfaces that a class implements"""
implements = list(self.node.findall(nimplements)) or []
for impl in implements:
name = impl.get("name")
# Special case, because the GIR file are inconsistent
if name == "Gio.Icon":
name = "Icon"
# If the interface is purposefully not bound
if "--%s" % name in interfaces:
continue
try:
intf = self.gir.interfaces[name]
except KeyError:
intf = self.gir.interfaces[naming.girname_to_ctype[name]]
type = naming.type(intf.ctype)
if "." in type.ada:
self.pkg.add_with(type.ada[:type.ada.rfind(".")])
self.pkg.add_with("Glib.Types")
impl = dict(
name=name,
adatype=base_name(type.ada),
impl=type.ada,
interface=intf,
body="",
pkg="%(typename)s" % self._subst)
impl["code"] = \
"""package Implements_%(adatype)s is new Glib.Types.Implements
(%(impl)s, %(pkg)s_Record, %(pkg)s);
function "+"
(Widget : access %(pkg)s_Record'Class)
return %(impl)s
renames Implements_%(adatype)s.To_Interface;
function "-"
(Interf : %(impl)s)
return %(pkg)s
renames Implements_%(adatype)s.To_Object;
""" % impl
self.implements[name] = impl
# For an interface, we also define "+" to cast to itself. This is used
# when writting custom bodies for the methods of the interface, so that
# those bodies can be reused for the types that implement the
# interface. See GtkTreeModel.
if self.is_interface:
self.implements[""] = {
"name": self._subst["typename"],
"interface": None,
"code": """function "+" (W : %(typename)s) return %(typename)s;
pragma Inline ("+"); """ % self._subst,
"body": """function "+" (W : %(typename)s) return %(typename)s is
begin
return W;
end "+";""" % self._subst,
}
if self.implements:
# Duplicate the subprograms from the interfaces. This doesn't
# quite work: for instance, Gtk.About_Dialog already has a
# Get_Name, so we can't redefine the one inherited from Buildable.
section = self.pkg.section(
"Inherited subprograms (from interfaces)")
for impl in sorted(self.implements.keys()):
impl = self.implements[impl]
if impl["name"] == "Buildable":
# Do not repeat for buildable, that's rarely used
section.add_comment(
"Methods inherited from the Buildable interface are"
+ " not duplicated here since they are meant to be"
+ " used by tools, mostly. If you need to call them,"
+ ' use an explicit cast through the "-" operator'
+ " below.")
continue
interf = impl["interface"]
# Ignore interfaces that we haven't bound
if interf is not None and not hasattr(interf, "gtkpkg"):
print("%s: methods for interface %s were not bound" % (
self.name, impl["name"]))
elif interf is not None:
all = interf.node.findall(nmethod)
for c in all:
cname = c.get(cidentifier)
gtkmethod = interf.gtkpkg.get_method(cname)
interfmethod = self.gtkpkg.get_method(cname)
if interfmethod.bind():
self._handle_function(
section, c, showdoc=False, gtkmethod=gtkmethod,
ismethod=True, isinherited=True)
section = self.pkg.section("Interfaces")
section.add_comment(
"This class implements several interfaces. See Glib.Types")
for impl in sorted(self.implements.keys()):
impl = self.implements[impl]
section.add_comment("")
section.add_comment('- "%(name)s"' % impl)
section.add(impl["code"])
if impl["body"]:
section.add(impl["body"], in_spec=False)
def add_list_binding(self, section, adaname, ctype, singleList):
"""Generate a list instance"""
conv = "%s->Address" % ctype.ada
decl = ""
body = ""
if conv not in self.conversions:
self.conversions[conv] = True
base = "function Convert (R : %s) return System.Address" % (
ctype.ada)
decl += base + ';\n'
body += base + " is\nbegin\n"
if isinstance(ctype, Proxy):
body += "return Glib.To_Address (Glib.C_Proxy (R));"
else:
body += "return Get_Object (R);"
body += "\nend Convert;\n\n"
conv = "Address->%s" % ctype.ada
if conv not in self.conversions:
self.conversions[conv] = True
decl += "function Convert (R : System.Address) return %s;\n" % (
ctype.ada)
body += "function Convert (R : System.Address) return %s is\n" % (
ctype.ada)
if isinstance(ctype, Proxy):
body += "begin\nreturn %s" % ctype.ada \
+ "(Glib.C_Proxy'(Glib.To_Proxy (R)));"
elif isinstance(ctype, Tagged):
# Not a GObject ?
body += "begin\nreturn From_Object(R);"
else:
body += "Stub : %s_Record;" % ctype.ada
body += "begin\n"
body += "return %s (Glib.Object.Get_User_Data (R, Stub));" % (
ctype.ada)
body += "\nend Convert;"
if singleList:
pkg = "GSlist"
generic = "Generic_SList"
else:
pkg = "Glist"
generic = "Generic_List"
self.pkg.add_with("Glib.%s" % pkg)
decl += "package %s is new %s (%s);\n" % (adaname, generic, ctype.ada)
section.add(decl)
section.add(body, in_spec=False)
def record_binding(
self, section, ctype, adaname, type, override_fields,
unions, private):
"""Create the binding for a <record> or <union> type.
override_fields has the same format as returned by
GtkAdaPackage.records()
"""
base = adaname or base_name(type.ada)
try:
node = gir.records[ctype]
except KeyError:
# The package doesn't contain a type (for instance GtkMain)
return
gir.bound.add(ctype)
is_union = node.tag == nunion
first_field_ctype = None
fields = [] # array of (name,type,default) tuples
# Check if we have forced the mapping as a C proxy ?
if (naming.type_exceptions.get(ctype, None) is None
or not isinstance(naming.type_exceptions[ctype], Proxy)):
for field in node.findall(nfield):
name = field.get("name")
ftype = None
type = _get_type(field)
cb = field.findall(ncallback)
if type:
ftype = override_fields.get(name, None)
if ftype is None:
if not first_field_ctype:
t = field.findall(ntype)
assert t, "No type info for %s.%s" % (ctype, name)
ctype = t[0].get(ctype_qname)
if not ctype:
# <type name="..."> has no c:type attribute,
# so we try to map the name to a Girname
ctype = naming.girname_to_ctype[
t[0].get("name")]
first_field_ctype = ctype
ftype = type
elif cb:
# ??? JL: Should properly bind the callback here.
# For now, we just use a System.Address to maintain the
# record integrity
ftype = override_fields.get(name, None)
if ftype is None:
ftype = AdaType(
"System.Address", pkg=self.pkg)
else:
print("WARNING: Field '%s.%s' has no type" % (name, base))
print(" generated record is most certainly incorrect")
if ftype is not None:
if ftype.ada in ("GSList", "GList") and private:
ftype = "System.Address"
default_value = "System.Null_Address"
else:
default_value = ftype.default_record_field_value
ftype = ftype.record_field_type(pkg=self.pkg)
self.pkg.add_with(package_name(ftype))
fields.append((naming.case(name), ftype, default_value))
if not fields:
section.add(
("\ntype %(typename)s is new Glib.C_Proxy;\n"
+ "function From_Object_Free (B : access %(typename)s) "
+ "return %(typename)s;\npragma Inline (From_Object_Free);")
% {"typename": base})
section.add("""
function From_Object_Free (B : access %(typename)s) return %(typename)s is
Result : constant %(typename)s := B.all;
begin
Glib.g_free (B.all'Address);
return Result;
end From_Object_Free;""" % {"typename": base}, in_spec=False)
else:
if private:
section.add(
("\ntype %(typename)s is private;\n"
+ "function From_Object_Free (B : access %(typename)s)"
+ " return %(typename)s;\n"
+ "pragma Inline (From_Object_Free);")
% {"typename": base})
adder = self.pkg.add_private
else:
adder = section.add
if is_union:
enums = self.get_enumeration_values(first_field_ctype)
enums_dict = {ctype: adatype for ctype, adatype in enums}
text = "\ntype %s (%s : %s := %s) is record\n" % (
base, fields[0][0], fields[0][1],
enums[0][1]) + \
" case %s is\n" % fields[0][0]
for index, f in enumerate(fields):
if index != 0:
when_stmt = []
if unions:
for v, key in unions:
# applies to field
if key.lower() == f[0].lower():
when_stmt.append(enums_dict[v])
else:
when_stmt = [enums[index][1]]
if not when_stmt:
print("ERROR: no discrimant value for field %s"
% f[0])
text += "\n when %s =>\n %s : %s;\n" % (
"\n | ".join(when_stmt), f[0], f[1])
text += " end case;\nend record;\n"
text += "pragma Convention (C, %s);\n" % base
text += "pragma Unchecked_Union(%s);\n" % base
adder("\n" + Code(text).format())
else:
c = []
for f in fields:
if f[2]:
c.append('%s : %s := %s;' % (f[0], f[1], f[2]))
else:
c.append('%s : %s;' % (f[0], f[1]))
c = Code('\ntype %s is record\n' % base
+ '\n'.join(c)
+ '\nend record;\npragma Convention (C, %s);\n' % base)
adder(c.format())
if not private:
section.add(
("\nfunction From_Object_Free (B : access %(type)s)"
+ " return %(type)s;\n"
+ "pragma Inline (From_Object_Free);") % {"type": base})
section.add("""
function From_Object_Free (B : access %(typename)s) return %(typename)s is
Result : constant %(typename)s := B.all;
begin
Glib.g_free (B.all'Address);
return Result;
end From_Object_Free;""" % {"typename": base}, in_spec=False)
section.add(Code(_get_clean_doc(node), iscomment=True))
def get_enumeration_values(sef, enum_ctype):
"""Return the list of enumeration values for the given enum, as a list
of tuples (C identifier, ada identifier)"""
node = gir.enums[enum_ctype]
return [(m.get(cidentifier), naming.adamethod_name(m.get(cidentifier)))
for m in node.findall(nmember)]
def constants_binding(self, section, regexp, prefix):
constants = []
r = re.compile(regexp)
for name, node in gir.constants.items():
if r.match(name):
name = name.replace(prefix, "").title()
gir.bound.add(node.get(ctype_qname))
type = node.findall(ntype)
ctype = type[0].get(ctype_qname)
ftype = naming.type(name="", cname=ctype)
deprecated = node.get("deprecated")
deprecated_version = node.get("deprecated-version")
constant_str = '%s : constant %s := "%s";' % (
name,
ftype.ada,
node.get("value"),
)
if deprecated:
if deprecated_version:
constant_str += (
'\npragma Obsolescent (%s, Message => "Deprecated since %s");\n'
% (name, node.get("deprecated-version"))
)
else:
constant_str += '\npragma Obsolescent (%s);\n' % (name)
constants.append(constant_str)
constants.sort()
section.add("\n".join(constants))
def enumeration_binding(self, section, ctype, type, prefix,
asbitfield=False, ignore=""):
"""Add to the section the Ada type definition for the <enumeration>
ctype. type is the corresponding instance of CType().
This function also handles <bitfield> types.
:param prefix: is removed from the values to get the default Ada name,
which can be overridden in data.cname_to_adaname.
:param ignore: space-separated list of values that should not be
bound.
"""
base = base_name(type.ada)
node = gir.enums[ctype]
current = 0
is_default_representation = True
gir.bound.add(ctype)
members = []
ignore = set(ignore.split())
for member in node.findall(nmember):
cname = member.get(cidentifier)
if cname in ignore:
continue
m = naming.adamethod_name(cname, warning_if_not_found=False)
if m is None:
continue
if cname == m:
# No special case ? Attempt a simple rule (remove leading
# Gtk prefix, and capitalize the value)
m = cname.replace(prefix, "").title()
else:
m = base_name(m)
# For proper substitution in docs
naming.add_cmethod(
cname=cname,
adaname="%s.%s" % (self.name, m))
value = int(member.get("value"))
if value != current:
is_default_representation = False
current += 1
members.append((m, value))
decl = ""
if node.tag == nenumeration and not asbitfield:
section.add(
"type %s is " % base
+ "(\n" + ",\n".join(m[0]
for m in sorted(members, key=lambda m: m[1]))
+ ");\n"
+ "pragma Convention (C, %s);\n" % base)
section.add(Code(_get_clean_doc(node), iscomment=True))
if not is_default_representation:
repr = (" for %s use (\n" % base
+ ",\n".join(" %s => %s" % m
for m in sorted(members, key=lambda m: m[1]))
+ ");\n")
section.add(repr)
elif node.tag == nbitfield or asbitfield:
section.add(
"\ntype %s is " % base
+ "mod 2 ** Integer'Size;\n"
+ "pragma Convention (C, %s);\n" % base)
section.add(Code(_get_clean_doc(node), iscomment=True))
for m, value in members:
decl += "%s : constant %s := %s;\n" % (m, base, value)
section.add(decl)
section.pkg.section("Enumeration Properties").add(
"package %s_Properties is\n" % base
+ " new Generic_Internal_Discrete_Property (%s);\n" % base
+ "type Property_%s is new %s_Properties.Property;\n\n" % (
base, base))
self.pkg.add_with("Glib.Generic_Properties")
def generate(self, gir):
if self._generated:
return
extra = self.gtkpkg.extra()
if extra:
self.node.extend(extra)
# The parent is unfortunately specified as a GIR name. But that creates
# ambiguities when loading both Gtk and Gdk, which for instance both
# define "Window". So we first look in the same file as the current
# Class.
parent = gir._get_class_node(
self.rootNode,
girname=self.gtkpkg.parent_type() or self.node.get("parent"))
parent = naming.type(
name=self.gtkpkg.parent_type() or self.node.get(
"parent"), # GIRName
cname=parent and parent.get(ctype_qname)).ada
if parent and parent.rfind(".") != -1:
self._subst["parent_pkg"] = package_name(parent)
self._subst["parent"] = base_name(parent)
else:
self._subst["parent_pkg"] = None
self._subst["parent"] = parent
self._generated = True
girdoc = _get_clean_doc(self.node)
into = self.gtkpkg.into()
if into:
# Make sure we have already generated the other package, so that
# its types go first.
klass = gir.classes.get(into, None) or gir.ctype_interfaces[into]
klass.generate(gir)
into = klass.name # from now on, we want the Ada name
# Do not integrate the documentation from the other package, it
# is likely useless anyway
girdoc = ""
# Override the type exception. For instance, from now on we
# want to use "Gtk.Box.Gtk_HBox" rather than "Gtk.HBox.Gtk_HBox"
# which doesn't exist
typename = "%s.%s" % (into, self._subst["typename"])
# retrieve the old type
oldtype = naming.type(cname=self.ctype)
newtype = None
# and create the new one accordingly
if isinstance(oldtype, Tagged):
newtype = Tagged(typename)
elif isinstance(oldtype, GObject):
newtype = GObject(typename)
elif isinstance(oldtype, Proxy):
newtype = Proxy(typename)
elif isinstance(oldtype, Record):
newtype = Record(typename)
else:
raise Exception("Can't override %s for a package with 'into'"
% oldtype)
naming.add_type_exception(
override=True,
cname=self.ctype,
type=newtype)
self.pkg = gir.get_package(into or self.ada_package_name,
ctype=self.ctype,
doc=girdoc)
if self._subst["parent_pkg"]:
self.pkg.add_with("%(parent_pkg)s" % self._subst)
self.gtktype = self.gtkpkg.get_type(self._subst["typename"])
section = self.pkg.section("")
if self.gtkpkg.is_obsolete():
section.add("pragma Obsolescent;")
if not self.has_toplevel_type:
pass
elif self.is_interface:
self.pkg.add_with("Glib.Types")
section.add(
"""type %(typename)s is new Glib.Types.GType_Interface;
Null_%(typename)s : constant %(typename)s;""" % self._subst)
self.pkg.add_private("""
Null_%(typename)s : constant %(typename)s :=
%(typename)s (Glib.Types.Null_Interface);""" %
self._subst)
elif self.gtktype.is_subtype():
section.add(
"""
subtype %(typename)s_Record is %(parent)s_Record;
subtype %(typename)s is %(parent)s;""" % self._subst)
elif self.is_proxy:
section.add("""
type %(typename)s is new Glib.C_Proxy;""" % self._subst)
elif self.is_boxed:
# The type is not private so that we can directly instantiate
# generic packages for lists of this type.
section.add("""
type %(typename)s is new Glib.C_Boxed with null record;
Null_%(typename)s : constant %(typename)s;
function From_Object (Object : System.Address) return %(typename)s;
function From_Object_Free (B : access %(typename)s'Class) return %(typename)s;
pragma Inline (From_Object_Free, From_Object);
""" % self._subst)
# Insert constant declaration at the end of the package, to avoid
# freezing issues
self.pkg.add_private(
("\n Null_%(typename)s : constant %(typename)s :="
+ " (Glib.C_Boxed with null record);\n") %
self._subst, at_end=True)
section.add("""
function From_Object_Free
(B : access %(typename)s'Class) return %(typename)s
is
Result : constant %(typename)s := %(typename)s (B.all);
begin
Glib.g_free (B.all'Address);
return Result;
end From_Object_Free;
function From_Object (Object : System.Address) return %(typename)s is
S : %(typename)s;
begin
S.Set_Object (Object);
return S;
end From_Object;
""" % self._subst, in_spec=False)
elif self._subst["parent"] is None:
# Likely a public record type (ie with visible fields).
# Automatically add it to the list of records to bind.
self.gtkpkg.add_record_type(self.ctype)
else:
section.add("""
type %(typename)s_Record is new %(parent)s_Record with null record;
type %(typename)s is access all %(typename)s_Record'Class;"""
% self._subst)
for ctype, enum, prefix, asbitfield, ignore in \
self.gtkpkg.enumerations():
self.enumeration_binding(
section, ctype, enum, prefix, asbitfield=asbitfield,
ignore=ignore)
for regexp, prefix in self.gtkpkg.constants():
self.constants_binding(section, regexp, prefix)
for ctype, enum, adaname, fields, unions, private in \
self.gtkpkg.records():
self.record_binding(
section, ctype, adaname, enum, fields, unions, private)
for ada, ctype, single, sect_name in self.gtkpkg.lists():
sect = self.pkg.section(sect_name)
self.add_list_binding(sect, ada, ctype, single)
if extra:
for p in extra:
if p.tag == "with_spec":
self.pkg.add_with(
p.get("pkg", "Missing package name in <extra>"),
do_use=p.get("use", "true").lower() == "true")
elif p.tag == "with_body":
self.pkg.add_with(
p.get("pkg"), specs=False,
do_use=p.get("use", "true").lower() == "true")
elif p.tag == "type":
naming.add_type_exception(
cname=p.get("ctype"),
type=Proxy(p.get("ada"), p.get("properties", None)))
section.add(p.text)
elif p.tag == "body":
if p.get("before", "true").lower() == "true":
# Go before the body of generated subprograms, so that
# we can add type definition
section.add(p.text, in_spec=False)
self._constructors()
self._method_get_type()
self._methods()
if extra:
s = None
for p in extra:
if p.tag == "spec":
if p.get("private", "False").lower() == "true":
self.pkg.add_private(p.text, at_end=True)
else:
s = s or self.pkg.section("GtkAda additions")
s.add(p.text)
elif p.tag == "body" \
and p.get("before", "true").lower() != "true":
s.add(p.text, in_spec=False)
self._functions()
self._globals()
self._fields()
self._virtual_methods()
if not into and self.name != "Gtk.Widget":
self._implements()
self._properties()
self._signals()
# Set up and invoke our command line parser
parser = OptionParser()
parser.add_option(
"--gir-file",
help="input GTK .gir file",
action="append",
dest="gir_file",
metavar="FILE")
parser.add_option(
"--xml-file",
help="input GtkAda binding.xml file",
dest="xml_file",
metavar="FILE")
parser.add_option(
"--ada-output",
help="Ada language output file",
dest="ada_outfile",
metavar="FILE")
parser.add_option(
"--c-output",
help="C language output file",
dest="c_outfile",
metavar="FILE")
(options, args) = parser.parse_args()
# Command line argument sanity checking: make sure that all of our
# inputs and outputs are specified.
missing_files = []
if options.gir_file is None:
missing_files.append("GIR file")
if options.xml_file is None:
missing_files.append("binding.xml file")
if options.ada_outfile is None:
missing_files.append("Ada output file")
if options.c_outfile is None:
missing_files.append("C output file")
if missing_files:
parser.error('Must specify files:\n\t' + ', '.join(missing_files))
gtkada = GtkAda(options.xml_file)
gir = GIR(options.gir_file)
Package.copyright_header = \
"""------------------------------------------------------------------------------
-- --
-- Copyright (C) 1998-2000 E. Briot, J. Brobecker and A. Charlet --
-- Copyright (C) 2000-2022, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
"""
gir.ccode = \
"""/*****************************************************************************
* GtkAda - Ada95 binding for Gtk+/Gnome *
* *
* Copyright (C) 1998-2000 E. Briot, J. Brobecker and A. Charlet *
* Copyright (C) 2000-2022, AdaCore *
* *
* This library is free software; you can redistribute it and/or modify it *
* under terms of the GNU General Public License as published by the Free *
* Software Foundation; either version 3, or (at your option) any later *
* version. This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
* TABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* As a special exception under Section 7 of GPL version 3, you are granted *
* additional permissions described in the GCC Runtime Library Exception, *
* version 3.1, as published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License and *
* a copy of the GCC Runtime Library Exception along with this program; *
* see the files COPYING3 and COPYING.RUNTIME respectively. If not, see *
* <http://www.gnu.org/licenses/>. *
*
*****************************************************************************
This file is automatically generated from the .gir files
*/
#include <gtk/gtk.h>
"""
for the_ctype in enums:
node = Element(
nclass,
{ctype_qname: the_ctype})
root = Element(nclass)
cl = gir._create_class(rootNode=root, node=node,
identifier_prefix='',
is_interface=False,
is_gobject=False,
has_toplevel_type=False)
cl.generate(gir)
for name in interfaces:
if name.startswith("--"):
gir.bound.add(name[2:])
continue
gir.interfaces[name].generate(gir)
gir.bound.add(name)
for the_ctype in binding:
if the_ctype.startswith("--"):
gir.bound.add(the_ctype[2:])
continue
try:
e = gir.classes[the_ctype]
except KeyError:
cl = gtkada.get_pkg(the_ctype)
if not cl:
raise
node = Element(nclass, {ctype_qname: the_ctype})
e = gir.classes[the_ctype] = gir._create_class(
rootNode=root, node=node, is_interface=False,
identifier_prefix='')
e.generate(gir)
gir.bound.add(the_ctype)
with open(options.ada_outfile, "wb") as out:
with open(options.c_outfile, "wb") as cout:
gir.generate(out, cout)
gir.show_unbound()