aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/coredata.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/coredata.py')
-rw-r--r--mesonbuild/coredata.py101
1 files changed, 5 insertions, 96 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 76da0b6..782e770 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -6,7 +6,7 @@ from __future__ import annotations
import copy
-from . import mlog, mparser, options
+from . import mlog, options
import pickle, os, uuid
import sys
from itertools import chain
@@ -16,14 +16,16 @@ from dataclasses import dataclass
from .mesonlib import (
MesonBugException,
- MesonException, EnvironmentException, MachineChoice, PerMachine,
+ MesonException, MachineChoice, PerMachine,
PerMachineDefaultable,
OptionKey, OptionType, stringlistify,
pickle_load
)
+
+from .machinefile import CmdLineFileParser
+
import ast
import argparse
-import configparser
import enum
import shlex
import typing as T
@@ -760,99 +762,6 @@ class CoreData:
mlog.warning('Base option \'b_bitcode\' is enabled, which is incompatible with many linker options. Incompatible options such as \'b_asneeded\' have been disabled.', fatal=False)
mlog.warning('Please see https://mesonbuild.com/Builtin-options.html#Notes_about_Apple_Bitcode_support for more details.', fatal=False)
-class CmdLineFileParser(configparser.ConfigParser):
- def __init__(self) -> None:
- # We don't want ':' as key delimiter, otherwise it would break when
- # storing subproject options like "subproject:option=value"
- super().__init__(delimiters=['='], interpolation=None)
-
- def read(self, filenames: T.Union['StrOrBytesPath', T.Iterable['StrOrBytesPath']], encoding: T.Optional[str] = 'utf-8') -> T.List[str]:
- return super().read(filenames, encoding)
-
- def optionxform(self, optionstr: str) -> str:
- # Don't call str.lower() on keys
- return optionstr
-
-class MachineFileParser():
- def __init__(self, filenames: T.List[str], sourcedir: str) -> None:
- self.parser = CmdLineFileParser()
- self.constants: T.Dict[str, T.Union[str, bool, int, T.List[str]]] = {'True': True, 'False': False}
- self.sections: T.Dict[str, T.Dict[str, T.Union[str, bool, int, T.List[str]]]] = {}
-
- for fname in filenames:
- try:
- with open(fname, encoding='utf-8') as f:
- content = f.read()
- except UnicodeDecodeError as e:
- raise EnvironmentException(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 EnvironmentException(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, T.Union[str, bool, int, T.List[str]]]:
- self.scope = self.constants.copy()
- section: T.Dict[str, T.Union[str, bool, int, T.List[str]]] = {}
- for entry, value in self.parser.items(s):
- if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry:
- raise EnvironmentException(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 EnvironmentException('value cannot be empty')
- res = self._evaluate_statement(ast.lines[0])
- except MesonException as e:
- raise EnvironmentException(f'Malformed value in machine file variable {entry!r}: {str(e)}.')
- except KeyError as e:
- raise EnvironmentException(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) -> T.Union[str, bool, int, T.List[str]]:
- 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):
- # TODO: This is where recursive types would come in handy
- return [self._evaluate_statement(arg) for arg in node.args.arguments]
- 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)) or \
- (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 EnvironmentException('Unsupported node type')
-
-def parse_machine_files(filenames: T.List[str], sourcedir: str):
- parser = MachineFileParser(filenames, sourcedir)
- return parser.sections
-
def get_cmd_line_file(build_dir: str) -> str:
return os.path.join(build_dir, 'meson-private', 'cmd_line.txt')