1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
# SPDX-License-Identifier: Apache-2.0
# Copyright 2013-2024 Contributors to the The Meson project
# Copyright © 2024 Intel Corporation
from __future__ import annotations
import typing as T
import configparser
import os
from . import mparser
from .cmdline import CmdLineFileParser
from .mesonlib import MesonException
if T.TYPE_CHECKING:
from .options import ElementaryOptionValues
class MachineFileParser():
def __init__(self, filenames: T.List[str], sourcedir: str) -> None:
self.parser = CmdLineFileParser()
self.constants: T.Dict[str, ElementaryOptionValues] = {'True': True, 'False': False}
self.sections: T.Dict[str, T.Dict[str, ElementaryOptionValues]] = {}
for fname in filenames:
try:
with open(fname, encoding='utf-8') as f:
content = f.read()
except UnicodeDecodeError as e:
raise MesonException(f'Malformed machine file {fname!r} failed to parse as unicode: {e}')
content = content.replace('@GLOBAL_SOURCE_ROOT@', sourcedir)
content = content.replace('@DIRNAME@', os.path.dirname(fname))
try:
self.parser.read_string(content, fname)
except configparser.Error as e:
raise MesonException(f'Malformed machine file: {e}')
# Parse [constants] first so they can be used in other sections
if self.parser.has_section('constants'):
self.constants.update(self._parse_section('constants'))
for s in self.parser.sections():
if s == 'constants':
continue
self.sections[s] = self._parse_section(s)
def _parse_section(self, s: str) -> T.Dict[str, ElementaryOptionValues]:
self.scope = self.constants.copy()
section: T.Dict[str, ElementaryOptionValues] = {}
for entry, value in self.parser.items(s):
if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry:
raise MesonException(f'Malformed variable name {entry!r} in machine file.')
# Windows paths...
value = value.replace('\\', '\\\\')
try:
ast = mparser.Parser(value, 'machinefile').parse()
if not ast.lines:
raise MesonException('value cannot be empty')
res = self._evaluate_statement(ast.lines[0])
except MesonException as e:
raise MesonException(f'Malformed value in machine file variable {entry!r}: {str(e)}.')
except KeyError as e:
raise MesonException(f'Undefined constant {e.args[0]!r} in machine file variable {entry!r}.')
section[entry] = res
self.scope[entry] = res
return section
def _evaluate_statement(self, node: mparser.BaseNode) -> ElementaryOptionValues:
if isinstance(node, (mparser.StringNode)):
return node.value
elif isinstance(node, mparser.BooleanNode):
return node.value
elif isinstance(node, mparser.NumberNode):
return node.value
elif isinstance(node, mparser.ParenthesizedNode):
return self._evaluate_statement(node.inner)
elif isinstance(node, mparser.ArrayNode):
a = [self._evaluate_statement(arg) for arg in node.args.arguments]
assert all(isinstance(s, str) for s in a), 'for mypy'
return T.cast('T.List[str]', a)
elif isinstance(node, mparser.IdNode):
return self.scope[node.value]
elif isinstance(node, mparser.ArithmeticNode):
l = self._evaluate_statement(node.left)
r = self._evaluate_statement(node.right)
if node.operation == 'add':
if isinstance(l, str) and isinstance(r, str):
return l + r
if isinstance(l, list) and isinstance(r, list):
return l + r
elif node.operation == 'div':
if isinstance(l, str) and isinstance(r, str):
return os.path.join(l, r)
raise MesonException('Unsupported node type')
def parse_machine_files(filenames: T.List[str], sourcedir: str) -> T.Dict[str, T.Dict[str, ElementaryOptionValues]]:
parser = MachineFileParser(filenames, sourcedir)
return parser.sections
class MachineFileStore:
def __init__(self, native_files: T.Optional[T.List[str]], cross_files: T.Optional[T.List[str]], source_dir: str):
self.native = parse_machine_files(native_files if native_files is not None else [], source_dir)
self.cross = parse_machine_files(cross_files if cross_files is not None else [], source_dir)
|