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

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
5050
5151 /* preprocess.c */
5252 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);
5355 char *expand_string(const char *in);
5456 char *expand_dollar(const char **str);
5557 char *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
177177 return NULL;
178178 }
179179
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+
180246 /*
181247 * Evaluate a clause with arguments. argc/argv are arguments from the upper
182248 * function call.
@@ -185,14 +251,26 @@ static char *function_expand(const char *name, int argc, char *argv[]) · 2 unmodified lines
185251 */
186252 static char *eval_clause(const char *str, size_t len, int argc, char *argv[])
187253 {
188− char *tmp, *name, *res, *prev, *p;
254+ char *tmp, *name, *res, *endptr, *prev, *p;
189255 int new_argc = 0;
190256 char *new_argv[FUNCTION_MAX_ARGS];
191257 int nest = 0;
192258 int i;
259+ unsigned long n;
193260
194261 tmp = xstrndup(str, len);
195262
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+
196274 prev = p = tmp;
197275
198276 /*
@@ -238,6 +316,11 @@ static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) · 39 unmodified lines
238316 new_argv[i] = expand_string_with_args(new_argv[i + 1],
239317 argc, argv);
240318
319+ /* Search for variables */
320+ res = variable_expand(name, new_argc, new_argv);
321+ if (res)
322+ goto free;
323+
241324 /* Look for built-in functions */
242325 res = function_expand(name, new_argc, new_argv);
243326 if (res)
@@ -255,6 +338,7 @@ free: · 11 unmodified lines
255338 for (i = 0; i < new_argc; i++)
256339 free(new_argv[i]);
257340 free(name);
341+free_tmp:
258342 free(tmp);
259343
260344 return res;
scripts/kconfig/zconf.l+15−2
@@ -1,12 +1,13 @@
11 %option nostdinit noyywrap never-interactive full ecs
22 %option 8bit nodefault yylineno
3−%x COMMAND HELP STRING PARAM
3+%x COMMAND HELP STRING PARAM ASSIGN_VAL
44 %{
55 /*
66 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
77 * Released under the terms of the GNU GPL v2.0.
88 */
99
10+#include <assert.h>
1011 #include <limits.h>
1112 #include <stdio.h>
1213 #include <stdlib.h>
@@ -111,8 +112,10 @@ n [A-Za-z0-9_-] · 98 unmodified lines
111112 }
112113 alloc_string(yytext, yyleng);
113114 yylval.string = text;
114− return T_WORD;
115+ return T_VARIABLE;
115116 }
117+ "=" { BEGIN(ASSIGN_VAL); return T_ASSIGN; }
118+ [[:blank:]]+
116119 . warn_ignored_character(*yytext);
117120 \n {
118121 BEGIN(INITIAL);
@@ -120,6 +123,16 @@ n [A-Za-z0-9_-] · 1 unmodified line
120123 }
121124 }
122125
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+
123136 <PARAM>{
124137 "&&" return T_AND;
125138 "||" return T_OR;
scripts/kconfig/zconf.y+18−1
@@ -77,6 +77,9 @@ static struct menu *current_menu, *current_entry; · 76 unmodified lines
7777 %token T_CLOSE_PAREN
7878 %token T_OPEN_PAREN
7979 %token T_EOL
80+%token <string> T_VARIABLE
81+%token T_ASSIGN
82+%token <string> T_ASSIGN_VAL
8083
8184 %left T_OR
8285 %left T_AND
@@ -92,7 +95,7 @@ static struct menu *current_menu, *current_entry; · 9 unmodified lines
9295 %type <id> end
9396 %type <id> option_name
9497 %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
9699
97100 %destructor {
98101 fprintf(stderr, "%s:%d: missing end statement for this entry\n",
@@ -143,6 +146,7 @@ common_stmt: · 44 unmodified lines
143146 | config_stmt
144147 | menuconfig_stmt
145148 | source_stmt
149+ | assignment_stmt
146150 ;
147151
148152 option_error:
@@ -511,6 +515,15 @@ symbol: nonconst_symbol · 362 unmodified lines
511515 word_opt: /* empty */ { $$ = NULL; }
512516 | T_WORD
513517
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+
514527 %%
515528
516529 void conf_parse(const char *name)
@@ -525,6 +538,10 @@ void conf_parse(const char *name) · 8 unmodified lines
525538 if (getenv("ZCONF_DEBUG"))
526539 yydebug = 1;
527540 yyparse();
541+
542+ /* Variables are expanded in the parse phase. We can free them here. */
543+ variable_all_del();
544+
528545 if (yynerrs)
529546 exit(1);
530547 if (!modules_sym)