Received: by 10.223.185.116 with SMTP id b49csp1044816wrg; Fri, 16 Feb 2018 11:23:26 -0800 (PST) X-Google-Smtp-Source: AH8x2273Q76T3WH2NPw5dZE4ziB6KdT78keR32VxNX/TGo9xYDdyzfImBCym6ZApbZn0zs0ETeJu X-Received: by 2002:a17:902:6083:: with SMTP id s3-v6mr6601074plj.6.1518809006824; Fri, 16 Feb 2018 11:23:26 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1518809006; cv=none; d=google.com; s=arc-20160816; b=zeTMJ9sE5TTs4dJ1HW+oFKpgbjZipyCZ7fYKgpff3GKm878IFVA/yn5yAMqLVomyM5 Cnj94e3OwjsZf2wGX7LQC2kgWy4aSK5DTCX12EMj6fB7Qc/u9ODW6wKaTZIuGlgUjhXI UNory2/vJh5qQ+AeogfxSYEHmvjlrA5DaWVHcFX8I2J/2mwAx6y077J/7HlmmcZYUca5 3Njd39ZLPvUtNidIvTRkxo4QuOPi9emCnVU67r78it47W45Qj2vYgZiVBsvyRrwDsaO/ moXwdZQ/Gdd/Y3WHgRQG3LE/tqKSZ0Gt3MGR4lYciS+IrcG39aQdfM9/+6W3kzzK7MjU XrFQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:dkim-filter :arc-authentication-results; bh=BFV7tpwgyTEsSRyQedUPFLKAzO55hNEeU6iegJTYxjI=; b=K+WLhjXaeVYXYWT9h11KTJ8G8kHenLm1BVaXPoAeCeAQ/Dxz5/bO6A5bYOMwA3No/2 /vNOUOffr9d4XtaanGF22XNIkrcxDjH2RL7SltJXTbdbFrP8gSaz+tHslc5OJGQ9BTus 8UHU9bkzCBwQ7bbsBngfzQifIOWNdDyObPJcZ5UcF8yU/LSvlSnTrBi3YTRcKfPVD9J5 Je4gNpp9PrPgg3F8nXGCoX7efG2M6ytD4jg+T+0ErhsYDoGm5pZgUqIbPf9mKIIv2Aru 6C9Y+kA8kHg2/DeLPUqMmx9ibdP1XHhd+cHqRWbN0F7DwKpj9XnpzNNEbPswUP0MEUus NnSw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@nifty.com header.s=dec2015msa header.b=KuqKp2ZZ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c129si978487pfa.362.2018.02.16.11.23.12; Fri, 16 Feb 2018 11:23:26 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@nifty.com header.s=dec2015msa header.b=KuqKp2ZZ; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752791AbeBPSlV (ORCPT + 99 others); Fri, 16 Feb 2018 13:41:21 -0500 Received: from conuserg-07.nifty.com ([210.131.2.74]:57762 "EHLO conuserg-07.nifty.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752521AbeBPSlS (ORCPT ); Fri, 16 Feb 2018 13:41:18 -0500 Received: from grover.sesame (FL1-125-199-20-195.osk.mesh.ad.jp [125.199.20.195]) (authenticated) by conuserg-07.nifty.com with ESMTP id w1GIdBTA013418; Sat, 17 Feb 2018 03:39:19 +0900 DKIM-Filter: OpenDKIM Filter v2.10.3 conuserg-07.nifty.com w1GIdBTA013418 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nifty.com; s=dec2015msa; t=1518806360; bh=BFV7tpwgyTEsSRyQedUPFLKAzO55hNEeU6iegJTYxjI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KuqKp2ZZchCXXLv+B/nim5UNeYXRSCu23oX5x2/k95dYeJJuwRqGIYzNp1K2yOgju yedX0fwakSe09IkLf/lDZRMCIdeD+phS66rIyjNB3jSY9gSFiC9AHcPX4m8cDv8a1n XIyihjq++yr/ApmLwKmOnJ+3dcCtj9xmBhnwrq5X9BB/9WnzVib8tgUEvYy1rt6N2+ O3jrkQdgabvKiciBF209Pxjsj6jtqkrjkAgfSJWua7LvI/N3k0WK0YiYCvKk7xpb0J h0Nu8ciVW/Q1xwZwyn2Nu+KtBHaNN9sNVmVEsOLrxH7ezi4fEsm/cWTqICHS9XHFz7 M8JtBtBjbPpaA== X-Nifty-SrcIP: [125.199.20.195] From: Masahiro Yamada To: linux-kbuild@vger.kernel.org, Linus Torvalds Cc: Greg Kroah-Hartman , Arnd Bergmann , Kees Cook , Randy Dunlap , Ulf Magnusson , Sam Ravnborg , Michal Marek , Masahiro Yamada , linux-kernel@vger.kernel.org Subject: [PATCH 07/23] kconfig: add function support and implement 'shell' function Date: Sat, 17 Feb 2018 03:38:35 +0900 Message-Id: <1518806331-7101-8-git-send-email-yamada.masahiro@socionext.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1518806331-7101-1-git-send-email-yamada.masahiro@socionext.com> References: <1518806331-7101-1-git-send-email-yamada.masahiro@socionext.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This commit adds a new concept 'function' to Kconfig. A function call resembles a variable reference with arguments, and looks like this: $(function arg1, arg2, arg3, ...) (Actually, this syntax was inspired by Makefile.) Real examples will look like this: $(shell true) $(cc-option -fstackprotector) This commit adds the basic infrastructure to add, delete, evaluate functions. Also, add the first built-in function $(shell ...). This evaluates to 'y' if the given command exits with 0, 'n' otherwise. I am also planning to support user-defined functions (a.k.a 'macro') for cases where hard-coding is not preferred. If you want to try this feature, the hello-world code is someting below. Example code: config CC_IS_GCC bool default $(shell $CC --version | grep -q gcc) config CC_IS_CLANG bool default $(shell $CC --version | grep -q clang) config CC_HAS_OZ bool default $(shell $CC -Werror -Oz -c -x c /dev/null -o /dev/null) Result: $ make -s alldefconfig && tail -n 3 .config CONFIG_CC_IS_GCC=y # CONFIG_CC_IS_CLANG is not set # CONFIG_CC_HAS_OZ is not set $ make CC=clang -s alldefconfig && tail -n 3 .config # CONFIG_CC_IS_GCC is not set CONFIG_CC_IS_CLANG=y CONFIG_CC_HAS_OZ=y A function call can appear anywhere a symbol reference can appear. So, the following code is possible. Example code: config CC_NAME string default "gcc" if $(shell $CC --version | grep -q gcc) default "clang" if $(shell $CC --version | grep -q clang) default "unknown compiler" Result: $ make -s alldefconfig && tail -n 1 .config CONFIG_CC_NAME="gcc" $ make CC=clang -s alldefconfig && tail -n 1 .config CONFIG_CC_NAME="clang" Signed-off-by: Masahiro Yamada --- Reminder for myself: Update Documentation/kbuild/kconfig-language.txt scripts/kconfig/function.c | 149 ++++++++++++++++++++++++++++++++++++++++++++ scripts/kconfig/lkc_proto.h | 5 ++ scripts/kconfig/util.c | 46 +++++++++++--- scripts/kconfig/zconf.l | 38 ++++++++++- scripts/kconfig/zconf.y | 9 +++ 5 files changed, 238 insertions(+), 9 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..60e59be --- /dev/null +++ b/scripts/kconfig/function.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2018 Masahiro Yamada + +#include +#include +#include + +#include "list.h" + +#define FUNCTION_MAX_ARGS 10 + +static LIST_HEAD(function_list); + +struct function { + const char *name; + char *(*func)(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)(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 = name; + f->func = func; + + list_add_tail(&f->node, &function_list); +} + +static void func_del(struct function *f) +{ + list_del(&f->node); + 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(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; + str = NULL; + delim = ","; + } + + res = func_call(argc, argv); + + free(expanded); + + return res ?: xstrdup(""); +} + +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(int argc, char *argv[]) +{ + static const char *pre = "("; + static const char *post = ") >/dev/null 2>&1"; + char *cmd; + int ret; + + if (argc != 2) + return NULL; + + /* + * Surround the command with ( ) in case it is piped commands. + * Also, redirect stdout and stderr to /dev/null. + */ + cmd = xmalloc(strlen(pre) + strlen(argv[1]) + strlen(post) + 1); + strcpy(cmd, pre); + strcat(cmd, argv[1]); + strcat(cmd, post); + + ret = system(cmd); + + free(cmd); + + return xstrdup(ret == 0 ? "y" : "n"); +} + +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 dddf85b..ed89fb9 100644 --- a/scripts/kconfig/util.c +++ b/scripts/kconfig/util.c @@ -93,9 +93,10 @@ static char *env_expand_n(const char *name, size_t n) } /* - * 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) { @@ -113,11 +114,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++; + } + } 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.l b/scripts/kconfig/zconf.l index 0d89ea6..f433ab0 100644 --- a/scripts/kconfig/zconf.l +++ b/scripts/kconfig/zconf.l @@ -1,7 +1,7 @@ %option nostdinit noyywrap never-interactive full ecs %option 8bit nodefault perf-report perf-report %option noinput -%x COMMAND HELP STRING PARAM +%x COMMAND HELP STRING PARAM FUNCTION %{ /* * Copyright (C) 2002 Roman Zippel @@ -25,6 +25,7 @@ static struct { static char *text; static int text_size, text_asize; +static int function_nest; struct buffer { struct buffer *parent; @@ -138,6 +139,12 @@ n [$A-Za-z0-9_-] new_string(); BEGIN(STRING); } + "$(" { + new_string(); + append_string(yytext, yyleng); + function_nest = 0; + BEGIN(FUNCTION); + } \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; ({n}|[/.])+ { const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); @@ -196,6 +203,35 @@ n [$A-Za-z0-9_-] } } +{ + [^()\n]* { + append_string(yytext, yyleng); + } + "(" { + append_string(yytext, yyleng); + function_nest++; + } + ")" { + append_string(yytext, yyleng); + if (function_nest-- == 0) { + BEGIN(PARAM); + yylval.string = text; + return T_WORD; + } + } + \n { + fprintf(stderr, + "%s:%d:warning: multi-line function not supported\n", + zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + <> { + BEGIN(INITIAL); + } +} + { [ \t]+ { ts = 0; diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y index 784083d..d9977de 100644 --- a/scripts/kconfig/zconf.y +++ b/scripts/kconfig/zconf.y @@ -534,11 +534,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) @@ -778,4 +786,5 @@ void zconfdump(FILE *out) #include "confdata.c" #include "expr.c" #include "symbol.c" +#include "function.c" #include "menu.c" -- 2.7.4