diff options
| author | Philip Lorenz <philip.lorenz@bmw.de> | 2024-10-30 10:26:43 +0100 |
|---|---|---|
| committer | Steve Sakoman <steve@sakoman.com> | 2024-11-15 06:05:32 -0800 |
| commit | 3aaed26728b2d8bcb66db94792fc080a37e2d9d3 (patch) | |
| tree | 3f3d9fcba6c7783a4ee9d831c73e8e0ab46e6477 | |
| parent | 51bd4260cb9592af4b63059a30f4f977e0a2fad6 (diff) | |
| download | poky-3aaed26728b2d8bcb66db94792fc080a37e2d9d3.tar.gz | |
bitbake: codeparser: Fix handling of string AST nodes with older Python versions
Commits 4591011449212c8e494ea42348acb2d27a82a51b and
6c19b6cf105ac321ec89da1a876a317020c45ab7 unconditionally changed
codeparser to rely on CPython 3.8 semantics. However, kirkstone
continues to support CPython versions >= 3.6.0 and as such string AST
nodes were no longer correctly identified.
Fix this by continuing to use `ast.Str` for Python versions < 3.8.0 and
only using the new code path for more recent versions. Detecting which
version of the AST API to use seems to be non-trivial so the Python
feature version is used instead.
Instances of this issue can be identified when executing bitbake with
debug logging:
while parsing MACHINE_ARCH, in call of d.getVar, argument
''TUNE_PKGARCH'' is not a string literal
As a consequence of these parsing issues, bitbake may assume that task
inputs haven't changed and as such erroneously reuse sstate objects when
it shouldn't.
(Bitbake rev: fb73c495c45d1d4107cfd60b67a5b4f11a99647b)
Signed-off-by: Philip Lorenz <philip.lorenz@bmw.de>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
| -rw-r--r-- | bitbake/lib/bb/codeparser.py | 46 |
1 files changed, 33 insertions, 13 deletions
diff --git a/bitbake/lib/bb/codeparser.py b/bitbake/lib/bb/codeparser.py index 6ce0c5182f..39dba266cd 100644 --- a/bitbake/lib/bb/codeparser.py +++ b/bitbake/lib/bb/codeparser.py | |||
| @@ -201,6 +201,22 @@ class DummyLogger(): | |||
| 201 | def flush(self): | 201 | def flush(self): |
| 202 | return | 202 | return |
| 203 | 203 | ||
| 204 | |||
| 205 | # Starting with Python 3.8, the ast module exposes all string nodes as a | ||
| 206 | # Constant. While earlier versions of the module also have the Constant type | ||
| 207 | # those use the Str type to encapsulate strings. | ||
| 208 | if sys.version_info < (3, 8): | ||
| 209 | def node_str_value(node): | ||
| 210 | if isinstance(node, ast.Str): | ||
| 211 | return node.s | ||
| 212 | return None | ||
| 213 | else: | ||
| 214 | def node_str_value(node): | ||
| 215 | if isinstance(node, ast.Constant) and isinstance(node.value, str): | ||
| 216 | return node.value | ||
| 217 | return None | ||
| 218 | |||
| 219 | |||
| 204 | class PythonParser(): | 220 | class PythonParser(): |
| 205 | getvars = (".getVar", ".appendVar", ".prependVar", "oe.utils.conditional") | 221 | getvars = (".getVar", ".appendVar", ".prependVar", "oe.utils.conditional") |
| 206 | getvarflags = (".getVarFlag", ".appendVarFlag", ".prependVarFlag") | 222 | getvarflags = (".getVarFlag", ".appendVarFlag", ".prependVarFlag") |
| @@ -225,19 +241,22 @@ class PythonParser(): | |||
| 225 | def visit_Call(self, node): | 241 | def visit_Call(self, node): |
| 226 | name = self.called_node_name(node.func) | 242 | name = self.called_node_name(node.func) |
| 227 | if name and (name.endswith(self.getvars) or name.endswith(self.getvarflags) or name in self.containsfuncs or name in self.containsanyfuncs): | 243 | if name and (name.endswith(self.getvars) or name.endswith(self.getvarflags) or name in self.containsfuncs or name in self.containsanyfuncs): |
| 228 | if isinstance(node.args[0], ast.Constant) and isinstance(node.args[0].value, str): | 244 | varname = node_str_value(node.args[0]) |
| 229 | varname = node.args[0].value | 245 | if varname is not None: |
| 230 | if name in self.containsfuncs and isinstance(node.args[1], ast.Constant): | 246 | arg_str_value = None |
| 247 | if len(node.args) >= 2: | ||
| 248 | arg_str_value = node_str_value(node.args[1]) | ||
| 249 | if name in self.containsfuncs and arg_str_value is not None: | ||
| 231 | if varname not in self.contains: | 250 | if varname not in self.contains: |
| 232 | self.contains[varname] = set() | 251 | self.contains[varname] = set() |
| 233 | self.contains[varname].add(node.args[1].value) | 252 | self.contains[varname].add(arg_str_value) |
| 234 | elif name in self.containsanyfuncs and isinstance(node.args[1], ast.Constant): | 253 | elif name in self.containsanyfuncs and arg_str_value is not None: |
| 235 | if varname not in self.contains: | 254 | if varname not in self.contains: |
| 236 | self.contains[varname] = set() | 255 | self.contains[varname] = set() |
| 237 | self.contains[varname].update(node.args[1].value.split()) | 256 | self.contains[varname].update(arg_str_value.split()) |
| 238 | elif name.endswith(self.getvarflags): | 257 | elif name.endswith(self.getvarflags): |
| 239 | if isinstance(node.args[1], ast.Constant): | 258 | if arg_str_value is not None: |
| 240 | self.references.add('%s[%s]' % (varname, node.args[1].value)) | 259 | self.references.add('%s[%s]' % (varname, arg_str_value)) |
| 241 | else: | 260 | else: |
| 242 | self.warn(node.func, node.args[1]) | 261 | self.warn(node.func, node.args[1]) |
| 243 | else: | 262 | else: |
| @@ -245,10 +264,10 @@ class PythonParser(): | |||
| 245 | else: | 264 | else: |
| 246 | self.warn(node.func, node.args[0]) | 265 | self.warn(node.func, node.args[0]) |
| 247 | elif name and name.endswith(".expand"): | 266 | elif name and name.endswith(".expand"): |
| 248 | if isinstance(node.args[0], ast.Constant): | 267 | arg_str_value = node_str_value(node.args[0]) |
| 249 | value = node.args[0].value | 268 | if arg_str_value is not None: |
| 250 | d = bb.data.init() | 269 | d = bb.data.init() |
| 251 | parser = d.expandWithRefs(value, self.name) | 270 | parser = d.expandWithRefs(arg_str_value, self.name) |
| 252 | self.references |= parser.references | 271 | self.references |= parser.references |
| 253 | self.execs |= parser.execs | 272 | self.execs |= parser.execs |
| 254 | for varname in parser.contains: | 273 | for varname in parser.contains: |
| @@ -256,8 +275,9 @@ class PythonParser(): | |||
| 256 | self.contains[varname] = set() | 275 | self.contains[varname] = set() |
| 257 | self.contains[varname] |= parser.contains[varname] | 276 | self.contains[varname] |= parser.contains[varname] |
| 258 | elif name in self.execfuncs: | 277 | elif name in self.execfuncs: |
| 259 | if isinstance(node.args[0], ast.Constant): | 278 | arg_str_value = node_str_value(node.args[0]) |
| 260 | self.var_execs.add(node.args[0].value) | 279 | if arg_str_value is not None: |
| 280 | self.var_execs.add(arg_str_value) | ||
| 261 | else: | 281 | else: |
| 262 | self.warn(node.func, node.args[0]) | 282 | self.warn(node.func, node.args[0]) |
| 263 | elif name and isinstance(node.func, (ast.Name, ast.Attribute)): | 283 | elif name and isinstance(node.func, (ast.Name, ast.Attribute)): |
