diff options
author | Patrick Williams <patrick@stwcx.xyz> | 2016-03-30 23:21:19 +0300 |
---|---|---|
committer | Patrick Williams <patrick@stwcx.xyz> | 2016-03-30 23:21:19 +0300 |
commit | b4a027550acf2c1051c34f997b8e7e845017af4b (patch) | |
tree | 9e38d3c17b42cb1e6765620a87e908973a93c821 /yocto-poky/bitbake/lib/bb | |
parent | 2fe86d90044af218ced8f42fdded6b136f1046d2 (diff) | |
parent | f1e5d6968976c2341c6d554bfcc8895f1b33c26b (diff) | |
download | openbmc-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__.py | 2 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/build.py | 7 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/command.py | 10 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/cooker.py | 32 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/cookerdata.py | 6 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/fetch2/__init__.py | 2 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/fetch2/hg.py | 1 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/fetch2/svn.py | 9 | ||||
-rwxr-xr-x | yocto-poky/bitbake/lib/bb/main.py | 7 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/runqueue.py | 9 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/siggen.py | 10 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/taskdata.py | 12 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/tests/utils.py | 203 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/tinfoil.py | 9 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py | 229 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/ui/knotty.py | 2 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/ui/toasterui.py | 127 | ||||
-rw-r--r-- | yocto-poky/bitbake/lib/bb/utils.py | 105 |
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) |