summaryrefslogtreecommitdiff
path: root/yocto-poky/bitbake/lib/bb
diff options
context:
space:
mode:
authorPatrick Williams <patrick@stwcx.xyz>2016-03-30 23:21:19 +0300
committerPatrick Williams <patrick@stwcx.xyz>2016-03-30 23:21:19 +0300
commitb4a027550acf2c1051c34f997b8e7e845017af4b (patch)
tree9e38d3c17b42cb1e6765620a87e908973a93c821 /yocto-poky/bitbake/lib/bb
parent2fe86d90044af218ced8f42fdded6b136f1046d2 (diff)
parentf1e5d6968976c2341c6d554bfcc8895f1b33c26b (diff)
downloadopenbmc-b4a027550acf2c1051c34f997b8e7e845017af4b.tar.xz
Merge commit 'f1e5d6968976c2341c6d554bfcc8895f1b33c26b' from yocto-2.0.1
Diffstat (limited to 'yocto-poky/bitbake/lib/bb')
-rw-r--r--yocto-poky/bitbake/lib/bb/__init__.py2
-rw-r--r--yocto-poky/bitbake/lib/bb/build.py7
-rw-r--r--yocto-poky/bitbake/lib/bb/command.py10
-rw-r--r--yocto-poky/bitbake/lib/bb/cooker.py32
-rw-r--r--yocto-poky/bitbake/lib/bb/cookerdata.py6
-rw-r--r--yocto-poky/bitbake/lib/bb/fetch2/__init__.py2
-rw-r--r--yocto-poky/bitbake/lib/bb/fetch2/hg.py1
-rw-r--r--yocto-poky/bitbake/lib/bb/fetch2/svn.py9
-rwxr-xr-xyocto-poky/bitbake/lib/bb/main.py7
-rw-r--r--yocto-poky/bitbake/lib/bb/runqueue.py9
-rw-r--r--yocto-poky/bitbake/lib/bb/siggen.py10
-rw-r--r--yocto-poky/bitbake/lib/bb/taskdata.py12
-rw-r--r--yocto-poky/bitbake/lib/bb/tests/utils.py203
-rw-r--r--yocto-poky/bitbake/lib/bb/tinfoil.py9
-rw-r--r--yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py229
-rw-r--r--yocto-poky/bitbake/lib/bb/ui/knotty.py2
-rw-r--r--yocto-poky/bitbake/lib/bb/ui/toasterui.py127
-rw-r--r--yocto-poky/bitbake/lib/bb/utils.py105
18 files changed, 658 insertions, 124 deletions
diff --git a/yocto-poky/bitbake/lib/bb/__init__.py b/yocto-poky/bitbake/lib/bb/__init__.py
index 1f7946e7b..ac62d262c 100644
--- a/yocto-poky/bitbake/lib/bb/__init__.py
+++ b/yocto-poky/bitbake/lib/bb/__init__.py
@@ -21,7 +21,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-__version__ = "1.27.1"
+__version__ = "1.28.0"
import sys
if sys.version_info < (2, 7, 3):
diff --git a/yocto-poky/bitbake/lib/bb/build.py b/yocto-poky/bitbake/lib/bb/build.py
index 948c3951f..22428a649 100644
--- a/yocto-poky/bitbake/lib/bb/build.py
+++ b/yocto-poky/bitbake/lib/bb/build.py
@@ -413,6 +413,13 @@ def _exec_task(fn, task, d, quieterr):
nice = int(nice) - curnice
newnice = os.nice(nice)
logger.debug(1, "Renice to %s " % newnice)
+ ionice = localdata.getVar("BB_TASK_IONICE_LEVEL", True)
+ if ionice:
+ try:
+ cls, prio = ionice.split(".", 1)
+ bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
+ except:
+ bb.warn("Invalid ionice level %s" % ionice)
bb.utils.mkdirhier(tempdir)
diff --git a/yocto-poky/bitbake/lib/bb/command.py b/yocto-poky/bitbake/lib/bb/command.py
index 398c1d6a6..74106d143 100644
--- a/yocto-poky/bitbake/lib/bb/command.py
+++ b/yocto-poky/bitbake/lib/bb/command.py
@@ -181,6 +181,16 @@ class CommandsSync:
value = str(params[1])
command.cooker.data.setVar(varname, value)
+ def getSetVariable(self, command, params):
+ """
+ Read the value of a variable from data and set it into the datastore
+ which effectively expands and locks the value.
+ """
+ varname = params[0]
+ result = self.getVariable(command, params)
+ command.cooker.data.setVar(varname, result)
+ return result
+
def setConfig(self, command, params):
"""
Set the value of variable in configuration
diff --git a/yocto-poky/bitbake/lib/bb/cooker.py b/yocto-poky/bitbake/lib/bb/cooker.py
index a0d7d59ea..4df88818f 100644
--- a/yocto-poky/bitbake/lib/bb/cooker.py
+++ b/yocto-poky/bitbake/lib/bb/cooker.py
@@ -255,6 +255,11 @@ class BBCooker:
self.state = state.initial
self.caches_array = []
+ # Need to preserve BB_CONSOLELOG over resets
+ consolelog = None
+ if hasattr(self, "data"):
+ consolelog = self.data.getVar("BB_CONSOLELOG", True)
+
if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
self.enableDataTracking()
@@ -281,6 +286,8 @@ class BBCooker:
self.data = self.databuilder.data
self.data_hash = self.databuilder.data_hash
+ if consolelog:
+ self.data.setVar("BB_CONSOLELOG", consolelog)
# we log all events to a file if so directed
if self.configuration.writeeventlog:
@@ -531,6 +538,11 @@ class BBCooker:
for o in options:
if o in ['prefile', 'postfile']:
clean = False
+ server_val = getattr(self.configuration, "%s_server" % o)
+ if not options[o] and server_val:
+ # restore value provided on server start
+ setattr(self.configuration, o, server_val)
+ continue
setattr(self.configuration, o, options[o])
for k in bb.utils.approved_variables():
if k in environment and k not in self.configuration.env:
@@ -1391,10 +1403,28 @@ class BBCooker:
build.reset_cache()
self.buildSetVars()
+ # If we are told to do the None task then query the default task
+ if (task == None):
+ task = self.configuration.cmd
+
+ if not task.startswith("do_"):
+ task = "do_%s" % task
+
taskdata, runlist, fulltargetlist = self.buildTaskData(targets, task, self.configuration.abort)
buildname = self.data.getVar("BUILDNAME", False)
- bb.event.fire(bb.event.BuildStarted(buildname, fulltargetlist), self.data)
+
+ # make targets to always look as <target>:do_<task>
+ ntargets = []
+ for target in fulltargetlist:
+ if ":" in target:
+ if ":do_" not in target:
+ target = "%s:do_%s" % tuple(target.split(":", 1))
+ else:
+ target = "%s:%s" % (target, task)
+ ntargets.append(target)
+
+ bb.event.fire(bb.event.BuildStarted(buildname, ntargets), self.data)
rq = bb.runqueue.RunQueue(self, self.data, self.recipecache, taskdata, runlist)
if 'universe' in targets:
diff --git a/yocto-poky/bitbake/lib/bb/cookerdata.py b/yocto-poky/bitbake/lib/bb/cookerdata.py
index f19c28388..671c0cb0e 100644
--- a/yocto-poky/bitbake/lib/bb/cookerdata.py
+++ b/yocto-poky/bitbake/lib/bb/cookerdata.py
@@ -63,9 +63,9 @@ class ConfigParameters(object):
raise Exception("Unable to set configuration option 'cmd' on the server: %s" % error)
if not self.options.pkgs_to_build:
- bbpkgs, error = server.runCommand(["getVariable", "BBPKGS"])
+ bbpkgs, error = server.runCommand(["getVariable", "BBTARGETS"])
if error:
- raise Exception("Unable to get the value of BBPKGS from the server: %s" % error)
+ raise Exception("Unable to get the value of BBTARGETS from the server: %s" % error)
if bbpkgs:
self.options.pkgs_to_build.extend(bbpkgs.split())
@@ -129,6 +129,8 @@ class CookerConfiguration(object):
self.extra_assume_provided = []
self.prefile = []
self.postfile = []
+ self.prefile_server = []
+ self.postfile_server = []
self.debug = 0
self.cmd = None
self.abort = True
diff --git a/yocto-poky/bitbake/lib/bb/fetch2/__init__.py b/yocto-poky/bitbake/lib/bb/fetch2/__init__.py
index 288a1c8fd..a9c044b6a 100644
--- a/yocto-poky/bitbake/lib/bb/fetch2/__init__.py
+++ b/yocto-poky/bitbake/lib/bb/fetch2/__init__.py
@@ -955,7 +955,7 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
origud.method.download(origud, ld)
if hasattr(origud.method,"build_mirror_data"):
origud.method.build_mirror_data(origud, ld)
- return ud.localpath
+ return origud.localpath
# Otherwise the result is a local file:// and we symlink to it
if not os.path.exists(origud.localpath):
if os.path.islink(origud.localpath):
diff --git a/yocto-poky/bitbake/lib/bb/fetch2/hg.py b/yocto-poky/bitbake/lib/bb/fetch2/hg.py
index bbb4ed95d..3b743ff51 100644
--- a/yocto-poky/bitbake/lib/bb/fetch2/hg.py
+++ b/yocto-poky/bitbake/lib/bb/fetch2/hg.py
@@ -28,6 +28,7 @@ import os
import sys
import logging
import bb
+import errno
from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
diff --git a/yocto-poky/bitbake/lib/bb/fetch2/svn.py b/yocto-poky/bitbake/lib/bb/fetch2/svn.py
index 1733c2beb..8a291935c 100644
--- a/yocto-poky/bitbake/lib/bb/fetch2/svn.py
+++ b/yocto-poky/bitbake/lib/bb/fetch2/svn.py
@@ -54,6 +54,11 @@ class Svn(FetchMethod):
ud.module = ud.parm["module"]
+ if not "path_spec" in ud.parm:
+ ud.path_spec = ud.module
+ else:
+ ud.path_spec = ud.parm["path_spec"]
+
# Create paths to svn checkouts
relpath = self._strip_leading_slashes(ud.path)
ud.pkgdir = os.path.join(data.expand('${SVNDIR}', d), ud.host, relpath)
@@ -102,7 +107,7 @@ class Svn(FetchMethod):
if command == "fetch":
transportuser = ud.parm.get("transportuser", "")
- svncmd = "%s co %s %s://%s%s/%s%s %s" % (ud.basecmd, " ".join(options), proto, transportuser, svnroot, ud.module, suffix, ud.module)
+ svncmd = "%s co %s %s://%s%s/%s%s %s" % (ud.basecmd, " ".join(options), proto, transportuser, svnroot, ud.module, suffix, ud.path_spec)
elif command == "update":
svncmd = "%s update %s" % (ud.basecmd, " ".join(options))
else:
@@ -149,7 +154,7 @@ class Svn(FetchMethod):
os.chdir(ud.pkgdir)
# tar them up to a defined filename
- runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, ud.module), d, cleanup = [ud.localpath])
+ runfetchcmd("tar %s -czf %s %s" % (tar_flags, ud.localpath, ud.path_spec), d, cleanup = [ud.localpath])
def clean(self, ud, d):
""" Clean SVN specific files and dirs """
diff --git a/yocto-poky/bitbake/lib/bb/main.py b/yocto-poky/bitbake/lib/bb/main.py
index 8762f7220..c8530fc3d 100755
--- a/yocto-poky/bitbake/lib/bb/main.py
+++ b/yocto-poky/bitbake/lib/bb/main.py
@@ -383,6 +383,13 @@ def bitbake_main(configParams, configuration):
# Collect the feature set for the UI
featureset = getattr(ui_module, "featureSet", [])
+ if configParams.server_only:
+ for param in ('prefile', 'postfile'):
+ value = getattr(configParams, param)
+ if value:
+ setattr(configuration, "%s_server" % param, value)
+ param = "%s_server" % param
+
if not configParams.remote_server:
# we start a server with a given configuration
server = start_server(servermodule, configParams, configuration, featureset)
diff --git a/yocto-poky/bitbake/lib/bb/runqueue.py b/yocto-poky/bitbake/lib/bb/runqueue.py
index 2b71eed06..878028aa9 100644
--- a/yocto-poky/bitbake/lib/bb/runqueue.py
+++ b/yocto-poky/bitbake/lib/bb/runqueue.py
@@ -797,6 +797,15 @@ class RunQueueData:
st = "do_%s" % st
invalidate_task(fn, st, True)
+ # Create and print to the logs a virtual/xxxx -> PN (fn) table
+ virtmap = taskData.get_providermap()
+ virtpnmap = {}
+ for v in virtmap:
+ virtpnmap[v] = self.dataCache.pkg_fn[virtmap[v]]
+ bb.debug(2, "%s resolved to: %s (%s)" % (v, virtpnmap[v], virtmap[v]))
+ if hasattr(bb.parse.siggen, "tasks_resolved"):
+ bb.parse.siggen.tasks_resolved(virtmap, virtpnmap, self.dataCache)
+
# Iterate over the task list and call into the siggen code
dealtwith = set()
todeal = set(range(len(self.runq_fnid)))
diff --git a/yocto-poky/bitbake/lib/bb/siggen.py b/yocto-poky/bitbake/lib/bb/siggen.py
index 298527221..0352e4523 100644
--- a/yocto-poky/bitbake/lib/bb/siggen.py
+++ b/yocto-poky/bitbake/lib/bb/siggen.py
@@ -80,6 +80,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
self.taskdeps = {}
self.runtaskdeps = {}
self.file_checksum_values = {}
+ self.taints = {}
self.gendeps = {}
self.lookupcache = {}
self.pkgnameextract = re.compile("(?P<fn>.*)\..*")
@@ -199,11 +200,14 @@ class SignatureGeneratorBasic(SignatureGenerator):
if 'nostamp' in taskdep and task in taskdep['nostamp']:
# Nostamp tasks need an implicit taint so that they force any dependent tasks to run
import uuid
- data = data + str(uuid.uuid4())
+ taint = str(uuid.uuid4())
+ data = data + taint
+ self.taints[k] = "nostamp:" + taint
taint = self.read_taint(fn, task, dataCache.stamp[fn])
if taint:
data = data + taint
+ self.taints[k] = taint
logger.warn("%s is tainted from a forced run" % k)
h = hashlib.md5(data).hexdigest()
@@ -247,6 +251,10 @@ class SignatureGeneratorBasic(SignatureGenerator):
if taint:
data['taint'] = taint
+ if runtime and k in self.taints:
+ if 'nostamp:' in self.taints[k]:
+ data['taint'] = self.taints[k]
+
fd, tmpfile = tempfile.mkstemp(dir=os.path.dirname(sigfile), prefix="sigtask.")
try:
with os.fdopen(fd, "wb") as stream:
diff --git a/yocto-poky/bitbake/lib/bb/taskdata.py b/yocto-poky/bitbake/lib/bb/taskdata.py
index 5fab7043c..4d12b3325 100644
--- a/yocto-poky/bitbake/lib/bb/taskdata.py
+++ b/yocto-poky/bitbake/lib/bb/taskdata.py
@@ -612,6 +612,18 @@ class TaskData:
break
# self.dump_data()
+ def get_providermap(self):
+ virts = []
+ virtmap = {}
+
+ for name in self.build_names_index:
+ if name.startswith("virtual/"):
+ virts.append(name)
+ for v in virts:
+ if self.have_build_target(v):
+ virtmap[v] = self.fn_index[self.get_provider(v)[0]]
+ return virtmap
+
def dump_data(self):
"""
Dump some debug information on the internal data structures
diff --git a/yocto-poky/bitbake/lib/bb/tests/utils.py b/yocto-poky/bitbake/lib/bb/tests/utils.py
index 9171509a6..a035ccf17 100644
--- a/yocto-poky/bitbake/lib/bb/tests/utils.py
+++ b/yocto-poky/bitbake/lib/bb/tests/utils.py
@@ -376,3 +376,206 @@ do_functionname() {
(updated, newlines) = bb.utils.edit_metadata(self._origfile.splitlines(True), varlist, handle_var)
self.assertTrue(updated, 'List should be updated but isn\'t')
self.assertEqual(newlines, newfile5.splitlines(True))
+
+
+class EditBbLayersConf(unittest.TestCase):
+
+ def _test_bblayers_edit(self, before, after, add, remove, notadded, notremoved):
+ with tempfile.NamedTemporaryFile('w', delete=False) as tf:
+ tf.write(before)
+ tf.close()
+ try:
+ actual_notadded, actual_notremoved = bb.utils.edit_bblayers_conf(tf.name, add, remove)
+ with open(tf.name) as f:
+ actual_after = f.readlines()
+ self.assertEqual(after.splitlines(True), actual_after)
+ self.assertEqual(notadded, actual_notadded)
+ self.assertEqual(notremoved, actual_notremoved)
+ finally:
+ os.remove(tf.name)
+
+
+ def test_bblayers_remove(self):
+ before = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS = " \
+ /home/user/path/layer1 \
+ /home/user/path/layer2 \
+ /home/user/path/subpath/layer3 \
+ /home/user/path/layer4 \
+ "
+"""
+ after = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS = " \
+ /home/user/path/layer1 \
+ /home/user/path/subpath/layer3 \
+ /home/user/path/layer4 \
+ "
+"""
+ self._test_bblayers_edit(before, after,
+ None,
+ '/home/user/path/layer2',
+ [],
+ [])
+
+
+ def test_bblayers_add(self):
+ before = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS = " \
+ /home/user/path/layer1 \
+ /home/user/path/layer2 \
+ /home/user/path/subpath/layer3 \
+ /home/user/path/layer4 \
+ "
+"""
+ after = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS = " \
+ /home/user/path/layer1 \
+ /home/user/path/layer2 \
+ /home/user/path/subpath/layer3 \
+ /home/user/path/layer4 \
+ /other/path/to/layer5 \
+ "
+"""
+ self._test_bblayers_edit(before, after,
+ '/other/path/to/layer5/',
+ None,
+ [],
+ [])
+
+
+ def test_bblayers_add_remove(self):
+ before = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS = " \
+ /home/user/path/layer1 \
+ /home/user/path/layer2 \
+ /home/user/path/subpath/layer3 \
+ /home/user/path/layer4 \
+ "
+"""
+ after = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS = " \
+ /home/user/path/layer1 \
+ /home/user/path/layer2 \
+ /home/user/path/layer4 \
+ /other/path/to/layer5 \
+ "
+"""
+ self._test_bblayers_edit(before, after,
+ ['/other/path/to/layer5', '/home/user/path/layer2/'], '/home/user/path/subpath/layer3/',
+ ['/home/user/path/layer2'],
+ [])
+
+
+ def test_bblayers_add_remove_home(self):
+ before = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS = " \
+ ~/path/layer1 \
+ ~/path/layer2 \
+ ~/otherpath/layer3 \
+ ~/path/layer4 \
+ "
+"""
+ after = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS = " \
+ ~/path/layer2 \
+ ~/path/layer4 \
+ ~/path2/layer5 \
+ "
+"""
+ self._test_bblayers_edit(before, after,
+ [os.environ['HOME'] + '/path/layer4', '~/path2/layer5'],
+ [os.environ['HOME'] + '/otherpath/layer3', '~/path/layer1', '~/path/notinlist'],
+ [os.environ['HOME'] + '/path/layer4'],
+ ['~/path/notinlist'])
+
+
+ def test_bblayers_add_remove_plusequals(self):
+ before = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS += " \
+ /home/user/path/layer1 \
+ /home/user/path/layer2 \
+ "
+"""
+ after = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS += " \
+ /home/user/path/layer2 \
+ /home/user/path/layer3 \
+ "
+"""
+ self._test_bblayers_edit(before, after,
+ '/home/user/path/layer3',
+ '/home/user/path/layer1',
+ [],
+ [])
+
+
+ def test_bblayers_add_remove_plusequals2(self):
+ before = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS += " \
+ /home/user/path/layer1 \
+ /home/user/path/layer2 \
+ /home/user/path/layer3 \
+ "
+BBLAYERS += "/home/user/path/layer4"
+BBLAYERS += "/home/user/path/layer5"
+"""
+ after = r"""
+# A comment
+
+BBPATH = "${TOPDIR}"
+BBFILES ?= ""
+BBLAYERS += " \
+ /home/user/path/layer2 \
+ /home/user/path/layer3 \
+ "
+BBLAYERS += "/home/user/path/layer5"
+BBLAYERS += "/home/user/otherpath/layer6"
+"""
+ self._test_bblayers_edit(before, after,
+ ['/home/user/otherpath/layer6', '/home/user/path/layer3'], ['/home/user/path/layer1', '/home/user/path/layer4', '/home/user/path/layer7'],
+ ['/home/user/path/layer3'],
+ ['/home/user/path/layer7'])
diff --git a/yocto-poky/bitbake/lib/bb/tinfoil.py b/yocto-poky/bitbake/lib/bb/tinfoil.py
index 1ea46d8ee..7aa653f1a 100644
--- a/yocto-poky/bitbake/lib/bb/tinfoil.py
+++ b/yocto-poky/bitbake/lib/bb/tinfoil.py
@@ -36,13 +36,13 @@ class Tinfoil:
# Set up logging
self.logger = logging.getLogger('BitBake')
- console = logging.StreamHandler(output)
- bb.msg.addDefaultlogFilter(console)
+ self._log_hdlr = logging.StreamHandler(output)
+ bb.msg.addDefaultlogFilter(self._log_hdlr)
format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
if output.isatty():
format.enable_color()
- console.setFormatter(format)
- self.logger.addHandler(console)
+ self._log_hdlr.setFormatter(format)
+ self.logger.addHandler(self._log_hdlr)
self.config = CookerConfiguration()
configparams = TinfoilConfigParameters(parse_only=True)
@@ -88,6 +88,7 @@ class Tinfoil:
self.cooker.shutdown(force=True)
self.cooker.post_serve()
self.cooker.unlockBitbake()
+ self.logger.removeHandler(self._log_hdlr)
class TinfoilConfigParameters(ConfigParameters):
diff --git a/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py b/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py
index 6e313fee8..78f1e9274 100644
--- a/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py
+++ b/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py
@@ -66,6 +66,7 @@ class ORMWrapper(object):
def __init__(self):
self.layer_version_objects = []
+ self.layer_version_built = []
self.task_objects = {}
self.recipe_objects = {}
@@ -94,8 +95,8 @@ class ORMWrapper(object):
created = False
if not key in vars(self)[dictname].keys():
- vars(self)[dictname][key] = clazz.objects.create(**kwargs)
- created = True
+ vars(self)[dictname][key], created = \
+ clazz.objects.get_or_create(**kwargs)
return (vars(self)[dictname][key], created)
@@ -161,8 +162,6 @@ class ORMWrapper(object):
build.bitbake_version=build_info['bitbake_version']
build.save()
- Target.objects.filter(build = build).delete()
-
else:
build = Build.objects.create(
project = prj,
@@ -183,18 +182,26 @@ class ORMWrapper(object):
return build
- def create_target_objects(self, target_info):
- assert 'build' in target_info
- assert 'targets' in target_info
-
- targets = []
- for tgt_name in target_info['targets']:
- tgt_object = Target.objects.create( build = target_info['build'],
- target = tgt_name,
- is_image = False,
- )
- targets.append(tgt_object)
- return targets
+ @staticmethod
+ def get_or_create_targets(target_info):
+ result = []
+ for target in target_info['targets']:
+ task = ''
+ if ':' in target:
+ target, task = target.split(':', 1)
+ if task.startswith('do_'):
+ task = task[3:]
+ if task == 'build':
+ task = ''
+ obj, created = Target.objects.get_or_create(build=target_info['build'],
+ target=target)
+ if created:
+ obj.is_image = False
+ if task:
+ obj.task = task
+ obj.save()
+ result.append(obj)
+ return result
def update_build_object(self, build, errors, warnings, taskfailures):
assert isinstance(build,Build)
@@ -269,23 +276,66 @@ class ORMWrapper(object):
assert not recipe_information['file_path'].startswith("/") # we should have layer-relative paths at all times
- recipe_object, created = self._cached_get_or_create(Recipe, layer_version=recipe_information['layer_version'],
+
+ def update_recipe_obj(recipe_object):
+ object_changed = False
+ for v in vars(recipe_object):
+ if v in recipe_information.keys():
+ object_changed = True
+ vars(recipe_object)[v] = recipe_information[v]
+
+ if object_changed:
+ recipe_object.save()
+
+ recipe, created = self._cached_get_or_create(Recipe, layer_version=recipe_information['layer_version'],
file_path=recipe_information['file_path'], pathflags = recipe_information['pathflags'])
- if created and must_exist:
- raise NotExisting("Recipe object created when expected to exist", recipe_information)
- object_changed = False
- for v in vars(recipe_object):
- if v in recipe_information.keys():
- object_changed = True
- vars(recipe_object)[v] = recipe_information[v]
+ update_recipe_obj(recipe)
- if object_changed:
- recipe_object.save()
+ built_recipe = None
+ # Create a copy of the recipe for historical puposes and update it
+ for built_layer in self.layer_version_built:
+ if built_layer.layer == recipe_information['layer_version'].layer:
+ built_recipe, c = self._cached_get_or_create(Recipe,
+ layer_version=built_layer,
+ file_path=recipe_information['file_path'],
+ pathflags = recipe_information['pathflags'])
+ update_recipe_obj(built_recipe)
+ break
- return recipe_object
+
+ # If we're in analysis mode then we are wholly responsible for the data
+ # and therefore we return the 'real' recipe rather than the build
+ # history copy of the recipe.
+ if recipe_information['layer_version'].build is not None and \
+ recipe_information['layer_version'].build.project == \
+ Project.objects.get_default_project():
+ return recipe
+
+ return built_recipe
def get_update_layer_version_object(self, build_obj, layer_obj, layer_version_information):
+ if isinstance(layer_obj, Layer_Version):
+ # We already found our layer version for this build so just
+ # update it with the new build information
+ logger.debug("We found our layer from toaster")
+ layer_obj.local_path = layer_version_information['local_path']
+ layer_obj.save()
+ self.layer_version_objects.append(layer_obj)
+
+ # create a new copy of this layer version as a snapshot for
+ # historical purposes
+ layer_copy, c = Layer_Version.objects.get_or_create(build=build_obj,
+ layer=layer_obj.layer,
+ commit=layer_version_information['commit'],
+ local_path = layer_version_information['local_path'],
+ )
+ logger.info("created new historical layer version %d", layer_copy.pk)
+
+ self.layer_version_built.append(layer_copy)
+
+ return layer_obj
+
assert isinstance(build_obj, Build)
assert isinstance(layer_obj, Layer)
assert 'branch' in layer_version_information
@@ -293,14 +343,20 @@ class ORMWrapper(object):
assert 'priority' in layer_version_information
assert 'local_path' in layer_version_information
+ # If we're doing a command line build then associate this new layer with the
+ # project to avoid it 'contaminating' toaster data
+ project = None
+ if build_obj.project == Project.objects.get_default_project():
+ project = build_obj.project
+
layer_version_object, _ = Layer_Version.objects.get_or_create(
- build = build_obj,
- layer = layer_obj,
- branch = layer_version_information['branch'],
- commit = layer_version_information['commit'],
- priority = layer_version_information['priority'],
- local_path = layer_version_information['local_path'],
- )
+ build = build_obj,
+ layer = layer_obj,
+ branch = layer_version_information['branch'],
+ commit = layer_version_information['commit'],
+ priority = layer_version_information['priority'],
+ local_path = layer_version_information['local_path'],
+ project=project)
self.layer_version_objects.append(layer_version_object)
@@ -335,8 +391,15 @@ class ORMWrapper(object):
localdirname = os.path.join(bc.be.sourcedir, localdirname)
#logger.debug(1, "Localdirname %s lcal_path %s" % (localdirname, layer_information['local_path']))
if localdirname.startswith(layer_information['local_path']):
+ # If the build request came from toaster this field
+ # should contain the information from the layer_version
+ # That created this build request.
+ if brl.layer_version:
+ return brl.layer_version
+
# we matched the BRLayer, but we need the layer_version that generated this BR; reverse of the Project.schedule_build()
#logger.debug(1, "Matched %s to BRlayer %s" % (pformat(layer_information["local_path"]), localdirname))
+
for pl in buildrequest.project.projectlayer_set.filter(layercommit__layer__name = brl.name):
if pl.layercommit.layer.vcs_url == brl.giturl :
layer = pl.layercommit.layer
@@ -353,26 +416,29 @@ class ORMWrapper(object):
files = filedata['files']
syms = filedata['syms']
- # we insert directories, ordered by name depth
+ # always create the root directory as a special case;
+ # note that this is never displayed, so the owner, group,
+ # size, permission are irrelevant
+ tf_obj = Target_File.objects.create(target = target_obj,
+ path = '/',
+ size = 0,
+ owner = '',
+ group = '',
+ permission = '',
+ inodetype = Target_File.ITYPE_DIRECTORY)
+ tf_obj.save()
+
+ # insert directories, ordered by name depth
for d in sorted(dirs, key=lambda x:len(x[-1].split("/"))):
(user, group, size) = d[1:4]
permission = d[0][1:]
path = d[4].lstrip(".")
+
+ # we already created the root directory, so ignore any
+ # entry for it
if len(path) == 0:
- # we create the root directory as a special case
- path = "/"
- tf_obj = Target_File.objects.create(
- target = target_obj,
- path = path,
- size = size,
- inodetype = Target_File.ITYPE_DIRECTORY,
- permission = permission,
- owner = user,
- group = group,
- )
- tf_obj.directory = tf_obj
- tf_obj.save()
continue
+
parent_path = "/".join(path.split("/")[:len(path.split("/")) - 1])
if len(parent_path) == 0:
parent_path = "/"
@@ -461,6 +527,12 @@ class ORMWrapper(object):
errormsg = ""
for p in packagedict:
searchname = p
+ if p not in pkgpnmap:
+ logger.warning("Image packages list contains %p, but is"
+ " missing from all packages list where the"
+ " metadata comes from. Skipping...", p)
+ continue
+
if 'OPKGN' in pkgpnmap[p].keys():
searchname = pkgpnmap[p]['OPKGN']
@@ -504,13 +576,20 @@ class ORMWrapper(object):
elif deptype == 'recommends':
tdeptype = Package_Dependency.TYPE_TRECOMMENDS
- packagedeps_objs.append(Package_Dependency( package = packagedict[p]['object'],
- depends_on = packagedict[px]['object'],
- dep_type = tdeptype,
- target = target_obj))
+ try:
+ packagedeps_objs.append(Package_Dependency(
+ package = packagedict[p]['object'],
+ depends_on = packagedict[px]['object'],
+ dep_type = tdeptype,
+ target = target_obj))
+ except KeyError as e:
+ logger.warn("Could not add dependency to the package %s "
+ "because %s is an unknown package", p, px)
if len(packagedeps_objs) > 0:
Package_Dependency.objects.bulk_create(packagedeps_objs)
+ else:
+ logger.info("No package dependencies created")
if len(errormsg) > 0:
logger.warn("buildinfohelper: target_package_info could not identify recipes: \n%s", errormsg)
@@ -686,6 +765,7 @@ class BuildInfoHelper(object):
def __init__(self, server, has_build_history = False):
self.internal_state = {}
self.internal_state['taskdata'] = {}
+ self.internal_state['targets'] = []
self.task_order = 0
self.autocommit_step = 1
self.server = server
@@ -704,7 +784,7 @@ class BuildInfoHelper(object):
## methods to convert event/external info into objects that the ORM layer uses
- def _get_build_information(self, consolelogfile):
+ def _get_build_information(self, build_log_path):
build_info = {}
# Generate an identifier for each new build
@@ -713,7 +793,7 @@ class BuildInfoHelper(object):
build_info['distro_version'] = self.server.runCommand(["getVariable", "DISTRO_VERSION"])[0]
build_info['started_on'] = timezone.now()
build_info['completed_on'] = timezone.now()
- build_info['cooker_log_path'] = consolelogfile
+ build_info['cooker_log_path'] = build_log_path
build_info['build_name'] = self.server.runCommand(["getVariable", "BUILDNAME"])[0]
build_info['bitbake_version'] = self.server.runCommand(["getVariable", "BB_VERSION"])[0]
@@ -764,8 +844,15 @@ class BuildInfoHelper(object):
if not localdirname.startswith("/"):
localdirname = os.path.join(bc.be.sourcedir, localdirname)
if path.startswith(localdirname):
+ # If the build request came from toaster this field
+ # should contain the information from the layer_version
+ # That created this build request.
+ if brl.layer_version:
+ return brl.layer_version
+
#logger.warn("-- managed: matched path %s with layer %s " % (path, localdirname))
# we matched the BRLayer, but we need the layer_version that generated this br
+
for lvo in self.orm_wrapper.layer_version_objects:
if brl.name == lvo.layer.name:
return lvo
@@ -774,7 +861,7 @@ class BuildInfoHelper(object):
logger.warn("Could not match layer version for recipe path %s : %s", path, self.orm_wrapper.layer_version_objects)
#mockup the new layer
- unknown_layer, _ = Layer.objects.get_or_create(name="__FIXME__unidentified_layer", layer_index_url="")
+ unknown_layer, _ = Layer.objects.get_or_create(name="Unidentified layer", layer_index_url="")
unknown_layer_version_obj, _ = Layer_Version.objects.get_or_create(layer = unknown_layer, build = self.internal_state['build'])
# append it so we don't run into this error again and again
@@ -847,9 +934,9 @@ class BuildInfoHelper(object):
logger.warn("buildinfohelper: cannot identify layer exception:%s ", nee)
- def store_started_build(self, event, consolelogfile):
+ def store_started_build(self, event, build_log_path):
assert '_pkgs' in vars(event)
- build_information = self._get_build_information(consolelogfile)
+ build_information = self._get_build_information(build_log_path)
build_obj = self.orm_wrapper.create_build_object(build_information, self.brbe, self.project)
@@ -869,7 +956,7 @@ class BuildInfoHelper(object):
target_information['targets'] = event._pkgs
target_information['build'] = build_obj
- self.internal_state['targets'] = self.orm_wrapper.create_target_objects(target_information)
+ self.internal_state['targets'] = self.orm_wrapper.get_or_create_targets(target_information)
# Save build configuration
data = self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0]
@@ -996,7 +1083,7 @@ class BuildInfoHelper(object):
task_information['disk_io'] = taskstats['disk_io']
if 'elapsed_time' in taskstats:
task_information['elapsed_time'] = taskstats['elapsed_time']
- self.orm_wrapper.get_update_task_object(task_information, True) # must exist
+ self.orm_wrapper.get_update_task_object(task_information)
def update_and_store_task(self, event):
assert 'taskfile' in vars(event)
@@ -1097,15 +1184,22 @@ class BuildInfoHelper(object):
# for all image targets
for target in self.internal_state['targets']:
if target.is_image:
+ pkgdata = BuildInfoHelper._get_data_from_event(event)['pkgdata']
+ imgdata = BuildInfoHelper._get_data_from_event(event)['imgdata'][target.target]
+ filedata = BuildInfoHelper._get_data_from_event(event)['filedata'][target.target]
+
try:
- pkgdata = BuildInfoHelper._get_data_from_event(event)['pkgdata']
- imgdata = BuildInfoHelper._get_data_from_event(event)['imgdata'][target.target]
self.orm_wrapper.save_target_package_information(self.internal_state['build'], target, imgdata, pkgdata, self.internal_state['recipes'])
- filedata = BuildInfoHelper._get_data_from_event(event)['filedata'][target.target]
+ except KeyError as e:
+ logger.warn("KeyError in save_target_package_information"
+ "%s ", e)
+
+ try:
self.orm_wrapper.save_target_file_information(self.internal_state['build'], target, filedata)
- except KeyError:
- # we must have not got the data for this image, nothing to save
- pass
+ except KeyError as e:
+ logger.warn("KeyError in save_target_file_information"
+ "%s ", e)
+
@@ -1306,7 +1400,9 @@ class BuildInfoHelper(object):
log_information = {}
log_information['build'] = self.internal_state['build']
- if event.levelno == formatter.ERROR:
+ if event.levelno == formatter.CRITICAL:
+ log_information['level'] = LogMessage.CRITICAL
+ elif event.levelno == formatter.ERROR:
log_information['level'] = LogMessage.ERROR
elif event.levelno == formatter.WARNING:
log_information['level'] = LogMessage.WARNING
@@ -1319,6 +1415,7 @@ class BuildInfoHelper(object):
log_information['pathname'] = event.pathname
log_information['lineno'] = event.lineno
logger.info("Logging error 2: %s", log_information)
+
self.orm_wrapper.create_logmessage(log_information)
def close(self, errorcode):
diff --git a/yocto-poky/bitbake/lib/bb/ui/knotty.py b/yocto-poky/bitbake/lib/bb/ui/knotty.py
index 2bee242eb..90c318376 100644
--- a/yocto-poky/bitbake/lib/bb/ui/knotty.py
+++ b/yocto-poky/bitbake/lib/bb/ui/knotty.py
@@ -230,7 +230,7 @@ def _log_settings_from_server(server):
if error:
logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error)
raise BaseException(error)
- consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"])
+ consolelogfile, error = server.runCommand(["getSetVariable", "BB_CONSOLELOG"])
if error:
logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error)
raise BaseException(error)
diff --git a/yocto-poky/bitbake/lib/bb/ui/toasterui.py b/yocto-poky/bitbake/lib/bb/ui/toasterui.py
index e0c278bb3..3d261503e 100644
--- a/yocto-poky/bitbake/lib/bb/ui/toasterui.py
+++ b/yocto-poky/bitbake/lib/bb/ui/toasterui.py
@@ -21,6 +21,7 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from __future__ import division
+import time
import sys
try:
import bb
@@ -43,8 +44,6 @@ featureSet = [bb.cooker.CookerFeatures.HOB_EXTRA_CACHES, bb.cooker.CookerFeature
logger = logging.getLogger("ToasterLogger")
interactive = sys.stdout.isatty()
-
-
def _log_settings_from_server(server):
# Get values of variables which control our output
includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
@@ -59,12 +58,56 @@ def _log_settings_from_server(server):
if error:
logger.error("Unable to get the value of BB_CONSOLELOG variable: %s", error)
raise BaseException(error)
- return includelogs, loglines, consolelogfile
+ return consolelogfile
+
+# create a log file for a single build and direct the logger at it;
+# log file name is timestamped to the millisecond (depending
+# on system clock accuracy) to ensure it doesn't overlap with
+# other log file names
+#
+# returns (log file, path to log file) for a build
+def _open_build_log(log_dir):
+ format_str = "%(levelname)s: %(message)s"
+
+ now = time.time()
+ now_ms = int((now - int(now)) * 1000)
+ time_str = time.strftime('build_%Y%m%d_%H%M%S', time.localtime(now))
+ log_file_name = time_str + ('.%d.log' % now_ms)
+ build_log_file_path = os.path.join(log_dir, log_file_name)
+
+ build_log = logging.FileHandler(build_log_file_path)
+
+ logformat = bb.msg.BBLogFormatter(format_str)
+ build_log.setFormatter(logformat)
+ bb.msg.addDefaultlogFilter(build_log)
+ logger.addHandler(build_log)
+
+ return (build_log, build_log_file_path)
+
+# stop logging to the build log if it exists
+def _close_build_log(build_log):
+ if build_log:
+ build_log.flush()
+ build_log.close()
+ logger.removeHandler(build_log)
+
+def main(server, eventHandler, params):
+ # set to a logging.FileHandler instance when a build starts;
+ # see _open_build_log()
+ build_log = None
+
+ # set to the log path when a build starts
+ build_log_file_path = None
-def main(server, eventHandler, params ):
helper = uihelper.BBUIHelper()
+ # TODO don't use log output to determine when bitbake has started
+ #
+ # WARNING: this log handler cannot be removed, as localhostbecontroller
+ # relies on output in the toaster_ui.log file to determine whether
+ # the bitbake server has started, which only happens if
+ # this logger is setup here (see the TODO in the loop below)
console = logging.StreamHandler(sys.stdout)
format_str = "%(levelname)s: %(message)s"
formatter = bb.msg.BBLogFormatter(format_str)
@@ -73,8 +116,6 @@ def main(server, eventHandler, params ):
logger.addHandler(console)
logger.setLevel(logging.INFO)
- _, _, consolelogfile = _log_settings_from_server(server)
-
# verify and warn
build_history_enabled = True
inheritlist, _ = server.runCommand(["getVariable", "INHERIT"])
@@ -87,8 +128,9 @@ def main(server, eventHandler, params ):
logger.error("ToasterUI can only work in observer mode")
return 1
-
+ # set to 1 when toasterui needs to shut down
main.shutdown = 0
+
interrupted = False
return_value = 0
errors = 0
@@ -98,25 +140,31 @@ def main(server, eventHandler, params ):
buildinfohelper = BuildInfoHelper(server, build_history_enabled)
- if buildinfohelper.brbe is not None and consolelogfile:
- # if we are under managed mode we have no other UI and we need to write our own file
- bb.utils.mkdirhier(os.path.dirname(consolelogfile))
- conlogformat = bb.msg.BBLogFormatter(format_str)
- consolelog = logging.FileHandler(consolelogfile)
- bb.msg.addDefaultlogFilter(consolelog)
- consolelog.setFormatter(conlogformat)
- logger.addHandler(consolelog)
-
+ # write our own log files into bitbake's log directory;
+ # we're only interested in the path to the parent directory of
+ # this file, as we're writing our own logs into the same directory
+ consolelogfile = _log_settings_from_server(server)
+ log_dir = os.path.dirname(consolelogfile)
+ bb.utils.mkdirhier(log_dir)
while True:
try:
event = eventHandler.waitEvent(0.25)
if first:
first = False
+
+ # TODO don't use log output to determine when bitbake has started
+ #
+ # this is the line localhostbecontroller needs to
+ # see in toaster_ui.log which it uses to decide whether
+ # the bitbake server has started...
logger.info("ToasterUI waiting for events")
if event is None:
if main.shutdown > 0:
+ # if shutting down, close any open build log first
+ _close_build_log(build_log)
+
break
continue
@@ -125,19 +173,32 @@ def main(server, eventHandler, params ):
# pylint: disable=protected-access
# the code will look into the protected variables of the event; no easy way around this
+ # we treat ParseStarted as the first event of toaster-triggered
+ # builds; that way we get the Build Configuration included in the log
+ # and any errors that occur before BuildStarted is fired
+ if isinstance(event, bb.event.ParseStarted):
+ if not (build_log and build_log_file_path):
+ build_log, build_log_file_path = _open_build_log(log_dir)
+ continue
+
if isinstance(event, bb.event.BuildStarted):
- buildinfohelper.store_started_build(event, consolelogfile)
+ # command-line builds don't fire a ParseStarted event,
+ # so we have to start the log file for those on BuildStarted instead
+ if not (build_log and build_log_file_path):
+ build_log, build_log_file_path = _open_build_log(log_dir)
+
+ buildinfohelper.store_started_build(event, build_log_file_path)
if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)):
buildinfohelper.update_and_store_task(event)
- logger.warn("Logfile for task %s", event.logfile)
+ logger.info("Logfile for task %s", event.logfile)
continue
if isinstance(event, bb.build.TaskBase):
logger.info(event._message)
if isinstance(event, bb.event.LogExecTTY):
- logger.warn(event.msg)
+ logger.info(event.msg)
continue
if isinstance(event, logging.LogRecord):
@@ -145,10 +206,12 @@ def main(server, eventHandler, params ):
event.levelno = formatter.ERROR
buildinfohelper.store_log_event(event)
+
if event.levelno >= formatter.ERROR:
errors = errors + 1
elif event.levelno == formatter.WARNING:
warnings = warnings + 1
+
# For "normal" logging conditions, don't show note logs from tasks
# but do show them if the user has changed the default log level to
# include verbose/debug messages
@@ -169,8 +232,6 @@ def main(server, eventHandler, params ):
# timing and error informations from the parsing phase in Toaster
if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)):
continue
- if isinstance(event, bb.event.ParseStarted):
- continue
if isinstance(event, bb.event.ParseProgress):
continue
if isinstance(event, bb.event.ParseCompleted):
@@ -246,6 +307,12 @@ def main(server, eventHandler, params ):
errorcode = 1
logger.error("Command execution failed: %s", event.error)
+ # turn off logging to the current build log
+ _close_build_log(build_log)
+
+ # reset ready for next BuildStarted
+ build_log = None
+
# update the build info helper on BuildCompleted, not on CommandXXX
buildinfohelper.update_build_information(event, errors, warnings, taskfailures)
buildinfohelper.close(errorcode)
@@ -254,7 +321,6 @@ def main(server, eventHandler, params ):
# we start a new build info
if buildinfohelper.brbe is not None:
-
logger.debug("ToasterUI under BuildEnvironment management - exiting after the build")
server.terminateServer()
else:
@@ -296,8 +362,9 @@ def main(server, eventHandler, params ):
continue
if isinstance(event, bb.cooker.CookerExit):
- # exit when the server exits
- break
+ # shutdown when bitbake server shuts down
+ main.shutdown = 1
+ continue
# ignore
if isinstance(event, (bb.event.BuildBase,
@@ -308,14 +375,15 @@ def main(server, eventHandler, params ):
bb.event.OperationProgress,
bb.command.CommandFailed,
bb.command.CommandExit,
- bb.command.CommandCompleted)):
+ bb.command.CommandCompleted,
+ bb.event.ReachableStamps)):
continue
if isinstance(event, bb.event.DepTreeGenerated):
buildinfohelper.store_dependency_information(event)
continue
- logger.error("Unknown event: %s", event)
+ logger.warn("Unknown event: %s", event)
return_value += 1
except EnvironmentError as ioerror:
@@ -335,7 +403,7 @@ def main(server, eventHandler, params ):
if tb is not None:
curr = tb
while curr is not None:
- logger.warn("Error data dump %s\n%s\n" , traceback.format_tb(curr,1), pformat(curr.tb_frame.f_locals))
+ logger.error("Error data dump %s\n%s\n" , traceback.format_tb(curr,1), pformat(curr.tb_frame.f_locals))
curr = curr.tb_next
# save them to database, if possible; if it fails, we already logged to console.
@@ -347,9 +415,8 @@ def main(server, eventHandler, params ):
# make sure we return with an error
return_value += 1
- if interrupted:
- if return_value == 0:
- return_value += 1
+ if interrupted and return_value == 0:
+ return_value += 1
logger.warn("Return value is %d", return_value)
return return_value
diff --git a/yocto-poky/bitbake/lib/bb/utils.py b/yocto-poky/bitbake/lib/bb/utils.py
index 91faa494c..31ec2b7c9 100644
--- a/yocto-poky/bitbake/lib/bb/utils.py
+++ b/yocto-poky/bitbake/lib/bb/utils.py
@@ -1177,7 +1177,7 @@ def edit_metadata(meta_lines, variables, varfunc, match_overrides=False):
if not skip:
if checkspc:
checkspc = False
- if newlines[-1] == '\n' and line == '\n':
+ if newlines and newlines[-1] == '\n' and line == '\n':
# Squash blank line if there are two consecutive blanks after a removal
continue
newlines.append(line)
@@ -1201,7 +1201,19 @@ def edit_metadata_file(meta_file, variables, varfunc):
def edit_bblayers_conf(bblayers_conf, add, remove):
- """Edit bblayers.conf, adding and/or removing layers"""
+ """Edit bblayers.conf, adding and/or removing layers
+ Parameters:
+ bblayers_conf: path to bblayers.conf file to edit
+ add: layer path (or list of layer paths) to add; None or empty
+ list to add nothing
+ remove: layer path (or list of layer paths) to remove; None or
+ empty list to remove nothing
+ Returns a tuple:
+ notadded: list of layers specified to be added but weren't
+ (because they were already in the list)
+ notremoved: list of layers that were specified to be removed
+ but weren't (because they weren't in the list)
+ """
import fnmatch
@@ -1210,6 +1222,13 @@ def edit_bblayers_conf(bblayers_conf, add, remove):
pth = pth[:-1]
return pth
+ approved = bb.utils.approved_variables()
+ def canonicalise_path(pth):
+ pth = remove_trailing_sep(pth)
+ if 'HOME' in approved and '~' in pth:
+ pth = os.path.expanduser(pth)
+ return pth
+
def layerlist_param(value):
if not value:
return []
@@ -1218,48 +1237,80 @@ def edit_bblayers_conf(bblayers_conf, add, remove):
else:
return [remove_trailing_sep(value)]
- notadded = []
- notremoved = []
-
addlayers = layerlist_param(add)
removelayers = layerlist_param(remove)
# Need to use a list here because we can't set non-local variables from a callback in python 2.x
bblayercalls = []
+ removed = []
+ plusequals = False
+ orig_bblayers = []
+
+ def handle_bblayers_firstpass(varname, origvalue, op, newlines):
+ bblayercalls.append(op)
+ if op == '=':
+ del orig_bblayers[:]
+ orig_bblayers.extend([canonicalise_path(x) for x in origvalue.split()])
+ return (origvalue, None, 2, False)
def handle_bblayers(varname, origvalue, op, newlines):
- bblayercalls.append(varname)
updated = False
bblayers = [remove_trailing_sep(x) for x in origvalue.split()]
if removelayers:
for removelayer in removelayers:
- matched = False
for layer in bblayers:
- if fnmatch.fnmatch(layer, removelayer):
+ if fnmatch.fnmatch(canonicalise_path(layer), canonicalise_path(removelayer)):
updated = True
- matched = True
bblayers.remove(layer)
+ removed.append(removelayer)
break
- if not matched:
- notremoved.append(removelayer)
- if addlayers:
+ if addlayers and not plusequals:
for addlayer in addlayers:
if addlayer not in bblayers:
updated = True
bblayers.append(addlayer)
- else:
- notadded.append(addlayer)
+ del addlayers[:]
if updated:
+ if op == '+=' and not bblayers:
+ bblayers = None
return (bblayers, None, 2, False)
else:
return (origvalue, None, 2, False)
- edit_metadata_file(bblayers_conf, ['BBLAYERS'], handle_bblayers)
+ with open(bblayers_conf, 'r') as f:
+ (_, newlines) = edit_metadata(f, ['BBLAYERS'], handle_bblayers_firstpass)
if not bblayercalls:
raise Exception('Unable to find BBLAYERS in %s' % bblayers_conf)
+ # Try to do the "smart" thing depending on how the user has laid out
+ # their bblayers.conf file
+ if bblayercalls.count('+=') > 1:
+ plusequals = True
+
+ removelayers_canon = [canonicalise_path(layer) for layer in removelayers]
+ notadded = []
+ for layer in addlayers:
+ layer_canon = canonicalise_path(layer)
+ if layer_canon in orig_bblayers and not layer_canon in removelayers_canon:
+ notadded.append(layer)
+ notadded_canon = [canonicalise_path(layer) for layer in notadded]
+ addlayers[:] = [layer for layer in addlayers if canonicalise_path(layer) not in notadded_canon]
+
+ (updated, newlines) = edit_metadata(newlines, ['BBLAYERS'], handle_bblayers)
+ if addlayers:
+ # Still need to add these
+ for addlayer in addlayers:
+ newlines.append('BBLAYERS += "%s"\n' % addlayer)
+ updated = True
+
+ if updated:
+ with open(bblayers_conf, 'w') as f:
+ f.writelines(newlines)
+
+ notremoved = list(set(removelayers) - set(removed))
+
return (notadded, notremoved)
@@ -1310,3 +1361,27 @@ def signal_on_parent_exit(signame):
result = cdll['libc.so.6'].prctl(PR_SET_PDEATHSIG, signum)
if result != 0:
raise PrCtlError('prctl failed with error code %s' % result)
+
+#
+# Manually call the ioprio syscall. We could depend on other libs like psutil
+# however this gets us enough of what we need to bitbake for now without the
+# dependency
+#
+_unamearch = os.uname()[4]
+IOPRIO_WHO_PROCESS = 1
+IOPRIO_CLASS_SHIFT = 13
+
+def ioprio_set(who, cls, value):
+ NR_ioprio_set = None
+ if _unamearch == "x86_64":
+ NR_ioprio_set = 251
+ elif _unamearch[0] == "i" and _unamearch[2:3] == "86":
+ NR_ioprio_set = 289
+
+ if NR_ioprio_set:
+ ioprio = value | (cls << IOPRIO_CLASS_SHIFT)
+ rc = cdll['libc.so.6'].syscall(NR_ioprio_set, IOPRIO_WHO_PROCESS, who, ioprio)
+ if rc != 0:
+ raise ValueError("Unable to set ioprio, syscall returned %s" % rc)
+ else:
+ bb.warn("Unable to set IO Prio for arch %s" % _unamearch)