dbus/glib/dbus-glib-tool.c
Colin Walters 03f6615eac 2005-02-17 Colin Walters <walters@verbum.org>
This patch is based on initial work from
	Paul Kuliniewicz <kuliniew@purdue.edu>.

	* glib/dbus-gvalue.c (dbus_gvalue_init): New function; move
	initialization of GValue from dbus type to here.
	(dbus_gvalue_genmarshal_name_from_type): New function; generates a string
	for the "glib-genmarshal" program from a DBus type.
	(dbus_gvalue_binding_type_from_type): New function; turns a DBus type
	into the C name for it we use in the glib bindings.
	(dbus_gvalue_ctype_from_type): New function; maps a DBus type into a
	glib C type (not GValue).
	(dbus_gvalue_demarshal): invoke dbus_gvalue_init.

	* glib/dbus-gutils.c (_dbus_gutils_wincaps_to_uscore): Moved here
	from dbus-gobject.c.

	* glib/dbus-gutils.h: Prototype it.

	* glib/dbus-gproxy.c: Include new dbus-gobject.h.
	(marshal_dbus_message_to_g_marshaller): Use new shared function
	dbus_glib_marshal_dbus_message_to_gvalue_array.

	* glib/dbus-gparser.c (parse_interface, parse_method): Handle c_name attribute.
	Will be changed once we have annotations.

	* glib/dbus-gobject.c: Change info_hash_mutex from GStaticMutex to
	GStaticRWLock.  Callers updated.
	(wincaps_to_uscore): Move to dbus-gutils.c.  Callers updated.
	(string_table_next): New function for iterating over zero-terminated
	string value array.
	(string_table_lookup): New function; retrieves specific entry in
	array.
	(get_method_data): New function; look up method data in object data chunk.
	(object_error_domain_prefix_from_object_info)
	(object_error_code_from_object_info): New functions, but not implemented yet.
	(method_interface_from_object_info): New function; retrieve interface name.
	(method_name_from_object_info): New function; retrieve method name.
	(method_arg_info_from_object_info): New function; retrieve argument data.
	(arg_iterate): New function; iterates over serialized argument data.
	(method_dir_signature_from_object_info): New function; returns a
	GString holding type signature for arguments for just one
	direction (input or output).
	(method_input_signature_from_object_info)
	(method_output_signature_from_object_info): New functions.
	(dbus_glib_marshal_dbus_message_to_gvalue_array): New shared function;
	converts dbus message arguments into a GValue array.  Used for both
	signal handling and method invocation.
	(struct DBusGlibWriteIterfaceData): New utility structure.
	(write_interface): New function; generate introspection XML for
	an interface.
	(introspect_interfaces): New function; gathers all interface->methods,
	generates introspection XML for them.
	(handle_introspect): Invoke introspect_interfaces.
	(get_object_property): Be sure to zero-initalize stack-allocated GValue.
	(lookup_object_and_method): New function; examines an incoming message
	and attempts to match it up (via interface, method name, and argument
	signature) with a known object and method.
	(gerror_domaincode_to_dbus_error_name): New function; converts a
	GError domain and code into a DBus error name.  Needs GError data
	added to object introspection to work well.
	(gerror_to_dbus_error_message): Creates a DBusMessage error return from
	GError.
	(invoke_object_method): New function to invoke an object method
	looked up via lookup_object_and_method.  Parses the incoming
	message, turns it into a GValue array, then invokes the marshaller
	specified in the DBusGMethodInfo.  Creates a new message with
	either return values or error message as appropriate.
	(gobject_message_function): Invoke lookup_object_and_method and
	invoke_object_method.

	* glib/dbus-glib-tool.c: Include dbus-binding-tool-glib.h.
	(enum DBusBindingOutputMode): New enum for binding output modes.
	(pretty_print): Print binding names.
	(dbus_binding_tool_error_quark): GError bits.
	(version): Fix typo.
	(main): Create GIOChannel for output.  Parse new --mode argument,
	possible values are "pretty-print", "glib-server", "glib-client".
	Use mode to invoke appropriate function.

	* glib/dbus-gobject.h: Prototype dbus_glib_marshal_dbus_message_to_gvalue_array.

	* glib/dbus-glib-tool.h: New header, just includes GError bits
	for now.

	* glib/dbus-gidl.c (struct InterfaceInfo): Add bindings hashtable;
	maps binding style to name.
	(struct MethodInfo): Ditto.
	(get_hash_keys, get_hash_key): Utility function, returns keys for
	a GHashTable.
	(interface_info_new, method_info_new): Initialize bindings.
	(interface_info_unref, method_info_unref): Destroy bindings.
	(method_info_get_binding_names, method_info_get_binding_name)
	(interface_info_get_binding_names, interface_info_get_binding_name):
	Functions for retrieving binding names.
	(method_info_set_binding_name, interface_info_set_binding_name):
	Functions for setting binding names.

	* glib/dbus-binding-tool-glib.h: New file, has prototypes
	for glib binding generation.

	* glib/dbus-binding-tool-glib.c: New file, implements server-side
	and client-side glib glue generation.

	* glib/Makefile.am (dbus_binding_tool_SOURCES): Add
	dbus-binding-tool-glib.c, dbus-binding-tool-glib.h,
	dbus-glib-tool.h.

	* dbus/dbus-glib.h (struct DBusGMethodMarshaller): Remove in favor
	of using GClosureMarshal directly.
	(struct DBusGObjectInfo): Add n_infos member.

	* test/glib/test-service-glib.xml: New file; contains introspection data
	for MyTestObject used in test-service-glib.c.

	* test/glib/test-service-glib.c (enum MyObjectError): New GError enum.
	(my_object_do_nothing, my_object_increment, my_object_throw_error)
	(my_object_uppercase, my_object_many_args): New test methods.
	(main): Use dbus_g_object_class_install_info to include generated object
	info.

	* test/glib/Makefile.am: Generate server-side glue for test-service-glib.c,
	as well as client-side bindings.

	* test/glib/test-dbus-glib.c: Include test-service-glib-bindings.h.
	(main): Activate TestSuiteGLibService; test invoke a bunch of its methods
	using both the dbus_gproxy stuff directly as well as the generated bindings.
2005-02-17 17:41:30 +00:00

420 lines
10 KiB
C

/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-glib-tool.c Tool used by apps using glib bindings
*
* Copyright (C) 2003, 2004 Red Hat, Inc.
*
* Licensed under the Academic Free License version 2.1
*
* 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 Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <config.h>
#include "dbus-gidl.h"
#include "dbus-gparser.h"
#include "dbus-gutils.h"
#include "dbus-binding-tool-glib.h"
#include <locale.h>
#include <libintl.h>
#define _(x) dgettext (GETTEXT_PACKAGE, x)
#define N_(x) x
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef DBUS_BUILD_TESTS
static void run_all_tests (const char *test_data_dir);
#endif
typedef enum {
DBUS_BINDING_OUTPUT_NONE,
DBUS_BINDING_OUTPUT_PRETTY,
DBUS_BINDING_OUTPUT_GLIB_SERVER,
DBUS_BINDING_OUTPUT_GLIB_CLIENT,
} DBusBindingOutputMode;
static void
indent (int depth)
{
depth *= 2; /* 2-space indent */
while (depth > 0)
{
putc (' ', stdout);
--depth;
}
}
static void pretty_print (BaseInfo *base,
int depth);
static void
pretty_print_list (GSList *list,
int depth)
{
GSList *tmp;
tmp = list;
while (tmp != NULL)
{
pretty_print (tmp->data, depth);
tmp = tmp->next;
}
}
static void
pretty_print (BaseInfo *base,
int depth)
{
InfoType t;
const char *name;
t = base_info_get_type (base);
name = base_info_get_name (base);
indent (depth);
switch (t)
{
case INFO_TYPE_NODE:
{
NodeInfo *n = (NodeInfo*) base;
if (name == NULL)
printf (_("<anonymous node> {\n"));
else
printf (_("node \"%s\" {\n"), name);
pretty_print_list (node_info_get_interfaces (n), depth + 1);
pretty_print_list (node_info_get_nodes (n), depth + 1);
indent (depth);
printf ("}\n");
}
break;
case INFO_TYPE_INTERFACE:
{
InterfaceInfo *i = (InterfaceInfo*) base;
GSList *binding_types, *elt;
g_assert (name != NULL);
printf (_("interface \"%s\" {\n"), name);
binding_types = interface_info_get_binding_names (i);
for (elt = binding_types; elt; elt = elt->next)
{
const char *binding_type = elt->data;
const char *binding_name = interface_info_get_binding_name (i, binding_type);
printf (_(" (binding \"%s\": \"%s\") "),
binding_type, binding_name);
}
g_slist_free (binding_types);
pretty_print_list (interface_info_get_methods (i), depth + 1);
pretty_print_list (interface_info_get_signals (i), depth + 1);
pretty_print_list (interface_info_get_properties (i), depth + 1);
indent (depth);
printf ("}\n");
}
break;
case INFO_TYPE_METHOD:
{
MethodInfo *m = (MethodInfo*) base;
GSList *binding_types, *elt;
g_assert (name != NULL);
binding_types = method_info_get_binding_names (m);
printf (_("method \"%s\""), name);
for (elt = binding_types; elt; elt = elt->next)
{
const char *binding_type = elt->data;
const char *binding_name = method_info_get_binding_name (m, binding_type);
printf (_(" (binding \"%s\": \"%s\") "),
binding_type, binding_name);
}
g_slist_free (binding_types);
pretty_print_list (method_info_get_args (m), depth + 1);
indent (depth);
printf (")\n");
}
break;
case INFO_TYPE_SIGNAL:
{
SignalInfo *s = (SignalInfo*) base;
g_assert (name != NULL);
printf (_("signal \"%s\" (\n"), name);
pretty_print_list (signal_info_get_args (s), depth + 1);
indent (depth);
printf (")\n");
}
break;
case INFO_TYPE_PROPERTY:
{
PropertyInfo *a = (PropertyInfo*) base;
int pt = property_info_get_type (a);
PropertyAccessFlags acc = property_info_get_access (a);
printf ("%s%s %s",
acc & PROPERTY_READ ? "read" : "",
acc & PROPERTY_WRITE ? "write" : "",
_dbus_gutils_type_to_string (pt));
if (name)
printf (" %s\n", name);
else
printf ("\n");
}
break;
case INFO_TYPE_ARG:
{
ArgInfo *a = (ArgInfo*) base;
int at = arg_info_get_type (a);
ArgDirection d = arg_info_get_direction (a);
printf ("%s %s",
d == ARG_IN ? "in" : "out",
_dbus_gutils_type_to_string (at));
if (name)
printf (" %s\n", name);
else
printf ("\n");
}
break;
}
}
GQuark
dbus_binding_tool_error_quark (void)
{
static GQuark quark = 0;
if (!quark)
quark = g_quark_from_static_string ("dbus_binding_tool_error");
return quark;
}
static void
usage (int ecode)
{
fprintf (stderr, "dbus-binding-tool [--version] [--help] [--pretty-print]\n");
exit (ecode);
}
static void
version (void)
{
printf ("D-BUS Binding Tool %s\n"
"Copyright (C) 2003-2005 Red Hat, Inc.\n"
"This is free software; see the source for copying conditions.\n"
"There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
VERSION);
exit (0);
}
int
main (int argc, char **argv)
{
const char *prev_arg;
int i;
GSList *files;
DBusBindingOutputMode outputmode;
gboolean end_of_args;
GSList *tmp;
GIOChannel *channel;
GError *error;
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, DBUS_LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
g_type_init ();
outputmode = DBUS_BINDING_OUTPUT_NONE;
end_of_args = FALSE;
files = NULL;
prev_arg = NULL;
i = 1;
while (i < argc)
{
const char *arg = argv[i];
if (!end_of_args)
{
if (strcmp (arg, "--help") == 0 ||
strcmp (arg, "-h") == 0 ||
strcmp (arg, "-?") == 0)
usage (0);
else if (strcmp (arg, "--version") == 0)
version ();
#ifdef DBUS_BUILD_TESTS
else if (strcmp (arg, "--self-test") == 0)
run_all_tests (NULL);
#endif /* DBUS_BUILD_TESTS */
else if (strncmp (arg, "--mode=", 7) == 0)
{
const char *mode = arg + 7;
if (!strcmp (mode, "pretty"))
outputmode = DBUS_BINDING_OUTPUT_PRETTY;
else if (!strcmp (mode, "glib-server"))
outputmode = DBUS_BINDING_OUTPUT_GLIB_SERVER;
else if (!strcmp (mode, "glib-client"))
outputmode = DBUS_BINDING_OUTPUT_GLIB_CLIENT;
else
usage (1);
}
else if (arg[0] == '-' &&
arg[1] == '-' &&
arg[2] == '\0')
end_of_args = TRUE;
else if (arg[0] == '-')
{
usage (1);
}
else
{
files = g_slist_prepend (files, (char*) arg);
}
}
else
files = g_slist_prepend (files, (char*) arg);
prev_arg = arg;
++i;
}
error = NULL;
channel = g_io_channel_unix_new (fileno (stdout));
if (!g_io_channel_set_encoding (channel, NULL, &error))
{
fprintf (stderr, _("Couldn't set channel encoding to NULL: %s\n"),
error->message);
exit (1);
}
files = g_slist_reverse (files);
tmp = files;
while (tmp != NULL)
{
NodeInfo *node;
GError *error;
const char *filename;
filename = tmp->data;
error = NULL;
node = description_load_from_file (filename,
&error);
if (node == NULL)
{
g_assert (error != NULL);
fprintf (stderr, _("Unable to load \"%s\": %s\n"),
filename, error->message);
g_error_free (error);
exit (1);
}
else
{
switch (outputmode)
{
case DBUS_BINDING_OUTPUT_PRETTY:
pretty_print ((BaseInfo*) node, 0);
break;
case DBUS_BINDING_OUTPUT_GLIB_SERVER:
if (!dbus_binding_tool_output_glib_server ((BaseInfo *) node, channel, &error))
{
g_error (_("Compilation failed: %s\n"), error->message);
exit (1);
}
break;
case DBUS_BINDING_OUTPUT_GLIB_CLIENT:
if (!dbus_binding_tool_output_glib_client ((BaseInfo *) node, channel, &error))
{
g_error (_("Compilation failed: %s\n"), error->message);
exit (1);
}
break;
case DBUS_BINDING_OUTPUT_NONE:
break;
}
}
if (node)
node_info_unref (node);
tmp = tmp->next;
}
if (!g_io_channel_flush (channel, &error))
{
g_error (_("Failed to flush IO channel: %s"), error->message);
exit (1);
}
return 0;
}
#ifdef DBUS_BUILD_TESTS
static void
test_die (const char *failure)
{
fprintf (stderr, "Unit test failed: %s\n", failure);
exit (1);
}
/**
* @ingroup DBusGTool
* Unit test for GLib utility tool
* @returns #TRUE on success.
*/
static gboolean
_dbus_gtool_test (const char *test_data_dir)
{
return TRUE;
}
static void
run_all_tests (const char *test_data_dir)
{
if (test_data_dir == NULL)
test_data_dir = g_getenv ("DBUS_TEST_DATA");
if (test_data_dir != NULL)
printf ("Test data in %s\n", test_data_dir);
else
printf ("No test data!\n");
printf ("%s: running binding tests\n", "dbus-binding-tool");
if (!_dbus_gtool_test (test_data_dir))
test_die ("gtool");
printf ("%s: completed successfully\n", "dbus-binding-tool");
}
#endif /* DBUS_BUILD_TESTS */