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/kconfig | |
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/kconfig')
-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) |