History

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

Masahiro Yamada committed 8 years ago

Commit 9ced3bd1 parent 9de07154 files · +120 −4Browse files

Showing the changes to scripts (4 of 4 files). Show all

Unified · Split

4 files
  1. scripts/kconfig/lkc_proto.h +2 −0
  2. scripts/kconfig/preprocess.c +85 −1
  3. scripts/kconfig/zconf.l +15 −2
  4. scripts/kconfig/zconf.y +18 −1
scripts/kconfig/lkc_proto.h+2−0
@@ -50,6 +50,8 @@ const char * prop_get_type_name(enum prop_type type); · 49 unmodified lines
50 50
51/* preprocess.c */51/* preprocess.c */
52void env_write_dep(FILE *f, const char *auto_conf_name);52void env_write_dep(FILE *f, const char *auto_conf_name);
53void variable_add(const char *name, const char *value);
54void variable_all_del(void);
53char *expand_string(const char *in);55char *expand_string(const char *in);
54char *expand_dollar(const char **str);56char *expand_dollar(const char **str);
55char *expand_one_token(const char **str);57char *expand_one_token(const char **str);
scripts/kconfig/preprocess.c+85−1
@@ -177,6 +177,72 @@ static char *function_expand(const char *name, int argc, char *argv[]) · 176 unmodified lines
177 return NULL;177 return NULL;
178}178}
179 179
180/*
181 * Variables (and user-defined functions)
182 */
183static LIST_HEAD(variable_list);
184
185struct variable {
186 char *name;
187 char *value;
188 struct list_head node;
189};
190
191static 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
203static 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
214void 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
230static 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
238void 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
180/*246/*
181 * Evaluate a clause with arguments. argc/argv are arguments from the upper247 * Evaluate a clause with arguments. argc/argv are arguments from the upper
182 * function call.248 * function call.
@@ -185,14 +251,26 @@ static char *function_expand(const char *name, int argc, char *argv[]) · 2 unmodified lines
185 */251 */
186static char *eval_clause(const char *str, size_t len, int argc, char *argv[])252static 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[]) · 39 unmodified lines
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: · 11 unmodified lines
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);
341free_tmp:
258 free(tmp);342 free(tmp);
259 343
260 return res;344 return res;
scripts/kconfig/zconf.l+15−2
@@ -1,12 +1,13 @@
1%option nostdinit noyywrap never-interactive full ecs1%option nostdinit noyywrap never-interactive full ecs
2%option 8bit nodefault yylineno2%option 8bit nodefault yylineno
3%x COMMAND HELP STRING PARAM3%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_-] · 98 unmodified lines
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_-] · 1 unmodified line
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;
scripts/kconfig/zconf.y+18−1
@@ -77,6 +77,9 @@ static struct menu *current_menu, *current_entry; · 76 unmodified lines
77%token T_CLOSE_PAREN77%token T_CLOSE_PAREN
78%token T_OPEN_PAREN78%token T_OPEN_PAREN
79%token T_EOL79%token T_EOL
80%token <string> T_VARIABLE
81%token T_ASSIGN
82%token <string> T_ASSIGN_VAL
80 83
81%left T_OR84%left T_OR
82%left T_AND85%left T_AND
@@ -92,7 +95,7 @@ static struct menu *current_menu, *current_entry; · 9 unmodified lines
92%type <id> end95%type <id> end
93%type <id> option_name96%type <id> option_name
94%type <menu> if_entry menu_entry choice_entry97%type <menu> if_entry menu_entry choice_entry
95%type <string> symbol_option_arg word_opt98%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: · 44 unmodified lines
143 | config_stmt146 | config_stmt
144 | menuconfig_stmt147 | menuconfig_stmt
145 | source_stmt148 | source_stmt
149 | assignment_stmt
146;150;
147 151
148option_error:152option_error:
@@ -511,6 +515,15 @@ symbol: nonconst_symbol · 362 unmodified lines
511word_opt: /* empty */ { $$ = NULL; }515word_opt: /* empty */ { $$ = NULL; }
512 | T_WORD516 | T_WORD
513 517
518/* assignment statement */
519
520assignment_stmt: T_VARIABLE T_ASSIGN assign_val T_EOL { variable_add($1, $3); free($1); free($3); }
521
522assign_val:
523 /* empty */ { $$ = xstrdup(""); };
524 | T_ASSIGN_VAL
525;
526
514%%527%%
515 528
516void conf_parse(const char *name)529void conf_parse(const char *name)
@@ -525,6 +538,10 @@ void conf_parse(const char *name) · 8 unmodified lines
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)