linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ulf Magnusson <ulfalizer@gmail.com>
To: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Linux Kbuild mailing list <linux-kbuild@vger.kernel.org>,
	Sam Ravnborg <sam@ravnborg.org>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Arnd Bergmann <arnd@arndb.de>, Kees Cook <keescook@chromium.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Randy Dunlap <rdunlap@infradead.org>,
	"Luis R . Rodriguez" <mcgrof@kernel.org>,
	Nicolas Pitre <nico@linaro.org>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2 07/21] kconfig: add function support and implement 'shell' function
Date: Sun, 1 Apr 2018 06:19:31 +0200	[thread overview]
Message-ID: <CAFkk2KQn2TQ8iRqNQADN2WgYGchMaZ-pA7dyT_qmPb1Ed5pS2Q@mail.gmail.com> (raw)
In-Reply-To: <1522128575-5326-8-git-send-email-yamada.masahiro@socionext.com>

On Tue, Mar 27, 2018 at 7:29 AM, Masahiro Yamada
<yamada.masahiro@socionext.com> wrote:
> This commit adds a new concept 'function' to do more text processing
> in Kconfig.
>
> A function call looks like this:
>
>   $(function arg1, arg2, arg3, ...)
>
> (Actually, this syntax was inspired by make.)
>
> Real examples will look like this:
>
>   $(shell echo hello world)
>   $(cc-option -fstackprotector)
>
> This commit adds the basic infrastructure to add, delete, evaluate
> functions, and also the first built-in function $(shell ...).  This
> accepts a single command to execute.  It returns the standard output
> from it.
>
> [Example code]
>
>   config HELLO
>           string
>           default "$(shell echo hello world)"
>
>   config Y
>           def_bool $(shell echo y)
>
> [Result]
>
>   $ make -s alldefconfig && tail -n 2 .config
>   CONFIG_HELLO="hello world"
>   CONFIG_Y=y
>
> Caveat:
> Like environments, functions are expanded in the lexer.  You cannot
> pass symbols to function arguments.  This is a limitation to simplify
> the implementation.  I want to avoid the dynamic function evaluation,
> which would introduce much more complexity.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
>
> Reminder for myself:
> Update Documentation/kbuild/kconfig-language.txt
>
>
> Changes in v2:
>   - Use 'shell' for getting stdout from the comment.
>     It was 'shell-stdout' in the previous version.
>   - Symplify the implementation since the expansion has been moved to
>     lexer.
>
>  scripts/kconfig/function.c  | 170 ++++++++++++++++++++++++++++++++++++++++++++
>  scripts/kconfig/lkc_proto.h |   5 ++
>  scripts/kconfig/util.c      |  46 +++++++++---
>  scripts/kconfig/zconf.y     |   9 +++
>  4 files changed, 222 insertions(+), 8 deletions(-)
>  create mode 100644 scripts/kconfig/function.c
>
> diff --git a/scripts/kconfig/function.c b/scripts/kconfig/function.c
> new file mode 100644
> index 0000000..913685f
> --- /dev/null
> +++ b/scripts/kconfig/function.c
> @@ -0,0 +1,170 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com>
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "list.h"
> +
> +#define FUNCTION_MAX_ARGS              10
> +
> +static LIST_HEAD(function_list);
> +
> +struct function {
> +       char *name;
> +       char *(*func)(struct function *f, int argc, char *argv[]);
> +       struct list_head node;
> +};
> +
> +static struct function *func_lookup(const char *name)
> +{
> +       struct function *f;
> +
> +       list_for_each_entry(f, &function_list, node) {
> +               if (!strcmp(name, f->name))
> +                       return f;
> +       }
> +
> +       return NULL;
> +}
> +
> +static void func_add(const char *name,
> +                    char *(*func)(struct function *f, int argc, char *argv[]))
> +{
> +       struct function *f;
> +
> +       f = func_lookup(name);
> +       if (f) {
> +               fprintf(stderr, "%s: function already exists. ignored.\n", name);
> +               return;
> +       }
> +
> +       f = xmalloc(sizeof(*f));
> +       f->name = xstrdup(name);
> +       f->func = func;
> +
> +       list_add_tail(&f->node, &function_list);
> +}
> +
> +static void func_del(struct function *f)
> +{
> +       list_del(&f->node);
> +       free(f->name);
> +       free(f);
> +}
> +
> +static char *func_call(int argc, char *argv[])
> +{
> +       struct function *f;
> +
> +       f = func_lookup(argv[0]);
> +       if (!f) {
> +               fprintf(stderr, "%s: function not found\n", argv[0]);
> +               return NULL;
> +       }
> +
> +       return f->func(f, argc, argv);
> +}
> +
> +static char *func_eval(const char *func)
> +{
> +       char *expanded, *saveptr, *str, *token, *res;
> +       const char *delim;
> +       int argc = 0;
> +       char *argv[FUNCTION_MAX_ARGS];
> +
> +       expanded = expand_string_value(func);
> +
> +       str = expanded;
> +       delim = " ";
> +
> +       while ((token = strtok_r(str, delim, &saveptr))) {
> +               argv[argc++] = token;

Would be nice to error out if the array is overstepped.

> +               str = NULL;
> +               delim = ",";
> +       }
> +
> +       res = func_call(argc, argv);
> +
> +       free(expanded);
> +
> +       return res ?: xstrdup("");
> +}

Since only 'macro' will take multiple parameters, I wonder if it might
be better to implement the argument parsing there, and simply pass the
string (minus the function name) as-is to functions.

You would then be able to have ',' in shell commands, which might be
required -- think gcc -Wl,option and the like.

> +
> +char *func_eval_n(const char *func, size_t n)
> +{
> +       char *tmp, *res;
> +
> +       tmp = xmalloc(n + 1);
> +       memcpy(tmp, func, n);
> +       *(tmp + n) = '\0';
> +
> +       res = func_eval(tmp);
> +
> +       free(tmp);
> +
> +       return res;
> +}
> +
> +/* built-in functions */
> +static char *do_shell(struct function *f, int argc, char *argv[])
> +{
> +       static const char *pre = "(";
> +       static const char *post = ") 2>/dev/null";

Arrays seem neater, since the pointers aren't needed.

> +       FILE *p;
> +       char buf[256];
> +       char *cmd;
> +       int ret;

Could get rid of 'ret' and just do

    if (pclose(p) == -1)
        perror(cmd);

> +
> +       if (argc != 2)
> +               return NULL;
> +
> +       /*
> +        * Surround the command with ( ) in case it is piped commands.
> +        * Also, redirect stderr to /dev/null.
> +        */
> +       cmd = xmalloc(strlen(pre) + strlen(argv[1]) + strlen(post) + 1);
> +       strcpy(cmd, pre);
> +       strcat(cmd, argv[1]);
> +       strcat(cmd, post);
> +
> +       p = popen(cmd, "r");
> +       if (!p) {
> +               perror(cmd);
> +               goto free;
> +       }
> +       if (fgets(buf, sizeof(buf), p)) {
> +               size_t len = strlen(buf);
> +
> +               if (buf[len - 1] == '\n')
> +                       buf[len - 1] = '\0';
> +       } else {
> +               buf[0] = '\0';
> +       }
> +
> +       ret = pclose(p);
> +       if (ret == -1)
> +               perror(cmd);
> +
> +free:
> +       free(cmd);
> +
> +       return xstrdup(buf);
> +}
> +
> +void func_init(void)
> +{
> +       /* register built-in functions */
> +       func_add("shell", do_shell);
> +}
> +
> +void func_exit(void)
> +{
> +       struct function *f, *tmp;
> +
> +       /* unregister all functions */
> +       list_for_each_entry_safe(f, tmp, &function_list, node)
> +               func_del(f);
> +}
> diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
> index 9884adc..09a4f53 100644
> --- a/scripts/kconfig/lkc_proto.h
> +++ b/scripts/kconfig/lkc_proto.h
> @@ -48,5 +48,10 @@ const char * sym_get_string_value(struct symbol *sym);
>
>  const char * prop_get_type_name(enum prop_type type);
>
> +/* function.c */
> +char *func_eval_n(const char *func, size_t n);
> +void func_init(void);
> +void func_exit(void);
> +
>  /* expr.c */
>  void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken);
> diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
> index 3d27c49..218b051 100644
> --- a/scripts/kconfig/util.c
> +++ b/scripts/kconfig/util.c
> @@ -13,9 +13,10 @@
>  #include "lkc.h"
>
>  /*
> - * Expand environments embedded in the string given in argument. Environments
> - * to be expanded shall be prefixed by a '$'. Unknown environment expands to
> - * the empty string.
> + * Expand environments and functions embedded in the string given in argument.
> + * Environments to be expanded shall be prefixed by a '$'. Functions to be
> + * evaluated shall be surrounded by $(). Unknown environment/function expands
> + * to the empty string.
>   */
>  char *expand_string_value(const char *in)
>  {
> @@ -33,11 +34,40 @@ char *expand_string_value(const char *in)
>         while ((p = strchr(in, '$'))) {
>                 char *new;
>
> -               q = p + 1;
> -               while (isalnum(*q) || *q == '_')
> -                       q++;
> -
> -               new = env_expand_n(p + 1, q - p - 1);
> +               /*
> +                * If the next character is '(', it is a function.
> +                * Otherwise, environment.
> +                */
> +               if (*(p + 1) == '(') {
> +                       int nest = 0;
> +
> +                       q = p + 2;
> +                       while (1) {
> +                               if (*q == '\0') {
> +                                       fprintf(stderr,
> +                                               "unterminated function: %s\n",
> +                                               p);
> +                                       new = xstrdup("");
> +                                       break;
> +                               } else if (*q == '(') {
> +                                       nest++;
> +                               } else if (*q == ')') {
> +                                       if (nest-- == 0) {
> +                                               new = func_eval_n(p + 2,
> +                                                                 q - p - 2);
> +                                               q++;
> +                                               break;
> +                                       }
> +                               }
> +                               q++;
> +                       }

A loop like this might work too:

    q = p + 1;
    do {
        if (*q == '\0') {
            *error*
            val = ...
            goto error;
        }

        if (*q == '(')
            nest++;
        if (*q == ')')
            nest--;
        q++;
    } while (nest > 0);

    val = func_eval_n(...)
error:

> +               } else {
> +                       q = p + 1;
> +                       while (isalnum(*q) || *q == '_')
> +                               q++;
> +
> +                       new = env_expand_n(p + 1, q - p - 1);
> +               }
>
>                 reslen = strlen(res) + (p - in) + strlen(new) + 1;
>                 res = xrealloc(res, reslen);
> diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y
> index d8120c7..feaea18 100644
> --- a/scripts/kconfig/zconf.y
> +++ b/scripts/kconfig/zconf.y
> @@ -520,11 +520,19 @@ void conf_parse(const char *name)
>
>         zconf_initscan(name);
>
> +       func_init();
>         _menu_init();
>
>         if (getenv("ZCONF_DEBUG"))
>                 yydebug = 1;
>         yyparse();
> +
> +       /*
> +        * Currently, functions are evaluated only when Kconfig files are
> +        * parsed. We can free functions here.
> +        */
> +       func_exit();
> +
>         if (yynerrs)
>                 exit(1);
>         if (!modules_sym)
> @@ -765,4 +773,5 @@ void zconfdump(FILE *out)
>  #include "confdata.c"
>  #include "expr.c"
>  #include "symbol.c"
> +#include "function.c"
>  #include "menu.c"
> --
> 2.7.4
>

Cheers,
Ulf

  parent reply	other threads:[~2018-04-01  4:19 UTC|newest]

Thread overview: 73+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-03-27  5:29 [PATCH v2 00/21] kconfig: move compiler capability tests to Kconfig Masahiro Yamada
2018-03-27  5:29 ` [PATCH v2 01/21] kbuild: remove kbuild cache Masahiro Yamada
2018-03-28  3:26   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 02/21] kbuild: remove CONFIG_CROSS_COMPILE support Masahiro Yamada
2018-03-28  3:28   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 03/21] kconfig: move and rename sym_expand_string_value() Masahiro Yamada
2018-03-28  3:29   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 04/21] kconfig: reference environments directly and remove 'option env=' syntax Masahiro Yamada
2018-03-28  3:33   ` Kees Cook
2018-03-29  2:19   ` Ulf Magnusson
2018-03-29  2:56     ` Ulf Magnusson
2018-03-29 17:38       ` Ulf Magnusson
2018-03-30  5:30     ` Masahiro Yamada
2018-04-01  2:27   ` Ulf Magnusson
2018-04-01  2:40     ` Ulf Magnusson
2018-04-13  6:02     ` Masahiro Yamada
2018-03-27  5:29 ` [PATCH v2 05/21] kconfig: remove string expansion in file_lookup() Masahiro Yamada
2018-03-28  3:34   ` Kees Cook
2018-04-01  2:52   ` Ulf Magnusson
2018-03-27  5:29 ` [PATCH v2 06/21] kconfig: remove string expansion for mainmenu after yyparse() Masahiro Yamada
2018-03-28  3:35   ` Kees Cook
2018-04-01  2:59   ` Ulf Magnusson
2018-03-27  5:29 ` [PATCH v2 07/21] kconfig: add function support and implement 'shell' function Masahiro Yamada
2018-03-28  3:41   ` Kees Cook
2018-04-13  5:32     ` Masahiro Yamada
2018-03-29  2:42   ` Ulf Magnusson
2018-04-01  4:19   ` Ulf Magnusson [this message]
2018-04-13  5:37     ` Masahiro Yamada
2018-03-27  5:29 ` [PATCH v2 08/21] kconfig: replace $UNAME_RELEASE with function call Masahiro Yamada
2018-03-28  3:42   ` Kees Cook
2018-04-01  4:38   ` Ulf Magnusson
2018-03-27  5:29 ` [PATCH v2 09/21] kconfig: add 'macro' keyword to support user-defined function Masahiro Yamada
2018-03-28  3:45   ` Kees Cook
2018-04-01  6:05   ` Ulf Magnusson
2018-04-01  6:49     ` Ulf Magnusson
2018-04-13  5:44     ` Masahiro Yamada
2018-03-27  5:29 ` [PATCH v2 10/21] kconfig: add 'success' and 'cc-option' macros Masahiro Yamada
2018-03-28  3:46   ` Kees Cook
2018-04-01  6:28   ` Ulf Magnusson
2018-03-27  5:29 ` [PATCH v2 11/21] stack-protector: test compiler capability in Kconfig and drop AUTO mode Masahiro Yamada
2018-03-28 11:18   ` Kees Cook
2018-04-09  8:54     ` Masahiro Yamada
2018-04-09 15:04       ` Kees Cook
2018-04-10  3:15         ` Masahiro Yamada
2018-03-27  5:29 ` [PATCH v2 12/21] kconfig: show compiler version text in the top comment Masahiro Yamada
2018-03-28  3:26   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 13/21] kconfig: add CC_IS_GCC and GCC_VERSION Masahiro Yamada
2018-03-28 11:19   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 14/21] kconfig: add CC_IS_CLANG and CLANG_VERSION Masahiro Yamada
2018-03-28 11:22   ` Kees Cook
2018-03-28 11:52     ` Masahiro Yamada
2018-03-27  5:29 ` [PATCH v2 15/21] gcov: remove CONFIG_GCOV_FORMAT_AUTODETECT Masahiro Yamada
2018-03-27  9:12   ` Peter Oberparleiter
2018-03-28 11:24   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 16/21] kcov: imply GCC_PLUGINS and GCC_PLUGIN_SANCOV instead of select'ing them Masahiro Yamada
2018-03-28 11:25   ` Kees Cook
2018-03-28 11:53   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 17/21] gcc-plugins: always build plugins with C++ Masahiro Yamada
2018-03-28 11:29   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 18/21] gcc-plugins: move GCC version check for PowerPC to Kconfig Masahiro Yamada
2018-03-28 11:30   ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 19/21] gcc-plugins: test GCC plugin support in Kconfig Masahiro Yamada
2018-03-28 11:44   ` Kees Cook
2018-04-11 15:55     ` Masahiro Yamada
2018-04-11 16:09       ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 20/21] gcc-plugins: enable GCC_PLUGINS for COMPILE_TEST Masahiro Yamada
2018-03-28 11:47   ` Kees Cook
2018-04-10  6:15     ` Masahiro Yamada
2018-04-10  7:00       ` Kees Cook
2018-03-27  5:29 ` [PATCH v2 21/21] arm64: move GCC version check for ARCH_SUPPORTS_INT128 to Kconfig Masahiro Yamada
2018-03-27 17:28   ` Will Deacon
2018-03-28 11:55   ` Kees Cook
2018-03-27 16:39 ` [PATCH v2 00/21] kconfig: move compiler capability tests " Masahiro Yamada

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAFkk2KQn2TQ8iRqNQADN2WgYGchMaZ-pA7dyT_qmPb1Ed5pS2Q@mail.gmail.com \
    --to=ulfalizer@gmail.com \
    --cc=arnd@arndb.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=keescook@chromium.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mcgrof@kernel.org \
    --cc=nico@linaro.org \
    --cc=rdunlap@infradead.org \
    --cc=sam@ravnborg.org \
    --cc=tglx@linutronix.de \
    --cc=torvalds@linux-foundation.org \
    --cc=yamada.masahiro@socionext.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).