summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJussi Kukkonen <jku@openedhand.com>2008-10-17 10:19:28 +0000
committerJussi Kukkonen <jku@openedhand.com>2008-10-17 10:19:28 +0000
commit6fa0bd353c6d9a2835b0afd84858598183a79a33 (patch)
treeb4f2ffcbe56408e224f57f041febbe7f5ad35ce5 /tools
parent91f8ef474447239730cb4b9829baddb780a8082c (diff)
downloadgupnp-6fa0bd353c6d9a2835b0afd84858598183a79a33.tar.gz
2007-10-17 Jussi Kukkonen <jku@linux.intel.com>
* tools/gupnp-binding-tool: Add serverside binding generation * doc/client-tutorial.xml * doc/server-tutorial.xml * doc/gupnp-binding-tool.xml Update documentation for new binding generation. git-svn-id: https://svn.o-hand.com/repos/gupnp/trunk/gupnp@1308 d8cb91d7-bff9-0310-92b9-80b65e4482b2
Diffstat (limited to 'tools')
-rwxr-xr-xtools/gupnp-binding-tool321
1 files changed, 249 insertions, 72 deletions
diff --git a/tools/gupnp-binding-tool b/tools/gupnp-binding-tool
index 393169a..0a70ea3 100755
--- a/tools/gupnp-binding-tool
+++ b/tools/gupnp-binding-tool
@@ -23,32 +23,33 @@ import sys, os.path, re, xml.etree.ElementTree as ET
from optparse import OptionParser
-# upnp type: (C type, GType, get function)
+# upnp type: (C type, GType, type for g_value_get_*
+# and g_value_set_*)
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')
+ 'ui1': ('guint ', 'G_TYPE_UINT', 'uint'),
+ 'ui2': ('guint ', 'G_TYPE_UINT', 'uint'),
+ 'ui4': ('guint ', 'G_TYPE_UINT', 'uint'),
+ 'i1': ('gint' , 'G_TYPE_INT', 'int'),
+ 'i2': ('gint ', 'G_TYPE_INT', 'int'),
+ 'i4': ('gint ', 'G_TYPE_INT', 'int'),
+ 'int': ('gint ', 'G_TYPE_INT', 'int'),
+ 'r4': ('gfloat ', 'G_TYPE_FLOAT', 'float'),
+ 'r8': ('gdouble ', 'G_TYPE_DOUBLE', 'double'),
+ 'number': ('gdouble ', 'G_TYPE_DOUBLE', 'double'),
+ 'fixed.14.4': ('gdouble ', 'G_TYPE_DOUBLE', 'double'),
+ 'float': ('gdouble ', 'G_TYPE_DOUBLE', 'double'),
+ 'char': ('gchar ', 'G_TYPE_CHAR', 'char'),
+ 'string': ('gchar *', 'G_TYPE_STRING', 'string'),
+ 'date': ('gchar *', 'GUPNP_TYPE_DATE', 'string'),
+ 'dateTime': ('gchar *', 'GUPNP_TYPE_DATE_TIME', 'string'),
+ 'dateTime.tz': ('gchar *', 'GUPNP_TYPE_DATE_TIME_TZ', 'string'),
+ 'time': ('gchar *', 'GUPNP_TYPE_TIME', 'string'),
+ 'time.tz': ('gchar *', 'GUPNP_TYPE_TIME_TZ', 'string'),
+ 'boolean': ('gboolean ', 'G_TYPE_BOOLEAN', 'boolean'),
+ 'bin.base64': ('gchar *', 'GUPNP_TYPE_BIN_BASE64', 'string'),
+ 'bin.hex': ('gchar *', 'GUPNP_TYPE_BIN_HEX', 'string'),
+ 'uri': ('gchar *', 'GUPNP_TYPE_URI', 'string'),
+ 'uuid': ('gchar *', 'GUPNP_TYPE_UUID', 'string')
}
@@ -56,6 +57,7 @@ class Action:
def __init__(self):
self.name = None
self.c_name = None
+ self.c_prefixed_name = None
self.in_args = []
self.out_args = []
self.notify_vars = []
@@ -77,6 +79,9 @@ class Variable:
self.ctype = None
self.gtype = None
self.get_function = None
+ self.set_function = None
+ self.notified = True
+ self.dummy = False
def camelCaseToLowerCase (str):
@@ -86,21 +91,22 @@ def camelCaseToLowerCase (str):
return re.sub('[^a-z0-9]', '_', lower_case)
-def getActions(scpd, prefix, variables):
+def getActions(action_elements, prefix, variables):
"""
- Parse the SCPD provided into a list of Action objects.
+ Parse the action element list 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"):
+ for actionElement in action_elements:
a = Action()
actions.append(a)
a.name = actionElement.find("{urn:schemas-upnp-org:service-1-0}name").text
-
+
if a.name is None:
raise Exception("No name found for action")
- a.c_name = prefix + camelCaseToLowerCase (a.name)
+ a.c_name = camelCaseToLowerCase (a.name)
+ a.c_prefixed_name = prefix + a.c_name
for argElement in actionElement.findall("{urn:schemas-upnp-org:service-1-0}argumentList/{urn:schemas-upnp-org:service-1-0}argument"):
arg = Argument()
@@ -115,7 +121,7 @@ def getActions(scpd, prefix, variables):
if var.name == var_name:
arg.related_var = var
break
- if arg.related_var == None:
+ if arg.related_var is 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
@@ -124,42 +130,50 @@ def getActions(scpd, prefix, variables):
a.in_args.append(arg)
else:
a.out_args.append(arg)
-
+
return actions
-def getVariables(scpd, prefix):
+def getVariables(var_elements, prefix):
"""
- Parse the SCPD xml into a list of Variable objects.
+ Parse the state variable element list into a list of Variable objects.
"""
variables = []
- for varElement in scpd.findall("{urn:schemas-upnp-org:service-1-0}serviceStateTable/{urn:schemas-upnp-org:service-1-0}stateVariable"):
+ for varElement in var_elements:
var = Variable()
variables.append(var)
var.name = varElement.find("{urn:schemas-upnp-org:service-1-0}name").text
+ if var.name.startswith("A_ARG_TYPE_"):
+ # dummy state variable, only there to give type info to getter/setter
+ var.dummy = True
+
var.c_name = camelCaseToLowerCase (var.name)
var.c_prefixed_name = prefix + var.c_name
- var.notified = (varElement.attrib["sendEvents"] == "yes")
+
+ if (varElement.get("sendEvents") == "no"):
+ var.notified = False
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];
+ (var.ctype, var.gtype, g_value_type) = typemap[dataType];
+ var.get_function = "g_value_get_" + g_value_type
+ var.set_function = "g_value_set_" + g_value_type
return variables
-def printClientSyncFunctionBinding(a):
- indent = (2 + len (a.c_name)) * " "
+def printClientSyncActionBinding(a):
+ indent = (2 + len (a.c_prefixed_name)) * " "
print "static inline gboolean"
- print "%s (GUPnPServiceProxy *proxy," % a.c_name
+ print "%s (GUPnPServiceProxy *proxy," % a.c_prefixed_name
for arg in a.in_args:
- print "%s%sin_%s," % (indent, arg.related_var.ctype, arg.c_name)
+ print "%sconst %sin_%s," % (indent, arg.related_var.ctype, arg.c_name)
for arg in a.out_args:
print "%s%s*out_%s," % (indent, arg.related_var.ctype, arg.c_name)
@@ -182,23 +196,23 @@ def printClientSyncFunctionBinding(a):
print "}\n"
-def printClientAsyncFunctionBinding(a):
+def printClientAsyncActionBinding(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.c_name)) * " "
- print "typedef void (*%s_reply) (GUPnPServiceProxy *proxy," % a.c_name
+ indent = (24 + len (a.c_prefixed_name)) * " "
+ print "typedef void (*%s_reply) (GUPnPServiceProxy *proxy," % a.c_prefixed_name
for arg in a.out_args:
- print "%s%sout_%s," % (indent, arg.related_var.ctype, arg.c_name)
+ print "%sconst %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
- indent = (30 + len (a.c_name)) * " "
- print "static void _%s_async_callback (GUPnPServiceProxy *proxy," % a.c_name
+ indent = (30 + len (a.c_prefixed_name)) * " "
+ print "static void _%s_async_callback (GUPnPServiceProxy *proxy," % a.c_prefixed_name
print "%sGUPnPServiceProxyAction *action," % indent
print "%sgpointer user_data)" % indent
print "{"
@@ -213,7 +227,7 @@ def printClientAsyncFunctionBinding(a):
for arg in a.out_args:
print " \"%s\", %s, &%s," % (arg.name, arg.related_var.gtype, arg.c_name)
print " NULL);"
- print " ((%s_reply)cbdata->cb)" % a.c_name
+ print " ((%s_reply)cbdata->cb)" % a.c_prefixed_name
print " (proxy,"
for arg in a.out_args:
print " %s," % arg.c_name
@@ -224,12 +238,12 @@ def printClientAsyncFunctionBinding(a):
print
# Entry point
- indent = (8 + len (a.c_name)) * " "
+ indent = (8 + len (a.c_prefixed_name)) * " "
print "static inline GUPnPServiceProxyAction *"
- print "%s_async (GUPnPServiceProxy *proxy," % a.c_name
+ print "%s_async (GUPnPServiceProxy *proxy," % a.c_prefixed_name
for arg in a.in_args:
- print "%s%sin_%s," % (indent, arg.related_var.ctype, arg.c_name)
- print "%s%s_reply callback," % (indent, a.c_name)
+ print "%sconst %sin_%s," % (indent, arg.related_var.ctype, arg.c_name)
+ print "%s%s_reply callback," % (indent, a.c_prefixed_name)
print "%sgpointer userdata)" % indent
print "{"
print " GUPnPServiceProxyAction* action;"
@@ -240,7 +254,7 @@ def printClientAsyncFunctionBinding(a):
print " cbdata->userdata = userdata;"
print " action = gupnp_service_proxy_begin_action"
print " (proxy, \"%s\"," % a.name
- print " _%s_async_callback, cbdata," % a.c_name
+ print " _%s_async_callback, cbdata," % a.c_prefixed_name
for arg in a.in_args:
print " \"%s\", %s, in_%s," % (arg.name, arg.related_var.gtype, arg.c_name)
print " NULL);"
@@ -248,7 +262,8 @@ def printClientAsyncFunctionBinding(a):
print " return action;"
print "}"
-def printClientVariableBinding(v):
+
+def printClientVariableNotifyBinding(v):
asyncdata = " struct {GCallback cb; gpointer userdata; } *cbdata;"
ctype = re.sub ("^gchar", "const gchar", v.ctype);
@@ -303,22 +318,131 @@ def printClientVariableBinding(v):
print " cbdata);"
print "}"
+
+def printServerVariableQueryBinding(v):
+ asyncdata = " struct {GCallback cb; gpointer userdata; } *cbdata;"
+
+ # User callback proto
+ indent = (28 + len (v.ctype)+ len (v.c_prefixed_name)) * " "
+ print "typedef %s(*%s_query_callback) (GUPnPService *service," % (v.ctype, v.c_prefixed_name)
+ print "%sgpointer userdata);" % indent
+ print
+
+ indent = (12 + len (v.c_prefixed_name)) * " "
+ print "static void"
+ print "_%s_query_cb (GUPnPService *service," % v.c_prefixed_name
+ print "%sgchar *variable," % indent
+ print "%sGValue *value," % indent
+ print "%sgpointer userdata)" % indent
+ print "{"
+ print asyncdata
+ print " %s%s;" % (v.ctype, v.c_name)
+ print
+
+ indent = (36 + len (v.c_name) + len (v.c_prefixed_name)) * " "
+ print " cbdata = userdata;"
+ print " %s = ((%s_query_callback)cbdata->cb) (service," % (v.c_name, v.c_prefixed_name)
+ print "%scbdata->userdata);" % indent
+ print " g_value_init (value, %s);" % v.gtype
+ print " %s (value, %s);" % (v.set_function, v.c_name)
+ print "}"
+ print
+
+ indent = (16 + len (v.c_prefixed_name)) * " "
+ print "gulong"
+ print "%s_query_connect (GUPnPService *service," % v.c_prefixed_name
+ print "%s%s_query_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 g_signal_connect_data (service,"
+ print " \"query-variable::%s\"," % v.name
+ print " G_CALLBACK (_%s_query_cb)," % v.c_prefixed_name
+ print " cbdata,"
+ print " _free_cb_data,"
+ print " 0);"
+ print "}"
+ print
+
+
+def printServerActionBinding(a):
+ # getter and setter func for GUPnPServiceAction
+ indent = (13 + len (a.c_prefixed_name)) * " "
+ if len (a.in_args) > 0:
+ print "void"
+ print "%s_action_get (GUPnPServiceAction *action," % (a.c_prefixed_name)
+ for arg in a.in_args[:-1]:
+ print "%s%s*in_%s," % (indent, arg.related_var.ctype, arg.c_name)
+ print "%s%s*in_%s)" % (indent, a.in_args[-1].related_var.ctype, a.in_args[-1].c_name)
+ print "{"
+ print " gupnp_service_action_get (action,"
+ for arg in a.in_args:
+ print " \"%s\", %s, in_%s," % (arg.name, arg.related_var.gtype, arg.c_name)
+ print " NULL);"
+ print "}"
+ print
+ if len (a.out_args) > 0:
+ print "void"
+ print "%s_action_set (GUPnPServiceAction *action," % (a.c_prefixed_name)
+ for arg in a.out_args[:-1]:
+ print "const %s%sout_%s," % (indent, arg.related_var.ctype, arg.c_name)
+ print "%sconst %sout_%s)" % (indent, a.out_args[-1].related_var.ctype, a.out_args[-1].c_name)
+ print "{"
+ print " gupnp_service_action_set (action,"
+ for arg in a.out_args:
+ print " \"%s\", %s, out_%s," % (arg.name, arg.related_var.gtype, arg.c_name)
+ print " NULL);"
+ print "}"
+ print
+
+ indent = (17 + len (a.c_prefixed_name)) * " "
+ print "gulong"
+ print "%s_action_connect (GUPnPService *service," % a.c_prefixed_name
+ print "%sGCallback callback," % indent
+ print "%sgpointer userdata)" % indent
+ print "{"
+ print " return g_signal_connect (service,"
+ print " \"action-invoked::%s\"," % a.name
+ print " callback,"
+ print " userdata);"
+ print "}"
+ print
+
+def PrintServerVariableNotifyBinding(v):
+ indent = (18 + len (v.c_prefixed_name)) * " "
+ print "void"
+ print "%s_variable_notify (GUPnPService *service," % v.c_prefixed_name
+ print "%sconst %s%s)" % (indent ,v.ctype, v.c_name)
+ print "{"
+ print " gupnp_service_notify (service,"
+ print " \"%s\", %s, %s," % (v.name, v.gtype, v.c_name)
+ print " NULL);"
+ print "}"
+ print
+
def parseSCPD(scpd, prefix):
+ """
+ Parse the scpd file into lists of Action and Variable objects.
+ """
if prefix != "":
- prefix = prefix + "_"
+ prefix = prefix.lower() + "_"
+
+ action_elements = scpd.findall("{urn:schemas-upnp-org:service-1-0}actionList/{urn:schemas-upnp-org:service-1-0}action")
+ var_elements = scpd.findall("{urn:schemas-upnp-org:service-1-0}serviceStateTable/{urn:schemas-upnp-org:service-1-0}stateVariable")
- variables = getVariables(scpd, prefix)
- actions = getActions(scpd, prefix, variables)
+ variables = getVariables(var_elements, prefix)
+ actions = getActions(action_elements, prefix, variables)
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()
+def printClientBindings(binding_name, actions, variables):
+ define = "GUPNP_GENERATED_CLIENT_BINDING_%s" % binding_name
print "/* Generated by gupnp-binding-tool */"
print
@@ -331,16 +455,53 @@ def printClientBindings(service_filename, prefix):
print
print "G_BEGIN_DECLS"
- (actions, variables) = parseSCPD (ET.parse(service_filename), prefix)
-
for a in actions:
print "\n/* action %s */\n" % a.name
- printClientSyncFunctionBinding(a)
- printClientAsyncFunctionBinding(a)
+ printClientSyncActionBinding(a)
+ printClientAsyncActionBinding(a)
+ for v in variables:
+ if (not v.dummy) and v.notified:
+ print "\n/* state variable %s */\n" % v.name
+ printClientVariableNotifyBinding(v)
+
+ print
+ print "G_END_DECLS"
+ print
+ print "#endif /* %s */" % define
+
+
+def printServerBindings(binding_name, actions, variables):
+ define = "GUPNP_GENERATED_CLIENT_BINDING_%s" % binding_name
+
+ print "/* Generated by gupnp-binding-tool */"
+ print
+ print "#include <glib/gtypes.h>"
+ print "#include <glib/gerror.h>"
+ print "#include <libgupnp/gupnp-service.h>"
+ print
+ print "#ifndef %s" % define
+ print "#define %s" % define
+ print
+ print "G_BEGIN_DECLS"
+ print
+
+ print "static void"
+ print "_free_cb_data (gpointer data, GClosure *closure)"
+ print "{"
+ print " struct {GCallback cb; gpointer userdata; } *cbdata = data;"
+ print " g_slice_free1 (sizeof (*cbdata), cbdata);"
+ print "}"
+ print
+
+ for a in actions:
+ print "\n/* action %s */\n" % a.name
+ printServerActionBinding(a)
for v in variables:
- if v.notified:
+ if not v.dummy:
print "\n/* state variable %s */\n" % v.name
- printClientVariableBinding(v)
+ printServerVariableQueryBinding(v)
+ if v.notified:
+ PrintServerVariableNotifyBinding(v)
print
print "G_END_DECLS"
@@ -349,7 +510,7 @@ def printClientBindings(service_filename, prefix):
def main ():
- parser = OptionParser("Usage: gupnp-binding-tool [options] service-filename")
+ parser = OptionParser("Usage: %prog [options] service-filename")
parser.add_option("-p", "--prefix", dest="prefix",
metavar="PREFIX", default="",
help="set prefix for generated function names")
@@ -361,10 +522,26 @@ def main ():
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("Expected 1 argument, got %d" % len(args))
+
+ # get a name for header ifdef
+ if options.prefix == "":
+ base = re.sub("[^a-zA-Z0-9]", "_", os.path.basename(args[0]))
+ binding_name = base.upper()
+ else:
+ binding_name = options.prefix.upper()
+
+ # parse scpd file, extract action list and state variable list
+ scpd = ET.parse(args[0])
+ (actions, variables) = parseSCPD (scpd, options.prefix)
+ if (len(actions) == 0 and len(variables) == 0):
+ raise Exception ("No actions or variables found in document")
+
+ # generate bindings
if (options.mode == "client"):
- printClientBindings(args[0], options.prefix)
+ printClientBindings(binding_name, actions, variables)
else:
- print raise Exception ("Not implemented yet")
+ printServerBindings(binding_name, actions, variables)
+
if __name__ == "__main__":
main()