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 <[email protected]>
---

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

Reply via email to