summaryrefslogtreecommitdiffstats
path: root/scripts/kconfig/preprocess.c
diff options
context:
space:
mode:
authorMasahiro Yamada <yamada.masahiro@socionext.com>2018-05-28 05:21:40 -0400
committerMasahiro Yamada <yamada.masahiro@socionext.com>2018-05-28 14:28:58 -0400
commit104daea149c45cc84842ce77a9bd6436d19f3dd8 (patch)
tree7f0aaa24e9fa03154a74b2bd7bcb93f24d678ea4 /scripts/kconfig/preprocess.c
parentf1089c92da791034af73478159626007cba7f092 (diff)
kconfig: reference environment variables directly and remove 'option env='
To get access to environment variables, Kconfig needs to define a symbol using "option env=" syntax. It is tedious to add a symbol entry for each environment variable given that we need to define much more such as 'CC', 'AS', 'srctree' etc. to evaluate the compiler capability in Kconfig. Adding '$' for symbol references is grammatically inconsistent. Looking at the code, the symbols prefixed with 'S' are expanded by: - conf_expand_value() This is used to expand 'arch/$ARCH/defconfig' and 'defconfig_list' - sym_expand_string_value() This is used to expand strings in 'source' and 'mainmenu' All of them are fixed values independent of user configuration. So, they can be changed into the direct expansion instead of symbols. This change makes the code much cleaner. The bounce symbols 'SRCARCH', 'ARCH', 'SUBARCH', 'KERNELVERSION' are gone. sym_init() hard-coding 'UNAME_RELEASE' is also gone. 'UNAME_RELEASE' should be replaced with an environment variable. ARCH_DEFCONFIG is a normal symbol, so it should be simply referenced without '$' prefix. The new syntax is addicted by Make. The variable reference needs parentheses, like $(FOO), but you can omit them for single-letter variables, like $F. Yet, in Makefiles, people tend to use the parenthetical form for consistency / clarification. At this moment, only the environment variable is supported, but I will extend the concept of 'variable' later on. The variables are expanded in the lexer so we can simplify the token handling on the parser side. For example, the following code works. [Example code] config MY_TOOLCHAIN_LIST string default "My tools: CC=$(CC), AS=$(AS), CPP=$(CPP)" [Result] $ make -s alldefconfig && tail -n 1 .config CONFIG_MY_TOOLCHAIN_LIST="My tools: CC=gcc, AS=as, CPP=gcc -E" Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Reviewed-by: Kees Cook <keescook@chromium.org>
Diffstat (limited to 'scripts/kconfig/preprocess.c')
-rw-r--r--scripts/kconfig/preprocess.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c
new file mode 100644
index 000000000000..a2eb2eb02929
--- /dev/null
+++ b/scripts/kconfig/preprocess.c
@@ -0,0 +1,238 @@
1// SPDX-License-Identifier: GPL-2.0
2//
3// Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com>
4
5#include <stdarg.h>
6#include <stdbool.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include "list.h"
12
13static void __attribute__((noreturn)) pperror(const char *format, ...)
14{
15 va_list ap;
16
17 fprintf(stderr, "%s:%d: ", current_file->name, yylineno);
18 va_start(ap, format);
19 vfprintf(stderr, format, ap);
20 va_end(ap);
21 fprintf(stderr, "\n");
22
23 exit(1);
24}
25
26/*
27 * Environment variables
28 */
29static LIST_HEAD(env_list);
30
31struct env {
32 char *name;
33 char *value;
34 struct list_head node;
35};
36
37static void env_add(const char *name, const char *value)
38{
39 struct env *e;
40
41 e = xmalloc(sizeof(*e));
42 e->name = xstrdup(name);
43 e->value = xstrdup(value);
44
45 list_add_tail(&e->node, &env_list);
46}
47
48static void env_del(struct env *e)
49{
50 list_del(&e->node);
51 free(e->name);
52 free(e->value);
53 free(e);
54}
55
56/* The returned pointer must be freed when done */
57static char *env_expand(const char *name)
58{
59 struct env *e;
60 const char *value;
61
62 if (!*name)
63 return NULL;
64
65 list_for_each_entry(e, &env_list, node) {
66 if (!strcmp(name, e->name))
67 return xstrdup(e->value);
68 }
69
70 value = getenv(name);
71 if (!value)
72 return NULL;
73
74 /*
75 * We need to remember all referenced environment variables.
76 * They will be written out to include/config/auto.conf.cmd
77 */
78 env_add(name, value);
79
80 return xstrdup(value);
81}
82
83void env_write_dep(FILE *f, const char *autoconfig_name)
84{
85 struct env *e, *tmp;
86
87 list_for_each_entry_safe(e, tmp, &env_list, node) {
88 fprintf(f, "ifneq \"$(%s)\" \"%s\"\n", e->name, e->value);
89 fprintf(f, "%s: FORCE\n", autoconfig_name);
90 fprintf(f, "endif\n");
91 env_del(e);
92 }
93}
94
95static char *eval_clause(const char *str, size_t len)
96{
97 char *tmp, *name, *res;
98
99 tmp = xstrndup(str, len);
100
101 name = expand_string(tmp);
102
103 res = env_expand(name);
104 if (res)
105 goto free;
106
107 res = xstrdup("");
108free:
109 free(name);
110 free(tmp);
111
112 return res;
113}
114
115/*
116 * Expand a string that follows '$'
117 *
118 * For example, if the input string is
119 * ($(FOO)$($(BAR)))$(BAZ)
120 * this helper evaluates
121 * $($(FOO)$($(BAR)))
122 * and returns a new string containing the expansion (note that the string is
123 * recursively expanded), also advancing 'str' to point to the next character
124 * after the corresponding closing parenthesis, in this case, *str will be
125 * $(BAR)
126 */
127char *expand_dollar(const char **str)
128{
129 const char *p = *str;
130 const char *q;
131 int nest = 0;
132
133 /*
134 * In Kconfig, variable references always start with "$(".
135 * Neither single-letter variables as in $A nor curly braces as in ${CC}
136 * are supported. '$' not followed by '(' loses its special meaning.
137 */
138 if (*p != '(') {
139 *str = p;
140 return xstrdup("$");
141 }
142
143 p++;
144 q = p;
145 while (*q) {
146 if (*q == '(') {
147 nest++;
148 } else if (*q == ')') {
149 if (nest-- == 0)
150 break;
151 }
152 q++;
153 }
154
155 if (!*q)
156 pperror("unterminated reference to '%s': missing ')'", p);
157
158 /* Advance 'str' to after the expanded initial portion of the string */
159 *str = q + 1;
160
161 return eval_clause(p, q - p);
162}
163
164static char *__expand_string(const char **str, bool (*is_end)(char c))
165{
166 const char *in, *p;
167 char *expansion, *out;
168 size_t in_len, out_len;
169
170 out = xmalloc(1);
171 *out = 0;
172 out_len = 1;
173
174 p = in = *str;
175
176 while (1) {
177 if (*p == '$') {
178 in_len = p - in;
179 p++;
180 expansion = expand_dollar(&p);
181 out_len += in_len + strlen(expansion);
182 out = xrealloc(out, out_len);
183 strncat(out, in, in_len);
184 strcat(out, expansion);
185 free(expansion);
186 in = p;
187 continue;
188 }
189
190 if (is_end(*p))
191 break;
192
193 p++;
194 }
195
196 in_len = p - in;
197 out_len += in_len;
198 out = xrealloc(out, out_len);
199 strncat(out, in, in_len);
200
201 /* Advance 'str' to the end character */
202 *str = p;
203
204 return out;
205}
206
207static bool is_end_of_str(char c)
208{
209 return !c;
210}
211
212/*
213 * Expand variables in the given string. Undefined variables
214 * expand to an empty string.
215 * The returned string must be freed when done.
216 */
217char *expand_string(const char *in)
218{
219 return __expand_string(&in, is_end_of_str);
220}
221
222static bool is_end_of_token(char c)
223{
224 /* Why are '.' and '/' valid characters for symbols? */
225 return !(isalnum(c) || c == '_' || c == '-' || c == '.' || c == '/');
226}
227
228/*
229 * Expand variables in a token. The parsing stops when a token separater
230 * (in most cases, it is a whitespace) is encountered. 'str' is updated to
231 * point to the next character.
232 *
233 * The returned string must be freed when done.
234 */
235char *expand_one_token(const char **str)
236{
237 return __expand_string(str, is_end_of_token);
238}