diff options
author | Chris Larson <chris_larson@mentor.com> | 2010-12-28 09:21:19 -0700 |
---|---|---|
committer | Chris Larson <chris_larson@mentor.com> | 2010-12-28 09:21:19 -0700 |
commit | 199722d4333f4b0ec64ef2cb93ab681115dfc3d0 (patch) | |
tree | 713159c4c5bdf8e2892386c0f8bc5c8aaa94ac34 | |
parent | 4247d8ca5b818a9b0409667d67b534cead00cb69 (diff) | |
parent | d0206a116e96d9ab74cc637caa2543da22d16ac1 (diff) | |
download | openembedded-199722d4333f4b0ec64ef2cb93ab681115dfc3d0.tar.gz |
Merge branch 'typing'
* typing:
base: start leveraging oe.types
Add flags for variable typing
Implement variable typing
-rw-r--r-- | classes/base.bbclass | 29 | ||||
-rw-r--r-- | classes/image.bbclass | 3 | ||||
-rw-r--r-- | classes/patch.bbclass | 5 | ||||
-rw-r--r-- | classes/typecheck.bbclass | 12 | ||||
-rw-r--r-- | conf/bitbake.conf | 36 | ||||
-rw-r--r-- | lib/oe/_types.py | 59 | ||||
-rw-r--r-- | lib/oe/test_types.py | 56 | ||||
-rw-r--r-- | lib/oe/types.py | 97 |
8 files changed, 283 insertions, 14 deletions
diff --git a/classes/base.bbclass b/classes/base.bbclass index 25520786a9..c76b77ddef 100644 --- a/classes/base.bbclass +++ b/classes/base.bbclass @@ -10,11 +10,13 @@ inherit utils inherit utility-tasks inherit metadata_scm -OE_IMPORTS += "oe.path oe.utils oe.packagegroup sys os time" +OE_IMPORTS += "oe.path oe.utils oe.packagegroup oe.types sys os time" +OE_IMPORTS[type] = "list" python oe_import () { if isinstance(e, bb.event.ConfigParsed): import os, sys + bbpath = e.data.getVar("BBPATH", True).split(":") sys.path[0:0] = [os.path.join(dir, "lib") for dir in bbpath] @@ -86,6 +88,7 @@ DEPENDS_virtclass-nativesdk_prepend="${@base_deps(d)} " SCENEFUNCS += "base_scenefunction" +SCENEFUNCS[type] = "list" python base_scenefunction () { stamp = bb.data.getVar('STAMP', d, 1) + ".needclean" @@ -94,8 +97,8 @@ python base_scenefunction () { } python base_do_setscene () { - for f in (bb.data.getVar('SCENEFUNCS', d, 1) or '').split(): - bb.build.exec_func(f, d) + for func in oe.types.value('SCENEFUNCS', d): + bb.build.exec_func(func, d) if not os.path.exists(bb.data.getVar('STAMP', d, 1) + ".do_setscene"): bb.build.make_stamp("do_setscene", d) } @@ -110,12 +113,11 @@ python base_do_fetch() { localdata = bb.data.createCopy(d) bb.data.update_data(localdata) - src_uri = bb.data.getVar('SRC_URI', localdata, 1) + src_uri = oe.types.value('SRC_URI', localdata) if not src_uri: return 1 - try: - bb.fetch.init(src_uri.split(),d) + bb.fetch.init(src_uri, d) except bb.fetch.NoMethodError: (type, value, traceback) = sys.exc_info() raise bb.build.FuncFailed("No method: %s" % value) @@ -144,7 +146,7 @@ python base_do_fetch() { # Check each URI first_uri = True - for url in src_uri.split(): + for url in src_uri: localpath = bb.data.expand(bb.fetch.localpath(url, localdata), localdata) (type,host,path,_,_,params) = bb.decodeurl(url) uri = "%s://%s%s" % (type,host,path) @@ -202,13 +204,13 @@ do_unpack[dirs] = "${WORKDIR}" python base_do_unpack() { from glob import glob - src_uri = d.getVar("SRC_URI", True) + src_uri = oe.types.value("SRC_URI", d) if not src_uri: return - srcurldata = bb.fetch.init(src_uri.split(), d, True) - filespath = d.getVar("FILESPATH", True).split(":") + srcurldata = bb.fetch.init(src_uri, d, True) + filespath = oe.types.value("FILESPATH", d) - for url in src_uri.split(): + for url in src_uri: urldata = srcurldata[url] if urldata.type == "file" and "*" in urldata.path: # The fetch code doesn't know how to handle globs, so @@ -266,7 +268,7 @@ python build_summary() { statusmsg = "\n%s\n%s\n" % (bb.data.getVar("BUILDCFG_HEADER", e.data, 1), "\n".join(statuslines)) bb.plain(statusmsg) - needed_vars = bb.data.getVar("BUILDCFG_NEEDEDVARS", e.data, 1).split() + needed_vars = oe.types.value("BUILDCFG_NEEDEDVARS", e.data) pesteruser = [] for v in needed_vars: val = bb.data.getVar(v, e.data, 1) @@ -331,8 +333,7 @@ def set_multimach_arch(d): multiarch = pkg_arch - packages = bb.data.getVar('PACKAGES', d, 1).split() - for pkg in packages: + for pkg in oe.types.value('PACKAGES', d): pkgarch = bb.data.getVar("PACKAGE_ARCH_%s" % pkg, d, 1) # We could look for != PACKAGE_ARCH here but how to choose diff --git a/classes/image.bbclass b/classes/image.bbclass index d98ba5cbba..7c1f3e1df2 100644 --- a/classes/image.bbclass +++ b/classes/image.bbclass @@ -31,6 +31,7 @@ IMAGE_BOOT ?= "${IMAGE_INITSCRIPTS} \ # some default locales IMAGE_LINGUAS ?= "de-de fr-fr en-gb" +IMAGE_LINGUAS[type] = "list" LINGUAS_INSTALL = "" LINGUAS_INSTALL_linux = "${@base_ifelse(d.getVar('IMAGE_LINGUAS', True), \ @@ -44,6 +45,7 @@ RDEPENDS += "${@' '.join(oe.packagegroup.active_packages('${IMAGE_FEATURES}'.spl IMAGE_FEATURES ?= "" +IMAGE_FEATURES[type] = "list" IMAGE_FEATURES_prepend = "image_base " # Define our always included package group @@ -85,6 +87,7 @@ do_rootfs[recrdeptask] += "do_deploy do_populate_sysroot" EXCLUDE_FROM_WORLD = "1" USE_DEVFS ?= "0" +USE_DEVFS[type] = "boolean" PID = "${@os.getpid()}" diff --git a/classes/patch.bbclass b/classes/patch.bbclass index 7629e9a15a..bc52ae7e16 100644 --- a/classes/patch.bbclass +++ b/classes/patch.bbclass @@ -5,6 +5,11 @@ QUILTRCFILE ?= "${STAGING_BINDIR_NATIVE}/quiltrc" PATCHDEPENDENCY = "${PATCHTOOL}-native:do_populate_sysroot" +PATCHTOOL[type] = "choice" +PATCHTOOL[choices] = "patch quilt git" +PATCHRESOLVE[type] = "choice" +PATCHRESOLVE[choices] = "noop user" + python patch_do_patch() { import oe.patch import oe.unpack diff --git a/classes/typecheck.bbclass b/classes/typecheck.bbclass new file mode 100644 index 0000000000..646cd4eed2 --- /dev/null +++ b/classes/typecheck.bbclass @@ -0,0 +1,12 @@ +# Check types of bitbake configuration variables +# +# See oe.types for details. + +python check_types() { + import oe.types + if isinstance(e, bb.event.ConfigParsed): + for key in e.data.keys(): + if e.data.getVarFlag(key, "type"): + oe.types.value(key, e.data) +} +addhandler check_types diff --git a/conf/bitbake.conf b/conf/bitbake.conf index 49e92d26a2..17b9b281f6 100644 --- a/conf/bitbake.conf +++ b/conf/bitbake.conf @@ -2,6 +2,7 @@ # Recipe Parsing ################################################################## BBMASK = "/(nonworking|obsolete)/" +BBMASK[type] = "regex" ################################################################## # Standard target filesystem layout. @@ -172,6 +173,7 @@ ASSUME_PROVIDED = "\ texinfo-native \ util-linux-native \ " +ASSUME_PROVIDED[type] = "list" ################################################################## # Package default variables. @@ -211,7 +213,9 @@ HOMEPAGE = "unknown" # Ensure that -dev packages recommend the corresponding -dev packages of their # deps, and the same for -dbg. DEPCHAIN_PRE = "" +DEPCHAIN_PRE[type] = "list" DEPCHAIN_POST = "-dev -dbg" +DEPCHAIN_POST[type] = "list" DEPENDS = "" RDEPENDS = "" @@ -232,8 +236,10 @@ SOLIBSDEV_darwin8 = ".dylib" SOLIBSDEV_darwin9 = ".dylib" PACKAGES = "${PN}-dbg ${PN} ${PN}-doc ${PN}-dev ${PN}-static ${PN}-locale" +PACKAGES[type] = "list" #enable this when bitbake is upgraded to cope with ++ values in the field PACKAGES_DYNAMIC = "${PN}-locale-*" +PACKAGES_DYNAMIC[type] = "list" FILES = "" FILES_${PN} = "${bindir}/* ${sbindir}/* ${libexecdir}/* ${libdir}/lib*${SOLIBS} \ @@ -270,8 +276,14 @@ FILES_${PN}-locale = "${datadir}/locale" # file:// search paths FILE_DIRNAME = "${@os.path.dirname(bb.data.getVar('FILE', d))}" FILESPATHBASE = "${FILE_DIRNAME}" +FILESPATHBASE[type] = "list" +FILESPATHBASE[separator] = ":" FILESPATHPKG = "${PF}:${P}:${PN}:${BP}:${BPN}:files:." +FILESPATHPKG[type] = "list" +FILESPATHPKG[separator] = ":" FILESPATH = "${@':'.join(uniq(os.path.normpath(os.path.join(fp, p, o)) for fp in '${FILESPATHBASE}'.split(':') for p in '${FILESPATHPKG}'.split(':') for o in reversed([''] + filter(None, '${OVERRIDES}'.split(':')))))}" +FILESPATH[type] = "list" +FILESPATH[separator] = ":" FILESDIR = "${@bb.which(d.getVar('FILESPATH', 1), '.')}" ################################################################## @@ -427,8 +439,11 @@ EXTRA_IMAGEDEPENDS = "" # by default this is unset and should be so for libtools < 2.4 LIBTOOL_HAS_SYSROOT ?= "no" +LIBTOOL_HAS_SYSROOT[type] = "boolean" PATH_prepend = "${STAGING_BINDIR_CROSS}:${STAGING_DIR_NATIVE}${sbindir_native}:${STAGING_BINDIR_NATIVE}:${STAGING_DIR_NATIVE}${base_sbindir_native}:${STAGING_DIR_NATIVE}/${base_bindir_native}:" +PATH[type] = "list" +PATH[separator] = ":" export PATH ################################################################## @@ -601,8 +616,10 @@ BB_LOCALCOUNT_OVERRIDE = "1" # Value for revision counter in GIT recipes (NNNN in PV="1.0+gitrNNNN+a7401084a72285c8d3485bacf4eed593f303b1b7-r4.4") LOCALCOUNT = "0" +LOCALCOUNT[type] = "integer" SRC_URI = "file://${FILE}" +SRC_URI[type] = "list" ################################################################## # UI/Interaction Configuration @@ -654,6 +671,8 @@ export HOSTNAME := "${@os.uname()[1]}" PKG_CONFIG_DIR = "${STAGING_LIBDIR}/pkgconfig" export PKG_CONFIG_PATH = "${STAGING_LIBDIR}/pkgconfig:${STAGING_DATADIR}/pkgconfig" PKG_CONFIG_PATH .= ":${STAGING_LIBDIR_NATIVE}/pkgconfig:${STAGING_DATADIR_NATIVE}/pkgconfig" +PKG_CONFIG_PATH[type] = "list" +PKG_CONFIG_PATH[separator] = ":" export PKG_CONFIG_SYSROOT_DIR = "${STAGING_DIR}/${BASE_PACKAGE_ARCH}${TARGET_VENDOR}-${TARGET_OS}" export PKG_CONFIG_DISABLE_UNINSTALLED = "yes" @@ -674,9 +693,11 @@ ENTERPRISE_DISTRO ?= "0" BUILDCFG_HEADER = "Build Configuration:" BUILDCFG_VARS ?= "BB_VERSION METADATA_BRANCH METADATA_REVISION TARGET_ARCH TARGET_OS MACHINE DISTRO DISTRO_VERSION" +BUILDCFG_VARS[type] = "list" BUILDCFG_VARS_append_arm = " TARGET_FPU" BUILDCFG_VARS_append_armeb = " TARGET_FPU" BUILDCFG_NEEDEDVARS ?= "TARGET_ARCH TARGET_OS" +BUILDCFG_NEEDEDVARS[type] = "list" ### ### Config file processing @@ -688,6 +709,8 @@ MACHINE_OVERRIDES ?= "" OVERRIDES = "pn-${PN}:fail-fast:build-${BUILD_OS}:${TARGET_ARCH}:${TARGET_OS}:${DISTRO}:\ ${@[':'.join(d.getVar('MACHINE_OVERRIDES', True).split())+':',''][d.getVar('MACHINE_OVERRIDES', True).strip() == '']}\ ${MACHINE}:local" +OVERRIDES[type] = "list" +OVERRIDES[separator] = ":" ################################################################## # Include the rest of the config files. @@ -721,6 +744,7 @@ PCMCIA_MANAGER ?= "pcmcia-cs" DEFAULT_TASK_PROVIDER ?= "task-base" MACHINE_TASK_PROVIDER ?= "${DEFAULT_TASK_PROVIDER}" IMAGE_ROOTFS_SIZE ?= "65536" +IMAGE_ROOTFS_SIZE[type] = "integer" IMAGE_ROOTFS_SIZE_ext2 ?= "65536" IMAGE_ROOTFS_SIZE_ext2.gz ?= "65536" IMAGE_ROOTFS_SIZE_ext3 ?= "65536" @@ -733,6 +757,7 @@ CACHE := "${CACHE}" # Default to disabling packaged staging code export PSTAGING_ACTIVE = "0" +PSTAGING_ACTIVE[type] = "boolean" ################################################################## # Magic Cookie for SANITY CHECK @@ -750,14 +775,21 @@ DISTRO_FEATURES += ' ${@["", "thumb-interwork"][bb.data.getVar('THUMB_INTERWORK' # This is used to limit what packages goes into images built, so set big by default ROOT_FLASH_SIZE ?= "256" +ROOT_FLASH_SIZE[type] = "integer" MACHINE_GUI_CLASS ?= "smallscreen" +MACHINE_GUI_CLASS[type] = "choice" +MACHINE_GUI_CLASS[choices] = "bigscreen smallscreen" # GUI_MACHINE_CLASS is deprecated, please use MACHINE_GUI_CLASS instead GUI_MACHINE_CLASS ?= "${MACHINE_GUI_CLASS}" MACHINE_DISPLAY_WIDTH_PIXELS ?= "240" +MACHINE_DISPLAY_WIDTH_PIXELS[type] = "integer" MACHINE_DISPLAY_HEIGHT_PIXELS ?= "320" +MACHINE_DISPLAY_HEIGHT_PIXELS[type] = "integer" MACHINE_DISPLAY_ORIENTATION ?= "0" +MACHINE_DISPLAY_ORIENTATION[type] = "boolean" MACHINE_DISPLAY_BPP ?= "16" +MACHINE_DISPLAY_BPP[type] = "integer" DISTRO_EXTRA_RDEPENDS ?= "" DISTRO_EXTRA_RRECOMMENDS ?= "" @@ -766,6 +798,9 @@ MACHINE_EXTRA_RRECOMMENDS ?= "" MACHINE_ESSENTIAL_EXTRA_RDEPENDS ?= "" MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS ?= "" +DISTRO_FEATURES[type] = "list" +MACHINE_FEATURES[type] = "list" + COMBINED_FEATURES = "\ ${@base_both_contain("DISTRO_FEATURES", "MACHINE_FEATURES", "alsa", d)} \ ${@base_both_contain("DISTRO_FEATURES", "MACHINE_FEATURES", "bluetooth", d)} \ @@ -796,6 +831,7 @@ COMBINED_FEATURES += "${@base_ifelse( \ (base_contains('MACHINE_FEATURES', 'pcmcia', True, False, d) and \ base_contains('DISTRO_FEATURES', 'pcmcia', True, False, d))), \ 'hostap', '')}" +COMBINED_FEATURES[type] = "list" # Make sure MACHINE isn't exported # (breaks binutils at least) diff --git a/lib/oe/_types.py b/lib/oe/_types.py new file mode 100644 index 0000000000..91afb8ab5d --- /dev/null +++ b/lib/oe/_types.py @@ -0,0 +1,59 @@ +import re + +class OEList(list): + name = "list" + + def __init__(self, value, separator = None): + if value is not None: + list.__init__(self, value.split(separator)) + else: + list.__init__(self) + + if separator is None: + self.separator = " " + else: + self.separator = separator + + def __str__(self): + return self.separator.join(self) + +def choice(value, choices): + if not isinstance(value, basestring): + raise TypeError("choice accepts a string, not '%s'" % type(value)) + + value = value.lower() + choices = choices.lower() + if value not in choices.split(): + raise ValueError("Invalid choice '%s'. Valid choices: %s" % + (value, choices)) + return value + +def regex(value, regexflags=None): + flagval = 0 + if regexflags: + for flag in regexflags.split(): + flag = flag.upper() + try: + flagval |= getattr(re, flag) + except AttributeError: + raise ValueError("Invalid regex flag '%s'" % flag) + + try: + return re.compile(value, flagval) + except re.error, exc: + raise ValueError("Invalid regex value '%s': %s" % + (value, exc.args[0])) + +def boolean(value): + if not isinstance(value, basestring): + raise TypeError("boolean accepts a string, not '%s'" % type(value)) + + value = value.lower() + if value in ('yes', 'y', 'true', 't', '1'): + return True + elif value in ('no', 'n', 'false', 'f', '0'): + return False + raise ValueError("Invalid boolean value '%s'" % value) + +def integer(value): + return int(value) diff --git a/lib/oe/test_types.py b/lib/oe/test_types.py new file mode 100644 index 0000000000..494abfae43 --- /dev/null +++ b/lib/oe/test_types.py @@ -0,0 +1,56 @@ +import unittest +from oe.types import create, factory + +class TestTypes(unittest.TestCase): + def assertIsInstance(self, obj, cls): + return self.assertTrue(isinstance(obj, cls)) + + def assertIsNot(self, obj, other): + return self.assertFalse(obj is other) + + def assertFactoryCreated(self, value, type, **flags): + obj = factory(type) + self.assertIsNot(obj, None) + self.assertIsInstance(create(value, type, **flags), obj) + +class TestBooleanType(TestTypes): + def test_boolean(self): + self.assertRaises(ValueError, create, '', 'boolean') + self.assertRaises(ValueError, create, 'foo', 'boolean') + self.assertRaises(TypeError, create, object(), 'boolean') + + def test_boolean_true(self): + self.assertEqual(create('y', 'boolean'), True) + self.assertEqual(create('yes', 'boolean'), True) + self.assertEqual(create('1', 'boolean'), True) + self.assertEqual(create('t', 'boolean'), True) + self.assertEqual(create('true', 'boolean'), True) + self.assertEqual(create('TRUE', 'boolean'), True) + self.assertEqual(create('truE', 'boolean'), True) + + def test_boolean_false(self): + self.assertEqual(create('n', 'boolean'), False) + self.assertEqual(create('no', 'boolean'), False) + self.assertEqual(create('0', 'boolean'), False) + self.assertEqual(create('f', 'boolean'), False) + self.assertEqual(create('false', 'boolean'), False) + self.assertEqual(create('FALSE', 'boolean'), False) + self.assertEqual(create('faLse', 'boolean'), False) + +class TestList(TestTypes): + def assertListEqual(self, value, valid, sep=None): + obj = create(value, 'list', separator=sep) + self.assertEqual(obj, valid) + if sep is not None: + self.assertEqual(obj.separator, sep) + self.assertEqual(str(obj), obj.separator.join(obj)) + + def test_list_nosep(self): + testlist = ['alpha', 'beta', 'theta'] + self.assertListEqual('alpha beta theta', testlist) + self.assertListEqual('alpha beta\ttheta', testlist) + self.assertListEqual('alpha', ['alpha']) + + def test_list_usersep(self): + self.assertListEqual('foo:bar', ['foo', 'bar'], ':') + self.assertListEqual('foo:bar:baz', ['foo', 'bar', 'baz'], ':') diff --git a/lib/oe/types.py b/lib/oe/types.py new file mode 100644 index 0000000000..963e964212 --- /dev/null +++ b/lib/oe/types.py @@ -0,0 +1,97 @@ +# Constructs objects of the specified type for a given variable in the +# metadata. The 'type' flag of the variable defines which of the factory +# functions in this module will be called. +# +# If no type is defined, the value() function will simply return the expanded +# string value as is. + +import bb +import inspect +import _types + +types = {} + +class MissingFlag(TypeError): + 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): + try: + return types[var_type] + except KeyError: + raise TypeError("Invalid type '%s'" % var_type) + +def create(value, var_type, **flags): + 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 value(key, d): + """Construct a value for a metadata variable, based upon its flags""" + + var_type = d.getVarFlag(key, 'type') + flags = d.getVarFlags(key) + + try: + return create(d.getVar(key, True) or '', var_type, **flags) + except (TypeError, ValueError), exc: + bb.fatal("%s: %s" % (key, str(exc))) + +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 by oe.types""" + 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): + factory_setup(name, factory) + types[factory.name] = factory + +# Set the 'flags' and 'optflags' attributes of all our types +for name in dir(_types): + if name.startswith('_'): + continue + + obj = getattr(_types, name) + if not callable(obj): + continue + + register(name, obj) |