cmake: CMakeTraceParser improvements

- handle cached CMake variables differently
 - associate variables with source files
 - better performance (str to Path and generator expressions)
pull/8812/head
Daniel Mensinger 4 years ago
parent 25fa2d4f7b
commit 0e777e7c90
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 4
      mesonbuild/cmake/generator.py
  2. 54
      mesonbuild/cmake/traceparser.py

@ -23,6 +23,10 @@ def parse_generator_expressions(raw: str) -> str:
use cases. use cases.
''' '''
# Early abort if no generator expression present
if '$<' not in raw:
return raw
out = '' # type: str out = '' # type: str
i = 0 # type: int i = 0 # type: int

@ -22,21 +22,31 @@ from ..mesonlib import version_compare
import typing as T import typing as T
from pathlib import Path from pathlib import Path
from functools import lru_cache
import re import re
import json import json
import textwrap import textwrap
class CMakeTraceLine: class CMakeTraceLine:
def __init__(self, file: Path, line: int, func: str, args: T.List[str]) -> None: def __init__(self, file_str: str, line: int, func: str, args: T.List[str]) -> None:
self.file = file self.file = CMakeTraceLine._to_path(file_str)
self.line = line self.line = line
self.func = func.lower() self.func = func.lower()
self.args = args self.args = args
@staticmethod
@lru_cache(maxsize=None)
def _to_path(file_str: str) -> Path:
return Path(file_str)
def __repr__(self) -> str: def __repr__(self) -> str:
s = 'CMake TRACE: {0}:{1} {2}({3})' s = 'CMake TRACE: {0}:{1} {2}({3})'
return s.format(self.file, self.line, self.func, self.args) return s.format(self.file, self.line, self.func, self.args)
class CMakeCacheEntry(T.NamedTuple):
value: T.List[str]
type: str
class CMakeTarget: class CMakeTarget:
def __init__( def __init__(
self, self,
@ -81,8 +91,10 @@ class CMakeGeneratorTarget(CMakeTarget):
class CMakeTraceParser: class CMakeTraceParser:
def __init__(self, cmake_version: str, build_dir: Path, permissive: bool = True) -> None: def __init__(self, cmake_version: str, build_dir: Path, permissive: bool = True) -> None:
self.vars = {} # type: T.Dict[str, T.List[str]] self.vars: T.Dict[str, T.List[str]] = {}
self.targets = {} # type: T.Dict[str, CMakeTarget] self.vars_by_file: T.Dict[Path, T.Dict[str, T.List[str]]] = {}
self.targets: T.Dict[str, CMakeTarget] = {}
self.cache: T.Dict[str, CMakeCacheEntry] = {}
self.explicit_headers = set() # type: T.Set[Path] self.explicit_headers = set() # type: T.Set[Path]
@ -234,6 +246,14 @@ class CMakeTraceParser:
""" """
# DOC: https://cmake.org/cmake/help/latest/command/set.html # DOC: https://cmake.org/cmake/help/latest/command/set.html
cache_type = None
cache_force = 'FORCE' in tline.args
try:
cache_idx = tline.args.index('CACHE')
cache_type = tline.args[cache_idx + 1]
except (ValueError, IndexError):
pass
# 1st remove PARENT_SCOPE and CACHE from args # 1st remove PARENT_SCOPE and CACHE from args
args = [] args = []
for i in tline.args: for i in tline.args:
@ -256,12 +276,19 @@ class CMakeTraceParser:
identifier = args.pop(0) identifier = args.pop(0)
value = ' '.join(args) value = ' '.join(args)
# Write to the CMake cache instead
if cache_type:
# Honor how the CMake FORCE parameter works
if identifier not in self.cache or cache_force:
self.cache[identifier] = CMakeCacheEntry(value.split(';'), cache_type)
if not value: if not value:
# Same as unset # Same as unset
if identifier in self.vars: if identifier in self.vars:
del self.vars[identifier] del self.vars[identifier]
else: else:
self.vars[identifier] = value.split(';') self.vars[identifier] = value.split(';')
self.vars_by_file.setdefault(tline.file, {})[identifier] = value.split(';')
def _cmake_unset(self, tline: CMakeTraceLine) -> None: def _cmake_unset(self, tline: CMakeTraceLine) -> None:
# DOC: https://cmake.org/cmake/help/latest/command/unset.html # DOC: https://cmake.org/cmake/help/latest/command/unset.html
@ -437,17 +464,18 @@ class CMakeTraceParser:
if not value: if not value:
return return
def do_target(tgt: str) -> None: def do_target(t: str) -> None:
if i not in self.targets: if t not in self.targets:
return self._gen_exception('set_property', f'TARGET {i} not found', tline) return self._gen_exception('set_property', f'TARGET {t} not found', tline)
if identifier not in self.targets[i].properties: tgt = self.targets[t]
self.targets[i].properties[identifier] = [] if identifier not in tgt.properties:
tgt.properties[identifier] = []
if append: if append:
self.targets[i].properties[identifier] += value tgt.properties[identifier] += value
else: else:
self.targets[i].properties[identifier] = value tgt.properties[identifier] = value
def do_source(src: str) -> None: def do_source(src: str) -> None:
if identifier != 'HEADER_FILE_ONLY' or not self._str_to_bool(value): if identifier != 'HEADER_FILE_ONLY' or not self._str_to_bool(value):
@ -652,7 +680,7 @@ class CMakeTraceParser:
argl = args.split(' ') argl = args.split(' ')
argl = list(map(lambda x: x.strip(), argl)) argl = list(map(lambda x: x.strip(), argl))
yield CMakeTraceLine(Path(file), int(line), func, argl) yield CMakeTraceLine(file, int(line), func, argl)
def _lex_trace_json(self, trace: str) -> T.Generator[CMakeTraceLine, None, None]: def _lex_trace_json(self, trace: str) -> T.Generator[CMakeTraceLine, None, None]:
lines = trace.splitlines(keepends=False) lines = trace.splitlines(keepends=False)
@ -667,7 +695,7 @@ class CMakeTraceParser:
for j in args: for j in args:
assert isinstance(j, str) assert isinstance(j, str)
args = [parse_generator_expressions(x) for x in args] args = [parse_generator_expressions(x) for x in args]
yield CMakeTraceLine(Path(data['file']), data['line'], data['cmd'], args) yield CMakeTraceLine(data['file'], data['line'], data['cmd'], args)
def _flatten_args(self, args: T.List[str]) -> T.List[str]: def _flatten_args(self, args: T.List[str]) -> T.List[str]:
# Split lists in arguments # Split lists in arguments

Loading…
Cancel
Save