summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Larson <chris_larson@mentor.com>2010-12-28 09:21:19 -0700
committerChris Larson <chris_larson@mentor.com>2010-12-28 09:21:19 -0700
commit199722d4333f4b0ec64ef2cb93ab681115dfc3d0 (patch)
tree713159c4c5bdf8e2892386c0f8bc5c8aaa94ac34
parent4247d8ca5b818a9b0409667d67b534cead00cb69 (diff)
parentd0206a116e96d9ab74cc637caa2543da22d16ac1 (diff)
downloadopenembedded-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.bbclass29
-rw-r--r--classes/image.bbclass3
-rw-r--r--classes/patch.bbclass5
-rw-r--r--classes/typecheck.bbclass12
-rw-r--r--conf/bitbake.conf36
-rw-r--r--lib/oe/_types.py59
-rw-r--r--lib/oe/test_types.py56
-rw-r--r--lib/oe/types.py97
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)