[PATCH 08/23] kconfig: add 'macro' keyword to support user-defined function

From: Masahiro Yamada
Date: Fri Feb 16 2018 - 13:48:07 EST


Now, we got a basic ability to test compiler capability in Kconfig.

config CC_HAS_STACKPROTECTOR
bool
default $(shell $CC -Werror -fstack-protector -c -x c /dev/null -o /dev/null)

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 implement a new function, but I do not like
to hard-code specialized functions like this. Hence, here is another
feature to add functions from Kconfig files.

A user-defined function can be defined as a string type symbol with
a special keyword 'macro'. It can be referenced in the same way as
built-in functions. This feature was also inspired by Makefile where
user-defined functions are referenced by $(call func-name, args...),
but I omitted the 'call' to makes it shorter.

The macro definition can contain $(1), $(2), ... which will be replaced
with arguments from the caller.

Example code:

config cc-option
string
macro $(shell $CC -Werror $(1) -c -x c /dev/null -o /dev/null)

config CC_HAS_STACKPROTECTOR
bool
default $(cc-option -fstack-protector)

Signed-off-by: Masahiro Yamada <yamada.masahiro@xxxxxxxxxxxxx>
---

Reminder for myself:
Update Documentation/kbuild/kconfig-language.txt

scripts/kconfig/function.c | 66 +++++++++++++++++++++++++++++++++++++++++----
scripts/kconfig/kconf_id.c | 1 +
scripts/kconfig/lkc_proto.h | 1 +
scripts/kconfig/zconf.y | 8 ++++++
4 files changed, 71 insertions(+), 5 deletions(-)

diff --git a/scripts/kconfig/function.c b/scripts/kconfig/function.c
index 60e59be..f7f154d 100644
--- a/scripts/kconfig/function.c
+++ b/scripts/kconfig/function.c
@@ -14,7 +14,8 @@ static LIST_HEAD(function_list);

struct function {
const char *name;
- char *(*func)(int argc, char *argv[]);
+ char *(*func)(struct function *f, int argc, char *argv[]);
+ void *priv;
struct list_head node;
};

@@ -30,7 +31,9 @@ static struct function *func_lookup(const char *name)
return NULL;
}

-static void func_add(const char *name, char *(*func)(int argc, char *argv[]))
+static void func_add(const char *name,
+ char *(*func)(struct function *f, int argc, char *argv[]),
+ void *priv)
{
struct function *f;

@@ -43,6 +46,7 @@ static void func_add(const char *name, char *(*func)(int argc, char *argv[]))
f = xmalloc(sizeof(*f));
f->name = name;
f->func = func;
+ f->priv = priv;

list_add_tail(&f->node, &function_list);
}
@@ -50,6 +54,7 @@ static void func_add(const char *name, char *(*func)(int argc, char *argv[]))
static void func_del(struct function *f)
{
list_del(&f->node);
+ free(f->priv);
free(f);
}

@@ -63,7 +68,7 @@ static char *func_call(int argc, char *argv[])
return NULL;
}

- return f->func(argc, argv);
+ return f->func(f, argc, argv);
}

static char *func_eval(const char *func)
@@ -106,8 +111,59 @@ char *func_eval_n(const char *func, size_t n)
return res;
}

+/* run user-defined function */
+static char *do_macro(struct function *f, int argc, char *argv[])
+{
+ char *new;
+ char *src, *p, *res;
+ size_t newlen;
+ int n;
+
+ new = xmalloc(1);
+ *new = 0;
+
+ /*
+ * This is a format string. $(1), $(2), ... must be replaced with
+ * function arguments.
+ */
+ src = f->priv;
+ p = src;
+
+ while ((p = strstr(p, "$("))) {
+ if (isdigit(p[2]) && p[3] == ')') {
+ n = p[2] - '0';
+ if (n < argc) {
+ newlen = strlen(new) + (p - src) +
+ strlen(argv[n]) + 1;
+ new = xrealloc(new, newlen);
+ strncat(new, src, p - src);
+ strcat(new, argv[n]);
+ src = p + 4;
+ }
+ p += 2;
+ }
+ p += 2;
+ }
+
+ newlen = strlen(new) + strlen(src) + 1;
+ new = xrealloc(new, newlen);
+ strcat(new, src);
+
+ res = expand_string_value(new);
+
+ free(new);
+
+ return res;
+}
+
+/* add user-defined function (macro) */
+void func_add_macro(const char *name, char *macro)
+{
+ func_add(name, do_macro, macro);
+}
+
/* built-in functions */
-static char *do_shell(int argc, char *argv[])
+static char *do_shell(struct function *f, int argc, char *argv[])
{
static const char *pre = "(";
static const char *post = ") >/dev/null 2>&1";
@@ -136,7 +192,7 @@ static char *do_shell(int argc, char *argv[])
void func_init(void)
{
/* register built-in functions */
- func_add("shell", do_shell);
+ func_add("shell", do_shell, NULL);
}

void func_exit(void)
diff --git a/scripts/kconfig/kconf_id.c b/scripts/kconfig/kconf_id.c
index b3e0ea0..5a1357d 100644
--- a/scripts/kconfig/kconf_id.c
+++ b/scripts/kconfig/kconf_id.c
@@ -28,6 +28,7 @@ static struct kconf_id kconf_id_array[] = {
{ "imply", T_IMPLY, TF_COMMAND },
{ "range", T_RANGE, TF_COMMAND },
{ "visible", T_VISIBLE, TF_COMMAND },
+ { "macro", T_MACRO, TF_COMMAND },
{ "option", T_OPTION, TF_COMMAND },
{ "on", T_ON, TF_PARAM },
{ "modules", T_OPT_MODULES, TF_OPTION },
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index 09a4f53..25caca3 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -50,6 +50,7 @@ const char * prop_get_type_name(enum prop_type type);

/* function.c */
char *func_eval_n(const char *func, size_t n);
+void func_add_macro(const char *name, char *macro);
void func_init(void);
void func_exit(void);

diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y
index d9977de..19452b6 100644
--- a/scripts/kconfig/zconf.y
+++ b/scripts/kconfig/zconf.y
@@ -65,6 +65,7 @@ static struct menu *current_menu, *current_entry;
%token <id>T_IMPLY
%token <id>T_RANGE
%token <id>T_VISIBLE
+%token <id>T_MACRO
%token <id>T_OPTION
%token <id>T_ON
%token <string> T_WORD
@@ -199,6 +200,7 @@ config_option_list:
| config_option_list config_option
| config_option_list symbol_option
| config_option_list depends
+ | config_option_list macro
| config_option_list help
| config_option_list option_error
| config_option_list T_EOL
@@ -246,6 +248,12 @@ config_option: T_RANGE symbol symbol if_expr T_EOL
printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
};

+macro: T_MACRO T_WORD T_EOL
+{
+ current_entry->sym->flags |= SYMBOL_AUTO;
+ func_add_macro(current_entry->sym->name, $2);
+}
+
symbol_option: T_OPTION symbol_option_list T_EOL
;

--
2.7.4