[PATCH wayland v2 2/3] scanner: add a new --include-core-only option

Pekka Paalanen ppaalanen at gmail.com
Wed Apr 29 03:10:09 PDT 2015


On Tue, 28 Apr 2015 22:57:17 +0300
Giulio Camuffo <giuliocamuffo at gmail.com> wrote:

> When using this new option the generated code will include the new
> core headers instead of the old ones. The default needs to remain
> unchanged for backward compatibility with old code.
> The option handling logic code in comes directly from weston's
> shared/option-parser.c.

Hi,

Weston had a good reason to not use getopt() I believe: each module
parses its own arguments in turn, so command line options are parsed in
several passes, and an unknown option is an error only when all modules
have had a go.

getopt_long() is a GNU extension, but libwayland is already littered
with _GNU_SOURCE.

Would it make sense to use getopt_long() instead of copying Weston's
pecualiarities?

> ---
> 
> v2: - rename --include-core-headers to --include-core-only
>     - show the new option in the help
>     - use a function to get the headers name instead of the nested ternaries
> 
>  src/scanner.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++------------
>  1 file changed, 207 insertions(+), 52 deletions(-)
> 
> diff --git a/src/scanner.c b/src/scanner.c
> index adc9aa3..a1a1361 100644
> --- a/src/scanner.c
> +++ b/src/scanner.c
> @@ -39,12 +39,16 @@ enum side {
>  static int
>  usage(int ret)
>  {
> -	fprintf(stderr, "usage: ./scanner [client-header|server-header|code]"
> +	fprintf(stderr, "usage: ./scanner [OPTION] [client-header|server-header|code]"
>  		" [input_file output_file]\n");
>  	fprintf(stderr, "\n");
>  	fprintf(stderr, "Converts XML protocol descriptions supplied on "
>  			"stdin or input file to client\n"
> -			"headers, server headers, or protocol marshalling code.\n");
> +			"headers, server headers, or protocol marshalling code.\n\n");
> +	fprintf(stderr, "options:\n");
> +	fprintf(stderr, "    --include-core-only     include the core version of the headers,\n"
> +	                "                            that is e.g. wayland-client-core.h instead\n"
> +	                "                            of wayland-client.h.\n");
>  	exit(ret);
>  }
>  
> @@ -68,6 +72,7 @@ struct protocol {
>  	int null_run_length;
>  	char *copyright;
>  	struct description *description;
> +	int include_core_headers;

bool core_headers;?

>  };
>  
>  struct interface {
> @@ -985,10 +990,64 @@ format_copyright(const char *copyright)
>  }
>  
>  static void
> +emit_types_forward_declarations(struct protocol *protocol,
> +				struct wl_list *message_list,
> +				struct wl_array *types)
> +{
> +	struct message *m;
> +	struct arg *a;
> +	int length;
> +	char **p;
> +
> +	wl_list_for_each(m, message_list, link) {
> +		length = 0;
> +		m->all_null = 1;
> +		wl_list_for_each(a, &m->arg_list, link) {
> +			length++;
> +			switch (a->type) {
> +			case NEW_ID:
> +			case OBJECT:
> +				if (!a->interface_name)
> +					continue;
> +
> +				m->all_null = 0;
> +				p = fail_on_null(wl_array_add(types, sizeof *p));
> +				*p = a->interface_name;
> +				break;
> +			default:
> +				break;
> +			}
> +		}
> +
> +		if (m->all_null && length > protocol->null_run_length)
> +			protocol->null_run_length = length;
> +	}
> +}
> +
> +static int
> +cmp_names(const void *p1, const void *p2)
> +{
> +	const char * const *s1 = p1, * const *s2 = p2;
> +
> +	return strcmp(*s1, *s2);
> +}
> +
> +static const char *
> +get_include_name(int core, enum side side)
> +{
> +	if (side == SERVER)
> +		return core ? "wayland-server-core.h" : "wayland-server.h";
> +	else
> +		return core ? "wayland-client-core.h" : "wayland-client.h";
> +}
> +
> +static void
>  emit_header(struct protocol *protocol, enum side side)
>  {
>  	struct interface *i;
> +	struct wl_array types;
>  	const char *s = (side == SERVER) ? "SERVER" : "CLIENT";
> +	char **p, *prev;
>  
>  	if (protocol->copyright)
>  		format_copyright(protocol->copyright);
> @@ -1007,17 +1066,39 @@ emit_header(struct protocol *protocol, enum side side)
>  	       "struct wl_resource;\n\n",
>  	       protocol->uppercase_name, s,
>  	       protocol->uppercase_name, s,
> -	       (side == SERVER) ? "wayland-server.h" : "wayland-client.h");
> +	       get_include_name(protocol->include_core_headers, side));
>  
> -	wl_list_for_each(i, &protocol->interface_list, link)
> -		printf("struct %s;\n", i->name);
> -	printf("\n");
> +	wl_array_init(&types);
> +	wl_list_for_each(i, &protocol->interface_list, link) {
> +		emit_types_forward_declarations(protocol, &i->request_list, &types);
> +		emit_types_forward_declarations(protocol, &i->event_list, &types);
> +	}

This collects all the object types from arguments...

>  
>  	wl_list_for_each(i, &protocol->interface_list, link) {
> +		p = fail_on_null(wl_array_add(&types, sizeof *p));
> +		*p = i->name;
> +	}

...and this collects all object types from new interfaces themselves.
Ok.

> +
> +	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("struct %s;\n", *p);
> +		prev = *p;
> +	}
> +	printf("\n");

And then you emit a sorted list of them with duplicates removed. A
useful change.

You do not mention this in the commit message, though.

After a bit of pondering, I understood why this is necessary: this is
how you get the forward declarations for all message arguments. Would
have been worth to note that. It is a change visible outside.

> +
> +	prev = NULL;
> +	wl_array_for_each(p, &types) {
> +		if (prev && strcmp(*p, prev) == 0)
> +			continue;
>  		printf("extern const struct wl_interface "
> -		       "%s_interface;\n",
> -		       i->name);
> +		       "%s_interface;\n", *p);
> +		prev = *p;
>  	}
> +
> +	wl_array_release(&types);
>  	printf("\n");
>  
>  	wl_list_for_each(i, &protocol->interface_list, link) {
> @@ -1044,41 +1125,6 @@ emit_header(struct protocol *protocol, enum side side)
>  }
>  
>  static void
> -emit_types_forward_declarations(struct protocol *protocol,
> -				struct wl_list *message_list,
> -				struct wl_array *types)
> -{
> -	struct message *m;
> -	struct arg *a;
> -	int length;
> -	char **p;
> -
> -	wl_list_for_each(m, message_list, link) {
> -		length = 0;
> -		m->all_null = 1;
> -		wl_list_for_each(a, &m->arg_list, link) {
> -			length++;
> -			switch (a->type) {
> -			case NEW_ID:
> -			case OBJECT:
> -				if (!a->interface_name)
> -					continue;
> -
> -				m->all_null = 0;
> -				p = fail_on_null(wl_array_add(types, sizeof *p));
> -				*p = a->interface_name;
> -				break;
> -			default:
> -				break;
> -			}
> -		}
> -
> -		if (m->all_null && length > protocol->null_run_length)
> -			protocol->null_run_length = length;
> -	}
> -}
> -
> -static void
>  emit_null_run(struct protocol *protocol)
>  {
>  	int i;
> @@ -1181,14 +1227,6 @@ emit_messages(struct wl_list *message_list,
>  	printf("};\n\n");
>  }
>  
> -static int
> -cmp_names(const void *p1, const void *p2)
> -{
> -	const char * const *s1 = p1, * const *s2 = p2;
> -
> -	return strcmp(*s1, *s2);
> -}
> -
>  static void
>  emit_code(struct protocol *protocol)
>  {
> @@ -1253,6 +1291,115 @@ emit_code(struct protocol *protocol)
>  	}
>  }
>  
> +enum option_type {
> +	OPTION_INTEGER,
> +	OPTION_UNSIGNED_INTEGER,
> +	OPTION_STRING,
> +	OPTION_BOOLEAN
> +};
> +
> +struct option {
> +	enum option_type type;
> +	const char *name;
> +	int short_name;
> +	void *data;
> +};
> +
> +static int
> +handle_option(const struct option *option, char *value)
> +{
> +	char* p;
> +
> +	switch (option->type) {
> +	case OPTION_INTEGER:
> +		* (int32_t *) option->data = strtol(value, &p, 0);
> +		return *value && !*p;
> +	case OPTION_UNSIGNED_INTEGER:
> +		* (uint32_t *) option->data = strtoul(value, &p, 0);
> +		return *value && !*p;
> +	case OPTION_STRING:
> +		* (char **) option->data = strdup(value);
> +		return 1;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static int
> +long_option(const struct option *options, int count, char *arg)
> +{
> +	int k, len;
> +
> +	for (k = 0; k < count; k++) {
> +		if (!options[k].name)
> +			continue;
> +
> +		len = strlen(options[k].name);
> +		if (strncmp(options[k].name, arg + 2, len) != 0)
> +			continue;
> +
> +		if (options[k].type == OPTION_BOOLEAN) {
> +			if (!arg[len + 2]) {
> +				* (int32_t *) options[k].data = 1;
> +
> +				return 1;
> +			}
> +		} else if (arg[len+2] == '=') {
> +			return handle_option(options + k, arg + len + 3);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +short_option(const struct option *options, int count, char *arg)
> +{
> +	int k;
> +
> +	if (!arg[1])
> +		return 0;
> +
> +	for (k = 0; k < count; k++) {
> +		if (options[k].short_name != arg[1])
> +			continue;
> +
> +		if (options[k].type == OPTION_BOOLEAN) {
> +			if (!arg[2]) {
> +				* (int32_t *) options[k].data = 1;
> +
> +				return 1;
> +			}
> +		} else {
> +			return handle_option(options + k, arg + 2);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +parse_options(const struct option *options,
> +	      int count, int *argc, char *argv[])
> +{
> +	int i, j;
> +
> +	for (i = 1, j = 1; i < *argc; i++) {
> +		if (argv[i][0] == '-') {
> +			if (argv[i][1] == '-') {
> +				if (long_option(options, count, argv[i]))
> +					continue;
> +			} else if (short_option(options, count, argv[i]))
> +				continue;
> +		}
> +		argv[j++] = argv[i];
> +	}
> +	argv[j] = NULL;
> +	*argc = j;
> +
> +	return j;
> +}

Since all we need is another boolean option, using getopt_long should
make all this much less code.

> +
>  int main(int argc, char *argv[])
>  {
>  	struct parse_context ctx;
> @@ -1260,15 +1407,22 @@ int main(int argc, char *argv[])
>  	FILE *input = stdin;
>  	int len;
>  	void *buf;
> +	int help = 0, include_core = 0;
>  	enum {
>  		CLIENT_HEADER,
>  		SERVER_HEADER,
>  		CODE,
>  	} mode;
>  
> +	const struct option options[] = {
> +		{ OPTION_BOOLEAN, "help", 'h', &help },
> +		{ OPTION_BOOLEAN, "include-core-only", 0, &include_core },
> +	};
> +	parse_options(options, sizeof(options) / sizeof(options[0]), &argc, argv);
> +
>  	if (argc != 2 && argc != 4)
>  		usage(EXIT_FAILURE);
> -	else if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0)
> +	else if (strcmp(argv[1], "help") == 0 || help)
>  		usage(EXIT_SUCCESS);
>  	else if (strcmp(argv[1], "client-header") == 0)
>  		mode = CLIENT_HEADER;
> @@ -1297,6 +1451,7 @@ int main(int argc, char *argv[])
>  	protocol.type_index = 0;
>  	protocol.null_run_length = 0;
>  	protocol.copyright = NULL;
> +	protocol.include_core_headers = include_core;
>  	memset(&ctx, 0, sizeof ctx);
>  	ctx.protocol = &protocol;
>  

I have checked that the generated headers and code from wayland.xml
do not change without the new option, excluding the the order of some
declarations which is insignificant.

Also I checked that the difference between without and with
--include-core-only indeed is just the header to be included.

Techincal comments aside, this patch is:
Reviewed-by: Pekka Paalanen <pekka.paalanen at collabora.co.uk>


Thanks,
pq


More information about the wayland-devel mailing list