diff options
author | Jussi Kukkonen <jku@openedhand.com> | 2008-10-17 10:19:28 +0000 |
---|---|---|
committer | Jussi Kukkonen <jku@openedhand.com> | 2008-10-17 10:19:28 +0000 |
commit | 6fa0bd353c6d9a2835b0afd84858598183a79a33 (patch) | |
tree | b4f2ffcbe56408e224f57f041febbe7f5ad35ce5 /tools | |
parent | 91f8ef474447239730cb4b9829baddb780a8082c (diff) | |
download | gupnp-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-x | tools/gupnp-binding-tool | 321 |
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() |