diff options
author | Jussi Kukkonen <jku@openedhand.com> | 2008-10-17 10:19:22 +0000 |
---|---|---|
committer | Jussi Kukkonen <jku@openedhand.com> | 2008-10-17 10:19:22 +0000 |
commit | 91f8ef474447239730cb4b9829baddb780a8082c (patch) | |
tree | 9f0c347e2331e6f73b3d99a254ffb2e33065bda1 /tools | |
parent | 3e528acd79d55fd9fe778a3ecdde50263da3ddce (diff) | |
download | gupnp-91f8ef474447239730cb4b9829baddb780a8082c.tar.gz |
2008-10-17 Jussi Kukkonen <jku@linux.intel.com>
* tools/gupnp-binding-tool:
gupnp-binding-tool improvements
- add support for all upnp types
- add "--prefix" option
- prettify binding tool output
- make var and function names lower_case, not CamelCase
- generate state variable notification bindings
This change makes gupnp-binding-tool output incompatible with
the old one.
git-svn-id: https://svn.o-hand.com/repos/gupnp/trunk/gupnp@1307 d8cb91d7-bff9-0310-92b9-80b65e4482b2
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/gupnp-binding-tool | 325 |
1 files changed, 248 insertions, 77 deletions
diff --git a/tools/gupnp-binding-tool b/tools/gupnp-binding-tool index 5df8575..393169a 100755 --- a/tools/gupnp-binding-tool +++ b/tools/gupnp-binding-tool @@ -1,6 +1,7 @@ #! /usr/bin/python # Copyright (C) 2008 OpenedHand Ltd +# Copyright (C) 2008 Intel Corporation # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software @@ -15,59 +16,83 @@ # this program; if not, write to the Free Software Foundation, Inc., 51 Franklin # St, Fifth Floor, Boston, MA 02110-1301 USA - # TODO: -# - add the rest of the types # - finish code cleanup -# - add optional service namespacing to functions + +import sys, os.path, re, xml.etree.ElementTree as ET +from optparse import OptionParser + + +# upnp type: (C type, GType, get function) +typemap = { + 'ui1': ('guint ', 'G_TYPE_UINT', 'g_value_get_uint'), + 'ui2': ('guint ', 'G_TYPE_UINT', 'g_value_get_uint'), + 'ui4': ('guint ', 'G_TYPE_UINT', 'g_value_get_uint'), + 'i1': ('gint' , 'G_TYPE_INT', 'g_value_get_int'), + 'i2': ('gint ', 'G_TYPE_INT', 'g_value_get_int'), + 'i4': ('gint ', 'G_TYPE_INT', 'g_value_get_int'), + 'int': ('gint ', 'G_TYPE_INT', 'g_value_get_int'), + 'r4': ('gfloat ', 'G_TYPE_FLOAT', 'g_value_get_float'), + 'r8': ('gdouble ', 'G_TYPE_DOUBLE', 'g_value_get_double'), + 'number': ('gdouble ', 'G_TYPE_DOUBLE', 'g_value_get_double'), + 'fixed.14.4': ('gdouble ', 'G_TYPE_DOUBLE', 'g_value_get_double'), + 'float': ('gdouble ', 'G_TYPE_DOUBLE', 'g_value_get_double'), + 'char': ('gchar ', 'G_TYPE_CHAR', 'g_value_get_char'), + 'string': ('gchar *', 'G_TYPE_STRING', 'g_value_get_string'), + 'date': ('gchar *', 'GUPNP_TYPE_DATE', 'g_value_get_string'), + 'dateTime': ('gchar *', 'GUPNP_TYPE_DATE_TIME', 'g_value_get_string'), + 'dateTime.tz': ('gchar *', 'GUPNP_TYPE_DATE_TIME_TZ', 'g_value_get_string'), + 'time': ('gchar *', 'GUPNP_TYPE_TIME', 'g_value_get_string'), + 'time.tz': ('gchar *', 'GUPNP_TYPE_TIME_TZ', 'g_value_get_string'), + 'boolean': ('gboolean ', 'G_TYPE_BOOLEAN', 'g_value_get_boolean'), + 'bin.base64': ('gchar *', 'GUPNP_TYPE_BIN_BASE64', 'g_value_get_string'), + 'bin.hex': ('gchar *', 'GUPNP_TYPE_BIN_HEX', 'g_value_get_string'), + 'uri': ('gchar *', 'GUPNP_TYPE_URI', 'g_value_get_string'), + 'uuid': ('gchar *', 'GUPNP_TYPE_UUID', 'g_value_get_string') +} class Action: def __init__(self): self.name = None + self.c_name = None self.in_args = [] self.out_args = [] + self.notify_vars = [] class Argument: def __init__(self): self.name = None + self.c_name = None self.direction = None - self.gtype = None + self.related_var = None + + +class Variable: + def __init__(self): + self.name = None + self.c_name = None + self.c_prefixed_name = None self.ctype = None + self.gtype = None + self.get_function = None -def findStateArgType(scpd, name): - for argElement in scpd.findall("{urn:schemas-upnp-org:service-1-0}serviceStateTable/{urn:schemas-upnp-org:service-1-0}stateVariable"): - arg_name = argElement.find("{urn:schemas-upnp-org:service-1-0}name").text - if arg_name == name: - return argElement.find("{urn:schemas-upnp-org:service-1-0}dataType").text - return None - - -def mapTypes(scpd, argElement): - dataType = findStateArgType(scpd, argElement.find("{urn:schemas-upnp-org:service-1-0}relatedStateVariable").text) - if dataType == "string": - return ("char *", "G_TYPE_STRING") - elif dataType == "ui2": - # Stupid GType doesn't have short types, so use an int anyway. - return ("unsigned int", "G_TYPE_UINT") - elif dataType == "ui4": - return ("unsigned int", "G_TYPE_UINT") - elif dataType == "boolean": - return ("gboolean", "G_TYPE_BOOLEAN") - # TODO: lots of types missing - else: - raise Exception("Unknown dataType %s" % dataType) +def camelCaseToLowerCase (str): + # http://www.djangosnippets.org/snippets/585/ + tmp = re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', '_\\1', str) + lower_case = tmp.lower().strip('_') + return re.sub('[^a-z0-9]', '_', lower_case) -def getActions(scpd): +def getActions(scpd, prefix, variables): """ Parse the SCPD provided into a list of Action objects. """ actions = [] - + for actionElement in scpd.findall("{urn:schemas-upnp-org:service-1-0}actionList/{urn:schemas-upnp-org:service-1-0}action"): a = Action() actions.append(a) @@ -75,125 +100,271 @@ def getActions(scpd): if a.name is None: raise Exception("No name found for action") - + a.c_name = prefix + camelCaseToLowerCase (a.name) + for argElement in actionElement.findall("{urn:schemas-upnp-org:service-1-0}argumentList/{urn:schemas-upnp-org:service-1-0}argument"): - arg = Argument() - - arg.name = argElement.find("{urn:schemas-upnp-org:service-1-0}name").text - if arg.name is None: - raise Exception("No name found for argument") - (arg.ctype, arg.gtype) = mapTypes(scpd, argElement) - arg.direction = argElement.find("{urn:schemas-upnp-org:service-1-0}direction").text - - if arg.direction == "in": - a.in_args.append(arg) - else: - a.out_args.append(arg) + arg = Argument() + + arg.name = argElement.find("{urn:schemas-upnp-org:service-1-0}name").text + if arg.name is None: + raise Exception("No name found for argument") + arg.c_name = camelCaseToLowerCase (arg.name) + + var_name = argElement.find("{urn:schemas-upnp-org:service-1-0}relatedStateVariable").text + for var in variables: + if var.name == var_name: + arg.related_var = var + break + if arg.related_var == None: + raise Exception("%s: related state variable %s not found" % (arg.name, var_name)) + + arg.direction = argElement.find("{urn:schemas-upnp-org:service-1-0}direction").text + + if arg.direction == "in": + a.in_args.append(arg) + else: + a.out_args.append(arg) + return actions -def printSyncBinding(a): - indent = (2 + len (a.name)) * " " +def getVariables(scpd, prefix): + """ + Parse the SCPD xml into a list of Variable objects. + """ + + variables = [] - print "static inline gboolean" - print "%s (GUPnPServiceProxy *proxy," % a.name + for varElement in scpd.findall("{urn:schemas-upnp-org:service-1-0}serviceStateTable/{urn:schemas-upnp-org:service-1-0}stateVariable"): + var = Variable() + variables.append(var) + + var.name = varElement.find("{urn:schemas-upnp-org:service-1-0}name").text + var.c_name = camelCaseToLowerCase (var.name) + var.c_prefixed_name = prefix + var.c_name + var.notified = (varElement.attrib["sendEvents"] == "yes") + dataType = varElement.find("{urn:schemas-upnp-org:service-1-0}dataType").text + if not dataType in typemap: + raise Exception("Unknown dataType %s" % data_type) + (var.ctype, var.gtype, var.get_function) = typemap[dataType]; + + return variables + + +def printClientSyncFunctionBinding(a): + indent = (2 + len (a.c_name)) * " " + + print "static inline gboolean" + print "%s (GUPnPServiceProxy *proxy," % a.c_name + for arg in a.in_args: - print "%s%s in_%s," % (indent, arg.ctype, arg.name) + print "%s%sin_%s," % (indent, arg.related_var.ctype, arg.c_name) for arg in a.out_args: - print "%s%s* out_%s," % (indent, arg.ctype, arg.name) + print "%s%s*out_%s," % (indent, arg.related_var.ctype, arg.c_name) print "%sGError **error)" % indent print "{" - print " return gupnp_service_proxy_send_action (proxy, \"%s\", error, " % a.name + print " return gupnp_service_proxy_send_action" + print " (proxy, \"%s\", error, " % a.name for arg in a.in_args: - print " \"%s\", %s, in_%s," % (arg.name, arg.gtype, arg.name) - print " NULL, " + print " \"%s\", %s, in_%s," % (arg.name, arg.related_var.gtype, arg.c_name) + print " NULL, " for arg in a.out_args: - print " \"%s\", %s, out_%s," % (arg.name, arg.gtype, arg.name) - print " NULL" - print " );" + print " \"%s\", %s, out_%s," % (arg.name, arg.related_var.gtype, arg.c_name) + print " NULL);" print "}\n" -def printAsyncBinding(a): +def printClientAsyncFunctionBinding(a): # Magic struct to pass data around. Defined here so that we don't have # multiple copies of the struct definition. asyncdata = " struct {GCallback cb; gpointer userdata; } *cbdata;" # User-callback prototype - indent = (24 + len (a.name)) * " " - print "typedef void (*%s_reply) (GUPnPServiceProxy *proxy," % a.name + indent = (24 + len (a.c_name)) * " " + print "typedef void (*%s_reply) (GUPnPServiceProxy *proxy," % a.c_name for arg in a.out_args: - print "%s%s out_%s," % (indent, arg.ctype, arg.name) + print "%s%sout_%s," % (indent, arg.related_var.ctype, arg.c_name) print "%sGError *error," % indent print "%sgpointer userdata);" % indent print # Generated async callback handler, calls user-provided callback - print "static void _%s_async_callback (GUPnPServiceProxy *proxy, GUPnPServiceProxyAction *action, gpointer user_data)" % a.name + indent = (30 + len (a.c_name)) * " " + print "static void _%s_async_callback (GUPnPServiceProxy *proxy," % a.c_name + print "%sGUPnPServiceProxyAction *action," % indent + print "%sgpointer user_data)" % indent print "{" print asyncdata print " GError *error = NULL;" for arg in a.out_args: - print " %s %s;" % (arg.ctype, arg.name) + print " %s%s;" % (arg.related_var.ctype, arg.c_name) + print print " cbdata = user_data;" print " gupnp_service_proxy_end_action" print " (proxy, action, &error," for arg in a.out_args: - print " \"%s\", %s, &%s," % (arg.name, arg.gtype, arg.name) + print " \"%s\", %s, &%s," % (arg.name, arg.related_var.gtype, arg.c_name) print " NULL);" - print " ((%s_reply)cbdata->cb) (proxy," % a.name + print " ((%s_reply)cbdata->cb)" % a.c_name + print " (proxy," for arg in a.out_args: - print " %s," % arg.name - print " error, cbdata->userdata);" + print " %s," % arg.c_name + print " error, cbdata->userdata);" + print print " g_slice_free1 (sizeof (*cbdata), cbdata);" print "}" print # Entry point - indent = (8 + len (a.name)) * " " + indent = (8 + len (a.c_name)) * " " print "static inline GUPnPServiceProxyAction *" - print "%s_async (GUPnPServiceProxy *proxy," % a.name + print "%s_async (GUPnPServiceProxy *proxy," % a.c_name for arg in a.in_args: - print "%s%s in_%s," % (indent, arg.ctype, arg.name) - print "%s%s_reply callback," % (indent, a.name) + print "%s%sin_%s," % (indent, arg.related_var.ctype, arg.c_name) + print "%s%s_reply callback," % (indent, a.c_name) print "%sgpointer userdata)" % indent print "{" print " GUPnPServiceProxyAction* action;" print asyncdata + print print " cbdata = g_slice_alloc (sizeof (*cbdata));" print " cbdata->cb = G_CALLBACK (callback);" print " cbdata->userdata = userdata;" print " action = gupnp_service_proxy_begin_action" print " (proxy, \"%s\"," % a.name - print " _%s_async_callback, cbdata," % a.name + print " _%s_async_callback, cbdata," % a.c_name for arg in a.in_args: - print " \"%s\", %s, in_%s," % (arg.name, arg.gtype, arg.name) + print " \"%s\", %s, in_%s," % (arg.name, arg.related_var.gtype, arg.c_name) print " NULL);" + print print " return action;" print "}" +def printClientVariableBinding(v): + asyncdata = " struct {GCallback cb; gpointer userdata; } *cbdata;" + ctype = re.sub ("^gchar", "const gchar", v.ctype); + + # callback prototype + indent = (22 + len (v.c_prefixed_name)) * " " + print "typedef void" + print "(*%s_changed_callback) (GUPnPServiceProxy *proxy," % v.c_prefixed_name + print "%s%s%s," % (indent, ctype, v.c_name) + print "%sgpointer userdata);" % indent + print + + # private callback + indent = (20 + len (v.c_prefixed_name)) * " " + print "static void" + print "_%s_changed_callback (GUPnPServiceProxy *proxy," % v.c_prefixed_name + print "%sconst gchar *variable," % indent + print "%sGValue *value," % indent + print "%sgpointer userdata)" % indent + print "{" + print asyncdata + print " %s%s;" % (ctype, v.c_name) + print + print " cbdata = userdata;" + print " %s = %s (value);" % (v.c_name, v.get_function) + print " ((%s_changed_callback)cbdata->cb)" % v.c_prefixed_name + print " (proxy," + print " %s," % v.c_name + print " cbdata->userdata);" + print + print " g_slice_free1 (sizeof (*cbdata), cbdata);" + print "}" + print + + # public notify_add function + indent = (13 + len (v.c_prefixed_name)) * " " + print "static inline gboolean" + print "%s_add_notify (GUPnPServiceProxy *proxy," % v.c_prefixed_name + print "%s%s_changed_callback callback," % (indent, v.c_prefixed_name) + print "%sgpointer userdata)" % indent + print "{" + print asyncdata + print + print " cbdata = g_slice_alloc (sizeof (*cbdata));" + print " cbdata->cb = G_CALLBACK (callback);" + print " cbdata->userdata = userdata;" + print + print " return gupnp_service_proxy_add_notify" + print " (proxy," + print " \"%s\"," % v.name + print " %s," % v.gtype + print " _%s_changed_callback," % v.c_prefixed_name + print " cbdata);" + print "}" + +def parseSCPD(scpd, prefix): + if prefix != "": + prefix = prefix + "_" + + variables = getVariables(scpd, prefix) + actions = getActions(scpd, prefix, variables) -def printBindings(actions): + return (actions, variables) + + +def printClientBindings(service_filename, prefix): + if prefix == "": + base = re.sub("[^a-zA-Z0-9]", "_", os.path.basename(service_filename)) + define = "GUPNP_GENERATED_CLIENT_BINDING_%s" % base.upper() + else: + define = "GUPNP_GENERATED_CLIENT_BINDING_%s" % prefix.upper() + + print "/* Generated by gupnp-binding-tool */" + print + print "#include <glib/gtypes.h>" + print "#include <glib/gerror.h>" print "#include <libgupnp/gupnp-service-proxy.h>" print + print "#ifndef %s" % define + print "#define %s" % define + print print "G_BEGIN_DECLS" - + + (actions, variables) = parseSCPD (ET.parse(service_filename), prefix) + for a in actions: - print "\n/* %s */\n" % a.name - printSyncBinding(a) - printAsyncBinding(a) - + print "\n/* action %s */\n" % a.name + printClientSyncFunctionBinding(a) + printClientAsyncFunctionBinding(a) + for v in variables: + if v.notified: + print "\n/* state variable %s */\n" % v.name + printClientVariableBinding(v) + print print "G_END_DECLS" + print + print "#endif /* %s */" % define + -import sys, xml.etree.ElementTree as ET +def main (): + parser = OptionParser("Usage: gupnp-binding-tool [options] service-filename") + parser.add_option("-p", "--prefix", dest="prefix", + metavar="PREFIX", default="", + help="set prefix for generated function names") + parser.add_option("-m", "--mode", type="choice", dest="mode", + metavar="MODE", default="client", + choices=("client", "server"), + help="select generation mode, 'client' or 'server'") + + (options, args) = parser.parse_args() + if len(args) != 1: + parser.error("Expected 1 argument, got %d" % len(args)) + if (options.mode == "client"): + printClientBindings(args[0], options.prefix) + else: + print raise Exception ("Not implemented yet") -printBindings(getActions(ET.parse(sys.argv[1]))) +if __name__ == "__main__": + main() |