[RFC wayland] scanner: Add support for dynamic symbol loading

Jonas Ådahl jadahl at gmail.com
Thu Feb 18 10:51:33 UTC 2016


In certain configurations the wl_interface symbols up various
interfaces from wayland core (such as wl_surface_interface,
wl_pointer_interface etc) are not available at build time, for example
when the application will dlopen() the required symbols at run time.

To make it possible to build files generated by wayland-scanner this
way, it needs to be possible to generate files that don't reference any
external symbols, while enabling the application to provide these
symbols at a later state.

To provide this, two new commands are added to wayland-scanner:
'dynamic-code' and 'dynamic-header'.

The 'dynamic-code' command generates a C source code file similar to the
one generated by the code command, but instead of filling out the
'types' wl_interface array with external symbols, it fills it with only
NULL pointers, while adding a function for providing all the necessary
wl_interface pointers. The application must call this function before
using any Wayland IPC functionality.

The 'dynamic-header' command generates a C header file with the
declaration of the function the application must call to initialize the
wl_interface types array.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---

Hi,

While adding support for using wayland-protocol extensions in libSDL I came
across this issue with the generated -protocol.c file. The issue is that SDL
will not link to libwayland-client on build time but instead on-demand dlopen
all of the required symbols at run time. This makes all -protocol.c files
generated by wayland-scanner fail to build since they reference wl_interface
symbols for various core protocol interfaces.

To make it possible to support building protocol extension files and then load
them at run time I had to add this feature to wayland-scanner to make it work.

I imagine another potential solution would be to split up libwayland-client.so
and let libSDL run wayland-scanner on the installed wayland.xml and keep its
own copies of all the wl_interface symbols, but it  at a glance adding this
feature to wayland-scanner seems like the better option.

Let me know what you all think.


Jonas



 src/scanner.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 231 insertions(+), 37 deletions(-)

diff --git a/src/scanner.c b/src/scanner.c
index d3e2328..de9802f 100644
--- a/src/scanner.c
+++ b/src/scanner.c
@@ -27,6 +27,7 @@
 
 #include "config.h"
 
+#include <assert.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -53,10 +54,17 @@ enum side {
 	SERVER,
 };
 
+enum emit_mode {
+	EMIT_CODE_STATIC,
+	EMIT_CODE_DYNAMIC,
+	EMIT_HEADER_DYNAMIC,
+};
+
 static int
 usage(int ret)
 {
-	fprintf(stderr, "usage: ./scanner [OPTION] [client-header|server-header|code]"
+	fprintf(stderr, "usage: ./scanner [OPTION] [client-header|server-header|code|"
+		"dynamic-code|dynamic-header]"
 		" [input_file output_file]\n");
 	fprintf(stderr, "\n");
 	fprintf(stderr, "Converts XML protocol descriptions supplied on "
@@ -133,11 +141,17 @@ struct description {
 	char *text;
 };
 
+struct interface_type {
+	const char *interface_name;
+	struct wl_array type_indices;
+};
+
 struct protocol {
 	char *name;
 	char *uppercase_name;
 	struct wl_list interface_list;
 	int type_index;
+	struct wl_array interface_types;
 	int null_run_length;
 	char *copyright;
 	struct description *description;
@@ -163,7 +177,6 @@ struct message {
 	char *uppercase_name;
 	struct wl_list arg_list;
 	struct wl_list link;
-	int arg_count;
 	int new_id_count;
 	int type_index;
 	int all_null;
@@ -739,7 +752,6 @@ start_element(void *data, const char *element_name, const char **atts)
 			arg->summary = xstrdup(summary);
 
 		wl_list_insert(ctx->message->arg_list.prev, &arg->link);
-		ctx->message->arg_count++;
 	} else if (strcmp(element_name, "enum") == 0) {
 		if (name == NULL)
 			fail(&ctx->loc, "no enum name given");
@@ -1310,6 +1322,16 @@ format_copyright(const char *copyright)
 }
 
 static void
+append_interface_name(struct wl_array *types,
+		      struct arg *a)
+{
+	char **p;
+
+	p = fail_on_null(wl_array_add(types, sizeof *p));
+	*p = a->interface_name;
+}
+
+static void
 emit_types_forward_declarations(struct protocol *protocol,
 				struct wl_list *message_list,
 				struct wl_array *types)
@@ -1317,7 +1339,6 @@ emit_types_forward_declarations(struct protocol *protocol,
 	struct message *m;
 	struct arg *a;
 	int length;
-	char **p;
 
 	wl_list_for_each(m, message_list, link) {
 		length = 0;
@@ -1331,8 +1352,9 @@ emit_types_forward_declarations(struct protocol *protocol,
 					continue;
 
 				m->all_null = 0;
-				p = fail_on_null(wl_array_add(types, sizeof *p));
-				*p = a->interface_name;
+
+				if (types)
+					append_interface_name(types, a);
 				break;
 			default:
 				break;
@@ -1452,12 +1474,91 @@ emit_null_run(struct protocol *protocol)
 		printf("\tNULL,\n");
 }
 
+static struct interface_type *
+ensure_interface_type(struct protocol *protocol,
+		      const char *interface_name)
+{
+	struct interface_type *interface_type;
+
+	wl_array_for_each(interface_type, &protocol->interface_types) {
+		if (strcmp(interface_type->interface_name, interface_name) == 0)
+			return interface_type;
+	}
+
+	interface_type = wl_array_add(&protocol->interface_types,
+				      sizeof *interface_type);
+	interface_type->interface_name = interface_name;
+	wl_array_init(&interface_type->type_indices);
+
+	return interface_type;
+}
+
 static void
-emit_types(struct protocol *protocol, struct wl_list *message_list)
+collect_interface_type(struct protocol *protocol,
+		       const char *interface_name)
+{
+	struct interface_type *interface_type;
+	int *type_index;
+
+	interface_type = ensure_interface_type(protocol, interface_name);
+	type_index = wl_array_add(&interface_type->type_indices,
+				  sizeof *type_index);
+	*type_index = protocol->null_run_length + protocol->type_index;
+}
+
+static void
+emit_interface_type(struct protocol *protocol,
+		    const char *interface_name,
+		    enum emit_mode emit_mode)
+{
+	switch (emit_mode) {
+	case EMIT_CODE_STATIC:
+		if (interface_name)
+			printf("\t&%s_interface,\n", interface_name);
+		else
+			printf("\tNULL,\n");
+		break;
+	case EMIT_CODE_DYNAMIC:
+		printf("\tNULL,\n");
+		collect_interface_type(protocol, interface_name);
+		break;
+	case  EMIT_HEADER_DYNAMIC:
+		collect_interface_type(protocol, interface_name);
+		break;
+	}
+}
+
+static void
+emit_message_arg_types(struct protocol *protocol,
+		       struct message *message,
+		       enum emit_mode emit_mode)
 {
-	struct message *m;
 	struct arg *a;
 
+	wl_list_for_each(a, &message->arg_list, link) {
+		switch (a->type) {
+		case NEW_ID:
+		case OBJECT:
+			emit_interface_type(protocol,
+					    a->interface_name,
+					    emit_mode);
+			break;
+		default:
+			if (emit_mode != EMIT_HEADER_DYNAMIC)
+				printf("\tNULL,\n");
+			break;
+		}
+		protocol->type_index++;
+	}
+}
+
+static void
+emit_types(struct protocol *protocol,
+	   struct wl_list *message_list,
+	   enum emit_mode emit_mode)
+{
+	struct message *m;
+
 	wl_list_for_each(m, message_list, link) {
 		if (m->all_null) {
 			m->type_index = 0;
@@ -1466,23 +1567,8 @@ emit_types(struct protocol *protocol, struct wl_list *message_list)
 
 		m->type_index =
 			protocol->null_run_length + protocol->type_index;
-		protocol->type_index += m->arg_count;
 
-		wl_list_for_each(a, &m->arg_list, link) {
-			switch (a->type) {
-			case NEW_ID:
-			case OBJECT:
-				if (a->interface_name)
-					printf("\t&%s_interface,\n",
-					       a->interface_name);
-				else
-					printf("\tNULL,\n");
-				break;
-			default:
-				printf("\tNULL,\n");
-				break;
-			}
-		}
+		emit_message_arg_types(protocol, m, emit_mode);
 	}
 }
 
@@ -1547,7 +1633,33 @@ emit_messages(struct wl_list *message_list,
 }
 
 static void
-emit_code(struct protocol *protocol)
+emit_dynamic_interface_filler(struct protocol *protocol)
+{
+	struct interface_type *interface_type;
+	int *type_index;
+	bool first_type = true;
+
+	printf("WL_EXPORT void wpdyn_%s_fill_types(", protocol->name);
+
+	wl_array_for_each(interface_type, &protocol->interface_types) {
+		printf("%sconst struct wl_interface *%s_interface",
+		       first_type ? "\n\t" : ",\n\t",
+		       interface_type->interface_name);
+		first_type = false;
+	}
+	printf(")\n{");
+
+	wl_array_for_each(interface_type, &protocol->interface_types) {
+		wl_array_for_each(type_index, &interface_type->type_indices) {
+			printf("\n\ttypes[%d] = %s_interface;",
+			       *type_index, interface_type->interface_name);
+		}
+	}
+	printf("\n}\n\n");
+}
+
+static void
+emit_code(struct protocol *protocol, enum emit_mode emit_mode)
 {
 	struct interface *i, *next;
 	struct wl_array types;
@@ -1565,27 +1677,48 @@ emit_code(struct protocol *protocol)
 		emit_types_forward_declarations(protocol, &i->request_list, &types);
 		emit_types_forward_declarations(protocol, &i->event_list, &types);
 	}
-	qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names);
-	prev = NULL;
-	wl_array_for_each(p, &types) {
-		if (prev && strcmp(*p, prev) == 0)
-			continue;
-		printf("extern const struct wl_interface %s_interface;\n", *p);
-		prev = *p;
+
+	switch (emit_mode) {
+	case EMIT_CODE_STATIC:
+		qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names);
+		prev = NULL;
+		wl_array_for_each(p, &types) {
+			if (prev && strcmp(*p, prev) == 0)
+				continue;
+			printf("extern const struct wl_interface %s_interface;\n", *p);
+			prev = *p;
+		}
+		printf("\n");
+		break;
+	case EMIT_CODE_DYNAMIC:
+		break;
+	case EMIT_HEADER_DYNAMIC:
+		assert(!"Invalid emit mode when emitting code\n");
+		break;
 	}
+
 	wl_array_release(&types);
-	printf("\n");
 
 	printf("static const struct wl_interface *types[] = {\n");
 	emit_null_run(protocol);
 	wl_list_for_each(i, &protocol->interface_list, link) {
-		emit_types(protocol, &i->request_list);
-		emit_types(protocol, &i->event_list);
+		emit_types(protocol, &i->request_list, emit_mode);
+		emit_types(protocol, &i->event_list, emit_mode);
 	}
 	printf("};\n\n");
 
-	wl_list_for_each_safe(i, next, &protocol->interface_list, link) {
+	switch (emit_mode) {
+	case EMIT_CODE_STATIC:
+		break;
+	case EMIT_CODE_DYNAMIC:
+		emit_dynamic_interface_filler(protocol);
+		break;
+	case EMIT_HEADER_DYNAMIC:
+		assert(!"Invalid emit mode when emitting code\n");
+		break;
+	}
 
+	wl_list_for_each_safe(i, next, &protocol->interface_list, link) {
 		emit_messages(&i->request_list, i, "requests");
 		emit_messages(&i->event_list, i, "events");
 
@@ -1614,8 +1747,56 @@ emit_code(struct protocol *protocol)
 }
 
 static void
+emit_dynamic_header(struct protocol *protocol)
+{
+	struct interface *i;
+	struct interface_type *interface_type;
+	bool first_type = true;
+
+	if (protocol->copyright)
+		format_copyright(protocol->copyright);
+
+	printf("#ifndef %s_PROTOCOL_DYNAMIC_H\n"
+	       "#define %s_PROTOCOL_DYNAMIC_H\n\n"
+	       "#include <stdlib.h>\n"
+	       "#include <stdint.h>\n"
+	       "#include \"wayland-util.h\"\n\n",
+	       protocol->uppercase_name,
+	       protocol->uppercase_name);
+
+	printf("void wpdyn_%s_fill_types(", protocol->name);
+
+	/* Prepare message struct states. */
+	wl_list_for_each(i, &protocol->interface_list, link) {
+		emit_types_forward_declarations(protocol, &i->request_list, NULL);
+		emit_types_forward_declarations(protocol, &i->event_list, NULL);
+	}
+
+	/* Collect interface type information. */
+	wl_list_for_each(i, &protocol->interface_list, link) {
+		emit_types(protocol, &i->request_list, EMIT_HEADER_DYNAMIC);
+		emit_types(protocol, &i->event_list, EMIT_HEADER_DYNAMIC);
+	}
+
+	wl_array_for_each(interface_type, &protocol->interface_types) {
+		printf("%sconst struct wl_interface *%s_interface",
+		       first_type ? "\n\t" : ",\n\t",
+		       interface_type->interface_name);
+		first_type = false;
+	}
+	printf(");\n\n"
+	       "#endif /* %s_PROTOCOL_DYNAMIC_H */\n",
+	       protocol->uppercase_name);
+}
+
+static void
 free_protocol(struct protocol *protocol)
 {
+	struct interface_type *interface_type;
+
+	wl_array_for_each(interface_type, &protocol->interface_types)
+		wl_array_release(&interface_type->type_indices);
+	wl_array_release(&protocol->interface_types);
 	free(protocol->name);
 	free(protocol->uppercase_name);
 	free(protocol->copyright);
@@ -1637,6 +1818,8 @@ int main(int argc, char *argv[])
 		CLIENT_HEADER,
 		SERVER_HEADER,
 		CODE,
+		DYNAMIC_CODE,
+		DYNAMIC_HEADER,
 	} mode;
 
 	static const struct option options[] = {
@@ -1679,6 +1862,10 @@ int main(int argc, char *argv[])
 		mode = SERVER_HEADER;
 	else if (strcmp(argv[0], "code") == 0)
 		mode = CODE;
+	else if (strcmp(argv[0], "dynamic-code") == 0)
+		mode = DYNAMIC_CODE;
+	else if (strcmp(argv[0], "dynamic-header") == 0)
+		mode = DYNAMIC_HEADER;
 	else
 		usage(EXIT_FAILURE);
 
@@ -1701,6 +1888,7 @@ int main(int argc, char *argv[])
 	/* initialize protocol structure */
 	memset(&protocol, 0, sizeof protocol);
 	wl_list_init(&protocol.interface_list);
+	wl_array_init(&protocol.interface_types);
 	protocol.core_headers = core_headers;
 
 	/* initialize context */
@@ -1761,7 +1949,13 @@ int main(int argc, char *argv[])
 			emit_header(&protocol, SERVER);
 			break;
 		case CODE:
-			emit_code(&protocol);
+			emit_code(&protocol, EMIT_CODE_STATIC);
+			break;
+		case DYNAMIC_CODE:
+			emit_code(&protocol, EMIT_CODE_DYNAMIC);
+			break;
+		case DYNAMIC_HEADER:
+			emit_dynamic_header(&protocol);
 			break;
 	}
 
-- 
2.4.3



More information about the wayland-devel mailing list