Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp2145497imm; Mon, 28 May 2018 02:32:48 -0700 (PDT) X-Google-Smtp-Source: AB8JxZry0aFHzXcdvCNFiJMcdUN5B6IB34wt1S3cH97KV63K9fLqWxN6Sq8u3Wap95gNQOQjC/Al X-Received: by 2002:a63:42c4:: with SMTP id p187-v6mr10196425pga.345.1527499968600; Mon, 28 May 2018 02:32:48 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527499968; cv=none; d=google.com; s=arc-20160816; b=cQG2ZjQ8dQ6e6bdFXuuOpHvf31AuhVU3qw2EZq6AhSaEnHYTGt8G+Nr1gRiV5oClcJ UtjndhUHVsw9Qf5DpRwr4lfkeOW0nqfUTILMlfMKnnleuUm+KV5ualgMdddikz6TAIp8 6QlJ84AS8S5r03s3Yt21Yfaa1Q9BofQ9n+M+gdA3j0Ky/fL997clRAUjIB6CMSwc8xmE KOCmicfiCDPnZRXi5V8GmdF7xdg9nkIZWjfk9aj9sl6Akl+6SFeTLL/I8vNm/ZPCd10b zwUsG/2JeKwCXgtiGjZxz/nYFZSxS2NugUkuso6kZt5x+c937zjhkh7VhMZevkdUuiVp /8kg== 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=Lz8oEi9ZSRwrQ6hRlMHpxdGoVFHUa6dhLfqmgm+mxNc=; b=ZxJ1T6u4VQ4EIcCX104bgWhHpzUcVrmPT0TrHdlJTmiMzzC+sBBWGXiUwVklfTu8t3 6mDVCZh4A4fGKZbpAZIHCMWo5HJaeOLdyXVMdaq1WfQYssFX/fhFnQi1VNz7okMxD0mt pJ+qbtKZkEYQHyDweTXgSdgI+jKO4z4dXPzu0sVlPjlJPf1wXz/L9+35ROBteTloFzJF cKMwxNupZb8s2lN1qKn/90/5DeZLKJblPie1/vwThLDlpxL21riUzao4nNy+HvcBJPjt bU3jq+D0sSivKUCWS1ctx+eWUiRMg5IJ9hqbDCDN8SrdQF+lvO9QjZ4iPTsNMPOYMYgT GLIg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@nifty.com header.s=dec2015msa header.b=pzN3qfdP; 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 t4-v6si1030582pgp.594.2018.05.28.02.32.33; Mon, 28 May 2018 02:32:48 -0700 (PDT) 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=pzN3qfdP; 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 S1754517AbeE1JaX (ORCPT + 99 others); Mon, 28 May 2018 05:30:23 -0400 Received: from conuserg-10.nifty.com ([210.131.2.77]:21572 "EHLO conuserg-10.nifty.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754292AbeE1JYP (ORCPT ); Mon, 28 May 2018 05:24:15 -0400 Received: from grover.sesame (FL1-125-199-20-195.osk.mesh.ad.jp [125.199.20.195]) (authenticated) by conuserg-10.nifty.com with ESMTP id w4S9MInT027506; Mon, 28 May 2018 18:22:29 +0900 DKIM-Filter: OpenDKIM Filter v2.10.3 conuserg-10.nifty.com w4S9MInT027506 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nifty.com; s=dec2015msa; t=1527499349; bh=Lz8oEi9ZSRwrQ6hRlMHpxdGoVFHUa6dhLfqmgm+mxNc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pzN3qfdPOfC/El/gTKMCX1LzobOjcdOqCsgRxpmutAaKwUeRnYJDiA+EpQIhX8Wxc MRiNHoGHkwDyEhva+zkwMNBCBcd1TQQTOMsC4zWE97K62B2S/kGjcaGAU86q+gPPl7 dDXJvlLLSEOlfOpw8nZAHs1LP1AwDrQ1sw9eNqusI4NkN8eb94v+Dq7yo+SbiDR0TU dajivVFfb/0USACkVUWXHDKk2xdeJ92Jdqe79zF1vvhOpBNOzyWqlm/fPMry+HjM4n Q4kq4tHZ3A15Xvfy1TsiZ42ynmIjbh5vMhDGrvyB/ckN5g+LoXF2NtgNS/OS2tjgGk TJkb21MXiaHnw== X-Nifty-SrcIP: [125.199.20.195] From: Masahiro Yamada To: linux-kbuild@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Kees Cook , Nicholas Piggin , "Luis R . Rodriguez" , Randy Dunlap , Ulf Magnusson , Sam Ravnborg , Linus Torvalds , Masahiro Yamada Subject: [PATCH v5 12/31] kconfig: support user-defined function and recursively expanded variable Date: Mon, 28 May 2018 18:21:49 +0900 Message-Id: <1527499328-13213-13-git-send-email-yamada.masahiro@socionext.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1527499328-13213-1-git-send-email-yamada.masahiro@socionext.com> References: <1527499328-13213-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 Now, we got a basic ability to test compiler capability in Kconfig. config CC_HAS_STACKPROTECTOR def_bool $(shell,($(CC) -Werror -fstack-protector -E -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 that. 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 my-func,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 as 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 in the next commit. 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) -E -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 v5: - Remove lazy expansion support and simplify the code again Changes in v4: None 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 | 86 +++++++++++++++++++++++++++++++++++++++++++- scripts/kconfig/zconf.l | 17 +++++++-- scripts/kconfig/zconf.y | 19 +++++++++- 4 files changed, 120 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 528be59..46487fe 100644 --- a/scripts/kconfig/preprocess.c +++ b/scripts/kconfig/preprocess.c @@ -178,6 +178,72 @@ static char *function_expand(const char *name, int argc, char *argv[]) } /* + * 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, &variable_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(&v->node, &variable_list); + } + + v->value = xstrdup(value); +} + +static void variable_del(struct variable *v) +{ + list_del(&v->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, &variable_list, node) + variable_del(v); +} + +/* * Evaluate a clause with arguments. argc/argv are arguments from the upper * function call. * @@ -185,14 +251,26 @@ static char *function_expand(const char *name, int argc, char *argv[]) */ static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) { - char *tmp, *name, *res, *prev, *p; + char *tmp, *name, *res, *endptr, *prev, *p; int new_argc = 0; char *new_argv[FUNCTION_MAX_ARGS]; int nest = 0; int i; + unsigned long n; tmp = xstrndup(str, len); + /* + * If variable name is '1', '2', etc. It is generally an argument + * from a user-function call (i.e. local-scope variable). If not + * available, then look-up global-scope variables. + */ + n = strtoul(tmp, &endptr, 10); + if (!*endptr && n > 0 && n <= argc) { + res = xstrdup(argv[n - 1]); + goto free_tmp; + } + prev = p = tmp; /* @@ -238,6 +316,11 @@ static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) new_argv[i] = expand_string_with_args(new_argv[i + 1], argc, argv); + /* Search for variables */ + res = variable_expand(name, new_argc, new_argv); + if (res) + goto free; + /* Look for built-in functions */ res = function_expand(name, new_argc, new_argv); if (res) @@ -255,6 +338,7 @@ static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) for (i = 0; i < new_argc; i++) free(new_argv[i]); free(name); +free_tmp: free(tmp); return res; diff --git a/scripts/kconfig/zconf.l b/scripts/kconfig/zconf.l index 9a14797..dd08f7a 100644 --- a/scripts/kconfig/zconf.l +++ b/scripts/kconfig/zconf.l @@ -1,12 +1,13 @@ %option nostdinit noyywrap never-interactive full ecs %option 8bit nodefault yylineno -%x COMMAND HELP STRING PARAM +%x COMMAND HELP STRING PARAM ASSIGN_VAL %{ /* * Copyright (C) 2002 Roman Zippel * Released under the terms of the GNU GPL v2.0. */ +#include #include #include #include @@ -111,8 +112,10 @@ n [A-Za-z0-9_-] } alloc_string(yytext, yyleng); yylval.string = text; - return T_WORD; + return T_VARIABLE; } + "=" { BEGIN(ASSIGN_VAL); return T_ASSIGN; } + [[:blank:]]+ . warn_ignored_character(*yytext); \n { BEGIN(INITIAL); @@ -120,6 +123,16 @@ n [A-Za-z0-9_-] } } +{ + [^[:blank:]\n]+.* { + alloc_string(yytext, yyleng); + yylval.string = text; + return T_ASSIGN_VAL; + } + \n { BEGIN(INITIAL); return T_EOL; } + . +} + { "&&" return T_AND; "||" return T_OR; diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y index 8a82aaf..e15e8c7 100644 --- a/scripts/kconfig/zconf.y +++ b/scripts/kconfig/zconf.y @@ -77,6 +77,9 @@ static struct menu *current_menu, *current_entry; %token T_CLOSE_PAREN %token T_OPEN_PAREN %token T_EOL +%token T_VARIABLE +%token T_ASSIGN +%token T_ASSIGN_VAL %left T_OR %left T_AND @@ -92,7 +95,7 @@ static struct menu *current_menu, *current_entry; %type end %type option_name %type if_entry menu_entry choice_entry -%type symbol_option_arg word_opt +%type symbol_option_arg word_opt assign_val %destructor { fprintf(stderr, "%s:%d: missing end statement for this entry\n", @@ -143,6 +146,7 @@ common_stmt: | config_stmt | menuconfig_stmt | source_stmt + | assignment_stmt ; option_error: @@ -511,6 +515,15 @@ symbol: nonconst_symbol word_opt: /* empty */ { $$ = NULL; } | T_WORD +/* assignment statement */ + +assignment_stmt: T_VARIABLE T_ASSIGN assign_val T_EOL { variable_add($1, $3); free($1); free($3); } + +assign_val: + /* empty */ { $$ = xstrdup(""); }; + | T_ASSIGN_VAL +; + %% void conf_parse(const char *name) @@ -525,6 +538,10 @@ void conf_parse(const char *name) if (getenv("ZCONF_DEBUG")) yydebug = 1; yyparse(); + + /* Variables are expanded in the parse phase. We can free them here. */ + variable_all_del(); + if (yynerrs) exit(1); if (!modules_sym) -- 2.7.4