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>
This commit is contained in:
parent
9de071536c
commit
9ced3bddec
@ -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);
|
||||
|
@ -177,6 +177,72 @@ static char *function_expand(const char *name, int argc, char *argv[])
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 @@ free:
|
||||
for (i = 0; i < new_argc; i++)
|
||||
free(new_argv[i]);
|
||||
free(name);
|
||||
free_tmp:
|
||||
free(tmp);
|
||||
|
||||
return res;
|
||||
|
@ -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 <zippel@linux-m68k.org>
|
||||
* Released under the terms of the GNU GPL v2.0.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -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_-]
|
||||
}
|
||||
}
|
||||
|
||||
<ASSIGN_VAL>{
|
||||
[^[:blank:]\n]+.* {
|
||||
alloc_string(yytext, yyleng);
|
||||
yylval.string = text;
|
||||
return T_ASSIGN_VAL;
|
||||
}
|
||||
\n { BEGIN(INITIAL); return T_EOL; }
|
||||
.
|
||||
}
|
||||
|
||||
<PARAM>{
|
||||
"&&" return T_AND;
|
||||
"||" return T_OR;
|
||||
|
@ -77,6 +77,9 @@ static struct menu *current_menu, *current_entry;
|
||||
%token T_CLOSE_PAREN
|
||||
%token T_OPEN_PAREN
|
||||
%token T_EOL
|
||||
%token <string> T_VARIABLE
|
||||
%token T_ASSIGN
|
||||
%token <string> T_ASSIGN_VAL
|
||||
|
||||
%left T_OR
|
||||
%left T_AND
|
||||
@ -92,7 +95,7 @@ static struct menu *current_menu, *current_entry;
|
||||
%type <id> end
|
||||
%type <id> option_name
|
||||
%type <menu> if_entry menu_entry choice_entry
|
||||
%type <string> symbol_option_arg word_opt
|
||||
%type <string> 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)
|
||||
|
Loading…
Reference in New Issue
Block a user