Commit 8639f69a61b47971dba47ab5ed72e47436729bb1
1 parent
ee3e520dad
Exists in
smarc_8mq_lf_v2020.04
and in
17 other branches
genconfig.py: Print defconfig next to warnings
At present we sometimes see warnings of the form: /tmp/tmpMA89kB:36: warning: overriding the value of CMD_SPL. Old value: "y", new value: "y". This is not very useful as it does not show whch defconfig file it relates to. Update the tool to show this. Signed-off-by: Simon Glass <sjg@chromium.org>
Showing 2 changed files with 21 additions and 8 deletions Inline Diff
tools/buildman/kconfiglib.py
1 | # | 1 | # |
2 | # SPDX-License-Identifier: ISC | 2 | # SPDX-License-Identifier: ISC |
3 | # | 3 | # |
4 | # Author: Ulf Magnusson | 4 | # Author: Ulf Magnusson |
5 | # https://github.com/ulfalizer/Kconfiglib | 5 | # https://github.com/ulfalizer/Kconfiglib |
6 | 6 | ||
7 | # This is Kconfiglib, a Python library for scripting, debugging, and extracting | 7 | # This is Kconfiglib, a Python library for scripting, debugging, and extracting |
8 | # information from Kconfig-based configuration systems. To view the | 8 | # information from Kconfig-based configuration systems. To view the |
9 | # documentation, run | 9 | # documentation, run |
10 | # | 10 | # |
11 | # $ pydoc kconfiglib | 11 | # $ pydoc kconfiglib |
12 | # | 12 | # |
13 | # or, if you prefer HTML, | 13 | # or, if you prefer HTML, |
14 | # | 14 | # |
15 | # $ pydoc -w kconfiglib | 15 | # $ pydoc -w kconfiglib |
16 | # | 16 | # |
17 | # The examples/ subdirectory contains examples, to be run with e.g. | 17 | # The examples/ subdirectory contains examples, to be run with e.g. |
18 | # | 18 | # |
19 | # $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py | 19 | # $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py |
20 | # | 20 | # |
21 | # Look in testsuite.py for the test suite. | 21 | # Look in testsuite.py for the test suite. |
22 | 22 | ||
23 | """ | 23 | """ |
24 | Kconfiglib is a Python library for scripting and extracting information from | 24 | Kconfiglib is a Python library for scripting and extracting information from |
25 | Kconfig-based configuration systems. Features include the following: | 25 | Kconfig-based configuration systems. Features include the following: |
26 | 26 | ||
27 | - Symbol values and properties can be looked up and values assigned | 27 | - Symbol values and properties can be looked up and values assigned |
28 | programmatically. | 28 | programmatically. |
29 | - .config files can be read and written. | 29 | - .config files can be read and written. |
30 | - Expressions can be evaluated in the context of a Kconfig configuration. | 30 | - Expressions can be evaluated in the context of a Kconfig configuration. |
31 | - Relations between symbols can be quickly determined, such as finding all | 31 | - Relations between symbols can be quickly determined, such as finding all |
32 | symbols that reference a particular symbol. | 32 | symbols that reference a particular symbol. |
33 | - Highly compatible with the scripts/kconfig/*conf utilities. The test suite | 33 | - Highly compatible with the scripts/kconfig/*conf utilities. The test suite |
34 | automatically compares outputs between Kconfiglib and the C implementation | 34 | automatically compares outputs between Kconfiglib and the C implementation |
35 | for a large number of cases. | 35 | for a large number of cases. |
36 | 36 | ||
37 | For the Linux kernel, scripts are run using | 37 | For the Linux kernel, scripts are run using |
38 | 38 | ||
39 | $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>] | 39 | $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>] |
40 | 40 | ||
41 | Using the 'scriptconfig' target ensures that required environment variables | 41 | Using the 'scriptconfig' target ensures that required environment variables |
42 | (SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly. | 42 | (SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly. |
43 | 43 | ||
44 | Scripts receive the name of the Kconfig file to load in sys.argv[1]. As of | 44 | Scripts receive the name of the Kconfig file to load in sys.argv[1]. As of |
45 | Linux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory. | 45 | Linux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory. |
46 | If an argument is provided with SCRIPT_ARG, it appears as sys.argv[2]. | 46 | If an argument is provided with SCRIPT_ARG, it appears as sys.argv[2]. |
47 | 47 | ||
48 | To get an interactive Python prompt with Kconfiglib preloaded and a Config | 48 | To get an interactive Python prompt with Kconfiglib preloaded and a Config |
49 | object 'c' created, run | 49 | object 'c' created, run |
50 | 50 | ||
51 | $ make iscriptconfig [ARCH=<arch>] | 51 | $ make iscriptconfig [ARCH=<arch>] |
52 | 52 | ||
53 | Kconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python | 53 | Kconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python |
54 | interpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy | 54 | interpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy |
55 | works well too, and might give a nice speedup for long-running jobs. | 55 | works well too, and might give a nice speedup for long-running jobs. |
56 | 56 | ||
57 | The examples/ directory contains short example scripts, which can be run with | 57 | The examples/ directory contains short example scripts, which can be run with |
58 | e.g. | 58 | e.g. |
59 | 59 | ||
60 | $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py | 60 | $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py |
61 | 61 | ||
62 | or | 62 | or |
63 | 63 | ||
64 | $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel | 64 | $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel |
65 | 65 | ||
66 | testsuite.py contains the test suite. See the top of the script for how to run | 66 | testsuite.py contains the test suite. See the top of the script for how to run |
67 | it. | 67 | it. |
68 | 68 | ||
69 | Credits: Written by Ulf "Ulfalizer" Magnusson | 69 | Credits: Written by Ulf "Ulfalizer" Magnusson |
70 | 70 | ||
71 | Send bug reports, suggestions and other feedback to ulfalizer a.t Google's | 71 | Send bug reports, suggestions and other feedback to ulfalizer a.t Google's |
72 | email service. Don't wrestle with internal APIs. Tell me what you need and I | 72 | email service. Don't wrestle with internal APIs. Tell me what you need and I |
73 | might add it in a safe way as a client API instead.""" | 73 | might add it in a safe way as a client API instead.""" |
74 | 74 | ||
75 | import os | 75 | import os |
76 | import re | 76 | import re |
77 | import sys | 77 | import sys |
78 | 78 | ||
79 | # File layout: | 79 | # File layout: |
80 | # | 80 | # |
81 | # Public classes | 81 | # Public classes |
82 | # Public functions | 82 | # Public functions |
83 | # Internal classes | 83 | # Internal classes |
84 | # Internal functions | 84 | # Internal functions |
85 | # Internal global constants | 85 | # Internal global constants |
86 | 86 | ||
87 | # Line length: 79 columns | 87 | # Line length: 79 columns |
88 | 88 | ||
89 | # | 89 | # |
90 | # Public classes | 90 | # Public classes |
91 | # | 91 | # |
92 | 92 | ||
93 | class Config(object): | 93 | class Config(object): |
94 | 94 | ||
95 | """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the | 95 | """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the |
96 | set of symbols and other items appearing in the configuration together with | 96 | set of symbols and other items appearing in the configuration together with |
97 | their values. Creating any number of Config objects -- including for | 97 | their values. Creating any number of Config objects -- including for |
98 | different architectures -- is safe; Kconfiglib has no global state.""" | 98 | different architectures -- is safe; Kconfiglib has no global state.""" |
99 | 99 | ||
100 | # | 100 | # |
101 | # Public interface | 101 | # Public interface |
102 | # | 102 | # |
103 | 103 | ||
104 | def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True, | 104 | def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True, |
105 | print_undef_assign=False): | 105 | print_undef_assign=False): |
106 | """Creates a new Config object, representing a Kconfig configuration. | 106 | """Creates a new Config object, representing a Kconfig configuration. |
107 | Raises Kconfig_Syntax_Error on syntax errors. | 107 | Raises Kconfig_Syntax_Error on syntax errors. |
108 | 108 | ||
109 | filename (default: "Kconfig"): The base Kconfig file of the | 109 | filename (default: "Kconfig"): The base Kconfig file of the |
110 | configuration. For the Linux kernel, you'll probably want "Kconfig" | 110 | configuration. For the Linux kernel, you'll probably want "Kconfig" |
111 | from the top-level directory, as environment variables will make | 111 | from the top-level directory, as environment variables will make |
112 | sure the right Kconfig is included from there | 112 | sure the right Kconfig is included from there |
113 | (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make | 113 | (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make |
114 | scriptconfig', the filename of the base base Kconfig file will be in | 114 | scriptconfig', the filename of the base base Kconfig file will be in |
115 | sys.argv[1]. | 115 | sys.argv[1]. |
116 | 116 | ||
117 | base_dir (default: None): The base directory relative to which 'source' | 117 | base_dir (default: None): The base directory relative to which 'source' |
118 | statements within Kconfig files will work. For the Linux kernel this | 118 | statements within Kconfig files will work. For the Linux kernel this |
119 | should be the top-level directory of the kernel tree. $-references | 119 | should be the top-level directory of the kernel tree. $-references |
120 | to existing environment variables will be expanded. | 120 | to existing environment variables will be expanded. |
121 | 121 | ||
122 | If None (the default), the environment variable 'srctree' will be | 122 | If None (the default), the environment variable 'srctree' will be |
123 | used if set, and the current directory otherwise. 'srctree' is set | 123 | used if set, and the current directory otherwise. 'srctree' is set |
124 | by the Linux makefiles to the top-level kernel directory. A default | 124 | by the Linux makefiles to the top-level kernel directory. A default |
125 | of "." would not work with an alternative build directory. | 125 | of "." would not work with an alternative build directory. |
126 | 126 | ||
127 | print_warnings (default: True): Set to True if warnings related to this | 127 | print_warnings (default: True): Set to True if warnings related to this |
128 | configuration should be printed to stderr. This can be changed later | 128 | configuration should be printed to stderr. This can be changed later |
129 | with Config.set_print_warnings(). It is provided as a constructor | 129 | with Config.set_print_warnings(). It is provided as a constructor |
130 | argument since warnings might be generated during parsing. | 130 | argument since warnings might be generated during parsing. |
131 | 131 | ||
132 | print_undef_assign (default: False): Set to True if informational | 132 | print_undef_assign (default: False): Set to True if informational |
133 | messages related to assignments to undefined symbols should be | 133 | messages related to assignments to undefined symbols should be |
134 | printed to stderr for this configuration. Can be changed later with | 134 | printed to stderr for this configuration. Can be changed later with |
135 | Config.set_print_undef_assign().""" | 135 | Config.set_print_undef_assign().""" |
136 | 136 | ||
137 | # The set of all symbols, indexed by name (a string) | 137 | # The set of all symbols, indexed by name (a string) |
138 | self.syms = {} | 138 | self.syms = {} |
139 | # Python 2/3 compatibility hack. This is the only one needed. | 139 | # Python 2/3 compatibility hack. This is the only one needed. |
140 | if sys.version_info[0] >= 3: | 140 | if sys.version_info[0] >= 3: |
141 | self.syms_iter = self.syms.values | 141 | self.syms_iter = self.syms.values |
142 | else: | 142 | else: |
143 | self.syms_iter = self.syms.itervalues | 143 | self.syms_iter = self.syms.itervalues |
144 | 144 | ||
145 | # The set of all defined symbols in the configuration in the order they | 145 | # The set of all defined symbols in the configuration in the order they |
146 | # appear in the Kconfig files. This excludes the special symbols n, m, | 146 | # appear in the Kconfig files. This excludes the special symbols n, m, |
147 | # and y as well as symbols that are referenced but never defined. | 147 | # and y as well as symbols that are referenced but never defined. |
148 | self.kconfig_syms = [] | 148 | self.kconfig_syms = [] |
149 | 149 | ||
150 | # The set of all named choices (yes, choices can have names), indexed | 150 | # The set of all named choices (yes, choices can have names), indexed |
151 | # by name (a string) | 151 | # by name (a string) |
152 | self.named_choices = {} | 152 | self.named_choices = {} |
153 | 153 | ||
154 | # Lists containing all choices, menus and comments in the configuration | 154 | # Lists containing all choices, menus and comments in the configuration |
155 | self.choices = [] | 155 | self.choices = [] |
156 | self.menus = [] | 156 | self.menus = [] |
157 | self.comments = [] | 157 | self.comments = [] |
158 | 158 | ||
159 | def register_special_symbol(type_, name, val): | 159 | def register_special_symbol(type_, name, val): |
160 | sym = Symbol() | 160 | sym = Symbol() |
161 | sym.is_special_ = True | 161 | sym.is_special_ = True |
162 | sym.is_defined_ = True | 162 | sym.is_defined_ = True |
163 | sym.config = self | 163 | sym.config = self |
164 | sym.name = name | 164 | sym.name = name |
165 | sym.type = type_ | 165 | sym.type = type_ |
166 | sym.cached_val = val | 166 | sym.cached_val = val |
167 | self.syms[name] = sym | 167 | self.syms[name] = sym |
168 | return sym | 168 | return sym |
169 | 169 | ||
170 | # The special symbols n, m and y, used as shorthand for "n", "m" and | 170 | # The special symbols n, m and y, used as shorthand for "n", "m" and |
171 | # "y" | 171 | # "y" |
172 | self.n = register_special_symbol(TRISTATE, "n", "n") | 172 | self.n = register_special_symbol(TRISTATE, "n", "n") |
173 | self.m = register_special_symbol(TRISTATE, "m", "m") | 173 | self.m = register_special_symbol(TRISTATE, "m", "m") |
174 | self.y = register_special_symbol(TRISTATE, "y", "y") | 174 | self.y = register_special_symbol(TRISTATE, "y", "y") |
175 | # DEFCONFIG_LIST uses this | 175 | # DEFCONFIG_LIST uses this |
176 | register_special_symbol(STRING, "UNAME_RELEASE", os.uname()[2]) | 176 | register_special_symbol(STRING, "UNAME_RELEASE", os.uname()[2]) |
177 | 177 | ||
178 | # The symbol with "option defconfig_list" set, containing a list of | 178 | # The symbol with "option defconfig_list" set, containing a list of |
179 | # default .config files | 179 | # default .config files |
180 | self.defconfig_sym = None | 180 | self.defconfig_sym = None |
181 | 181 | ||
182 | # See Symbol.get_(src)arch() | 182 | # See Symbol.get_(src)arch() |
183 | self.arch = os.environ.get("ARCH") | 183 | self.arch = os.environ.get("ARCH") |
184 | self.srcarch = os.environ.get("SRCARCH") | 184 | self.srcarch = os.environ.get("SRCARCH") |
185 | 185 | ||
186 | # See Config.__init__(). We need this for get_defconfig_filename(). | 186 | # See Config.__init__(). We need this for get_defconfig_filename(). |
187 | self.srctree = os.environ.get("srctree") | 187 | self.srctree = os.environ.get("srctree") |
188 | if self.srctree is None: | 188 | if self.srctree is None: |
189 | self.srctree = "." | 189 | self.srctree = "." |
190 | 190 | ||
191 | self.filename = filename | 191 | self.filename = filename |
192 | if base_dir is None: | 192 | if base_dir is None: |
193 | self.base_dir = self.srctree | 193 | self.base_dir = self.srctree |
194 | else: | 194 | else: |
195 | self.base_dir = os.path.expandvars(base_dir) | 195 | self.base_dir = os.path.expandvars(base_dir) |
196 | 196 | ||
197 | # The 'mainmenu' text | 197 | # The 'mainmenu' text |
198 | self.mainmenu_text = None | 198 | self.mainmenu_text = None |
199 | 199 | ||
200 | # The filename of the most recently loaded .config file | 200 | # The filename of the most recently loaded .config file |
201 | self.config_filename = None | 201 | self.config_filename = None |
202 | # The textual header of the most recently loaded .config, uncommented | 202 | # The textual header of the most recently loaded .config, uncommented |
203 | self.config_header = None | 203 | self.config_header = None |
204 | 204 | ||
205 | self.print_warnings = print_warnings | 205 | self.print_warnings = print_warnings |
206 | self.print_undef_assign = print_undef_assign | 206 | self.print_undef_assign = print_undef_assign |
207 | self._warnings = [] | ||
207 | 208 | ||
208 | # For parsing routines that stop when finding a line belonging to a | 209 | # For parsing routines that stop when finding a line belonging to a |
209 | # different construct, these holds that line and the tokenized version | 210 | # different construct, these holds that line and the tokenized version |
210 | # of that line. The purpose is to avoid having to re-tokenize the line, | 211 | # of that line. The purpose is to avoid having to re-tokenize the line, |
211 | # which is inefficient and causes problems when recording references to | 212 | # which is inefficient and causes problems when recording references to |
212 | # symbols. | 213 | # symbols. |
213 | self.end_line = None | 214 | self.end_line = None |
214 | self.end_line_tokens = None | 215 | self.end_line_tokens = None |
215 | 216 | ||
216 | # See the comment in _parse_expr(). | 217 | # See the comment in _parse_expr(). |
217 | self._cur_item = None | 218 | self._cur_item = None |
218 | self._line = None | 219 | self._line = None |
219 | self._filename = None | 220 | self._filename = None |
220 | self._linenr = None | 221 | self._linenr = None |
221 | self._transform_m = None | 222 | self._transform_m = None |
222 | 223 | ||
223 | # Parse the Kconfig files | 224 | # Parse the Kconfig files |
224 | self.top_block = self._parse_file(filename, None, None, None) | 225 | self.top_block = self._parse_file(filename, None, None, None) |
225 | 226 | ||
226 | # Build Symbol.dep for all symbols | 227 | # Build Symbol.dep for all symbols |
227 | self._build_dep() | 228 | self._build_dep() |
228 | 229 | ||
229 | def get_arch(self): | 230 | def get_arch(self): |
230 | """Returns the value the environment variable ARCH had at the time the | 231 | """Returns the value the environment variable ARCH had at the time the |
231 | Config instance was created, or None if ARCH was not set. For the | 232 | Config instance was created, or None if ARCH was not set. For the |
232 | kernel, this corresponds to the architecture being built for, with | 233 | kernel, this corresponds to the architecture being built for, with |
233 | values such as "i386" or "mips".""" | 234 | values such as "i386" or "mips".""" |
234 | return self.arch | 235 | return self.arch |
235 | 236 | ||
236 | def get_srcarch(self): | 237 | def get_srcarch(self): |
237 | """Returns the value the environment variable SRCARCH had at the time | 238 | """Returns the value the environment variable SRCARCH had at the time |
238 | the Config instance was created, or None if SRCARCH was not set. For | 239 | the Config instance was created, or None if SRCARCH was not set. For |
239 | the kernel, this corresponds to the particular arch/ subdirectory | 240 | the kernel, this corresponds to the particular arch/ subdirectory |
240 | containing architecture-specific code.""" | 241 | containing architecture-specific code.""" |
241 | return self.srcarch | 242 | return self.srcarch |
242 | 243 | ||
243 | def get_srctree(self): | 244 | def get_srctree(self): |
244 | """Returns the value the environment variable srctree had at the time | 245 | """Returns the value the environment variable srctree had at the time |
245 | the Config instance was created, or None if srctree was not defined. | 246 | the Config instance was created, or None if srctree was not defined. |
246 | This variable points to the source directory and is used when building | 247 | This variable points to the source directory and is used when building |
247 | in a separate directory.""" | 248 | in a separate directory.""" |
248 | return self.srctree | 249 | return self.srctree |
249 | 250 | ||
250 | def get_base_dir(self): | 251 | def get_base_dir(self): |
251 | """Returns the base directory relative to which 'source' statements | 252 | """Returns the base directory relative to which 'source' statements |
252 | will work, passed as an argument to Config.__init__().""" | 253 | will work, passed as an argument to Config.__init__().""" |
253 | return self.base_dir | 254 | return self.base_dir |
254 | 255 | ||
255 | def get_kconfig_filename(self): | 256 | def get_kconfig_filename(self): |
256 | """Returns the name of the (base) kconfig file this configuration was | 257 | """Returns the name of the (base) kconfig file this configuration was |
257 | loaded from.""" | 258 | loaded from.""" |
258 | return self.filename | 259 | return self.filename |
259 | 260 | ||
260 | def get_config_filename(self): | 261 | def get_config_filename(self): |
261 | """Returns the filename of the most recently loaded configuration file, | 262 | """Returns the filename of the most recently loaded configuration file, |
262 | or None if no configuration has been loaded.""" | 263 | or None if no configuration has been loaded.""" |
263 | return self.config_filename | 264 | return self.config_filename |
264 | 265 | ||
265 | def get_config_header(self): | 266 | def get_config_header(self): |
266 | """Returns the (uncommented) textual header of the .config file most | 267 | """Returns the (uncommented) textual header of the .config file most |
267 | recently loaded with load_config(). Returns None if no .config file has | 268 | recently loaded with load_config(). Returns None if no .config file has |
268 | been loaded or if the most recently loaded .config file has no header. | 269 | been loaded or if the most recently loaded .config file has no header. |
269 | The header consists of all lines up to but not including the first line | 270 | The header consists of all lines up to but not including the first line |
270 | that either | 271 | that either |
271 | 272 | ||
272 | 1. Does not start with "#" | 273 | 1. Does not start with "#" |
273 | 2. Has the form "# CONFIG_FOO is not set." | 274 | 2. Has the form "# CONFIG_FOO is not set." |
274 | """ | 275 | """ |
275 | return self.config_header | 276 | return self.config_header |
276 | 277 | ||
277 | def get_mainmenu_text(self): | 278 | def get_mainmenu_text(self): |
278 | """Returns the text of the 'mainmenu' statement (with $-references to | 279 | """Returns the text of the 'mainmenu' statement (with $-references to |
279 | symbols replaced by symbol values), or None if the configuration has no | 280 | symbols replaced by symbol values), or None if the configuration has no |
280 | 'mainmenu' statement.""" | 281 | 'mainmenu' statement.""" |
281 | return None if self.mainmenu_text is None else \ | 282 | return None if self.mainmenu_text is None else \ |
282 | self._expand_sym_refs(self.mainmenu_text) | 283 | self._expand_sym_refs(self.mainmenu_text) |
283 | 284 | ||
284 | def get_defconfig_filename(self): | 285 | def get_defconfig_filename(self): |
285 | """Returns the name of the defconfig file, which is the first existing | 286 | """Returns the name of the defconfig file, which is the first existing |
286 | file in the list given in a symbol having 'option defconfig_list' set. | 287 | file in the list given in a symbol having 'option defconfig_list' set. |
287 | $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if | 288 | $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if |
288 | FOO has the value "foo"). Returns None in case of no defconfig file. | 289 | FOO has the value "foo"). Returns None in case of no defconfig file. |
289 | Setting 'option defconfig_list' on multiple symbols currently results | 290 | Setting 'option defconfig_list' on multiple symbols currently results |
290 | in undefined behavior. | 291 | in undefined behavior. |
291 | 292 | ||
292 | If the environment variable 'srctree' was set when the Config was | 293 | If the environment variable 'srctree' was set when the Config was |
293 | created, get_defconfig_filename() will first look relative to that | 294 | created, get_defconfig_filename() will first look relative to that |
294 | directory before looking in the current directory; see | 295 | directory before looking in the current directory; see |
295 | Config.__init__(). | 296 | Config.__init__(). |
296 | 297 | ||
297 | WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses | 298 | WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses |
298 | the --defconfig=<defconfig> option when calling the C implementation of | 299 | the --defconfig=<defconfig> option when calling the C implementation of |
299 | e.g. 'make defconfig'. This option overrides the 'option | 300 | e.g. 'make defconfig'. This option overrides the 'option |
300 | defconfig_list' symbol, meaning the result from | 301 | defconfig_list' symbol, meaning the result from |
301 | get_defconfig_filename() might not match what 'make defconfig' would | 302 | get_defconfig_filename() might not match what 'make defconfig' would |
302 | use. That probably ought to be worked around somehow, so that this | 303 | use. That probably ought to be worked around somehow, so that this |
303 | function always gives the "expected" result.""" | 304 | function always gives the "expected" result.""" |
304 | if self.defconfig_sym is None: | 305 | if self.defconfig_sym is None: |
305 | return None | 306 | return None |
306 | for filename, cond_expr in self.defconfig_sym.def_exprs: | 307 | for filename, cond_expr in self.defconfig_sym.def_exprs: |
307 | if self._eval_expr(cond_expr) == "y": | 308 | if self._eval_expr(cond_expr) == "y": |
308 | filename = self._expand_sym_refs(filename) | 309 | filename = self._expand_sym_refs(filename) |
309 | # We first look in $srctree. os.path.join() won't work here as | 310 | # We first look in $srctree. os.path.join() won't work here as |
310 | # an absolute path in filename would override $srctree. | 311 | # an absolute path in filename would override $srctree. |
311 | srctree_filename = os.path.normpath(self.srctree + "/" + | 312 | srctree_filename = os.path.normpath(self.srctree + "/" + |
312 | filename) | 313 | filename) |
313 | if os.path.exists(srctree_filename): | 314 | if os.path.exists(srctree_filename): |
314 | return srctree_filename | 315 | return srctree_filename |
315 | if os.path.exists(filename): | 316 | if os.path.exists(filename): |
316 | return filename | 317 | return filename |
317 | return None | 318 | return None |
318 | 319 | ||
319 | def get_symbol(self, name): | 320 | def get_symbol(self, name): |
320 | """Returns the symbol with name 'name', or None if no such symbol | 321 | """Returns the symbol with name 'name', or None if no such symbol |
321 | appears in the configuration. An alternative shorthand is conf[name], | 322 | appears in the configuration. An alternative shorthand is conf[name], |
322 | where conf is a Config instance, though that will instead raise | 323 | where conf is a Config instance, though that will instead raise |
323 | KeyError if the symbol does not exist.""" | 324 | KeyError if the symbol does not exist.""" |
324 | return self.syms.get(name) | 325 | return self.syms.get(name) |
325 | 326 | ||
326 | def __getitem__(self, name): | 327 | def __getitem__(self, name): |
327 | """Returns the symbol with name 'name'. Raises KeyError if the symbol | 328 | """Returns the symbol with name 'name'. Raises KeyError if the symbol |
328 | does not appear in the configuration.""" | 329 | does not appear in the configuration.""" |
329 | return self.syms[name] | 330 | return self.syms[name] |
330 | 331 | ||
331 | def get_symbols(self, all_symbols=True): | 332 | def get_symbols(self, all_symbols=True): |
332 | """Returns a list of symbols from the configuration. An alternative for | 333 | """Returns a list of symbols from the configuration. An alternative for |
333 | iterating over all defined symbols (in the order of definition) is | 334 | iterating over all defined symbols (in the order of definition) is |
334 | 335 | ||
335 | for sym in config: | 336 | for sym in config: |
336 | ... | 337 | ... |
337 | 338 | ||
338 | which relies on Config implementing __iter__() and is equivalent to | 339 | which relies on Config implementing __iter__() and is equivalent to |
339 | 340 | ||
340 | for sym in config.get_symbols(False): | 341 | for sym in config.get_symbols(False): |
341 | ... | 342 | ... |
342 | 343 | ||
343 | all_symbols (default: True): If True, all symbols -- including special | 344 | all_symbols (default: True): If True, all symbols -- including special |
344 | and undefined symbols -- will be included in the result, in an | 345 | and undefined symbols -- will be included in the result, in an |
345 | undefined order. If False, only symbols actually defined and not | 346 | undefined order. If False, only symbols actually defined and not |
346 | merely referred to in the configuration will be included in the | 347 | merely referred to in the configuration will be included in the |
347 | result, and will appear in the order that they are defined within | 348 | result, and will appear in the order that they are defined within |
348 | the Kconfig configuration files.""" | 349 | the Kconfig configuration files.""" |
349 | return list(self.syms.values()) if all_symbols else self.kconfig_syms | 350 | return list(self.syms.values()) if all_symbols else self.kconfig_syms |
350 | 351 | ||
351 | def __iter__(self): | 352 | def __iter__(self): |
352 | """Convenience function for iterating over the set of all defined | 353 | """Convenience function for iterating over the set of all defined |
353 | symbols in the configuration, used like | 354 | symbols in the configuration, used like |
354 | 355 | ||
355 | for sym in conf: | 356 | for sym in conf: |
356 | ... | 357 | ... |
357 | 358 | ||
358 | The iteration happens in the order of definition within the Kconfig | 359 | The iteration happens in the order of definition within the Kconfig |
359 | configuration files. Symbols only referred to but not defined will not | 360 | configuration files. Symbols only referred to but not defined will not |
360 | be included, nor will the special symbols n, m, and y. If you want to | 361 | be included, nor will the special symbols n, m, and y. If you want to |
361 | include such symbols as well, see config.get_symbols().""" | 362 | include such symbols as well, see config.get_symbols().""" |
362 | return iter(self.kconfig_syms) | 363 | return iter(self.kconfig_syms) |
363 | 364 | ||
364 | def get_choices(self): | 365 | def get_choices(self): |
365 | """Returns a list containing all choice statements in the | 366 | """Returns a list containing all choice statements in the |
366 | configuration, in the order they appear in the Kconfig files.""" | 367 | configuration, in the order they appear in the Kconfig files.""" |
367 | return self.choices | 368 | return self.choices |
368 | 369 | ||
369 | def get_menus(self): | 370 | def get_menus(self): |
370 | """Returns a list containing all menus in the configuration, in the | 371 | """Returns a list containing all menus in the configuration, in the |
371 | order they appear in the Kconfig files.""" | 372 | order they appear in the Kconfig files.""" |
372 | return self.menus | 373 | return self.menus |
373 | 374 | ||
374 | def get_comments(self): | 375 | def get_comments(self): |
375 | """Returns a list containing all comments in the configuration, in the | 376 | """Returns a list containing all comments in the configuration, in the |
376 | order they appear in the Kconfig files.""" | 377 | order they appear in the Kconfig files.""" |
377 | return self.comments | 378 | return self.comments |
378 | 379 | ||
379 | def get_top_level_items(self): | 380 | def get_top_level_items(self): |
380 | """Returns a list containing the items (symbols, menus, choices, and | 381 | """Returns a list containing the items (symbols, menus, choices, and |
381 | comments) at the top level of the configuration -- that is, all items | 382 | comments) at the top level of the configuration -- that is, all items |
382 | that do not appear within a menu or choice. The items appear in the | 383 | that do not appear within a menu or choice. The items appear in the |
383 | same order as within the configuration.""" | 384 | same order as within the configuration.""" |
384 | return self.top_block | 385 | return self.top_block |
385 | 386 | ||
386 | def load_config(self, filename, replace=True): | 387 | def load_config(self, filename, replace=True): |
387 | """Loads symbol values from a file in the familiar .config format. | 388 | """Loads symbol values from a file in the familiar .config format. |
388 | Equivalent to calling Symbol.set_user_value() to set each of the | 389 | Equivalent to calling Symbol.set_user_value() to set each of the |
389 | values. | 390 | values. |
390 | 391 | ||
391 | "# CONFIG_FOO is not set" within a .config file is treated specially | 392 | "# CONFIG_FOO is not set" within a .config file is treated specially |
392 | and sets the user value of FOO to 'n'. The C implementation works the | 393 | and sets the user value of FOO to 'n'. The C implementation works the |
393 | same way. | 394 | same way. |
394 | 395 | ||
395 | filename: The .config file to load. $-references to existing | 396 | filename: The .config file to load. $-references to existing |
396 | environment variables will be expanded. For scripts to work even when | 397 | environment variables will be expanded. For scripts to work even when |
397 | an alternative build directory is used with the Linux kernel, you | 398 | an alternative build directory is used with the Linux kernel, you |
398 | need to refer to the top-level kernel directory with "$srctree". | 399 | need to refer to the top-level kernel directory with "$srctree". |
399 | 400 | ||
400 | replace (default: True): True if the configuration should replace the | 401 | replace (default: True): True if the configuration should replace the |
401 | old configuration; False if it should add to it.""" | 402 | old configuration; False if it should add to it. |
402 | 403 | ||
404 | Returns a list or warnings (hopefully empty) | ||
405 | """ | ||
406 | |||
407 | self._warnings = [] | ||
403 | # Put this first so that a missing file doesn't screw up our state | 408 | # Put this first so that a missing file doesn't screw up our state |
404 | filename = os.path.expandvars(filename) | 409 | filename = os.path.expandvars(filename) |
405 | line_feeder = _FileFeed(filename) | 410 | line_feeder = _FileFeed(filename) |
406 | 411 | ||
407 | self.config_filename = filename | 412 | self.config_filename = filename |
408 | 413 | ||
409 | # | 414 | # |
410 | # Read header | 415 | # Read header |
411 | # | 416 | # |
412 | 417 | ||
413 | def is_header_line(line): | 418 | def is_header_line(line): |
414 | return line is not None and line.startswith("#") and \ | 419 | return line is not None and line.startswith("#") and \ |
415 | not _unset_re_match(line) | 420 | not _unset_re_match(line) |
416 | 421 | ||
417 | self.config_header = None | 422 | self.config_header = None |
418 | 423 | ||
419 | line = line_feeder.peek_next() | 424 | line = line_feeder.peek_next() |
420 | if is_header_line(line): | 425 | if is_header_line(line): |
421 | self.config_header = "" | 426 | self.config_header = "" |
422 | while is_header_line(line_feeder.peek_next()): | 427 | while is_header_line(line_feeder.peek_next()): |
423 | self.config_header += line_feeder.get_next()[1:] | 428 | self.config_header += line_feeder.get_next()[1:] |
424 | # Remove trailing newline | 429 | # Remove trailing newline |
425 | if self.config_header.endswith("\n"): | 430 | if self.config_header.endswith("\n"): |
426 | self.config_header = self.config_header[:-1] | 431 | self.config_header = self.config_header[:-1] |
427 | 432 | ||
428 | # | 433 | # |
429 | # Read assignments. Hotspot for some workloads. | 434 | # Read assignments. Hotspot for some workloads. |
430 | # | 435 | # |
431 | 436 | ||
432 | def warn_override(filename, linenr, name, old_user_val, new_user_val): | 437 | def warn_override(filename, linenr, name, old_user_val, new_user_val): |
433 | self._warn('overriding the value of {0}. ' | 438 | self._warn('overriding the value of {0}. ' |
434 | 'Old value: "{1}", new value: "{2}".' | 439 | 'Old value: "{1}", new value: "{2}".' |
435 | .format(name, old_user_val, new_user_val), | 440 | .format(name, old_user_val, new_user_val), |
436 | filename, linenr) | 441 | filename, linenr) |
437 | 442 | ||
438 | # Invalidate everything to keep things simple. It might be possible to | 443 | # Invalidate everything to keep things simple. It might be possible to |
439 | # improve performance for the case where multiple configurations are | 444 | # improve performance for the case where multiple configurations are |
440 | # loaded by only invalidating a symbol (and its dependent symbols) if | 445 | # loaded by only invalidating a symbol (and its dependent symbols) if |
441 | # the new user value differs from the old. One complication would be | 446 | # the new user value differs from the old. One complication would be |
442 | # that symbols not mentioned in the .config must lose their user value | 447 | # that symbols not mentioned in the .config must lose their user value |
443 | # when replace = True, which is the usual case. | 448 | # when replace = True, which is the usual case. |
444 | if replace: | 449 | if replace: |
445 | self.unset_user_values() | 450 | self.unset_user_values() |
446 | else: | 451 | else: |
447 | self._invalidate_all() | 452 | self._invalidate_all() |
448 | 453 | ||
449 | while 1: | 454 | while 1: |
450 | line = line_feeder.get_next() | 455 | line = line_feeder.get_next() |
451 | if line is None: | 456 | if line is None: |
452 | return | 457 | return self._warnings |
453 | 458 | ||
454 | line = line.rstrip() | 459 | line = line.rstrip() |
455 | 460 | ||
456 | set_match = _set_re_match(line) | 461 | set_match = _set_re_match(line) |
457 | if set_match: | 462 | if set_match: |
458 | name, val = set_match.groups() | 463 | name, val = set_match.groups() |
459 | 464 | ||
460 | if val.startswith('"'): | 465 | if val.startswith('"'): |
461 | if len(val) < 2 or val[-1] != '"': | 466 | if len(val) < 2 or val[-1] != '"': |
462 | _parse_error(line, "malformed string literal", | 467 | _parse_error(line, "malformed string literal", |
463 | line_feeder.filename, line_feeder.linenr) | 468 | line_feeder.filename, line_feeder.linenr) |
464 | # Strip quotes and remove escapings. The unescaping | 469 | # Strip quotes and remove escapings. The unescaping |
465 | # procedure should be safe since " can only appear as \" | 470 | # procedure should be safe since " can only appear as \" |
466 | # inside the string. | 471 | # inside the string. |
467 | val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\") | 472 | val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\") |
468 | 473 | ||
469 | if name in self.syms: | 474 | if name in self.syms: |
470 | sym = self.syms[name] | 475 | sym = self.syms[name] |
471 | if sym.user_val is not None: | 476 | if sym.user_val is not None: |
472 | warn_override(line_feeder.filename, line_feeder.linenr, | 477 | warn_override(line_feeder.filename, line_feeder.linenr, |
473 | name, sym.user_val, val) | 478 | name, sym.user_val, val) |
474 | 479 | ||
475 | if sym.is_choice_sym: | 480 | if sym.is_choice_sym: |
476 | user_mode = sym.parent.user_mode | 481 | user_mode = sym.parent.user_mode |
477 | if user_mode is not None and user_mode != val: | 482 | if user_mode is not None and user_mode != val: |
478 | self._warn("assignment to {0} changes mode of " | 483 | self._warn("assignment to {0} changes mode of " |
479 | 'containing choice from "{1}" to "{2}".' | 484 | 'containing choice from "{1}" to "{2}".' |
480 | .format(name, val, user_mode), | 485 | .format(name, val, user_mode), |
481 | line_feeder.filename, | 486 | line_feeder.filename, |
482 | line_feeder.linenr) | 487 | line_feeder.linenr) |
483 | 488 | ||
484 | sym._set_user_value_no_invalidate(val, True) | 489 | sym._set_user_value_no_invalidate(val, True) |
485 | else: | 490 | else: |
486 | if self.print_undef_assign: | 491 | if self.print_undef_assign: |
487 | _stderr_msg('note: attempt to assign the value "{0}" ' | 492 | _stderr_msg('note: attempt to assign the value "{0}" ' |
488 | "to the undefined symbol {1}." | 493 | "to the undefined symbol {1}." |
489 | .format(val, name), | 494 | .format(val, name), |
490 | line_feeder.filename, line_feeder.linenr) | 495 | line_feeder.filename, line_feeder.linenr) |
491 | else: | 496 | else: |
492 | unset_match = _unset_re_match(line) | 497 | unset_match = _unset_re_match(line) |
493 | if unset_match: | 498 | if unset_match: |
494 | name = unset_match.group(1) | 499 | name = unset_match.group(1) |
495 | if name in self.syms: | 500 | if name in self.syms: |
496 | sym = self.syms[name] | 501 | sym = self.syms[name] |
497 | if sym.user_val is not None: | 502 | if sym.user_val is not None: |
498 | warn_override(line_feeder.filename, | 503 | warn_override(line_feeder.filename, |
499 | line_feeder.linenr, | 504 | line_feeder.linenr, |
500 | name, sym.user_val, "n") | 505 | name, sym.user_val, "n") |
501 | 506 | ||
502 | sym._set_user_value_no_invalidate("n", True) | 507 | sym._set_user_value_no_invalidate("n", True) |
503 | 508 | ||
504 | def write_config(self, filename, header=None): | 509 | def write_config(self, filename, header=None): |
505 | """Writes out symbol values in the familiar .config format. | 510 | """Writes out symbol values in the familiar .config format. |
506 | 511 | ||
507 | Kconfiglib makes sure the format matches what the C implementation | 512 | Kconfiglib makes sure the format matches what the C implementation |
508 | would generate, down to whitespace. This eases testing. | 513 | would generate, down to whitespace. This eases testing. |
509 | 514 | ||
510 | filename: The filename under which to save the configuration. | 515 | filename: The filename under which to save the configuration. |
511 | 516 | ||
512 | header (default: None): A textual header that will appear at the | 517 | header (default: None): A textual header that will appear at the |
513 | beginning of the file, with each line commented out automatically. | 518 | beginning of the file, with each line commented out automatically. |
514 | None means no header.""" | 519 | None means no header.""" |
515 | 520 | ||
516 | for sym in self.syms_iter(): | 521 | for sym in self.syms_iter(): |
517 | sym.already_written = False | 522 | sym.already_written = False |
518 | 523 | ||
519 | with open(filename, "w") as f: | 524 | with open(filename, "w") as f: |
520 | # Write header | 525 | # Write header |
521 | if header is not None: | 526 | if header is not None: |
522 | f.write(_comment(header)) | 527 | f.write(_comment(header)) |
523 | f.write("\n") | 528 | f.write("\n") |
524 | 529 | ||
525 | # Build and write configuration | 530 | # Build and write configuration |
526 | conf_strings = [] | 531 | conf_strings = [] |
527 | _make_block_conf(self.top_block, conf_strings.append) | 532 | _make_block_conf(self.top_block, conf_strings.append) |
528 | f.write("\n".join(conf_strings)) | 533 | f.write("\n".join(conf_strings)) |
529 | f.write("\n") | 534 | f.write("\n") |
530 | 535 | ||
531 | def eval(self, s): | 536 | def eval(self, s): |
532 | """Returns the value of the expression 's' -- where 's' is represented | 537 | """Returns the value of the expression 's' -- where 's' is represented |
533 | as a string -- in the context of the configuration. Raises | 538 | as a string -- in the context of the configuration. Raises |
534 | Kconfig_Syntax_Error if syntax errors are detected in 's'. | 539 | Kconfig_Syntax_Error if syntax errors are detected in 's'. |
535 | 540 | ||
536 | For example, if FOO and BAR are tristate symbols at least one of which | 541 | For example, if FOO and BAR are tristate symbols at least one of which |
537 | has the value "y", then config.eval("y && (FOO || BAR)") => "y" | 542 | has the value "y", then config.eval("y && (FOO || BAR)") => "y" |
538 | 543 | ||
539 | This function always yields a tristate value. To get the value of | 544 | This function always yields a tristate value. To get the value of |
540 | non-bool, non-tristate symbols, use Symbol.get_value(). | 545 | non-bool, non-tristate symbols, use Symbol.get_value(). |
541 | 546 | ||
542 | The result of this function is consistent with how evaluation works for | 547 | The result of this function is consistent with how evaluation works for |
543 | conditional expressions in the configuration as well as in the C | 548 | conditional expressions in the configuration as well as in the C |
544 | implementation. "m" and m are rewritten as '"m" && MODULES' and 'm && | 549 | implementation. "m" and m are rewritten as '"m" && MODULES' and 'm && |
545 | MODULES', respectively, and a result of "m" will get promoted to "y" if | 550 | MODULES', respectively, and a result of "m" will get promoted to "y" if |
546 | we're running without modules. | 551 | we're running without modules. |
547 | 552 | ||
548 | Syntax checking is somewhat lax, partly to be compatible with lax | 553 | Syntax checking is somewhat lax, partly to be compatible with lax |
549 | parsing in the C implementation.""" | 554 | parsing in the C implementation.""" |
550 | return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed | 555 | return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed |
551 | None, # Current symbol/choice | 556 | None, # Current symbol/choice |
552 | s)) # line | 557 | s)) # line |
553 | 558 | ||
554 | def unset_user_values(self): | 559 | def unset_user_values(self): |
555 | """Resets the values of all symbols, as if Config.load_config() or | 560 | """Resets the values of all symbols, as if Config.load_config() or |
556 | Symbol.set_user_value() had never been called.""" | 561 | Symbol.set_user_value() had never been called.""" |
557 | for sym in self.syms_iter(): | 562 | for sym in self.syms_iter(): |
558 | sym._unset_user_value_no_recursive_invalidate() | 563 | sym._unset_user_value_no_recursive_invalidate() |
559 | 564 | ||
560 | def set_print_warnings(self, print_warnings): | 565 | def set_print_warnings(self, print_warnings): |
561 | """Determines whether warnings related to this configuration (for | 566 | """Determines whether warnings related to this configuration (for |
562 | things like attempting to assign illegal values to symbols with | 567 | things like attempting to assign illegal values to symbols with |
563 | Symbol.set_user_value()) should be printed to stderr. | 568 | Symbol.set_user_value()) should be printed to stderr. |
564 | 569 | ||
565 | print_warnings: True if warnings should be printed.""" | 570 | print_warnings: True if warnings should be printed.""" |
566 | self.print_warnings = print_warnings | 571 | self.print_warnings = print_warnings |
567 | 572 | ||
568 | def set_print_undef_assign(self, print_undef_assign): | 573 | def set_print_undef_assign(self, print_undef_assign): |
569 | """Determines whether informational messages related to assignments to | 574 | """Determines whether informational messages related to assignments to |
570 | undefined symbols should be printed to stderr for this configuration. | 575 | undefined symbols should be printed to stderr for this configuration. |
571 | 576 | ||
572 | print_undef_assign: If True, such messages will be printed.""" | 577 | print_undef_assign: If True, such messages will be printed.""" |
573 | self.print_undef_assign = print_undef_assign | 578 | self.print_undef_assign = print_undef_assign |
574 | 579 | ||
575 | def __str__(self): | 580 | def __str__(self): |
576 | """Returns a string containing various information about the Config.""" | 581 | """Returns a string containing various information about the Config.""" |
577 | return _lines("Configuration", | 582 | return _lines("Configuration", |
578 | "File : " + | 583 | "File : " + |
579 | self.filename, | 584 | self.filename, |
580 | "Base directory : " + | 585 | "Base directory : " + |
581 | self.base_dir, | 586 | self.base_dir, |
582 | "Value of $ARCH at creation time : " + | 587 | "Value of $ARCH at creation time : " + |
583 | ("(not set)" if self.arch is None else self.arch), | 588 | ("(not set)" if self.arch is None else self.arch), |
584 | "Value of $SRCARCH at creation time : " + | 589 | "Value of $SRCARCH at creation time : " + |
585 | ("(not set)" if self.srcarch is None else | 590 | ("(not set)" if self.srcarch is None else |
586 | self.srcarch), | 591 | self.srcarch), |
587 | "Source tree (derived from $srctree;", | 592 | "Source tree (derived from $srctree;", |
588 | "defaults to '.' if $srctree isn't set) : " + | 593 | "defaults to '.' if $srctree isn't set) : " + |
589 | self.srctree, | 594 | self.srctree, |
590 | "Most recently loaded .config : " + | 595 | "Most recently loaded .config : " + |
591 | ("(no .config loaded)" | 596 | ("(no .config loaded)" |
592 | if self.config_filename is None else | 597 | if self.config_filename is None else |
593 | self.config_filename), | 598 | self.config_filename), |
594 | "Print warnings : " + | 599 | "Print warnings : " + |
595 | BOOL_STR[self.print_warnings], | 600 | BOOL_STR[self.print_warnings], |
596 | "Print assignments to undefined symbols : " + | 601 | "Print assignments to undefined symbols : " + |
597 | BOOL_STR[self.print_undef_assign]) | 602 | BOOL_STR[self.print_undef_assign]) |
598 | 603 | ||
599 | # | 604 | # |
600 | # Private methods | 605 | # Private methods |
601 | # | 606 | # |
602 | 607 | ||
603 | # | 608 | # |
604 | # Kconfig parsing | 609 | # Kconfig parsing |
605 | # | 610 | # |
606 | 611 | ||
607 | def _parse_file(self, filename, parent, deps, visible_if_deps, res=None): | 612 | def _parse_file(self, filename, parent, deps, visible_if_deps, res=None): |
608 | """Parses the Kconfig file 'filename'. Returns a list with the Items in | 613 | """Parses the Kconfig file 'filename'. Returns a list with the Items in |
609 | the file. See _parse_block() for the meaning of the parameters.""" | 614 | the file. See _parse_block() for the meaning of the parameters.""" |
610 | return self._parse_block(_FileFeed(filename), None, parent, deps, | 615 | return self._parse_block(_FileFeed(filename), None, parent, deps, |
611 | visible_if_deps, res) | 616 | visible_if_deps, res) |
612 | 617 | ||
613 | def _parse_block(self, line_feeder, end_marker, parent, deps, | 618 | def _parse_block(self, line_feeder, end_marker, parent, deps, |
614 | visible_if_deps, res=None): | 619 | visible_if_deps, res=None): |
615 | """Parses a block, which is the contents of either a file or an if, | 620 | """Parses a block, which is the contents of either a file or an if, |
616 | menu, or choice statement. Returns a list with the Items in the block. | 621 | menu, or choice statement. Returns a list with the Items in the block. |
617 | 622 | ||
618 | line_feeder: A _FileFeed instance feeding lines from a file. The | 623 | line_feeder: A _FileFeed instance feeding lines from a file. The |
619 | Kconfig language is line-based in practice. | 624 | Kconfig language is line-based in practice. |
620 | 625 | ||
621 | end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for | 626 | end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for |
622 | ifs. None for files. | 627 | ifs. None for files. |
623 | 628 | ||
624 | parent: The enclosing menu or choice, or None if we're at the top | 629 | parent: The enclosing menu or choice, or None if we're at the top |
625 | level. | 630 | level. |
626 | 631 | ||
627 | deps: Dependencies from enclosing menus, choices and ifs. | 632 | deps: Dependencies from enclosing menus, choices and ifs. |
628 | 633 | ||
629 | visible_if_deps (default: None): 'visible if' dependencies from | 634 | visible_if_deps (default: None): 'visible if' dependencies from |
630 | enclosing menus. | 635 | enclosing menus. |
631 | 636 | ||
632 | res (default: None): The list to add items to. If None, a new list is | 637 | res (default: None): The list to add items to. If None, a new list is |
633 | created to hold the items.""" | 638 | created to hold the items.""" |
634 | 639 | ||
635 | block = [] if res is None else res | 640 | block = [] if res is None else res |
636 | 641 | ||
637 | while 1: | 642 | while 1: |
638 | # Do we already have a tokenized line that we determined wasn't | 643 | # Do we already have a tokenized line that we determined wasn't |
639 | # part of whatever we were parsing earlier? See comment in | 644 | # part of whatever we were parsing earlier? See comment in |
640 | # Config.__init__(). | 645 | # Config.__init__(). |
641 | if self.end_line is not None: | 646 | if self.end_line is not None: |
642 | line = self.end_line | 647 | line = self.end_line |
643 | tokens = self.end_line_tokens | 648 | tokens = self.end_line_tokens |
644 | tokens.unget_all() | 649 | tokens.unget_all() |
645 | 650 | ||
646 | self.end_line = None | 651 | self.end_line = None |
647 | self.end_line_tokens = None | 652 | self.end_line_tokens = None |
648 | else: | 653 | else: |
649 | line = line_feeder.get_next() | 654 | line = line_feeder.get_next() |
650 | if line is None: | 655 | if line is None: |
651 | if end_marker is not None: | 656 | if end_marker is not None: |
652 | raise Kconfig_Syntax_Error("Unexpected end of file {0}" | 657 | raise Kconfig_Syntax_Error("Unexpected end of file {0}" |
653 | .format(line_feeder.filename)) | 658 | .format(line_feeder.filename)) |
654 | return block | 659 | return block |
655 | 660 | ||
656 | tokens = self._tokenize(line, False, line_feeder.filename, | 661 | tokens = self._tokenize(line, False, line_feeder.filename, |
657 | line_feeder.linenr) | 662 | line_feeder.linenr) |
658 | 663 | ||
659 | t0 = tokens.get_next() | 664 | t0 = tokens.get_next() |
660 | if t0 is None: | 665 | if t0 is None: |
661 | continue | 666 | continue |
662 | 667 | ||
663 | # Cases are ordered roughly by frequency, which speeds things up a | 668 | # Cases are ordered roughly by frequency, which speeds things up a |
664 | # bit | 669 | # bit |
665 | 670 | ||
666 | if t0 == T_CONFIG or t0 == T_MENUCONFIG: | 671 | if t0 == T_CONFIG or t0 == T_MENUCONFIG: |
667 | # The tokenizer will automatically allocate a new Symbol object | 672 | # The tokenizer will automatically allocate a new Symbol object |
668 | # for any new names it encounters, so we don't need to worry | 673 | # for any new names it encounters, so we don't need to worry |
669 | # about that here. | 674 | # about that here. |
670 | sym = tokens.get_next() | 675 | sym = tokens.get_next() |
671 | 676 | ||
672 | # Symbols defined in multiple places get the parent of their | 677 | # Symbols defined in multiple places get the parent of their |
673 | # first definition. However, for symbols whose parents are | 678 | # first definition. However, for symbols whose parents are |
674 | # choice statements, the choice statement takes precedence. | 679 | # choice statements, the choice statement takes precedence. |
675 | if not sym.is_defined_ or isinstance(parent, Choice): | 680 | if not sym.is_defined_ or isinstance(parent, Choice): |
676 | sym.parent = parent | 681 | sym.parent = parent |
677 | 682 | ||
678 | sym.is_defined_ = True | 683 | sym.is_defined_ = True |
679 | 684 | ||
680 | self.kconfig_syms.append(sym) | 685 | self.kconfig_syms.append(sym) |
681 | block.append(sym) | 686 | block.append(sym) |
682 | 687 | ||
683 | self._parse_properties(line_feeder, sym, deps, visible_if_deps) | 688 | self._parse_properties(line_feeder, sym, deps, visible_if_deps) |
684 | 689 | ||
685 | elif t0 == T_SOURCE: | 690 | elif t0 == T_SOURCE: |
686 | kconfig_file = tokens.get_next() | 691 | kconfig_file = tokens.get_next() |
687 | exp_kconfig_file = self._expand_sym_refs(kconfig_file) | 692 | exp_kconfig_file = self._expand_sym_refs(kconfig_file) |
688 | f = os.path.join(self.base_dir, exp_kconfig_file) | 693 | f = os.path.join(self.base_dir, exp_kconfig_file) |
689 | if not os.path.exists(f): | 694 | if not os.path.exists(f): |
690 | raise IOError('{0}:{1}: sourced file "{2}" (expands to ' | 695 | raise IOError('{0}:{1}: sourced file "{2}" (expands to ' |
691 | '"{3}") not found. Perhaps base_dir ' | 696 | '"{3}") not found. Perhaps base_dir ' |
692 | '(argument to Config.__init__(), currently ' | 697 | '(argument to Config.__init__(), currently ' |
693 | '"{4}") is set to the wrong value.' | 698 | '"{4}") is set to the wrong value.' |
694 | .format(line_feeder.filename, | 699 | .format(line_feeder.filename, |
695 | line_feeder.linenr, | 700 | line_feeder.linenr, |
696 | kconfig_file, exp_kconfig_file, | 701 | kconfig_file, exp_kconfig_file, |
697 | self.base_dir)) | 702 | self.base_dir)) |
698 | # Add items to the same block | 703 | # Add items to the same block |
699 | self._parse_file(f, parent, deps, visible_if_deps, block) | 704 | self._parse_file(f, parent, deps, visible_if_deps, block) |
700 | 705 | ||
701 | elif t0 == end_marker: | 706 | elif t0 == end_marker: |
702 | # We have reached the end of the block | 707 | # We have reached the end of the block |
703 | return block | 708 | return block |
704 | 709 | ||
705 | elif t0 == T_IF: | 710 | elif t0 == T_IF: |
706 | # If statements are treated as syntactic sugar for adding | 711 | # If statements are treated as syntactic sugar for adding |
707 | # dependencies to enclosed items and do not have an explicit | 712 | # dependencies to enclosed items and do not have an explicit |
708 | # object representation. | 713 | # object representation. |
709 | 714 | ||
710 | dep_expr = self._parse_expr(tokens, None, line, | 715 | dep_expr = self._parse_expr(tokens, None, line, |
711 | line_feeder.filename, | 716 | line_feeder.filename, |
712 | line_feeder.linenr) | 717 | line_feeder.linenr) |
713 | # Add items to the same block | 718 | # Add items to the same block |
714 | self._parse_block(line_feeder, T_ENDIF, parent, | 719 | self._parse_block(line_feeder, T_ENDIF, parent, |
715 | _make_and(dep_expr, deps), | 720 | _make_and(dep_expr, deps), |
716 | visible_if_deps, block) | 721 | visible_if_deps, block) |
717 | 722 | ||
718 | elif t0 == T_COMMENT: | 723 | elif t0 == T_COMMENT: |
719 | comment = Comment() | 724 | comment = Comment() |
720 | 725 | ||
721 | comment.config = self | 726 | comment.config = self |
722 | comment.parent = parent | 727 | comment.parent = parent |
723 | comment.filename = line_feeder.filename | 728 | comment.filename = line_feeder.filename |
724 | comment.linenr = line_feeder.linenr | 729 | comment.linenr = line_feeder.linenr |
725 | comment.text = tokens.get_next() | 730 | comment.text = tokens.get_next() |
726 | 731 | ||
727 | self.comments.append(comment) | 732 | self.comments.append(comment) |
728 | block.append(comment) | 733 | block.append(comment) |
729 | 734 | ||
730 | self._parse_properties(line_feeder, comment, deps, | 735 | self._parse_properties(line_feeder, comment, deps, |
731 | visible_if_deps) | 736 | visible_if_deps) |
732 | 737 | ||
733 | elif t0 == T_MENU: | 738 | elif t0 == T_MENU: |
734 | menu = Menu() | 739 | menu = Menu() |
735 | 740 | ||
736 | menu.config = self | 741 | menu.config = self |
737 | menu.parent = parent | 742 | menu.parent = parent |
738 | menu.filename = line_feeder.filename | 743 | menu.filename = line_feeder.filename |
739 | menu.linenr = line_feeder.linenr | 744 | menu.linenr = line_feeder.linenr |
740 | menu.title = tokens.get_next() | 745 | menu.title = tokens.get_next() |
741 | 746 | ||
742 | self.menus.append(menu) | 747 | self.menus.append(menu) |
743 | block.append(menu) | 748 | block.append(menu) |
744 | 749 | ||
745 | # Parse properties and contents | 750 | # Parse properties and contents |
746 | self._parse_properties(line_feeder, menu, deps, | 751 | self._parse_properties(line_feeder, menu, deps, |
747 | visible_if_deps) | 752 | visible_if_deps) |
748 | menu.block = self._parse_block(line_feeder, T_ENDMENU, menu, | 753 | menu.block = self._parse_block(line_feeder, T_ENDMENU, menu, |
749 | menu.dep_expr, | 754 | menu.dep_expr, |
750 | _make_and(visible_if_deps, | 755 | _make_and(visible_if_deps, |
751 | menu.visible_if_expr)) | 756 | menu.visible_if_expr)) |
752 | 757 | ||
753 | elif t0 == T_CHOICE: | 758 | elif t0 == T_CHOICE: |
754 | name = tokens.get_next() | 759 | name = tokens.get_next() |
755 | if name is None: | 760 | if name is None: |
756 | choice = Choice() | 761 | choice = Choice() |
757 | self.choices.append(choice) | 762 | self.choices.append(choice) |
758 | else: | 763 | else: |
759 | # Named choice | 764 | # Named choice |
760 | choice = self.named_choices.get(name) | 765 | choice = self.named_choices.get(name) |
761 | if choice is None: | 766 | if choice is None: |
762 | choice = Choice() | 767 | choice = Choice() |
763 | choice.name = name | 768 | choice.name = name |
764 | self.named_choices[name] = choice | 769 | self.named_choices[name] = choice |
765 | self.choices.append(choice) | 770 | self.choices.append(choice) |
766 | 771 | ||
767 | choice.config = self | 772 | choice.config = self |
768 | choice.parent = parent | 773 | choice.parent = parent |
769 | 774 | ||
770 | choice.def_locations.append((line_feeder.filename, | 775 | choice.def_locations.append((line_feeder.filename, |
771 | line_feeder.linenr)) | 776 | line_feeder.linenr)) |
772 | 777 | ||
773 | # Parse properties and contents | 778 | # Parse properties and contents |
774 | self._parse_properties(line_feeder, choice, deps, | 779 | self._parse_properties(line_feeder, choice, deps, |
775 | visible_if_deps) | 780 | visible_if_deps) |
776 | choice.block = self._parse_block(line_feeder, T_ENDCHOICE, | 781 | choice.block = self._parse_block(line_feeder, T_ENDCHOICE, |
777 | choice, deps, visible_if_deps) | 782 | choice, deps, visible_if_deps) |
778 | 783 | ||
779 | choice._determine_actual_symbols() | 784 | choice._determine_actual_symbols() |
780 | 785 | ||
781 | # If no type is specified for the choice, its type is that of | 786 | # If no type is specified for the choice, its type is that of |
782 | # the first choice item with a specified type | 787 | # the first choice item with a specified type |
783 | if choice.type == UNKNOWN: | 788 | if choice.type == UNKNOWN: |
784 | for item in choice.actual_symbols: | 789 | for item in choice.actual_symbols: |
785 | if item.type != UNKNOWN: | 790 | if item.type != UNKNOWN: |
786 | choice.type = item.type | 791 | choice.type = item.type |
787 | break | 792 | break |
788 | 793 | ||
789 | # Each choice item of UNKNOWN type gets the type of the choice | 794 | # Each choice item of UNKNOWN type gets the type of the choice |
790 | for item in choice.actual_symbols: | 795 | for item in choice.actual_symbols: |
791 | if item.type == UNKNOWN: | 796 | if item.type == UNKNOWN: |
792 | item.type = choice.type | 797 | item.type = choice.type |
793 | 798 | ||
794 | block.append(choice) | 799 | block.append(choice) |
795 | 800 | ||
796 | elif t0 == T_MAINMENU: | 801 | elif t0 == T_MAINMENU: |
797 | text = tokens.get_next() | 802 | text = tokens.get_next() |
798 | if self.mainmenu_text is not None: | 803 | if self.mainmenu_text is not None: |
799 | self._warn("overriding 'mainmenu' text. " | 804 | self._warn("overriding 'mainmenu' text. " |
800 | 'Old value: "{0}", new value: "{1}".' | 805 | 'Old value: "{0}", new value: "{1}".' |
801 | .format(self.mainmenu_text, text), | 806 | .format(self.mainmenu_text, text), |
802 | line_feeder.filename, line_feeder.linenr) | 807 | line_feeder.filename, line_feeder.linenr) |
803 | self.mainmenu_text = text | 808 | self.mainmenu_text = text |
804 | 809 | ||
805 | else: | 810 | else: |
806 | _parse_error(line, "unrecognized construct", | 811 | _parse_error(line, "unrecognized construct", |
807 | line_feeder.filename, line_feeder.linenr) | 812 | line_feeder.filename, line_feeder.linenr) |
808 | 813 | ||
809 | def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps): | 814 | def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps): |
810 | """Parsing of properties for symbols, menus, choices, and comments. | 815 | """Parsing of properties for symbols, menus, choices, and comments. |
811 | Takes care of propagating dependencies from enclosing menus and ifs.""" | 816 | Takes care of propagating dependencies from enclosing menus and ifs.""" |
812 | 817 | ||
813 | def parse_val_and_cond(tokens, line, filename, linenr): | 818 | def parse_val_and_cond(tokens, line, filename, linenr): |
814 | """Parses '<expr1> if <expr2>' constructs, where the 'if' part is | 819 | """Parses '<expr1> if <expr2>' constructs, where the 'if' part is |
815 | optional. Returns a tuple containing the parsed expressions, with | 820 | optional. Returns a tuple containing the parsed expressions, with |
816 | None as the second element if the 'if' part is missing.""" | 821 | None as the second element if the 'if' part is missing.""" |
817 | val = self._parse_expr(tokens, stmt, line, filename, linenr, False) | 822 | val = self._parse_expr(tokens, stmt, line, filename, linenr, False) |
818 | if tokens.check(T_IF): | 823 | if tokens.check(T_IF): |
819 | return (val, self._parse_expr(tokens, stmt, line, filename, | 824 | return (val, self._parse_expr(tokens, stmt, line, filename, |
820 | linenr)) | 825 | linenr)) |
821 | return (val, None) | 826 | return (val, None) |
822 | 827 | ||
823 | # In case the symbol is defined in multiple locations, we need to | 828 | # In case the symbol is defined in multiple locations, we need to |
824 | # remember what prompts, defaults, and selects are new for this | 829 | # remember what prompts, defaults, and selects are new for this |
825 | # definition, as "depends on" should only apply to the local | 830 | # definition, as "depends on" should only apply to the local |
826 | # definition. | 831 | # definition. |
827 | new_prompt = None | 832 | new_prompt = None |
828 | new_def_exprs = [] | 833 | new_def_exprs = [] |
829 | new_selects = [] | 834 | new_selects = [] |
830 | 835 | ||
831 | # Dependencies from 'depends on' statements | 836 | # Dependencies from 'depends on' statements |
832 | depends_on_expr = None | 837 | depends_on_expr = None |
833 | 838 | ||
834 | while 1: | 839 | while 1: |
835 | line = line_feeder.get_next() | 840 | line = line_feeder.get_next() |
836 | if line is None: | 841 | if line is None: |
837 | break | 842 | break |
838 | 843 | ||
839 | filename = line_feeder.filename | 844 | filename = line_feeder.filename |
840 | linenr = line_feeder.linenr | 845 | linenr = line_feeder.linenr |
841 | 846 | ||
842 | tokens = self._tokenize(line, False, filename, linenr) | 847 | tokens = self._tokenize(line, False, filename, linenr) |
843 | 848 | ||
844 | t0 = tokens.get_next() | 849 | t0 = tokens.get_next() |
845 | if t0 is None: | 850 | if t0 is None: |
846 | continue | 851 | continue |
847 | 852 | ||
848 | # Cases are ordered roughly by frequency, which speeds things up a | 853 | # Cases are ordered roughly by frequency, which speeds things up a |
849 | # bit | 854 | # bit |
850 | 855 | ||
851 | if t0 == T_DEPENDS: | 856 | if t0 == T_DEPENDS: |
852 | if not tokens.check(T_ON): | 857 | if not tokens.check(T_ON): |
853 | _parse_error(line, 'expected "on" after "depends"', | 858 | _parse_error(line, 'expected "on" after "depends"', |
854 | filename, linenr) | 859 | filename, linenr) |
855 | 860 | ||
856 | parsed_deps = self._parse_expr(tokens, stmt, line, filename, | 861 | parsed_deps = self._parse_expr(tokens, stmt, line, filename, |
857 | linenr) | 862 | linenr) |
858 | 863 | ||
859 | if isinstance(stmt, (Menu, Comment)): | 864 | if isinstance(stmt, (Menu, Comment)): |
860 | stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps) | 865 | stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps) |
861 | else: | 866 | else: |
862 | depends_on_expr = _make_and(depends_on_expr, parsed_deps) | 867 | depends_on_expr = _make_and(depends_on_expr, parsed_deps) |
863 | 868 | ||
864 | elif t0 == T_HELP: | 869 | elif t0 == T_HELP: |
865 | # Find first non-blank (not all-space) line and get its | 870 | # Find first non-blank (not all-space) line and get its |
866 | # indentation | 871 | # indentation |
867 | line = line_feeder.next_nonblank() | 872 | line = line_feeder.next_nonblank() |
868 | if line is None: | 873 | if line is None: |
869 | stmt.help = "" | 874 | stmt.help = "" |
870 | break | 875 | break |
871 | indent = _indentation(line) | 876 | indent = _indentation(line) |
872 | if indent == 0: | 877 | if indent == 0: |
873 | # If the first non-empty lines has zero indent, there is no | 878 | # If the first non-empty lines has zero indent, there is no |
874 | # help text | 879 | # help text |
875 | stmt.help = "" | 880 | stmt.help = "" |
876 | line_feeder.unget() | 881 | line_feeder.unget() |
877 | break | 882 | break |
878 | 883 | ||
879 | # The help text goes on till the first non-empty line with less | 884 | # The help text goes on till the first non-empty line with less |
880 | # indent | 885 | # indent |
881 | help_lines = [_deindent(line, indent)] | 886 | help_lines = [_deindent(line, indent)] |
882 | while 1: | 887 | while 1: |
883 | line = line_feeder.get_next() | 888 | line = line_feeder.get_next() |
884 | if line is None or \ | 889 | if line is None or \ |
885 | (not line.isspace() and _indentation(line) < indent): | 890 | (not line.isspace() and _indentation(line) < indent): |
886 | stmt.help = "".join(help_lines) | 891 | stmt.help = "".join(help_lines) |
887 | break | 892 | break |
888 | help_lines.append(_deindent(line, indent)) | 893 | help_lines.append(_deindent(line, indent)) |
889 | 894 | ||
890 | if line is None: | 895 | if line is None: |
891 | break | 896 | break |
892 | 897 | ||
893 | line_feeder.unget() | 898 | line_feeder.unget() |
894 | 899 | ||
895 | elif t0 == T_SELECT or t0 == T_IMPLY: | 900 | elif t0 == T_SELECT or t0 == T_IMPLY: |
896 | target = tokens.get_next() | 901 | target = tokens.get_next() |
897 | 902 | ||
898 | stmt.referenced_syms.add(target) | 903 | stmt.referenced_syms.add(target) |
899 | stmt.selected_syms.add(target) | 904 | stmt.selected_syms.add(target) |
900 | 905 | ||
901 | if tokens.check(T_IF): | 906 | if tokens.check(T_IF): |
902 | new_selects.append((target, | 907 | new_selects.append((target, |
903 | self._parse_expr(tokens, stmt, line, | 908 | self._parse_expr(tokens, stmt, line, |
904 | filename, linenr))) | 909 | filename, linenr))) |
905 | else: | 910 | else: |
906 | new_selects.append((target, None)) | 911 | new_selects.append((target, None)) |
907 | 912 | ||
908 | elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING): | 913 | elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING): |
909 | stmt.type = TOKEN_TO_TYPE[t0] | 914 | stmt.type = TOKEN_TO_TYPE[t0] |
910 | if tokens.peek_next() is not None: | 915 | if tokens.peek_next() is not None: |
911 | new_prompt = parse_val_and_cond(tokens, line, filename, | 916 | new_prompt = parse_val_and_cond(tokens, line, filename, |
912 | linenr) | 917 | linenr) |
913 | 918 | ||
914 | elif t0 == T_DEFAULT: | 919 | elif t0 == T_DEFAULT: |
915 | new_def_exprs.append(parse_val_and_cond(tokens, line, filename, | 920 | new_def_exprs.append(parse_val_and_cond(tokens, line, filename, |
916 | linenr)) | 921 | linenr)) |
917 | 922 | ||
918 | elif t0 == T_DEF_BOOL: | 923 | elif t0 == T_DEF_BOOL: |
919 | stmt.type = BOOL | 924 | stmt.type = BOOL |
920 | if tokens.peek_next() is not None: | 925 | if tokens.peek_next() is not None: |
921 | new_def_exprs.append(parse_val_and_cond(tokens, line, | 926 | new_def_exprs.append(parse_val_and_cond(tokens, line, |
922 | filename, linenr)) | 927 | filename, linenr)) |
923 | 928 | ||
924 | elif t0 == T_PROMPT: | 929 | elif t0 == T_PROMPT: |
925 | # 'prompt' properties override each other within a single | 930 | # 'prompt' properties override each other within a single |
926 | # definition of a symbol, but additional prompts can be added | 931 | # definition of a symbol, but additional prompts can be added |
927 | # by defining the symbol multiple times; hence 'new_prompt' | 932 | # by defining the symbol multiple times; hence 'new_prompt' |
928 | # instead of 'prompt'. | 933 | # instead of 'prompt'. |
929 | new_prompt = parse_val_and_cond(tokens, line, filename, linenr) | 934 | new_prompt = parse_val_and_cond(tokens, line, filename, linenr) |
930 | 935 | ||
931 | elif t0 == T_RANGE: | 936 | elif t0 == T_RANGE: |
932 | low = tokens.get_next() | 937 | low = tokens.get_next() |
933 | high = tokens.get_next() | 938 | high = tokens.get_next() |
934 | stmt.referenced_syms.add(low) | 939 | stmt.referenced_syms.add(low) |
935 | stmt.referenced_syms.add(high) | 940 | stmt.referenced_syms.add(high) |
936 | 941 | ||
937 | if tokens.check(T_IF): | 942 | if tokens.check(T_IF): |
938 | stmt.ranges.append((low, high, | 943 | stmt.ranges.append((low, high, |
939 | self._parse_expr(tokens, stmt, line, | 944 | self._parse_expr(tokens, stmt, line, |
940 | filename, linenr))) | 945 | filename, linenr))) |
941 | else: | 946 | else: |
942 | stmt.ranges.append((low, high, None)) | 947 | stmt.ranges.append((low, high, None)) |
943 | 948 | ||
944 | elif t0 == T_DEF_TRISTATE: | 949 | elif t0 == T_DEF_TRISTATE: |
945 | stmt.type = TRISTATE | 950 | stmt.type = TRISTATE |
946 | if tokens.peek_next() is not None: | 951 | if tokens.peek_next() is not None: |
947 | new_def_exprs.append(parse_val_and_cond(tokens, line, | 952 | new_def_exprs.append(parse_val_and_cond(tokens, line, |
948 | filename, linenr)) | 953 | filename, linenr)) |
949 | 954 | ||
950 | elif t0 == T_OPTION: | 955 | elif t0 == T_OPTION: |
951 | if tokens.check(T_ENV) and tokens.check(T_EQUAL): | 956 | if tokens.check(T_ENV) and tokens.check(T_EQUAL): |
952 | env_var = tokens.get_next() | 957 | env_var = tokens.get_next() |
953 | 958 | ||
954 | stmt.is_special_ = True | 959 | stmt.is_special_ = True |
955 | stmt.is_from_env = True | 960 | stmt.is_from_env = True |
956 | 961 | ||
957 | if env_var not in os.environ: | 962 | if env_var not in os.environ: |
958 | self._warn("The symbol {0} references the " | 963 | self._warn("The symbol {0} references the " |
959 | "non-existent environment variable {1} and " | 964 | "non-existent environment variable {1} and " |
960 | "will get the empty string as its value. " | 965 | "will get the empty string as its value. " |
961 | "If you're using Kconfiglib via " | 966 | "If you're using Kconfiglib via " |
962 | "'make (i)scriptconfig', it should have " | 967 | "'make (i)scriptconfig', it should have " |
963 | "set up the environment correctly for you. " | 968 | "set up the environment correctly for you. " |
964 | "If you still got this message, that " | 969 | "If you still got this message, that " |
965 | "might be an error, and you should email " | 970 | "might be an error, and you should email " |
966 | "ulfalizer a.t Google's email service.""" | 971 | "ulfalizer a.t Google's email service.""" |
967 | .format(stmt.name, env_var), | 972 | .format(stmt.name, env_var), |
968 | filename, linenr) | 973 | filename, linenr) |
969 | 974 | ||
970 | stmt.cached_val = "" | 975 | stmt.cached_val = "" |
971 | else: | 976 | else: |
972 | stmt.cached_val = os.environ[env_var] | 977 | stmt.cached_val = os.environ[env_var] |
973 | 978 | ||
974 | elif tokens.check(T_DEFCONFIG_LIST): | 979 | elif tokens.check(T_DEFCONFIG_LIST): |
975 | self.defconfig_sym = stmt | 980 | self.defconfig_sym = stmt |
976 | 981 | ||
977 | elif tokens.check(T_MODULES): | 982 | elif tokens.check(T_MODULES): |
978 | # To reduce warning spam, only warn if 'option modules' is | 983 | # To reduce warning spam, only warn if 'option modules' is |
979 | # set on some symbol that isn't MODULES, which should be | 984 | # set on some symbol that isn't MODULES, which should be |
980 | # safe. I haven't run into any projects that make use | 985 | # safe. I haven't run into any projects that make use |
981 | # modules besides the kernel yet, and there it's likely to | 986 | # modules besides the kernel yet, and there it's likely to |
982 | # keep being called "MODULES". | 987 | # keep being called "MODULES". |
983 | if stmt.name != "MODULES": | 988 | if stmt.name != "MODULES": |
984 | self._warn("the 'modules' option is not supported. " | 989 | self._warn("the 'modules' option is not supported. " |
985 | "Let me know if this is a problem for you; " | 990 | "Let me know if this is a problem for you; " |
986 | "it shouldn't be that hard to implement. " | 991 | "it shouldn't be that hard to implement. " |
987 | "(Note that modules are still supported -- " | 992 | "(Note that modules are still supported -- " |
988 | "Kconfiglib just assumes the symbol name " | 993 | "Kconfiglib just assumes the symbol name " |
989 | "MODULES, like older versions of the C " | 994 | "MODULES, like older versions of the C " |
990 | "implementation did when 'option modules' " | 995 | "implementation did when 'option modules' " |
991 | "wasn't used.)", | 996 | "wasn't used.)", |
992 | filename, linenr) | 997 | filename, linenr) |
993 | 998 | ||
994 | elif tokens.check(T_ALLNOCONFIG_Y): | 999 | elif tokens.check(T_ALLNOCONFIG_Y): |
995 | if not isinstance(stmt, Symbol): | 1000 | if not isinstance(stmt, Symbol): |
996 | _parse_error(line, | 1001 | _parse_error(line, |
997 | "the 'allnoconfig_y' option is only " | 1002 | "the 'allnoconfig_y' option is only " |
998 | "valid for symbols", | 1003 | "valid for symbols", |
999 | filename, linenr) | 1004 | filename, linenr) |
1000 | stmt.allnoconfig_y = True | 1005 | stmt.allnoconfig_y = True |
1001 | 1006 | ||
1002 | else: | 1007 | else: |
1003 | _parse_error(line, "unrecognized option", filename, linenr) | 1008 | _parse_error(line, "unrecognized option", filename, linenr) |
1004 | 1009 | ||
1005 | elif t0 == T_VISIBLE: | 1010 | elif t0 == T_VISIBLE: |
1006 | if not tokens.check(T_IF): | 1011 | if not tokens.check(T_IF): |
1007 | _parse_error(line, 'expected "if" after "visible"', | 1012 | _parse_error(line, 'expected "if" after "visible"', |
1008 | filename, linenr) | 1013 | filename, linenr) |
1009 | if not isinstance(stmt, Menu): | 1014 | if not isinstance(stmt, Menu): |
1010 | _parse_error(line, | 1015 | _parse_error(line, |
1011 | "'visible if' is only valid for menus", | 1016 | "'visible if' is only valid for menus", |
1012 | filename, linenr) | 1017 | filename, linenr) |
1013 | 1018 | ||
1014 | parsed_deps = self._parse_expr(tokens, stmt, line, filename, | 1019 | parsed_deps = self._parse_expr(tokens, stmt, line, filename, |
1015 | linenr) | 1020 | linenr) |
1016 | stmt.visible_if_expr = _make_and(stmt.visible_if_expr, | 1021 | stmt.visible_if_expr = _make_and(stmt.visible_if_expr, |
1017 | parsed_deps) | 1022 | parsed_deps) |
1018 | 1023 | ||
1019 | elif t0 == T_OPTIONAL: | 1024 | elif t0 == T_OPTIONAL: |
1020 | if not isinstance(stmt, Choice): | 1025 | if not isinstance(stmt, Choice): |
1021 | _parse_error(line, | 1026 | _parse_error(line, |
1022 | '"optional" is only valid for choices', | 1027 | '"optional" is only valid for choices', |
1023 | filename, | 1028 | filename, |
1024 | linenr) | 1029 | linenr) |
1025 | stmt.optional = True | 1030 | stmt.optional = True |
1026 | 1031 | ||
1027 | else: | 1032 | else: |
1028 | # See comment in Config.__init__() | 1033 | # See comment in Config.__init__() |
1029 | self.end_line = line | 1034 | self.end_line = line |
1030 | self.end_line_tokens = tokens | 1035 | self.end_line_tokens = tokens |
1031 | break | 1036 | break |
1032 | 1037 | ||
1033 | # Done parsing properties. Now propagate 'depends on' and enclosing | 1038 | # Done parsing properties. Now propagate 'depends on' and enclosing |
1034 | # menu/if dependencies to expressions. | 1039 | # menu/if dependencies to expressions. |
1035 | 1040 | ||
1036 | # The set of symbols referenced directly by the statement plus all | 1041 | # The set of symbols referenced directly by the statement plus all |
1037 | # symbols referenced by enclosing menus and ifs | 1042 | # symbols referenced by enclosing menus and ifs |
1038 | stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps) | 1043 | stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps) |
1039 | 1044 | ||
1040 | # Save original dependencies from enclosing menus and ifs | 1045 | # Save original dependencies from enclosing menus and ifs |
1041 | stmt.deps_from_containing = deps | 1046 | stmt.deps_from_containing = deps |
1042 | 1047 | ||
1043 | if isinstance(stmt, (Menu, Comment)): | 1048 | if isinstance(stmt, (Menu, Comment)): |
1044 | stmt.dep_expr = _make_and(stmt.orig_deps, deps) | 1049 | stmt.dep_expr = _make_and(stmt.orig_deps, deps) |
1045 | else: | 1050 | else: |
1046 | # Symbol or Choice | 1051 | # Symbol or Choice |
1047 | 1052 | ||
1048 | # See comment for 'menu_dep' | 1053 | # See comment for 'menu_dep' |
1049 | stmt.menu_dep = depends_on_expr | 1054 | stmt.menu_dep = depends_on_expr |
1050 | 1055 | ||
1051 | # Propagate dependencies to prompts | 1056 | # Propagate dependencies to prompts |
1052 | 1057 | ||
1053 | if new_prompt is not None: | 1058 | if new_prompt is not None: |
1054 | # Propagate 'visible if' dependencies from enclosing menus | 1059 | # Propagate 'visible if' dependencies from enclosing menus |
1055 | prompt, cond_expr = new_prompt | 1060 | prompt, cond_expr = new_prompt |
1056 | cond_expr = _make_and(cond_expr, visible_if_deps) | 1061 | cond_expr = _make_and(cond_expr, visible_if_deps) |
1057 | # Propagate 'depends on' dependencies | 1062 | # Propagate 'depends on' dependencies |
1058 | new_prompt = (prompt, _make_and(cond_expr, depends_on_expr)) | 1063 | new_prompt = (prompt, _make_and(cond_expr, depends_on_expr)) |
1059 | # Save original | 1064 | # Save original |
1060 | stmt.orig_prompts.append(new_prompt) | 1065 | stmt.orig_prompts.append(new_prompt) |
1061 | # Finalize with dependencies from enclosing menus and ifs | 1066 | # Finalize with dependencies from enclosing menus and ifs |
1062 | stmt.prompts.append((new_prompt[0], | 1067 | stmt.prompts.append((new_prompt[0], |
1063 | _make_and(new_prompt[1], deps))) | 1068 | _make_and(new_prompt[1], deps))) |
1064 | 1069 | ||
1065 | # Propagate dependencies to defaults | 1070 | # Propagate dependencies to defaults |
1066 | 1071 | ||
1067 | # Propagate 'depends on' dependencies | 1072 | # Propagate 'depends on' dependencies |
1068 | new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr)) | 1073 | new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr)) |
1069 | for val_expr, cond_expr in new_def_exprs] | 1074 | for val_expr, cond_expr in new_def_exprs] |
1070 | # Save original | 1075 | # Save original |
1071 | stmt.orig_def_exprs.extend(new_def_exprs) | 1076 | stmt.orig_def_exprs.extend(new_def_exprs) |
1072 | # Finalize with dependencies from enclosing menus and ifs | 1077 | # Finalize with dependencies from enclosing menus and ifs |
1073 | stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps)) | 1078 | stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps)) |
1074 | for val_expr, cond_expr in new_def_exprs]) | 1079 | for val_expr, cond_expr in new_def_exprs]) |
1075 | 1080 | ||
1076 | # Propagate dependencies to selects | 1081 | # Propagate dependencies to selects |
1077 | 1082 | ||
1078 | # Only symbols can select | 1083 | # Only symbols can select |
1079 | if isinstance(stmt, Symbol): | 1084 | if isinstance(stmt, Symbol): |
1080 | # Propagate 'depends on' dependencies | 1085 | # Propagate 'depends on' dependencies |
1081 | new_selects = [(target, _make_and(cond_expr, depends_on_expr)) | 1086 | new_selects = [(target, _make_and(cond_expr, depends_on_expr)) |
1082 | for target, cond_expr in new_selects] | 1087 | for target, cond_expr in new_selects] |
1083 | # Save original | 1088 | # Save original |
1084 | stmt.orig_selects.extend(new_selects) | 1089 | stmt.orig_selects.extend(new_selects) |
1085 | # Finalize with dependencies from enclosing menus and ifs | 1090 | # Finalize with dependencies from enclosing menus and ifs |
1086 | for target, cond in new_selects: | 1091 | for target, cond in new_selects: |
1087 | target.rev_dep = _make_or(target.rev_dep, | 1092 | target.rev_dep = _make_or(target.rev_dep, |
1088 | _make_and(stmt, | 1093 | _make_and(stmt, |
1089 | _make_and(cond, deps))) | 1094 | _make_and(cond, deps))) |
1090 | 1095 | ||
1091 | def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None, | 1096 | def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None, |
1092 | transform_m=True): | 1097 | transform_m=True): |
1093 | """Parses an expression from the tokens in 'feed' using a simple | 1098 | """Parses an expression from the tokens in 'feed' using a simple |
1094 | top-down approach. The result has the form | 1099 | top-down approach. The result has the form |
1095 | '(<operator>, [<parsed operands>])', where <operator> is e.g. | 1100 | '(<operator>, [<parsed operands>])', where <operator> is e.g. |
1096 | kconfiglib.AND. If there is only one operand (i.e., no && or ||), then | 1101 | kconfiglib.AND. If there is only one operand (i.e., no && or ||), then |
1097 | the operand is returned directly. This also goes for subexpressions. | 1102 | the operand is returned directly. This also goes for subexpressions. |
1098 | 1103 | ||
1099 | feed: _Feed instance containing the tokens for the expression. | 1104 | feed: _Feed instance containing the tokens for the expression. |
1100 | 1105 | ||
1101 | cur_item: The item (Symbol, Choice, Menu, or Comment) currently being | 1106 | cur_item: The item (Symbol, Choice, Menu, or Comment) currently being |
1102 | parsed, or None if we're not parsing an item. Used for recording | 1107 | parsed, or None if we're not parsing an item. Used for recording |
1103 | references to symbols. | 1108 | references to symbols. |
1104 | 1109 | ||
1105 | line: The line containing the expression being parsed. | 1110 | line: The line containing the expression being parsed. |
1106 | 1111 | ||
1107 | filename (default: None): The file containing the expression. | 1112 | filename (default: None): The file containing the expression. |
1108 | 1113 | ||
1109 | linenr (default: None): The line number containing the expression. | 1114 | linenr (default: None): The line number containing the expression. |
1110 | 1115 | ||
1111 | transform_m (default: False): Determines if 'm' should be rewritten to | 1116 | transform_m (default: False): Determines if 'm' should be rewritten to |
1112 | 'm && MODULES' -- see parse_val_and_cond(). | 1117 | 'm && MODULES' -- see parse_val_and_cond(). |
1113 | 1118 | ||
1114 | Expression grammar, in decreasing order of precedence: | 1119 | Expression grammar, in decreasing order of precedence: |
1115 | 1120 | ||
1116 | <expr> -> <symbol> | 1121 | <expr> -> <symbol> |
1117 | <symbol> '=' <symbol> | 1122 | <symbol> '=' <symbol> |
1118 | <symbol> '!=' <symbol> | 1123 | <symbol> '!=' <symbol> |
1119 | '(' <expr> ')' | 1124 | '(' <expr> ')' |
1120 | '!' <expr> | 1125 | '!' <expr> |
1121 | <expr> '&&' <expr> | 1126 | <expr> '&&' <expr> |
1122 | <expr> '||' <expr>""" | 1127 | <expr> '||' <expr>""" |
1123 | 1128 | ||
1124 | # Use instance variables to avoid having to pass these as arguments | 1129 | # Use instance variables to avoid having to pass these as arguments |
1125 | # through the top-down parser in _parse_expr_rec(), which is tedious | 1130 | # through the top-down parser in _parse_expr_rec(), which is tedious |
1126 | # and obfuscates the code. A profiler run shows no noticeable | 1131 | # and obfuscates the code. A profiler run shows no noticeable |
1127 | # performance difference. | 1132 | # performance difference. |
1128 | self._cur_item = cur_item | 1133 | self._cur_item = cur_item |
1129 | self._transform_m = transform_m | 1134 | self._transform_m = transform_m |
1130 | self._line = line | 1135 | self._line = line |
1131 | self._filename = filename | 1136 | self._filename = filename |
1132 | self._linenr = linenr | 1137 | self._linenr = linenr |
1133 | 1138 | ||
1134 | return self._parse_expr_rec(feed) | 1139 | return self._parse_expr_rec(feed) |
1135 | 1140 | ||
1136 | def _parse_expr_rec(self, feed): | 1141 | def _parse_expr_rec(self, feed): |
1137 | or_term = self._parse_or_term(feed) | 1142 | or_term = self._parse_or_term(feed) |
1138 | if not feed.check(T_OR): | 1143 | if not feed.check(T_OR): |
1139 | # Common case -- no need for an OR node since it's just a single | 1144 | # Common case -- no need for an OR node since it's just a single |
1140 | # operand | 1145 | # operand |
1141 | return or_term | 1146 | return or_term |
1142 | or_terms = [or_term, self._parse_or_term(feed)] | 1147 | or_terms = [or_term, self._parse_or_term(feed)] |
1143 | while feed.check(T_OR): | 1148 | while feed.check(T_OR): |
1144 | or_terms.append(self._parse_or_term(feed)) | 1149 | or_terms.append(self._parse_or_term(feed)) |
1145 | return (OR, or_terms) | 1150 | return (OR, or_terms) |
1146 | 1151 | ||
1147 | def _parse_or_term(self, feed): | 1152 | def _parse_or_term(self, feed): |
1148 | and_term = self._parse_factor(feed) | 1153 | and_term = self._parse_factor(feed) |
1149 | if not feed.check(T_AND): | 1154 | if not feed.check(T_AND): |
1150 | # Common case -- no need for an AND node since it's just a single | 1155 | # Common case -- no need for an AND node since it's just a single |
1151 | # operand | 1156 | # operand |
1152 | return and_term | 1157 | return and_term |
1153 | and_terms = [and_term, self._parse_factor(feed)] | 1158 | and_terms = [and_term, self._parse_factor(feed)] |
1154 | while feed.check(T_AND): | 1159 | while feed.check(T_AND): |
1155 | and_terms.append(self._parse_factor(feed)) | 1160 | and_terms.append(self._parse_factor(feed)) |
1156 | return (AND, and_terms) | 1161 | return (AND, and_terms) |
1157 | 1162 | ||
1158 | def _parse_factor(self, feed): | 1163 | def _parse_factor(self, feed): |
1159 | token = feed.get_next() | 1164 | token = feed.get_next() |
1160 | 1165 | ||
1161 | if isinstance(token, (Symbol, str)): | 1166 | if isinstance(token, (Symbol, str)): |
1162 | if self._cur_item is not None and isinstance(token, Symbol): | 1167 | if self._cur_item is not None and isinstance(token, Symbol): |
1163 | self._cur_item.referenced_syms.add(token) | 1168 | self._cur_item.referenced_syms.add(token) |
1164 | 1169 | ||
1165 | next_token = feed.peek_next() | 1170 | next_token = feed.peek_next() |
1166 | # For conditional expressions ('depends on <expr>', | 1171 | # For conditional expressions ('depends on <expr>', |
1167 | # '... if <expr>', # etc.), "m" and m are rewritten to | 1172 | # '... if <expr>', # etc.), "m" and m are rewritten to |
1168 | # "m" && MODULES. | 1173 | # "m" && MODULES. |
1169 | if next_token != T_EQUAL and next_token != T_UNEQUAL: | 1174 | if next_token != T_EQUAL and next_token != T_UNEQUAL: |
1170 | if self._transform_m and (token is self.m or token == "m"): | 1175 | if self._transform_m and (token is self.m or token == "m"): |
1171 | return (AND, ["m", self._sym_lookup("MODULES")]) | 1176 | return (AND, ["m", self._sym_lookup("MODULES")]) |
1172 | return token | 1177 | return token |
1173 | 1178 | ||
1174 | relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL | 1179 | relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL |
1175 | token_2 = feed.get_next() | 1180 | token_2 = feed.get_next() |
1176 | if self._cur_item is not None and isinstance(token_2, Symbol): | 1181 | if self._cur_item is not None and isinstance(token_2, Symbol): |
1177 | self._cur_item.referenced_syms.add(token_2) | 1182 | self._cur_item.referenced_syms.add(token_2) |
1178 | return (relation, token, token_2) | 1183 | return (relation, token, token_2) |
1179 | 1184 | ||
1180 | if token == T_NOT: | 1185 | if token == T_NOT: |
1181 | return (NOT, self._parse_factor(feed)) | 1186 | return (NOT, self._parse_factor(feed)) |
1182 | 1187 | ||
1183 | if token == T_OPEN_PAREN: | 1188 | if token == T_OPEN_PAREN: |
1184 | expr_parse = self._parse_expr_rec(feed) | 1189 | expr_parse = self._parse_expr_rec(feed) |
1185 | if not feed.check(T_CLOSE_PAREN): | 1190 | if not feed.check(T_CLOSE_PAREN): |
1186 | _parse_error(self._line, "missing end parenthesis", | 1191 | _parse_error(self._line, "missing end parenthesis", |
1187 | self._filename, self._linenr) | 1192 | self._filename, self._linenr) |
1188 | return expr_parse | 1193 | return expr_parse |
1189 | 1194 | ||
1190 | _parse_error(self._line, "malformed expression", self._filename, | 1195 | _parse_error(self._line, "malformed expression", self._filename, |
1191 | self._linenr) | 1196 | self._linenr) |
1192 | 1197 | ||
1193 | def _tokenize(self, s, for_eval, filename=None, linenr=None): | 1198 | def _tokenize(self, s, for_eval, filename=None, linenr=None): |
1194 | """Returns a _Feed instance containing tokens derived from the string | 1199 | """Returns a _Feed instance containing tokens derived from the string |
1195 | 's'. Registers any new symbols encountered (via _sym_lookup()). | 1200 | 's'. Registers any new symbols encountered (via _sym_lookup()). |
1196 | 1201 | ||
1197 | (I experimented with a pure regular expression implementation, but it | 1202 | (I experimented with a pure regular expression implementation, but it |
1198 | came out slower, less readable, and wouldn't have been as flexible.) | 1203 | came out slower, less readable, and wouldn't have been as flexible.) |
1199 | 1204 | ||
1200 | for_eval: True when parsing an expression for a call to Config.eval(), | 1205 | for_eval: True when parsing an expression for a call to Config.eval(), |
1201 | in which case we should not treat the first token specially nor | 1206 | in which case we should not treat the first token specially nor |
1202 | register new symbols.""" | 1207 | register new symbols.""" |
1203 | 1208 | ||
1204 | s = s.strip() | 1209 | s = s.strip() |
1205 | if s == "" or s[0] == "#": | 1210 | if s == "" or s[0] == "#": |
1206 | return _Feed([]) | 1211 | return _Feed([]) |
1207 | 1212 | ||
1208 | if for_eval: | 1213 | if for_eval: |
1209 | previous = None # The previous token seen | 1214 | previous = None # The previous token seen |
1210 | tokens = [] | 1215 | tokens = [] |
1211 | i = 0 # The current index in the string being tokenized | 1216 | i = 0 # The current index in the string being tokenized |
1212 | 1217 | ||
1213 | else: | 1218 | else: |
1214 | # The initial word on a line is parsed specially. Let | 1219 | # The initial word on a line is parsed specially. Let |
1215 | # command_chars = [A-Za-z0-9_]. Then | 1220 | # command_chars = [A-Za-z0-9_]. Then |
1216 | # - leading non-command_chars characters are ignored, and | 1221 | # - leading non-command_chars characters are ignored, and |
1217 | # - the first token consists the following one or more | 1222 | # - the first token consists the following one or more |
1218 | # command_chars characters. | 1223 | # command_chars characters. |
1219 | # This is why things like "----help--" are accepted. | 1224 | # This is why things like "----help--" are accepted. |
1220 | initial_token_match = _initial_token_re_match(s) | 1225 | initial_token_match = _initial_token_re_match(s) |
1221 | if initial_token_match is None: | 1226 | if initial_token_match is None: |
1222 | return _Feed([]) | 1227 | return _Feed([]) |
1223 | keyword = _get_keyword(initial_token_match.group(1)) | 1228 | keyword = _get_keyword(initial_token_match.group(1)) |
1224 | if keyword == T_HELP: | 1229 | if keyword == T_HELP: |
1225 | # Avoid junk after "help", e.g. "---", being registered as a | 1230 | # Avoid junk after "help", e.g. "---", being registered as a |
1226 | # symbol | 1231 | # symbol |
1227 | return _Feed([T_HELP]) | 1232 | return _Feed([T_HELP]) |
1228 | if keyword is None: | 1233 | if keyword is None: |
1229 | # We expect a keyword as the first token | 1234 | # We expect a keyword as the first token |
1230 | _tokenization_error(s, filename, linenr) | 1235 | _tokenization_error(s, filename, linenr) |
1231 | 1236 | ||
1232 | previous = keyword | 1237 | previous = keyword |
1233 | tokens = [keyword] | 1238 | tokens = [keyword] |
1234 | # The current index in the string being tokenized | 1239 | # The current index in the string being tokenized |
1235 | i = initial_token_match.end() | 1240 | i = initial_token_match.end() |
1236 | 1241 | ||
1237 | # _tokenize() is a hotspot during parsing, and this speeds things up a | 1242 | # _tokenize() is a hotspot during parsing, and this speeds things up a |
1238 | # bit | 1243 | # bit |
1239 | strlen = len(s) | 1244 | strlen = len(s) |
1240 | append = tokens.append | 1245 | append = tokens.append |
1241 | 1246 | ||
1242 | # Main tokenization loop. (Handles tokens past the first one.) | 1247 | # Main tokenization loop. (Handles tokens past the first one.) |
1243 | while i < strlen: | 1248 | while i < strlen: |
1244 | # Test for an identifier/keyword preceded by whitespace first; this | 1249 | # Test for an identifier/keyword preceded by whitespace first; this |
1245 | # is the most common case. | 1250 | # is the most common case. |
1246 | id_keyword_match = _id_keyword_re_match(s, i) | 1251 | id_keyword_match = _id_keyword_re_match(s, i) |
1247 | if id_keyword_match: | 1252 | if id_keyword_match: |
1248 | # We have an identifier or keyword. The above also stripped any | 1253 | # We have an identifier or keyword. The above also stripped any |
1249 | # whitespace for us. | 1254 | # whitespace for us. |
1250 | name = id_keyword_match.group(1) | 1255 | name = id_keyword_match.group(1) |
1251 | # Jump past it | 1256 | # Jump past it |
1252 | i = id_keyword_match.end() | 1257 | i = id_keyword_match.end() |
1253 | 1258 | ||
1254 | keyword = _get_keyword(name) | 1259 | keyword = _get_keyword(name) |
1255 | if keyword is not None: | 1260 | if keyword is not None: |
1256 | # It's a keyword | 1261 | # It's a keyword |
1257 | append(keyword) | 1262 | append(keyword) |
1258 | elif previous in STRING_LEX: | 1263 | elif previous in STRING_LEX: |
1259 | # What would ordinarily be considered an identifier is | 1264 | # What would ordinarily be considered an identifier is |
1260 | # treated as a string after certain tokens | 1265 | # treated as a string after certain tokens |
1261 | append(name) | 1266 | append(name) |
1262 | else: | 1267 | else: |
1263 | # It's a symbol name. _sym_lookup() will take care of | 1268 | # It's a symbol name. _sym_lookup() will take care of |
1264 | # allocating a new Symbol instance if it's the first time | 1269 | # allocating a new Symbol instance if it's the first time |
1265 | # we see it. | 1270 | # we see it. |
1266 | sym = self._sym_lookup(name, for_eval) | 1271 | sym = self._sym_lookup(name, for_eval) |
1267 | 1272 | ||
1268 | if previous == T_CONFIG or previous == T_MENUCONFIG: | 1273 | if previous == T_CONFIG or previous == T_MENUCONFIG: |
1269 | # If the previous token is T_(MENU)CONFIG | 1274 | # If the previous token is T_(MENU)CONFIG |
1270 | # ("(menu)config"), we're tokenizing the first line of | 1275 | # ("(menu)config"), we're tokenizing the first line of |
1271 | # a symbol definition, and should remember this as a | 1276 | # a symbol definition, and should remember this as a |
1272 | # location where the symbol is defined | 1277 | # location where the symbol is defined |
1273 | sym.def_locations.append((filename, linenr)) | 1278 | sym.def_locations.append((filename, linenr)) |
1274 | else: | 1279 | else: |
1275 | # Otherwise, it's a reference to the symbol | 1280 | # Otherwise, it's a reference to the symbol |
1276 | sym.ref_locations.append((filename, linenr)) | 1281 | sym.ref_locations.append((filename, linenr)) |
1277 | 1282 | ||
1278 | append(sym) | 1283 | append(sym) |
1279 | 1284 | ||
1280 | else: | 1285 | else: |
1281 | # Not an identifier/keyword | 1286 | # Not an identifier/keyword |
1282 | 1287 | ||
1283 | while i < strlen and s[i].isspace(): | 1288 | while i < strlen and s[i].isspace(): |
1284 | i += 1 | 1289 | i += 1 |
1285 | if i == strlen: | 1290 | if i == strlen: |
1286 | break | 1291 | break |
1287 | c = s[i] | 1292 | c = s[i] |
1288 | i += 1 | 1293 | i += 1 |
1289 | 1294 | ||
1290 | # String literal (constant symbol) | 1295 | # String literal (constant symbol) |
1291 | if c == '"' or c == "'": | 1296 | if c == '"' or c == "'": |
1292 | if "\\" in s: | 1297 | if "\\" in s: |
1293 | # Slow path: This could probably be sped up, but it's a | 1298 | # Slow path: This could probably be sped up, but it's a |
1294 | # very unusual case anyway. | 1299 | # very unusual case anyway. |
1295 | quote = c | 1300 | quote = c |
1296 | val = "" | 1301 | val = "" |
1297 | while 1: | 1302 | while 1: |
1298 | if i >= len(s): | 1303 | if i >= len(s): |
1299 | _tokenization_error(s, filename, linenr) | 1304 | _tokenization_error(s, filename, linenr) |
1300 | c = s[i] | 1305 | c = s[i] |
1301 | if c == quote: | 1306 | if c == quote: |
1302 | break | 1307 | break |
1303 | if c == "\\": | 1308 | if c == "\\": |
1304 | if i + 1 >= len(s): | 1309 | if i + 1 >= len(s): |
1305 | _tokenization_error(s, filename, linenr) | 1310 | _tokenization_error(s, filename, linenr) |
1306 | val += s[i + 1] | 1311 | val += s[i + 1] |
1307 | i += 2 | 1312 | i += 2 |
1308 | else: | 1313 | else: |
1309 | val += c | 1314 | val += c |
1310 | i += 1 | 1315 | i += 1 |
1311 | i += 1 | 1316 | i += 1 |
1312 | append(val) | 1317 | append(val) |
1313 | else: | 1318 | else: |
1314 | # Fast path: If the string contains no backslashes | 1319 | # Fast path: If the string contains no backslashes |
1315 | # (almost always) we can simply look for the matching | 1320 | # (almost always) we can simply look for the matching |
1316 | # quote. | 1321 | # quote. |
1317 | end = s.find(c, i) | 1322 | end = s.find(c, i) |
1318 | if end == -1: | 1323 | if end == -1: |
1319 | _tokenization_error(s, filename, linenr) | 1324 | _tokenization_error(s, filename, linenr) |
1320 | append(s[i:end]) | 1325 | append(s[i:end]) |
1321 | i = end + 1 | 1326 | i = end + 1 |
1322 | 1327 | ||
1323 | elif c == "&": | 1328 | elif c == "&": |
1324 | # Invalid characters are ignored | 1329 | # Invalid characters are ignored |
1325 | if i >= len(s) or s[i] != "&": continue | 1330 | if i >= len(s) or s[i] != "&": continue |
1326 | append(T_AND) | 1331 | append(T_AND) |
1327 | i += 1 | 1332 | i += 1 |
1328 | 1333 | ||
1329 | elif c == "|": | 1334 | elif c == "|": |
1330 | # Invalid characters are ignored | 1335 | # Invalid characters are ignored |
1331 | if i >= len(s) or s[i] != "|": continue | 1336 | if i >= len(s) or s[i] != "|": continue |
1332 | append(T_OR) | 1337 | append(T_OR) |
1333 | i += 1 | 1338 | i += 1 |
1334 | 1339 | ||
1335 | elif c == "!": | 1340 | elif c == "!": |
1336 | if i < len(s) and s[i] == "=": | 1341 | if i < len(s) and s[i] == "=": |
1337 | append(T_UNEQUAL) | 1342 | append(T_UNEQUAL) |
1338 | i += 1 | 1343 | i += 1 |
1339 | else: | 1344 | else: |
1340 | append(T_NOT) | 1345 | append(T_NOT) |
1341 | 1346 | ||
1342 | elif c == "=": append(T_EQUAL) | 1347 | elif c == "=": append(T_EQUAL) |
1343 | elif c == "(": append(T_OPEN_PAREN) | 1348 | elif c == "(": append(T_OPEN_PAREN) |
1344 | elif c == ")": append(T_CLOSE_PAREN) | 1349 | elif c == ")": append(T_CLOSE_PAREN) |
1345 | elif c == "#": break # Comment | 1350 | elif c == "#": break # Comment |
1346 | 1351 | ||
1347 | else: continue # Invalid characters are ignored | 1352 | else: continue # Invalid characters are ignored |
1348 | 1353 | ||
1349 | previous = tokens[-1] | 1354 | previous = tokens[-1] |
1350 | 1355 | ||
1351 | return _Feed(tokens) | 1356 | return _Feed(tokens) |
1352 | 1357 | ||
1353 | def _sym_lookup(self, name, for_eval=False): | 1358 | def _sym_lookup(self, name, for_eval=False): |
1354 | """Fetches the symbol 'name' from the symbol table, creating and | 1359 | """Fetches the symbol 'name' from the symbol table, creating and |
1355 | registering it if it does not exist. If 'for_eval' is True, the symbol | 1360 | registering it if it does not exist. If 'for_eval' is True, the symbol |
1356 | won't be added to the symbol table if it does not exist -- this is for | 1361 | won't be added to the symbol table if it does not exist -- this is for |
1357 | Config.eval().""" | 1362 | Config.eval().""" |
1358 | if name in self.syms: | 1363 | if name in self.syms: |
1359 | return self.syms[name] | 1364 | return self.syms[name] |
1360 | 1365 | ||
1361 | new_sym = Symbol() | 1366 | new_sym = Symbol() |
1362 | new_sym.config = self | 1367 | new_sym.config = self |
1363 | new_sym.name = name | 1368 | new_sym.name = name |
1364 | if for_eval: | 1369 | if for_eval: |
1365 | self._warn("no symbol {0} in configuration".format(name)) | 1370 | self._warn("no symbol {0} in configuration".format(name)) |
1366 | else: | 1371 | else: |
1367 | self.syms[name] = new_sym | 1372 | self.syms[name] = new_sym |
1368 | return new_sym | 1373 | return new_sym |
1369 | 1374 | ||
1370 | # | 1375 | # |
1371 | # Expression evaluation | 1376 | # Expression evaluation |
1372 | # | 1377 | # |
1373 | 1378 | ||
1374 | def _eval_expr(self, expr): | 1379 | def _eval_expr(self, expr): |
1375 | """Evaluates an expression to "n", "m", or "y".""" | 1380 | """Evaluates an expression to "n", "m", or "y".""" |
1376 | 1381 | ||
1377 | # Handles e.g. an "x if y" condition where the "if y" part is missing. | 1382 | # Handles e.g. an "x if y" condition where the "if y" part is missing. |
1378 | if expr is None: | 1383 | if expr is None: |
1379 | return "y" | 1384 | return "y" |
1380 | 1385 | ||
1381 | res = self._eval_expr_rec(expr) | 1386 | res = self._eval_expr_rec(expr) |
1382 | if res == "m": | 1387 | if res == "m": |
1383 | # Promote "m" to "y" if we're running without modules. | 1388 | # Promote "m" to "y" if we're running without modules. |
1384 | # | 1389 | # |
1385 | # Internally, "m" is often rewritten to "m" && MODULES by both the | 1390 | # Internally, "m" is often rewritten to "m" && MODULES by both the |
1386 | # C implementation and Kconfiglib, which takes care of cases where | 1391 | # C implementation and Kconfiglib, which takes care of cases where |
1387 | # "m" should be demoted to "n" instead. | 1392 | # "m" should be demoted to "n" instead. |
1388 | modules_sym = self.syms.get("MODULES") | 1393 | modules_sym = self.syms.get("MODULES") |
1389 | if modules_sym is None or modules_sym.get_value() != "y": | 1394 | if modules_sym is None or modules_sym.get_value() != "y": |
1390 | return "y" | 1395 | return "y" |
1391 | return res | 1396 | return res |
1392 | 1397 | ||
1393 | def _eval_expr_rec(self, expr): | 1398 | def _eval_expr_rec(self, expr): |
1394 | if isinstance(expr, Symbol): | 1399 | if isinstance(expr, Symbol): |
1395 | # Non-bool/tristate symbols are always "n" in a tristate sense, | 1400 | # Non-bool/tristate symbols are always "n" in a tristate sense, |
1396 | # regardless of their value | 1401 | # regardless of their value |
1397 | if expr.type != BOOL and expr.type != TRISTATE: | 1402 | if expr.type != BOOL and expr.type != TRISTATE: |
1398 | return "n" | 1403 | return "n" |
1399 | return expr.get_value() | 1404 | return expr.get_value() |
1400 | 1405 | ||
1401 | if isinstance(expr, str): | 1406 | if isinstance(expr, str): |
1402 | return expr if (expr == "y" or expr == "m") else "n" | 1407 | return expr if (expr == "y" or expr == "m") else "n" |
1403 | 1408 | ||
1404 | # Ordered by frequency | 1409 | # Ordered by frequency |
1405 | 1410 | ||
1406 | if expr[0] == AND: | 1411 | if expr[0] == AND: |
1407 | res = "y" | 1412 | res = "y" |
1408 | for subexpr in expr[1]: | 1413 | for subexpr in expr[1]: |
1409 | ev = self._eval_expr_rec(subexpr) | 1414 | ev = self._eval_expr_rec(subexpr) |
1410 | # Return immediately upon discovering an "n" term | 1415 | # Return immediately upon discovering an "n" term |
1411 | if ev == "n": | 1416 | if ev == "n": |
1412 | return "n" | 1417 | return "n" |
1413 | if ev == "m": | 1418 | if ev == "m": |
1414 | res = "m" | 1419 | res = "m" |
1415 | # 'res' is either "m" or "y" here; we already handled the | 1420 | # 'res' is either "m" or "y" here; we already handled the |
1416 | # short-circuiting "n" case in the loop. | 1421 | # short-circuiting "n" case in the loop. |
1417 | return res | 1422 | return res |
1418 | 1423 | ||
1419 | if expr[0] == NOT: | 1424 | if expr[0] == NOT: |
1420 | ev = self._eval_expr_rec(expr[1]) | 1425 | ev = self._eval_expr_rec(expr[1]) |
1421 | if ev == "y": | 1426 | if ev == "y": |
1422 | return "n" | 1427 | return "n" |
1423 | return "y" if (ev == "n") else "m" | 1428 | return "y" if (ev == "n") else "m" |
1424 | 1429 | ||
1425 | if expr[0] == OR: | 1430 | if expr[0] == OR: |
1426 | res = "n" | 1431 | res = "n" |
1427 | for subexpr in expr[1]: | 1432 | for subexpr in expr[1]: |
1428 | ev = self._eval_expr_rec(subexpr) | 1433 | ev = self._eval_expr_rec(subexpr) |
1429 | # Return immediately upon discovering a "y" term | 1434 | # Return immediately upon discovering a "y" term |
1430 | if ev == "y": | 1435 | if ev == "y": |
1431 | return "y" | 1436 | return "y" |
1432 | if ev == "m": | 1437 | if ev == "m": |
1433 | res = "m" | 1438 | res = "m" |
1434 | # 'res' is either "n" or "m" here; we already handled the | 1439 | # 'res' is either "n" or "m" here; we already handled the |
1435 | # short-circuiting "y" case in the loop. | 1440 | # short-circuiting "y" case in the loop. |
1436 | return res | 1441 | return res |
1437 | 1442 | ||
1438 | if expr[0] == EQUAL: | 1443 | if expr[0] == EQUAL: |
1439 | return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n" | 1444 | return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n" |
1440 | 1445 | ||
1441 | if expr[0] == UNEQUAL: | 1446 | if expr[0] == UNEQUAL: |
1442 | return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n" | 1447 | return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n" |
1443 | 1448 | ||
1444 | _internal_error("Internal error while evaluating expression: " | 1449 | _internal_error("Internal error while evaluating expression: " |
1445 | "unknown operation {0}.".format(expr[0])) | 1450 | "unknown operation {0}.".format(expr[0])) |
1446 | 1451 | ||
1447 | def _eval_min(self, e1, e2): | 1452 | def _eval_min(self, e1, e2): |
1448 | """Returns the minimum value of the two expressions. Equates None with | 1453 | """Returns the minimum value of the two expressions. Equates None with |
1449 | 'y'.""" | 1454 | 'y'.""" |
1450 | e1_eval = self._eval_expr(e1) | 1455 | e1_eval = self._eval_expr(e1) |
1451 | e2_eval = self._eval_expr(e2) | 1456 | e2_eval = self._eval_expr(e2) |
1452 | return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval | 1457 | return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval |
1453 | 1458 | ||
1454 | def _eval_max(self, e1, e2): | 1459 | def _eval_max(self, e1, e2): |
1455 | """Returns the maximum value of the two expressions. Equates None with | 1460 | """Returns the maximum value of the two expressions. Equates None with |
1456 | 'y'.""" | 1461 | 'y'.""" |
1457 | e1_eval = self._eval_expr(e1) | 1462 | e1_eval = self._eval_expr(e1) |
1458 | e2_eval = self._eval_expr(e2) | 1463 | e2_eval = self._eval_expr(e2) |
1459 | return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval | 1464 | return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval |
1460 | 1465 | ||
1461 | # | 1466 | # |
1462 | # Dependency tracking (for caching and invalidation) | 1467 | # Dependency tracking (for caching and invalidation) |
1463 | # | 1468 | # |
1464 | 1469 | ||
1465 | def _build_dep(self): | 1470 | def _build_dep(self): |
1466 | """Populates the Symbol.dep sets, linking the symbol to the symbols | 1471 | """Populates the Symbol.dep sets, linking the symbol to the symbols |
1467 | that immediately depend on it in the sense that changing the value of | 1472 | that immediately depend on it in the sense that changing the value of |
1468 | the symbol might affect the values of those other symbols. This is used | 1473 | the symbol might affect the values of those other symbols. This is used |
1469 | for caching/invalidation purposes. The calculated sets might be larger | 1474 | for caching/invalidation purposes. The calculated sets might be larger |
1470 | than necessary as we don't do any complicated analysis of the | 1475 | than necessary as we don't do any complicated analysis of the |
1471 | expressions.""" | 1476 | expressions.""" |
1472 | 1477 | ||
1473 | # Adds 'sym' as a directly dependent symbol to all symbols that appear | 1478 | # Adds 'sym' as a directly dependent symbol to all symbols that appear |
1474 | # in the expression 'e' | 1479 | # in the expression 'e' |
1475 | def add_expr_deps(e, sym): | 1480 | def add_expr_deps(e, sym): |
1476 | for s in _get_expr_syms(e): | 1481 | for s in _get_expr_syms(e): |
1477 | s.dep.add(sym) | 1482 | s.dep.add(sym) |
1478 | 1483 | ||
1479 | # The directly dependent symbols of a symbol are: | 1484 | # The directly dependent symbols of a symbol are: |
1480 | # - Any symbols whose prompts, default values, rev_dep (select | 1485 | # - Any symbols whose prompts, default values, rev_dep (select |
1481 | # condition), or ranges depend on the symbol | 1486 | # condition), or ranges depend on the symbol |
1482 | # - Any symbols that belong to the same choice statement as the symbol | 1487 | # - Any symbols that belong to the same choice statement as the symbol |
1483 | # (these won't be included in 'dep' as that makes the dependency | 1488 | # (these won't be included in 'dep' as that makes the dependency |
1484 | # graph unwieldy, but Symbol._get_dependent() will include them) | 1489 | # graph unwieldy, but Symbol._get_dependent() will include them) |
1485 | # - Any symbols in a choice statement that depends on the symbol | 1490 | # - Any symbols in a choice statement that depends on the symbol |
1486 | for sym in self.syms_iter(): | 1491 | for sym in self.syms_iter(): |
1487 | for _, e in sym.prompts: | 1492 | for _, e in sym.prompts: |
1488 | add_expr_deps(e, sym) | 1493 | add_expr_deps(e, sym) |
1489 | 1494 | ||
1490 | for v, e in sym.def_exprs: | 1495 | for v, e in sym.def_exprs: |
1491 | add_expr_deps(v, sym) | 1496 | add_expr_deps(v, sym) |
1492 | add_expr_deps(e, sym) | 1497 | add_expr_deps(e, sym) |
1493 | 1498 | ||
1494 | add_expr_deps(sym.rev_dep, sym) | 1499 | add_expr_deps(sym.rev_dep, sym) |
1495 | 1500 | ||
1496 | for l, u, e in sym.ranges: | 1501 | for l, u, e in sym.ranges: |
1497 | add_expr_deps(l, sym) | 1502 | add_expr_deps(l, sym) |
1498 | add_expr_deps(u, sym) | 1503 | add_expr_deps(u, sym) |
1499 | add_expr_deps(e, sym) | 1504 | add_expr_deps(e, sym) |
1500 | 1505 | ||
1501 | if sym.is_choice_sym: | 1506 | if sym.is_choice_sym: |
1502 | choice = sym.parent | 1507 | choice = sym.parent |
1503 | for _, e in choice.prompts: | 1508 | for _, e in choice.prompts: |
1504 | add_expr_deps(e, sym) | 1509 | add_expr_deps(e, sym) |
1505 | for _, e in choice.def_exprs: | 1510 | for _, e in choice.def_exprs: |
1506 | add_expr_deps(e, sym) | 1511 | add_expr_deps(e, sym) |
1507 | 1512 | ||
1508 | def _eq_to_sym(self, eq): | 1513 | def _eq_to_sym(self, eq): |
1509 | """_expr_depends_on() helper. For (in)equalities of the form sym = y/m | 1514 | """_expr_depends_on() helper. For (in)equalities of the form sym = y/m |
1510 | or sym != n, returns sym. For other (in)equalities, returns None.""" | 1515 | or sym != n, returns sym. For other (in)equalities, returns None.""" |
1511 | relation, left, right = eq | 1516 | relation, left, right = eq |
1512 | 1517 | ||
1513 | def transform_y_m_n(item): | 1518 | def transform_y_m_n(item): |
1514 | if item is self.y: return "y" | 1519 | if item is self.y: return "y" |
1515 | if item is self.m: return "m" | 1520 | if item is self.m: return "m" |
1516 | if item is self.n: return "n" | 1521 | if item is self.n: return "n" |
1517 | return item | 1522 | return item |
1518 | 1523 | ||
1519 | left = transform_y_m_n(left) | 1524 | left = transform_y_m_n(left) |
1520 | right = transform_y_m_n(right) | 1525 | right = transform_y_m_n(right) |
1521 | 1526 | ||
1522 | # Make sure the symbol (if any) appears to the left | 1527 | # Make sure the symbol (if any) appears to the left |
1523 | if not isinstance(left, Symbol): | 1528 | if not isinstance(left, Symbol): |
1524 | left, right = right, left | 1529 | left, right = right, left |
1525 | if not isinstance(left, Symbol): | 1530 | if not isinstance(left, Symbol): |
1526 | return None | 1531 | return None |
1527 | if (relation == EQUAL and (right == "y" or right == "m")) or \ | 1532 | if (relation == EQUAL and (right == "y" or right == "m")) or \ |
1528 | (relation == UNEQUAL and right == "n"): | 1533 | (relation == UNEQUAL and right == "n"): |
1529 | return left | 1534 | return left |
1530 | return None | 1535 | return None |
1531 | 1536 | ||
1532 | def _expr_depends_on(self, expr, sym): | 1537 | def _expr_depends_on(self, expr, sym): |
1533 | """Reimplementation of expr_depends_symbol() from mconf.c. Used to | 1538 | """Reimplementation of expr_depends_symbol() from mconf.c. Used to |
1534 | determine if a submenu should be implicitly created, which influences | 1539 | determine if a submenu should be implicitly created, which influences |
1535 | what items inside choice statements are considered choice items.""" | 1540 | what items inside choice statements are considered choice items.""" |
1536 | if expr is None: | 1541 | if expr is None: |
1537 | return False | 1542 | return False |
1538 | 1543 | ||
1539 | def rec(expr): | 1544 | def rec(expr): |
1540 | if isinstance(expr, str): | 1545 | if isinstance(expr, str): |
1541 | return False | 1546 | return False |
1542 | if isinstance(expr, Symbol): | 1547 | if isinstance(expr, Symbol): |
1543 | return expr is sym | 1548 | return expr is sym |
1544 | 1549 | ||
1545 | if expr[0] in (EQUAL, UNEQUAL): | 1550 | if expr[0] in (EQUAL, UNEQUAL): |
1546 | return self._eq_to_sym(expr) is sym | 1551 | return self._eq_to_sym(expr) is sym |
1547 | if expr[0] == AND: | 1552 | if expr[0] == AND: |
1548 | for and_expr in expr[1]: | 1553 | for and_expr in expr[1]: |
1549 | if rec(and_expr): | 1554 | if rec(and_expr): |
1550 | return True | 1555 | return True |
1551 | return False | 1556 | return False |
1552 | 1557 | ||
1553 | return rec(expr) | 1558 | return rec(expr) |
1554 | 1559 | ||
1555 | def _invalidate_all(self): | 1560 | def _invalidate_all(self): |
1556 | for sym in self.syms_iter(): | 1561 | for sym in self.syms_iter(): |
1557 | sym._invalidate() | 1562 | sym._invalidate() |
1558 | 1563 | ||
1559 | # | 1564 | # |
1560 | # Printing and misc. | 1565 | # Printing and misc. |
1561 | # | 1566 | # |
1562 | 1567 | ||
1563 | def _expand_sym_refs(self, s): | 1568 | def _expand_sym_refs(self, s): |
1564 | """Expands $-references to symbols in 's' to symbol values, or to the | 1569 | """Expands $-references to symbols in 's' to symbol values, or to the |
1565 | empty string for undefined symbols.""" | 1570 | empty string for undefined symbols.""" |
1566 | 1571 | ||
1567 | while 1: | 1572 | while 1: |
1568 | sym_ref_match = _sym_ref_re_search(s) | 1573 | sym_ref_match = _sym_ref_re_search(s) |
1569 | if sym_ref_match is None: | 1574 | if sym_ref_match is None: |
1570 | return s | 1575 | return s |
1571 | 1576 | ||
1572 | sym_name = sym_ref_match.group(0)[1:] | 1577 | sym_name = sym_ref_match.group(0)[1:] |
1573 | sym = self.syms.get(sym_name) | 1578 | sym = self.syms.get(sym_name) |
1574 | expansion = "" if sym is None else sym.get_value() | 1579 | expansion = "" if sym is None else sym.get_value() |
1575 | 1580 | ||
1576 | s = s[:sym_ref_match.start()] + \ | 1581 | s = s[:sym_ref_match.start()] + \ |
1577 | expansion + \ | 1582 | expansion + \ |
1578 | s[sym_ref_match.end():] | 1583 | s[sym_ref_match.end():] |
1579 | 1584 | ||
1580 | def _expr_val_str(self, expr, no_value_str="(none)", | 1585 | def _expr_val_str(self, expr, no_value_str="(none)", |
1581 | get_val_instead_of_eval=False): | 1586 | get_val_instead_of_eval=False): |
1582 | """Printing helper. Returns a string with 'expr' and its value. | 1587 | """Printing helper. Returns a string with 'expr' and its value. |
1583 | 1588 | ||
1584 | no_value_str: String to return when 'expr' is missing (None). | 1589 | no_value_str: String to return when 'expr' is missing (None). |
1585 | 1590 | ||
1586 | get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant | 1591 | get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant |
1587 | symbol) and get its value directly instead of evaluating it to a | 1592 | symbol) and get its value directly instead of evaluating it to a |
1588 | tristate value.""" | 1593 | tristate value.""" |
1589 | 1594 | ||
1590 | if expr is None: | 1595 | if expr is None: |
1591 | return no_value_str | 1596 | return no_value_str |
1592 | 1597 | ||
1593 | if get_val_instead_of_eval: | 1598 | if get_val_instead_of_eval: |
1594 | if isinstance(expr, str): | 1599 | if isinstance(expr, str): |
1595 | return _expr_to_str(expr) | 1600 | return _expr_to_str(expr) |
1596 | val = expr.get_value() | 1601 | val = expr.get_value() |
1597 | else: | 1602 | else: |
1598 | val = self._eval_expr(expr) | 1603 | val = self._eval_expr(expr) |
1599 | 1604 | ||
1600 | return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val)) | 1605 | return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val)) |
1601 | 1606 | ||
1602 | def _get_sym_or_choice_str(self, sc): | 1607 | def _get_sym_or_choice_str(self, sc): |
1603 | """Symbols and choices have many properties in common, so we factor out | 1608 | """Symbols and choices have many properties in common, so we factor out |
1604 | common __str__() stuff here. "sc" is short for "symbol or choice".""" | 1609 | common __str__() stuff here. "sc" is short for "symbol or choice".""" |
1605 | 1610 | ||
1606 | # As we deal a lot with string representations here, use some | 1611 | # As we deal a lot with string representations here, use some |
1607 | # convenient shorthand: | 1612 | # convenient shorthand: |
1608 | s = _expr_to_str | 1613 | s = _expr_to_str |
1609 | 1614 | ||
1610 | # | 1615 | # |
1611 | # Common symbol/choice properties | 1616 | # Common symbol/choice properties |
1612 | # | 1617 | # |
1613 | 1618 | ||
1614 | user_val_str = "(no user value)" if sc.user_val is None else \ | 1619 | user_val_str = "(no user value)" if sc.user_val is None else \ |
1615 | s(sc.user_val) | 1620 | s(sc.user_val) |
1616 | 1621 | ||
1617 | # Build prompts string | 1622 | # Build prompts string |
1618 | if not sc.prompts: | 1623 | if not sc.prompts: |
1619 | prompts_str = " (no prompts)" | 1624 | prompts_str = " (no prompts)" |
1620 | else: | 1625 | else: |
1621 | prompts_str_rows = [] | 1626 | prompts_str_rows = [] |
1622 | for prompt, cond_expr in sc.orig_prompts: | 1627 | for prompt, cond_expr in sc.orig_prompts: |
1623 | if cond_expr is None: | 1628 | if cond_expr is None: |
1624 | prompts_str_rows.append(' "{0}"'.format(prompt)) | 1629 | prompts_str_rows.append(' "{0}"'.format(prompt)) |
1625 | else: | 1630 | else: |
1626 | prompts_str_rows.append( | 1631 | prompts_str_rows.append( |
1627 | ' "{0}" if {1}'.format(prompt, | 1632 | ' "{0}" if {1}'.format(prompt, |
1628 | self._expr_val_str(cond_expr))) | 1633 | self._expr_val_str(cond_expr))) |
1629 | prompts_str = "\n".join(prompts_str_rows) | 1634 | prompts_str = "\n".join(prompts_str_rows) |
1630 | 1635 | ||
1631 | # Build locations string | 1636 | # Build locations string |
1632 | if not sc.def_locations: | 1637 | if not sc.def_locations: |
1633 | locations_str = "(no locations)" | 1638 | locations_str = "(no locations)" |
1634 | else: | 1639 | else: |
1635 | locations_str = " ".join(["{0}:{1}".format(filename, linenr) for | 1640 | locations_str = " ".join(["{0}:{1}".format(filename, linenr) for |
1636 | (filename, linenr) in sc.def_locations]) | 1641 | (filename, linenr) in sc.def_locations]) |
1637 | 1642 | ||
1638 | # Build additional-dependencies-from-menus-and-ifs string | 1643 | # Build additional-dependencies-from-menus-and-ifs string |
1639 | additional_deps_str = " " + \ | 1644 | additional_deps_str = " " + \ |
1640 | self._expr_val_str(sc.deps_from_containing, | 1645 | self._expr_val_str(sc.deps_from_containing, |
1641 | "(no additional dependencies)") | 1646 | "(no additional dependencies)") |
1642 | 1647 | ||
1643 | # | 1648 | # |
1644 | # Symbol-specific stuff | 1649 | # Symbol-specific stuff |
1645 | # | 1650 | # |
1646 | 1651 | ||
1647 | if isinstance(sc, Symbol): | 1652 | if isinstance(sc, Symbol): |
1648 | # Build ranges string | 1653 | # Build ranges string |
1649 | if isinstance(sc, Symbol): | 1654 | if isinstance(sc, Symbol): |
1650 | if not sc.ranges: | 1655 | if not sc.ranges: |
1651 | ranges_str = " (no ranges)" | 1656 | ranges_str = " (no ranges)" |
1652 | else: | 1657 | else: |
1653 | ranges_str_rows = [] | 1658 | ranges_str_rows = [] |
1654 | for l, u, cond_expr in sc.ranges: | 1659 | for l, u, cond_expr in sc.ranges: |
1655 | if cond_expr is None: | 1660 | if cond_expr is None: |
1656 | ranges_str_rows.append(" [{0}, {1}]".format(s(l), | 1661 | ranges_str_rows.append(" [{0}, {1}]".format(s(l), |
1657 | s(u))) | 1662 | s(u))) |
1658 | else: | 1663 | else: |
1659 | ranges_str_rows.append(" [{0}, {1}] if {2}" | 1664 | ranges_str_rows.append(" [{0}, {1}] if {2}" |
1660 | .format(s(l), s(u), | 1665 | .format(s(l), s(u), |
1661 | self._expr_val_str(cond_expr))) | 1666 | self._expr_val_str(cond_expr))) |
1662 | ranges_str = "\n".join(ranges_str_rows) | 1667 | ranges_str = "\n".join(ranges_str_rows) |
1663 | 1668 | ||
1664 | # Build default values string | 1669 | # Build default values string |
1665 | if not sc.def_exprs: | 1670 | if not sc.def_exprs: |
1666 | defaults_str = " (no default values)" | 1671 | defaults_str = " (no default values)" |
1667 | else: | 1672 | else: |
1668 | defaults_str_rows = [] | 1673 | defaults_str_rows = [] |
1669 | for val_expr, cond_expr in sc.orig_def_exprs: | 1674 | for val_expr, cond_expr in sc.orig_def_exprs: |
1670 | row_str = " " + self._expr_val_str(val_expr, "(none)", | 1675 | row_str = " " + self._expr_val_str(val_expr, "(none)", |
1671 | sc.type == STRING) | 1676 | sc.type == STRING) |
1672 | defaults_str_rows.append(row_str) | 1677 | defaults_str_rows.append(row_str) |
1673 | defaults_str_rows.append(" Condition: " + | 1678 | defaults_str_rows.append(" Condition: " + |
1674 | self._expr_val_str(cond_expr)) | 1679 | self._expr_val_str(cond_expr)) |
1675 | defaults_str = "\n".join(defaults_str_rows) | 1680 | defaults_str = "\n".join(defaults_str_rows) |
1676 | 1681 | ||
1677 | # Build selects string | 1682 | # Build selects string |
1678 | if not sc.orig_selects: | 1683 | if not sc.orig_selects: |
1679 | selects_str = " (no selects)" | 1684 | selects_str = " (no selects)" |
1680 | else: | 1685 | else: |
1681 | selects_str_rows = [] | 1686 | selects_str_rows = [] |
1682 | for target, cond_expr in sc.orig_selects: | 1687 | for target, cond_expr in sc.orig_selects: |
1683 | if cond_expr is None: | 1688 | if cond_expr is None: |
1684 | selects_str_rows.append(" {0}".format(target.name)) | 1689 | selects_str_rows.append(" {0}".format(target.name)) |
1685 | else: | 1690 | else: |
1686 | selects_str_rows.append( | 1691 | selects_str_rows.append( |
1687 | " {0} if {1}".format(target.name, | 1692 | " {0} if {1}".format(target.name, |
1688 | self._expr_val_str(cond_expr))) | 1693 | self._expr_val_str(cond_expr))) |
1689 | selects_str = "\n".join(selects_str_rows) | 1694 | selects_str = "\n".join(selects_str_rows) |
1690 | 1695 | ||
1691 | res = _lines("Symbol " + | 1696 | res = _lines("Symbol " + |
1692 | ("(no name)" if sc.name is None else sc.name), | 1697 | ("(no name)" if sc.name is None else sc.name), |
1693 | "Type : " + TYPENAME[sc.type], | 1698 | "Type : " + TYPENAME[sc.type], |
1694 | "Value : " + s(sc.get_value()), | 1699 | "Value : " + s(sc.get_value()), |
1695 | "User value : " + user_val_str, | 1700 | "User value : " + user_val_str, |
1696 | "Visibility : " + s(_get_visibility(sc)), | 1701 | "Visibility : " + s(_get_visibility(sc)), |
1697 | "Is choice item : " + BOOL_STR[sc.is_choice_sym], | 1702 | "Is choice item : " + BOOL_STR[sc.is_choice_sym], |
1698 | "Is defined : " + BOOL_STR[sc.is_defined_], | 1703 | "Is defined : " + BOOL_STR[sc.is_defined_], |
1699 | "Is from env. : " + BOOL_STR[sc.is_from_env], | 1704 | "Is from env. : " + BOOL_STR[sc.is_from_env], |
1700 | "Is special : " + BOOL_STR[sc.is_special_] + "\n") | 1705 | "Is special : " + BOOL_STR[sc.is_special_] + "\n") |
1701 | if sc.ranges: | 1706 | if sc.ranges: |
1702 | res += _lines("Ranges:", ranges_str + "\n") | 1707 | res += _lines("Ranges:", ranges_str + "\n") |
1703 | res += _lines("Prompts:", | 1708 | res += _lines("Prompts:", |
1704 | prompts_str, | 1709 | prompts_str, |
1705 | "Default values:", | 1710 | "Default values:", |
1706 | defaults_str, | 1711 | defaults_str, |
1707 | "Selects:", | 1712 | "Selects:", |
1708 | selects_str, | 1713 | selects_str, |
1709 | "Reverse (select-related) dependencies:", | 1714 | "Reverse (select-related) dependencies:", |
1710 | " (no reverse dependencies)" if sc.rev_dep == "n" | 1715 | " (no reverse dependencies)" if sc.rev_dep == "n" |
1711 | else " " + self._expr_val_str(sc.rev_dep), | 1716 | else " " + self._expr_val_str(sc.rev_dep), |
1712 | "Additional dependencies from enclosing menus " | 1717 | "Additional dependencies from enclosing menus " |
1713 | "and ifs:", | 1718 | "and ifs:", |
1714 | additional_deps_str, | 1719 | additional_deps_str, |
1715 | "Locations: " + locations_str) | 1720 | "Locations: " + locations_str) |
1716 | 1721 | ||
1717 | return res | 1722 | return res |
1718 | 1723 | ||
1719 | # | 1724 | # |
1720 | # Choice-specific stuff | 1725 | # Choice-specific stuff |
1721 | # | 1726 | # |
1722 | 1727 | ||
1723 | # Build selected symbol string | 1728 | # Build selected symbol string |
1724 | sel = sc.get_selection() | 1729 | sel = sc.get_selection() |
1725 | sel_str = "(no selection)" if sel is None else sel.name | 1730 | sel_str = "(no selection)" if sel is None else sel.name |
1726 | 1731 | ||
1727 | # Build default values string | 1732 | # Build default values string |
1728 | if not sc.def_exprs: | 1733 | if not sc.def_exprs: |
1729 | defaults_str = " (no default values)" | 1734 | defaults_str = " (no default values)" |
1730 | else: | 1735 | else: |
1731 | defaults_str_rows = [] | 1736 | defaults_str_rows = [] |
1732 | for sym, cond_expr in sc.orig_def_exprs: | 1737 | for sym, cond_expr in sc.orig_def_exprs: |
1733 | if cond_expr is None: | 1738 | if cond_expr is None: |
1734 | defaults_str_rows.append(" {0}".format(sym.name)) | 1739 | defaults_str_rows.append(" {0}".format(sym.name)) |
1735 | else: | 1740 | else: |
1736 | defaults_str_rows.append(" {0} if {1}".format(sym.name, | 1741 | defaults_str_rows.append(" {0} if {1}".format(sym.name, |
1737 | self._expr_val_str(cond_expr))) | 1742 | self._expr_val_str(cond_expr))) |
1738 | defaults_str = "\n".join(defaults_str_rows) | 1743 | defaults_str = "\n".join(defaults_str_rows) |
1739 | 1744 | ||
1740 | # Build contained symbols string | 1745 | # Build contained symbols string |
1741 | names = [sym.name for sym in sc.actual_symbols] | 1746 | names = [sym.name for sym in sc.actual_symbols] |
1742 | syms_string = " ".join(names) if names else "(empty)" | 1747 | syms_string = " ".join(names) if names else "(empty)" |
1743 | 1748 | ||
1744 | return _lines("Choice", | 1749 | return _lines("Choice", |
1745 | "Name (for named choices): " + | 1750 | "Name (for named choices): " + |
1746 | ("(no name)" if sc.name is None else sc.name), | 1751 | ("(no name)" if sc.name is None else sc.name), |
1747 | "Type : " + TYPENAME[sc.type], | 1752 | "Type : " + TYPENAME[sc.type], |
1748 | "Selected symbol : " + sel_str, | 1753 | "Selected symbol : " + sel_str, |
1749 | "User value : " + user_val_str, | 1754 | "User value : " + user_val_str, |
1750 | "Mode : " + s(sc.get_mode()), | 1755 | "Mode : " + s(sc.get_mode()), |
1751 | "Visibility : " + s(_get_visibility(sc)), | 1756 | "Visibility : " + s(_get_visibility(sc)), |
1752 | "Optional : " + BOOL_STR[sc.optional], | 1757 | "Optional : " + BOOL_STR[sc.optional], |
1753 | "Prompts:", | 1758 | "Prompts:", |
1754 | prompts_str, | 1759 | prompts_str, |
1755 | "Defaults:", | 1760 | "Defaults:", |
1756 | defaults_str, | 1761 | defaults_str, |
1757 | "Choice symbols:", | 1762 | "Choice symbols:", |
1758 | " " + syms_string, | 1763 | " " + syms_string, |
1759 | "Additional dependencies from enclosing menus and " | 1764 | "Additional dependencies from enclosing menus and " |
1760 | "ifs:", | 1765 | "ifs:", |
1761 | additional_deps_str, | 1766 | additional_deps_str, |
1762 | "Locations: " + locations_str) | 1767 | "Locations: " + locations_str) |
1763 | 1768 | ||
1764 | def _warn(self, msg, filename=None, linenr=None): | 1769 | def _warn(self, msg, filename=None, linenr=None): |
1765 | """For printing warnings to stderr.""" | 1770 | """For printing warnings to stderr.""" |
1771 | msg = _build_msg("warning: " + msg, filename, linenr) | ||
1766 | if self.print_warnings: | 1772 | if self.print_warnings: |
1767 | _stderr_msg("warning: " + msg, filename, linenr) | 1773 | sys.stderr.write(msg + "\n") |
1774 | self._warnings.append(msg) | ||
1768 | 1775 | ||
1769 | class Item(object): | 1776 | class Item(object): |
1770 | 1777 | ||
1771 | """Base class for symbols and other Kconfig constructs. Subclasses are | 1778 | """Base class for symbols and other Kconfig constructs. Subclasses are |
1772 | Symbol, Choice, Menu, and Comment.""" | 1779 | Symbol, Choice, Menu, and Comment.""" |
1773 | 1780 | ||
1774 | def is_symbol(self): | 1781 | def is_symbol(self): |
1775 | """Returns True if the item is a symbol. Short for | 1782 | """Returns True if the item is a symbol. Short for |
1776 | isinstance(item, kconfiglib.Symbol).""" | 1783 | isinstance(item, kconfiglib.Symbol).""" |
1777 | return isinstance(self, Symbol) | 1784 | return isinstance(self, Symbol) |
1778 | 1785 | ||
1779 | def is_choice(self): | 1786 | def is_choice(self): |
1780 | """Returns True if the item is a choice. Short for | 1787 | """Returns True if the item is a choice. Short for |
1781 | isinstance(item, kconfiglib.Choice).""" | 1788 | isinstance(item, kconfiglib.Choice).""" |
1782 | return isinstance(self, Choice) | 1789 | return isinstance(self, Choice) |
1783 | 1790 | ||
1784 | def is_menu(self): | 1791 | def is_menu(self): |
1785 | """Returns True if the item is a menu. Short for | 1792 | """Returns True if the item is a menu. Short for |
1786 | isinstance(item, kconfiglib.Menu).""" | 1793 | isinstance(item, kconfiglib.Menu).""" |
1787 | return isinstance(self, Menu) | 1794 | return isinstance(self, Menu) |
1788 | 1795 | ||
1789 | def is_comment(self): | 1796 | def is_comment(self): |
1790 | """Returns True if the item is a comment. Short for | 1797 | """Returns True if the item is a comment. Short for |
1791 | isinstance(item, kconfiglib.Comment).""" | 1798 | isinstance(item, kconfiglib.Comment).""" |
1792 | return isinstance(self, Comment) | 1799 | return isinstance(self, Comment) |
1793 | 1800 | ||
1794 | class Symbol(Item): | 1801 | class Symbol(Item): |
1795 | 1802 | ||
1796 | """Represents a configuration symbol - e.g. FOO for | 1803 | """Represents a configuration symbol - e.g. FOO for |
1797 | 1804 | ||
1798 | config FOO | 1805 | config FOO |
1799 | ...""" | 1806 | ...""" |
1800 | 1807 | ||
1801 | # | 1808 | # |
1802 | # Public interface | 1809 | # Public interface |
1803 | # | 1810 | # |
1804 | 1811 | ||
1805 | def get_config(self): | 1812 | def get_config(self): |
1806 | """Returns the Config instance this symbol is from.""" | 1813 | """Returns the Config instance this symbol is from.""" |
1807 | return self.config | 1814 | return self.config |
1808 | 1815 | ||
1809 | def get_name(self): | 1816 | def get_name(self): |
1810 | """Returns the name of the symbol.""" | 1817 | """Returns the name of the symbol.""" |
1811 | return self.name | 1818 | return self.name |
1812 | 1819 | ||
1813 | def get_type(self): | 1820 | def get_type(self): |
1814 | """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE, | 1821 | """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE, |
1815 | STRING, HEX, or INT. These are defined at the top level of the module, | 1822 | STRING, HEX, or INT. These are defined at the top level of the module, |
1816 | so you'd do something like | 1823 | so you'd do something like |
1817 | 1824 | ||
1818 | if sym.get_type() == kconfiglib.STRING: | 1825 | if sym.get_type() == kconfiglib.STRING: |
1819 | ...""" | 1826 | ...""" |
1820 | return self.type | 1827 | return self.type |
1821 | 1828 | ||
1822 | def get_prompts(self): | 1829 | def get_prompts(self): |
1823 | """Returns a list of prompts defined for the symbol, in the order they | 1830 | """Returns a list of prompts defined for the symbol, in the order they |
1824 | appear in the configuration files. Returns the empty list for symbols | 1831 | appear in the configuration files. Returns the empty list for symbols |
1825 | with no prompt. | 1832 | with no prompt. |
1826 | 1833 | ||
1827 | This list will have a single entry for the vast majority of symbols | 1834 | This list will have a single entry for the vast majority of symbols |
1828 | having prompts, but having multiple prompts for a single symbol is | 1835 | having prompts, but having multiple prompts for a single symbol is |
1829 | possible through having multiple 'config' entries for it.""" | 1836 | possible through having multiple 'config' entries for it.""" |
1830 | return [prompt for prompt, _ in self.orig_prompts] | 1837 | return [prompt for prompt, _ in self.orig_prompts] |
1831 | 1838 | ||
1832 | def get_help(self): | 1839 | def get_help(self): |
1833 | """Returns the help text of the symbol, or None if the symbol has no | 1840 | """Returns the help text of the symbol, or None if the symbol has no |
1834 | help text.""" | 1841 | help text.""" |
1835 | return self.help | 1842 | return self.help |
1836 | 1843 | ||
1837 | def get_parent(self): | 1844 | def get_parent(self): |
1838 | """Returns the menu or choice statement that contains the symbol, or | 1845 | """Returns the menu or choice statement that contains the symbol, or |
1839 | None if the symbol is at the top level. Note that if statements are | 1846 | None if the symbol is at the top level. Note that if statements are |
1840 | treated as syntactic and do not have an explicit class | 1847 | treated as syntactic and do not have an explicit class |
1841 | representation.""" | 1848 | representation.""" |
1842 | return self.parent | 1849 | return self.parent |
1843 | 1850 | ||
1844 | def get_def_locations(self): | 1851 | def get_def_locations(self): |
1845 | """Returns a list of (filename, linenr) tuples, where filename (string) | 1852 | """Returns a list of (filename, linenr) tuples, where filename (string) |
1846 | and linenr (int) represent a location where the symbol is defined. For | 1853 | and linenr (int) represent a location where the symbol is defined. For |
1847 | the vast majority of symbols this list will only contain one element. | 1854 | the vast majority of symbols this list will only contain one element. |
1848 | For the following Kconfig, FOO would get two entries: the lines marked | 1855 | For the following Kconfig, FOO would get two entries: the lines marked |
1849 | with *. | 1856 | with *. |
1850 | 1857 | ||
1851 | config FOO * | 1858 | config FOO * |
1852 | bool "foo prompt 1" | 1859 | bool "foo prompt 1" |
1853 | 1860 | ||
1854 | config FOO * | 1861 | config FOO * |
1855 | bool "foo prompt 2" | 1862 | bool "foo prompt 2" |
1856 | """ | 1863 | """ |
1857 | return self.def_locations | 1864 | return self.def_locations |
1858 | 1865 | ||
1859 | def get_ref_locations(self): | 1866 | def get_ref_locations(self): |
1860 | """Returns a list of (filename, linenr) tuples, where filename (string) | 1867 | """Returns a list of (filename, linenr) tuples, where filename (string) |
1861 | and linenr (int) represent a location where the symbol is referenced in | 1868 | and linenr (int) represent a location where the symbol is referenced in |
1862 | the configuration. For example, the lines marked by * would be included | 1869 | the configuration. For example, the lines marked by * would be included |
1863 | for FOO below: | 1870 | for FOO below: |
1864 | 1871 | ||
1865 | config A | 1872 | config A |
1866 | bool | 1873 | bool |
1867 | default BAR || FOO * | 1874 | default BAR || FOO * |
1868 | 1875 | ||
1869 | config B | 1876 | config B |
1870 | tristate | 1877 | tristate |
1871 | depends on FOO * | 1878 | depends on FOO * |
1872 | default m if FOO * | 1879 | default m if FOO * |
1873 | 1880 | ||
1874 | if FOO * | 1881 | if FOO * |
1875 | config A | 1882 | config A |
1876 | bool "A" | 1883 | bool "A" |
1877 | endif | 1884 | endif |
1878 | 1885 | ||
1879 | config FOO (definition not included) | 1886 | config FOO (definition not included) |
1880 | bool | 1887 | bool |
1881 | """ | 1888 | """ |
1882 | return self.ref_locations | 1889 | return self.ref_locations |
1883 | 1890 | ||
1884 | def get_value(self): | 1891 | def get_value(self): |
1885 | """Calculate and return the value of the symbol. See also | 1892 | """Calculate and return the value of the symbol. See also |
1886 | Symbol.set_user_value().""" | 1893 | Symbol.set_user_value().""" |
1887 | 1894 | ||
1888 | if self.cached_val is not None: | 1895 | if self.cached_val is not None: |
1889 | return self.cached_val | 1896 | return self.cached_val |
1890 | 1897 | ||
1891 | # As a quirk of Kconfig, undefined symbols get their name as their | 1898 | # As a quirk of Kconfig, undefined symbols get their name as their |
1892 | # value. This is why things like "FOO = bar" work for seeing if FOO has | 1899 | # value. This is why things like "FOO = bar" work for seeing if FOO has |
1893 | # the value "bar". | 1900 | # the value "bar". |
1894 | if self.type == UNKNOWN: | 1901 | if self.type == UNKNOWN: |
1895 | self.cached_val = self.name | 1902 | self.cached_val = self.name |
1896 | return self.name | 1903 | return self.name |
1897 | 1904 | ||
1898 | new_val = DEFAULT_VALUE[self.type] | 1905 | new_val = DEFAULT_VALUE[self.type] |
1899 | vis = _get_visibility(self) | 1906 | vis = _get_visibility(self) |
1900 | 1907 | ||
1901 | # This is easiest to calculate together with the value | 1908 | # This is easiest to calculate together with the value |
1902 | self.write_to_conf = False | 1909 | self.write_to_conf = False |
1903 | 1910 | ||
1904 | if self.type == BOOL or self.type == TRISTATE: | 1911 | if self.type == BOOL or self.type == TRISTATE: |
1905 | # The visibility and mode (modules-only or single-selection) of | 1912 | # The visibility and mode (modules-only or single-selection) of |
1906 | # choice items will be taken into account in _get_visibility() | 1913 | # choice items will be taken into account in _get_visibility() |
1907 | if self.is_choice_sym: | 1914 | if self.is_choice_sym: |
1908 | if vis != "n": | 1915 | if vis != "n": |
1909 | choice = self.parent | 1916 | choice = self.parent |
1910 | mode = choice.get_mode() | 1917 | mode = choice.get_mode() |
1911 | 1918 | ||
1912 | self.write_to_conf = (mode != "n") | 1919 | self.write_to_conf = (mode != "n") |
1913 | 1920 | ||
1914 | if mode == "y": | 1921 | if mode == "y": |
1915 | if choice.get_selection() is self: | 1922 | if choice.get_selection() is self: |
1916 | new_val = "y" | 1923 | new_val = "y" |
1917 | else: | 1924 | else: |
1918 | new_val = "n" | 1925 | new_val = "n" |
1919 | elif mode == "m": | 1926 | elif mode == "m": |
1920 | if self.user_val == "m" or self.user_val == "y": | 1927 | if self.user_val == "m" or self.user_val == "y": |
1921 | new_val = "m" | 1928 | new_val = "m" |
1922 | 1929 | ||
1923 | else: | 1930 | else: |
1924 | # If the symbol is visible and has a user value, use that. | 1931 | # If the symbol is visible and has a user value, use that. |
1925 | # Otherwise, look at defaults. | 1932 | # Otherwise, look at defaults. |
1926 | use_defaults = True | 1933 | use_defaults = True |
1927 | 1934 | ||
1928 | if vis != "n": | 1935 | if vis != "n": |
1929 | self.write_to_conf = True | 1936 | self.write_to_conf = True |
1930 | if self.user_val is not None: | 1937 | if self.user_val is not None: |
1931 | new_val = self.config._eval_min(self.user_val, vis) | 1938 | new_val = self.config._eval_min(self.user_val, vis) |
1932 | use_defaults = False | 1939 | use_defaults = False |
1933 | 1940 | ||
1934 | if use_defaults: | 1941 | if use_defaults: |
1935 | for val_expr, cond_expr in self.def_exprs: | 1942 | for val_expr, cond_expr in self.def_exprs: |
1936 | cond_eval = self.config._eval_expr(cond_expr) | 1943 | cond_eval = self.config._eval_expr(cond_expr) |
1937 | if cond_eval != "n": | 1944 | if cond_eval != "n": |
1938 | self.write_to_conf = True | 1945 | self.write_to_conf = True |
1939 | new_val = self.config._eval_min(val_expr, | 1946 | new_val = self.config._eval_min(val_expr, |
1940 | cond_eval) | 1947 | cond_eval) |
1941 | break | 1948 | break |
1942 | 1949 | ||
1943 | # Reverse (select-related) dependencies take precedence | 1950 | # Reverse (select-related) dependencies take precedence |
1944 | rev_dep_val = self.config._eval_expr(self.rev_dep) | 1951 | rev_dep_val = self.config._eval_expr(self.rev_dep) |
1945 | if rev_dep_val != "n": | 1952 | if rev_dep_val != "n": |
1946 | self.write_to_conf = True | 1953 | self.write_to_conf = True |
1947 | new_val = self.config._eval_max(new_val, rev_dep_val) | 1954 | new_val = self.config._eval_max(new_val, rev_dep_val) |
1948 | 1955 | ||
1949 | # Promote "m" to "y" for booleans | 1956 | # Promote "m" to "y" for booleans |
1950 | if new_val == "m" and self.type == BOOL: | 1957 | if new_val == "m" and self.type == BOOL: |
1951 | new_val = "y" | 1958 | new_val = "y" |
1952 | 1959 | ||
1953 | elif self.type == INT or self.type == HEX: | 1960 | elif self.type == INT or self.type == HEX: |
1954 | has_active_range = False | 1961 | has_active_range = False |
1955 | low = None | 1962 | low = None |
1956 | high = None | 1963 | high = None |
1957 | use_defaults = True | 1964 | use_defaults = True |
1958 | 1965 | ||
1959 | base = 16 if self.type == HEX else 10 | 1966 | base = 16 if self.type == HEX else 10 |
1960 | 1967 | ||
1961 | for l, h, cond_expr in self.ranges: | 1968 | for l, h, cond_expr in self.ranges: |
1962 | if self.config._eval_expr(cond_expr) != "n": | 1969 | if self.config._eval_expr(cond_expr) != "n": |
1963 | has_active_range = True | 1970 | has_active_range = True |
1964 | 1971 | ||
1965 | low_str = _str_val(l) | 1972 | low_str = _str_val(l) |
1966 | high_str = _str_val(h) | 1973 | high_str = _str_val(h) |
1967 | low = int(low_str, base) if \ | 1974 | low = int(low_str, base) if \ |
1968 | _is_base_n(low_str, base) else 0 | 1975 | _is_base_n(low_str, base) else 0 |
1969 | high = int(high_str, base) if \ | 1976 | high = int(high_str, base) if \ |
1970 | _is_base_n(high_str, base) else 0 | 1977 | _is_base_n(high_str, base) else 0 |
1971 | 1978 | ||
1972 | break | 1979 | break |
1973 | 1980 | ||
1974 | if vis != "n": | 1981 | if vis != "n": |
1975 | self.write_to_conf = True | 1982 | self.write_to_conf = True |
1976 | 1983 | ||
1977 | if self.user_val is not None and \ | 1984 | if self.user_val is not None and \ |
1978 | _is_base_n(self.user_val, base) and \ | 1985 | _is_base_n(self.user_val, base) and \ |
1979 | (not has_active_range or | 1986 | (not has_active_range or |
1980 | low <= int(self.user_val, base) <= high): | 1987 | low <= int(self.user_val, base) <= high): |
1981 | 1988 | ||
1982 | # If the user value is OK, it is stored in exactly the same | 1989 | # If the user value is OK, it is stored in exactly the same |
1983 | # form as specified in the assignment (with or without | 1990 | # form as specified in the assignment (with or without |
1984 | # "0x", etc). | 1991 | # "0x", etc). |
1985 | 1992 | ||
1986 | use_defaults = False | 1993 | use_defaults = False |
1987 | new_val = self.user_val | 1994 | new_val = self.user_val |
1988 | 1995 | ||
1989 | if use_defaults: | 1996 | if use_defaults: |
1990 | for val_expr, cond_expr in self.def_exprs: | 1997 | for val_expr, cond_expr in self.def_exprs: |
1991 | if self.config._eval_expr(cond_expr) != "n": | 1998 | if self.config._eval_expr(cond_expr) != "n": |
1992 | self.write_to_conf = True | 1999 | self.write_to_conf = True |
1993 | 2000 | ||
1994 | # If the default value is OK, it is stored in exactly | 2001 | # If the default value is OK, it is stored in exactly |
1995 | # the same form as specified. Otherwise, it is clamped | 2002 | # the same form as specified. Otherwise, it is clamped |
1996 | # to the range, and the output has "0x" as appropriate | 2003 | # to the range, and the output has "0x" as appropriate |
1997 | # for the type. | 2004 | # for the type. |
1998 | 2005 | ||
1999 | new_val = _str_val(val_expr) | 2006 | new_val = _str_val(val_expr) |
2000 | 2007 | ||
2001 | if _is_base_n(new_val, base): | 2008 | if _is_base_n(new_val, base): |
2002 | new_val_num = int(new_val, base) | 2009 | new_val_num = int(new_val, base) |
2003 | if has_active_range: | 2010 | if has_active_range: |
2004 | clamped_val = None | 2011 | clamped_val = None |
2005 | 2012 | ||
2006 | if new_val_num < low: | 2013 | if new_val_num < low: |
2007 | clamped_val = low | 2014 | clamped_val = low |
2008 | elif new_val_num > high: | 2015 | elif new_val_num > high: |
2009 | clamped_val = high | 2016 | clamped_val = high |
2010 | 2017 | ||
2011 | if clamped_val is not None: | 2018 | if clamped_val is not None: |
2012 | new_val = (hex(clamped_val) if \ | 2019 | new_val = (hex(clamped_val) if \ |
2013 | self.type == HEX else str(clamped_val)) | 2020 | self.type == HEX else str(clamped_val)) |
2014 | 2021 | ||
2015 | break | 2022 | break |
2016 | else: # For the for loop | 2023 | else: # For the for loop |
2017 | # If no user value or default kicks in but the hex/int has | 2024 | # If no user value or default kicks in but the hex/int has |
2018 | # an active range, then the low end of the range is used, | 2025 | # an active range, then the low end of the range is used, |
2019 | # provided it's > 0, with "0x" prepended as appropriate. | 2026 | # provided it's > 0, with "0x" prepended as appropriate. |
2020 | if has_active_range and low > 0: | 2027 | if has_active_range and low > 0: |
2021 | new_val = (hex(low) if self.type == HEX else str(low)) | 2028 | new_val = (hex(low) if self.type == HEX else str(low)) |
2022 | 2029 | ||
2023 | elif self.type == STRING: | 2030 | elif self.type == STRING: |
2024 | use_defaults = True | 2031 | use_defaults = True |
2025 | 2032 | ||
2026 | if vis != "n": | 2033 | if vis != "n": |
2027 | self.write_to_conf = True | 2034 | self.write_to_conf = True |
2028 | if self.user_val is not None: | 2035 | if self.user_val is not None: |
2029 | new_val = self.user_val | 2036 | new_val = self.user_val |
2030 | use_defaults = False | 2037 | use_defaults = False |
2031 | 2038 | ||
2032 | if use_defaults: | 2039 | if use_defaults: |
2033 | for val_expr, cond_expr in self.def_exprs: | 2040 | for val_expr, cond_expr in self.def_exprs: |
2034 | if self.config._eval_expr(cond_expr) != "n": | 2041 | if self.config._eval_expr(cond_expr) != "n": |
2035 | self.write_to_conf = True | 2042 | self.write_to_conf = True |
2036 | new_val = _str_val(val_expr) | 2043 | new_val = _str_val(val_expr) |
2037 | break | 2044 | break |
2038 | 2045 | ||
2039 | self.cached_val = new_val | 2046 | self.cached_val = new_val |
2040 | return new_val | 2047 | return new_val |
2041 | 2048 | ||
2042 | def get_user_value(self): | 2049 | def get_user_value(self): |
2043 | """Returns the value assigned to the symbol in a .config or via | 2050 | """Returns the value assigned to the symbol in a .config or via |
2044 | Symbol.set_user_value() (provided the value was valid for the type of | 2051 | Symbol.set_user_value() (provided the value was valid for the type of |
2045 | the symbol). Returns None in case of no user value.""" | 2052 | the symbol). Returns None in case of no user value.""" |
2046 | return self.user_val | 2053 | return self.user_val |
2047 | 2054 | ||
2048 | def get_upper_bound(self): | 2055 | def get_upper_bound(self): |
2049 | """For string/hex/int symbols and for bool and tristate symbols that | 2056 | """For string/hex/int symbols and for bool and tristate symbols that |
2050 | cannot be modified (see is_modifiable()), returns None. | 2057 | cannot be modified (see is_modifiable()), returns None. |
2051 | 2058 | ||
2052 | Otherwise, returns the highest value the symbol can be set to with | 2059 | Otherwise, returns the highest value the symbol can be set to with |
2053 | Symbol.set_user_value() (that will not be truncated): one of "m" or | 2060 | Symbol.set_user_value() (that will not be truncated): one of "m" or |
2054 | "y", arranged from lowest to highest. This corresponds to the highest | 2061 | "y", arranged from lowest to highest. This corresponds to the highest |
2055 | value the symbol could be given in e.g. the 'make menuconfig' | 2062 | value the symbol could be given in e.g. the 'make menuconfig' |
2056 | interface. | 2063 | interface. |
2057 | 2064 | ||
2058 | See also the tri_less*() and tri_greater*() functions, which could come | 2065 | See also the tri_less*() and tri_greater*() functions, which could come |
2059 | in handy.""" | 2066 | in handy.""" |
2060 | if self.type != BOOL and self.type != TRISTATE: | 2067 | if self.type != BOOL and self.type != TRISTATE: |
2061 | return None | 2068 | return None |
2062 | rev_dep = self.config._eval_expr(self.rev_dep) | 2069 | rev_dep = self.config._eval_expr(self.rev_dep) |
2063 | # A bool selected to "m" gets promoted to "y", pinning it | 2070 | # A bool selected to "m" gets promoted to "y", pinning it |
2064 | if rev_dep == "m" and self.type == BOOL: | 2071 | if rev_dep == "m" and self.type == BOOL: |
2065 | return None | 2072 | return None |
2066 | vis = _get_visibility(self) | 2073 | vis = _get_visibility(self) |
2067 | if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]: | 2074 | if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]: |
2068 | return vis | 2075 | return vis |
2069 | return None | 2076 | return None |
2070 | 2077 | ||
2071 | def get_lower_bound(self): | 2078 | def get_lower_bound(self): |
2072 | """For string/hex/int symbols and for bool and tristate symbols that | 2079 | """For string/hex/int symbols and for bool and tristate symbols that |
2073 | cannot be modified (see is_modifiable()), returns None. | 2080 | cannot be modified (see is_modifiable()), returns None. |
2074 | 2081 | ||
2075 | Otherwise, returns the lowest value the symbol can be set to with | 2082 | Otherwise, returns the lowest value the symbol can be set to with |
2076 | Symbol.set_user_value() (that will not be truncated): one of "n" or | 2083 | Symbol.set_user_value() (that will not be truncated): one of "n" or |
2077 | "m", arranged from lowest to highest. This corresponds to the lowest | 2084 | "m", arranged from lowest to highest. This corresponds to the lowest |
2078 | value the symbol could be given in e.g. the 'make menuconfig' | 2085 | value the symbol could be given in e.g. the 'make menuconfig' |
2079 | interface. | 2086 | interface. |
2080 | 2087 | ||
2081 | See also the tri_less*() and tri_greater*() functions, which could come | 2088 | See also the tri_less*() and tri_greater*() functions, which could come |
2082 | in handy.""" | 2089 | in handy.""" |
2083 | if self.type != BOOL and self.type != TRISTATE: | 2090 | if self.type != BOOL and self.type != TRISTATE: |
2084 | return None | 2091 | return None |
2085 | rev_dep = self.config._eval_expr(self.rev_dep) | 2092 | rev_dep = self.config._eval_expr(self.rev_dep) |
2086 | # A bool selected to "m" gets promoted to "y", pinning it | 2093 | # A bool selected to "m" gets promoted to "y", pinning it |
2087 | if rev_dep == "m" and self.type == BOOL: | 2094 | if rev_dep == "m" and self.type == BOOL: |
2088 | return None | 2095 | return None |
2089 | if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]: | 2096 | if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]: |
2090 | return rev_dep | 2097 | return rev_dep |
2091 | return None | 2098 | return None |
2092 | 2099 | ||
2093 | def get_assignable_values(self): | 2100 | def get_assignable_values(self): |
2094 | """For string/hex/int symbols and for bool and tristate symbols that | 2101 | """For string/hex/int symbols and for bool and tristate symbols that |
2095 | cannot be modified (see is_modifiable()), returns the empty list. | 2102 | cannot be modified (see is_modifiable()), returns the empty list. |
2096 | 2103 | ||
2097 | Otherwise, returns a list containing the user values that can be | 2104 | Otherwise, returns a list containing the user values that can be |
2098 | assigned to the symbol (that won't be truncated). Usage example: | 2105 | assigned to the symbol (that won't be truncated). Usage example: |
2099 | 2106 | ||
2100 | if "m" in sym.get_assignable_values(): | 2107 | if "m" in sym.get_assignable_values(): |
2101 | sym.set_user_value("m") | 2108 | sym.set_user_value("m") |
2102 | 2109 | ||
2103 | This is basically a more convenient interface to | 2110 | This is basically a more convenient interface to |
2104 | get_lower/upper_bound() when wanting to test if a particular tristate | 2111 | get_lower/upper_bound() when wanting to test if a particular tristate |
2105 | value can be assigned.""" | 2112 | value can be assigned.""" |
2106 | if self.type != BOOL and self.type != TRISTATE: | 2113 | if self.type != BOOL and self.type != TRISTATE: |
2107 | return [] | 2114 | return [] |
2108 | rev_dep = self.config._eval_expr(self.rev_dep) | 2115 | rev_dep = self.config._eval_expr(self.rev_dep) |
2109 | # A bool selected to "m" gets promoted to "y", pinning it | 2116 | # A bool selected to "m" gets promoted to "y", pinning it |
2110 | if rev_dep == "m" and self.type == BOOL: | 2117 | if rev_dep == "m" and self.type == BOOL: |
2111 | return [] | 2118 | return [] |
2112 | res = ["n", "m", "y"][TRI_TO_INT[rev_dep] : | 2119 | res = ["n", "m", "y"][TRI_TO_INT[rev_dep] : |
2113 | TRI_TO_INT[_get_visibility(self)] + 1] | 2120 | TRI_TO_INT[_get_visibility(self)] + 1] |
2114 | return res if len(res) > 1 else [] | 2121 | return res if len(res) > 1 else [] |
2115 | 2122 | ||
2116 | def get_visibility(self): | 2123 | def get_visibility(self): |
2117 | """Returns the visibility of the symbol: one of "n", "m" or "y". For | 2124 | """Returns the visibility of the symbol: one of "n", "m" or "y". For |
2118 | bool and tristate symbols, this is an upper bound on the value users | 2125 | bool and tristate symbols, this is an upper bound on the value users |
2119 | can set for the symbol. For other types of symbols, a visibility of "n" | 2126 | can set for the symbol. For other types of symbols, a visibility of "n" |
2120 | means the user value will be ignored. A visibility of "n" corresponds | 2127 | means the user value will be ignored. A visibility of "n" corresponds |
2121 | to not being visible in the 'make *config' interfaces. | 2128 | to not being visible in the 'make *config' interfaces. |
2122 | 2129 | ||
2123 | Example (assuming we're running with modules enabled -- i.e., MODULES | 2130 | Example (assuming we're running with modules enabled -- i.e., MODULES |
2124 | set to 'y'): | 2131 | set to 'y'): |
2125 | 2132 | ||
2126 | # Assume this has been assigned 'n' | 2133 | # Assume this has been assigned 'n' |
2127 | config N_SYM | 2134 | config N_SYM |
2128 | tristate "N_SYM" | 2135 | tristate "N_SYM" |
2129 | 2136 | ||
2130 | # Assume this has been assigned 'm' | 2137 | # Assume this has been assigned 'm' |
2131 | config M_SYM | 2138 | config M_SYM |
2132 | tristate "M_SYM" | 2139 | tristate "M_SYM" |
2133 | 2140 | ||
2134 | # Has visibility 'n' | 2141 | # Has visibility 'n' |
2135 | config A | 2142 | config A |
2136 | tristate "A" | 2143 | tristate "A" |
2137 | depends on N_SYM | 2144 | depends on N_SYM |
2138 | 2145 | ||
2139 | # Has visibility 'm' | 2146 | # Has visibility 'm' |
2140 | config B | 2147 | config B |
2141 | tristate "B" | 2148 | tristate "B" |
2142 | depends on M_SYM | 2149 | depends on M_SYM |
2143 | 2150 | ||
2144 | # Has visibility 'y' | 2151 | # Has visibility 'y' |
2145 | config C | 2152 | config C |
2146 | tristate "C" | 2153 | tristate "C" |
2147 | 2154 | ||
2148 | # Has no prompt, and hence visibility 'n' | 2155 | # Has no prompt, and hence visibility 'n' |
2149 | config D | 2156 | config D |
2150 | tristate | 2157 | tristate |
2151 | 2158 | ||
2152 | Having visibility be tri-valued ensures that e.g. a symbol cannot be | 2159 | Having visibility be tri-valued ensures that e.g. a symbol cannot be |
2153 | set to "y" by the user if it depends on a symbol with value "m", which | 2160 | set to "y" by the user if it depends on a symbol with value "m", which |
2154 | wouldn't be safe. | 2161 | wouldn't be safe. |
2155 | 2162 | ||
2156 | You should probably look at get_lower/upper_bound(), | 2163 | You should probably look at get_lower/upper_bound(), |
2157 | get_assignable_values() and is_modifiable() before using this.""" | 2164 | get_assignable_values() and is_modifiable() before using this.""" |
2158 | return _get_visibility(self) | 2165 | return _get_visibility(self) |
2159 | 2166 | ||
2160 | def get_referenced_symbols(self, refs_from_enclosing=False): | 2167 | def get_referenced_symbols(self, refs_from_enclosing=False): |
2161 | """Returns the set() of all symbols referenced by this symbol. For | 2168 | """Returns the set() of all symbols referenced by this symbol. For |
2162 | example, the symbol defined by | 2169 | example, the symbol defined by |
2163 | 2170 | ||
2164 | config FOO | 2171 | config FOO |
2165 | bool | 2172 | bool |
2166 | prompt "foo" if A && B | 2173 | prompt "foo" if A && B |
2167 | default C if D | 2174 | default C if D |
2168 | depends on E | 2175 | depends on E |
2169 | select F if G | 2176 | select F if G |
2170 | 2177 | ||
2171 | references the symbols A through G. | 2178 | references the symbols A through G. |
2172 | 2179 | ||
2173 | refs_from_enclosing (default: False): If True, the symbols referenced | 2180 | refs_from_enclosing (default: False): If True, the symbols referenced |
2174 | by enclosing menus and ifs will be included in the result.""" | 2181 | by enclosing menus and ifs will be included in the result.""" |
2175 | return self.all_referenced_syms if refs_from_enclosing else \ | 2182 | return self.all_referenced_syms if refs_from_enclosing else \ |
2176 | self.referenced_syms | 2183 | self.referenced_syms |
2177 | 2184 | ||
2178 | def get_selected_symbols(self): | 2185 | def get_selected_symbols(self): |
2179 | """Returns the set() of all symbols X for which this symbol has a | 2186 | """Returns the set() of all symbols X for which this symbol has a |
2180 | 'select X' or 'select X if Y' (regardless of whether Y is satisfied or | 2187 | 'select X' or 'select X if Y' (regardless of whether Y is satisfied or |
2181 | not). This is a subset of the symbols returned by | 2188 | not). This is a subset of the symbols returned by |
2182 | get_referenced_symbols().""" | 2189 | get_referenced_symbols().""" |
2183 | return self.selected_syms | 2190 | return self.selected_syms |
2184 | 2191 | ||
2185 | def set_user_value(self, v): | 2192 | def set_user_value(self, v): |
2186 | """Sets the user value of the symbol. | 2193 | """Sets the user value of the symbol. |
2187 | 2194 | ||
2188 | Equal in effect to assigning the value to the symbol within a .config | 2195 | Equal in effect to assigning the value to the symbol within a .config |
2189 | file. Use get_lower/upper_bound() or get_assignable_values() to find | 2196 | file. Use get_lower/upper_bound() or get_assignable_values() to find |
2190 | the range of currently assignable values for bool and tristate symbols; | 2197 | the range of currently assignable values for bool and tristate symbols; |
2191 | setting values outside this range will cause the user value to differ | 2198 | setting values outside this range will cause the user value to differ |
2192 | from the result of Symbol.get_value() (be truncated). Values that are | 2199 | from the result of Symbol.get_value() (be truncated). Values that are |
2193 | invalid for the type (such as a_bool.set_user_value("foo")) are | 2200 | invalid for the type (such as a_bool.set_user_value("foo")) are |
2194 | ignored, and a warning is emitted if an attempt is made to assign such | 2201 | ignored, and a warning is emitted if an attempt is made to assign such |
2195 | a value. | 2202 | a value. |
2196 | 2203 | ||
2197 | For any type of symbol, is_modifiable() can be used to check if a user | 2204 | For any type of symbol, is_modifiable() can be used to check if a user |
2198 | value will currently have any effect on the symbol, as determined by | 2205 | value will currently have any effect on the symbol, as determined by |
2199 | its visibility and range of assignable values. Any value that is valid | 2206 | its visibility and range of assignable values. Any value that is valid |
2200 | for the type (bool, tristate, etc.) will end up being reflected in | 2207 | for the type (bool, tristate, etc.) will end up being reflected in |
2201 | get_user_value() though, and might have an effect later if conditions | 2208 | get_user_value() though, and might have an effect later if conditions |
2202 | change. To get rid of the user value, use unset_user_value(). | 2209 | change. To get rid of the user value, use unset_user_value(). |
2203 | 2210 | ||
2204 | Any symbols dependent on the symbol are (recursively) invalidated, so | 2211 | Any symbols dependent on the symbol are (recursively) invalidated, so |
2205 | things will just work with regards to dependencies. | 2212 | things will just work with regards to dependencies. |
2206 | 2213 | ||
2207 | v: The user value to give to the symbol.""" | 2214 | v: The user value to give to the symbol.""" |
2208 | self._set_user_value_no_invalidate(v, False) | 2215 | self._set_user_value_no_invalidate(v, False) |
2209 | 2216 | ||
2210 | # There might be something more efficient you could do here, but play | 2217 | # There might be something more efficient you could do here, but play |
2211 | # it safe. | 2218 | # it safe. |
2212 | if self.name == "MODULES": | 2219 | if self.name == "MODULES": |
2213 | self.config._invalidate_all() | 2220 | self.config._invalidate_all() |
2214 | return | 2221 | return |
2215 | 2222 | ||
2216 | self._invalidate() | 2223 | self._invalidate() |
2217 | self._invalidate_dependent() | 2224 | self._invalidate_dependent() |
2218 | 2225 | ||
2219 | def unset_user_value(self): | 2226 | def unset_user_value(self): |
2220 | """Resets the user value of the symbol, as if the symbol had never | 2227 | """Resets the user value of the symbol, as if the symbol had never |
2221 | gotten a user value via Config.load_config() or | 2228 | gotten a user value via Config.load_config() or |
2222 | Symbol.set_user_value().""" | 2229 | Symbol.set_user_value().""" |
2223 | self._unset_user_value_no_recursive_invalidate() | 2230 | self._unset_user_value_no_recursive_invalidate() |
2224 | self._invalidate_dependent() | 2231 | self._invalidate_dependent() |
2225 | 2232 | ||
2226 | def is_modifiable(self): | 2233 | def is_modifiable(self): |
2227 | """Returns True if the value of the symbol could be modified by calling | 2234 | """Returns True if the value of the symbol could be modified by calling |
2228 | Symbol.set_user_value(). | 2235 | Symbol.set_user_value(). |
2229 | 2236 | ||
2230 | For bools and tristates, this corresponds to the symbol being visible | 2237 | For bools and tristates, this corresponds to the symbol being visible |
2231 | in the 'make menuconfig' interface and not already being pinned to a | 2238 | in the 'make menuconfig' interface and not already being pinned to a |
2232 | specific value (e.g. because it is selected by another symbol). | 2239 | specific value (e.g. because it is selected by another symbol). |
2233 | 2240 | ||
2234 | For strings and numbers, this corresponds to just being visible. (See | 2241 | For strings and numbers, this corresponds to just being visible. (See |
2235 | Symbol.get_visibility().)""" | 2242 | Symbol.get_visibility().)""" |
2236 | if self.is_special_: | 2243 | if self.is_special_: |
2237 | return False | 2244 | return False |
2238 | if self.type == BOOL or self.type == TRISTATE: | 2245 | if self.type == BOOL or self.type == TRISTATE: |
2239 | rev_dep = self.config._eval_expr(self.rev_dep) | 2246 | rev_dep = self.config._eval_expr(self.rev_dep) |
2240 | # A bool selected to "m" gets promoted to "y", pinning it | 2247 | # A bool selected to "m" gets promoted to "y", pinning it |
2241 | if rev_dep == "m" and self.type == BOOL: | 2248 | if rev_dep == "m" and self.type == BOOL: |
2242 | return False | 2249 | return False |
2243 | return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep] | 2250 | return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep] |
2244 | return _get_visibility(self) != "n" | 2251 | return _get_visibility(self) != "n" |
2245 | 2252 | ||
2246 | def is_defined(self): | 2253 | def is_defined(self): |
2247 | """Returns False if the symbol is referred to in the Kconfig but never | 2254 | """Returns False if the symbol is referred to in the Kconfig but never |
2248 | actually defined.""" | 2255 | actually defined.""" |
2249 | return self.is_defined_ | 2256 | return self.is_defined_ |
2250 | 2257 | ||
2251 | def is_special(self): | 2258 | def is_special(self): |
2252 | """Returns True if the symbol is one of the special symbols n, m, y, or | 2259 | """Returns True if the symbol is one of the special symbols n, m, y, or |
2253 | UNAME_RELEASE, or gets its value from the environment.""" | 2260 | UNAME_RELEASE, or gets its value from the environment.""" |
2254 | return self.is_special_ | 2261 | return self.is_special_ |
2255 | 2262 | ||
2256 | def is_from_environment(self): | 2263 | def is_from_environment(self): |
2257 | """Returns True if the symbol gets its value from the environment.""" | 2264 | """Returns True if the symbol gets its value from the environment.""" |
2258 | return self.is_from_env | 2265 | return self.is_from_env |
2259 | 2266 | ||
2260 | def has_ranges(self): | 2267 | def has_ranges(self): |
2261 | """Returns True if the symbol is of type INT or HEX and has ranges that | 2268 | """Returns True if the symbol is of type INT or HEX and has ranges that |
2262 | limit what values it can take on.""" | 2269 | limit what values it can take on.""" |
2263 | return bool(self.ranges) | 2270 | return bool(self.ranges) |
2264 | 2271 | ||
2265 | def is_choice_symbol(self): | 2272 | def is_choice_symbol(self): |
2266 | """Returns True if the symbol is in a choice statement and is an actual | 2273 | """Returns True if the symbol is in a choice statement and is an actual |
2267 | choice symbol (see Choice.get_symbols()).""" | 2274 | choice symbol (see Choice.get_symbols()).""" |
2268 | return self.is_choice_sym | 2275 | return self.is_choice_sym |
2269 | 2276 | ||
2270 | def is_choice_selection(self): | 2277 | def is_choice_selection(self): |
2271 | """Returns True if the symbol is contained in a choice statement and is | 2278 | """Returns True if the symbol is contained in a choice statement and is |
2272 | the selected item. Equivalent to | 2279 | the selected item. Equivalent to |
2273 | 2280 | ||
2274 | sym.is_choice_symbol() and sym.get_parent().get_selection() is sym""" | 2281 | sym.is_choice_symbol() and sym.get_parent().get_selection() is sym""" |
2275 | return self.is_choice_sym and self.parent.get_selection() is self | 2282 | return self.is_choice_sym and self.parent.get_selection() is self |
2276 | 2283 | ||
2277 | def is_allnoconfig_y(self): | 2284 | def is_allnoconfig_y(self): |
2278 | """Returns True if the symbol has the 'allnoconfig_y' option set.""" | 2285 | """Returns True if the symbol has the 'allnoconfig_y' option set.""" |
2279 | return self.allnoconfig_y | 2286 | return self.allnoconfig_y |
2280 | 2287 | ||
2281 | def __str__(self): | 2288 | def __str__(self): |
2282 | """Returns a string containing various information about the symbol.""" | 2289 | """Returns a string containing various information about the symbol.""" |
2283 | return self.config._get_sym_or_choice_str(self) | 2290 | return self.config._get_sym_or_choice_str(self) |
2284 | 2291 | ||
2285 | # | 2292 | # |
2286 | # Private methods | 2293 | # Private methods |
2287 | # | 2294 | # |
2288 | 2295 | ||
2289 | def __init__(self): | 2296 | def __init__(self): |
2290 | """Symbol constructor -- not intended to be called directly by | 2297 | """Symbol constructor -- not intended to be called directly by |
2291 | Kconfiglib clients.""" | 2298 | Kconfiglib clients.""" |
2292 | 2299 | ||
2293 | self.name = None | 2300 | self.name = None |
2294 | self.type = UNKNOWN | 2301 | self.type = UNKNOWN |
2295 | self.prompts = [] | 2302 | self.prompts = [] |
2296 | self.def_exprs = [] # 'default' properties | 2303 | self.def_exprs = [] # 'default' properties |
2297 | self.ranges = [] # 'range' properties (for int and hex) | 2304 | self.ranges = [] # 'range' properties (for int and hex) |
2298 | self.help = None # Help text | 2305 | self.help = None # Help text |
2299 | self.rev_dep = "n" # Reverse (select-related) dependencies | 2306 | self.rev_dep = "n" # Reverse (select-related) dependencies |
2300 | self.config = None | 2307 | self.config = None |
2301 | self.parent = None | 2308 | self.parent = None |
2302 | 2309 | ||
2303 | self.user_val = None # Value set by user | 2310 | self.user_val = None # Value set by user |
2304 | 2311 | ||
2305 | # The prompt, default value and select conditions without any | 2312 | # The prompt, default value and select conditions without any |
2306 | # dependencies from menus and ifs propagated to them | 2313 | # dependencies from menus and ifs propagated to them |
2307 | self.orig_prompts = [] | 2314 | self.orig_prompts = [] |
2308 | self.orig_def_exprs = [] | 2315 | self.orig_def_exprs = [] |
2309 | self.orig_selects = [] | 2316 | self.orig_selects = [] |
2310 | 2317 | ||
2311 | # Dependencies inherited from containing menus and ifs | 2318 | # Dependencies inherited from containing menus and ifs |
2312 | self.deps_from_containing = None | 2319 | self.deps_from_containing = None |
2313 | # The set of symbols referenced by this symbol (see | 2320 | # The set of symbols referenced by this symbol (see |
2314 | # get_referenced_symbols()) | 2321 | # get_referenced_symbols()) |
2315 | self.referenced_syms = set() | 2322 | self.referenced_syms = set() |
2316 | # The set of symbols selected by this symbol (see | 2323 | # The set of symbols selected by this symbol (see |
2317 | # get_selected_symbols()) | 2324 | # get_selected_symbols()) |
2318 | self.selected_syms = set() | 2325 | self.selected_syms = set() |
2319 | # Like 'referenced_syms', but includes symbols from | 2326 | # Like 'referenced_syms', but includes symbols from |
2320 | # dependencies inherited from enclosing menus and ifs | 2327 | # dependencies inherited from enclosing menus and ifs |
2321 | self.all_referenced_syms = set() | 2328 | self.all_referenced_syms = set() |
2322 | 2329 | ||
2323 | # This records only dependencies specified with 'depends on'. Needed | 2330 | # This records only dependencies specified with 'depends on'. Needed |
2324 | # when determining actual choice items (hrrrr...). See also | 2331 | # when determining actual choice items (hrrrr...). See also |
2325 | # Choice._determine_actual_symbols(). | 2332 | # Choice._determine_actual_symbols(). |
2326 | self.menu_dep = None | 2333 | self.menu_dep = None |
2327 | 2334 | ||
2328 | # See Symbol.get_ref/def_locations(). | 2335 | # See Symbol.get_ref/def_locations(). |
2329 | self.def_locations = [] | 2336 | self.def_locations = [] |
2330 | self.ref_locations = [] | 2337 | self.ref_locations = [] |
2331 | 2338 | ||
2332 | # Populated in Config._build_dep() after parsing. Links the symbol to | 2339 | # Populated in Config._build_dep() after parsing. Links the symbol to |
2333 | # the symbols that immediately depend on it (in a caching/invalidation | 2340 | # the symbols that immediately depend on it (in a caching/invalidation |
2334 | # sense). The total set of dependent symbols for the symbol (the | 2341 | # sense). The total set of dependent symbols for the symbol (the |
2335 | # transitive closure) is calculated on an as-needed basis in | 2342 | # transitive closure) is calculated on an as-needed basis in |
2336 | # _get_dependent(). | 2343 | # _get_dependent(). |
2337 | self.dep = set() | 2344 | self.dep = set() |
2338 | 2345 | ||
2339 | # Cached values | 2346 | # Cached values |
2340 | 2347 | ||
2341 | # Caches the calculated value | 2348 | # Caches the calculated value |
2342 | self.cached_val = None | 2349 | self.cached_val = None |
2343 | # Caches the visibility, which acts as an upper bound on the value | 2350 | # Caches the visibility, which acts as an upper bound on the value |
2344 | self.cached_visibility = None | 2351 | self.cached_visibility = None |
2345 | # Caches the total list of dependent symbols. Calculated in | 2352 | # Caches the total list of dependent symbols. Calculated in |
2346 | # _get_dependent(). | 2353 | # _get_dependent(). |
2347 | self.cached_deps = None | 2354 | self.cached_deps = None |
2348 | 2355 | ||
2349 | # Flags | 2356 | # Flags |
2350 | 2357 | ||
2351 | # Does the symbol have an entry in the Kconfig file? The trailing | 2358 | # Does the symbol have an entry in the Kconfig file? The trailing |
2352 | # underscore avoids a collision with is_defined(). | 2359 | # underscore avoids a collision with is_defined(). |
2353 | self.is_defined_ = False | 2360 | self.is_defined_ = False |
2354 | # Should the symbol get an entry in .config? | 2361 | # Should the symbol get an entry in .config? |
2355 | self.write_to_conf = False | 2362 | self.write_to_conf = False |
2356 | # Set to true when _make_conf() is called on a symbol, so that symbols | 2363 | # Set to true when _make_conf() is called on a symbol, so that symbols |
2357 | # defined in multiple locations only get one .config entry. We need to | 2364 | # defined in multiple locations only get one .config entry. We need to |
2358 | # reset it prior to writing out a new .config. | 2365 | # reset it prior to writing out a new .config. |
2359 | self.already_written = False | 2366 | self.already_written = False |
2360 | # This is set to True for "actual" choice symbols; see | 2367 | # This is set to True for "actual" choice symbols; see |
2361 | # Choice._determine_actual_symbols(). | 2368 | # Choice._determine_actual_symbols(). |
2362 | self.is_choice_sym = False | 2369 | self.is_choice_sym = False |
2363 | # Does the symbol get its value in some special way, e.g. from the | 2370 | # Does the symbol get its value in some special way, e.g. from the |
2364 | # environment or by being one of the special symbols n, m, and y? If | 2371 | # environment or by being one of the special symbols n, m, and y? If |
2365 | # so, the value is stored in self.cached_val, which is never | 2372 | # so, the value is stored in self.cached_val, which is never |
2366 | # invalidated. The trailing underscore avoids a collision with | 2373 | # invalidated. The trailing underscore avoids a collision with |
2367 | # is_special(). | 2374 | # is_special(). |
2368 | self.is_special_ = False | 2375 | self.is_special_ = False |
2369 | # Does the symbol get its value from the environment? | 2376 | # Does the symbol get its value from the environment? |
2370 | self.is_from_env = False | 2377 | self.is_from_env = False |
2371 | # Does the symbol have the 'allnoconfig_y' option set? | 2378 | # Does the symbol have the 'allnoconfig_y' option set? |
2372 | self.allnoconfig_y = False | 2379 | self.allnoconfig_y = False |
2373 | 2380 | ||
2374 | def _invalidate(self): | 2381 | def _invalidate(self): |
2375 | if self.is_special_: | 2382 | if self.is_special_: |
2376 | return | 2383 | return |
2377 | 2384 | ||
2378 | if self.is_choice_sym: | 2385 | if self.is_choice_sym: |
2379 | self.parent._invalidate() | 2386 | self.parent._invalidate() |
2380 | 2387 | ||
2381 | self.cached_val = None | 2388 | self.cached_val = None |
2382 | self.cached_visibility = None | 2389 | self.cached_visibility = None |
2383 | 2390 | ||
2384 | def _invalidate_dependent(self): | 2391 | def _invalidate_dependent(self): |
2385 | for sym in self._get_dependent(): | 2392 | for sym in self._get_dependent(): |
2386 | sym._invalidate() | 2393 | sym._invalidate() |
2387 | 2394 | ||
2388 | def _set_user_value_no_invalidate(self, v, suppress_load_warnings): | 2395 | def _set_user_value_no_invalidate(self, v, suppress_load_warnings): |
2389 | """Like set_user_value(), but does not invalidate any symbols. | 2396 | """Like set_user_value(), but does not invalidate any symbols. |
2390 | 2397 | ||
2391 | suppress_load_warnings: some warnings are annoying when loading a | 2398 | suppress_load_warnings: some warnings are annoying when loading a |
2392 | .config that can be helpful when manually invoking set_user_value(). | 2399 | .config that can be helpful when manually invoking set_user_value(). |
2393 | This flag is set to True to suppress such warnings. | 2400 | This flag is set to True to suppress such warnings. |
2394 | 2401 | ||
2395 | Perhaps this could be made optional for load_config() instead.""" | 2402 | Perhaps this could be made optional for load_config() instead.""" |
2396 | 2403 | ||
2397 | if self.is_special_: | 2404 | if self.is_special_: |
2398 | if self.is_from_env: | 2405 | if self.is_from_env: |
2399 | self.config._warn('attempt to assign the value "{0}" to the ' | 2406 | self.config._warn('attempt to assign the value "{0}" to the ' |
2400 | 'symbol {1}, which gets its value from the ' | 2407 | 'symbol {1}, which gets its value from the ' |
2401 | 'environment. Assignment ignored.' | 2408 | 'environment. Assignment ignored.' |
2402 | .format(v, self.name)) | 2409 | .format(v, self.name)) |
2403 | else: | 2410 | else: |
2404 | self.config._warn('attempt to assign the value "{0}" to the ' | 2411 | self.config._warn('attempt to assign the value "{0}" to the ' |
2405 | 'special symbol {1}. Assignment ignored.' | 2412 | 'special symbol {1}. Assignment ignored.' |
2406 | .format(v, self.name)) | 2413 | .format(v, self.name)) |
2407 | return | 2414 | return |
2408 | 2415 | ||
2409 | if not self.is_defined_: | 2416 | if not self.is_defined_: |
2410 | filename, linenr = self.ref_locations[0] | 2417 | filename, linenr = self.ref_locations[0] |
2411 | if self.config.print_undef_assign: | 2418 | if self.config.print_undef_assign: |
2412 | _stderr_msg('note: attempt to assign the value "{0}" to {1}, ' | 2419 | _stderr_msg('note: attempt to assign the value "{0}" to {1}, ' |
2413 | "which is referenced at {2}:{3} but never " | 2420 | "which is referenced at {2}:{3} but never " |
2414 | "defined. Assignment ignored." | 2421 | "defined. Assignment ignored." |
2415 | .format(v, self.name, filename, linenr)) | 2422 | .format(v, self.name, filename, linenr)) |
2416 | return | 2423 | return |
2417 | 2424 | ||
2418 | # Check if the value is valid for our type | 2425 | # Check if the value is valid for our type |
2419 | if not ((self.type == BOOL and (v == "y" or v == "n") ) or | 2426 | if not ((self.type == BOOL and (v == "y" or v == "n") ) or |
2420 | (self.type == TRISTATE and (v == "y" or v == "m" or | 2427 | (self.type == TRISTATE and (v == "y" or v == "m" or |
2421 | v == "n") ) or | 2428 | v == "n") ) or |
2422 | (self.type == STRING ) or | 2429 | (self.type == STRING ) or |
2423 | (self.type == INT and _is_base_n(v, 10) ) or | 2430 | (self.type == INT and _is_base_n(v, 10) ) or |
2424 | (self.type == HEX and _is_base_n(v, 16) )): | 2431 | (self.type == HEX and _is_base_n(v, 16) )): |
2425 | self.config._warn('the value "{0}" is invalid for {1}, which has ' | 2432 | self.config._warn('the value "{0}" is invalid for {1}, which has ' |
2426 | "type {2}. Assignment ignored." | 2433 | "type {2}. Assignment ignored." |
2427 | .format(v, self.name, TYPENAME[self.type])) | 2434 | .format(v, self.name, TYPENAME[self.type])) |
2428 | return | 2435 | return |
2429 | 2436 | ||
2430 | if not self.prompts and not suppress_load_warnings: | 2437 | if not self.prompts and not suppress_load_warnings: |
2431 | self.config._warn('assigning "{0}" to the symbol {1} which ' | 2438 | self.config._warn('assigning "{0}" to the symbol {1} which ' |
2432 | 'lacks prompts and thus has visibility "n". ' | 2439 | 'lacks prompts and thus has visibility "n". ' |
2433 | 'The assignment will have no effect.' | 2440 | 'The assignment will have no effect.' |
2434 | .format(v, self.name)) | 2441 | .format(v, self.name)) |
2435 | 2442 | ||
2436 | self.user_val = v | 2443 | self.user_val = v |
2437 | 2444 | ||
2438 | if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE): | 2445 | if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE): |
2439 | choice = self.parent | 2446 | choice = self.parent |
2440 | if v == "y": | 2447 | if v == "y": |
2441 | choice.user_val = self | 2448 | choice.user_val = self |
2442 | choice.user_mode = "y" | 2449 | choice.user_mode = "y" |
2443 | elif v == "m": | 2450 | elif v == "m": |
2444 | choice.user_val = None | 2451 | choice.user_val = None |
2445 | choice.user_mode = "m" | 2452 | choice.user_mode = "m" |
2446 | 2453 | ||
2447 | def _unset_user_value_no_recursive_invalidate(self): | 2454 | def _unset_user_value_no_recursive_invalidate(self): |
2448 | self._invalidate() | 2455 | self._invalidate() |
2449 | self.user_val = None | 2456 | self.user_val = None |
2450 | 2457 | ||
2451 | if self.is_choice_sym: | 2458 | if self.is_choice_sym: |
2452 | self.parent._unset_user_value() | 2459 | self.parent._unset_user_value() |
2453 | 2460 | ||
2454 | def _make_conf(self, append_fn): | 2461 | def _make_conf(self, append_fn): |
2455 | if self.already_written: | 2462 | if self.already_written: |
2456 | return | 2463 | return |
2457 | 2464 | ||
2458 | self.already_written = True | 2465 | self.already_written = True |
2459 | 2466 | ||
2460 | # Note: write_to_conf is determined in get_value() | 2467 | # Note: write_to_conf is determined in get_value() |
2461 | val = self.get_value() | 2468 | val = self.get_value() |
2462 | if not self.write_to_conf: | 2469 | if not self.write_to_conf: |
2463 | return | 2470 | return |
2464 | 2471 | ||
2465 | if self.type == BOOL or self.type == TRISTATE: | 2472 | if self.type == BOOL or self.type == TRISTATE: |
2466 | if val == "y" or val == "m": | 2473 | if val == "y" or val == "m": |
2467 | append_fn("CONFIG_{0}={1}".format(self.name, val)) | 2474 | append_fn("CONFIG_{0}={1}".format(self.name, val)) |
2468 | else: | 2475 | else: |
2469 | append_fn("# CONFIG_{0} is not set".format(self.name)) | 2476 | append_fn("# CONFIG_{0} is not set".format(self.name)) |
2470 | 2477 | ||
2471 | elif self.type == INT or self.type == HEX: | 2478 | elif self.type == INT or self.type == HEX: |
2472 | append_fn("CONFIG_{0}={1}".format(self.name, val)) | 2479 | append_fn("CONFIG_{0}={1}".format(self.name, val)) |
2473 | 2480 | ||
2474 | elif self.type == STRING: | 2481 | elif self.type == STRING: |
2475 | # Escape \ and " | 2482 | # Escape \ and " |
2476 | append_fn('CONFIG_{0}="{1}"' | 2483 | append_fn('CONFIG_{0}="{1}"' |
2477 | .format(self.name, | 2484 | .format(self.name, |
2478 | val.replace("\\", "\\\\").replace('"', '\\"'))) | 2485 | val.replace("\\", "\\\\").replace('"', '\\"'))) |
2479 | 2486 | ||
2480 | else: | 2487 | else: |
2481 | _internal_error("Internal error while creating .config: unknown " | 2488 | _internal_error("Internal error while creating .config: unknown " |
2482 | 'type "{0}".'.format(self.type)) | 2489 | 'type "{0}".'.format(self.type)) |
2483 | 2490 | ||
2484 | def _get_dependent(self): | 2491 | def _get_dependent(self): |
2485 | """Returns the set of symbols that should be invalidated if the value | 2492 | """Returns the set of symbols that should be invalidated if the value |
2486 | of the symbol changes, because they might be affected by the change. | 2493 | of the symbol changes, because they might be affected by the change. |
2487 | Note that this is an internal API -- it's probably of limited | 2494 | Note that this is an internal API -- it's probably of limited |
2488 | usefulness to clients.""" | 2495 | usefulness to clients.""" |
2489 | if self.cached_deps is not None: | 2496 | if self.cached_deps is not None: |
2490 | return self.cached_deps | 2497 | return self.cached_deps |
2491 | 2498 | ||
2492 | res = set(self.dep) | 2499 | res = set(self.dep) |
2493 | for s in self.dep: | 2500 | for s in self.dep: |
2494 | res |= s._get_dependent() | 2501 | res |= s._get_dependent() |
2495 | 2502 | ||
2496 | if self.is_choice_sym: | 2503 | if self.is_choice_sym: |
2497 | # Choice symbols also depend (recursively) on their siblings. The | 2504 | # Choice symbols also depend (recursively) on their siblings. The |
2498 | # siblings are not included in 'dep' to avoid dependency loops. | 2505 | # siblings are not included in 'dep' to avoid dependency loops. |
2499 | for sibling in self.parent.actual_symbols: | 2506 | for sibling in self.parent.actual_symbols: |
2500 | if sibling is not self: | 2507 | if sibling is not self: |
2501 | res.add(sibling) | 2508 | res.add(sibling) |
2502 | res |= sibling.dep | 2509 | res |= sibling.dep |
2503 | for s in sibling.dep: | 2510 | for s in sibling.dep: |
2504 | res |= s._get_dependent() | 2511 | res |= s._get_dependent() |
2505 | 2512 | ||
2506 | self.cached_deps = res | 2513 | self.cached_deps = res |
2507 | return res | 2514 | return res |
2508 | 2515 | ||
2509 | def _has_auto_menu_dep_on(self, on): | 2516 | def _has_auto_menu_dep_on(self, on): |
2510 | """See Choice._determine_actual_symbols().""" | 2517 | """See Choice._determine_actual_symbols().""" |
2511 | if not isinstance(self.parent, Choice): | 2518 | if not isinstance(self.parent, Choice): |
2512 | _internal_error("Attempt to determine auto menu dependency for " | 2519 | _internal_error("Attempt to determine auto menu dependency for " |
2513 | "symbol ouside of choice.") | 2520 | "symbol ouside of choice.") |
2514 | 2521 | ||
2515 | if not self.prompts: | 2522 | if not self.prompts: |
2516 | # If we have no prompt, use the menu dependencies instead (what was | 2523 | # If we have no prompt, use the menu dependencies instead (what was |
2517 | # specified with 'depends on') | 2524 | # specified with 'depends on') |
2518 | return self.menu_dep is not None and \ | 2525 | return self.menu_dep is not None and \ |
2519 | self.config._expr_depends_on(self.menu_dep, on) | 2526 | self.config._expr_depends_on(self.menu_dep, on) |
2520 | 2527 | ||
2521 | for _, cond_expr in self.prompts: | 2528 | for _, cond_expr in self.prompts: |
2522 | if self.config._expr_depends_on(cond_expr, on): | 2529 | if self.config._expr_depends_on(cond_expr, on): |
2523 | return True | 2530 | return True |
2524 | 2531 | ||
2525 | return False | 2532 | return False |
2526 | 2533 | ||
2527 | class Menu(Item): | 2534 | class Menu(Item): |
2528 | 2535 | ||
2529 | """Represents a menu statement.""" | 2536 | """Represents a menu statement.""" |
2530 | 2537 | ||
2531 | # | 2538 | # |
2532 | # Public interface | 2539 | # Public interface |
2533 | # | 2540 | # |
2534 | 2541 | ||
2535 | def get_config(self): | 2542 | def get_config(self): |
2536 | """Return the Config instance this menu is from.""" | 2543 | """Return the Config instance this menu is from.""" |
2537 | return self.config | 2544 | return self.config |
2538 | 2545 | ||
2539 | def get_title(self): | 2546 | def get_title(self): |
2540 | """Returns the title text of the menu.""" | 2547 | """Returns the title text of the menu.""" |
2541 | return self.title | 2548 | return self.title |
2542 | 2549 | ||
2543 | def get_parent(self): | 2550 | def get_parent(self): |
2544 | """Returns the menu or choice statement that contains the menu, or | 2551 | """Returns the menu or choice statement that contains the menu, or |
2545 | None if the menu is at the top level. Note that if statements are | 2552 | None if the menu is at the top level. Note that if statements are |
2546 | treated as syntactic sugar and do not have an explicit class | 2553 | treated as syntactic sugar and do not have an explicit class |
2547 | representation.""" | 2554 | representation.""" |
2548 | return self.parent | 2555 | return self.parent |
2549 | 2556 | ||
2550 | def get_location(self): | 2557 | def get_location(self): |
2551 | """Returns the location of the menu as a (filename, linenr) tuple, | 2558 | """Returns the location of the menu as a (filename, linenr) tuple, |
2552 | where filename is a string and linenr an int.""" | 2559 | where filename is a string and linenr an int.""" |
2553 | return (self.filename, self.linenr) | 2560 | return (self.filename, self.linenr) |
2554 | 2561 | ||
2555 | def get_items(self, recursive=False): | 2562 | def get_items(self, recursive=False): |
2556 | """Returns a list containing the items (symbols, menus, choice | 2563 | """Returns a list containing the items (symbols, menus, choice |
2557 | statements and comments) in in the menu, in the same order that the | 2564 | statements and comments) in in the menu, in the same order that the |
2558 | items appear within the menu. | 2565 | items appear within the menu. |
2559 | 2566 | ||
2560 | recursive (default: False): True if items contained in items within the | 2567 | recursive (default: False): True if items contained in items within the |
2561 | menu should be included recursively (preorder).""" | 2568 | menu should be included recursively (preorder).""" |
2562 | 2569 | ||
2563 | if not recursive: | 2570 | if not recursive: |
2564 | return self.block | 2571 | return self.block |
2565 | 2572 | ||
2566 | res = [] | 2573 | res = [] |
2567 | for item in self.block: | 2574 | for item in self.block: |
2568 | res.append(item) | 2575 | res.append(item) |
2569 | if isinstance(item, Menu): | 2576 | if isinstance(item, Menu): |
2570 | res.extend(item.get_items(True)) | 2577 | res.extend(item.get_items(True)) |
2571 | elif isinstance(item, Choice): | 2578 | elif isinstance(item, Choice): |
2572 | res.extend(item.get_items()) | 2579 | res.extend(item.get_items()) |
2573 | return res | 2580 | return res |
2574 | 2581 | ||
2575 | def get_symbols(self, recursive=False): | 2582 | def get_symbols(self, recursive=False): |
2576 | """Returns a list containing the symbols in the menu, in the same order | 2583 | """Returns a list containing the symbols in the menu, in the same order |
2577 | that they appear within the menu. | 2584 | that they appear within the menu. |
2578 | 2585 | ||
2579 | recursive (default: False): True if symbols contained in items within | 2586 | recursive (default: False): True if symbols contained in items within |
2580 | the menu should be included recursively.""" | 2587 | the menu should be included recursively.""" |
2581 | 2588 | ||
2582 | return [item for item in self.get_items(recursive) if | 2589 | return [item for item in self.get_items(recursive) if |
2583 | isinstance(item, Symbol)] | 2590 | isinstance(item, Symbol)] |
2584 | 2591 | ||
2585 | def get_visibility(self): | 2592 | def get_visibility(self): |
2586 | """Returns the visibility of the menu. This also affects the visibility | 2593 | """Returns the visibility of the menu. This also affects the visibility |
2587 | of subitems. See also Symbol.get_visibility().""" | 2594 | of subitems. See also Symbol.get_visibility().""" |
2588 | return self.config._eval_expr(self.dep_expr) | 2595 | return self.config._eval_expr(self.dep_expr) |
2589 | 2596 | ||
2590 | def get_visible_if_visibility(self): | 2597 | def get_visible_if_visibility(self): |
2591 | """Returns the visibility the menu gets from its 'visible if' | 2598 | """Returns the visibility the menu gets from its 'visible if' |
2592 | condition. "y" if the menu has no 'visible if' condition.""" | 2599 | condition. "y" if the menu has no 'visible if' condition.""" |
2593 | return self.config._eval_expr(self.visible_if_expr) | 2600 | return self.config._eval_expr(self.visible_if_expr) |
2594 | 2601 | ||
2595 | def get_referenced_symbols(self, refs_from_enclosing=False): | 2602 | def get_referenced_symbols(self, refs_from_enclosing=False): |
2596 | """See Symbol.get_referenced_symbols().""" | 2603 | """See Symbol.get_referenced_symbols().""" |
2597 | return self.all_referenced_syms if refs_from_enclosing else \ | 2604 | return self.all_referenced_syms if refs_from_enclosing else \ |
2598 | self.referenced_syms | 2605 | self.referenced_syms |
2599 | 2606 | ||
2600 | def __str__(self): | 2607 | def __str__(self): |
2601 | """Returns a string containing various information about the menu.""" | 2608 | """Returns a string containing various information about the menu.""" |
2602 | depends_on_str = self.config._expr_val_str(self.orig_deps, | 2609 | depends_on_str = self.config._expr_val_str(self.orig_deps, |
2603 | "(no dependencies)") | 2610 | "(no dependencies)") |
2604 | visible_if_str = self.config._expr_val_str(self.visible_if_expr, | 2611 | visible_if_str = self.config._expr_val_str(self.visible_if_expr, |
2605 | "(no dependencies)") | 2612 | "(no dependencies)") |
2606 | 2613 | ||
2607 | additional_deps_str = " " + \ | 2614 | additional_deps_str = " " + \ |
2608 | self.config._expr_val_str(self.deps_from_containing, | 2615 | self.config._expr_val_str(self.deps_from_containing, |
2609 | "(no additional dependencies)") | 2616 | "(no additional dependencies)") |
2610 | 2617 | ||
2611 | return _lines("Menu", | 2618 | return _lines("Menu", |
2612 | "Title : " + self.title, | 2619 | "Title : " + self.title, |
2613 | "'depends on' dependencies : " + depends_on_str, | 2620 | "'depends on' dependencies : " + depends_on_str, |
2614 | "'visible if' dependencies : " + visible_if_str, | 2621 | "'visible if' dependencies : " + visible_if_str, |
2615 | "Additional dependencies from enclosing menus and " | 2622 | "Additional dependencies from enclosing menus and " |
2616 | "ifs:", | 2623 | "ifs:", |
2617 | additional_deps_str, | 2624 | additional_deps_str, |
2618 | "Location: {0}:{1}".format(self.filename, self.linenr)) | 2625 | "Location: {0}:{1}".format(self.filename, self.linenr)) |
2619 | 2626 | ||
2620 | # | 2627 | # |
2621 | # Private methods | 2628 | # Private methods |
2622 | # | 2629 | # |
2623 | 2630 | ||
2624 | def __init__(self): | 2631 | def __init__(self): |
2625 | """Menu constructor -- not intended to be called directly by | 2632 | """Menu constructor -- not intended to be called directly by |
2626 | Kconfiglib clients.""" | 2633 | Kconfiglib clients.""" |
2627 | 2634 | ||
2628 | self.title = None | 2635 | self.title = None |
2629 | self.dep_expr = None | 2636 | self.dep_expr = None |
2630 | self.visible_if_expr = None | 2637 | self.visible_if_expr = None |
2631 | self.block = None | 2638 | self.block = None |
2632 | self.config = None | 2639 | self.config = None |
2633 | self.parent = None | 2640 | self.parent = None |
2634 | 2641 | ||
2635 | # Dependency expression without dependencies from enclosing menus and | 2642 | # Dependency expression without dependencies from enclosing menus and |
2636 | # ifs propagated | 2643 | # ifs propagated |
2637 | self.orig_deps = None | 2644 | self.orig_deps = None |
2638 | 2645 | ||
2639 | # Dependencies inherited from containing menus and ifs | 2646 | # Dependencies inherited from containing menus and ifs |
2640 | self.deps_from_containing = None | 2647 | self.deps_from_containing = None |
2641 | # The set of symbols referenced by this menu (see | 2648 | # The set of symbols referenced by this menu (see |
2642 | # get_referenced_symbols()) | 2649 | # get_referenced_symbols()) |
2643 | self.referenced_syms = set() | 2650 | self.referenced_syms = set() |
2644 | # Like 'referenced_syms', but includes symbols from | 2651 | # Like 'referenced_syms', but includes symbols from |
2645 | # dependencies inherited from enclosing menus and ifs | 2652 | # dependencies inherited from enclosing menus and ifs |
2646 | self.all_referenced_syms = None | 2653 | self.all_referenced_syms = None |
2647 | 2654 | ||
2648 | self.filename = None | 2655 | self.filename = None |
2649 | self.linenr = None | 2656 | self.linenr = None |
2650 | 2657 | ||
2651 | def _make_conf(self, append_fn): | 2658 | def _make_conf(self, append_fn): |
2652 | if self.config._eval_expr(self.dep_expr) != "n" and \ | 2659 | if self.config._eval_expr(self.dep_expr) != "n" and \ |
2653 | self.config._eval_expr(self.visible_if_expr) != "n": | 2660 | self.config._eval_expr(self.visible_if_expr) != "n": |
2654 | append_fn("\n#\n# {0}\n#".format(self.title)) | 2661 | append_fn("\n#\n# {0}\n#".format(self.title)) |
2655 | _make_block_conf(self.block, append_fn) | 2662 | _make_block_conf(self.block, append_fn) |
2656 | 2663 | ||
2657 | class Choice(Item): | 2664 | class Choice(Item): |
2658 | 2665 | ||
2659 | """Represents a choice statement. A choice can be in one of three modes: | 2666 | """Represents a choice statement. A choice can be in one of three modes: |
2660 | 2667 | ||
2661 | "n" - The choice is not visible and no symbols can be selected. | 2668 | "n" - The choice is not visible and no symbols can be selected. |
2662 | 2669 | ||
2663 | "m" - Any number of symbols can be set to "m". The rest will be "n". This | 2670 | "m" - Any number of symbols can be set to "m". The rest will be "n". This |
2664 | is safe since potentially conflicting options don't actually get | 2671 | is safe since potentially conflicting options don't actually get |
2665 | compiled into the kernel simultaneously with "m". | 2672 | compiled into the kernel simultaneously with "m". |
2666 | 2673 | ||
2667 | "y" - One symbol will be "y" while the rest are "n". | 2674 | "y" - One symbol will be "y" while the rest are "n". |
2668 | 2675 | ||
2669 | Only tristate choices can be in "m" mode, and the visibility of the choice | 2676 | Only tristate choices can be in "m" mode, and the visibility of the choice |
2670 | is an upper bound on the mode, so that e.g. a choice that depends on a | 2677 | is an upper bound on the mode, so that e.g. a choice that depends on a |
2671 | symbol with value "m" will be in "m" mode. | 2678 | symbol with value "m" will be in "m" mode. |
2672 | 2679 | ||
2673 | The mode changes automatically when a value is assigned to a symbol within | 2680 | The mode changes automatically when a value is assigned to a symbol within |
2674 | the choice. | 2681 | the choice. |
2675 | 2682 | ||
2676 | See Symbol.get_visibility() too.""" | 2683 | See Symbol.get_visibility() too.""" |
2677 | 2684 | ||
2678 | # | 2685 | # |
2679 | # Public interface | 2686 | # Public interface |
2680 | # | 2687 | # |
2681 | 2688 | ||
2682 | def get_config(self): | 2689 | def get_config(self): |
2683 | """Returns the Config instance this choice is from.""" | 2690 | """Returns the Config instance this choice is from.""" |
2684 | return self.config | 2691 | return self.config |
2685 | 2692 | ||
2686 | def get_name(self): | 2693 | def get_name(self): |
2687 | """For named choices, returns the name. Returns None for unnamed | 2694 | """For named choices, returns the name. Returns None for unnamed |
2688 | choices. No named choices appear anywhere in the kernel Kconfig files | 2695 | choices. No named choices appear anywhere in the kernel Kconfig files |
2689 | as of Linux 3.7.0-rc8.""" | 2696 | as of Linux 3.7.0-rc8.""" |
2690 | return self.name | 2697 | return self.name |
2691 | 2698 | ||
2692 | def get_type(self): | 2699 | def get_type(self): |
2693 | """Returns the type of the choice. See Symbol.get_type().""" | 2700 | """Returns the type of the choice. See Symbol.get_type().""" |
2694 | return self.type | 2701 | return self.type |
2695 | 2702 | ||
2696 | def get_prompts(self): | 2703 | def get_prompts(self): |
2697 | """Returns a list of prompts defined for the choice, in the order they | 2704 | """Returns a list of prompts defined for the choice, in the order they |
2698 | appear in the configuration files. Returns the empty list for choices | 2705 | appear in the configuration files. Returns the empty list for choices |
2699 | with no prompt. | 2706 | with no prompt. |
2700 | 2707 | ||
2701 | This list will have a single entry for the vast majority of choices | 2708 | This list will have a single entry for the vast majority of choices |
2702 | having prompts, but having multiple prompts for a single choice is | 2709 | having prompts, but having multiple prompts for a single choice is |
2703 | possible through having multiple 'choice' entries for it (though I'm | 2710 | possible through having multiple 'choice' entries for it (though I'm |
2704 | not sure if that ever happens in practice).""" | 2711 | not sure if that ever happens in practice).""" |
2705 | return [prompt for prompt, _ in self.orig_prompts] | 2712 | return [prompt for prompt, _ in self.orig_prompts] |
2706 | 2713 | ||
2707 | def get_help(self): | 2714 | def get_help(self): |
2708 | """Returns the help text of the choice, or None if the choice has no | 2715 | """Returns the help text of the choice, or None if the choice has no |
2709 | help text.""" | 2716 | help text.""" |
2710 | return self.help | 2717 | return self.help |
2711 | 2718 | ||
2712 | def get_parent(self): | 2719 | def get_parent(self): |
2713 | """Returns the menu or choice statement that contains the choice, or | 2720 | """Returns the menu or choice statement that contains the choice, or |
2714 | None if the choice is at the top level. Note that if statements are | 2721 | None if the choice is at the top level. Note that if statements are |
2715 | treated as syntactic sugar and do not have an explicit class | 2722 | treated as syntactic sugar and do not have an explicit class |
2716 | representation.""" | 2723 | representation.""" |
2717 | return self.parent | 2724 | return self.parent |
2718 | 2725 | ||
2719 | def get_def_locations(self): | 2726 | def get_def_locations(self): |
2720 | """Returns a list of (filename, linenr) tuples, where filename (string) | 2727 | """Returns a list of (filename, linenr) tuples, where filename (string) |
2721 | and linenr (int) represent a location where the choice is defined. For | 2728 | and linenr (int) represent a location where the choice is defined. For |
2722 | the vast majority of choices (all of them as of Linux 3.7.0-rc8) this | 2729 | the vast majority of choices (all of them as of Linux 3.7.0-rc8) this |
2723 | list will only contain one element, but its possible for named choices | 2730 | list will only contain one element, but its possible for named choices |
2724 | to be defined in multiple locations.""" | 2731 | to be defined in multiple locations.""" |
2725 | return self.def_locations | 2732 | return self.def_locations |
2726 | 2733 | ||
2727 | def get_selection(self): | 2734 | def get_selection(self): |
2728 | """Returns the symbol selected (either by the user or through | 2735 | """Returns the symbol selected (either by the user or through |
2729 | defaults), or None if either no symbol is selected or the mode is not | 2736 | defaults), or None if either no symbol is selected or the mode is not |
2730 | "y".""" | 2737 | "y".""" |
2731 | if self.cached_selection is not None: | 2738 | if self.cached_selection is not None: |
2732 | if self.cached_selection == NO_SELECTION: | 2739 | if self.cached_selection == NO_SELECTION: |
2733 | return None | 2740 | return None |
2734 | return self.cached_selection | 2741 | return self.cached_selection |
2735 | 2742 | ||
2736 | if self.get_mode() != "y": | 2743 | if self.get_mode() != "y": |
2737 | return self._cache_ret(None) | 2744 | return self._cache_ret(None) |
2738 | 2745 | ||
2739 | # User choice available? | 2746 | # User choice available? |
2740 | if self.user_val is not None and _get_visibility(self.user_val) == "y": | 2747 | if self.user_val is not None and _get_visibility(self.user_val) == "y": |
2741 | return self._cache_ret(self.user_val) | 2748 | return self._cache_ret(self.user_val) |
2742 | 2749 | ||
2743 | if self.optional: | 2750 | if self.optional: |
2744 | return self._cache_ret(None) | 2751 | return self._cache_ret(None) |
2745 | 2752 | ||
2746 | return self._cache_ret(self.get_selection_from_defaults()) | 2753 | return self._cache_ret(self.get_selection_from_defaults()) |
2747 | 2754 | ||
2748 | def get_selection_from_defaults(self): | 2755 | def get_selection_from_defaults(self): |
2749 | """Like Choice.get_selection(), but acts as if no symbol has been | 2756 | """Like Choice.get_selection(), but acts as if no symbol has been |
2750 | selected by the user and no 'optional' flag is in effect.""" | 2757 | selected by the user and no 'optional' flag is in effect.""" |
2751 | 2758 | ||
2752 | if not self.actual_symbols: | 2759 | if not self.actual_symbols: |
2753 | return None | 2760 | return None |
2754 | 2761 | ||
2755 | for symbol, cond_expr in self.def_exprs: | 2762 | for symbol, cond_expr in self.def_exprs: |
2756 | if self.config._eval_expr(cond_expr) != "n": | 2763 | if self.config._eval_expr(cond_expr) != "n": |
2757 | chosen_symbol = symbol | 2764 | chosen_symbol = symbol |
2758 | break | 2765 | break |
2759 | else: | 2766 | else: |
2760 | chosen_symbol = self.actual_symbols[0] | 2767 | chosen_symbol = self.actual_symbols[0] |
2761 | 2768 | ||
2762 | # Is the chosen symbol visible? | 2769 | # Is the chosen symbol visible? |
2763 | if _get_visibility(chosen_symbol) != "n": | 2770 | if _get_visibility(chosen_symbol) != "n": |
2764 | return chosen_symbol | 2771 | return chosen_symbol |
2765 | # Otherwise, pick the first visible symbol | 2772 | # Otherwise, pick the first visible symbol |
2766 | for sym in self.actual_symbols: | 2773 | for sym in self.actual_symbols: |
2767 | if _get_visibility(sym) != "n": | 2774 | if _get_visibility(sym) != "n": |
2768 | return sym | 2775 | return sym |
2769 | return None | 2776 | return None |
2770 | 2777 | ||
2771 | def get_user_selection(self): | 2778 | def get_user_selection(self): |
2772 | """If the choice is in "y" mode and has a user-selected symbol, returns | 2779 | """If the choice is in "y" mode and has a user-selected symbol, returns |
2773 | that symbol. Otherwise, returns None.""" | 2780 | that symbol. Otherwise, returns None.""" |
2774 | return self.user_val | 2781 | return self.user_val |
2775 | 2782 | ||
2776 | def get_items(self): | 2783 | def get_items(self): |
2777 | """Gets all items contained in the choice in the same order as within | 2784 | """Gets all items contained in the choice in the same order as within |
2778 | the configuration ("items" instead of "symbols" since choices and | 2785 | the configuration ("items" instead of "symbols" since choices and |
2779 | comments might appear within choices. This only happens in one place as | 2786 | comments might appear within choices. This only happens in one place as |
2780 | of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig).""" | 2787 | of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig).""" |
2781 | return self.block | 2788 | return self.block |
2782 | 2789 | ||
2783 | def get_symbols(self): | 2790 | def get_symbols(self): |
2784 | """Returns a list containing the choice's symbols. | 2791 | """Returns a list containing the choice's symbols. |
2785 | 2792 | ||
2786 | A quirk (perhaps a bug) of Kconfig is that you can put items within a | 2793 | A quirk (perhaps a bug) of Kconfig is that you can put items within a |
2787 | choice that will not be considered members of the choice insofar as | 2794 | choice that will not be considered members of the choice insofar as |
2788 | selection is concerned. This happens for example if one symbol within a | 2795 | selection is concerned. This happens for example if one symbol within a |
2789 | choice 'depends on' the symbol preceding it, or if you put non-symbol | 2796 | choice 'depends on' the symbol preceding it, or if you put non-symbol |
2790 | items within choices. | 2797 | items within choices. |
2791 | 2798 | ||
2792 | As of Linux 3.7.0-rc8, this seems to be used intentionally in one | 2799 | As of Linux 3.7.0-rc8, this seems to be used intentionally in one |
2793 | place: drivers/usb/gadget/Kconfig. | 2800 | place: drivers/usb/gadget/Kconfig. |
2794 | 2801 | ||
2795 | This function returns the "proper" symbols of the choice in the order | 2802 | This function returns the "proper" symbols of the choice in the order |
2796 | they appear in the choice, excluding such items. If you want all items | 2803 | they appear in the choice, excluding such items. If you want all items |
2797 | in the choice, use get_items().""" | 2804 | in the choice, use get_items().""" |
2798 | return self.actual_symbols | 2805 | return self.actual_symbols |
2799 | 2806 | ||
2800 | def get_referenced_symbols(self, refs_from_enclosing=False): | 2807 | def get_referenced_symbols(self, refs_from_enclosing=False): |
2801 | """See Symbol.get_referenced_symbols().""" | 2808 | """See Symbol.get_referenced_symbols().""" |
2802 | return self.all_referenced_syms if refs_from_enclosing else \ | 2809 | return self.all_referenced_syms if refs_from_enclosing else \ |
2803 | self.referenced_syms | 2810 | self.referenced_syms |
2804 | 2811 | ||
2805 | def get_visibility(self): | 2812 | def get_visibility(self): |
2806 | """Returns the visibility of the choice statement: one of "n", "m" or | 2813 | """Returns the visibility of the choice statement: one of "n", "m" or |
2807 | "y". This acts as an upper limit on the mode of the choice (though bool | 2814 | "y". This acts as an upper limit on the mode of the choice (though bool |
2808 | choices can only have the mode "y"). See the class documentation for an | 2815 | choices can only have the mode "y"). See the class documentation for an |
2809 | explanation of modes.""" | 2816 | explanation of modes.""" |
2810 | return _get_visibility(self) | 2817 | return _get_visibility(self) |
2811 | 2818 | ||
2812 | def get_mode(self): | 2819 | def get_mode(self): |
2813 | """Returns the mode of the choice. See the class documentation for | 2820 | """Returns the mode of the choice. See the class documentation for |
2814 | an explanation of modes.""" | 2821 | an explanation of modes.""" |
2815 | minimum_mode = "n" if self.optional else "m" | 2822 | minimum_mode = "n" if self.optional else "m" |
2816 | mode = self.user_mode if self.user_mode is not None else minimum_mode | 2823 | mode = self.user_mode if self.user_mode is not None else minimum_mode |
2817 | mode = self.config._eval_min(mode, _get_visibility(self)) | 2824 | mode = self.config._eval_min(mode, _get_visibility(self)) |
2818 | 2825 | ||
2819 | # Promote "m" to "y" for boolean choices | 2826 | # Promote "m" to "y" for boolean choices |
2820 | if mode == "m" and self.type == BOOL: | 2827 | if mode == "m" and self.type == BOOL: |
2821 | return "y" | 2828 | return "y" |
2822 | 2829 | ||
2823 | return mode | 2830 | return mode |
2824 | 2831 | ||
2825 | def is_optional(self): | 2832 | def is_optional(self): |
2826 | """Returns True if the choice has the 'optional' flag set (and so will | 2833 | """Returns True if the choice has the 'optional' flag set (and so will |
2827 | default to "n" mode).""" | 2834 | default to "n" mode).""" |
2828 | return self.optional | 2835 | return self.optional |
2829 | 2836 | ||
2830 | def __str__(self): | 2837 | def __str__(self): |
2831 | """Returns a string containing various information about the choice | 2838 | """Returns a string containing various information about the choice |
2832 | statement.""" | 2839 | statement.""" |
2833 | return self.config._get_sym_or_choice_str(self) | 2840 | return self.config._get_sym_or_choice_str(self) |
2834 | 2841 | ||
2835 | # | 2842 | # |
2836 | # Private methods | 2843 | # Private methods |
2837 | # | 2844 | # |
2838 | 2845 | ||
2839 | def __init__(self): | 2846 | def __init__(self): |
2840 | """Choice constructor -- not intended to be called directly by | 2847 | """Choice constructor -- not intended to be called directly by |
2841 | Kconfiglib clients.""" | 2848 | Kconfiglib clients.""" |
2842 | 2849 | ||
2843 | self.name = None # Yes, choices can be named | 2850 | self.name = None # Yes, choices can be named |
2844 | self.type = UNKNOWN | 2851 | self.type = UNKNOWN |
2845 | self.prompts = [] | 2852 | self.prompts = [] |
2846 | self.def_exprs = [] # 'default' properties | 2853 | self.def_exprs = [] # 'default' properties |
2847 | self.help = None # Help text | 2854 | self.help = None # Help text |
2848 | self.block = None # List of contained items | 2855 | self.block = None # List of contained items |
2849 | self.config = None | 2856 | self.config = None |
2850 | self.parent = None | 2857 | self.parent = None |
2851 | 2858 | ||
2852 | self.user_val = None | 2859 | self.user_val = None |
2853 | self.user_mode = None | 2860 | self.user_mode = None |
2854 | 2861 | ||
2855 | # We need to filter out symbols that appear within the choice block but | 2862 | # We need to filter out symbols that appear within the choice block but |
2856 | # are not considered choice items (see | 2863 | # are not considered choice items (see |
2857 | # Choice._determine_actual_symbols()) This list holds the "actual" | 2864 | # Choice._determine_actual_symbols()) This list holds the "actual" |
2858 | # choice items. | 2865 | # choice items. |
2859 | self.actual_symbols = [] | 2866 | self.actual_symbols = [] |
2860 | 2867 | ||
2861 | # The prompts and default values without any dependencies from | 2868 | # The prompts and default values without any dependencies from |
2862 | # enclosing menus and ifs propagated | 2869 | # enclosing menus and ifs propagated |
2863 | self.orig_prompts = [] | 2870 | self.orig_prompts = [] |
2864 | self.orig_def_exprs = [] | 2871 | self.orig_def_exprs = [] |
2865 | 2872 | ||
2866 | # Dependencies inherited from containing menus and ifs | 2873 | # Dependencies inherited from containing menus and ifs |
2867 | self.deps_from_containing = None | 2874 | self.deps_from_containing = None |
2868 | # The set of symbols referenced by this choice (see | 2875 | # The set of symbols referenced by this choice (see |
2869 | # get_referenced_symbols()) | 2876 | # get_referenced_symbols()) |
2870 | self.referenced_syms = set() | 2877 | self.referenced_syms = set() |
2871 | # Like 'referenced_syms', but includes symbols from | 2878 | # Like 'referenced_syms', but includes symbols from |
2872 | # dependencies inherited from enclosing menus and ifs | 2879 | # dependencies inherited from enclosing menus and ifs |
2873 | self.all_referenced_syms = set() | 2880 | self.all_referenced_syms = set() |
2874 | 2881 | ||
2875 | # See Choice.get_def_locations() | 2882 | # See Choice.get_def_locations() |
2876 | self.def_locations = [] | 2883 | self.def_locations = [] |
2877 | 2884 | ||
2878 | # Cached values | 2885 | # Cached values |
2879 | self.cached_selection = None | 2886 | self.cached_selection = None |
2880 | self.cached_visibility = None | 2887 | self.cached_visibility = None |
2881 | 2888 | ||
2882 | self.optional = False | 2889 | self.optional = False |
2883 | 2890 | ||
2884 | def _determine_actual_symbols(self): | 2891 | def _determine_actual_symbols(self): |
2885 | """If a symbol's visibility depends on the preceding symbol within a | 2892 | """If a symbol's visibility depends on the preceding symbol within a |
2886 | choice, it is no longer viewed as a choice item. (This is quite | 2893 | choice, it is no longer viewed as a choice item. (This is quite |
2887 | possibly a bug, but some things consciously use it... ugh. It stems | 2894 | possibly a bug, but some things consciously use it... ugh. It stems |
2888 | from automatic submenu creation.) In addition, it's possible to have | 2895 | from automatic submenu creation.) In addition, it's possible to have |
2889 | choices and comments within choices, and those shouldn't be considered | 2896 | choices and comments within choices, and those shouldn't be considered |
2890 | choice items either. Only drivers/usb/gadget/Kconfig seems to depend on | 2897 | choice items either. Only drivers/usb/gadget/Kconfig seems to depend on |
2891 | any of this. This method computes the "actual" items in the choice and | 2898 | any of this. This method computes the "actual" items in the choice and |
2892 | sets the is_choice_sym flag on them (retrieved via is_choice_symbol()). | 2899 | sets the is_choice_sym flag on them (retrieved via is_choice_symbol()). |
2893 | 2900 | ||
2894 | Don't let this scare you: an earlier version simply checked for a | 2901 | Don't let this scare you: an earlier version simply checked for a |
2895 | sequence of symbols where all symbols after the first appeared in the | 2902 | sequence of symbols where all symbols after the first appeared in the |
2896 | 'depends on' expression of the first, and that worked fine. The added | 2903 | 'depends on' expression of the first, and that worked fine. The added |
2897 | complexity is to be future-proof in the event that | 2904 | complexity is to be future-proof in the event that |
2898 | drivers/usb/gadget/Kconfig turns even more sinister. It might very well | 2905 | drivers/usb/gadget/Kconfig turns even more sinister. It might very well |
2899 | be overkilling things (especially if that file is refactored ;).""" | 2906 | be overkilling things (especially if that file is refactored ;).""" |
2900 | 2907 | ||
2901 | # Items might depend on each other in a tree structure, so we need a | 2908 | # Items might depend on each other in a tree structure, so we need a |
2902 | # stack to keep track of the current tentative parent | 2909 | # stack to keep track of the current tentative parent |
2903 | stack = [] | 2910 | stack = [] |
2904 | 2911 | ||
2905 | for item in self.block: | 2912 | for item in self.block: |
2906 | if not isinstance(item, Symbol): | 2913 | if not isinstance(item, Symbol): |
2907 | stack = [] | 2914 | stack = [] |
2908 | continue | 2915 | continue |
2909 | 2916 | ||
2910 | while stack: | 2917 | while stack: |
2911 | if item._has_auto_menu_dep_on(stack[-1]): | 2918 | if item._has_auto_menu_dep_on(stack[-1]): |
2912 | # The item should not be viewed as a choice item, so don't | 2919 | # The item should not be viewed as a choice item, so don't |
2913 | # set item.is_choice_sym | 2920 | # set item.is_choice_sym |
2914 | stack.append(item) | 2921 | stack.append(item) |
2915 | break | 2922 | break |
2916 | else: | 2923 | else: |
2917 | stack.pop() | 2924 | stack.pop() |
2918 | else: | 2925 | else: |
2919 | item.is_choice_sym = True | 2926 | item.is_choice_sym = True |
2920 | self.actual_symbols.append(item) | 2927 | self.actual_symbols.append(item) |
2921 | stack.append(item) | 2928 | stack.append(item) |
2922 | 2929 | ||
2923 | def _cache_ret(self, selection): | 2930 | def _cache_ret(self, selection): |
2924 | # As None is used to indicate the lack of a cached value we can't use | 2931 | # As None is used to indicate the lack of a cached value we can't use |
2925 | # that to cache the fact that the choice has no selection. Instead, we | 2932 | # that to cache the fact that the choice has no selection. Instead, we |
2926 | # use the symbolic constant NO_SELECTION. | 2933 | # use the symbolic constant NO_SELECTION. |
2927 | if selection is None: | 2934 | if selection is None: |
2928 | self.cached_selection = NO_SELECTION | 2935 | self.cached_selection = NO_SELECTION |
2929 | else: | 2936 | else: |
2930 | self.cached_selection = selection | 2937 | self.cached_selection = selection |
2931 | 2938 | ||
2932 | return selection | 2939 | return selection |
2933 | 2940 | ||
2934 | def _invalidate(self): | 2941 | def _invalidate(self): |
2935 | self.cached_selection = None | 2942 | self.cached_selection = None |
2936 | self.cached_visibility = None | 2943 | self.cached_visibility = None |
2937 | 2944 | ||
2938 | def _unset_user_value(self): | 2945 | def _unset_user_value(self): |
2939 | self._invalidate() | 2946 | self._invalidate() |
2940 | self.user_val = None | 2947 | self.user_val = None |
2941 | self.user_mode = None | 2948 | self.user_mode = None |
2942 | 2949 | ||
2943 | def _make_conf(self, append_fn): | 2950 | def _make_conf(self, append_fn): |
2944 | _make_block_conf(self.block, append_fn) | 2951 | _make_block_conf(self.block, append_fn) |
2945 | 2952 | ||
2946 | class Comment(Item): | 2953 | class Comment(Item): |
2947 | 2954 | ||
2948 | """Represents a comment statement.""" | 2955 | """Represents a comment statement.""" |
2949 | 2956 | ||
2950 | # | 2957 | # |
2951 | # Public interface | 2958 | # Public interface |
2952 | # | 2959 | # |
2953 | 2960 | ||
2954 | def get_config(self): | 2961 | def get_config(self): |
2955 | """Returns the Config instance this comment is from.""" | 2962 | """Returns the Config instance this comment is from.""" |
2956 | return self.config | 2963 | return self.config |
2957 | 2964 | ||
2958 | def get_text(self): | 2965 | def get_text(self): |
2959 | """Returns the text of the comment.""" | 2966 | """Returns the text of the comment.""" |
2960 | return self.text | 2967 | return self.text |
2961 | 2968 | ||
2962 | def get_parent(self): | 2969 | def get_parent(self): |
2963 | """Returns the menu or choice statement that contains the comment, or | 2970 | """Returns the menu or choice statement that contains the comment, or |
2964 | None if the comment is at the top level. Note that if statements are | 2971 | None if the comment is at the top level. Note that if statements are |
2965 | treated as syntactic sugar and do not have an explicit class | 2972 | treated as syntactic sugar and do not have an explicit class |
2966 | representation.""" | 2973 | representation.""" |
2967 | return self.parent | 2974 | return self.parent |
2968 | 2975 | ||
2969 | def get_location(self): | 2976 | def get_location(self): |
2970 | """Returns the location of the comment as a (filename, linenr) tuple, | 2977 | """Returns the location of the comment as a (filename, linenr) tuple, |
2971 | where filename is a string and linenr an int.""" | 2978 | where filename is a string and linenr an int.""" |
2972 | return (self.filename, self.linenr) | 2979 | return (self.filename, self.linenr) |
2973 | 2980 | ||
2974 | def get_visibility(self): | 2981 | def get_visibility(self): |
2975 | """Returns the visibility of the comment. See also | 2982 | """Returns the visibility of the comment. See also |
2976 | Symbol.get_visibility().""" | 2983 | Symbol.get_visibility().""" |
2977 | return self.config._eval_expr(self.dep_expr) | 2984 | return self.config._eval_expr(self.dep_expr) |
2978 | 2985 | ||
2979 | def get_referenced_symbols(self, refs_from_enclosing=False): | 2986 | def get_referenced_symbols(self, refs_from_enclosing=False): |
2980 | """See Symbol.get_referenced_symbols().""" | 2987 | """See Symbol.get_referenced_symbols().""" |
2981 | return self.all_referenced_syms if refs_from_enclosing else \ | 2988 | return self.all_referenced_syms if refs_from_enclosing else \ |
2982 | self.referenced_syms | 2989 | self.referenced_syms |
2983 | 2990 | ||
2984 | def __str__(self): | 2991 | def __str__(self): |
2985 | """Returns a string containing various information about the | 2992 | """Returns a string containing various information about the |
2986 | comment.""" | 2993 | comment.""" |
2987 | dep_str = self.config._expr_val_str(self.orig_deps, | 2994 | dep_str = self.config._expr_val_str(self.orig_deps, |
2988 | "(no dependencies)") | 2995 | "(no dependencies)") |
2989 | 2996 | ||
2990 | additional_deps_str = " " + \ | 2997 | additional_deps_str = " " + \ |
2991 | self.config._expr_val_str(self.deps_from_containing, | 2998 | self.config._expr_val_str(self.deps_from_containing, |
2992 | "(no additional dependencies)") | 2999 | "(no additional dependencies)") |
2993 | 3000 | ||
2994 | return _lines("Comment", | 3001 | return _lines("Comment", |
2995 | "Text: " + str(self.text), | 3002 | "Text: " + str(self.text), |
2996 | "Dependencies: " + dep_str, | 3003 | "Dependencies: " + dep_str, |
2997 | "Additional dependencies from enclosing menus and " | 3004 | "Additional dependencies from enclosing menus and " |
2998 | "ifs:", | 3005 | "ifs:", |
2999 | additional_deps_str, | 3006 | additional_deps_str, |
3000 | "Location: {0}:{1}".format(self.filename, self.linenr)) | 3007 | "Location: {0}:{1}".format(self.filename, self.linenr)) |
3001 | 3008 | ||
3002 | # | 3009 | # |
3003 | # Private methods | 3010 | # Private methods |
3004 | # | 3011 | # |
3005 | 3012 | ||
3006 | def __init__(self): | 3013 | def __init__(self): |
3007 | """Comment constructor -- not intended to be called directly by | 3014 | """Comment constructor -- not intended to be called directly by |
3008 | Kconfiglib clients.""" | 3015 | Kconfiglib clients.""" |
3009 | 3016 | ||
3010 | self.text = None | 3017 | self.text = None |
3011 | self.dep_expr = None | 3018 | self.dep_expr = None |
3012 | self.config = None | 3019 | self.config = None |
3013 | self.parent = None | 3020 | self.parent = None |
3014 | 3021 | ||
3015 | # Dependency expression without dependencies from enclosing menus and | 3022 | # Dependency expression without dependencies from enclosing menus and |
3016 | # ifs propagated | 3023 | # ifs propagated |
3017 | self.orig_deps = None | 3024 | self.orig_deps = None |
3018 | 3025 | ||
3019 | # Dependencies inherited from containing menus and ifs | 3026 | # Dependencies inherited from containing menus and ifs |
3020 | self.deps_from_containing = None | 3027 | self.deps_from_containing = None |
3021 | # The set of symbols referenced by this comment (see | 3028 | # The set of symbols referenced by this comment (see |
3022 | # get_referenced_symbols()) | 3029 | # get_referenced_symbols()) |
3023 | self.referenced_syms = set() | 3030 | self.referenced_syms = set() |
3024 | # Like 'referenced_syms', but includes symbols from | 3031 | # Like 'referenced_syms', but includes symbols from |
3025 | # dependencies inherited from enclosing menus and ifs | 3032 | # dependencies inherited from enclosing menus and ifs |
3026 | self.all_referenced_syms = None | 3033 | self.all_referenced_syms = None |
3027 | 3034 | ||
3028 | self.filename = None | 3035 | self.filename = None |
3029 | self.linenr = None | 3036 | self.linenr = None |
3030 | 3037 | ||
3031 | def _make_conf(self, append_fn): | 3038 | def _make_conf(self, append_fn): |
3032 | if self.config._eval_expr(self.dep_expr) != "n": | 3039 | if self.config._eval_expr(self.dep_expr) != "n": |
3033 | append_fn("\n#\n# {0}\n#".format(self.text)) | 3040 | append_fn("\n#\n# {0}\n#".format(self.text)) |
3034 | 3041 | ||
3035 | class Kconfig_Syntax_Error(Exception): | 3042 | class Kconfig_Syntax_Error(Exception): |
3036 | """Exception raised for syntax errors.""" | 3043 | """Exception raised for syntax errors.""" |
3037 | pass | 3044 | pass |
3038 | 3045 | ||
3039 | class Internal_Error(Exception): | 3046 | class Internal_Error(Exception): |
3040 | """Exception raised for internal errors.""" | 3047 | """Exception raised for internal errors.""" |
3041 | pass | 3048 | pass |
3042 | 3049 | ||
3043 | # | 3050 | # |
3044 | # Public functions | 3051 | # Public functions |
3045 | # | 3052 | # |
3046 | 3053 | ||
3047 | def tri_less(v1, v2): | 3054 | def tri_less(v1, v2): |
3048 | """Returns True if the tristate v1 is less than the tristate v2, where "n", | 3055 | """Returns True if the tristate v1 is less than the tristate v2, where "n", |
3049 | "m" and "y" are ordered from lowest to highest.""" | 3056 | "m" and "y" are ordered from lowest to highest.""" |
3050 | return TRI_TO_INT[v1] < TRI_TO_INT[v2] | 3057 | return TRI_TO_INT[v1] < TRI_TO_INT[v2] |
3051 | 3058 | ||
3052 | def tri_less_eq(v1, v2): | 3059 | def tri_less_eq(v1, v2): |
3053 | """Returns True if the tristate v1 is less than or equal to the tristate | 3060 | """Returns True if the tristate v1 is less than or equal to the tristate |
3054 | v2, where "n", "m" and "y" are ordered from lowest to highest.""" | 3061 | v2, where "n", "m" and "y" are ordered from lowest to highest.""" |
3055 | return TRI_TO_INT[v1] <= TRI_TO_INT[v2] | 3062 | return TRI_TO_INT[v1] <= TRI_TO_INT[v2] |
3056 | 3063 | ||
3057 | def tri_greater(v1, v2): | 3064 | def tri_greater(v1, v2): |
3058 | """Returns True if the tristate v1 is greater than the tristate v2, where | 3065 | """Returns True if the tristate v1 is greater than the tristate v2, where |
3059 | "n", "m" and "y" are ordered from lowest to highest.""" | 3066 | "n", "m" and "y" are ordered from lowest to highest.""" |
3060 | return TRI_TO_INT[v1] > TRI_TO_INT[v2] | 3067 | return TRI_TO_INT[v1] > TRI_TO_INT[v2] |
3061 | 3068 | ||
3062 | def tri_greater_eq(v1, v2): | 3069 | def tri_greater_eq(v1, v2): |
3063 | """Returns True if the tristate v1 is greater than or equal to the tristate | 3070 | """Returns True if the tristate v1 is greater than or equal to the tristate |
3064 | v2, where "n", "m" and "y" are ordered from lowest to highest.""" | 3071 | v2, where "n", "m" and "y" are ordered from lowest to highest.""" |
3065 | return TRI_TO_INT[v1] >= TRI_TO_INT[v2] | 3072 | return TRI_TO_INT[v1] >= TRI_TO_INT[v2] |
3066 | 3073 | ||
3067 | # | 3074 | # |
3068 | # Internal classes | 3075 | # Internal classes |
3069 | # | 3076 | # |
3070 | 3077 | ||
3071 | class _Feed(object): | 3078 | class _Feed(object): |
3072 | 3079 | ||
3073 | """Class for working with sequences in a stream-like fashion; handy for | 3080 | """Class for working with sequences in a stream-like fashion; handy for |
3074 | tokens.""" | 3081 | tokens.""" |
3075 | 3082 | ||
3076 | # This would be more helpful on the item classes, but would remove some | 3083 | # This would be more helpful on the item classes, but would remove some |
3077 | # flexibility | 3084 | # flexibility |
3078 | __slots__ = ['items', 'length', 'i'] | 3085 | __slots__ = ['items', 'length', 'i'] |
3079 | 3086 | ||
3080 | def __init__(self, items): | 3087 | def __init__(self, items): |
3081 | self.items = items | 3088 | self.items = items |
3082 | self.length = len(self.items) | 3089 | self.length = len(self.items) |
3083 | self.i = 0 | 3090 | self.i = 0 |
3084 | 3091 | ||
3085 | def get_next(self): | 3092 | def get_next(self): |
3086 | if self.i >= self.length: | 3093 | if self.i >= self.length: |
3087 | return None | 3094 | return None |
3088 | item = self.items[self.i] | 3095 | item = self.items[self.i] |
3089 | self.i += 1 | 3096 | self.i += 1 |
3090 | return item | 3097 | return item |
3091 | 3098 | ||
3092 | def peek_next(self): | 3099 | def peek_next(self): |
3093 | return None if self.i >= self.length else self.items[self.i] | 3100 | return None if self.i >= self.length else self.items[self.i] |
3094 | 3101 | ||
3095 | def check(self, token): | 3102 | def check(self, token): |
3096 | """Check if the next token is 'token'. If so, remove it from the token | 3103 | """Check if the next token is 'token'. If so, remove it from the token |
3097 | feed and return True. Otherwise, leave it in and return False.""" | 3104 | feed and return True. Otherwise, leave it in and return False.""" |
3098 | if self.i < self.length and self.items[self.i] == token: | 3105 | if self.i < self.length and self.items[self.i] == token: |
3099 | self.i += 1 | 3106 | self.i += 1 |
3100 | return True | 3107 | return True |
3101 | return False | 3108 | return False |
3102 | 3109 | ||
3103 | def unget_all(self): | 3110 | def unget_all(self): |
3104 | self.i = 0 | 3111 | self.i = 0 |
3105 | 3112 | ||
3106 | class _FileFeed(object): | 3113 | class _FileFeed(object): |
3107 | 3114 | ||
3108 | """Feeds lines from a file. Keeps track of the filename and current line | 3115 | """Feeds lines from a file. Keeps track of the filename and current line |
3109 | number. Joins any line ending in \\ with the following line. We need to be | 3116 | number. Joins any line ending in \\ with the following line. We need to be |
3110 | careful to get the line number right in the presence of continuation | 3117 | careful to get the line number right in the presence of continuation |
3111 | lines.""" | 3118 | lines.""" |
3112 | 3119 | ||
3113 | __slots__ = ['filename', 'lines', 'length', 'linenr'] | 3120 | __slots__ = ['filename', 'lines', 'length', 'linenr'] |
3114 | 3121 | ||
3115 | def __init__(self, filename): | 3122 | def __init__(self, filename): |
3116 | self.filename = _clean_up_path(filename) | 3123 | self.filename = _clean_up_path(filename) |
3117 | with open(filename, "r") as f: | 3124 | with open(filename, "r") as f: |
3118 | # No interleaving of I/O and processing yet. Don't know if it would | 3125 | # No interleaving of I/O and processing yet. Don't know if it would |
3119 | # help. | 3126 | # help. |
3120 | self.lines = f.readlines() | 3127 | self.lines = f.readlines() |
3121 | self.length = len(self.lines) | 3128 | self.length = len(self.lines) |
3122 | self.linenr = 0 | 3129 | self.linenr = 0 |
3123 | 3130 | ||
3124 | def get_next(self): | 3131 | def get_next(self): |
3125 | if self.linenr >= self.length: | 3132 | if self.linenr >= self.length: |
3126 | return None | 3133 | return None |
3127 | line = self.lines[self.linenr] | 3134 | line = self.lines[self.linenr] |
3128 | self.linenr += 1 | 3135 | self.linenr += 1 |
3129 | while line.endswith("\\\n"): | 3136 | while line.endswith("\\\n"): |
3130 | line = line[:-2] + self.lines[self.linenr] | 3137 | line = line[:-2] + self.lines[self.linenr] |
3131 | self.linenr += 1 | 3138 | self.linenr += 1 |
3132 | return line | 3139 | return line |
3133 | 3140 | ||
3134 | def peek_next(self): | 3141 | def peek_next(self): |
3135 | linenr = self.linenr | 3142 | linenr = self.linenr |
3136 | if linenr >= self.length: | 3143 | if linenr >= self.length: |
3137 | return None | 3144 | return None |
3138 | line = self.lines[linenr] | 3145 | line = self.lines[linenr] |
3139 | while line.endswith("\\\n"): | 3146 | while line.endswith("\\\n"): |
3140 | linenr += 1 | 3147 | linenr += 1 |
3141 | line = line[:-2] + self.lines[linenr] | 3148 | line = line[:-2] + self.lines[linenr] |
3142 | return line | 3149 | return line |
3143 | 3150 | ||
3144 | def unget(self): | 3151 | def unget(self): |
3145 | self.linenr -= 1 | 3152 | self.linenr -= 1 |
3146 | while self.lines[self.linenr].endswith("\\\n"): | 3153 | while self.lines[self.linenr].endswith("\\\n"): |
3147 | self.linenr -= 1 | 3154 | self.linenr -= 1 |
3148 | 3155 | ||
3149 | def next_nonblank(self): | 3156 | def next_nonblank(self): |
3150 | """Removes lines up to and including the next non-blank (not all-space) | 3157 | """Removes lines up to and including the next non-blank (not all-space) |
3151 | line and returns it. Returns None if there are no more non-blank | 3158 | line and returns it. Returns None if there are no more non-blank |
3152 | lines.""" | 3159 | lines.""" |
3153 | while 1: | 3160 | while 1: |
3154 | line = self.get_next() | 3161 | line = self.get_next() |
3155 | if line is None or not line.isspace(): | 3162 | if line is None or not line.isspace(): |
3156 | return line | 3163 | return line |
3157 | 3164 | ||
3158 | # | 3165 | # |
3159 | # Internal functions | 3166 | # Internal functions |
3160 | # | 3167 | # |
3161 | 3168 | ||
3162 | def _get_visibility(sc): | 3169 | def _get_visibility(sc): |
3163 | """Symbols and Choices have a "visibility" that acts as an upper bound on | 3170 | """Symbols and Choices have a "visibility" that acts as an upper bound on |
3164 | the values a user can set for them, corresponding to the visibility in e.g. | 3171 | the values a user can set for them, corresponding to the visibility in e.g. |
3165 | 'make menuconfig'. This function calculates the visibility for the Symbol | 3172 | 'make menuconfig'. This function calculates the visibility for the Symbol |
3166 | or Choice 'sc' -- the logic is nearly identical.""" | 3173 | or Choice 'sc' -- the logic is nearly identical.""" |
3167 | if sc.cached_visibility is None: | 3174 | if sc.cached_visibility is None: |
3168 | vis = "n" | 3175 | vis = "n" |
3169 | for _, cond_expr in sc.prompts: | 3176 | for _, cond_expr in sc.prompts: |
3170 | vis = sc.config._eval_max(vis, cond_expr) | 3177 | vis = sc.config._eval_max(vis, cond_expr) |
3171 | 3178 | ||
3172 | if isinstance(sc, Symbol) and sc.is_choice_sym: | 3179 | if isinstance(sc, Symbol) and sc.is_choice_sym: |
3173 | vis = sc.config._eval_min(vis, _get_visibility(sc.parent)) | 3180 | vis = sc.config._eval_min(vis, _get_visibility(sc.parent)) |
3174 | 3181 | ||
3175 | # Promote "m" to "y" if we're dealing with a non-tristate | 3182 | # Promote "m" to "y" if we're dealing with a non-tristate |
3176 | if vis == "m" and sc.type != TRISTATE: | 3183 | if vis == "m" and sc.type != TRISTATE: |
3177 | vis = "y" | 3184 | vis = "y" |
3178 | 3185 | ||
3179 | sc.cached_visibility = vis | 3186 | sc.cached_visibility = vis |
3180 | 3187 | ||
3181 | return sc.cached_visibility | 3188 | return sc.cached_visibility |
3182 | 3189 | ||
3183 | def _make_and(e1, e2): | 3190 | def _make_and(e1, e2): |
3184 | """Constructs an AND (&&) expression. Performs trivial simplification. | 3191 | """Constructs an AND (&&) expression. Performs trivial simplification. |
3185 | Nones equate to 'y'. | 3192 | Nones equate to 'y'. |
3186 | 3193 | ||
3187 | Note: returns None if e1 == e2 == None.""" | 3194 | Note: returns None if e1 == e2 == None.""" |
3188 | if e1 is None or e1 == "y": | 3195 | if e1 is None or e1 == "y": |
3189 | return e2 | 3196 | return e2 |
3190 | if e2 is None or e2 == "y": | 3197 | if e2 is None or e2 == "y": |
3191 | return e1 | 3198 | return e1 |
3192 | 3199 | ||
3193 | # Prefer to merge argument lists if possible to reduce the number of nodes | 3200 | # Prefer to merge argument lists if possible to reduce the number of nodes |
3194 | 3201 | ||
3195 | if isinstance(e1, tuple) and e1[0] == AND: | 3202 | if isinstance(e1, tuple) and e1[0] == AND: |
3196 | if isinstance(e2, tuple) and e2[0] == AND: | 3203 | if isinstance(e2, tuple) and e2[0] == AND: |
3197 | return (AND, e1[1] + e2[1]) | 3204 | return (AND, e1[1] + e2[1]) |
3198 | return (AND, e1[1] + [e2]) | 3205 | return (AND, e1[1] + [e2]) |
3199 | 3206 | ||
3200 | if isinstance(e2, tuple) and e2[0] == AND: | 3207 | if isinstance(e2, tuple) and e2[0] == AND: |
3201 | return (AND, e2[1] + [e1]) | 3208 | return (AND, e2[1] + [e1]) |
3202 | 3209 | ||
3203 | return (AND, [e1, e2]) | 3210 | return (AND, [e1, e2]) |
3204 | 3211 | ||
3205 | def _make_or(e1, e2): | 3212 | def _make_or(e1, e2): |
3206 | """Constructs an OR (||) expression. Performs trivial simplification and | 3213 | """Constructs an OR (||) expression. Performs trivial simplification and |
3207 | avoids Nones. Nones equate to 'y', which is usually what we want, but needs | 3214 | avoids Nones. Nones equate to 'y', which is usually what we want, but needs |
3208 | to be kept in mind.""" | 3215 | to be kept in mind.""" |
3209 | 3216 | ||
3210 | # Perform trivial simplification and avoid None's (which | 3217 | # Perform trivial simplification and avoid None's (which |
3211 | # correspond to y's) | 3218 | # correspond to y's) |
3212 | if e1 is None or e2 is None or e1 == "y" or e2 == "y": | 3219 | if e1 is None or e2 is None or e1 == "y" or e2 == "y": |
3213 | return "y" | 3220 | return "y" |
3214 | if e1 == "n": | 3221 | if e1 == "n": |
3215 | return e2 | 3222 | return e2 |
3216 | 3223 | ||
3217 | # Prefer to merge argument lists if possible to reduce the number of nodes | 3224 | # Prefer to merge argument lists if possible to reduce the number of nodes |
3218 | 3225 | ||
3219 | if isinstance(e1, tuple) and e1[0] == OR: | 3226 | if isinstance(e1, tuple) and e1[0] == OR: |
3220 | if isinstance(e2, tuple) and e2[0] == OR: | 3227 | if isinstance(e2, tuple) and e2[0] == OR: |
3221 | return (OR, e1[1] + e2[1]) | 3228 | return (OR, e1[1] + e2[1]) |
3222 | return (OR, e1[1] + [e2]) | 3229 | return (OR, e1[1] + [e2]) |
3223 | 3230 | ||
3224 | if isinstance(e2, tuple) and e2[0] == OR: | 3231 | if isinstance(e2, tuple) and e2[0] == OR: |
3225 | return (OR, e2[1] + [e1]) | 3232 | return (OR, e2[1] + [e1]) |
3226 | 3233 | ||
3227 | return (OR, [e1, e2]) | 3234 | return (OR, [e1, e2]) |
3228 | 3235 | ||
3229 | def _get_expr_syms_rec(expr, res): | 3236 | def _get_expr_syms_rec(expr, res): |
3230 | """_get_expr_syms() helper. Recurses through expressions.""" | 3237 | """_get_expr_syms() helper. Recurses through expressions.""" |
3231 | if isinstance(expr, Symbol): | 3238 | if isinstance(expr, Symbol): |
3232 | res.add(expr) | 3239 | res.add(expr) |
3233 | elif isinstance(expr, str): | 3240 | elif isinstance(expr, str): |
3234 | return | 3241 | return |
3235 | elif expr[0] == AND or expr[0] == OR: | 3242 | elif expr[0] == AND or expr[0] == OR: |
3236 | for term in expr[1]: | 3243 | for term in expr[1]: |
3237 | _get_expr_syms_rec(term, res) | 3244 | _get_expr_syms_rec(term, res) |
3238 | elif expr[0] == NOT: | 3245 | elif expr[0] == NOT: |
3239 | _get_expr_syms_rec(expr[1], res) | 3246 | _get_expr_syms_rec(expr[1], res) |
3240 | elif expr[0] == EQUAL or expr[0] == UNEQUAL: | 3247 | elif expr[0] == EQUAL or expr[0] == UNEQUAL: |
3241 | if isinstance(expr[1], Symbol): | 3248 | if isinstance(expr[1], Symbol): |
3242 | res.add(expr[1]) | 3249 | res.add(expr[1]) |
3243 | if isinstance(expr[2], Symbol): | 3250 | if isinstance(expr[2], Symbol): |
3244 | res.add(expr[2]) | 3251 | res.add(expr[2]) |
3245 | else: | 3252 | else: |
3246 | _internal_error("Internal error while fetching symbols from an " | 3253 | _internal_error("Internal error while fetching symbols from an " |
3247 | "expression with token stream {0}.".format(expr)) | 3254 | "expression with token stream {0}.".format(expr)) |
3248 | 3255 | ||
3249 | def _get_expr_syms(expr): | 3256 | def _get_expr_syms(expr): |
3250 | """Returns the set() of symbols appearing in expr.""" | 3257 | """Returns the set() of symbols appearing in expr.""" |
3251 | res = set() | 3258 | res = set() |
3252 | if expr is not None: | 3259 | if expr is not None: |
3253 | _get_expr_syms_rec(expr, res) | 3260 | _get_expr_syms_rec(expr, res) |
3254 | return res | 3261 | return res |
3255 | 3262 | ||
3256 | def _str_val(obj): | 3263 | def _str_val(obj): |
3257 | """Returns the value of obj as a string. If obj is not a string (constant | 3264 | """Returns the value of obj as a string. If obj is not a string (constant |
3258 | symbol), it must be a Symbol.""" | 3265 | symbol), it must be a Symbol.""" |
3259 | return obj if isinstance(obj, str) else obj.get_value() | 3266 | return obj if isinstance(obj, str) else obj.get_value() |
3260 | 3267 | ||
3261 | def _make_block_conf(block, append_fn): | 3268 | def _make_block_conf(block, append_fn): |
3262 | """Returns a list of .config strings for a block (list) of items.""" | 3269 | """Returns a list of .config strings for a block (list) of items.""" |
3263 | 3270 | ||
3264 | # Collect the substrings in a list and later use join() instead of += to | 3271 | # Collect the substrings in a list and later use join() instead of += to |
3265 | # build the final .config contents. With older Python versions, this yields | 3272 | # build the final .config contents. With older Python versions, this yields |
3266 | # linear instead of quadratic complexity. | 3273 | # linear instead of quadratic complexity. |
3267 | for item in block: | 3274 | for item in block: |
3268 | item._make_conf(append_fn) | 3275 | item._make_conf(append_fn) |
3269 | 3276 | ||
3270 | def _sym_str_string(sym_or_str): | 3277 | def _sym_str_string(sym_or_str): |
3271 | if isinstance(sym_or_str, str): | 3278 | if isinstance(sym_or_str, str): |
3272 | return '"' + sym_or_str + '"' | 3279 | return '"' + sym_or_str + '"' |
3273 | return sym_or_str.name | 3280 | return sym_or_str.name |
3274 | 3281 | ||
3275 | def _intersperse(lst, op): | 3282 | def _intersperse(lst, op): |
3276 | """_expr_to_str() helper. Gets the string representation of each expression | 3283 | """_expr_to_str() helper. Gets the string representation of each expression |
3277 | in lst and produces a list where op has been inserted between the | 3284 | in lst and produces a list where op has been inserted between the |
3278 | elements.""" | 3285 | elements.""" |
3279 | if not lst: | 3286 | if not lst: |
3280 | return "" | 3287 | return "" |
3281 | 3288 | ||
3282 | res = [] | 3289 | res = [] |
3283 | 3290 | ||
3284 | def handle_sub_expr(expr): | 3291 | def handle_sub_expr(expr): |
3285 | no_parens = isinstance(expr, (str, Symbol)) or \ | 3292 | no_parens = isinstance(expr, (str, Symbol)) or \ |
3286 | expr[0] in (EQUAL, UNEQUAL) or \ | 3293 | expr[0] in (EQUAL, UNEQUAL) or \ |
3287 | PRECEDENCE[op] <= PRECEDENCE[expr[0]] | 3294 | PRECEDENCE[op] <= PRECEDENCE[expr[0]] |
3288 | if not no_parens: | 3295 | if not no_parens: |
3289 | res.append("(") | 3296 | res.append("(") |
3290 | res.extend(_expr_to_str_rec(expr)) | 3297 | res.extend(_expr_to_str_rec(expr)) |
3291 | if not no_parens: | 3298 | if not no_parens: |
3292 | res.append(")") | 3299 | res.append(")") |
3293 | 3300 | ||
3294 | op_str = OP_TO_STR[op] | 3301 | op_str = OP_TO_STR[op] |
3295 | 3302 | ||
3296 | handle_sub_expr(lst[0]) | 3303 | handle_sub_expr(lst[0]) |
3297 | for expr in lst[1:]: | 3304 | for expr in lst[1:]: |
3298 | res.append(op_str) | 3305 | res.append(op_str) |
3299 | handle_sub_expr(expr) | 3306 | handle_sub_expr(expr) |
3300 | 3307 | ||
3301 | return res | 3308 | return res |
3302 | 3309 | ||
3303 | def _expr_to_str_rec(expr): | 3310 | def _expr_to_str_rec(expr): |
3304 | if expr is None: | 3311 | if expr is None: |
3305 | return [""] | 3312 | return [""] |
3306 | 3313 | ||
3307 | if isinstance(expr, (Symbol, str)): | 3314 | if isinstance(expr, (Symbol, str)): |
3308 | return [_sym_str_string(expr)] | 3315 | return [_sym_str_string(expr)] |
3309 | 3316 | ||
3310 | if expr[0] in (AND, OR): | 3317 | if expr[0] in (AND, OR): |
3311 | return _intersperse(expr[1], expr[0]) | 3318 | return _intersperse(expr[1], expr[0]) |
3312 | 3319 | ||
3313 | if expr[0] == NOT: | 3320 | if expr[0] == NOT: |
3314 | need_parens = not isinstance(expr[1], (str, Symbol)) | 3321 | need_parens = not isinstance(expr[1], (str, Symbol)) |
3315 | 3322 | ||
3316 | res = ["!"] | 3323 | res = ["!"] |
3317 | if need_parens: | 3324 | if need_parens: |
3318 | res.append("(") | 3325 | res.append("(") |
3319 | res.extend(_expr_to_str_rec(expr[1])) | 3326 | res.extend(_expr_to_str_rec(expr[1])) |
3320 | if need_parens: | 3327 | if need_parens: |
3321 | res.append(")") | 3328 | res.append(")") |
3322 | return res | 3329 | return res |
3323 | 3330 | ||
3324 | if expr[0] in (EQUAL, UNEQUAL): | 3331 | if expr[0] in (EQUAL, UNEQUAL): |
3325 | return [_sym_str_string(expr[1]), | 3332 | return [_sym_str_string(expr[1]), |
3326 | OP_TO_STR[expr[0]], | 3333 | OP_TO_STR[expr[0]], |
3327 | _sym_str_string(expr[2])] | 3334 | _sym_str_string(expr[2])] |
3328 | 3335 | ||
3329 | def _expr_to_str(expr): | 3336 | def _expr_to_str(expr): |
3330 | return "".join(_expr_to_str_rec(expr)) | 3337 | return "".join(_expr_to_str_rec(expr)) |
3331 | 3338 | ||
3332 | def _indentation(line): | 3339 | def _indentation(line): |
3333 | """Returns the length of the line's leading whitespace, treating tab stops | 3340 | """Returns the length of the line's leading whitespace, treating tab stops |
3334 | as being spaced 8 characters apart.""" | 3341 | as being spaced 8 characters apart.""" |
3335 | line = line.expandtabs() | 3342 | line = line.expandtabs() |
3336 | return len(line) - len(line.lstrip()) | 3343 | return len(line) - len(line.lstrip()) |
3337 | 3344 | ||
3338 | def _deindent(line, indent): | 3345 | def _deindent(line, indent): |
3339 | """Deindent 'line' by 'indent' spaces.""" | 3346 | """Deindent 'line' by 'indent' spaces.""" |
3340 | line = line.expandtabs() | 3347 | line = line.expandtabs() |
3341 | if len(line) <= indent: | 3348 | if len(line) <= indent: |
3342 | return line | 3349 | return line |
3343 | return line[indent:] | 3350 | return line[indent:] |
3344 | 3351 | ||
3345 | def _is_base_n(s, n): | 3352 | def _is_base_n(s, n): |
3346 | try: | 3353 | try: |
3347 | int(s, n) | 3354 | int(s, n) |
3348 | return True | 3355 | return True |
3349 | except ValueError: | 3356 | except ValueError: |
3350 | return False | 3357 | return False |
3351 | 3358 | ||
3352 | def _lines(*args): | 3359 | def _lines(*args): |
3353 | """Returns a string consisting of all arguments, with newlines inserted | 3360 | """Returns a string consisting of all arguments, with newlines inserted |
3354 | between them.""" | 3361 | between them.""" |
3355 | return "\n".join(args) | 3362 | return "\n".join(args) |
3356 | 3363 | ||
3357 | def _comment(s): | 3364 | def _comment(s): |
3358 | """Returns a new string with "#" inserted before each line in 's'.""" | 3365 | """Returns a new string with "#" inserted before each line in 's'.""" |
3359 | if not s: | 3366 | if not s: |
3360 | return "#" | 3367 | return "#" |
3361 | res = "".join(["#" + line for line in s.splitlines(True)]) | 3368 | res = "".join(["#" + line for line in s.splitlines(True)]) |
3362 | if s.endswith("\n"): | 3369 | if s.endswith("\n"): |
3363 | return res + "#" | 3370 | return res + "#" |
3364 | return res | 3371 | return res |
3365 | 3372 | ||
3366 | def _clean_up_path(path): | 3373 | def _clean_up_path(path): |
3367 | """Strips an initial "./" and any trailing slashes from 'path'.""" | 3374 | """Strips an initial "./" and any trailing slashes from 'path'.""" |
3368 | if path.startswith("./"): | 3375 | if path.startswith("./"): |
3369 | path = path[2:] | 3376 | path = path[2:] |
3370 | return path.rstrip("/") | 3377 | return path.rstrip("/") |
3371 | 3378 | ||
3372 | def _stderr_msg(msg, filename, linenr): | 3379 | def _build_msg(msg, filename, linenr): |
3373 | if filename is not None: | 3380 | if filename is not None: |
3374 | sys.stderr.write("{0}:{1}: ".format(_clean_up_path(filename), linenr)) | 3381 | msg = "{0}:{1}: ".format(_clean_up_path(filename), linenr) + msg |
3375 | sys.stderr.write(msg + "\n") | 3382 | return msg |
3383 | |||
3384 | def _stderr_msg(msg, filename, linenr): | ||
3385 | sys.stderr.write(_build_msg(msg, filename, linenr) + "\n") | ||
3376 | 3386 | ||
3377 | def _tokenization_error(s, filename, linenr): | 3387 | def _tokenization_error(s, filename, linenr): |
3378 | loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr) | 3388 | loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr) |
3379 | raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'" | 3389 | raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'" |
3380 | .format(loc, s.strip())) | 3390 | .format(loc, s.strip())) |
3381 | 3391 | ||
3382 | def _parse_error(s, msg, filename, linenr): | 3392 | def _parse_error(s, msg, filename, linenr): |
3383 | loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr) | 3393 | loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr) |
3384 | raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}" | 3394 | raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}" |
3385 | .format(loc, s.strip(), | 3395 | .format(loc, s.strip(), |
3386 | "." if msg is None else ": " + msg)) | 3396 | "." if msg is None else ": " + msg)) |
3387 | 3397 | ||
3388 | def _internal_error(msg): | 3398 | def _internal_error(msg): |
3389 | raise Internal_Error(msg + | 3399 | raise Internal_Error(msg + |
3390 | "\nSorry! You may want to send an email to ulfalizer a.t Google's " | 3400 | "\nSorry! You may want to send an email to ulfalizer a.t Google's " |
3391 | "email service to tell me about this. Include the message above and the " | 3401 | "email service to tell me about this. Include the message above and the " |
3392 | "stack trace and describe what you were doing.") | 3402 | "stack trace and describe what you were doing.") |
3393 | 3403 | ||
3394 | # | 3404 | # |
3395 | # Internal global constants | 3405 | # Internal global constants |
3396 | # | 3406 | # |
3397 | 3407 | ||
3398 | # Tokens | 3408 | # Tokens |
3399 | (T_AND, T_OR, T_NOT, | 3409 | (T_AND, T_OR, T_NOT, |
3400 | T_OPEN_PAREN, T_CLOSE_PAREN, | 3410 | T_OPEN_PAREN, T_CLOSE_PAREN, |
3401 | T_EQUAL, T_UNEQUAL, | 3411 | T_EQUAL, T_UNEQUAL, |
3402 | T_MAINMENU, T_MENU, T_ENDMENU, | 3412 | T_MAINMENU, T_MENU, T_ENDMENU, |
3403 | T_SOURCE, T_CHOICE, T_ENDCHOICE, | 3413 | T_SOURCE, T_CHOICE, T_ENDCHOICE, |
3404 | T_COMMENT, T_CONFIG, T_MENUCONFIG, | 3414 | T_COMMENT, T_CONFIG, T_MENUCONFIG, |
3405 | T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON, | 3415 | T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON, |
3406 | T_OPTIONAL, T_PROMPT, T_DEFAULT, | 3416 | T_OPTIONAL, T_PROMPT, T_DEFAULT, |
3407 | T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING, | 3417 | T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING, |
3408 | T_DEF_BOOL, T_DEF_TRISTATE, | 3418 | T_DEF_BOOL, T_DEF_TRISTATE, |
3409 | T_SELECT, T_IMPLY, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV, | 3419 | T_SELECT, T_IMPLY, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV, |
3410 | T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(40) | 3420 | T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(40) |
3411 | 3421 | ||
3412 | # The leading underscore before the function assignments below prevent pydoc | 3422 | # The leading underscore before the function assignments below prevent pydoc |
3413 | # from listing them. The constants could be hidden too, but they're fairly | 3423 | # from listing them. The constants could be hidden too, but they're fairly |
3414 | # obviously internal anyway, so don't bother spamming the code. | 3424 | # obviously internal anyway, so don't bother spamming the code. |
3415 | 3425 | ||
3416 | # Keyword to token map. Note that the get() method is assigned directly as a | 3426 | # Keyword to token map. Note that the get() method is assigned directly as a |
3417 | # small optimization. | 3427 | # small optimization. |
3418 | _get_keyword = \ | 3428 | _get_keyword = \ |
3419 | {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU, | 3429 | {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU, |
3420 | "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE, | 3430 | "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE, |
3421 | "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT, | 3431 | "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT, |
3422 | "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF, | 3432 | "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF, |
3423 | "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL, | 3433 | "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL, |
3424 | "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL, | 3434 | "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL, |
3425 | "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL, | 3435 | "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL, |
3426 | "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT, | 3436 | "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT, |
3427 | "imply": T_IMPLY, "range": T_RANGE, "option": T_OPTION, | 3437 | "imply": T_IMPLY, "range": T_RANGE, "option": T_OPTION, |
3428 | "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV, | 3438 | "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV, |
3429 | "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES, | 3439 | "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES, |
3430 | "visible": T_VISIBLE}.get | 3440 | "visible": T_VISIBLE}.get |
3431 | 3441 | ||
3432 | # Strings to use for True and False | 3442 | # Strings to use for True and False |
3433 | BOOL_STR = {False: "false", True: "true"} | 3443 | BOOL_STR = {False: "false", True: "true"} |
3434 | 3444 | ||
3435 | # Tokens after which identifier-like lexemes are treated as strings. T_CHOICE | 3445 | # Tokens after which identifier-like lexemes are treated as strings. T_CHOICE |
3436 | # is included to avoid symbols being registered for named choices. | 3446 | # is included to avoid symbols being registered for named choices. |
3437 | STRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE, | 3447 | STRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE, |
3438 | T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU)) | 3448 | T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU)) |
3439 | 3449 | ||
3440 | # Matches the initial token on a line; see _tokenize(). Also eats trailing | 3450 | # Matches the initial token on a line; see _tokenize(). Also eats trailing |
3441 | # whitespace as an optimization. | 3451 | # whitespace as an optimization. |
3442 | _initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match | 3452 | _initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match |
3443 | 3453 | ||
3444 | # Matches an identifier/keyword optionally preceded by whitespace. Also eats | 3454 | # Matches an identifier/keyword optionally preceded by whitespace. Also eats |
3445 | # trailing whitespace as an optimization. | 3455 | # trailing whitespace as an optimization. |
3446 | _id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match | 3456 | _id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match |
3447 | 3457 | ||
3448 | # Regular expressions for parsing .config files | 3458 | # Regular expressions for parsing .config files |
3449 | _set_re_match = re.compile(r"CONFIG_(\w+)=(.*)").match | 3459 | _set_re_match = re.compile(r"CONFIG_(\w+)=(.*)").match |
3450 | _unset_re_match = re.compile(r"# CONFIG_(\w+) is not set").match | 3460 | _unset_re_match = re.compile(r"# CONFIG_(\w+) is not set").match |
3451 | 3461 | ||
3452 | # Regular expression for finding $-references to symbols in strings | 3462 | # Regular expression for finding $-references to symbols in strings |
3453 | _sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search | 3463 | _sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search |
3454 | 3464 | ||
3455 | # Integers representing symbol types | 3465 | # Integers representing symbol types |
3456 | UNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6) | 3466 | UNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6) |
3457 | 3467 | ||
3458 | # Strings to use for types | 3468 | # Strings to use for types |
3459 | TYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate", | 3469 | TYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate", |
3460 | STRING: "string", HEX: "hex", INT: "int"} | 3470 | STRING: "string", HEX: "hex", INT: "int"} |
3461 | 3471 | ||
3462 | # Token to type mapping | 3472 | # Token to type mapping |
3463 | TOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING, | 3473 | TOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING, |
3464 | T_INT: INT, T_HEX: HEX} | 3474 | T_INT: INT, T_HEX: HEX} |
3465 | 3475 | ||
3466 | # Default values for symbols of different types (the value the symbol gets if | 3476 | # Default values for symbols of different types (the value the symbol gets if |
3467 | # it is not assigned a user value and none of its 'default' clauses kick in) | 3477 | # it is not assigned a user value and none of its 'default' clauses kick in) |
3468 | DEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""} | 3478 | DEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""} |
3469 | 3479 | ||
3470 | # Indicates that no item is selected in a choice statement | 3480 | # Indicates that no item is selected in a choice statement |
3471 | NO_SELECTION = 0 | 3481 | NO_SELECTION = 0 |
3472 | 3482 | ||
3473 | # Integers representing expression types | 3483 | # Integers representing expression types |
3474 | AND, OR, NOT, EQUAL, UNEQUAL = range(5) | 3484 | AND, OR, NOT, EQUAL, UNEQUAL = range(5) |
3475 | 3485 | ||
3476 | # Map from tristate values to integers | 3486 | # Map from tristate values to integers |
3477 | TRI_TO_INT = {"n": 0, "m": 1, "y": 2} | 3487 | TRI_TO_INT = {"n": 0, "m": 1, "y": 2} |
3478 | 3488 | ||
3479 | # Printing-related stuff | 3489 | # Printing-related stuff |
3480 | 3490 | ||
3481 | OP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "} | 3491 | OP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "} |
3482 | PRECEDENCE = {OR: 0, AND: 1, NOT: 2} | 3492 | PRECEDENCE = {OR: 0, AND: 1, NOT: 2} |
3483 | 3493 |
tools/genboardscfg.py
1 | #!/usr/bin/env python2 | 1 | #!/usr/bin/env python2 |
2 | # | 2 | # |
3 | # Author: Masahiro Yamada <yamada.m@jp.panasonic.com> | 3 | # Author: Masahiro Yamada <yamada.m@jp.panasonic.com> |
4 | # | 4 | # |
5 | # SPDX-License-Identifier: GPL-2.0+ | 5 | # SPDX-License-Identifier: GPL-2.0+ |
6 | # | 6 | # |
7 | 7 | ||
8 | """ | 8 | """ |
9 | Converter from Kconfig and MAINTAINERS to a board database. | 9 | Converter from Kconfig and MAINTAINERS to a board database. |
10 | 10 | ||
11 | Run 'tools/genboardscfg.py' to create a board database. | 11 | Run 'tools/genboardscfg.py' to create a board database. |
12 | 12 | ||
13 | Run 'tools/genboardscfg.py -h' for available options. | 13 | Run 'tools/genboardscfg.py -h' for available options. |
14 | 14 | ||
15 | Python 2.6 or later, but not Python 3.x is necessary to run this script. | 15 | Python 2.6 or later, but not Python 3.x is necessary to run this script. |
16 | """ | 16 | """ |
17 | 17 | ||
18 | import errno | 18 | import errno |
19 | import fnmatch | 19 | import fnmatch |
20 | import glob | 20 | import glob |
21 | import multiprocessing | 21 | import multiprocessing |
22 | import optparse | 22 | import optparse |
23 | import os | 23 | import os |
24 | import sys | 24 | import sys |
25 | import tempfile | 25 | import tempfile |
26 | import time | 26 | import time |
27 | 27 | ||
28 | sys.path.append(os.path.join(os.path.dirname(__file__), 'buildman')) | 28 | sys.path.append(os.path.join(os.path.dirname(__file__), 'buildman')) |
29 | import kconfiglib | 29 | import kconfiglib |
30 | 30 | ||
31 | ### constant variables ### | 31 | ### constant variables ### |
32 | OUTPUT_FILE = 'boards.cfg' | 32 | OUTPUT_FILE = 'boards.cfg' |
33 | CONFIG_DIR = 'configs' | 33 | CONFIG_DIR = 'configs' |
34 | SLEEP_TIME = 0.03 | 34 | SLEEP_TIME = 0.03 |
35 | COMMENT_BLOCK = '''# | 35 | COMMENT_BLOCK = '''# |
36 | # List of boards | 36 | # List of boards |
37 | # Automatically generated by %s: don't edit | 37 | # Automatically generated by %s: don't edit |
38 | # | 38 | # |
39 | # Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers | 39 | # Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers |
40 | 40 | ||
41 | ''' % __file__ | 41 | ''' % __file__ |
42 | 42 | ||
43 | ### helper functions ### | 43 | ### helper functions ### |
44 | def try_remove(f): | 44 | def try_remove(f): |
45 | """Remove a file ignoring 'No such file or directory' error.""" | 45 | """Remove a file ignoring 'No such file or directory' error.""" |
46 | try: | 46 | try: |
47 | os.remove(f) | 47 | os.remove(f) |
48 | except OSError as exception: | 48 | except OSError as exception: |
49 | # Ignore 'No such file or directory' error | 49 | # Ignore 'No such file or directory' error |
50 | if exception.errno != errno.ENOENT: | 50 | if exception.errno != errno.ENOENT: |
51 | raise | 51 | raise |
52 | 52 | ||
53 | def check_top_directory(): | 53 | def check_top_directory(): |
54 | """Exit if we are not at the top of source directory.""" | 54 | """Exit if we are not at the top of source directory.""" |
55 | for f in ('README', 'Licenses'): | 55 | for f in ('README', 'Licenses'): |
56 | if not os.path.exists(f): | 56 | if not os.path.exists(f): |
57 | sys.exit('Please run at the top of source directory.') | 57 | sys.exit('Please run at the top of source directory.') |
58 | 58 | ||
59 | def output_is_new(output): | 59 | def output_is_new(output): |
60 | """Check if the output file is up to date. | 60 | """Check if the output file is up to date. |
61 | 61 | ||
62 | Returns: | 62 | Returns: |
63 | True if the given output file exists and is newer than any of | 63 | True if the given output file exists and is newer than any of |
64 | *_defconfig, MAINTAINERS and Kconfig*. False otherwise. | 64 | *_defconfig, MAINTAINERS and Kconfig*. False otherwise. |
65 | """ | 65 | """ |
66 | try: | 66 | try: |
67 | ctime = os.path.getctime(output) | 67 | ctime = os.path.getctime(output) |
68 | except OSError as exception: | 68 | except OSError as exception: |
69 | if exception.errno == errno.ENOENT: | 69 | if exception.errno == errno.ENOENT: |
70 | # return False on 'No such file or directory' error | 70 | # return False on 'No such file or directory' error |
71 | return False | 71 | return False |
72 | else: | 72 | else: |
73 | raise | 73 | raise |
74 | 74 | ||
75 | for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): | 75 | for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): |
76 | for filename in fnmatch.filter(filenames, '*_defconfig'): | 76 | for filename in fnmatch.filter(filenames, '*_defconfig'): |
77 | if fnmatch.fnmatch(filename, '.*'): | 77 | if fnmatch.fnmatch(filename, '.*'): |
78 | continue | 78 | continue |
79 | filepath = os.path.join(dirpath, filename) | 79 | filepath = os.path.join(dirpath, filename) |
80 | if ctime < os.path.getctime(filepath): | 80 | if ctime < os.path.getctime(filepath): |
81 | return False | 81 | return False |
82 | 82 | ||
83 | for (dirpath, dirnames, filenames) in os.walk('.'): | 83 | for (dirpath, dirnames, filenames) in os.walk('.'): |
84 | for filename in filenames: | 84 | for filename in filenames: |
85 | if (fnmatch.fnmatch(filename, '*~') or | 85 | if (fnmatch.fnmatch(filename, '*~') or |
86 | not fnmatch.fnmatch(filename, 'Kconfig*') and | 86 | not fnmatch.fnmatch(filename, 'Kconfig*') and |
87 | not filename == 'MAINTAINERS'): | 87 | not filename == 'MAINTAINERS'): |
88 | continue | 88 | continue |
89 | filepath = os.path.join(dirpath, filename) | 89 | filepath = os.path.join(dirpath, filename) |
90 | if ctime < os.path.getctime(filepath): | 90 | if ctime < os.path.getctime(filepath): |
91 | return False | 91 | return False |
92 | 92 | ||
93 | # Detect a board that has been removed since the current board database | 93 | # Detect a board that has been removed since the current board database |
94 | # was generated | 94 | # was generated |
95 | with open(output) as f: | 95 | with open(output) as f: |
96 | for line in f: | 96 | for line in f: |
97 | if line[0] == '#' or line == '\n': | 97 | if line[0] == '#' or line == '\n': |
98 | continue | 98 | continue |
99 | defconfig = line.split()[6] + '_defconfig' | 99 | defconfig = line.split()[6] + '_defconfig' |
100 | if not os.path.exists(os.path.join(CONFIG_DIR, defconfig)): | 100 | if not os.path.exists(os.path.join(CONFIG_DIR, defconfig)): |
101 | return False | 101 | return False |
102 | 102 | ||
103 | return True | 103 | return True |
104 | 104 | ||
105 | ### classes ### | 105 | ### classes ### |
106 | class KconfigScanner: | 106 | class KconfigScanner: |
107 | 107 | ||
108 | """Kconfig scanner.""" | 108 | """Kconfig scanner.""" |
109 | 109 | ||
110 | ### constant variable only used in this class ### | 110 | ### constant variable only used in this class ### |
111 | _SYMBOL_TABLE = { | 111 | _SYMBOL_TABLE = { |
112 | 'arch' : 'SYS_ARCH', | 112 | 'arch' : 'SYS_ARCH', |
113 | 'cpu' : 'SYS_CPU', | 113 | 'cpu' : 'SYS_CPU', |
114 | 'soc' : 'SYS_SOC', | 114 | 'soc' : 'SYS_SOC', |
115 | 'vendor' : 'SYS_VENDOR', | 115 | 'vendor' : 'SYS_VENDOR', |
116 | 'board' : 'SYS_BOARD', | 116 | 'board' : 'SYS_BOARD', |
117 | 'config' : 'SYS_CONFIG_NAME', | 117 | 'config' : 'SYS_CONFIG_NAME', |
118 | 'options' : 'SYS_EXTRA_OPTIONS' | 118 | 'options' : 'SYS_EXTRA_OPTIONS' |
119 | } | 119 | } |
120 | 120 | ||
121 | def __init__(self): | 121 | def __init__(self): |
122 | """Scan all the Kconfig files and create a Config object.""" | 122 | """Scan all the Kconfig files and create a Config object.""" |
123 | # Define environment variables referenced from Kconfig | 123 | # Define environment variables referenced from Kconfig |
124 | os.environ['srctree'] = os.getcwd() | 124 | os.environ['srctree'] = os.getcwd() |
125 | os.environ['UBOOTVERSION'] = 'dummy' | 125 | os.environ['UBOOTVERSION'] = 'dummy' |
126 | os.environ['KCONFIG_OBJDIR'] = '' | 126 | os.environ['KCONFIG_OBJDIR'] = '' |
127 | self._conf = kconfiglib.Config() | 127 | self._conf = kconfiglib.Config(print_warnings=False) |
128 | 128 | ||
129 | def __del__(self): | 129 | def __del__(self): |
130 | """Delete a leftover temporary file before exit. | 130 | """Delete a leftover temporary file before exit. |
131 | 131 | ||
132 | The scan() method of this class creates a temporay file and deletes | 132 | The scan() method of this class creates a temporay file and deletes |
133 | it on success. If scan() method throws an exception on the way, | 133 | it on success. If scan() method throws an exception on the way, |
134 | the temporary file might be left over. In that case, it should be | 134 | the temporary file might be left over. In that case, it should be |
135 | deleted in this destructor. | 135 | deleted in this destructor. |
136 | """ | 136 | """ |
137 | if hasattr(self, '_tmpfile') and self._tmpfile: | 137 | if hasattr(self, '_tmpfile') and self._tmpfile: |
138 | try_remove(self._tmpfile) | 138 | try_remove(self._tmpfile) |
139 | 139 | ||
140 | def scan(self, defconfig): | 140 | def scan(self, defconfig): |
141 | """Load a defconfig file to obtain board parameters. | 141 | """Load a defconfig file to obtain board parameters. |
142 | 142 | ||
143 | Arguments: | 143 | Arguments: |
144 | defconfig: path to the defconfig file to be processed | 144 | defconfig: path to the defconfig file to be processed |
145 | 145 | ||
146 | Returns: | 146 | Returns: |
147 | A dictionary of board parameters. It has a form of: | 147 | A dictionary of board parameters. It has a form of: |
148 | { | 148 | { |
149 | 'arch': <arch_name>, | 149 | 'arch': <arch_name>, |
150 | 'cpu': <cpu_name>, | 150 | 'cpu': <cpu_name>, |
151 | 'soc': <soc_name>, | 151 | 'soc': <soc_name>, |
152 | 'vendor': <vendor_name>, | 152 | 'vendor': <vendor_name>, |
153 | 'board': <board_name>, | 153 | 'board': <board_name>, |
154 | 'target': <target_name>, | 154 | 'target': <target_name>, |
155 | 'config': <config_header_name>, | 155 | 'config': <config_header_name>, |
156 | 'options': <extra_options> | 156 | 'options': <extra_options> |
157 | } | 157 | } |
158 | """ | 158 | """ |
159 | # strip special prefixes and save it in a temporary file | 159 | # strip special prefixes and save it in a temporary file |
160 | fd, self._tmpfile = tempfile.mkstemp() | 160 | fd, self._tmpfile = tempfile.mkstemp() |
161 | with os.fdopen(fd, 'w') as f: | 161 | with os.fdopen(fd, 'w') as f: |
162 | for line in open(defconfig): | 162 | for line in open(defconfig): |
163 | colon = line.find(':CONFIG_') | 163 | colon = line.find(':CONFIG_') |
164 | if colon == -1: | 164 | if colon == -1: |
165 | f.write(line) | 165 | f.write(line) |
166 | else: | 166 | else: |
167 | f.write(line[colon + 1:]) | 167 | f.write(line[colon + 1:]) |
168 | 168 | ||
169 | self._conf.load_config(self._tmpfile) | 169 | warnings = self._conf.load_config(self._tmpfile) |
170 | if warnings: | ||
171 | for warning in warnings: | ||
172 | print '%s: %s' % (defconfig, warning) | ||
170 | 173 | ||
171 | try_remove(self._tmpfile) | 174 | try_remove(self._tmpfile) |
172 | self._tmpfile = None | 175 | self._tmpfile = None |
173 | 176 | ||
174 | params = {} | 177 | params = {} |
175 | 178 | ||
176 | # Get the value of CONFIG_SYS_ARCH, CONFIG_SYS_CPU, ... etc. | 179 | # Get the value of CONFIG_SYS_ARCH, CONFIG_SYS_CPU, ... etc. |
177 | # Set '-' if the value is empty. | 180 | # Set '-' if the value is empty. |
178 | for key, symbol in self._SYMBOL_TABLE.items(): | 181 | for key, symbol in self._SYMBOL_TABLE.items(): |
179 | value = self._conf.get_symbol(symbol).get_value() | 182 | value = self._conf.get_symbol(symbol).get_value() |
180 | if value: | 183 | if value: |
181 | params[key] = value | 184 | params[key] = value |
182 | else: | 185 | else: |
183 | params[key] = '-' | 186 | params[key] = '-' |
184 | 187 | ||
185 | defconfig = os.path.basename(defconfig) | 188 | defconfig = os.path.basename(defconfig) |
186 | params['target'], match, rear = defconfig.partition('_defconfig') | 189 | params['target'], match, rear = defconfig.partition('_defconfig') |
187 | assert match and not rear, '%s : invalid defconfig' % defconfig | 190 | assert match and not rear, '%s : invalid defconfig' % defconfig |
188 | 191 | ||
189 | # fix-up for aarch64 | 192 | # fix-up for aarch64 |
190 | if params['arch'] == 'arm' and params['cpu'] == 'armv8': | 193 | if params['arch'] == 'arm' and params['cpu'] == 'armv8': |
191 | params['arch'] = 'aarch64' | 194 | params['arch'] = 'aarch64' |
192 | 195 | ||
193 | # fix-up options field. It should have the form: | 196 | # fix-up options field. It should have the form: |
194 | # <config name>[:comma separated config options] | 197 | # <config name>[:comma separated config options] |
195 | if params['options'] != '-': | 198 | if params['options'] != '-': |
196 | params['options'] = params['config'] + ':' + \ | 199 | params['options'] = params['config'] + ':' + \ |
197 | params['options'].replace(r'\"', '"') | 200 | params['options'].replace(r'\"', '"') |
198 | elif params['config'] != params['target']: | 201 | elif params['config'] != params['target']: |
199 | params['options'] = params['config'] | 202 | params['options'] = params['config'] |
200 | 203 | ||
201 | return params | 204 | return params |
202 | 205 | ||
203 | def scan_defconfigs_for_multiprocess(queue, defconfigs): | 206 | def scan_defconfigs_for_multiprocess(queue, defconfigs): |
204 | """Scan defconfig files and queue their board parameters | 207 | """Scan defconfig files and queue their board parameters |
205 | 208 | ||
206 | This function is intended to be passed to | 209 | This function is intended to be passed to |
207 | multiprocessing.Process() constructor. | 210 | multiprocessing.Process() constructor. |
208 | 211 | ||
209 | Arguments: | 212 | Arguments: |
210 | queue: An instance of multiprocessing.Queue(). | 213 | queue: An instance of multiprocessing.Queue(). |
211 | The resulting board parameters are written into it. | 214 | The resulting board parameters are written into it. |
212 | defconfigs: A sequence of defconfig files to be scanned. | 215 | defconfigs: A sequence of defconfig files to be scanned. |
213 | """ | 216 | """ |
214 | kconf_scanner = KconfigScanner() | 217 | kconf_scanner = KconfigScanner() |
215 | for defconfig in defconfigs: | 218 | for defconfig in defconfigs: |
216 | queue.put(kconf_scanner.scan(defconfig)) | 219 | queue.put(kconf_scanner.scan(defconfig)) |
217 | 220 | ||
218 | def read_queues(queues, params_list): | 221 | def read_queues(queues, params_list): |
219 | """Read the queues and append the data to the paramers list""" | 222 | """Read the queues and append the data to the paramers list""" |
220 | for q in queues: | 223 | for q in queues: |
221 | while not q.empty(): | 224 | while not q.empty(): |
222 | params_list.append(q.get()) | 225 | params_list.append(q.get()) |
223 | 226 | ||
224 | def scan_defconfigs(jobs=1): | 227 | def scan_defconfigs(jobs=1): |
225 | """Collect board parameters for all defconfig files. | 228 | """Collect board parameters for all defconfig files. |
226 | 229 | ||
227 | This function invokes multiple processes for faster processing. | 230 | This function invokes multiple processes for faster processing. |
228 | 231 | ||
229 | Arguments: | 232 | Arguments: |
230 | jobs: The number of jobs to run simultaneously | 233 | jobs: The number of jobs to run simultaneously |
231 | """ | 234 | """ |
232 | all_defconfigs = [] | 235 | all_defconfigs = [] |
233 | for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): | 236 | for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): |
234 | for filename in fnmatch.filter(filenames, '*_defconfig'): | 237 | for filename in fnmatch.filter(filenames, '*_defconfig'): |
235 | if fnmatch.fnmatch(filename, '.*'): | 238 | if fnmatch.fnmatch(filename, '.*'): |
236 | continue | 239 | continue |
237 | all_defconfigs.append(os.path.join(dirpath, filename)) | 240 | all_defconfigs.append(os.path.join(dirpath, filename)) |
238 | 241 | ||
239 | total_boards = len(all_defconfigs) | 242 | total_boards = len(all_defconfigs) |
240 | processes = [] | 243 | processes = [] |
241 | queues = [] | 244 | queues = [] |
242 | for i in range(jobs): | 245 | for i in range(jobs): |
243 | defconfigs = all_defconfigs[total_boards * i / jobs : | 246 | defconfigs = all_defconfigs[total_boards * i / jobs : |
244 | total_boards * (i + 1) / jobs] | 247 | total_boards * (i + 1) / jobs] |
245 | q = multiprocessing.Queue(maxsize=-1) | 248 | q = multiprocessing.Queue(maxsize=-1) |
246 | p = multiprocessing.Process(target=scan_defconfigs_for_multiprocess, | 249 | p = multiprocessing.Process(target=scan_defconfigs_for_multiprocess, |
247 | args=(q, defconfigs)) | 250 | args=(q, defconfigs)) |
248 | p.start() | 251 | p.start() |
249 | processes.append(p) | 252 | processes.append(p) |
250 | queues.append(q) | 253 | queues.append(q) |
251 | 254 | ||
252 | # The resulting data should be accumulated to this list | 255 | # The resulting data should be accumulated to this list |
253 | params_list = [] | 256 | params_list = [] |
254 | 257 | ||
255 | # Data in the queues should be retrieved preriodically. | 258 | # Data in the queues should be retrieved preriodically. |
256 | # Otherwise, the queues would become full and subprocesses would get stuck. | 259 | # Otherwise, the queues would become full and subprocesses would get stuck. |
257 | while any([p.is_alive() for p in processes]): | 260 | while any([p.is_alive() for p in processes]): |
258 | read_queues(queues, params_list) | 261 | read_queues(queues, params_list) |
259 | # sleep for a while until the queues are filled | 262 | # sleep for a while until the queues are filled |
260 | time.sleep(SLEEP_TIME) | 263 | time.sleep(SLEEP_TIME) |
261 | 264 | ||
262 | # Joining subprocesses just in case | 265 | # Joining subprocesses just in case |
263 | # (All subprocesses should already have been finished) | 266 | # (All subprocesses should already have been finished) |
264 | for p in processes: | 267 | for p in processes: |
265 | p.join() | 268 | p.join() |
266 | 269 | ||
267 | # retrieve leftover data | 270 | # retrieve leftover data |
268 | read_queues(queues, params_list) | 271 | read_queues(queues, params_list) |
269 | 272 | ||
270 | return params_list | 273 | return params_list |
271 | 274 | ||
272 | class MaintainersDatabase: | 275 | class MaintainersDatabase: |
273 | 276 | ||
274 | """The database of board status and maintainers.""" | 277 | """The database of board status and maintainers.""" |
275 | 278 | ||
276 | def __init__(self): | 279 | def __init__(self): |
277 | """Create an empty database.""" | 280 | """Create an empty database.""" |
278 | self.database = {} | 281 | self.database = {} |
279 | 282 | ||
280 | def get_status(self, target): | 283 | def get_status(self, target): |
281 | """Return the status of the given board. | 284 | """Return the status of the given board. |
282 | 285 | ||
283 | The board status is generally either 'Active' or 'Orphan'. | 286 | The board status is generally either 'Active' or 'Orphan'. |
284 | Display a warning message and return '-' if status information | 287 | Display a warning message and return '-' if status information |
285 | is not found. | 288 | is not found. |
286 | 289 | ||
287 | Returns: | 290 | Returns: |
288 | 'Active', 'Orphan' or '-'. | 291 | 'Active', 'Orphan' or '-'. |
289 | """ | 292 | """ |
290 | if not target in self.database: | 293 | if not target in self.database: |
291 | print >> sys.stderr, "WARNING: no status info for '%s'" % target | 294 | print >> sys.stderr, "WARNING: no status info for '%s'" % target |
292 | return '-' | 295 | return '-' |
293 | 296 | ||
294 | tmp = self.database[target][0] | 297 | tmp = self.database[target][0] |
295 | if tmp.startswith('Maintained'): | 298 | if tmp.startswith('Maintained'): |
296 | return 'Active' | 299 | return 'Active' |
297 | elif tmp.startswith('Supported'): | 300 | elif tmp.startswith('Supported'): |
298 | return 'Active' | 301 | return 'Active' |
299 | elif tmp.startswith('Orphan'): | 302 | elif tmp.startswith('Orphan'): |
300 | return 'Orphan' | 303 | return 'Orphan' |
301 | else: | 304 | else: |
302 | print >> sys.stderr, ("WARNING: %s: unknown status for '%s'" % | 305 | print >> sys.stderr, ("WARNING: %s: unknown status for '%s'" % |
303 | (tmp, target)) | 306 | (tmp, target)) |
304 | return '-' | 307 | return '-' |
305 | 308 | ||
306 | def get_maintainers(self, target): | 309 | def get_maintainers(self, target): |
307 | """Return the maintainers of the given board. | 310 | """Return the maintainers of the given board. |
308 | 311 | ||
309 | Returns: | 312 | Returns: |
310 | Maintainers of the board. If the board has two or more maintainers, | 313 | Maintainers of the board. If the board has two or more maintainers, |
311 | they are separated with colons. | 314 | they are separated with colons. |
312 | """ | 315 | """ |
313 | if not target in self.database: | 316 | if not target in self.database: |
314 | print >> sys.stderr, "WARNING: no maintainers for '%s'" % target | 317 | print >> sys.stderr, "WARNING: no maintainers for '%s'" % target |
315 | return '' | 318 | return '' |
316 | 319 | ||
317 | return ':'.join(self.database[target][1]) | 320 | return ':'.join(self.database[target][1]) |
318 | 321 | ||
319 | def parse_file(self, file): | 322 | def parse_file(self, file): |
320 | """Parse a MAINTAINERS file. | 323 | """Parse a MAINTAINERS file. |
321 | 324 | ||
322 | Parse a MAINTAINERS file and accumulates board status and | 325 | Parse a MAINTAINERS file and accumulates board status and |
323 | maintainers information. | 326 | maintainers information. |
324 | 327 | ||
325 | Arguments: | 328 | Arguments: |
326 | file: MAINTAINERS file to be parsed | 329 | file: MAINTAINERS file to be parsed |
327 | """ | 330 | """ |
328 | targets = [] | 331 | targets = [] |
329 | maintainers = [] | 332 | maintainers = [] |
330 | status = '-' | 333 | status = '-' |
331 | for line in open(file): | 334 | for line in open(file): |
332 | # Check also commented maintainers | 335 | # Check also commented maintainers |
333 | if line[:3] == '#M:': | 336 | if line[:3] == '#M:': |
334 | line = line[1:] | 337 | line = line[1:] |
335 | tag, rest = line[:2], line[2:].strip() | 338 | tag, rest = line[:2], line[2:].strip() |
336 | if tag == 'M:': | 339 | if tag == 'M:': |
337 | maintainers.append(rest) | 340 | maintainers.append(rest) |
338 | elif tag == 'F:': | 341 | elif tag == 'F:': |
339 | # expand wildcard and filter by 'configs/*_defconfig' | 342 | # expand wildcard and filter by 'configs/*_defconfig' |
340 | for f in glob.glob(rest): | 343 | for f in glob.glob(rest): |
341 | front, match, rear = f.partition('configs/') | 344 | front, match, rear = f.partition('configs/') |
342 | if not front and match: | 345 | if not front and match: |
343 | front, match, rear = rear.rpartition('_defconfig') | 346 | front, match, rear = rear.rpartition('_defconfig') |
344 | if match and not rear: | 347 | if match and not rear: |
345 | targets.append(front) | 348 | targets.append(front) |
346 | elif tag == 'S:': | 349 | elif tag == 'S:': |
347 | status = rest | 350 | status = rest |
348 | elif line == '\n': | 351 | elif line == '\n': |
349 | for target in targets: | 352 | for target in targets: |
350 | self.database[target] = (status, maintainers) | 353 | self.database[target] = (status, maintainers) |
351 | targets = [] | 354 | targets = [] |
352 | maintainers = [] | 355 | maintainers = [] |
353 | status = '-' | 356 | status = '-' |
354 | if targets: | 357 | if targets: |
355 | for target in targets: | 358 | for target in targets: |
356 | self.database[target] = (status, maintainers) | 359 | self.database[target] = (status, maintainers) |
357 | 360 | ||
358 | def insert_maintainers_info(params_list): | 361 | def insert_maintainers_info(params_list): |
359 | """Add Status and Maintainers information to the board parameters list. | 362 | """Add Status and Maintainers information to the board parameters list. |
360 | 363 | ||
361 | Arguments: | 364 | Arguments: |
362 | params_list: A list of the board parameters | 365 | params_list: A list of the board parameters |
363 | """ | 366 | """ |
364 | database = MaintainersDatabase() | 367 | database = MaintainersDatabase() |
365 | for (dirpath, dirnames, filenames) in os.walk('.'): | 368 | for (dirpath, dirnames, filenames) in os.walk('.'): |
366 | if 'MAINTAINERS' in filenames: | 369 | if 'MAINTAINERS' in filenames: |
367 | database.parse_file(os.path.join(dirpath, 'MAINTAINERS')) | 370 | database.parse_file(os.path.join(dirpath, 'MAINTAINERS')) |
368 | 371 | ||
369 | for i, params in enumerate(params_list): | 372 | for i, params in enumerate(params_list): |
370 | target = params['target'] | 373 | target = params['target'] |
371 | params['status'] = database.get_status(target) | 374 | params['status'] = database.get_status(target) |
372 | params['maintainers'] = database.get_maintainers(target) | 375 | params['maintainers'] = database.get_maintainers(target) |
373 | params_list[i] = params | 376 | params_list[i] = params |
374 | 377 | ||
375 | def format_and_output(params_list, output): | 378 | def format_and_output(params_list, output): |
376 | """Write board parameters into a file. | 379 | """Write board parameters into a file. |
377 | 380 | ||
378 | Columnate the board parameters, sort lines alphabetically, | 381 | Columnate the board parameters, sort lines alphabetically, |
379 | and then write them to a file. | 382 | and then write them to a file. |
380 | 383 | ||
381 | Arguments: | 384 | Arguments: |
382 | params_list: The list of board parameters | 385 | params_list: The list of board parameters |
383 | output: The path to the output file | 386 | output: The path to the output file |
384 | """ | 387 | """ |
385 | FIELDS = ('status', 'arch', 'cpu', 'soc', 'vendor', 'board', 'target', | 388 | FIELDS = ('status', 'arch', 'cpu', 'soc', 'vendor', 'board', 'target', |
386 | 'options', 'maintainers') | 389 | 'options', 'maintainers') |
387 | 390 | ||
388 | # First, decide the width of each column | 391 | # First, decide the width of each column |
389 | max_length = dict([ (f, 0) for f in FIELDS]) | 392 | max_length = dict([ (f, 0) for f in FIELDS]) |
390 | for params in params_list: | 393 | for params in params_list: |
391 | for f in FIELDS: | 394 | for f in FIELDS: |
392 | max_length[f] = max(max_length[f], len(params[f])) | 395 | max_length[f] = max(max_length[f], len(params[f])) |
393 | 396 | ||
394 | output_lines = [] | 397 | output_lines = [] |
395 | for params in params_list: | 398 | for params in params_list: |
396 | line = '' | 399 | line = '' |
397 | for f in FIELDS: | 400 | for f in FIELDS: |
398 | # insert two spaces between fields like column -t would | 401 | # insert two spaces between fields like column -t would |
399 | line += ' ' + params[f].ljust(max_length[f]) | 402 | line += ' ' + params[f].ljust(max_length[f]) |
400 | output_lines.append(line.strip()) | 403 | output_lines.append(line.strip()) |
401 | 404 | ||
402 | # ignore case when sorting | 405 | # ignore case when sorting |
403 | output_lines.sort(key=str.lower) | 406 | output_lines.sort(key=str.lower) |
404 | 407 | ||
405 | with open(output, 'w') as f: | 408 | with open(output, 'w') as f: |
406 | f.write(COMMENT_BLOCK + '\n'.join(output_lines) + '\n') | 409 | f.write(COMMENT_BLOCK + '\n'.join(output_lines) + '\n') |
407 | 410 | ||
408 | def gen_boards_cfg(output, jobs=1, force=False): | 411 | def gen_boards_cfg(output, jobs=1, force=False): |
409 | """Generate a board database file. | 412 | """Generate a board database file. |
410 | 413 | ||
411 | Arguments: | 414 | Arguments: |
412 | output: The name of the output file | 415 | output: The name of the output file |
413 | jobs: The number of jobs to run simultaneously | 416 | jobs: The number of jobs to run simultaneously |
414 | force: Force to generate the output even if it is new | 417 | force: Force to generate the output even if it is new |
415 | """ | 418 | """ |
416 | check_top_directory() | 419 | check_top_directory() |
417 | 420 | ||
418 | if not force and output_is_new(output): | 421 | if not force and output_is_new(output): |
419 | print "%s is up to date. Nothing to do." % output | 422 | print "%s is up to date. Nothing to do." % output |
420 | sys.exit(0) | 423 | sys.exit(0) |
421 | 424 | ||
422 | params_list = scan_defconfigs(jobs) | 425 | params_list = scan_defconfigs(jobs) |
423 | insert_maintainers_info(params_list) | 426 | insert_maintainers_info(params_list) |
424 | format_and_output(params_list, output) | 427 | format_and_output(params_list, output) |
425 | 428 | ||
426 | def main(): | 429 | def main(): |
427 | try: | 430 | try: |
428 | cpu_count = multiprocessing.cpu_count() | 431 | cpu_count = multiprocessing.cpu_count() |
429 | except NotImplementedError: | 432 | except NotImplementedError: |
430 | cpu_count = 1 | 433 | cpu_count = 1 |
431 | 434 | ||
432 | parser = optparse.OptionParser() | 435 | parser = optparse.OptionParser() |
433 | # Add options here | 436 | # Add options here |
434 | parser.add_option('-f', '--force', action="store_true", default=False, | 437 | parser.add_option('-f', '--force', action="store_true", default=False, |
435 | help='regenerate the output even if it is new') | 438 | help='regenerate the output even if it is new') |
436 | parser.add_option('-j', '--jobs', type='int', default=cpu_count, | 439 | parser.add_option('-j', '--jobs', type='int', default=cpu_count, |
437 | help='the number of jobs to run simultaneously') | 440 | help='the number of jobs to run simultaneously') |
438 | parser.add_option('-o', '--output', default=OUTPUT_FILE, | 441 | parser.add_option('-o', '--output', default=OUTPUT_FILE, |
439 | help='output file [default=%s]' % OUTPUT_FILE) | 442 | help='output file [default=%s]' % OUTPUT_FILE) |
440 | (options, args) = parser.parse_args() | 443 | (options, args) = parser.parse_args() |
441 | 444 | ||
442 | gen_boards_cfg(options.output, jobs=options.jobs, force=options.force) | 445 | gen_boards_cfg(options.output, jobs=options.jobs, force=options.force) |
443 | 446 | ||
444 | if __name__ == '__main__': | 447 | if __name__ == '__main__': |
445 | main() | 448 | main() |
446 | 449 |
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102
-
mentioned in commit 4e1102