coredata: Introduce a class to act as the dependency cache
Handling the PKG_CONFIG_PATH variable in meson introduces a new problem for caching dependencies. We want to encode the pkg_config_path (or cross_pkg_config_path if we're cross compiling) to be part of the key, but we don't want to put that into the key for non-pkg-config dependencies to avoid spurious cache misses (since pkg_config_path isn't relevant to cmake, for example). However, on a cache lookup we can't know that a dependency is a pkg-config dependency until we've looked in the cache. My solution is a two layer cache, the first layer remains the same as before, the second layer is a dict-like object that encapsulates the dependency type information and uses pkg_config_path and cross_pkg_config_path as a sub key (and could be extended easily for other types). A new object type is introduced to encapsulate this so that callers don't need to be aware of the implementation details.
This commit is contained in:
parent
d941ca1b19
commit
5df6823bd8
|
@ -27,6 +27,11 @@ import ast
|
|||
import argparse
|
||||
import configparser
|
||||
from typing import Optional, Any, TypeVar, Generic, Type, List, Union
|
||||
import typing
|
||||
import enum
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from . import dependencies
|
||||
|
||||
version = '0.50.999'
|
||||
backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'vs2019', 'xcode']
|
||||
|
@ -221,6 +226,112 @@ def load_configs(filenames: List[str]) -> configparser.ConfigParser:
|
|||
return config
|
||||
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
CacheKeyType = typing.Tuple[typing.Tuple[typing.Any, ...], ...]
|
||||
SubCacheKeyType = typing.Tuple[typing.Any, ...]
|
||||
|
||||
|
||||
class DependencyCacheType(enum.Enum):
|
||||
|
||||
OTHER = 0
|
||||
PKG_CONFIG = 1
|
||||
|
||||
@classmethod
|
||||
def from_type(cls, dep: 'dependencies.Dependency') -> 'DependencyCacheType':
|
||||
from . import dependencies
|
||||
# As more types gain search overrides they'll need to be added here
|
||||
if isinstance(dep, dependencies.PkgConfigDependency):
|
||||
return cls.PKG_CONFIG
|
||||
return cls.OTHER
|
||||
|
||||
|
||||
class DependencySubCache:
|
||||
|
||||
def __init__(self, type_: DependencyCacheType):
|
||||
self.types = [type_]
|
||||
self.__cache = {} # type: typing.Dict[SubCacheKeyType, dependencies.Dependency]
|
||||
|
||||
def __getitem__(self, key: 'SubCacheKeyType') -> 'dependencies.Dependency':
|
||||
return self.__cache[key]
|
||||
|
||||
def __setitem__(self, key: 'SubCacheKeyType', value: 'dependencies.Dependency') -> None:
|
||||
self.__cache[key] = value
|
||||
|
||||
def __contains__(self, key: 'SubCacheKeyType') -> bool:
|
||||
return key in self.__cache
|
||||
|
||||
def values(self) -> typing.Iterable['dependencies.Dependency']:
|
||||
return self.__cache.values()
|
||||
|
||||
|
||||
class DependencyCache:
|
||||
|
||||
"""Class that stores a cache of dependencies.
|
||||
|
||||
This class is meant to encapsulate the fact that we need multiple keys to
|
||||
successfully lookup by providing a simple get/put interface.
|
||||
"""
|
||||
|
||||
def __init__(self, builtins: typing.Dict[str, UserOption[typing.Any]], cross: bool):
|
||||
self.__cache = OrderedDict() # type: typing.MutableMapping[CacheKeyType, DependencySubCache]
|
||||
self.__builtins = builtins
|
||||
self.__is_cross = cross
|
||||
|
||||
def __calculate_subkey(self, type_: DependencyCacheType) -> typing.Tuple[typing.Any, ...]:
|
||||
if type_ is DependencyCacheType.PKG_CONFIG:
|
||||
if self.__is_cross:
|
||||
return tuple(self.__builtins['cross_pkg_config_path'].value)
|
||||
return tuple(self.__builtins['pkg_config_path'].value)
|
||||
assert type_ is DependencyCacheType.OTHER, 'Someone forgot to update subkey calculations for a new type'
|
||||
return tuple()
|
||||
|
||||
def __iter__(self) -> typing.Iterator['CacheKeyType']:
|
||||
return self.keys()
|
||||
|
||||
def put(self, key: 'CacheKeyType', dep: 'dependencies.Dependency') -> None:
|
||||
t = DependencyCacheType.from_type(dep)
|
||||
if key not in self.__cache:
|
||||
self.__cache[key] = DependencySubCache(t)
|
||||
subkey = self.__calculate_subkey(t)
|
||||
self.__cache[key][subkey] = dep
|
||||
|
||||
def get(self, key: 'CacheKeyType') -> typing.Optional['dependencies.Dependency']:
|
||||
"""Get a value from the cache.
|
||||
|
||||
If there is no cache entry then None will be returned.
|
||||
"""
|
||||
try:
|
||||
val = self.__cache[key]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
for t in val.types:
|
||||
subkey = self.__calculate_subkey(t)
|
||||
try:
|
||||
return val[subkey]
|
||||
except KeyError:
|
||||
pass
|
||||
return None
|
||||
|
||||
def values(self) -> typing.Iterator['dependencies.Dependency']:
|
||||
for c in self.__cache.values():
|
||||
yield from c.values()
|
||||
|
||||
def keys(self) -> typing.Iterator['CacheKeyType']:
|
||||
return iter(self.__cache.keys())
|
||||
|
||||
def items(self) -> typing.Iterator[typing.Tuple['CacheKeyType', typing.List['dependencies.Dependency']]]:
|
||||
for k, v in self.__cache.items():
|
||||
vs = []
|
||||
for t in v.types:
|
||||
subkey = self.__calculate_subkey(t)
|
||||
if subkey in v:
|
||||
vs.append(v[subkey])
|
||||
yield k, vs
|
||||
|
||||
def clear(self) -> None:
|
||||
self.__cache.clear()
|
||||
|
||||
# This class contains all data that must persist over multiple
|
||||
# invocations of Meson. It is roughly the same thing as
|
||||
# cmakecache.
|
||||
|
|
|
@ -535,7 +535,7 @@ class Environment:
|
|||
self.coredata.meson_command = mesonlib.meson_command
|
||||
self.first_invocation = True
|
||||
|
||||
def is_cross_build(self):
|
||||
def is_cross_build(self) -> bool:
|
||||
return not self.machines.matches_build_machine(MachineChoice.HOST)
|
||||
|
||||
def dump_coredata(self):
|
||||
|
|
Loading…
Reference in New Issue