summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJussi Kukkonen <jku@openedhand.com>2008-10-17 10:19:22 +0000
committerJussi Kukkonen <jku@openedhand.com>2008-10-17 10:19:22 +0000
commit91f8ef474447239730cb4b9829baddb780a8082c (patch)
tree9f0c347e2331e6f73b3d99a254ffb2e33065bda1 /tools
parent3e528acd79d55fd9fe778a3ecdde50263da3ddce (diff)
downloadgupnp-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-xtools/gupnp-binding-tool325
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()