e4921fda5b
This implementation consists of two components: - Type creation python modules, whose job it is to construct objects of the defined type for a given variable in the metadata - typecheck.bbclass, which iterates over all configuration variables with a type defined and uses oe.types to check the validity of the values This gives us a few benefits: - Automatic sanity checking of all configuration variables with a defined type - Avoid duplicating the "how do I make use of the value of this variable" logic between its users. For variables like PATH, this is simply a split(), for boolean variables, the duplication can result in confusing, or even mismatched semantics (is this 0/1, empty/nonempty, what?) - Make it easier to create a configuration UI, as the type information could be used to provide a better interface than a text edit box (e.g checkbox for 'boolean', dropdown for 'choice') This functionality is entirely opt-in right now. To enable the configuration variable type checking, simply INHERIT += "typecheck". Example of a failing type check: BAZ = "foo" BAZ[type] = "boolean" $ bitbake -p FATAL: BAZ: Invalid boolean value 'foo' $ Examples of leveraging oe.types in a python snippet: PACKAGES[type] = "list" python () { import oe.data for pkg in oe.data.typed_value("PACKAGES", d): bb.note("package: %s" % pkg) } LIBTOOL_HAS_SYSROOT = "yes" LIBTOOL_HAS_SYSROOT[type] = "boolean" python () { import oe.data assert(oe.data.typed_value("LIBTOOL_HAS_SYSROOT", d) == True) } (From OE-Core rev: a04ce490e933fc7534db33f635b025c25329c564) Signed-off-by: Chris Larson <chris_larson@mentor.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
101 lines
2.8 KiB
Python
101 lines
2.8 KiB
Python
"""OpenEmbedded variable typing support
|
|
|
|
Types are defined in the metadata by name, using the 'type' flag on a
|
|
variable. Other flags may be utilized in the construction of the types. See
|
|
the arguments of the type's factory for details.
|
|
"""
|
|
|
|
import bb
|
|
import inspect
|
|
import types
|
|
|
|
available_types = {}
|
|
|
|
class MissingFlag(TypeError):
|
|
"""A particular flag is required to construct the type, but has not been
|
|
provided."""
|
|
def __init__(self, flag, type):
|
|
self.flag = flag
|
|
self.type = type
|
|
TypeError.__init__(self)
|
|
|
|
def __str__(self):
|
|
return "Type '%s' requires flag '%s'" % (self.type, self.flag)
|
|
|
|
def factory(var_type):
|
|
"""Return the factory for a specified type."""
|
|
if var_type is None:
|
|
raise TypeError("No type specified. Valid types: %s" %
|
|
', '.join(available_types))
|
|
try:
|
|
return available_types[var_type]
|
|
except KeyError:
|
|
raise TypeError("Invalid type '%s':\n Valid types: %s" %
|
|
(var_type, ', '.join(available_types)))
|
|
|
|
def create(value, var_type, **flags):
|
|
"""Create an object of the specified type, given the specified flags and
|
|
string value."""
|
|
obj = factory(var_type)
|
|
objflags = {}
|
|
for flag in obj.flags:
|
|
if flag not in flags:
|
|
if flag not in obj.optflags:
|
|
raise MissingFlag(flag, var_type)
|
|
else:
|
|
objflags[flag] = flags[flag]
|
|
|
|
return obj(value, **objflags)
|
|
|
|
def get_callable_args(obj):
|
|
"""Grab all but the first argument of the specified callable, returning
|
|
the list, as well as a list of which of the arguments have default
|
|
values."""
|
|
if type(obj) is type:
|
|
obj = obj.__init__
|
|
|
|
args, varargs, keywords, defaults = inspect.getargspec(obj)
|
|
flaglist = []
|
|
if args:
|
|
if len(args) > 1 and args[0] == 'self':
|
|
args = args[1:]
|
|
flaglist.extend(args)
|
|
|
|
optional = set()
|
|
if defaults:
|
|
optional |= set(flaglist[-len(defaults):])
|
|
return flaglist, optional
|
|
|
|
def factory_setup(name, obj):
|
|
"""Prepare a factory for use."""
|
|
args, optional = get_callable_args(obj)
|
|
extra_args = args[1:]
|
|
if extra_args:
|
|
obj.flags, optional = extra_args, optional
|
|
obj.optflags = set(optional)
|
|
else:
|
|
obj.flags = obj.optflags = ()
|
|
|
|
if not hasattr(obj, 'name'):
|
|
obj.name = name
|
|
|
|
def register(name, factory):
|
|
"""Register a type, given its name and a factory callable.
|
|
|
|
Determines the required and optional flags from the factory's
|
|
arguments."""
|
|
factory_setup(name, factory)
|
|
available_types[factory.name] = factory
|
|
|
|
|
|
# Register all our included types
|
|
for name in dir(types):
|
|
if name.startswith('_'):
|
|
continue
|
|
|
|
obj = getattr(types, name)
|
|
if not callable(obj):
|
|
continue
|
|
|
|
register(name, obj)
|