mirror of
https://github.com/ARM-software/devlib.git
synced 2025-02-25 05:57:51 +00:00
target: Introduce TypedKernelConfig
Maps Kconfig types to appropriate Python types, and act as a regular mapping with extended API: * tristate and bool values mapped to an Enum * int values to int * hex values to HexInt subclass of int that defaults to parsing and printing in hex format. Implement KernelConfig as a shim on top of TypedKernelConfig so they share most of the code. Code needing a TypedKernelConfig from a KernelConfig producer such as Target.config can trivially access the `typed_config` attribute of KernelConfig objects.
This commit is contained in:
parent
5b51c2644e
commit
f65130b7c7
232
devlib/target.py
232
devlib/target.py
@ -30,6 +30,14 @@ import xml.dom.minidom
|
|||||||
import copy
|
import copy
|
||||||
from collections import namedtuple, defaultdict
|
from collections import namedtuple, defaultdict
|
||||||
from pipes import quote
|
from pipes import quote
|
||||||
|
from past.types import basestring
|
||||||
|
from numbers import Number
|
||||||
|
try:
|
||||||
|
from collections.abc import Mapping
|
||||||
|
except ImportError:
|
||||||
|
from collections import Mapping
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
from devlib.host import LocalConnection, PACKAGE_BIN_DIRECTORY
|
from devlib.host import LocalConnection, PACKAGE_BIN_DIRECTORY
|
||||||
from devlib.module import get_module
|
from devlib.module import get_module
|
||||||
@ -1763,8 +1771,56 @@ class KernelVersion(object):
|
|||||||
__repr__ = __str__
|
__repr__ = __str__
|
||||||
|
|
||||||
|
|
||||||
class KernelConfig(object):
|
class HexInt(int):
|
||||||
|
"""
|
||||||
|
Subclass of :class:`int` that uses hexadecimal formatting by default.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __new__(cls, val=0, base=16):
|
||||||
|
super_new = super(HexInt, cls).__new__
|
||||||
|
if isinstance(val, Number):
|
||||||
|
return super_new(cls, val)
|
||||||
|
else:
|
||||||
|
return super_new(cls, val, base=base)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return hex(self)
|
||||||
|
|
||||||
|
|
||||||
|
class KernelConfigTristate(Enum):
|
||||||
|
YES = 'y'
|
||||||
|
NO = 'n'
|
||||||
|
MODULE = 'm'
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
"""
|
||||||
|
Allow using this enum to represent bool Kconfig type, although it is
|
||||||
|
technically different from tristate.
|
||||||
|
"""
|
||||||
|
return self in (self.YES, self.MODULE)
|
||||||
|
|
||||||
|
def __nonzero__(self):
|
||||||
|
"""
|
||||||
|
For Python 2.x compatibility.
|
||||||
|
"""
|
||||||
|
return self.__bool__()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_str(cls, str_):
|
||||||
|
for state in cls:
|
||||||
|
if state.value == str_:
|
||||||
|
return state
|
||||||
|
raise ValueError('No kernel config tristate value matches "{}"'.format(str_))
|
||||||
|
|
||||||
|
|
||||||
|
class TypedKernelConfig(Mapping):
|
||||||
|
"""
|
||||||
|
Mapping-like typed version of :class:`KernelConfig`.
|
||||||
|
|
||||||
|
Values are either :class:`str`, :class:`int`,
|
||||||
|
:class:`KernelConfigTristate`, or :class:`HexInt`. ``hex`` Kconfig type is
|
||||||
|
mapped to :class:`HexInt` and ``bool`` to :class:`KernelConfigTristate`.
|
||||||
|
"""
|
||||||
not_set_regex = re.compile(r'# (\S+) is not set')
|
not_set_regex = re.compile(r'# (\S+) is not set')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -1774,17 +1830,93 @@ class KernelConfig(object):
|
|||||||
name = 'CONFIG_' + name
|
name = 'CONFIG_' + name
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def iteritems(self):
|
def __init__(self, mapping=None):
|
||||||
return iter(self._config.items())
|
mapping = mapping if mapping is not None else {}
|
||||||
|
self._config = {
|
||||||
|
# Ensure we use the canonical name of the config keys for internal
|
||||||
|
# representation
|
||||||
|
self.get_config_name(k): v
|
||||||
|
for k, v in dict(mapping).items()
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, text):
|
@classmethod
|
||||||
self.text = text
|
def from_str(cls, text):
|
||||||
self._config = self._parse_text(text)
|
"""
|
||||||
|
Build a :class:`TypedKernelConfig` out of the string content of a
|
||||||
|
Kconfig file.
|
||||||
|
"""
|
||||||
|
return cls(cls._parse_text(text))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _val_to_str(val):
|
||||||
|
"Convert back values to Kconfig-style string value"
|
||||||
|
# Special case the gracefully handle the output of get()
|
||||||
|
if val is None:
|
||||||
|
return None
|
||||||
|
elif isinstance(val, KernelConfigTristate):
|
||||||
|
return val.value
|
||||||
|
elif isinstance(val, basestring):
|
||||||
|
return '"{}"'.format(val)
|
||||||
|
else:
|
||||||
|
return str(val)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '\n'.join(
|
||||||
|
'{}={}'.format(k, self._val_to_str(v))
|
||||||
|
for k, v in self.items()
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parse_val(k, v):
|
||||||
|
"""
|
||||||
|
Parse a value of types handled by Kconfig:
|
||||||
|
* string
|
||||||
|
* bool
|
||||||
|
* tristate
|
||||||
|
* hex
|
||||||
|
* int
|
||||||
|
|
||||||
|
Since bool cannot be distinguished from tristate, tristate is
|
||||||
|
always used. :meth:`KernelConfigTristate.__bool__` will allow using
|
||||||
|
it as a bool though, so it should not impact user code.
|
||||||
|
"""
|
||||||
|
if not v:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Handle "string" type
|
||||||
|
if v.startswith('"'):
|
||||||
|
# Strip enclosing "
|
||||||
|
return v[1:-1]
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
# Handles "bool" and "tristate" types
|
||||||
|
return KernelConfigTristate.from_str(v)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Handles "int" type
|
||||||
|
return int(v)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Handles "hex" type
|
||||||
|
return HexInt(v)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# If no type could be parsed
|
||||||
|
raise ValueError('Could not parse Kconfig key: {}={}'.format(
|
||||||
|
k, v
|
||||||
|
), k, v
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _parse_text(cls, text):
|
def _parse_text(cls, text):
|
||||||
config = {}
|
config = {}
|
||||||
for line in text.split('\n'):
|
for line in text.splitlines():
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
|
||||||
# skip empty lines
|
# skip empty lines
|
||||||
@ -1802,40 +1934,96 @@ class KernelConfig(object):
|
|||||||
name, value = line.split('=', 1)
|
name, value = line.split('=', 1)
|
||||||
|
|
||||||
name = cls.get_config_name(name.strip())
|
name = cls.get_config_name(name.strip())
|
||||||
config[name] = value.strip()
|
value = cls._parse_val(name, value.strip())
|
||||||
|
config[name] = value
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def get(self, name, strict=False):
|
def __getitem__(self, name):
|
||||||
name = self.get_config_name(name)
|
name = self.get_config_name(name)
|
||||||
res = self._config.get(name)
|
try:
|
||||||
|
return self._config[name]
|
||||||
if not res and strict:
|
except KeyError:
|
||||||
raise KernelConfigKeyError(
|
raise KernelConfigKeyError(
|
||||||
"{} is not exposed in kernel config".format(name),
|
"{} is not exposed in kernel config".format(name),
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
|
|
||||||
return self._config.get(name)
|
def __iter__(self):
|
||||||
|
return iter(self._config)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._config)
|
||||||
|
|
||||||
|
def __contains__(self, name):
|
||||||
|
name = self.get_config_name(name)
|
||||||
|
return name in self._config
|
||||||
|
|
||||||
def like(self, name):
|
def like(self, name):
|
||||||
regex = re.compile(name, re.I)
|
regex = re.compile(name, re.I)
|
||||||
result = {}
|
return {
|
||||||
for k, v in self._config.items():
|
k: v for k, v in self.items()
|
||||||
if regex.search(k):
|
if regex.search(k)
|
||||||
result[k] = v
|
}
|
||||||
return result
|
|
||||||
|
|
||||||
def is_enabled(self, name):
|
def is_enabled(self, name):
|
||||||
return self.get(name) == 'y'
|
return self.get(name) is KernelConfigTristate.YES
|
||||||
|
|
||||||
def is_module(self, name):
|
def is_module(self, name):
|
||||||
return self.get(name) == 'm'
|
return self.get(name) is KernelConfigTristate.MODULE
|
||||||
|
|
||||||
def is_not_set(self, name):
|
def is_not_set(self, name):
|
||||||
return self.get(name) == 'n'
|
return self.get(name) is KernelConfigTristate.NO
|
||||||
|
|
||||||
def has(self, name):
|
def has(self, name):
|
||||||
return self.get(name) in ['m', 'y']
|
return self.is_enabled(name) or self.is_module(name)
|
||||||
|
|
||||||
|
|
||||||
|
class KernelConfig(object):
|
||||||
|
"""
|
||||||
|
Backward compatibility shim on top of :class:`TypedKernelConfig`.
|
||||||
|
|
||||||
|
This class does not provide a Mapping API and only return string values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, text):
|
||||||
|
# Expose typed_config as a non-private attribute, so that user code
|
||||||
|
# needing it can get it from any existing producer of KernelConfig.
|
||||||
|
self.typed_config = TypedKernelConfig.from_str(text)
|
||||||
|
# Expose the original text for backward compatibility
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
get_config_name = TypedKernelConfig.get_config_name
|
||||||
|
not_set_regex = TypedKernelConfig.not_set_regex
|
||||||
|
|
||||||
|
def iteritems(self):
|
||||||
|
for k, v in self.typed_config.items():
|
||||||
|
yield (k, self.typed_config._val_to_str(v))
|
||||||
|
|
||||||
|
def get(self, name, strict=False):
|
||||||
|
if strict:
|
||||||
|
val = self.typed_config[name]
|
||||||
|
else:
|
||||||
|
val = self.typed_config.get(name)
|
||||||
|
|
||||||
|
return self.typed_config._val_to_str(val)
|
||||||
|
|
||||||
|
def like(self, name):
|
||||||
|
return {
|
||||||
|
k: self.typed_config._val_to_str(v)
|
||||||
|
for k, v in self.typed_config.like(name).items()
|
||||||
|
}
|
||||||
|
|
||||||
|
def is_enabled(self, name):
|
||||||
|
return self.typed_config.is_enabled(name)
|
||||||
|
|
||||||
|
def is_module(self, name):
|
||||||
|
return self.typed_config.is_module(name)
|
||||||
|
|
||||||
|
def is_not_set(self, name):
|
||||||
|
return self.typed_config.is_not_set(name)
|
||||||
|
|
||||||
|
def has(self, name):
|
||||||
|
return self.typed_config.has(name)
|
||||||
|
|
||||||
|
|
||||||
class LocalLinuxTarget(LinuxTarget):
|
class LocalLinuxTarget(LinuxTarget):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user