[patch] Automake backend parser script and others



Hi JP,

Finally I've finished integrating the new parser script to the automake
backend in gnome-build.  Currently it has more or less the same
functionality as before, but with the new architecture.  I also reworked
some parts of the C program to better fit the script and for future
extensions.

Attached is the patch file (which also has some minor fixes to other
files) and a tgz with the new files required.  Please note that
gbf-am-parse should be removed, since it's now generated at configure
time from gbf-am-parse.in (to allow some basic substitutions such as the
path to perl).

The most important thing I think it's missing (and what I'll work on
next) is, after project modifications, to reparse changed groups and
make the backend read them again.  This is because you can't really know
how will a modification affect other targets, and because I think it's
safer to do it this way (no sync problems between the project model and
the actual files).  Any ideas on how to do this are very welcome,
specially how to inform the controls a group has changed.  I was
thinking on using the event source, but I'm not sure at all.

May I commit?

Regards,
Gustavo

? autom4te.cache
? stamp-h1
? src/backends/libgbf_am/GBF
? src/backends/libgbf_am/config.log
? src/backends/libgbf_am/gbf-am-config.c
? src/backends/libgbf_am/gbf-am-config.h
? src/backends/libgbf_am/gbf-am-parse.in
? src/backends/libgbf_am/output.dtd
? src/backends/libgbf_am/todo.parser
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gnome-build/ChangeLog,v
retrieving revision 1.99
diff -u -r1.99 ChangeLog
--- ChangeLog	4 Jun 2002 19:17:57 -0000	1.99
+++ ChangeLog	28 Jun 2002 04:59:42 -0000
@@ -1,3 +1,57 @@
+2002-06-28  Gustavo Giraldez  <gustavo giraldez gmx net>
+
+	* configure.in: Added perl requirements for the automake parse
+	script.  Added gbf-am-parse to the list of configure generated
+	files
+
+	* gnome-build-clean.sh: minor fix to not count grep processes
+
+	* idl/Makefile.am: install gbf-types.idl
+
+	* src/backends/libgbf_am/GBF/Makefile.am,
+	* src/backends/libgbf_am/GBF/AmFiles.pm,
+	* src/backends/libgbf_am/GBF/General.pm: New perl modules for the
+	parser script.
+
+	* src/backends/libgbf_am/gbf-am-parse.in: New script.  As is
+	generated at configure time (some substitutions are required) it
+	replaces gbf-am-parse.
+	
+	* src/backends/libgbf_am/output.dtd: DTD describing parser output.
+	
+	* src/backends/libgbf_am/Makefile.am: include GBF subdir, add
+	gbf-am-config.[ch] files to compilation
+
+	* src/backends/libgbf_am/gbf-am-config.[ch]:  New files which
+	describe structures to accomodate project, group and target
+	configuration.
+	
+	* src/backends/libgbf_am/gbf-am-project.c: Integrated the new
+	parser script.  New method for spawning scripts should be better
+	when we decide to make such operations asynchronously.  Project
+	information is no longer kept in CORBA structs, but in plain C
+	structs for more flexibility.  Implemented functions to ease data
+	structures lifecycle managment.  Reworked and extended xml parser
+	to accomodate to output from the new script.
+
+	* src/backends/libgbf_am/main.c: Removed signal handler.
+
+	* src/backends/libgbf_am/test.c: Fixed some leaks.  Print group
+	hierarchy.
+
+	* src/controls/Makefile.am: Add src/lib as an include directory
+	for compilation.
+
+	* src/controls/control-factories.c, 
+	* src/controls/gbf-project-tree.c, 
+	* src/controls/gbf-target-tree.c:
+	Fixed some memory and object reference leaks.
+
+	* src/controls/test-controls.c: Fixed some leaks.  Changed
+	listener addition code with simpler sugar wrapper from bonobo.
+
+	* src/lib/Makefile.am: Put corba generated files in clean list.
+
 2002-06-04  Pablo Saratxaga  <pablo mandrakesoft com>
 
 	* configure.in: Added Vietnamese (vi) to ALL_LINGUAS
Index: configure.in
===================================================================
RCS file: /cvs/gnome/gnome-build/configure.in,v
retrieving revision 1.24
diff -u -r1.24 configure.in
--- configure.in	4 Jun 2002 19:17:57 -0000	1.24
+++ configure.in	28 Jun 2002 04:59:42 -0000
@@ -68,6 +68,18 @@
 AC_SUBST(GBF_DRUID_CFLAGS)
 AC_SUBST(GBF_DRUID_LIBS)
 
+dnl **************************
+dnl Backend requirements
+dnl **************************
+
+AC_PATH_PROG(PERL, perl)
+if test -z "$PERL"; then
+   AC_MSG_ERROR([perl not found])
+fi
+$PERL -e 'require 5.005;' || {
+   AC_MSG_ERROR([perl 5.005 or better is required])
+}
+
 dnl ***************
 dnl IDL 
 dnl ***************
@@ -126,9 +138,14 @@
 src/lib/Makefile
 src/backends/Makefile
 src/backends/libgbf_am/Makefile
+src/backends/libgbf_am/gbf-am-parse
+src/backends/libgbf_am/GBF/Makefile
 src/controls/Makefile
 po/Makefile.in
 idl/Makefile
 gnome-build.spec
 gnome-build-1.0.pc
+], [
+chmod +x src/backends/libgbf_am/gbf-am-parse
 ])
+
Index: gnome-build-clean.sh
===================================================================
RCS file: /cvs/gnome/gnome-build/gnome-build-clean.sh,v
retrieving revision 1.1
diff -u -r1.1 gnome-build-clean.sh
--- gnome-build-clean.sh	16 Jul 2001 04:20:34 -0000	1.1
+++ gnome-build-clean.sh	28 Jun 2002 04:59:42 -0000
@@ -15,7 +15,7 @@
 
     EGREP_PATTERN=`echo $NAME | sed -e 's/\(.\)\(.*\)/[\1]\2/' | sed -e 's/\[\\\^\]/\[\\^\]/'`;
 
-    COUNT=`ps auxww | egrep $EGREP_PATTERN | wc -l`;
+    COUNT=`ps auxww | egrep $EGREP_PATTERN | grep -v egrep | wc -l`;
 
     if [ $COUNT -gt 0 ]; then
 	if [ -z $FOUND_ANY ]; then
Index: idl/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-build/idl/Makefile.am,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Makefile.am
--- idl/Makefile.am	10 Dec 2000 04:27:09 -0000	1.1.1.1
+++ idl/Makefile.am	28 Jun 2002 04:59:42 -0000
@@ -2,6 +2,7 @@
 
 idl_DATA = \
         gbf-project.idl    \
+	gbf-types.idl      \
         gnome-build.idl 
 
-EXTRA_DIST = $(idl_DATA)
\ No newline at end of file
+EXTRA_DIST = $(idl_DATA)
Index: src/backends/libgbf_am/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-build/src/backends/libgbf_am/Makefile.am,v
retrieving revision 1.12
diff -u -r1.12 Makefile.am
--- src/backends/libgbf_am/Makefile.am	18 Mar 2002 04:48:35 -0000	1.12
+++ src/backends/libgbf_am/Makefile.am	28 Jun 2002 04:59:43 -0000
@@ -1,3 +1,5 @@
+SUBDIRS = GBF
+
 INCLUDES = 					\
         -DGNOMELOCALEDIR=\""$(datadir)/locale"\"\
         -DG_DISABLE_DEPRECATED 			\
@@ -14,6 +16,8 @@
 gbf_am_project_SOURCES = 	\
 	gbf-am-project.c	\
 	gbf-am-project.h	\
+	gbf-am-config.c		\
+	gbf-am-config.h		\
 	main.c
 
 gbf_am_project_LDADD = 				\
Index: src/backends/libgbf_am/gbf-am-project.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/backends/libgbf_am/gbf-am-project.c,v
retrieving revision 1.24
diff -u -r1.24 gbf-am-project.c
--- src/backends/libgbf_am/gbf-am-project.c	14 Mar 2002 07:52:14 -0000	1.24
+++ src/backends/libgbf_am/gbf-am-project.c	28 Jun 2002 04:59:52 -0000
@@ -40,9 +40,11 @@
 #include "unknown.xpm"
 #include "gnome-build.h"
 #include "gbf-am-project.h"
+#include "gbf-am-config.h"
+
+
+#define EMPTY_STRING ""
 
-#define SCRIPT_NAME "gbf-am-parse"
-#define BUILD_SCRIPT_NAME "gbf-am-build"
 
 typedef enum {
 	BUILD
@@ -56,28 +58,102 @@
 	GNOME_Development_BuildType build_type;
 } GbfAmProjectOp;
 
-typedef struct
-{
+/* structures for holding project data */
+typedef struct _GbfAmGroup        GbfAmGroup;
+typedef struct _GbfAmTarget       GbfAmTarget;
+typedef struct _GbfAmTargetSource GbfAmTargetSource;
+
+typedef enum {
+	SOURCE_FILE,
+	BUILT_SOURCE
+} GbfAmSourceType;
+
+struct _GbfAmGroup {
+	gchar       *id;
+
+	gchar       *name;
+	GbfAmGroup  *parent;
+
+	GList       *groups;
+	GList       *targets;
+
+	GbfAmConfigMapping *config;
+};
+
+struct _GbfAmTarget {
+	gchar       *id;
+
+	GbfAmGroup  *group;
+	gchar       *name;
+	gchar       *type;
+
+	GList       *sources;
+
+	GbfAmConfigMapping *config;
+};
+
+struct _GbfAmTargetSource {
+	gchar           *id;
+
+	GbfAmSourceType  type;
+	gchar           *uri;
+	GbfAmTarget     *target;
+
+	gchar           *depname;
+	GbfAmTarget     *dependency;
+};
+
+
+/* XML parser state */
+typedef struct _GbfAmProjectParseData GbfAmProjectParseData;
+
+struct _GbfAmProjectParseData {
 	GbfAmProject *prj;
 
 	/* For tracking state */
-	GList *groups;
-	GNOME_Development_Target *target;
-} GbfAmProjectParseData;
+	GSList             *groups;
+	GbfAmTarget        *target;
+	GbfAmConfigMapping *config;
+	gchar              *param_key;
+	
+	/* parser state */
+	enum {
+		PARSE_INITIAL,
+		PARSE_DONE,
+		
+		PARSE_PROJECT,
+		PARSE_GROUP,
+		PARSE_TARGET,
+		PARSE_SOURCE,
+		PARSE_DEPENDENCY,
+		PARSE_CONFIG,
+		PARSE_PARAM,
+		PARSE_ITEM,
+		PARSE_PARAM_DONE,
+		
+		PARSE_ERROR
+	} state, config_saved_state;
+};
 
 struct _GbfAmProjectPrivate {
 	char *project_dir;
 
-	char *parent_group;
+	/* project data */
+	GbfAmGroup *parent_group;
 	GHashTable *groups;
 	GHashTable *targets;
 	GHashTable *sources;
 	
+	GbfAmConfigMapping *config;
+
+	/* for building output parsing */
 	char *buffer;
 	
+	/* queue of operations */
 	GList *ops;
         gint op_handler;
 
+	/* aggregates */
 	BonoboPropertyBag *property_bag;
 	BonoboEventSource *event_source;
 };
@@ -86,9 +162,44 @@
 	PROP_PROJECT_DIR
 };
 
-#define MAX_GROUPS 32
-#define MAX_TARGETS 128
-#define MAX_SOURCES 128
+
+/* data structure for controlling script spawning */
+
+#define SCRIPT_NAME       "gbf-am-parse"
+#define BUILD_SCRIPT_NAME "gbf-am-build"
+
+#define READ_BUFFER_SIZE   32768
+#define READ_BUFFER_DELTA  16384
+
+typedef struct _GbfAmSpawnData GbfAmSpawnData;
+
+struct _GbfAmSpawnData {
+	GMainLoop *main_loop;
+	
+	/* timeout control */
+	gint ticks, max_ticks;
+	
+	/* child information */
+	gint child_pid;
+	gint child_status;
+	
+	/* buffers and related channels */
+	struct {
+		GIOChannel *channel;
+		gchar      *buffer;
+		gsize       size, length;
+	} input, output, error;
+	
+	/* main loop source ids */
+	guint input_source, output_source, error_source;
+};
+
+/* waitpid() check interval for synchronous script execution */
+#define CHECK_TIMEOUT 100
+
+/*
+ * Private prototypes -------------------------------------
+ */
 
 #define PARENT_TYPE         BONOBO_OBJECT_TYPE
 static BonoboObjectClass *parent_class = NULL;
@@ -103,6 +214,11 @@
 static void build_project_op (GbfAmProject *prj, 
 			      GNOME_Development_BuildType type);
 
+
+/*
+ * Queue operations ------------------------
+ */
+
 static gboolean
 check_queue (GbfAmProject *prj)
 {
@@ -146,108 +262,67 @@
 		g_idle_add ((GSourceFunc)check_queue, prj);
 }
 
-static void
-source_store (GbfAmProject *prj, GNOME_Development_TargetSource *source) 
-{
-	GbfAmProjectPrivate *priv;
-	GNOME_Development_Target *target;
-	
-	priv = prj->priv;
+/*
+ * Project modification functions -----------------------------
+ */
 
-	/* Add this source to the target */
-	target = g_hash_table_lookup (priv->targets, source->target_id);
-	if (target->sources._length < MAX_SOURCES) {
-		target->sources._buffer[target->sources._length] = CORBA_string_dup (source->id);
-		target->sources._length++;
-	}
-	
-	/* Store the source */
-	g_hash_table_insert (priv->sources, source->id, source);
-}
-
-static void
-source_remove (GbfAmProject *prj, GNOME_Development_TargetSource *source) 
+static xmlNodePtr 
+write_group (xmlDocPtr doc, xmlNodePtr node, GbfAmGroup *group)
 {
-	GbfAmProjectPrivate *priv;
-	GNOME_Development_Target *target;
-	int i;
-	
-	priv = prj->priv;
+	xmlNodePtr grp;
 
-	/* Remove this source from the target */
-	target = g_hash_table_lookup (priv->targets, source->target_id);
-	for (i = 0; i < target->sources._length; i++) {
-		if (!strcmp (target->sources._buffer[i], source->id)) {
-			CORBA_free (target->sources._buffer[i]);
-			target->sources._buffer[i] = target->sources._buffer[target->sources._length];
-			target->sources._length--;
-			break;
-		}
-	}
-	
-	/* Store the source */
-	g_hash_table_remove (priv->sources, source->id);
-	CORBA_free (source);
-}
-
-static xmlNodePtr
-write_group_chain (GbfAmProject *prj, xmlDocPtr doc, xmlNodePtr node, GNOME_Development_Group *group)
-{
-	GbfAmProjectPrivate *priv;
-	GNOME_Development_Group *write_group;
-	xmlNodePtr grp, first_grp = NULL, last_grp = NULL;
+	grp = xmlNewDocNode (doc, NULL, "group", NULL);
+	xmlSetProp (grp, "id", group->id);
+	xmlAddChild (node, grp);
 
-	priv = prj->priv;
-
-	write_group = group;
-	do {
-		grp = xmlNewDocNode (doc, NULL, "group", NULL);
-		xmlSetProp (grp, "name", write_group->name);
-		if (last_grp != NULL)
-			xmlAddChild (grp, last_grp);
-		if (first_grp == NULL)
-			first_grp = grp;
-		
-		write_group = g_hash_table_lookup (priv->groups, write_group->parent_id);
-		last_grp = grp;
-	} while (write_group != NULL);
-
-	xmlAddChild (node, last_grp);
-
-	return first_grp;
+	return grp;
 }
 
 static xmlNodePtr
-write_target (xmlDocPtr doc, xmlNodePtr node, GNOME_Development_Target *target)
+write_target (xmlDocPtr doc, xmlNodePtr node, GbfAmTarget *target)
 {
 	xmlNodePtr trg;
 
 	trg = xmlNewDocNode (doc, NULL, "target", NULL);
+	/* FIXME: we need to output id for existing targets and name/type for new ones */
+#if 1
+	if (target->group && target->group->id) {
+		/* strip the group id from the target id, since the script identifies
+		   targets only within the group */
+		xmlSetProp (trg, "id", target->id + strlen (target->group->id));
+	} else {
+		xmlSetProp (trg, "id", target->id);
+	}
+#else
 	xmlSetProp (trg, "name", target->name);
 	xmlSetProp (trg, "type", target->type);
+#endif
 	xmlAddChild (node, trg);
 
 	return trg;
 }
 
 static xmlNodePtr
-write_source (xmlDocPtr doc, xmlNodePtr node, GNOME_Development_TargetSource *source)
+write_source (GbfAmProject *prj, xmlDocPtr doc, xmlNodePtr node, GbfAmTargetSource *source)
 {
 	xmlNodePtr src;
 
 	src = xmlNewDocNode (doc, NULL, "source", NULL);
-	xmlSetProp (src, "uri", source->source);
+	g_assert (prj->priv->project_dir);
+	/* FIXME: actually check that the source uri is inside the project root */
+	/* skip file:/// and the project root from the uri */
+	xmlSetProp (src, "uri", source->uri + 7 + strlen (prj->priv->project_dir));
 	xmlAddChild (node, src);
 
 	return src;
 }
 	
 static gboolean
-write_add_source (GbfAmProject *prj, xmlDocPtr doc, GNOME_Development_TargetSource *source)
+write_add_source (GbfAmProject *prj, xmlDocPtr doc, GbfAmTargetSource *source)
 {
 	GbfAmProjectPrivate *priv;
-	GNOME_Development_Group *group;
-	GNOME_Development_Target *target;
+	GbfAmGroup *group;
+	GbfAmTarget *target;
 	xmlNodePtr cur;
 	
 	priv = prj->priv;
@@ -255,28 +330,29 @@
 	cur = xmlNewDocNode (doc, NULL, "add", NULL);
 	xmlSetProp (cur, "type", "source");
 	xmlAddChild (doc->children, cur);
-	
-	target = g_hash_table_lookup (priv->targets, source->target_id);
-	if (target == NULL)
+
+	/* FIXME: check for source->target != NULL */
+	target = g_hash_table_lookup (priv->targets, source->target->id);
+	if (target == NULL || target->group == NULL)
 		return FALSE;
 
-	group = g_hash_table_lookup (priv->groups, target->group_id);
+	group = g_hash_table_lookup (priv->groups, target->group->id);
 	if (group == NULL)
 		return FALSE;
 
-	cur = write_group_chain (prj, doc, cur, group);
+	cur = write_group (doc, cur, group);
 	cur = write_target (doc, cur, target);
-	cur = write_source (doc, cur, source);
+	cur = write_source (prj, doc, cur, source);
 	
 	return (cur != NULL);
 }
 
 static gboolean
-write_remove_source (GbfAmProject *prj, xmlDocPtr doc, GNOME_Development_TargetSource *source)
+write_remove_source (GbfAmProject *prj, xmlDocPtr doc, GbfAmTargetSource *source)
 {
 	GbfAmProjectPrivate *priv;
-	GNOME_Development_Group *group;
-	GNOME_Development_Target *target;
+	GbfAmGroup *group;
+	GbfAmTarget *target;
 	xmlNodePtr cur;
 
 	priv = prj->priv;
@@ -285,17 +361,18 @@
 	xmlSetProp (cur, "type", "source");
 	xmlAddChild (doc->children, cur);
 	
-	target = g_hash_table_lookup (priv->targets, source->target_id);
-	if (target == NULL)
+	/* FIXME: check for source->target != NULL */
+	target = g_hash_table_lookup (priv->targets, source->target->id);
+	if (target == NULL || target->group == NULL)
 		return FALSE;
 
-	group = g_hash_table_lookup (priv->groups, target->group_id);
+	group = g_hash_table_lookup (priv->groups, target->group->id);
 	if (group == NULL)
 		return FALSE;
 
-	cur = write_group_chain (prj, doc, cur, group);
+	cur = write_group (doc, cur, group);
 	cur = write_target (doc, cur, target);
-	cur = write_source (doc, cur, source);
+	cur = write_source (prj, doc, cur, source);
 
 	return (cur != NULL);
 }
@@ -318,31 +395,37 @@
 	return doc;
 }
 
-static char *
-make_script_path (const char *name)
-{
-	char *path = g_strjoin ("/", SCRIPTS_DIR, name, NULL);
-
-	return path;
-}
-
+/*
+ * Perl script output parser ------------------------------
+ */
+
+#define PARSER_ASSERT(x)  G_STMT_START {                      \
+	if (!(x)) { data->state = PARSE_ERROR; return; }      \
+	} G_STMT_END 
+ 
 static void
 sax_start_element (void *ctxt, const xmlChar *name, const xmlChar **attrs)
 {
 	GbfAmProjectParseData *data;
-	GbfAmProject *prj;
-	GbfAmProjectPrivate *priv;
-	GNOME_Development_Target *target;
-	GNOME_Development_Group *group;
-	GNOME_Development_TargetSource *source;
-	char id[1024];
-
+	GbfAmProject          *prj;
+	GbfAmProjectPrivate   *priv;
+	GbfAmTarget           *target;
+	GbfAmGroup            *group;
+	GbfAmTargetSource     *source;
+	
 	data = ctxt;
 	prj = data->prj;
 	priv = prj->priv;
 	
+	if (data->state == PARSE_ERROR || data->state == PARSE_DONE)
+		return;
+	
 	if (!strcmp (name, "project")) {
-		/* Find the name */
+		PARSER_ASSERT (data->state == PARSE_INITIAL);
+			
+		data->state = PARSE_PROJECT;
+
+		/* Find the project root */
 		while (attrs && *attrs != NULL) {
 			const xmlChar **val = attrs;
 			
@@ -351,11 +434,15 @@
 				priv->project_dir = g_strdup (*val);
 			attrs = ++val;
 		}
+
 	} else if (!strcmp (name, "group")) {
-		GNOME_Development_Group *parent_group;
+		GbfAmGroup *parent_group;
+		gchar *group_name = NULL, *group_id = NULL;
 		
-		group = GNOME_Development_Group__alloc ();
-		parent_group = (GNOME_Development_Group *) g_list_nth_data (data->groups, 0);
+		PARSER_ASSERT (data->state == PARSE_PROJECT ||
+			       data->state == PARSE_GROUP);
+
+		data->state = PARSE_GROUP;
 
 		/* Find the name */
 		while (attrs && *attrs != NULL) {
@@ -363,103 +450,313 @@
 			
 			val++;
 			if (!strcmp (*attrs, "name"))
-				group->name = CORBA_string_dup (*val);
+				group_name = g_strdup (*val);
+			else if (!strcmp (*attrs, "id"))
+				group_id = g_strdup (*val);
+			
 			attrs = ++val;
 		}
-		g_assert (group->name != NULL);
+		PARSER_ASSERT (group_name != NULL && group_id != NULL);
 
-		/* Compute the id */
+		group = g_new0 (GbfAmGroup, 1);
+		parent_group = (GbfAmGroup *) g_slist_nth_data (data->groups, 0);
+		group->name = group_name;
+		group->id = group_id;
+		
 		if (parent_group != NULL) {
-			g_snprintf (id, 1024, "%s%s", parent_group->name, group->name);
-			group->parent_id = CORBA_string_dup (parent_group->id);
+			group->parent = parent_group;
 		} else {
-			g_snprintf (id, 1024, "%s", group->name);
-			priv->parent_group = g_strdup (id);
-			group->parent_id = CORBA_string_dup ("");
+			priv->parent_group = group;
+			group->parent = NULL;
 		}
-		group->id = CORBA_string_dup (id);
 
 		/* Create group and target lists */
-		group->groups._maximum = MAX_GROUPS;
-		group->groups._length = 0;
-		group->groups._buffer = GNOME_Development_GroupList_allocbuf (MAX_GROUPS);
-		group->targets._maximum = MAX_TARGETS;
-		group->targets._length = 0;
-		group->targets._buffer = GNOME_Development_TargetList_allocbuf (MAX_TARGETS);
+		group->groups = NULL;
+		group->targets = NULL;
+		group->config = gbf_am_config_mapping_new ();
 		
 		/* Add this group to the parent */
-		if (parent_group != NULL && parent_group->groups._length < MAX_GROUPS) {
-			parent_group->groups._buffer[parent_group->groups._length] = CORBA_string_dup (id);
-			parent_group->groups._length++;
-		}
+		if (parent_group != NULL)
+			parent_group->groups = g_list_prepend (
+				parent_group->groups, group);
 		
 		/* Store the group */
-		g_hash_table_insert (priv->groups, g_strdup (id), group);
+		if (g_hash_table_lookup (priv->groups, group->id))
+			g_warning (_("Duplicate group id: %s"), group->id);
+		else
+			g_hash_table_insert (priv->groups, g_strdup (group->id), group);
 
 		/* Push the group on the stack */
-		data->groups = g_list_prepend (data->groups, group);
+		data->groups = g_slist_prepend (data->groups, group);
 
 	} else if (!strcmp (name, "target")) {
-		target = GNOME_Development_Target__alloc ();
-		group = (GNOME_Development_Group *) g_list_nth_data (data->groups, 0);
-		g_assert (group != NULL);
+		const gchar *id = NULL;
 		
+		group = (GbfAmGroup *) g_slist_nth_data (data->groups, 0);
+
+		PARSER_ASSERT (data->state == PARSE_GROUP);
+		g_assert (group != NULL && data->target == NULL);
+		
+		data->state = PARSE_TARGET;
+
+		target = g_new0 (GbfAmTarget, 1);
+
 		/* Find the name and type */
 		while (attrs && *attrs != NULL) {
 			const xmlChar **val = attrs;
 			
 			val++;
 			if (!strcmp (*attrs, "name"))
-				target->name = CORBA_string_dup (*val);
+				target->name = g_strdup (*val);
 			else if (!strcmp (*attrs, "type"))
-				target->type = CORBA_string_dup (*val);
+				target->type = g_strdup (*val);
+			else if (!strcmp (*attrs, "id"))
+				id = *val;
 
 			attrs = ++val;
 		}
+		
+		/* Compute the id using the given by the script if possible */
+		if (id)
+			target->id = g_strdup_printf ("%s%s", group->id, id);
+		else
+			target->id = g_strdup_printf ("%s%s", group->id, target->name);
 
-		/* Compute the id */
-		g_snprintf (id, 1024, "%s%s", group->id, target->name);
-		target->id = CORBA_string_dup (id);
-		target->group_id = CORBA_string_dup (group->id);
+		target->group = group;
 		
 		/* Create source list */
-		target->sources._maximum = MAX_SOURCES;
-		target->sources._length = 0;
-		target->sources._buffer = GNOME_Development_SourceList_allocbuf (MAX_SOURCES);
+		target->sources = NULL;
+		target->config = gbf_am_config_mapping_new ();
 
 		/* Add this target to the group */
-		if (group->targets._length < MAX_TARGETS) {
-			group->targets._buffer[group->targets._length] = CORBA_string_dup (id);
-			group->targets._length++;
-		}
+		group->targets = g_list_prepend (group->targets, target);
 		
 		/* Store the target */
-		g_hash_table_insert (priv->targets, g_strdup (id), target);
+		if (g_hash_table_lookup (priv->targets, target->id))
+			/* FIXME: we leak this target later */
+			g_warning (_("Duplicate target id: %s"), target->id);
+		else
+			g_hash_table_insert (priv->targets, g_strdup (target->id), target);
 
 		/* Make this the current target */
 		data->target = target;
+
 	} else if (!strcmp (name, "source")) {
-		source = GNOME_Development_TargetSource__alloc ();
 		target = data->target;
+
+		PARSER_ASSERT (data->state == PARSE_TARGET);
 		g_assert (target != NULL);
 		
+		data->state = PARSE_SOURCE;
+		
+		source = g_new0 (GbfAmTargetSource, 1);
 		/* Find the name and type */
 		while (attrs && *attrs != NULL) {
 			const xmlChar **val = attrs;
 			
 			val++;
-			if (!strcmp (*attrs, "uri"))
-				source->source = CORBA_string_dup (*val);
+			if (!strcmp (*attrs, "uri")) {
+				gchar *tmp_uri;
+				tmp_uri = g_build_filename (priv->project_dir,
+							    *val, NULL);
+				source->uri = g_strconcat ("file:///",
+							   g_path_skip_root (tmp_uri),
+							   NULL);
+				g_free (tmp_uri);
+			}
+			
+			attrs = ++val;
+		}
+		
+		/* Compute the id */
+		source->id = g_strdup_printf ("%s:%s", target->id, source->uri);
+		source->target = target;
+		source->type = SOURCE_FILE;
+		
+		/* Add this source to the target */
+		target->sources = g_list_prepend (target->sources, source);
+		
+		/* Store the source */
+		if (g_hash_table_lookup (priv->sources, source->id)) {
+			/* FIXME: we leak this source later, since it's not
+			   stored in the hash */
+			g_warning (_("Duplicate source id: %s"), source->id);
+		} else {
+			g_hash_table_insert (priv->sources, g_strdup (source->id), source);
+		}
+
+	} else if (!strcmp (name, "dependency")) {
+		source = g_new0 (GbfAmTargetSource, 1);
+		target = data->target;
+
+		PARSER_ASSERT (data->state == PARSE_TARGET);
+		g_assert (target != NULL);
+
+		data->state = PARSE_DEPENDENCY;
+		
+		/* Find the file */
+		while (attrs && *attrs != NULL) {
+			const xmlChar **val = attrs;
+			
+			val++;
+			if (!strcmp (*attrs, "file")) {
+				gchar *tmp_uri;
+				tmp_uri = g_build_filename (priv->project_dir,
+							    *val, NULL);
+				source->uri = g_strconcat ("file:///",
+							   g_path_skip_root (tmp_uri),
+							   NULL);
+				g_free (tmp_uri);
 
+			} else if (!strcmp (*attrs, "target"))
+				source->depname = g_strdup (*val);
+			
 			attrs = ++val;
 		}
 		
 		/* Compute the id */
-		g_snprintf (id, 1024, "%s%s", target->id, source->source);
-		source->id = CORBA_string_dup (id);
-		source->target_id = CORBA_string_dup (target->id);
+		source->id = g_strdup_printf ("%s:%s", target->id, source->uri);
+		source->target = target;
+		source->type = BUILT_SOURCE;
+		
+		/* Add this source to the target */
+		target->sources = g_list_prepend (target->sources, source);
+		
+		/* Store the source */
+		if (g_hash_table_lookup (priv->sources, source->id)) {
+			/* FIXME: we leak this source later, since it's not
+			   stored in the hash */
+			g_warning (_("Duplicate source id: %s"), source->id);
+		} else {
+			g_hash_table_insert (priv->sources, g_strdup (source->id), source);
+		}
+		
+	} else if (!strcmp (name, "config")) {
+		PARSER_ASSERT (data->state == PARSE_PROJECT ||
+			       data->state == PARSE_GROUP ||
+			       data->state == PARSE_TARGET);
+
+		switch (data->state) {
+		    case PARSE_PROJECT:
+			    data->config = data->prj->priv->config;
+			    break;
+		    case PARSE_GROUP:
+			    group = (GbfAmGroup *) g_slist_nth_data (data->groups, 0);
+			    g_assert (group != NULL);
+			    data->config = group->config;
+			    break;
+		    case PARSE_TARGET:
+			    g_assert (data->target != NULL);
+			    data->config = data->target->config;
+			    break;
+		    default:
+			    g_assert_not_reached ();
+			    break;
+		}
+		
+		data->config_saved_state = data->state;
+		data->state = PARSE_CONFIG;
+		
+	} else if (!strcmp (name, "param")) {
+		const gchar *param_name = NULL, *param_value = NULL;
+		
+		PARSER_ASSERT (data->state == PARSE_CONFIG);
+
+		/* Find the name and value */
+		while (attrs && *attrs != NULL) {
+			const xmlChar **val = attrs;
+			
+			val++;
+			if (!strcmp (*attrs, "name"))
+				param_name = *val;
+			else if (!strcmp (*attrs, "value"))
+				param_value = *val;
+			
+			attrs = ++val;
+		}
+		PARSER_ASSERT (name != NULL);
+
+		if (param_value != NULL) {
+			GbfAmConfigValue *param;
+
+			param = gbf_am_config_value_new (GBF_AM_TYPE_STRING);
+			gbf_am_config_value_set_string (param, param_value);
+
+			/* try to save the parameter in the config mapping */
+			if (!gbf_am_config_mapping_insert (data->config,
+							   param_name, param)) {
+				gbf_am_config_value_free (param);
+			}
+			
+			data->state = PARSE_PARAM_DONE;
+
+		} else {
+			data->param_key = g_strdup (param_name);
+			data->state = PARSE_PARAM;
 
-		source_store (prj, source);
+		}
+		
+	} else if (!strcmp (name, "item")) {
+		GbfAmConfigValue *param, *item;
+		const gchar *item_name = NULL, *item_value = NULL;
+
+		PARSER_ASSERT (data->state == PARSE_PARAM);
+		g_assert (data->param_key != NULL);
+
+		data->state = PARSE_ITEM;
+		
+		/* Find the name and value */
+		while (attrs && *attrs != NULL) {
+			const xmlChar **val = attrs;
+			
+			val++;
+			if (!strcmp (*attrs, "name"))
+				item_name = *val;
+			else if (!strcmp (*attrs, "value"))
+				item_value = *val;
+			
+			attrs = ++val;
+		}
+		PARSER_ASSERT (item_value != NULL);
+		
+		item = gbf_am_config_value_new (GBF_AM_TYPE_STRING);
+		gbf_am_config_value_set_string (item, item_value);
+		
+		param = gbf_am_config_mapping_lookup (data->config, data->param_key);
+		
+		/* if this is the first item, we must decide whether
+		   it's a list or a mapping.  if it's not the first
+		   one, we must check that the items are of the same
+		   type */
+		if (param == NULL) {
+			if (item_name != NULL) {
+				param = gbf_am_config_value_new (GBF_AM_TYPE_MAPPING);
+			} else {
+				param = gbf_am_config_value_new (GBF_AM_TYPE_LIST);
+			}
+			gbf_am_config_mapping_insert (data->config,
+						      data->param_key,
+						      param);
+		}
+		
+		switch (param->type) {
+		    case GBF_AM_TYPE_LIST:
+			    param->list = g_slist_prepend (param->list, item);
+			    break;
+			    
+		    case GBF_AM_TYPE_MAPPING:
+			    if (item_name == NULL ||
+				!gbf_am_config_mapping_insert (param->mapping,
+							       item_name,
+							       item)) {
+				    gbf_am_config_value_free (item);
+			    }
+			    break;
+			    
+		    default:
+			    g_assert_not_reached ();
+			    break;
+		}
 	}
 }
 
@@ -468,132 +765,582 @@
 {
 	GbfAmProjectParseData *data = ctx;
 
-	if (!strcmp (name, "group")) {
+	if (data->state == PARSE_ERROR || data->state == PARSE_DONE)
+		return;
+	
+	if (!strcmp (name, "project")) {
+		PARSER_ASSERT (data->state == PARSE_PROJECT);
+		data->state = PARSE_DONE;
+		
+	} else if (!strcmp (name, "group")) {
+		PARSER_ASSERT (data->state == PARSE_GROUP);
 		g_assert (data->groups != NULL);
-		data->groups = g_list_remove (data->groups, data->groups->data);
+		data->groups = g_slist_delete_link (data->groups, data->groups);
+		if (!data->groups)
+			data->state = PARSE_PROJECT;
+		
+		/* FIXME: resolve all target inter-dependencies here */
+
 	} else if (!strcmp (name, "target")) {
+		PARSER_ASSERT (data->state == PARSE_TARGET);
 		g_assert (data->target != NULL);
+		data->state = PARSE_GROUP;
 		data->target = NULL;
+
+	} else if (!strcmp (name, "source")) {
+		PARSER_ASSERT (data->state == PARSE_SOURCE);
+		data->state = PARSE_TARGET;
+
+	} else if (!strcmp (name, "dependency")) {
+		PARSER_ASSERT (data->state == PARSE_DEPENDENCY);
+		data->state = PARSE_TARGET;
+
+	} else if (!strcmp (name, "config")) {
+		PARSER_ASSERT (data->state == PARSE_CONFIG);
+		data->state = data->config_saved_state;
+		data->config_saved_state = PARSE_INITIAL;
+		data->config = NULL;
+		
+	} else if (!strcmp (name, "param")) {
+		PARSER_ASSERT (data->state == PARSE_PARAM ||
+			       data->state == PARSE_PARAM_DONE);
+		data->state = PARSE_CONFIG;
+		g_free (data->param_key);
+		data->param_key = NULL;
+		
+	} else if (!strcmp (name, "item")) {
+		PARSER_ASSERT (data->state == PARSE_ITEM);
+		data->state = PARSE_PARAM;
+
 	}
 }
 
+#undef PARSER_ASSERT
+
 static int
 process_project (GbfAmProject *prj, char *xml, int len)
 {
 	xmlSAXHandler handler;
 	GbfAmProjectParseData data;
+	int retval;
 	
 	memset (&handler, 0, sizeof (xmlSAXHandler));
 	handler.startElement = sax_start_element;
 	handler.endElement = sax_end_element;
 	handler.initialized = 0;
 	
+	data.state = PARSE_INITIAL;
+	data.config_saved_state = PARSE_INITIAL;
 	data.prj = prj;
 	data.groups = NULL;
 	data.target = NULL;
+	data.config = NULL;
+	data.param_key = NULL;
 	
 	/* Parse document */
-	return xmlSAXUserParseMemory (&handler, &data, xml, len);
+	retval = xmlSAXUserParseMemory (&handler, &data, xml, len);
+
+	if (data.state != PARSE_DONE)
+		retval = -1;
+
+	return retval;
 }
 
-static int
-get_project (GbfAmProject *prj, char *dir) 
+static char *
+make_script_path (const char *name)
 {
-	char *argv[] = { SCRIPT_NAME, "--get", dir, 0 };
-	int fd[2], t, len, res = 0;
-	char *script_path;
+	gchar *full_path;
+
+	full_path = g_build_filename (SCRIPTS_DIR, name, NULL);
+
+	return full_path;
+}
+
+static void
+shutdown_spawn (GbfAmSpawnData *data)
+{
+	g_return_if_fail (data != NULL);
 	
-	xmlSubstituteEntitiesDefault (TRUE);
+	if (data->child_pid)
+		kill (data->child_pid, SIGTERM);
 
-	pipe (fd);
+	/* input channel */
+	if (data->input.channel) {
+		g_io_channel_shutdown (data->input.channel, TRUE, NULL);
+		g_io_channel_unref (data->input.channel);
+		data->input.channel = NULL;
+	}
+	if (data->input_source) {
+		g_source_remove (data->input_source);
+		data->input_source = 0;
+	}
 
-	t = fork ();
-	if (t < 0) {
-		g_error ("Unable to fork.");
-	} else if (t) {
-		/* Parent */
-		char *xml;
+	/* output channel */
+	if (data->output.buffer) {
+		/* shrink buffer and add terminator */
+		data->output.buffer = g_realloc (data->output.buffer,
+						 ++data->output.length);
+		data->output.buffer [data->output.length - 1] = 0;
+	}
+	if (data->output.channel) {
+		g_io_channel_shutdown (data->output.channel, FALSE, NULL);
+		g_io_channel_unref (data->output.channel);
+		data->output.channel = NULL;
+	}
+	if (data->output_source) {
+		g_source_remove (data->output_source);
+		data->output_source = 0;
+	}
 
-		/* Close writing end */
-		close (fd[1]);	
+	/* error channel */
+	if (data->error.buffer) {
+		/* shrink buffer and add terminator */
+		data->error.buffer = g_realloc (data->error.buffer,
+						++data->error.length);
+		data->error.buffer [data->error.length - 1] = 0;
+	}
+	if (data->error.channel) {
+		g_io_channel_shutdown (data->error.channel, FALSE, NULL);
+		g_io_channel_unref (data->error.channel);
+		data->error.channel = NULL;
+	}
+	if (data->error_source) {
+		g_source_remove (data->error_source);
+		data->error_source = 0;
+	}
+
+	if (data->main_loop)
+		g_main_loop_quit (data->main_loop);
+}
+
+static void
+spawn_data_destroy (GbfAmSpawnData *data)
+{
+	g_return_if_fail (data != NULL);
+
+	shutdown_spawn (data);
+
+	if (data->input.buffer) {
+		/* input buffer is provided by the user, so it's not freed here */
+		data->input.buffer = NULL;
+		data->input.size = 0;
+		data->input.length = 0;
+	}
+	if (data->output.buffer) {
+		g_free (data->output.buffer);
+		data->output.buffer = NULL;
+		data->output.size = 0;
+		data->output.length = 0;
+	}
+	if (data->error.buffer) {
+		g_free (data->error.buffer);
+		data->error.buffer = NULL;
+		data->error.size = 0;
+		data->error.length = 0;
+	}
+	g_free (data);
+}
+
+static gboolean
+check_child (GbfAmSpawnData *data)
+{
+	if (waitpid (data->child_pid, &data->child_status, WNOHANG) != data->child_pid) {
+		data->ticks++;
+		if (data->ticks > data->max_ticks) {
+			/* reset timeout so we don't kill every 100 msec */
+			data->ticks = 0;
+			g_warning ("Timeout... sending TERM signal to child");
+			kill (data->child_pid, SIGTERM);
+		}
+		return TRUE;
+	}
+
+	data->child_pid = 0;
+	g_main_loop_quit (data->main_loop);
+	
+	return FALSE;
+}
+
+static gboolean
+write_child (GIOChannel *ioc, GIOCondition condition, gpointer user_data)
+{
+	GbfAmSpawnData *data = user_data;
+	gboolean retval = FALSE;
+	
+	if (condition == G_IO_HUP || condition == G_IO_NVAL)
+		return FALSE;
+
+	/* reset timeout */
+	data->ticks = 0;
+	if (condition == G_IO_OUT) {
+		gsize bytes_written;
+		GIOStatus status;
+		GError *error = NULL;
+
+		/* try to write all data remaining */
+		status = g_io_channel_write_chars (ioc, data->input.buffer
+						   + data->input.length,
+						   data->input.size
+						   - data->input.length,
+						   &bytes_written, &error);
+
+		switch (status) {
+		    case G_IO_STATUS_NORMAL:
+			    g_message ("wrote %d bytes", bytes_written);
+			    
+			    data->input.length += bytes_written;
+			    if (data->input.length < data->input.size) {
+				    retval = TRUE;
+			    } else {
+				    /* finished writing */
+				    retval = FALSE;
+			    }
+			    break;
+
+		    default:
+			    if (error) {
+				    g_warning ("Error while writing to stdin: %s",
+					       error->message);
+				    g_error_free (error);
+			    }
+
+			    retval = FALSE;
+			    break;
+		}
+	}
+
+	if (!retval) {
+		/* no more data to write or some error ocurred */
+		g_io_channel_shutdown (data->input.channel, TRUE, NULL);
+		g_io_channel_unref (data->input.channel);
+		data->input.channel = NULL;
+		/* returning false will remove the source */
+		data->input_source = 0;
+	}
+			    
+	return retval;
+}
+
+static gboolean
+read_output (GIOChannel *ioc, GIOCondition condition, gpointer user_data)
+{
+	GbfAmSpawnData *data = user_data;
+	gboolean retval = FALSE;
+	
+	if (condition == G_IO_HUP || condition == G_IO_NVAL)
+		return FALSE;
+	
+	/* reset timeout */
+	data->ticks = 0;
+	if (condition == G_IO_IN || condition == G_IO_PRI) {
+		gsize bytes_read;
+		GIOStatus status;
+		GError *error = NULL;
 		
-		/* Read document */
-		xml = malloc (102400);
-		fcntl(fd[0], F_SETFL, 0);  
-		for (len = 0; (t = read (fd[0], xml + len, 102399 - len)); )
-			len += t;
+		if (data->output.buffer == NULL) {
+			data->output.size = READ_BUFFER_SIZE;
+			data->output.buffer = g_malloc (data->output.size);
+			data->output.length = 0;
+		}
+
+		status = g_io_channel_read_chars (ioc, data->output.buffer
+						  + data->output.length,
+						  data->output.size
+						  - data->output.length,
+						  &bytes_read, &error);
+		switch (status) {
+		    case G_IO_STATUS_NORMAL:
+			    g_io_channel_flush (ioc, NULL);
+			    
+			    data->output.length += bytes_read;
+			    /* grow buffer if necessary */
+			    if (data->output.size - data->output.length
+				< READ_BUFFER_DELTA) {
+				    data->output.size += READ_BUFFER_DELTA;
+				    data->output.buffer = g_realloc (
+					    data->output.buffer, data->output.size);
+			    }
+			    retval = TRUE;
+			    break;
+			    
+		    case G_IO_STATUS_EOF:
+			    retval = FALSE;
+			    break;
+			    
+		    default:
+			    if (error) {
+				    g_warning ("Error while reading stdout: %s",
+					       error->message);
+				    g_error_free (error);
+			    }
 
-		if (len < 1 || len == 102399) {
-			free (xml);
-			g_warning ("Empty project returned");
-			return -1;
+			    retval = FALSE;
+			    break;
 		}
+	}
+	
+	if (!retval) {
+		/* eof was reached or some error ocurred */
+		g_io_channel_shutdown (data->error.channel, FALSE, NULL);
+		g_io_channel_unref (data->error.channel);
+		data->output.channel = NULL;
+		/* returning false will remove the source */
+		data->output_source = 0;
+	}
+			    
+	return retval;
+}
 
-		xml = realloc (xml, len + 1);
-		*(xml + len) = 0;
+static gboolean
+read_error (GIOChannel *ioc, GIOCondition condition, gpointer user_data)
+{
+	GbfAmSpawnData *data = user_data;
+	gboolean retval = FALSE;
+	
+	if (condition == G_IO_HUP || condition == G_IO_NVAL)
+		return FALSE;
+	
+	/* reset timeout */
+	data->ticks = 0;
+	if (condition == G_IO_IN || condition == G_IO_PRI) {
+		gsize bytes_read;
+		GIOStatus status;
+		GError *error = NULL;
+		
+		if (data->error.buffer == NULL) {
+			data->error.size = READ_BUFFER_SIZE;
+			data->error.buffer = g_malloc (data->error.size);
+			data->error.length = 0;
+		}
 
-		res = process_project (prj, xml, len + 1);
+		status = g_io_channel_read_chars (ioc, data->error.buffer
+						  + data->error.length,
+						  data->error.size
+						  - data->error.length,
+						  &bytes_read, &error);
+		switch (status) {
+		    case G_IO_STATUS_NORMAL:
+			    data->error.length += bytes_read;
+			    /* grow buffer if necessary */
+			    if (data->error.size - data->error.length
+				< READ_BUFFER_DELTA) {
+				    data->error.size += READ_BUFFER_DELTA;
+				    data->error.buffer = g_realloc (
+					    data->error.buffer, data->error.size);
+			    }
+			    retval = TRUE;
+			    
+			    break;
+			    
+		    case G_IO_STATUS_EOF:
+			    retval = FALSE;
+			    break;
+			    
+		    default:
+			    if (error) {
+				    g_warning ("Error while reading stderr: %s",
+					       error->message);
+				    g_error_free (error);
+			    }
+
+			    retval = FALSE;
+			    break;
+		}
+	}
+	
+	if (!retval) {
+		/* eof was reached or some error ocurred */
+		g_io_channel_shutdown (data->error.channel, FALSE, NULL);
+		g_io_channel_unref (data->error.channel);
+		data->error.channel = NULL;
+		/* returning false will remove the source */
+		data->error_source = 0;
+	}
+			    
+	return retval;
+}
+
+static GbfAmSpawnData * 
+spawn_script (gchar  **argv,
+	      gint     timeout,
+	      gchar   *input,
+	      gint     input_size,
+	      GIOFunc  input_cb,
+	      GIOFunc  output_cb,
+	      GIOFunc  error_cb)
+{
+	GbfAmSpawnData *data;
+	gint child_in, child_out, child_err;
+	GError *error;
+	gboolean async;
+	
+	data = g_new0 (GbfAmSpawnData, 1);
+
+	async = (timeout <= 0);
+
+	/* setup default callbacks */
+	if (input_cb == NULL) input_cb = write_child;
+	if (output_cb == NULL) output_cb = read_output;
+	if (error_cb == NULL) error_cb = read_error;
+	
+	/* set input buffer */
+	if (input) {
+		data->input.buffer = input;
+		data->input.size = input_size;
+		data->input.length = 0;  /* for input buffer length acts as an index */
+	}
+
+	/* set timeout */
+	data->ticks = 0;
+	data->max_ticks = timeout * 1000 / CHECK_TIMEOUT;
+	
+	/* crap!
+	   FIXME: putenv() is not portable */
+	putenv ("_MEMPROF_SOCKET=");
+	
+	g_message ("Spawning script");
+	
+	if (!g_spawn_async_with_pipes (NULL, argv, NULL,
+				       G_SPAWN_DO_NOT_REAP_CHILD,
+				       NULL, NULL,
+				       &data->child_pid,
+				       &child_in, &child_out, &child_err,
+				       &error)) {
+		g_warning ("Unable to fork: %s", error->message);
+		g_error_free (error);
+		g_free (data);
+		return NULL;
 
-		free (xml);
-		close (fd[0]);
 	} else {
-		/* Child */
+		if (!async) {
+			data->main_loop = g_main_loop_new (NULL, FALSE);
+		}
 
-		close (fd[0]);	/* Close reading end */
-		dup2 (fd[1], STDOUT_FILENO);
+		fcntl (child_in, F_SETFL, O_NONBLOCK);
+		fcntl (child_out, F_SETFL, O_NONBLOCK);
+		fcntl (child_err, F_SETFL, O_NONBLOCK);
+
+		if (input != NULL && input_size > 0) {
+			data->input.channel = g_io_channel_unix_new (child_in);
+			data->input_source = g_io_add_watch (data->input.channel,
+							     G_IO_OUT | G_IO_ERR |
+							     G_IO_HUP | G_IO_NVAL,
+							     input_cb, data);
+		} else {
+			/* we are not interested in stdin */
+			close (child_in);
+		}
+		
+		/* create watches for stdout and stderr */
+		data->output.channel = g_io_channel_unix_new (child_out);
+		data->output_source = g_io_add_watch (data->output.channel,
+						      G_IO_ERR | G_IO_HUP | G_IO_NVAL |
+						      G_IO_IN, output_cb, data);
+		data->error.channel = g_io_channel_unix_new (child_err);
+		data->error_source = g_io_add_watch (data->error.channel,
+						     G_IO_ERR | G_IO_HUP | G_IO_NVAL |
+						     G_IO_IN, error_cb, data);
+		
+		if (!async) {
+			/* for asynchronous spawn we must provide other means of
+			   checking for SIGCHLD and calling waitpid() */
+			g_timeout_add (CHECK_TIMEOUT, (GSourceFunc) check_child, data);
+		
+			g_main_loop_run (data->main_loop);
+			
+			/* close channels and remove io watches */
+			shutdown_spawn (data);
 
-		script_path = make_script_path (argv[0]);
-		execve (script_path, argv, __environ);
-		g_error ("Unable to run backend: %s", script_path);
+			/* destroy main loop */
+			g_main_loop_unref (data->main_loop);
+			data->main_loop = NULL;
+		}
+
+		return data;
 	}
-	
-	return res;
 }
 
 static int
-update_project (xmlDocPtr doc, char *dir) 
+get_project (GbfAmProject *prj, char *dir) 
 {
-	char *argv[] = { SCRIPT_NAME, "--modify", dir, 0 };
-	int fd[2];
-	FILE *f;
-	int pid;
-	char *script_path;
+	GbfAmSpawnData *data;
+	gchar  *argv [] = { SCRIPT_NAME, "-d", "--get", dir, 0 };
+	gint    retval;
 	
 	xmlSubstituteEntitiesDefault (TRUE);
 
-	pipe (fd);
+	argv [0] = make_script_path (argv [0]);
+	
+	data = spawn_script (argv, 30, NULL, 0, NULL, NULL, NULL);
 
-	pid = fork ();
-	if (pid < 0) {
-		g_error ("Unable to fork.");
-	} else if (pid) {
-		/* Parent */
-		int status;
-		
-		close (fd [0]);	/* Close reading end of pipe */
+	/* free the full script path returned from make_script_path */
+	g_free (argv [0]);
+
+	if (!data) 
+		return -1;
 
-		f = fdopen (fd [1], "w");
-		xmlDocDump (f, doc);
-		fclose (f);
+	retval = data->child_status;
+	if (data->child_status == 0) {
+		/* FIXME: process the errors!! */
+		if (data->error.length > 0) {
+			puts (data->error.buffer);
+		}
 		
-		waitpid (pid, &status, 0);
-		if (WIFEXITED (status))
-			return WEXITSTATUS (status);
-	} else {
-		/* Child */
+		/* eveything ok... process */
+		if (data->output.length > 0) {
+			retval = process_project (prj, data->output.buffer,
+						  data->output.length);
+		} else {
+			g_warning ("Child process returned no data");
+			retval = -1;
+		}
+	}
 
-		close (fd[1]);	/* Close reading end */
-		dup2 (fd[0], STDIN_FILENO);
+	if (data)
+		spawn_data_destroy (data);
+	
+	return retval;
+}
 
+static int
+update_project (xmlDocPtr doc, char *dir) 
+{
+	GbfAmSpawnData *data;
+	char *argv[] = { SCRIPT_NAME, "-d", "--set", "-", 0 };
+	xmlChar *xml_doc;
+	int xml_size;
+	int retval = -1;
+	
+	xmlSubstituteEntitiesDefault (TRUE);
 
-		script_path = make_script_path (argv[0]);
-		execve (script_path, argv, __environ);
-		g_error ("Unable to run backend: %s", script_path);
-	}
+	/* dump the document to memory */
+	xmlDocDumpMemory (doc, &xml_doc, &xml_size);
 	
-	return -1;
+	/* execute the script */
+	argv [0] = make_script_path (argv [0]);
+	data = spawn_script (argv, 30, xml_doc, xml_size, NULL, NULL, NULL);
+	g_free (argv [0]);
+	
+	xmlFree (xml_doc);
+	
+	if (data) {
+		/* FIXME: process the errors!! */
+		if (data->error.length > 0) {
+			puts (data->error.buffer);
+		}
+		
+		retval = data->child_status;
+		spawn_data_destroy (data);
+	}
+
+	return retval;
 }
 
+
+/*
+ * Build functions ----------------------------------------
+ */
+
 static gboolean
 parse_error_buffer (GbfAmProject *prj)
 {
@@ -782,6 +1529,121 @@
 	bonobo_arg_release (arg);
 }
 
+/*
+ * ---------------- Data structures managment
+ */
+
+static void
+group_free (GbfAmGroup *group)
+{
+	g_return_if_fail (group != NULL);
+
+	g_free (group->id);
+	g_free (group->name);
+
+	g_list_free (group->groups);
+	g_list_free (group->targets);
+	gbf_am_config_mapping_destroy (group->config);
+
+	g_free (group);
+}
+
+static void
+target_free (GbfAmTarget *target)
+{
+	g_return_if_fail (target != NULL);
+
+	g_free (target->id);
+	g_free (target->name);
+	g_free (target->type);
+
+	g_list_free (target->sources);
+	gbf_am_config_mapping_destroy (target->config);
+
+	g_free (target);
+}
+
+static void
+source_free (GbfAmTargetSource *source)
+{
+	g_return_if_fail (source != NULL);
+
+	g_free (source->id);
+	g_free (source->uri);
+	g_free (source->depname);
+	
+	g_free (source);
+}
+
+static void
+project_init (GbfAmProject *prj)
+{
+	GbfAmProjectPrivate *priv;
+
+	g_return_if_fail (prj != NULL);
+	
+	priv = prj->priv;
+	priv->parent_group = NULL;
+	priv->config = gbf_am_config_mapping_new ();
+	
+	priv->groups = g_hash_table_new_full (g_str_hash, g_str_equal,
+					      g_free, (GDestroyNotify) group_free);
+	priv->targets = g_hash_table_new_full (g_str_hash, g_str_equal,
+					       g_free, (GDestroyNotify) target_free);
+	priv->sources = g_hash_table_new_full (g_str_hash, g_str_equal,
+					       g_free, (GDestroyNotify) source_free);
+	
+	/* setup queue */
+	priv->ops = NULL;
+	priv->op_handler = -1;
+}
+
+/* free all project associated data */
+static void
+project_unload (GbfAmProject *prj)
+{
+	GbfAmProjectPrivate *priv;
+
+	g_return_if_fail (prj != NULL);
+	
+	priv = prj->priv;
+	if (priv->project_dir == NULL) 
+		/* FIXME: any better way to determine if the project has data? */
+		return;
+
+	priv->parent_group = NULL;
+	g_free (priv->project_dir);
+	priv->project_dir = NULL;
+	
+	if (priv->config) {
+		gbf_am_config_mapping_destroy (priv->config);
+		priv->config = NULL;
+	}
+	
+	if (priv->groups) {
+		g_hash_table_destroy (priv->groups);
+		priv->groups = NULL;
+	}
+	
+	if (priv->targets) {
+		g_hash_table_destroy (priv->targets);
+		priv->targets = NULL;
+	}
+	
+	if (priv->sources) {
+		g_hash_table_destroy (priv->sources);
+		priv->sources = NULL;
+	}
+
+	g_message ("project unloaded");
+	
+	/* FIXME: cleanup the ops queue */
+}
+
+/*
+ * IDL methods ------------------------------------------
+ */
+
 static void
 impl_load (PortableServer_Servant servant, 
 	   const CORBA_char *path,
@@ -794,6 +1656,12 @@
 	bonobo_return_if_fail (prj != NULL, ev);
 	priv = prj->priv;
 	
+	if (priv->project_dir) {
+		/* FIXME: do we really want to allow object reutilization */
+		project_unload (prj);
+		project_init (prj);
+	}
+	
 	if (!g_file_test (path, G_FILE_TEST_IS_DIR)) {		
 		bonobo_exception_set (ev, ex_GNOME_Development_Project_DoesntExist);
 		return;
@@ -829,17 +1697,55 @@
 {
 	GbfAmProject *prj;
 	GbfAmProjectPrivate *priv;
-	GNOME_Development_Group *group;
+	GNOME_Development_Group *corba_group;
+	GbfAmGroup *group;
+	GList *lp;
+	gint i;
 	
 	prj = GBF_AM_PROJECT (bonobo_object_from_servant (servant));
 	bonobo_return_val_if_fail (prj != NULL, NULL, ev);
 	priv = prj->priv;
 
 	group = g_hash_table_lookup (priv->groups, id);
-	if (group == NULL)
+	if (group == NULL) {
 		bonobo_exception_set (ev, ex_GNOME_Development_Project_DoesntExist);
-		
-	return ORBit_copy_value (group, TC_GNOME_Development_Group);
+		return CORBA_OBJECT_NIL;
+	}
+
+	/* create CORBA group structure */
+	corba_group = GNOME_Development_Group__alloc ();
+	
+	corba_group->id = CORBA_string_dup (group->id);
+	if (group->parent)
+		corba_group->parent_id = CORBA_string_dup (group->parent->id);
+	else
+		corba_group->parent_id = CORBA_string_dup (EMPTY_STRING);
+	
+	corba_group->name = CORBA_string_dup (group->name);
+
+	/* build children groups list */
+	corba_group->groups._maximum = corba_group->groups._length =
+		g_list_length (group->groups);
+	corba_group->groups._buffer = GNOME_Development_TargetList_allocbuf (
+		corba_group->groups._maximum);
+	corba_group->groups._release = CORBA_TRUE;
+	
+	for (lp = group->groups, i = 0; lp; lp = lp->next, i++)
+		corba_group->groups._buffer [i] =
+			CORBA_string_dup (((GbfAmGroup *) lp->data)->id);
+	
+	/* build targets list */
+	corba_group->targets._maximum = corba_group->targets._length =
+		g_list_length (group->targets);
+	corba_group->targets._buffer = GNOME_Development_TargetList_allocbuf (
+		corba_group->targets._maximum);
+	corba_group->targets._release = CORBA_TRUE;
+
+	for (lp = group->targets, i = 0; lp; lp = lp->next, i++)
+		corba_group->targets._buffer [i] =
+			CORBA_string_dup (((GbfAmTarget *) lp->data)->id);
+
+	return corba_group;
 }
 
 static Bonobo_Control
@@ -899,22 +1805,47 @@
 
 static GNOME_Development_Target *
 impl_get_target (PortableServer_Servant servant,
-		const CORBA_char *id,
-		CORBA_Environment *ev)
+		 const CORBA_char *id,
+		 CORBA_Environment *ev)
 {
 	GbfAmProject *prj;
 	GbfAmProjectPrivate *priv;
-	GNOME_Development_Target *target;
+	GNOME_Development_Target *corba_target;
+	GbfAmTarget *target;
+	GList *lp;
+	gint i;
 	
 	prj = GBF_AM_PROJECT (bonobo_object_from_servant (servant));
 	bonobo_return_val_if_fail (prj != NULL, NULL, ev);
 	priv = prj->priv;
 
 	target = g_hash_table_lookup (priv->targets, id);
-	if (target == NULL)
+	if (target == NULL) {
 		bonobo_exception_set (ev, ex_GNOME_Development_Project_DoesntExist);
+		return CORBA_OBJECT_NIL;
+	}
 		
-	return ORBit_copy_value (target, TC_GNOME_Development_Target);
+	corba_target = GNOME_Development_Target__alloc ();
+	
+	corba_target->id = CORBA_string_dup (target->id);
+	if (target->group)
+		corba_target->group_id = CORBA_string_dup (target->group->id);
+	else
+		corba_target->group_id = CORBA_string_dup (EMPTY_STRING);
+	
+	corba_target->name = CORBA_string_dup (target->name);
+	corba_target->type = CORBA_string_dup (target->type);
+	corba_target->sources._maximum = corba_target->sources._length =
+		g_list_length (target->sources);
+	corba_target->sources._buffer = GNOME_Development_TargetList_allocbuf (
+		corba_target->sources._maximum);
+	corba_target->sources._release = CORBA_TRUE;
+	
+	for (lp = target->sources, i = 0; lp; lp = lp->next, i++)
+		corba_target->sources._buffer [i] =
+			CORBA_string_dup (((GbfAmTargetSource *) lp->data)->id);
+	
+	return corba_target;
 }
 
 static void
@@ -941,9 +1872,11 @@
 	priv = prj->priv;
 
 	target_list = GNOME_Development_TargetList__alloc ();
+	
 	target_list->_maximum = g_hash_table_size (priv->targets);
 	target_list->_length = 0;
 	target_list->_buffer = GNOME_Development_TargetList_allocbuf (target_list->_maximum);
+	CORBA_sequence_set_release (target_list, CORBA_TRUE);
 	
 	g_hash_table_foreach (priv->targets, foreach_target, target_list);
 
@@ -1054,7 +1987,8 @@
 	icon->rgbaData._length = icon->height * total_width;
 	icon->rgbaData._maximum = icon->rgbaData._length;
 	icon->rgbaData._buffer = CORBA_sequence_CORBA_octet_allocbuf (icon->rgbaData._maximum);
-
+	icon->rgbaData._release = CORBA_TRUE;
+	
 	sp = gdk_pixbuf_get_pixels (pixbuf);
 	dp = icon->rgbaData._buffer;
 	for (i = 0; i < height; i ++) {
@@ -1097,17 +2031,30 @@
 {
 	GbfAmProject *prj;
 	GbfAmProjectPrivate *priv;
-	GNOME_Development_TargetSource *source;
+	GNOME_Development_TargetSource *corba_source;
+	GbfAmTargetSource *source;
 	
 	prj = GBF_AM_PROJECT (bonobo_object_from_servant (servant));
 	bonobo_return_val_if_fail (prj != NULL, NULL, ev);
 	priv = prj->priv;
 
 	source = g_hash_table_lookup (priv->sources, id);
-	if (source == NULL)
+	if (source == NULL) {
 		bonobo_exception_set (ev, ex_GNOME_Development_Project_DoesntExist);
-		
-	return ORBit_copy_value (source, TC_GNOME_Development_TargetSource);
+		return CORBA_OBJECT_NIL;
+	}
+	
+	corba_source = GNOME_Development_TargetSource__alloc ();
+	
+	corba_source->id = CORBA_string_dup (source->id);
+	if (source->target)
+		corba_source->target_id = CORBA_string_dup (source->target->id);
+	else
+		corba_source->target_id = CORBA_string_dup (EMPTY_STRING);
+	
+	corba_source->source = CORBA_string_dup (source->uri);
+	
+	return corba_source;
 }
 
 static void
@@ -1116,7 +2063,7 @@
 	GNOME_Development_SourceList *source_list;
 
 	source_list = data;
-	source_list->_buffer[source_list->_length] = CORBA_string_dup (key);
+	source_list->_buffer [source_list->_length] = CORBA_string_dup (key);
 	source_list->_length++;
 }
 
@@ -1131,11 +2078,14 @@
 	prj = GBF_AM_PROJECT (bonobo_object_from_servant (servant));
 	bonobo_return_val_if_fail (prj != NULL, NULL, ev);
 	priv = prj->priv;
-
+	
 	source_list = GNOME_Development_SourceList__alloc ();
+	
 	source_list->_maximum = g_hash_table_size (priv->sources);
 	source_list->_length = 0;
-	source_list->_buffer = GNOME_Development_SourceList_allocbuf (source_list->_maximum);
+	source_list->_buffer = GNOME_Development_SourceList_allocbuf (
+		source_list->_maximum);
+	CORBA_sequence_set_release (source_list, CORBA_TRUE);
 	
 	g_hash_table_foreach (priv->sources, foreach_source, source_list);
 
@@ -1165,6 +2115,17 @@
 	return CORBA_OBJECT_NIL;
 }
 
+/**
+ * impl_add_source:
+ * @servant: 
+ * @target_id: the target ID to where to add the source
+ * @uri: the FULL uri to the file.
+ * @ev: 
+ * 
+ * Add source implementation.  The uri must have the project root as its parent.
+ * 
+ * Return value: 
+ **/
 static CORBA_char *
 impl_add_source (PortableServer_Servant servant,
 		 const CORBA_char *target_id,
@@ -1173,7 +2134,7 @@
 {
 	GbfAmProject *prj;
 	GbfAmProjectPrivate *priv;
-	GNOME_Development_TargetSource *source;
+	GbfAmTargetSource *source;
 	xmlDocPtr doc;
 	char id[1024];
 	
@@ -1181,14 +2142,21 @@
 	bonobo_return_val_if_fail (prj != NULL, NULL, ev);
 	priv = prj->priv;
 
+	/* FIXME: check that the source is inside the project dir */
+	
+	/* --- FIXME: this should not be done here.  Instead we should
+	   read the output of the script for changes in the group */
+
 	/* Create a new source */
-	source = GNOME_Development_TargetSource__alloc ();
-	source->source = CORBA_string_dup (uri);
+	source = g_new0 (GbfAmTargetSource, 1);
+	source->uri = g_strdup (uri);
 
 	/* Compute the id */
-	g_snprintf (id, 1024, "%s%s", target_id, source->source);
-	source->id = CORBA_string_dup (id);
-	source->target_id = CORBA_string_dup (target_id);
+	g_snprintf (id, 1024, "%s%s", target_id, source->uri);
+	source->id = g_strdup (id);
+	source->target = g_hash_table_lookup (priv->targets, target_id);
+	
+	/* --- */
 	
 	/* Create the update xml */
 	doc = create_change_xml (prj);
@@ -1207,8 +2175,6 @@
 		return NULL;
 	}
 	
-	source_store (prj, source);
-
 	return CORBA_string_dup (source->id);
 }
 
@@ -1219,7 +2185,7 @@
 {
 	GbfAmProject *prj;
 	GbfAmProjectPrivate *priv;
-	GNOME_Development_TargetSource *source;
+	GbfAmTargetSource *source;
 	xmlDocPtr doc;
 	
 	prj = GBF_AM_PROJECT (bonobo_object_from_servant (servant));
@@ -1248,8 +2214,27 @@
 	/* Update the project */
 	if (update_project (doc, priv->project_dir) != 0)
 		bonobo_exception_set (ev, ex_GNOME_Development_Project_Malformed);
-	else
-		source_remove (prj, source);
+}
+
+static void
+gbf_am_project_dispose (GObject *object)
+{
+	GbfAmProject        *prj;
+	GbfAmProjectPrivate *priv;
+	
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GBF_IS_AM_PROJECT (object));
+
+	prj = GBF_AM_PROJECT (object);
+	project_unload (prj);
+
+	priv = prj->priv;
+	if (priv->buffer) {
+		g_free (priv->buffer);
+		priv->buffer = NULL;
+	}
+	
+	BONOBO_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
 }
 
 static void
@@ -1261,6 +2246,8 @@
 	object_class = G_OBJECT_CLASS (klass);
 	parent_class = gtk_type_class (PARENT_TYPE);
 
+	object_class->dispose = gbf_am_project_dispose;
+	
 	epv->load = impl_load;
 	epv->build = impl_build;
 	epv->getGroup = impl_get_group;
@@ -1293,14 +2280,9 @@
 
 	prj->priv = priv;
 
-	priv->parent_group = NULL;
-	priv->groups = g_hash_table_new (g_str_hash, g_str_equal);
-	priv->targets = g_hash_table_new (g_str_hash, g_str_equal);
-	priv->sources = g_hash_table_new (g_str_hash, g_str_equal);
-	
-	priv->ops = NULL;
-	priv->op_handler = -1;
+	project_init (prj);
 
+	/* setup aggregate interfaces */
 	priv->event_source = bonobo_event_source_new ();
 	bonobo_object_add_interface (BONOBO_OBJECT (prj), 
 				     BONOBO_OBJECT (priv->event_source));
@@ -1313,9 +2295,12 @@
 				 NULL, "The top-level project directory", 0);
 	bonobo_object_add_interface (BONOBO_OBJECT (prj), 
 				     BONOBO_OBJECT (priv->property_bag));
-
 }
 
+/*
+ * Property bag methods -----------------------------
+ */
+
 static void
 gbf_am_project_get_prop (BonoboPropertyBag *bag, BonoboArg *arg,
 			 guint arg_id, CORBA_Environment *ev,
@@ -1358,5 +2343,7 @@
 GbfAmProject *
 gbf_am_project_new (void)
 {
-    return GBF_AM_PROJECT (g_object_new (GBF_TYPE_AM_PROJECT, NULL));
+	g_message ("Creating project object");
+	
+	return GBF_AM_PROJECT (g_object_new (GBF_TYPE_AM_PROJECT, NULL));
 }
Index: src/backends/libgbf_am/main.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/backends/libgbf_am/main.c,v
retrieving revision 1.7
diff -u -r1.7 main.c
--- src/backends/libgbf_am/main.c	22 Feb 2002 22:37:18 -0000	1.7
+++ src/backends/libgbf_am/main.c	28 Jun 2002 04:59:54 -0000
@@ -8,35 +8,44 @@
 #include <errno.h>
 
 
-static void
-sigchld_handler (int signum)
-{
-    while (1) {
-	int pid = waitpid (WAIT_ANY, NULL, WNOHANG);
-	if (pid < 0 && errno != ECHILD)
-	    g_error ("waitpid: %s\n", g_strerror (errno));
-	else if (pid <= 0)
-	    break;
-    }
-}
-
-
 static BonoboObject *
 project_factory (BonoboGenericFactory *this_factory,
 		 const char *iid, gpointer data)
 {
-    static gboolean handler_installed = FALSE;
-
-    if (!handler_installed) {
-	/* Kill the zombies */
-	signal (SIGCHLD, sigchld_handler);
-	handler_installed = TRUE;
-    }
-
     return BONOBO_OBJECT (gbf_am_project_new ());
 }
 
-BONOBO_ACTIVATION_FACTORY ("OAFIID:GNOME_Development_Project_AutoMake_Factory",
-			   "Sample Echo component factory", "1.0",
+#define FACTORY_IID  "OAFIID:GNOME_Development_Project_AutoMake_Factory"
+
+#if 0
+BONOBO_ACTIVATION_FACTORY (FACTORY_IID,
+			   "GBF Automake project factory", "1.0",
 			   project_factory, NULL);
+#endif
+
+int main (int argc, char *argv [])
+{
+    BonoboGenericFactory *factory;
+    
+    if (!bonobo_init (&argc, argv))
+	g_error (_("Could not initialize Bonobo"));
+
+    factory = bonobo_generic_factory_new (FACTORY_IID, project_factory, NULL);
+    
+    if (factory) {
+	bonobo_running_context_auto_exit_unref (
+	    BONOBO_OBJECT (factory));
+	
+	g_message ("Ready");
+	
+	bonobo_main ();
+
+	sleep (1);
+/*  	while (CORBA_ORB_work_pending (bonobo_orb (), NULL)) */
+/*  	       CORBA_ORB_perform_work (bonobo_orb (), NULL); */
+	
+	return bonobo_debug_shutdown ();
+    } else
+	return 1;
+}
 
Index: src/backends/libgbf_am/test.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/backends/libgbf_am/test.c,v
retrieving revision 1.3
diff -u -r1.3 test.c
--- src/backends/libgbf_am/test.c	14 Mar 2002 07:52:14 -0000	1.3
+++ src/backends/libgbf_am/test.c	28 Jun 2002 04:59:55 -0000
@@ -45,6 +45,32 @@
 	return (GNOME_Development_Project) object;
 }
 
+static void
+print_group (GNOME_Development_Project project,
+	     gint                      depth,
+	     CORBA_char               *group_id)
+{
+	GNOME_Development_Group *group;
+	CORBA_Environment        ev;
+	int                      i;
+	
+	CORBA_exception_init (&ev);
+
+	group = GNOME_Development_Project_getGroup (project, group_id, &ev);
+	if (!BONOBO_EX (&ev)) {
+		for (i = 0; i < depth; i++)
+			g_print (" ");
+
+		g_print ("%s\n", group->name);
+		for (i = 0; i < group->groups._length; i++)
+			print_group (project, depth + 1, group->groups._buffer [i]);
+
+		CORBA_free (group);
+	}
+
+	CORBA_exception_free (&ev);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -77,7 +103,9 @@
 	project = activate_id (result->_buffer[0].iid);
 	if (project == CORBA_OBJECT_NIL)
 		return -1;
-
+	
+	CORBA_free (result);
+	
 	/* Do stuff with it */
 	if (argc > 1) 
 		GNOME_Development_Project_load (project, argv [1], &ev);
@@ -91,18 +119,21 @@
 	} else {
 		for (i = 0; i < source_list->_length; i++) {
 			GNOME_Development_TargetSource *source;
-			
+
 			source = GNOME_Development_Project_getSource (project, source_list->_buffer[i], &ev);
-			if (!BONOBO_EX (&ev))
+			if (!BONOBO_EX (&ev)) {
 				g_print ("%s\n", source->source);
+				CORBA_free (source);
+			}
 		}
+		CORBA_free (source_list);
 	}
 	g_print ("\n\n");
 
 	g_print ("*** Targets\n\n");
 	target_list = GNOME_Development_Project_getAllTargets (project, &ev);
 	if (BONOBO_EX (&ev)) {		
-		g_error ("Unable to get sources");
+		g_error ("Unable to get targets");
 	} else {
 		int j;		
 		for (i = 0; i < target_list->_length; i++) {
@@ -110,25 +141,36 @@
 			
 			target = GNOME_Development_Project_getTarget (project, target_list->_buffer[i], &ev);
 			if (!BONOBO_EX (&ev))
-				g_print ("%s\n", target->name);
+				g_print ("%s [%s]\n", target->name, target_list->_buffer [i]);
 
 			for (j = 0; j < target->sources._length; j++) {
 				GNOME_Development_TargetSource *source;
 				const CORBA_char *id;
 				
 				id = target->sources._buffer[j];
-				if (remove_id == NULL)
-					remove_id = CORBA_string_dup (id);
+
 				source = GNOME_Development_Project_getSource (project, id, &ev);
-				if (!BONOBO_EX (&ev))
+				if (!BONOBO_EX (&ev)) {
 					g_print ("  %s\n", source->source);
-				if (target_id == NULL) {
-					target_id = CORBA_string_dup (source->target_id);
-					uri = CORBA_string_dup (source->source);
+
+					if (remove_id == NULL && !strcmp (target->type, "program")) {
+						/* we need a source from a program, since other target
+						   writers are still not implemented */
+						target_id = CORBA_string_dup (source->target_id);
+						uri = CORBA_string_dup (source->source);
+						remove_id = CORBA_string_dup (id);
+					}
 				}
+				
+				CORBA_free (source);
 			}
+			CORBA_free (target);
 		}
+		CORBA_free (target_list);
 	}
+	
+	g_print ("\n\n*** Groups\n\n");
+	print_group (project, 0, "/");
 
 	GNOME_Development_Project_removeSource (project, remove_id, &ev);
 	if (BONOBO_EX (&ev))
@@ -143,14 +185,11 @@
 	else
 		g_print ("Added source\n");
 	CORBA_free (target_id);
-
+	
 	/* Clean up */
-	CORBA_exception_init (&ev);
-	GNOME_Development_Project_unref (project, &ev);
+	bonobo_object_release_unref (project, &ev);
 	if (BONOBO_EX (&ev))
 			g_error ("Unable to unref object");
-
-	CORBA_free (result);
 	
 	CORBA_exception_free (&ev);
 
Index: src/controls/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/Makefile.am,v
retrieving revision 1.21
diff -u -r1.21 Makefile.am
--- src/controls/Makefile.am	15 Mar 2002 22:02:12 -0000	1.21
+++ src/controls/Makefile.am	28 Jun 2002 04:59:55 -0000
@@ -1,4 +1,5 @@
 INCLUDES = \
+	-I$(top_srcdir)/src/lib \
 	-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
 	-DG_DISABLE_DEPRECATED \
 	-DGTK_DISABLE_DEPRECATED \
Index: src/controls/control-factories.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/control-factories.c,v
retrieving revision 1.20
diff -u -r1.20 control-factories.c
--- src/controls/control-factories.c	22 Feb 2002 14:06:07 -0000	1.20
+++ src/controls/control-factories.c	28 Jun 2002 04:59:56 -0000
@@ -143,7 +143,8 @@
 	bonobo_property_bag_add (pb, "project", PROP_PROJECT,
                              TC_GNOME_Development_Project, NULL, 
                              "Object reference of the project backend", 0);
-
+    bonobo_object_unref (BONOBO_OBJECT (pb));
+    
     /* Listen to insert menus */
 	g_signal_connect (control, "set_frame", 
 	                  G_CALLBACK (project_tree_control_set_frame_cb), pt);
@@ -193,6 +194,7 @@
 	bonobo_property_bag_add (pb, "project", PROP_PROJECT,
                              TC_GNOME_Development_Project, NULL, 
                              "Object reference of the project backend", 0);
+    bonobo_object_unref (BONOBO_OBJECT (pb));
 
     /* Listen to insert menus */
 	g_signal_connect (control, "set_frame",
@@ -227,6 +229,7 @@
 	bonobo_property_bag_add (pb, "project", PROP_PROJECT,
                              TC_GNOME_Development_Project, NULL, 
                              "Object reference of the project backend", 0);
+    bonobo_object_unref (BONOBO_OBJECT (pb));
 
     event_source = gbf_build_info_get_event_source (GBF_BUILD_INFO (bd));
     bonobo_object_add_interface (BONOBO_OBJECT (control), 
Index: src/controls/gbf-build-info.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/gbf-build-info.c,v
retrieving revision 1.13
diff -u -r1.13 gbf-build-info.c
--- src/controls/gbf-build-info.c	9 Apr 2002 21:42:29 -0000	1.13
+++ src/controls/gbf-build-info.c	28 Jun 2002 04:59:56 -0000
@@ -169,11 +169,18 @@
 	info = GBF_BUILD_INFO (object);
 	priv = info->priv;
 	
-	CORBA_exception_init (&ev);
+	if (priv) {
+		CORBA_exception_init (&ev);
 
-	priv->prj = bonobo_object_release_unref (priv->prj, &ev);
+		priv->prj = bonobo_object_release_unref (priv->prj, &ev);
 
-	CORBA_exception_free (&ev);
+		CORBA_exception_free (&ev);
+
+		/* FIXME: destroy the html objects */
+		
+		g_free (priv);
+		info->priv = NULL;
+	}
 
 	if (GTK_OBJECT_CLASS (parent_class)->destroy)
 		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
Index: src/controls/gbf-project-tree.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/gbf-project-tree.c,v
retrieving revision 1.30
diff -u -r1.30 gbf-project-tree.c
--- src/controls/gbf-project-tree.c	9 Apr 2002 21:42:29 -0000	1.30
+++ src/controls/gbf-project-tree.c	28 Jun 2002 04:59:59 -0000
@@ -207,23 +207,19 @@
 {
 	GbfProjectTree *tree;
 	GbfProjectTreePrivate *priv;
-	CORBA_Environment ev;
 	
 	tree = GBF_PROJECT_TREE (object);
 	priv = tree->priv;
 	
-	CORBA_exception_init (&ev);
-
-	priv->prj = bonobo_object_release_unref (priv->prj, &ev);
-
-	CORBA_exception_free (&ev);
+	if (priv) {
+		if (priv->prj != CORBA_OBJECT_NIL)
+			gbf_project_tree_set_project (tree, CORBA_OBJECT_NIL);
 	
-	if (priv->empty_node) {
-		g_object_unref (priv->empty_node);
-		priv->empty_node = NULL;
-	}
+		if (priv->empty_node) {
+			gbf_tree_data_free (priv->empty_node);
+			priv->empty_node = NULL;
+		}
 
-	if (priv) {
 		g_free (priv);
 		tree->priv = NULL;
 	}
@@ -327,6 +323,7 @@
 	path = g_hash_table_lookup (priv->dirs, parent_uri_str);
 	if (path != NULL) {
 		g_free (parent_uri_str);
+		gnome_vfs_uri_unref (parent_uri);
 		gnome_vfs_uri_unref (uri);
 		gtk_tree_model_get_iter (priv->tree_model, &iter, path);
 		return gtk_tree_iter_copy (&iter);
@@ -340,10 +337,15 @@
 	gtk_tree_store_append (GTK_TREE_STORE (priv->tree_model), &iter, parent_iter);
 	gtk_tree_store_set (GTK_TREE_STORE (priv->tree_model), &iter, 0, node, -1);
 	gtk_tree_iter_free (parent_iter);
-                        
+
+	gbf_tree_data_free (node);
+	
 	g_hash_table_insert (priv->dirs, parent_uri_str, 
 	                     gtk_tree_model_get_path (priv->tree_model, &iter));
 
+	gnome_vfs_uri_unref (uri);
+	gnome_vfs_uri_unref (parent_uri);
+	
 	return gtk_tree_iter_copy (&iter);
 }
 
@@ -387,6 +389,7 @@
 
 	gtk_tree_store_append (GTK_TREE_STORE (priv->tree_model), &iter, NULL);
 	gtk_tree_store_set (GTK_TREE_STORE (priv->tree_model), &iter, 0, node, -1);
+	gbf_tree_data_free (node);
 	
 	path = gtk_tree_path_new_root ();	
 	g_hash_table_insert (priv->dirs, g_strdup (uri), path);
@@ -420,6 +423,7 @@
 		gtk_tree_store_set (GTK_TREE_STORE (priv->tree_model), 
 				    &iter, 0, node, -1);
 		gtk_tree_iter_free (parent);
+		gbf_tree_data_free (node);
 	}
 	CORBA_free (source_list);
 	CORBA_exception_free (&ev);
@@ -521,6 +525,8 @@
 			g_error ("couldn't get event source for project");
 		else
 			Bonobo_EventSource_addListener (source, BONOBO_OBJREF (priv->listener), &ev);
+		
+		bonobo_object_unref (priv->listener);
 		bonobo_object_release_unref (source, &ev);
 	}
 
Index: src/controls/gbf-target-tree.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/gbf-target-tree.c,v
retrieving revision 1.29
diff -u -r1.29 gbf-target-tree.c
--- src/controls/gbf-target-tree.c	9 Apr 2002 21:42:28 -0000	1.29
+++ src/controls/gbf-target-tree.c	28 Jun 2002 05:00:00 -0000
@@ -209,25 +209,21 @@
 {
 	GbfTargetTree *tree;
 	GbfTargetTreePrivate *priv;
-	CORBA_Environment ev;
 	
 	tree = GBF_TARGET_TREE (object);
 	priv = tree->priv;
 	
-	CORBA_exception_init (&ev);
-
-	priv->prj = bonobo_object_release_unref (priv->prj, &ev);
-
-	CORBA_exception_free (&ev);
+	if (priv) {
+		if (priv->prj != CORBA_OBJECT_NIL)
+			gbf_target_tree_set_project (tree, CORBA_OBJECT_NIL);
 
-	g_hash_table_destroy (priv->icons);
+		g_hash_table_destroy (priv->icons);
 
-	if (priv->empty_node) {
-		g_object_unref (priv->empty_node);
-		priv->empty_node = NULL;
-	}
+		if (priv->empty_node) {
+			gbf_tree_data_free (priv->empty_node);
+			priv->empty_node = NULL;
+		}
 	
-	if (priv) {
 		g_free (priv);
 		tree->priv = NULL;
 	}
@@ -346,6 +342,7 @@
 			       NULL);
 	gtk_tree_store_set (GTK_TREE_STORE (priv->tree_model), &root_iter, 
 			    0, node, -1);
+	gbf_tree_data_free (node);
 
 	/* Add all the targets and their sources */
 	CORBA_exception_init (&ev);
@@ -372,6 +369,7 @@
 				       &iter, &root_iter);
 		gtk_tree_store_set (GTK_TREE_STORE (priv->tree_model), &iter, 
 				    0, node, -1);
+		gbf_tree_data_free (node);
 		
 		/* Add sources to the tree */
 		for (j = 0; j < target->sources._length; j++) {
@@ -389,9 +387,11 @@
 					       &child_iter, &iter);
 			gtk_tree_store_set (GTK_TREE_STORE (priv->tree_model), &child_iter,
 					    0, node, -1);
+			gbf_tree_data_free (node);
 		}
 	}
-	
+	CORBA_free (target_list);
+
 	/* Expand first levels. */
 	path = gtk_tree_path_new_root ();
 	gtk_tree_view_expand_row (GTK_TREE_VIEW (priv->tree), path, FALSE);
@@ -494,6 +494,8 @@
 			g_error ("couldn't get event source for project");
 		else
 			Bonobo_EventSource_addListener (source, BONOBO_OBJREF (priv->listener), &ev);
+
+		bonobo_object_unref (priv->listener);
 		bonobo_object_release_unref (source, &ev);
 	}
 
Index: src/controls/test-controls.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/test-controls.c,v
retrieving revision 1.3
diff -u -r1.3 test-controls.c
--- src/controls/test-controls.c	22 Feb 2002 01:41:35 -0000	1.3
+++ src/controls/test-controls.c	28 Jun 2002 05:00:01 -0000
@@ -91,8 +91,8 @@
 	dirname = gnome_vfs_uri_extract_dirname (vfs_uri);
 	gnome_vfs_uri_unref (vfs_uri);
 
-	if (proj != CORBA_OBJECT_NIL)
-		Bonobo_Unknown_unref (proj, &ev);
+  	if (proj != CORBA_OBJECT_NIL)
+  	    bonobo_object_release_unref (proj, &ev);
 
 	CORBA_exception_init (&ev);
 
@@ -103,6 +103,8 @@
 	proj = activate_id (result->_buffer[0].iid);
 	g_assert (proj != CORBA_OBJECT_NIL);
 
+	CORBA_free (result);
+	
 	GNOME_Development_Project_load (proj, dirname, &ev);
 	if (BONOBO_EX (&ev))
 		g_error ("Unable to load project");
@@ -116,14 +118,19 @@
     
 	bonobo_pbclient_set_value (pb, "project", arg, &ev);
 	g_assert (!BONOBO_EX (&ev));
-
+	bonobo_object_release_unref (pb, &ev);
+	g_assert (!BONOBO_EX (&ev));
+	
 	/* Tell GbfTargetTree to display the project. */
 	pb = Bonobo_Control_getProperties (target_ctrl, &ev);
 	g_assert (!BONOBO_EX (&ev));
     
 	bonobo_pbclient_set_value (pb, "project", arg, &ev);
 	g_assert (!BONOBO_EX (&ev));
+	bonobo_object_release_unref (pb, &ev);
+	g_assert (!BONOBO_EX (&ev));
 
+	BONOBO_ARG_SET_GENERAL (arg, CORBA_OBJECT_NIL, TC_GNOME_Development_Project, GNOME_Development_Project, NULL);
 	bonobo_arg_release (arg);
 	
 	CORBA_exception_free (&ev);
@@ -134,13 +141,15 @@
          gpointer   user_data, 
          char      *cname)
 {
+	gtk_widget_destroy (GTK_WIDGET (user_data));
+
 	bonobo_main_quit ();
 }
 
 static void
 event_cb (BonoboListener    *listener, 
           const char        *event,
-          CORBA_any         *any, 
+          const CORBA_any   *any, 
           CORBA_Environment *ev,
           gpointer           user_data)
 {
@@ -193,29 +202,42 @@
 static void
 add_event_listener (Bonobo_Control ctrl)
 {
-	BonoboListener *listener;
 	CORBA_Object source;
 	CORBA_Environment ev;
 	
 	CORBA_exception_init (&ev);
 	
-	listener = bonobo_listener_new (NULL, NULL);
-	g_signal_connect (listener, "event_notify",
-			  G_CALLBACK (event_cb), NULL);
-                      
 	source = Bonobo_Unknown_queryInterface (ctrl, 
 						"IDL:Bonobo/EventSource:1.0", 
 						&ev);
 	if (!BONOBO_EX (&ev))
-		Bonobo_EventSource_addListener (source, 
-						bonobo_object_corba_objref (BONOBO_OBJECT (listener)), 
-						&ev);
+		bonobo_event_source_client_add_listener (source,
+							 event_cb,
+							 NULL,
+							 &ev,
+							 NULL);
 	else
 		g_error ("couldn't get event source for widget");
 	
+	bonobo_object_release_unref (source, &ev);
+	
 	CORBA_exception_free (&ev);	
 }
 
+static void
+window_destroyed (GtkWidget *window,
+		  gpointer   user_data)
+{
+    BonoboUIComponent *ui_component;
+
+    ui_component = g_object_get_data (G_OBJECT (window), "ui_component");
+    if (ui_component) {
+	bonobo_ui_component_unset_container (ui_component, NULL);
+	bonobo_object_unref (ui_component);
+	g_object_set_data (G_OBJECT (window), "ui_component", NULL);
+    }
+}
+
 static BonoboWindow *
 create_window (void)
 {
@@ -231,11 +253,12 @@
 
 	/* This determines where the UI configuration info. will be stored */
 	bonobo_ui_engine_config_set_path (bonobo_window_get_ui_engine (win),
-					  "/test-project/UIConfig/kvps");
+					  "/apps/gnome-build/test-project/UIConfig/kvps");
 
 	/* Create a UI component with which to communicate with the window */
 	ui_component = bonobo_ui_component_new_default ();
-
+	g_object_set_data (G_OBJECT (win), "ui_component", ui_component);
+	
 	/* Associate the BonoboUIComponent with the container */
 	bonobo_ui_component_set_container (ui_component, 
 					   BONOBO_OBJREF (ui_container), 
@@ -257,24 +280,21 @@
 	paned = gtk_vpaned_new ();
 
 	/* Create GbfProjectTree control. */
-	project_ctrl = bonobo_activation_activate_from_id 
-		("OAFIID:GNOME_Development_ProjectTree", 0, NULL, NULL);
-	project_tree = bonobo_widget_new_control_from_objref 
-		(project_ctrl, BONOBO_OBJREF (ui_container));
+	project_tree = bonobo_widget_new_control ("OAFIID:GNOME_Development_ProjectTree",
+						  BONOBO_OBJREF (ui_container));
+	project_ctrl = bonobo_widget_get_objref (BONOBO_WIDGET (project_tree));
 	add_event_listener (project_ctrl);
-
+	
 	/* Create GbfTargetTree control. */
-	target_ctrl = bonobo_activation_activate_from_id
-		("OAFIID:GNOME_Development_TargetTree", 0, NULL, NULL);
-	target_tree = bonobo_widget_new_control_from_objref
-		(target_ctrl, BONOBO_OBJREF (ui_container));
+	target_tree = bonobo_widget_new_control ("OAFIID:GNOME_Development_TargetTree",
+						 BONOBO_OBJREF (ui_container));
+	target_ctrl = bonobo_widget_get_objref (BONOBO_WIDGET (target_tree));
 	add_event_listener (target_ctrl);
 
 	/* Create GbfBuildInfo control. */
-	info_ctrl = bonobo_activation_activate_from_id
-		("OAFIID:GNOME_Development_BuildInfo", 0, NULL, NULL);
-	build_info = bonobo_widget_new_control_from_objref
-		(info_ctrl, BONOBO_OBJREF (ui_container));
+	build_info = bonobo_widget_new_control ("OAFIID:GNOME_Development_BuildInfo", 
+						BONOBO_OBJREF (ui_container));
+	info_ctrl = bonobo_widget_get_objref (BONOBO_WIDGET (build_info));
 	add_event_listener (info_ctrl);
 
 	/* Add everything to the notebook and display it. */
@@ -289,6 +309,7 @@
 	bonobo_window_set_contents (win, paned);
 	gtk_widget_show_all (paned);
     
+	g_signal_connect (win, "destroy", G_CALLBACK (window_destroyed), NULL);
 	g_signal_connect (win, "delete_event", G_CALLBACK (bonobo_main_quit), NULL);
 
 	gtk_window_set_default_size (GTK_WINDOW (win), 500, 500);
@@ -299,10 +320,6 @@
 int
 main (int argc, char *argv[])
 {
-	CORBA_Environment ev;
-
-	CORBA_exception_init (&ev);
-
 	gnome_program_init ("test-controls", VERSION, LIBGNOMEUI_MODULE, 
 			    argc, argv, GNOME_PARAM_POPT_TABLE, 
 			    bonobo_activation_popt_options, NULL);
@@ -316,5 +333,10 @@
 
 	bonobo_main ();
 
+	if (proj)
+	    bonobo_object_release_unref (proj, NULL);
+	
+	bonobo_ui_debug_shutdown ();
+	
 	return 0;
 }
Index: src/lib/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-build/src/lib/Makefile.am,v
retrieving revision 1.8
diff -u -r1.8 Makefile.am
--- src/lib/Makefile.am	18 Mar 2002 04:48:36 -0000	1.8
+++ src/lib/Makefile.am	28 Jun 2002 05:00:04 -0000
@@ -25,6 +25,8 @@
 BUILT_SOURCES = \
 	$(CORBA_GENERATED)
 
+CLEANFILES = $(CORBA_GENERATED)
+
 lib_LTLIBRARIES = \
 	libgbf-1.la
 

Attachment: gnome-build_new_script.files.tar.gz
Description: GNU Zip compressed data



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]