meta-openembedded and poky: subtree updates

Squash of the following due to dependencies among them
and OpenBMC changes:

meta-openembedded: subtree update:d0748372d2..9201611135
meta-openembedded: subtree update:9201611135..17fd382f34
poky: subtree update:9052e5b32a..2e11d97b6c
poky: subtree update:2e11d97b6c..a8544811d7

The change log was too large for the jenkins plugin
to handle therefore it has been removed. Here is
the first and last commit of each subtree:

meta-openembedded:d0748372d2
      cppzmq: bump to version 4.6.0
meta-openembedded:17fd382f34
      mpv: Remove X11 dependency
poky:9052e5b32a
      package_ipk: Remove pointless comment to trigger rebuild
poky:a8544811d7
      pbzip2: Fix license warning

Change-Id: If0fc6c37629642ee207a4ca2f7aa501a2c673cd6
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
diff --git a/poky/bitbake/lib/bb/ui/buildinfohelper.py b/poky/bitbake/lib/bb/ui/buildinfohelper.py
index 5cbca97..82c62e3 100644
--- a/poky/bitbake/lib/bb/ui/buildinfohelper.py
+++ b/poky/bitbake/lib/bb/ui/buildinfohelper.py
@@ -935,7 +935,7 @@
 
             # only reset the build name if the one on the server is actually
             # a valid value for the build_name field
-            if build_name != None:
+            if build_name is not None:
                 build_info['build_name'] = build_name
                 changed = True
 
@@ -1194,7 +1194,7 @@
         evdata = BuildInfoHelper._get_data_from_event(event)
 
         for t in self.internal_state['targets']:
-            if t.is_image == True:
+            if t.is_image:
                 output_files = list(evdata.keys())
                 for output in output_files:
                     if t.target in output and 'rootfs' in output and not output.endswith(".manifest"):
@@ -1236,7 +1236,7 @@
                 task_information['outcome'] = Task.OUTCOME_PREBUILT
         else:
             task_information['task_executed'] = True
-            if 'noexec' in vars(event) and event.noexec == True:
+            if 'noexec' in vars(event) and event.noexec:
                 task_information['task_executed'] = False
                 task_information['outcome'] = Task.OUTCOME_EMPTY
                 task_information['script_type'] = Task.CODING_NA
@@ -1776,7 +1776,7 @@
         image_file_extensions_unique = {}
         image_fstypes = self.server.runCommand(
             ['getVariable', 'IMAGE_FSTYPES'])[0]
-        if image_fstypes != None:
+        if image_fstypes is not None:
             image_types_str = image_fstypes.strip()
             image_file_extensions = re.sub(r' {2,}', ' ', image_types_str)
             image_file_extensions_unique = set(image_file_extensions.split(' '))
diff --git a/poky/bitbake/lib/bb/ui/knotty.py b/poky/bitbake/lib/bb/ui/knotty.py
index 35736ad..87e873d 100644
--- a/poky/bitbake/lib/bb/ui/knotty.py
+++ b/poky/bitbake/lib/bb/ui/knotty.py
@@ -12,7 +12,6 @@
 
 import os
 import sys
-import xmlrpc.client as xmlrpclib
 import logging
 import progressbar
 import signal
@@ -35,15 +34,15 @@
         self.msg = msg
         self.extrapos = extrapos
         if not widgets:
-            widgets = [progressbar.Percentage(), ' ', progressbar.Bar(), ' ',
-            progressbar.ETA()]
-            self.extrapos = 4
+            widgets = [': ', progressbar.Percentage(), ' ', progressbar.Bar(),
+                       ' ', progressbar.ETA()]
+            self.extrapos = 5
 
         if resize_handler:
             self._resize_default = resize_handler
         else:
             self._resize_default = signal.getsignal(signal.SIGWINCH)
-        progressbar.ProgressBar.__init__(self, maxval, [self.msg + ": "] + widgets, fd=sys.stdout)
+        progressbar.ProgressBar.__init__(self, maxval, [self.msg] + widgets, fd=sys.stdout)
 
     def _handle_resize(self, signum=None, frame=None):
         progressbar.ProgressBar._handle_resize(self, signum, frame)
@@ -110,12 +109,11 @@
 
 
 class InteractConsoleLogFilter(logging.Filter):
-    def __init__(self, tf, format):
+    def __init__(self, tf):
         self.tf = tf
-        self.format = format
 
     def filter(self, record):
-        if record.levelno == self.format.NOTE and (record.msg.startswith("Running") or record.msg.startswith("recipe ")):
+        if record.levelno == bb.msg.BBLogFormatter.NOTE and (record.msg.startswith("Running") or record.msg.startswith("recipe ")):
             return False
         self.tf.clearFooter()
         return True
@@ -151,7 +149,7 @@
                 cr = (25, 80)
         return cr
 
-    def __init__(self, main, helper, console, errconsole, format, quiet):
+    def __init__(self, main, helper, handlers, quiet):
         self.main = main
         self.helper = helper
         self.cuu = None
@@ -181,7 +179,11 @@
             termios.tcsetattr(fd, termios.TCSADRAIN, new)
             curses.setupterm()
             if curses.tigetnum("colors") > 2:
-                format.enable_color()
+                for h in handlers:
+                    try:
+                        h.formatter.enable_color()
+                    except AttributeError:
+                        pass
             self.ed = curses.tigetstr("ed")
             if self.ed:
                 self.cuu = curses.tigetstr("cuu")
@@ -197,10 +199,9 @@
             self.interactive = False
             bb.note("Unable to use interactive mode for this terminal, using fallback")
             return
-        if console:
-            console.addFilter(InteractConsoleLogFilter(self, format))
-        if errconsole:
-            errconsole.addFilter(InteractConsoleLogFilter(self, format))
+
+        for h in handlers:
+            h.addFilter(InteractConsoleLogFilter(self))
 
         self.main_progress = None
 
@@ -255,19 +256,19 @@
                 start_time = activetasks[t].get("starttime", None)
                 if not pbar or pbar.bouncing != (progress < 0):
                     if progress < 0:
-                        pbar = BBProgress("0: %s (pid %s) " % (activetasks[t]["title"], t), 100, widgets=[progressbar.BouncingSlider(), ''], extrapos=2, resize_handler=self.sigwinch_handle)
+                        pbar = BBProgress("0: %s (pid %s)" % (activetasks[t]["title"], activetasks[t]["pid"]), 100, widgets=[' ', progressbar.BouncingSlider(), ''], extrapos=3, resize_handler=self.sigwinch_handle)
                         pbar.bouncing = True
                     else:
-                        pbar = BBProgress("0: %s (pid %s) " % (activetasks[t]["title"], t), 100, widgets=[progressbar.Percentage(), ' ', progressbar.Bar(), ''], extrapos=4, resize_handler=self.sigwinch_handle)
+                        pbar = BBProgress("0: %s (pid %s)" % (activetasks[t]["title"], activetasks[t]["pid"]), 100, widgets=[' ', progressbar.Percentage(), ' ', progressbar.Bar(), ''], extrapos=5, resize_handler=self.sigwinch_handle)
                         pbar.bouncing = False
                     activetasks[t]["progressbar"] = pbar
                 tasks.append((pbar, progress, rate, start_time))
             else:
                 start_time = activetasks[t].get("starttime", None)
                 if start_time:
-                    tasks.append("%s - %s (pid %s)" % (activetasks[t]["title"], self.elapsed(currenttime - start_time), t))
+                    tasks.append("%s - %s (pid %s)" % (activetasks[t]["title"], self.elapsed(currenttime - start_time), activetasks[t]["pid"]))
                 else:
-                    tasks.append("%s (pid %s)" % (activetasks[t]["title"], t))
+                    tasks.append("%s (pid %s)" % (activetasks[t]["title"], activetasks[t]["pid"]))
 
         if self.main.shutdown:
             content = "Waiting for %s running tasks to finish:" % len(activetasks)
@@ -363,7 +364,11 @@
     if error:
         logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error)
         raise BaseException(error)
-    return includelogs, loglines, consolelogfile
+    logconfigfile, error = server.runCommand([cmd, "BB_LOGCONFIG"])
+    if error:
+        logger.error("Unable to get the value of BB_LOGCONFIG variable: %s" % error)
+        raise BaseException(error)
+    return includelogs, loglines, consolelogfile, logconfigfile
 
 _evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
               "bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
@@ -380,7 +385,148 @@
     if not params.observe_only:
         params.updateToServer(server, os.environ.copy())
 
-    includelogs, loglines, consolelogfile = _log_settings_from_server(server, params.observe_only)
+    includelogs, loglines, consolelogfile, logconfigfile = _log_settings_from_server(server, params.observe_only)
+
+    loglevel, _ = bb.msg.constructLogOptions()
+
+    if params.options.quiet == 0:
+        console_loglevel = loglevel
+    elif params.options.quiet > 2:
+        console_loglevel = bb.msg.BBLogFormatter.ERROR
+    else:
+        console_loglevel = bb.msg.BBLogFormatter.WARNING
+
+    logconfig = {
+        "version": 1,
+        "handlers": {
+            "BitBake.console": {
+                "class": "logging.StreamHandler",
+                "formatter": "BitBake.consoleFormatter",
+                "level": console_loglevel,
+                "stream": "ext://sys.stdout",
+                "filters": ["BitBake.stdoutFilter"],
+                ".": {
+                    "is_console": True,
+                },
+            },
+            "BitBake.errconsole": {
+                "class": "logging.StreamHandler",
+                "formatter": "BitBake.consoleFormatter",
+                "level": loglevel,
+                "stream": "ext://sys.stderr",
+                "filters": ["BitBake.stderrFilter"],
+                ".": {
+                    "is_console": True,
+                },
+            },
+            # This handler can be used if specific loggers should print on
+            # the console at a lower severity than the default. It will
+            # display any messages sent to it that are lower than then
+            # BitBake.console logging level (so as to prevent duplication of
+            # messages). Nothing is attached to this handler by default
+            "BitBake.verbconsole": {
+                "class": "logging.StreamHandler",
+                "formatter": "BitBake.consoleFormatter",
+                "level": 1,
+                "stream": "ext://sys.stdout",
+                "filters": ["BitBake.verbconsoleFilter"],
+                ".": {
+                    "is_console": True,
+                },
+            },
+        },
+        "formatters": {
+            # This format instance will get color output enabled by the
+            # terminal
+            "BitBake.consoleFormatter" : {
+                "()": "bb.msg.BBLogFormatter",
+                "format": "%(levelname)s: %(message)s"
+            },
+            # The file log requires a separate instance so that it doesn't get
+            # color enabled
+            "BitBake.logfileFormatter": {
+                "()": "bb.msg.BBLogFormatter",
+                "format": "%(levelname)s: %(message)s"
+            }
+        },
+        "filters": {
+            "BitBake.stdoutFilter": {
+                "()": "bb.msg.LogFilterLTLevel",
+                "level": "ERROR"
+            },
+            "BitBake.stderrFilter": {
+                "()": "bb.msg.LogFilterGEQLevel",
+                "level": "ERROR"
+            },
+            "BitBake.verbconsoleFilter": {
+                "()": "bb.msg.LogFilterLTLevel",
+                "level": console_loglevel
+            },
+        },
+        "loggers": {
+            "BitBake": {
+                "level": loglevel,
+                "handlers": ["BitBake.console", "BitBake.errconsole"],
+            }
+        },
+        "disable_existing_loggers": False
+    }
+
+    # Enable the console log file if enabled
+    if consolelogfile and not params.options.show_environment and not params.options.show_versions:
+        logconfig = bb.msg.mergeLoggingConfig(logconfig, {
+                "version": 1,
+                "handlers" : {
+                    "BitBake.consolelog": {
+                        "class": "logging.FileHandler",
+                        "formatter": "BitBake.logfileFormatter",
+                        "level": loglevel,
+                        "filename": consolelogfile,
+                    },
+                    # Just like verbconsole, anything sent here will go to the
+                    # log file, unless it would go to BitBake.consolelog
+                    "BitBake.verbconsolelog" : {
+                        "class": "logging.FileHandler",
+                        "formatter": "BitBake.logfileFormatter",
+                        "level": 1,
+                        "filename": consolelogfile,
+                        "filters": ["BitBake.verbconsolelogFilter"],
+                    },
+                },
+                "filters": {
+                    "BitBake.verbconsolelogFilter": {
+                        "()": "bb.msg.LogFilterLTLevel",
+                        "level": loglevel,
+                    },
+                },
+                "loggers": {
+                    "BitBake": {
+                        "handlers": ["BitBake.consolelog"],
+                    },
+
+                    # Other interesting things that we want to keep an eye on
+                    # in the log files in case someone has an issue, but not
+                    # necessarily show to the user on the console
+                    "BitBake.SigGen.HashEquiv": {
+                        "level": "VERBOSE",
+                        "handlers": ["BitBake.verbconsolelog"],
+                    },
+                    "BitBake.RunQueue.HashEquiv": {
+                        "level": "VERBOSE",
+                        "handlers": ["BitBake.verbconsolelog"],
+                    }
+                }
+            })
+
+        bb.utils.mkdirhier(os.path.dirname(consolelogfile))
+        loglink = os.path.join(os.path.dirname(consolelogfile), 'console-latest.log')
+        bb.utils.remove(loglink)
+        try:
+           os.symlink(os.path.basename(consolelogfile), loglink)
+        except OSError:
+           pass
+
+    conf = bb.msg.setLoggingConfig(logconfig, logconfigfile)
 
     if sys.stdin.isatty() and sys.stdout.isatty():
         log_exec_tty = True
@@ -389,23 +535,9 @@
 
     helper = uihelper.BBUIHelper()
 
-    console = logging.StreamHandler(sys.stdout)
-    errconsole = logging.StreamHandler(sys.stderr)
-    format_str = "%(levelname)s: %(message)s"
-    format = bb.msg.BBLogFormatter(format_str)
-    if params.options.quiet == 0:
-        forcelevel = None
-    elif params.options.quiet > 2:
-        forcelevel = bb.msg.BBLogFormatter.ERROR
-    else:
-        forcelevel = bb.msg.BBLogFormatter.WARNING
-    bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut, forcelevel)
-    bb.msg.addDefaultlogFilter(errconsole, bb.msg.BBLogFilterStdErr)
-    console.setFormatter(format)
-    errconsole.setFormatter(format)
-    if not bb.msg.has_console_handler(logger):
-        logger.addHandler(console)
-        logger.addHandler(errconsole)
+    # Look for the specially designated handlers which need to be passed to the
+    # terminal handler
+    console_handlers = [h for h in conf.config['handlers'].values() if getattr(h, 'is_console', False)]
 
     bb.utils.set_process_name("KnottyUI")
 
@@ -413,24 +545,14 @@
         server.terminateServer()
         return
 
-    consolelog = None
-    if consolelogfile and not params.options.show_environment and not params.options.show_versions:
-        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)
-        loglink = os.path.join(os.path.dirname(consolelogfile), 'console-latest.log')
-        bb.utils.remove(loglink)
-        try:
-           os.symlink(os.path.basename(consolelogfile), loglink)
-        except OSError:
-           pass
-
     llevel, debug_domains = bb.msg.constructLogOptions()
     server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
 
+    # The logging_tree module is *extremely* helpful in debugging logging
+    # domains. Uncomment here to dump the logging tree when bitbake starts
+    #import logging_tree
+    #logging_tree.printout()
+
     universe = False
     if not params.observe_only:
         params.updateFromServer(server)
@@ -448,7 +570,7 @@
         if error:
             logger.error("Command '%s' failed: %s" % (cmdline, error))
             return 1
-        elif ret != True:
+        elif not ret:
             logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
             return 1
 
@@ -465,7 +587,7 @@
     printinterval = 5000
     lastprint = time.time()
 
-    termfilter = tf(main, helper, console, errconsole, format, params.options.quiet)
+    termfilter = tf(main, helper, console_handlers, params.options.quiet)
     atexit.register(termfilter.finish)
 
     while True:
@@ -477,7 +599,8 @@
             if event is None:
                 if main.shutdown > 1:
                     break
-                termfilter.updateFooter()
+                if not parseprogress:
+                    termfilter.updateFooter()
                 event = eventHandler.waitEvent(0.25)
                 if event is None:
                     continue
@@ -503,26 +626,26 @@
             if isinstance(event, logging.LogRecord):
                 lastprint = time.time()
                 printinterval = 5000
-                if event.levelno >= format.ERROR:
+                if event.levelno >= bb.msg.BBLogFormatter.ERROR:
                     errors = errors + 1
                     return_value = 1
-                elif event.levelno == format.WARNING:
+                elif event.levelno == bb.msg.BBLogFormatter.WARNING:
                     warnings = warnings + 1
 
                 if event.taskpid != 0:
                     # 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
-                    if event.levelno <= format.NOTE and (event.levelno < llevel or (event.levelno == format.NOTE and llevel != format.VERBOSE)):
+                    if event.levelno <= bb.msg.BBLogFormatter.NOTE and (event.levelno < llevel or (event.levelno == bb.msg.BBLogFormatter.NOTE and llevel != bb.msg.BBLogFormatter.VERBOSE)):
                         continue
 
                     # Prefix task messages with recipe/task
-                    if event.taskpid in helper.running_tasks and event.levelno != format.PLAIN:
-                        taskinfo = helper.running_tasks[event.taskpid]
+                    if event.taskpid in helper.pidmap and event.levelno != bb.msg.BBLogFormatter.PLAIN:
+                        taskinfo = helper.running_tasks[helper.pidmap[event.taskpid]]
                         event.msg = taskinfo['title'] + ': ' + event.msg
                 if hasattr(event, 'fn'):
                     event.msg = event.fn + ': ' + event.msg
-                logger.handle(event)
+                logging.getLogger(event.name).handle(event)
                 continue
 
             if isinstance(event, bb.build.TaskFailedSilent):
@@ -539,6 +662,7 @@
                     continue
                 if event.total == 0:
                     continue
+                termfilter.clearFooter()
                 parseprogress = new_progress("Parsing recipes", event.total).start()
                 continue
             if isinstance(event, bb.event.ParseProgress):
@@ -589,6 +713,7 @@
             if isinstance(event, bb.command.CommandExit):
                 if not return_value:
                     return_value = event.exitcode
+                main.shutdown = 2
                 continue
             if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)):
                 main.shutdown = 2
@@ -638,6 +763,7 @@
             if isinstance(event, bb.event.ProcessStarted):
                 if params.options.quiet > 1:
                     continue
+                termfilter.clearFooter()
                 parseprogress = new_progress(event.processname, event.total)
                 parseprogress.start(False)
                 continue
@@ -745,8 +871,6 @@
         if e.errno == errno.EPIPE:
             pass
 
-    if consolelog:
-        logger.removeHandler(consolelog)
-        consolelog.close()
+    logging.shutdown()
 
     return return_value
diff --git a/poky/bitbake/lib/bb/ui/ncurses.py b/poky/bitbake/lib/bb/ui/ncurses.py
index c422732..da4fbea 100644
--- a/poky/bitbake/lib/bb/ui/ncurses.py
+++ b/poky/bitbake/lib/bb/ui/ncurses.py
@@ -37,7 +37,7 @@
 
 
 import logging
-import os, sys, itertools, time, subprocess
+import os, sys, itertools, time
 
 try:
     import curses
@@ -46,7 +46,6 @@
 
 import bb
 import xmlrpc.client
-from bb import ui
 from bb.ui import uihelper
 
 parsespin = itertools.cycle( r'|/-\\' )
@@ -239,7 +238,7 @@
             if error:
                 print("Error running command '%s': %s" % (cmdline, error))
                 return
-            elif ret != True:
+            elif not ret:
                 print("Couldn't get default commandlind! %s" % ret)
                 return
         except xmlrpc.client.Fault as x:
diff --git a/poky/bitbake/lib/bb/ui/taskexp.py b/poky/bitbake/lib/bb/ui/taskexp.py
index 50a943c..8fff244 100644
--- a/poky/bitbake/lib/bb/ui/taskexp.py
+++ b/poky/bitbake/lib/bb/ui/taskexp.py
@@ -11,10 +11,8 @@
 import gi
 gi.require_version('Gtk', '3.0')
 from gi.repository import Gtk, Gdk, GObject
-from multiprocessing import Queue
 import threading
 from xmlrpc import client
-import time
 import bb
 import bb.event
 
@@ -202,7 +200,7 @@
         if error:
             print("Error running command '%s': %s" % (cmdline, error))
             return 1
-        elif ret != True:
+        elif not ret:
             print("Error running command '%s': returned %s" % (cmdline, ret))
             return 1
     except client.Fault as x:
diff --git a/poky/bitbake/lib/bb/ui/teamcity.py b/poky/bitbake/lib/bb/ui/teamcity.py
new file mode 100644
index 0000000..1854292
--- /dev/null
+++ b/poky/bitbake/lib/bb/ui/teamcity.py
@@ -0,0 +1,398 @@
+#
+# TeamCity UI Implementation
+#
+# Implements a TeamCity frontend for the BitBake utility, via service messages.
+# See https://www.jetbrains.com/help/teamcity/build-script-interaction-with-teamcity.html
+#
+# Based on ncurses.py and knotty.py, variously by Michael Lauer and Richard Purdie
+#
+# Copyright (C) 2006 Michael 'Mickey' Lauer
+# Copyright (C) 2006-2012 Richard Purdie
+# Copyright (C) 2018-2020 Agilent Technologies, Inc.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Author: Chris Laplante <chris.laplante@agilent.com>
+
+from __future__ import division
+
+import datetime
+import logging
+import math
+import os
+import re
+import sys
+import xmlrpc.client
+from collections import deque
+
+import bb
+import bb.build
+import bb.command
+import bb.cooker
+import bb.event
+import bb.exceptions
+import bb.runqueue
+from bb.ui import uihelper
+
+logger = logging.getLogger("BitBake")
+
+
+class TeamCityUI:
+    def __init__(self):
+        self._block_stack = []
+        self._last_progress_state = None
+
+    @classmethod
+    def escape_service_value(cls, value):
+        """
+        Escape a value for inclusion in a service message. TeamCity uses the vertical pipe character for escaping.
+        See: https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-Escapedvalues
+        """
+        return re.sub(r"(['|\[\]])", r"|\1", value).replace("\n", "|n").replace("\r", "|r")
+
+    @classmethod
+    def emit_service_message(cls, message_type, **kwargs):
+        print(cls.format_service_message(message_type, **kwargs), flush=True)
+
+    @classmethod
+    def format_service_message(cls, message_type, **kwargs):
+        payload = " ".join(["{0}='{1}'".format(k, cls.escape_service_value(v)) for k, v in kwargs.items()])
+        return "##teamcity[{0} {1}]".format(message_type, payload)
+
+    @classmethod
+    def emit_simple_service_message(cls, message_type, message):
+        print(cls.format_simple_service_message(message_type, message), flush=True)
+
+    @classmethod
+    def format_simple_service_message(cls, message_type, message):
+        return "##teamcity[{0} '{1}']".format(message_type, cls.escape_service_value(message))
+
+    @classmethod
+    def format_build_message(cls, text, status):
+        return cls.format_service_message("message", text=text, status=status)
+
+    def block_start(self, name):
+        self._block_stack.append(name)
+        self.emit_service_message("blockOpened", name=name)
+
+    def block_end(self):
+        if self._block_stack:
+            name = self._block_stack.pop()
+            self.emit_service_message("blockClosed", name=name)
+
+    def progress(self, message, percent, extra=None):
+        now = datetime.datetime.now()
+        percent = "{0: >3.0f}".format(percent)
+
+        report = False
+        if not self._last_progress_state \
+                or (self._last_progress_state[0] == message
+                    and self._last_progress_state[1] != percent
+                    and (now - self._last_progress_state[2]).microseconds >= 5000) \
+                or self._last_progress_state[0] != message:
+            report = True
+            self._last_progress_state = (message, percent, now)
+
+        if report or percent in [0, 100]:
+            self.emit_simple_service_message("progressMessage", "{0}: {1}%{2}".format(message, percent, extra or ""))
+
+
+class TeamcityLogFormatter(logging.Formatter):
+    def format(self, record):
+        details = ""
+        if hasattr(record, 'bb_exc_formatted'):
+            details = ''.join(record.bb_exc_formatted)
+        elif hasattr(record, 'bb_exc_info'):
+            etype, value, tb = record.bb_exc_info
+            formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
+            details = ''.join(formatted)
+
+        if record.levelno in [bb.msg.BBLogFormatter.ERROR, bb.msg.BBLogFormatter.CRITICAL]:
+            # ERROR gets a separate errorDetails field
+            msg = TeamCityUI.format_service_message("message", text=record.getMessage(), status="ERROR",
+                                                    errorDetails=details)
+        else:
+            payload = record.getMessage()
+            if details:
+                payload += "\n" + details
+            if record.levelno == bb.msg.BBLogFormatter.PLAIN:
+                msg = payload
+            elif record.levelno == bb.msg.BBLogFormatter.WARNING:
+                msg = TeamCityUI.format_service_message("message", text=payload, status="WARNING")
+            else:
+                msg = TeamCityUI.format_service_message("message", text=payload, status="NORMAL")
+
+        return msg
+
+
+_evt_list = ["bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
+             "bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
+             "bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted",
+             "bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed",
+             "bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit",
+             "bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted",
+             "bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed",
+             "bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent",
+             "bb.build.TaskProgress", "bb.event.ProcessStarted", "bb.event.ProcessProgress", "bb.event.ProcessFinished"]
+
+
+def _log_settings_from_server(server):
+    # Get values of variables which control our output
+    includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
+    if error:
+        logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error)
+        raise BaseException(error)
+    loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
+    if error:
+        logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error)
+        raise BaseException(error)
+    return includelogs, loglines
+
+
+def main(server, eventHandler, params):
+    params.updateToServer(server, os.environ.copy())
+
+    includelogs, loglines = _log_settings_from_server(server)
+
+    ui = TeamCityUI()
+
+    helper = uihelper.BBUIHelper()
+
+    console = logging.StreamHandler(sys.stdout)
+    errconsole = logging.StreamHandler(sys.stderr)
+    format = TeamcityLogFormatter()
+    if params.options.quiet == 0:
+        forcelevel = None
+    elif params.options.quiet > 2:
+        forcelevel = bb.msg.BBLogFormatter.ERROR
+    else:
+        forcelevel = bb.msg.BBLogFormatter.WARNING
+    bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut, forcelevel)
+    bb.msg.addDefaultlogFilter(errconsole, bb.msg.BBLogFilterStdErr)
+    console.setFormatter(format)
+    errconsole.setFormatter(format)
+    if not bb.msg.has_console_handler(logger):
+        logger.addHandler(console)
+        logger.addHandler(errconsole)
+
+    if params.options.remote_server and params.options.kill_server:
+        server.terminateServer()
+        return
+
+    if params.observe_only:
+        logger.error("Observe-only mode not supported in this UI")
+        return 1
+
+    llevel, debug_domains = bb.msg.constructLogOptions()
+    server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
+
+    try:
+        params.updateFromServer(server)
+        cmdline = params.parseActions()
+        if not cmdline:
+            logger.error("No task given")
+            return 1
+        if 'msg' in cmdline and cmdline['msg']:
+            logger.error(cmdline['msg'])
+            return 1
+        cmdline = cmdline['action']
+        ret, error = server.runCommand(cmdline)
+        if error:
+            logger.error("{0}: {1}".format(cmdline, error))
+            return 1
+        elif not ret:
+            logger.error("Couldn't get default commandline: {0}".format(re))
+            return 1
+    except xmlrpc.client.Fault as x:
+        logger.error("XMLRPC Fault getting commandline: {0}".format(x))
+        return 1
+
+    active_process_total = None
+    is_tasks_running = False
+
+    while True:
+        try:
+            event = eventHandler.waitEvent(0.25)
+            if not event:
+                continue
+
+            helper.eventHandler(event)
+
+            if isinstance(event, bb.build.TaskBase):
+                logger.info(event._message)
+            if isinstance(event, logging.LogRecord):
+                # Don't report sstate failures as errors, since Yocto will just run the tasks for real
+                if event.msg == "No suitable staging package found" or (event.msg.startswith(
+                        "Fetcher failure: Unable to find file") and "downloadfilename" in event.msg and "sstate" in event.msg):
+                    event.levelno = bb.msg.BBLogFormatter.WARNING
+                if event.taskpid != 0:
+                    # 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
+                    if event.levelno <= bb.msg.BBLogFormatter.NOTE and (event.levelno < llevel or (
+                            event.levelno == bb.msg.BBLogFormatter.NOTE and llevel != bb.msg.BBLogFormatter.VERBOSE)):
+                        continue
+
+                    # Prefix task messages with recipe/task
+                    if event.taskpid in helper.running_tasks and event.levelno != bb.msg.BBLogFormatter.PLAIN:
+                        taskinfo = helper.running_tasks[event.taskpid]
+                        event.msg = taskinfo['title'] + ': ' + event.msg
+                if hasattr(event, 'fn'):
+                    event.msg = event.fn + ': ' + event.msg
+                logger.handle(event)
+            if isinstance(event, bb.build.TaskFailedSilent):
+                logger.warning("Logfile for failed setscene task is %s" % event.logfile)
+                continue
+            if isinstance(event, bb.build.TaskFailed):
+                rt = "{0}-{1}:{2}".format(event.pn, event.pv.replace("AUTOINC", "0"), event.task)
+
+                logfile = event.logfile
+                if not logfile or not os.path.exists(logfile):
+                    TeamCityUI.emit_service_message("buildProblem", description="{0}\nUnknown failure (no log file available)".format(rt))
+                    if not event.task.endswith("_setscene"):
+                        server.runCommand(["stateForceShutdown"])
+                    continue
+
+                details = deque(maxlen=loglines)
+                error_lines = []
+                if includelogs and not event.errprinted:
+                    with open(logfile, "r") as f:
+                        while True:
+                            line = f.readline()
+                            if not line:
+                                break
+                            line = line.rstrip()
+                            details.append(' | %s' % line)
+                            # TODO: a less stupid check for errors
+                            if (event.task == "do_compile") and ("error:" in line):
+                                error_lines.append(line)
+
+                if error_lines:
+                    TeamCityUI.emit_service_message("compilationStarted", compiler=rt)
+                    for line in error_lines:
+                        TeamCityUI.emit_service_message("message", text=line, status="ERROR")
+                    TeamCityUI.emit_service_message("compilationFinished", compiler=rt)
+                else:
+                    TeamCityUI.emit_service_message("buildProblem", description=rt)
+
+                err = "Logfile of failure stored in: %s" % logfile
+                if details:
+                    ui.block_start("{0} task log".format(rt))
+                    # TeamCity seems to choke on service messages longer than about 63800 characters, so if error
+                    # details is longer than, say, 60000, batch it up into several messages.
+                    first_message = True
+                    while details:
+                        detail_len = 0
+                        batch = deque()
+                        while details and detail_len < 60000:
+                            # TODO: This code doesn't bother to handle lines that themselves are extremely long.
+                            line = details.popleft()
+                            batch.append(line)
+                            detail_len += len(line)
+
+                        if first_message:
+                            batch.appendleft("Log data follows:")
+                            first_message = False
+                            TeamCityUI.emit_service_message("message", text=err, status="ERROR",
+                                                            errorDetails="\n".join(batch))
+                        else:
+                            TeamCityUI.emit_service_message("message", text="[continued]", status="ERROR",
+                                                            errorDetails="\n".join(batch))
+                    ui.block_end()
+                else:
+                    TeamCityUI.emit_service_message("message", text=err, status="ERROR", errorDetails="")
+
+                if not event.task.endswith("_setscene"):
+                    server.runCommand(["stateForceShutdown"])
+
+            if isinstance(event, bb.event.ProcessStarted):
+                if event.processname in ["Initialising tasks", "Checking sstate mirror object availability"]:
+                    active_process_total = event.total
+                    ui.block_start(event.processname)
+            if isinstance(event, bb.event.ProcessFinished):
+                if event.processname in ["Initialising tasks", "Checking sstate mirror object availability"]:
+                    ui.progress(event.processname, 100)
+                    ui.block_end()
+            if isinstance(event, bb.event.ProcessProgress):
+                if event.processname in ["Initialising tasks",
+                                         "Checking sstate mirror object availability"] and active_process_total != 0:
+                    ui.progress(event.processname, event.progress * 100 / active_process_total)
+            if isinstance(event, bb.event.CacheLoadStarted):
+                ui.block_start("Loading cache")
+            if isinstance(event, bb.event.CacheLoadProgress):
+                if event.total != 0:
+                    ui.progress("Loading cache", math.floor(event.current * 100 / event.total))
+            if isinstance(event, bb.event.CacheLoadCompleted):
+                ui.progress("Loading cache", 100)
+                ui.block_end()
+            if isinstance(event, bb.event.ParseStarted):
+                ui.block_start("Parsing recipes and checking upstream revisions")
+            if isinstance(event, bb.event.ParseProgress):
+                if event.total != 0:
+                    ui.progress("Parsing recipes", math.floor(event.current * 100 / event.total))
+            if isinstance(event, bb.event.ParseCompleted):
+                ui.progress("Parsing recipes", 100)
+                ui.block_end()
+            if isinstance(event, bb.command.CommandCompleted):
+                return
+            if isinstance(event, bb.command.CommandFailed):
+                logger.error(str(event))
+                return 1
+            if isinstance(event, bb.event.MultipleProviders):
+                logger.warning(str(event))
+                continue
+            if isinstance(event, bb.event.NoProvider):
+                logger.error(str(event))
+                continue
+            if isinstance(event, bb.command.CommandExit):
+                return
+            if isinstance(event, bb.cooker.CookerExit):
+                return
+            if isinstance(event, bb.runqueue.sceneQueueTaskStarted):
+                if not is_tasks_running:
+                    is_tasks_running = True
+                    ui.block_start("Running tasks")
+                if event.stats.total != 0:
+                    ui.progress("Running setscene tasks", (
+                            event.stats.completed + event.stats.active + event.stats.failed + 1) * 100 / event.stats.total)
+            if isinstance(event, bb.runqueue.runQueueTaskStarted):
+                if not is_tasks_running:
+                    is_tasks_running = True
+                    ui.block_start("Running tasks")
+                if event.stats.total != 0:
+                    pseudo_total = event.stats.total - event.stats.skipped
+                    pseudo_complete = event.stats.completed + event.stats.active - event.stats.skipped + event.stats.failed + 1
+                    # TODO: sometimes this gives over 100%
+                    ui.progress("Running runqueue tasks", (pseudo_complete) * 100 / pseudo_total,
+                            " ({0}/{1})".format(pseudo_complete, pseudo_total))
+            if isinstance(event, bb.runqueue.sceneQueueTaskFailed):
+                logger.warning(str(event))
+                continue
+            if isinstance(event, bb.runqueue.runQueueTaskFailed):
+                logger.error(str(event))
+                return 1
+            if isinstance(event, bb.event.LogExecTTY):
+                pass
+        except EnvironmentError as ioerror:
+            # ignore interrupted io
+            if ioerror.args[0] == 4:
+                pass
+        except Exception as ex:
+            logger.error(str(ex))
+
+        # except KeyboardInterrupt:
+        #     if shutdown == 2:
+        #         mw.appendText("Third Keyboard Interrupt, exit.\n")
+        #         exitflag = True
+        #     if shutdown == 1:
+        #         mw.appendText("Second Keyboard Interrupt, stopping...\n")
+        #         _, error = server.runCommand(["stateForceShutdown"])
+        #         if error:
+        #             print("Unable to cleanly stop: %s" % error)
+        #     if shutdown == 0:
+        #         mw.appendText("Keyboard Interrupt, closing down...\n")
+        #         _, error = server.runCommand(["stateShutdown"])
+        #         if error:
+        #             print("Unable to cleanly shutdown: %s" % error)
+        #     shutdown = shutdown + 1
+        #     pass
diff --git a/poky/bitbake/lib/bb/ui/toasterui.py b/poky/bitbake/lib/bb/ui/toasterui.py
index 51892c9..9260f5d 100644
--- a/poky/bitbake/lib/bb/ui/toasterui.py
+++ b/poky/bitbake/lib/bb/ui/toasterui.py
@@ -176,7 +176,7 @@
         if error:
             logger.error("Command '%s' failed: %s" % (cmdline, error))
             return 1
-        elif ret != True:
+        elif not ret:
             logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
             return 1
 
diff --git a/poky/bitbake/lib/bb/ui/uievent.py b/poky/bitbake/lib/bb/ui/uievent.py
index fedb050..13d0d4a 100644
--- a/poky/bitbake/lib/bb/ui/uievent.py
+++ b/poky/bitbake/lib/bb/ui/uievent.py
@@ -46,7 +46,7 @@
                 self.EventHandle = ret
                 error = ""
 
-            if self.EventHandle != None:
+            if self.EventHandle is not None:
                 break
 
             errmsg = "Could not register UI event handler. Error: %s, host %s, "\
diff --git a/poky/bitbake/lib/bb/ui/uihelper.py b/poky/bitbake/lib/bb/ui/uihelper.py
index c8dd7df..48d808a 100644
--- a/poky/bitbake/lib/bb/ui/uihelper.py
+++ b/poky/bitbake/lib/bb/ui/uihelper.py
@@ -15,39 +15,48 @@
         # Running PIDs preserves the order tasks were executed in
         self.running_pids = []
         self.failed_tasks = []
+        self.pidmap = {}
         self.tasknumber_current = 0
         self.tasknumber_total = 0
 
     def eventHandler(self, event):
+        # PIDs are a bad idea as they can be reused before we process all UI events.
+        # We maintain a 'fuzzy' match for TaskProgress since there is no other way to match
+        def removetid(pid, tid):
+            self.running_pids.remove(tid)
+            del self.running_tasks[tid]
+            if self.pidmap[pid] == tid:
+                del self.pidmap[pid]
+            self.needUpdate = True
+
         if isinstance(event, bb.build.TaskStarted):
+            tid = event._fn + ":" + event._task
             if event._mc != "default":
-                self.running_tasks[event.pid] = { 'title' : "mc:%s:%s %s" % (event._mc, event._package, event._task), 'starttime' : time.time() }
+                self.running_tasks[tid] = { 'title' : "mc:%s:%s %s" % (event._mc, event._package, event._task), 'starttime' : time.time(), 'pid' : event.pid }
             else:
-                self.running_tasks[event.pid] = { 'title' : "%s %s" % (event._package, event._task), 'starttime' : time.time() }
-            self.running_pids.append(event.pid)
+                self.running_tasks[tid] = { 'title' : "%s %s" % (event._package, event._task), 'starttime' : time.time(), 'pid' : event.pid }
+            self.running_pids.append(tid)
+            self.pidmap[event.pid] = tid
             self.needUpdate = True
         elif isinstance(event, bb.build.TaskSucceeded):
-            del self.running_tasks[event.pid]
-            self.running_pids.remove(event.pid)
-            self.needUpdate = True
+            tid = event._fn + ":" + event._task
+            removetid(event.pid, tid)
         elif isinstance(event, bb.build.TaskFailedSilent):
-            del self.running_tasks[event.pid]
-            self.running_pids.remove(event.pid)
+            tid = event._fn + ":" + event._task
+            removetid(event.pid, tid)
             # Don't add to the failed tasks list since this is e.g. a setscene task failure
-            self.needUpdate = True
         elif isinstance(event, bb.build.TaskFailed):
-            del self.running_tasks[event.pid]
-            self.running_pids.remove(event.pid)
+            tid = event._fn + ":" + event._task
+            removetid(event.pid, tid)
             self.failed_tasks.append( { 'title' : "%s %s" % (event._package, event._task)})
-            self.needUpdate = True
         elif isinstance(event, bb.runqueue.runQueueTaskStarted):
             self.tasknumber_current = event.stats.completed + event.stats.active + event.stats.failed + 1
             self.tasknumber_total = event.stats.total
             self.needUpdate = True
         elif isinstance(event, bb.build.TaskProgress):
-            if event.pid > 0:
-                self.running_tasks[event.pid]['progress'] = event.progress
-                self.running_tasks[event.pid]['rate'] = event.rate
+            if event.pid > 0 and event.pid in self.pidmap:
+                self.running_tasks[self.pidmap[event.pid]]['progress'] = event.progress
+                self.running_tasks[self.pidmap[event.pid]]['rate'] = event.rate
                 self.needUpdate = True
         else:
             return False