[PATCH 12/30] kconfig: support variable and user-defined function
Now, we got a basic ability to test compiler capability in Kconfig. config CC_HAS_STACKPROTECTOR def_bool $(shell (($(CC) -Werror -fstack-protector -c -x c /dev/null -o /dev/null 2>/dev/null) && echo y) || echo n) This works, but it is ugly to repeat this long boilerplate. We want to describe like this: config CC_HAS_STACKPROTECTOR bool default $(cc-option -fstack-protector) It is straight-forward to add a new function, but I do not like to hard-code specialized functions like this. Hence, here is another feature, user-defined function. This works as a textual shorthand with parameterization. A user-defined function is defined by using the = operator, and can be referenced in the same way as built-in functions. A user-defined function in Make is referenced like $(call func-name, arg1, arg2), but I omitted the 'call' to make the syntax shorter. The definition of a user-defined function contains $(1), $(2), etc. in its body to reference the parameters. It is grammatically valid to pass more or fewer arguments when calling it. We already exploit this feature in our makefiles; scripts/Kbuild.include defines cc-option which takes two arguments at most, but most of the callers pass only one argument. By the way, a variable is supported at a subset of this feature since a variable is "a user-defined function with zero argument". In this context, I mean "variable" as recursively expanded variable. I will add a different flavored variable later on. The code above can be written as follows: [Example Code] success = $(shell ($(1)) >/dev/null 2>&1 && echo y || echo n) cc-option = $(success $(CC) -Werror $(1) -c -x c /dev/null -o /dev/null) config CC_HAS_STACKPROTECTOR def_bool $(cc-option -fstack-protector) [Result] $ make -s alldefconfig && tail -n 1 .config CONFIG_CC_HAS_STACKPROTECTOR=y Signed-off-by: Masahiro Yamada--- Changes in v3: - Re-implement the parse logic - Use = operator to define a user-defined function Changes in v2: - Use 'macro' directly instead of inside the string type symbol. scripts/kconfig/lkc_proto.h | 2 + scripts/kconfig/preprocess.c | 96 +++- scripts/kconfig/zconf.l | 17 +++- scripts/kconfig/zconf.y | 21 +- 4 files changed, 132 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index c46929f..2b16d6e 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -50,6 +50,8 @@ const char * prop_get_type_name(enum prop_type type); /* preprocess.c */ void env_write_dep(FILE *f, const char *auto_conf_name); +void variable_add(const char *name, const char *value); +void variable_all_del(void); char *expand_string(const char *in); char *expand_dollar(const char **str); char *expand_one_token(const char **str); diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c index f4c606f..1b746e0 100644 --- a/scripts/kconfig/preprocess.c +++ b/scripts/kconfig/preprocess.c @@ -79,6 +79,72 @@ void env_write_dep(FILE *f, const char *autoconfig_name) } /* + * Variables and User-defined Functions + */ +static LIST_HEAD(variable_list); + +struct variable { + char *name; + char *value; + struct list_head node; +}; + +static struct variable *variable_lookup(const char *name) +{ + struct variable *v; + + list_for_each_entry(v, _list, node) { + if (!strcmp(name, v->name)) + return v; + } + + return NULL; +} + +static char *variable_expand(const char *name, int argc, char *argv[]) +{ + struct variable *v; + + v = variable_lookup(name); + if (!v) + return NULL; + + return expand_string_with_args(v->value, argc, argv); +} + +void variable_add(const char *name, const char *value) +{ + struct variable *v; + + v = variable_lookup(name); + if (v) { + free(v->value); + } else { + v = xmalloc(sizeof(*v)); + v->name = xstrdup(name); + list_add_tail(>node, _list); + } + + v->value = xstrdup(value); +} + +static void variable_del(struct variable *v) +{ + list_del(>node); + free(v->name); + free(v->value); + free(v); +} + +void variable_all_del(void) +{ + struct variable *v, *tmp; + + list_for_each_entry_safe(v, tmp, _list, node) + variable_del(v); +} + +/* * Built-in Functions */ struct function { @@ -175,16 +241,30 @@ static char *function_call(const char *name, int argc, char *argv[]) * Evaluate a clause with arguments. argc/argv are arguments from the upper * function call. * + * Let's say 'foo' is defined as: + * foo = ABC$(1)PQR(2)XYZ + * and you want to evaluate $(foo x,y) + * + * First, this helper is called with: + * in :foo x,y + * argc:0 + * and then, recursively called
[PATCH 12/30] kconfig: support variable and user-defined function
Now, we got a basic ability to test compiler capability in Kconfig. config CC_HAS_STACKPROTECTOR def_bool $(shell (($(CC) -Werror -fstack-protector -c -x c /dev/null -o /dev/null 2>/dev/null) && echo y) || echo n) This works, but it is ugly to repeat this long boilerplate. We want to describe like this: config CC_HAS_STACKPROTECTOR bool default $(cc-option -fstack-protector) It is straight-forward to add a new function, but I do not like to hard-code specialized functions like this. Hence, here is another feature, user-defined function. This works as a textual shorthand with parameterization. A user-defined function is defined by using the = operator, and can be referenced in the same way as built-in functions. A user-defined function in Make is referenced like $(call func-name, arg1, arg2), but I omitted the 'call' to make the syntax shorter. The definition of a user-defined function contains $(1), $(2), etc. in its body to reference the parameters. It is grammatically valid to pass more or fewer arguments when calling it. We already exploit this feature in our makefiles; scripts/Kbuild.include defines cc-option which takes two arguments at most, but most of the callers pass only one argument. By the way, a variable is supported at a subset of this feature since a variable is "a user-defined function with zero argument". In this context, I mean "variable" as recursively expanded variable. I will add a different flavored variable later on. The code above can be written as follows: [Example Code] success = $(shell ($(1)) >/dev/null 2>&1 && echo y || echo n) cc-option = $(success $(CC) -Werror $(1) -c -x c /dev/null -o /dev/null) config CC_HAS_STACKPROTECTOR def_bool $(cc-option -fstack-protector) [Result] $ make -s alldefconfig && tail -n 1 .config CONFIG_CC_HAS_STACKPROTECTOR=y Signed-off-by: Masahiro Yamada --- Changes in v3: - Re-implement the parse logic - Use = operator to define a user-defined function Changes in v2: - Use 'macro' directly instead of inside the string type symbol. scripts/kconfig/lkc_proto.h | 2 + scripts/kconfig/preprocess.c | 96 +++- scripts/kconfig/zconf.l | 17 +++- scripts/kconfig/zconf.y | 21 +- 4 files changed, 132 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index c46929f..2b16d6e 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -50,6 +50,8 @@ const char * prop_get_type_name(enum prop_type type); /* preprocess.c */ void env_write_dep(FILE *f, const char *auto_conf_name); +void variable_add(const char *name, const char *value); +void variable_all_del(void); char *expand_string(const char *in); char *expand_dollar(const char **str); char *expand_one_token(const char **str); diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c index f4c606f..1b746e0 100644 --- a/scripts/kconfig/preprocess.c +++ b/scripts/kconfig/preprocess.c @@ -79,6 +79,72 @@ void env_write_dep(FILE *f, const char *autoconfig_name) } /* + * Variables and User-defined Functions + */ +static LIST_HEAD(variable_list); + +struct variable { + char *name; + char *value; + struct list_head node; +}; + +static struct variable *variable_lookup(const char *name) +{ + struct variable *v; + + list_for_each_entry(v, _list, node) { + if (!strcmp(name, v->name)) + return v; + } + + return NULL; +} + +static char *variable_expand(const char *name, int argc, char *argv[]) +{ + struct variable *v; + + v = variable_lookup(name); + if (!v) + return NULL; + + return expand_string_with_args(v->value, argc, argv); +} + +void variable_add(const char *name, const char *value) +{ + struct variable *v; + + v = variable_lookup(name); + if (v) { + free(v->value); + } else { + v = xmalloc(sizeof(*v)); + v->name = xstrdup(name); + list_add_tail(>node, _list); + } + + v->value = xstrdup(value); +} + +static void variable_del(struct variable *v) +{ + list_del(>node); + free(v->name); + free(v->value); + free(v); +} + +void variable_all_del(void) +{ + struct variable *v, *tmp; + + list_for_each_entry_safe(v, tmp, _list, node) + variable_del(v); +} + +/* * Built-in Functions */ struct function { @@ -175,16 +241,30 @@ static char *function_call(const char *name, int argc, char *argv[]) * Evaluate a clause with arguments. argc/argv are arguments from the upper * function call. * + * Let's say 'foo' is defined as: + * foo = ABC$(1)PQR(2)XYZ + * and you want to evaluate $(foo x,y) + * + * First, this helper is called with: + * in :foo x,y + * argc:0 + * and then, recursively called with: + * in: