[PATCH 12/30] kconfig: support variable and user-defined function

2018-04-12 Thread Masahiro Yamada
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

2018-04-12 Thread Masahiro Yamada
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: