diff options
| author | Masahiro Yamada <yamada.masahiro@socionext.com> | 2018-05-28 05:21:49 -0400 |
|---|---|---|
| committer | Masahiro Yamada <yamada.masahiro@socionext.com> | 2018-05-28 14:31:19 -0400 |
| commit | 9ced3bddec080e974e910bf887715540a8d9d96b (patch) | |
| tree | 4bb735afdabb6e4b9dd6f2d607a596fde7b37301 /scripts | |
| parent | 9de071536c87cb814e210bd762fcf7f645d514a9 (diff) | |
kconfig: support user-defined function and recursively expanded variable
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 <yamada.masahiro@socionext.com>
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/kconfig/lkc_proto.h | 2 | ||||
| -rw-r--r-- | scripts/kconfig/preprocess.c | 86 | ||||
| -rw-r--r-- | scripts/kconfig/zconf.l | 17 | ||||
| -rw-r--r-- | 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 c46929fab7d9..2b16d6e1b2db 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); | |||
| 50 | 50 | ||
| 51 | /* preprocess.c */ | 51 | /* preprocess.c */ |
| 52 | void env_write_dep(FILE *f, const char *auto_conf_name); | 52 | void env_write_dep(FILE *f, const char *auto_conf_name); |
| 53 | void variable_add(const char *name, const char *value); | ||
| 54 | void variable_all_del(void); | ||
| 53 | char *expand_string(const char *in); | 55 | char *expand_string(const char *in); |
| 54 | char *expand_dollar(const char **str); | 56 | char *expand_dollar(const char **str); |
| 55 | char *expand_one_token(const char **str); | 57 | char *expand_one_token(const char **str); |
diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c index 528be594e1d0..46487fe6b36c 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[]) | |||
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | /* | 180 | /* |
| 181 | * Variables (and user-defined functions) | ||
| 182 | */ | ||
| 183 | static LIST_HEAD(variable_list); | ||
| 184 | |||
| 185 | struct variable { | ||
| 186 | char *name; | ||
| 187 | char *value; | ||
| 188 | struct list_head node; | ||
| 189 | }; | ||
| 190 | |||
| 191 | static struct variable *variable_lookup(const char *name) | ||
| 192 | { | ||
| 193 | struct variable *v; | ||
| 194 | |||
| 195 | list_for_each_entry(v, &variable_list, node) { | ||
| 196 | if (!strcmp(name, v->name)) | ||
| 197 | return v; | ||
| 198 | } | ||
| 199 | |||
| 200 | return NULL; | ||
| 201 | } | ||
| 202 | |||
| 203 | static char *variable_expand(const char *name, int argc, char *argv[]) | ||
| 204 | { | ||
| 205 | struct variable *v; | ||
| 206 | |||
| 207 | v = variable_lookup(name); | ||
| 208 | if (!v) | ||
| 209 | return NULL; | ||
| 210 | |||
| 211 | return expand_string_with_args(v->value, argc, argv); | ||
| 212 | } | ||
| 213 | |||
| 214 | void variable_add(const char *name, const char *value) | ||
| 215 | { | ||
| 216 | struct variable *v; | ||
| 217 | |||
| 218 | v = variable_lookup(name); | ||
| 219 | if (v) { | ||
| 220 | free(v->value); | ||
| 221 | } else { | ||
| 222 | v = xmalloc(sizeof(*v)); | ||
| 223 | v->name = xstrdup(name); | ||
| 224 | list_add_tail(&v->node, &variable_list); | ||
| 225 | } | ||
| 226 | |||
| 227 | v->value = xstrdup(value); | ||
| 228 | } | ||
| 229 | |||
| 230 | static void variable_del(struct variable *v) | ||
| 231 | { | ||
| 232 | list_del(&v->node); | ||
| 233 | free(v->name); | ||
| 234 | free(v->value); | ||
| 235 | free(v); | ||
| 236 | } | ||
| 237 | |||
| 238 | void variable_all_del(void) | ||
| 239 | { | ||
| 240 | struct variable *v, *tmp; | ||
| 241 | |||
| 242 | list_for_each_entry_safe(v, tmp, &variable_list, node) | ||
| 243 | variable_del(v); | ||
| 244 | } | ||
| 245 | |||
| 246 | /* | ||
| 181 | * Evaluate a clause with arguments. argc/argv are arguments from the upper | 247 | * Evaluate a clause with arguments. argc/argv are arguments from the upper |
| 182 | * function call. | 248 | * function call. |
| 183 | * | 249 | * |
| @@ -185,14 +251,26 @@ static char *function_expand(const char *name, int argc, char *argv[]) | |||
| 185 | */ | 251 | */ |
| 186 | static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) | 252 | static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) |
| 187 | { | 253 | { |
| 188 | char *tmp, *name, *res, *prev, *p; | 254 | char *tmp, *name, *res, *endptr, *prev, *p; |
| 189 | int new_argc = 0; | 255 | int new_argc = 0; |
| 190 | char *new_argv[FUNCTION_MAX_ARGS]; | 256 | char *new_argv[FUNCTION_MAX_ARGS]; |
| 191 | int nest = 0; | 257 | int nest = 0; |
| 192 | int i; | 258 | int i; |
| 259 | unsigned long n; | ||
| 193 | 260 | ||
| 194 | tmp = xstrndup(str, len); | 261 | tmp = xstrndup(str, len); |
| 195 | 262 | ||
| 263 | /* | ||
| 264 | * If variable name is '1', '2', etc. It is generally an argument | ||
| 265 | * from a user-function call (i.e. local-scope variable). If not | ||
| 266 | * available, then look-up global-scope variables. | ||
| 267 | */ | ||
| 268 | n = strtoul(tmp, &endptr, 10); | ||
| 269 | if (!*endptr && n > 0 && n <= argc) { | ||
| 270 | res = xstrdup(argv[n - 1]); | ||
| 271 | goto free_tmp; | ||
| 272 | } | ||
| 273 | |||
| 196 | prev = p = tmp; | 274 | prev = p = tmp; |
| 197 | 275 | ||
| 198 | /* | 276 | /* |
| @@ -238,6 +316,11 @@ static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) | |||
| 238 | new_argv[i] = expand_string_with_args(new_argv[i + 1], | 316 | new_argv[i] = expand_string_with_args(new_argv[i + 1], |
| 239 | argc, argv); | 317 | argc, argv); |
| 240 | 318 | ||
| 319 | /* Search for variables */ | ||
| 320 | res = variable_expand(name, new_argc, new_argv); | ||
| 321 | if (res) | ||
| 322 | goto free; | ||
| 323 | |||
| 241 | /* Look for built-in functions */ | 324 | /* Look for built-in functions */ |
| 242 | res = function_expand(name, new_argc, new_argv); | 325 | res = function_expand(name, new_argc, new_argv); |
| 243 | if (res) | 326 | if (res) |
| @@ -255,6 +338,7 @@ free: | |||
| 255 | for (i = 0; i < new_argc; i++) | 338 | for (i = 0; i < new_argc; i++) |
| 256 | free(new_argv[i]); | 339 | free(new_argv[i]); |
| 257 | free(name); | 340 | free(name); |
| 341 | free_tmp: | ||
| 258 | free(tmp); | 342 | free(tmp); |
| 259 | 343 | ||
| 260 | return res; | 344 | return res; |
diff --git a/scripts/kconfig/zconf.l b/scripts/kconfig/zconf.l index 9a147977dc3f..dd08f7a38ccd 100644 --- a/scripts/kconfig/zconf.l +++ b/scripts/kconfig/zconf.l | |||
| @@ -1,12 +1,13 @@ | |||
| 1 | %option nostdinit noyywrap never-interactive full ecs | 1 | %option nostdinit noyywrap never-interactive full ecs |
| 2 | %option 8bit nodefault yylineno | 2 | %option 8bit nodefault yylineno |
| 3 | %x COMMAND HELP STRING PARAM | 3 | %x COMMAND HELP STRING PARAM ASSIGN_VAL |
| 4 | %{ | 4 | %{ |
| 5 | /* | 5 | /* |
| 6 | * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> | 6 | * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> |
| 7 | * Released under the terms of the GNU GPL v2.0. | 7 | * Released under the terms of the GNU GPL v2.0. |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <assert.h> | ||
| 10 | #include <limits.h> | 11 | #include <limits.h> |
| 11 | #include <stdio.h> | 12 | #include <stdio.h> |
| 12 | #include <stdlib.h> | 13 | #include <stdlib.h> |
| @@ -111,8 +112,10 @@ n [A-Za-z0-9_-] | |||
| 111 | } | 112 | } |
| 112 | alloc_string(yytext, yyleng); | 113 | alloc_string(yytext, yyleng); |
| 113 | yylval.string = text; | 114 | yylval.string = text; |
| 114 | return T_WORD; | 115 | return T_VARIABLE; |
| 115 | } | 116 | } |
| 117 | "=" { BEGIN(ASSIGN_VAL); return T_ASSIGN; } | ||
| 118 | [[:blank:]]+ | ||
| 116 | . warn_ignored_character(*yytext); | 119 | . warn_ignored_character(*yytext); |
| 117 | \n { | 120 | \n { |
| 118 | BEGIN(INITIAL); | 121 | BEGIN(INITIAL); |
| @@ -120,6 +123,16 @@ n [A-Za-z0-9_-] | |||
| 120 | } | 123 | } |
| 121 | } | 124 | } |
| 122 | 125 | ||
| 126 | <ASSIGN_VAL>{ | ||
| 127 | [^[:blank:]\n]+.* { | ||
| 128 | alloc_string(yytext, yyleng); | ||
| 129 | yylval.string = text; | ||
| 130 | return T_ASSIGN_VAL; | ||
| 131 | } | ||
| 132 | \n { BEGIN(INITIAL); return T_EOL; } | ||
| 133 | . | ||
| 134 | } | ||
| 135 | |||
| 123 | <PARAM>{ | 136 | <PARAM>{ |
| 124 | "&&" return T_AND; | 137 | "&&" return T_AND; |
| 125 | "||" return T_OR; | 138 | "||" return T_OR; |
diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y index 8a82aaf27581..e15e8c7063e0 100644 --- a/scripts/kconfig/zconf.y +++ b/scripts/kconfig/zconf.y | |||
| @@ -77,6 +77,9 @@ static struct menu *current_menu, *current_entry; | |||
| 77 | %token T_CLOSE_PAREN | 77 | %token T_CLOSE_PAREN |
| 78 | %token T_OPEN_PAREN | 78 | %token T_OPEN_PAREN |
| 79 | %token T_EOL | 79 | %token T_EOL |
| 80 | %token <string> T_VARIABLE | ||
| 81 | %token T_ASSIGN | ||
| 82 | %token <string> T_ASSIGN_VAL | ||
| 80 | 83 | ||
| 81 | %left T_OR | 84 | %left T_OR |
| 82 | %left T_AND | 85 | %left T_AND |
| @@ -92,7 +95,7 @@ static struct menu *current_menu, *current_entry; | |||
| 92 | %type <id> end | 95 | %type <id> end |
| 93 | %type <id> option_name | 96 | %type <id> option_name |
| 94 | %type <menu> if_entry menu_entry choice_entry | 97 | %type <menu> if_entry menu_entry choice_entry |
| 95 | %type <string> symbol_option_arg word_opt | 98 | %type <string> symbol_option_arg word_opt assign_val |
| 96 | 99 | ||
| 97 | %destructor { | 100 | %destructor { |
| 98 | fprintf(stderr, "%s:%d: missing end statement for this entry\n", | 101 | fprintf(stderr, "%s:%d: missing end statement for this entry\n", |
| @@ -143,6 +146,7 @@ common_stmt: | |||
| 143 | | config_stmt | 146 | | config_stmt |
| 144 | | menuconfig_stmt | 147 | | menuconfig_stmt |
| 145 | | source_stmt | 148 | | source_stmt |
| 149 | | assignment_stmt | ||
| 146 | ; | 150 | ; |
| 147 | 151 | ||
| 148 | option_error: | 152 | option_error: |
| @@ -511,6 +515,15 @@ symbol: nonconst_symbol | |||
| 511 | word_opt: /* empty */ { $$ = NULL; } | 515 | word_opt: /* empty */ { $$ = NULL; } |
| 512 | | T_WORD | 516 | | T_WORD |
| 513 | 517 | ||
| 518 | /* assignment statement */ | ||
| 519 | |||
| 520 | assignment_stmt: T_VARIABLE T_ASSIGN assign_val T_EOL { variable_add($1, $3); free($1); free($3); } | ||
| 521 | |||
| 522 | assign_val: | ||
| 523 | /* empty */ { $$ = xstrdup(""); }; | ||
| 524 | | T_ASSIGN_VAL | ||
| 525 | ; | ||
| 526 | |||
| 514 | %% | 527 | %% |
| 515 | 528 | ||
| 516 | void conf_parse(const char *name) | 529 | void conf_parse(const char *name) |
| @@ -525,6 +538,10 @@ void conf_parse(const char *name) | |||
| 525 | if (getenv("ZCONF_DEBUG")) | 538 | if (getenv("ZCONF_DEBUG")) |
| 526 | yydebug = 1; | 539 | yydebug = 1; |
| 527 | yyparse(); | 540 | yyparse(); |
| 541 | |||
| 542 | /* Variables are expanded in the parse phase. We can free them here. */ | ||
| 543 | variable_all_del(); | ||
| 544 | |||
| 528 | if (yynerrs) | 545 | if (yynerrs) |
| 529 | exit(1); | 546 | exit(1); |
| 530 | if (!modules_sym) | 547 | if (!modules_sym) |
