xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/bb/ui/teamcity.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# TeamCity UI Implementation
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun# Implements a TeamCity frontend for the BitBake utility, via service messages.
5*4882a593Smuzhiyun# See https://www.jetbrains.com/help/teamcity/build-script-interaction-with-teamcity.html
6*4882a593Smuzhiyun#
7*4882a593Smuzhiyun# Based on ncurses.py and knotty.py, variously by Michael Lauer and Richard Purdie
8*4882a593Smuzhiyun#
9*4882a593Smuzhiyun# Copyright (C) 2006 Michael 'Mickey' Lauer
10*4882a593Smuzhiyun# Copyright (C) 2006-2012 Richard Purdie
11*4882a593Smuzhiyun# Copyright (C) 2018-2020 Agilent Technologies, Inc.
12*4882a593Smuzhiyun#
13*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
14*4882a593Smuzhiyun#
15*4882a593Smuzhiyun# Author: Chris Laplante <chris.laplante@agilent.com>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyunfrom __future__ import division
18*4882a593Smuzhiyun
19*4882a593Smuzhiyunimport datetime
20*4882a593Smuzhiyunimport logging
21*4882a593Smuzhiyunimport math
22*4882a593Smuzhiyunimport os
23*4882a593Smuzhiyunimport re
24*4882a593Smuzhiyunimport sys
25*4882a593Smuzhiyunimport xmlrpc.client
26*4882a593Smuzhiyunfrom collections import deque
27*4882a593Smuzhiyun
28*4882a593Smuzhiyunimport bb
29*4882a593Smuzhiyunimport bb.build
30*4882a593Smuzhiyunimport bb.command
31*4882a593Smuzhiyunimport bb.cooker
32*4882a593Smuzhiyunimport bb.event
33*4882a593Smuzhiyunimport bb.exceptions
34*4882a593Smuzhiyunimport bb.runqueue
35*4882a593Smuzhiyunfrom bb.ui import uihelper
36*4882a593Smuzhiyun
37*4882a593Smuzhiyunlogger = logging.getLogger("BitBake")
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun
40*4882a593Smuzhiyunclass TeamCityUI:
41*4882a593Smuzhiyun    def __init__(self):
42*4882a593Smuzhiyun        self._block_stack = []
43*4882a593Smuzhiyun        self._last_progress_state = None
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun    @classmethod
46*4882a593Smuzhiyun    def escape_service_value(cls, value):
47*4882a593Smuzhiyun        """
48*4882a593Smuzhiyun        Escape a value for inclusion in a service message. TeamCity uses the vertical pipe character for escaping.
49*4882a593Smuzhiyun        See: https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-Escapedvalues
50*4882a593Smuzhiyun        """
51*4882a593Smuzhiyun        return re.sub(r"(['|\[\]])", r"|\1", value).replace("\n", "|n").replace("\r", "|r")
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun    @classmethod
54*4882a593Smuzhiyun    def emit_service_message(cls, message_type, **kwargs):
55*4882a593Smuzhiyun        print(cls.format_service_message(message_type, **kwargs), flush=True)
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun    @classmethod
58*4882a593Smuzhiyun    def format_service_message(cls, message_type, **kwargs):
59*4882a593Smuzhiyun        payload = " ".join(["{0}='{1}'".format(k, cls.escape_service_value(v)) for k, v in kwargs.items()])
60*4882a593Smuzhiyun        return "##teamcity[{0} {1}]".format(message_type, payload)
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun    @classmethod
63*4882a593Smuzhiyun    def emit_simple_service_message(cls, message_type, message):
64*4882a593Smuzhiyun        print(cls.format_simple_service_message(message_type, message), flush=True)
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun    @classmethod
67*4882a593Smuzhiyun    def format_simple_service_message(cls, message_type, message):
68*4882a593Smuzhiyun        return "##teamcity[{0} '{1}']".format(message_type, cls.escape_service_value(message))
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun    @classmethod
71*4882a593Smuzhiyun    def format_build_message(cls, text, status):
72*4882a593Smuzhiyun        return cls.format_service_message("message", text=text, status=status)
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun    def block_start(self, name):
75*4882a593Smuzhiyun        self._block_stack.append(name)
76*4882a593Smuzhiyun        self.emit_service_message("blockOpened", name=name)
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun    def block_end(self):
79*4882a593Smuzhiyun        if self._block_stack:
80*4882a593Smuzhiyun            name = self._block_stack.pop()
81*4882a593Smuzhiyun            self.emit_service_message("blockClosed", name=name)
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun    def progress(self, message, percent, extra=None):
84*4882a593Smuzhiyun        now = datetime.datetime.now()
85*4882a593Smuzhiyun        percent = "{0: >3.0f}".format(percent)
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun        report = False
88*4882a593Smuzhiyun        if not self._last_progress_state \
89*4882a593Smuzhiyun                or (self._last_progress_state[0] == message
90*4882a593Smuzhiyun                    and self._last_progress_state[1] != percent
91*4882a593Smuzhiyun                    and (now - self._last_progress_state[2]).microseconds >= 5000) \
92*4882a593Smuzhiyun                or self._last_progress_state[0] != message:
93*4882a593Smuzhiyun            report = True
94*4882a593Smuzhiyun            self._last_progress_state = (message, percent, now)
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun        if report or percent in [0, 100]:
97*4882a593Smuzhiyun            self.emit_simple_service_message("progressMessage", "{0}: {1}%{2}".format(message, percent, extra or ""))
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun
100*4882a593Smuzhiyunclass TeamcityLogFormatter(logging.Formatter):
101*4882a593Smuzhiyun    def format(self, record):
102*4882a593Smuzhiyun        details = ""
103*4882a593Smuzhiyun        if hasattr(record, 'bb_exc_formatted'):
104*4882a593Smuzhiyun            details = ''.join(record.bb_exc_formatted)
105*4882a593Smuzhiyun        elif hasattr(record, 'bb_exc_info'):
106*4882a593Smuzhiyun            etype, value, tb = record.bb_exc_info
107*4882a593Smuzhiyun            formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
108*4882a593Smuzhiyun            details = ''.join(formatted)
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun        if record.levelno in [bb.msg.BBLogFormatter.ERROR, bb.msg.BBLogFormatter.CRITICAL]:
111*4882a593Smuzhiyun            # ERROR gets a separate errorDetails field
112*4882a593Smuzhiyun            msg = TeamCityUI.format_service_message("message", text=record.getMessage(), status="ERROR",
113*4882a593Smuzhiyun                                                    errorDetails=details)
114*4882a593Smuzhiyun        else:
115*4882a593Smuzhiyun            payload = record.getMessage()
116*4882a593Smuzhiyun            if details:
117*4882a593Smuzhiyun                payload += "\n" + details
118*4882a593Smuzhiyun            if record.levelno == bb.msg.BBLogFormatter.PLAIN:
119*4882a593Smuzhiyun                msg = payload
120*4882a593Smuzhiyun            elif record.levelno == bb.msg.BBLogFormatter.WARNING:
121*4882a593Smuzhiyun                msg = TeamCityUI.format_service_message("message", text=payload, status="WARNING")
122*4882a593Smuzhiyun            else:
123*4882a593Smuzhiyun                msg = TeamCityUI.format_service_message("message", text=payload, status="NORMAL")
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun        return msg
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun_evt_list = ["bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
129*4882a593Smuzhiyun             "bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
130*4882a593Smuzhiyun             "bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted",
131*4882a593Smuzhiyun             "bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed",
132*4882a593Smuzhiyun             "bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit",
133*4882a593Smuzhiyun             "bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted",
134*4882a593Smuzhiyun             "bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed",
135*4882a593Smuzhiyun             "bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent",
136*4882a593Smuzhiyun             "bb.build.TaskProgress", "bb.event.ProcessStarted", "bb.event.ProcessProgress", "bb.event.ProcessFinished"]
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun
139*4882a593Smuzhiyundef _log_settings_from_server(server):
140*4882a593Smuzhiyun    # Get values of variables which control our output
141*4882a593Smuzhiyun    includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
142*4882a593Smuzhiyun    if error:
143*4882a593Smuzhiyun        logger.error("Unable to get the value of BBINCLUDELOGS variable: %s" % error)
144*4882a593Smuzhiyun        raise BaseException(error)
145*4882a593Smuzhiyun    loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
146*4882a593Smuzhiyun    if error:
147*4882a593Smuzhiyun        logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error)
148*4882a593Smuzhiyun        raise BaseException(error)
149*4882a593Smuzhiyun    return includelogs, loglines
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun
152*4882a593Smuzhiyundef main(server, eventHandler, params):
153*4882a593Smuzhiyun    params.updateToServer(server, os.environ.copy())
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun    includelogs, loglines = _log_settings_from_server(server)
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun    ui = TeamCityUI()
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun    helper = uihelper.BBUIHelper()
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun    console = logging.StreamHandler(sys.stdout)
162*4882a593Smuzhiyun    errconsole = logging.StreamHandler(sys.stderr)
163*4882a593Smuzhiyun    format = TeamcityLogFormatter()
164*4882a593Smuzhiyun    if params.options.quiet == 0:
165*4882a593Smuzhiyun        forcelevel = None
166*4882a593Smuzhiyun    elif params.options.quiet > 2:
167*4882a593Smuzhiyun        forcelevel = bb.msg.BBLogFormatter.ERROR
168*4882a593Smuzhiyun    else:
169*4882a593Smuzhiyun        forcelevel = bb.msg.BBLogFormatter.WARNING
170*4882a593Smuzhiyun    console.setFormatter(format)
171*4882a593Smuzhiyun    errconsole.setFormatter(format)
172*4882a593Smuzhiyun    if not bb.msg.has_console_handler(logger):
173*4882a593Smuzhiyun        logger.addHandler(console)
174*4882a593Smuzhiyun        logger.addHandler(errconsole)
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun    if params.options.remote_server and params.options.kill_server:
177*4882a593Smuzhiyun        server.terminateServer()
178*4882a593Smuzhiyun        return
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun    if params.observe_only:
181*4882a593Smuzhiyun        logger.error("Observe-only mode not supported in this UI")
182*4882a593Smuzhiyun        return 1
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun    llevel, debug_domains = bb.msg.constructLogOptions()
185*4882a593Smuzhiyun    server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun    try:
188*4882a593Smuzhiyun        params.updateFromServer(server)
189*4882a593Smuzhiyun        cmdline = params.parseActions()
190*4882a593Smuzhiyun        if not cmdline:
191*4882a593Smuzhiyun            logger.error("No task given")
192*4882a593Smuzhiyun            return 1
193*4882a593Smuzhiyun        if 'msg' in cmdline and cmdline['msg']:
194*4882a593Smuzhiyun            logger.error(cmdline['msg'])
195*4882a593Smuzhiyun            return 1
196*4882a593Smuzhiyun        cmdline = cmdline['action']
197*4882a593Smuzhiyun        ret, error = server.runCommand(cmdline)
198*4882a593Smuzhiyun        if error:
199*4882a593Smuzhiyun            logger.error("{0}: {1}".format(cmdline, error))
200*4882a593Smuzhiyun            return 1
201*4882a593Smuzhiyun        elif not ret:
202*4882a593Smuzhiyun            logger.error("Couldn't get default commandline: {0}".format(re))
203*4882a593Smuzhiyun            return 1
204*4882a593Smuzhiyun    except xmlrpc.client.Fault as x:
205*4882a593Smuzhiyun        logger.error("XMLRPC Fault getting commandline: {0}".format(x))
206*4882a593Smuzhiyun        return 1
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun    active_process_total = None
209*4882a593Smuzhiyun    is_tasks_running = False
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun    while True:
212*4882a593Smuzhiyun        try:
213*4882a593Smuzhiyun            event = eventHandler.waitEvent(0.25)
214*4882a593Smuzhiyun            if not event:
215*4882a593Smuzhiyun                continue
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun            helper.eventHandler(event)
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun            if isinstance(event, bb.build.TaskBase):
220*4882a593Smuzhiyun                logger.info(event._message)
221*4882a593Smuzhiyun            if isinstance(event, logging.LogRecord):
222*4882a593Smuzhiyun                # Don't report sstate failures as errors, since Yocto will just run the tasks for real
223*4882a593Smuzhiyun                if event.msg == "No suitable staging package found" or (event.msg.startswith(
224*4882a593Smuzhiyun                        "Fetcher failure: Unable to find file") and "downloadfilename" in event.msg and "sstate" in event.msg):
225*4882a593Smuzhiyun                    event.levelno = bb.msg.BBLogFormatter.WARNING
226*4882a593Smuzhiyun                if event.taskpid != 0:
227*4882a593Smuzhiyun                    # For "normal" logging conditions, don't show note logs from tasks
228*4882a593Smuzhiyun                    # but do show them if the user has changed the default log level to
229*4882a593Smuzhiyun                    # include verbose/debug messages
230*4882a593Smuzhiyun                    if event.levelno <= bb.msg.BBLogFormatter.NOTE and (event.levelno < llevel or (
231*4882a593Smuzhiyun                            event.levelno == bb.msg.BBLogFormatter.NOTE and llevel != bb.msg.BBLogFormatter.VERBOSE)):
232*4882a593Smuzhiyun                        continue
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun                    # Prefix task messages with recipe/task
235*4882a593Smuzhiyun                    if event.taskpid in helper.running_tasks and event.levelno != bb.msg.BBLogFormatter.PLAIN:
236*4882a593Smuzhiyun                        taskinfo = helper.running_tasks[event.taskpid]
237*4882a593Smuzhiyun                        event.msg = taskinfo['title'] + ': ' + event.msg
238*4882a593Smuzhiyun                if hasattr(event, 'fn'):
239*4882a593Smuzhiyun                    event.msg = event.fn + ': ' + event.msg
240*4882a593Smuzhiyun                logger.handle(event)
241*4882a593Smuzhiyun            if isinstance(event, bb.build.TaskFailedSilent):
242*4882a593Smuzhiyun                logger.warning("Logfile for failed setscene task is %s" % event.logfile)
243*4882a593Smuzhiyun                continue
244*4882a593Smuzhiyun            if isinstance(event, bb.build.TaskFailed):
245*4882a593Smuzhiyun                rt = "{0}-{1}:{2}".format(event.pn, event.pv.replace("AUTOINC", "0"), event.task)
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun                logfile = event.logfile
248*4882a593Smuzhiyun                if not logfile or not os.path.exists(logfile):
249*4882a593Smuzhiyun                    TeamCityUI.emit_service_message("buildProblem", description="{0}\nUnknown failure (no log file available)".format(rt))
250*4882a593Smuzhiyun                    if not event.task.endswith("_setscene"):
251*4882a593Smuzhiyun                        server.runCommand(["stateForceShutdown"])
252*4882a593Smuzhiyun                    continue
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun                details = deque(maxlen=loglines)
255*4882a593Smuzhiyun                error_lines = []
256*4882a593Smuzhiyun                if includelogs and not event.errprinted:
257*4882a593Smuzhiyun                    with open(logfile, "r") as f:
258*4882a593Smuzhiyun                        while True:
259*4882a593Smuzhiyun                            line = f.readline()
260*4882a593Smuzhiyun                            if not line:
261*4882a593Smuzhiyun                                break
262*4882a593Smuzhiyun                            line = line.rstrip()
263*4882a593Smuzhiyun                            details.append(' | %s' % line)
264*4882a593Smuzhiyun                            # TODO: a less stupid check for errors
265*4882a593Smuzhiyun                            if (event.task == "do_compile") and ("error:" in line):
266*4882a593Smuzhiyun                                error_lines.append(line)
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun                if error_lines:
269*4882a593Smuzhiyun                    TeamCityUI.emit_service_message("compilationStarted", compiler=rt)
270*4882a593Smuzhiyun                    for line in error_lines:
271*4882a593Smuzhiyun                        TeamCityUI.emit_service_message("message", text=line, status="ERROR")
272*4882a593Smuzhiyun                    TeamCityUI.emit_service_message("compilationFinished", compiler=rt)
273*4882a593Smuzhiyun                else:
274*4882a593Smuzhiyun                    TeamCityUI.emit_service_message("buildProblem", description=rt)
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun                err = "Logfile of failure stored in: %s" % logfile
277*4882a593Smuzhiyun                if details:
278*4882a593Smuzhiyun                    ui.block_start("{0} task log".format(rt))
279*4882a593Smuzhiyun                    # TeamCity seems to choke on service messages longer than about 63800 characters, so if error
280*4882a593Smuzhiyun                    # details is longer than, say, 60000, batch it up into several messages.
281*4882a593Smuzhiyun                    first_message = True
282*4882a593Smuzhiyun                    while details:
283*4882a593Smuzhiyun                        detail_len = 0
284*4882a593Smuzhiyun                        batch = deque()
285*4882a593Smuzhiyun                        while details and detail_len < 60000:
286*4882a593Smuzhiyun                            # TODO: This code doesn't bother to handle lines that themselves are extremely long.
287*4882a593Smuzhiyun                            line = details.popleft()
288*4882a593Smuzhiyun                            batch.append(line)
289*4882a593Smuzhiyun                            detail_len += len(line)
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun                        if first_message:
292*4882a593Smuzhiyun                            batch.appendleft("Log data follows:")
293*4882a593Smuzhiyun                            first_message = False
294*4882a593Smuzhiyun                            TeamCityUI.emit_service_message("message", text=err, status="ERROR",
295*4882a593Smuzhiyun                                                            errorDetails="\n".join(batch))
296*4882a593Smuzhiyun                        else:
297*4882a593Smuzhiyun                            TeamCityUI.emit_service_message("message", text="[continued]", status="ERROR",
298*4882a593Smuzhiyun                                                            errorDetails="\n".join(batch))
299*4882a593Smuzhiyun                    ui.block_end()
300*4882a593Smuzhiyun                else:
301*4882a593Smuzhiyun                    TeamCityUI.emit_service_message("message", text=err, status="ERROR", errorDetails="")
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun                if not event.task.endswith("_setscene"):
304*4882a593Smuzhiyun                    server.runCommand(["stateForceShutdown"])
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun            if isinstance(event, bb.event.ProcessStarted):
307*4882a593Smuzhiyun                if event.processname in ["Initialising tasks", "Checking sstate mirror object availability"]:
308*4882a593Smuzhiyun                    active_process_total = event.total
309*4882a593Smuzhiyun                    ui.block_start(event.processname)
310*4882a593Smuzhiyun            if isinstance(event, bb.event.ProcessFinished):
311*4882a593Smuzhiyun                if event.processname in ["Initialising tasks", "Checking sstate mirror object availability"]:
312*4882a593Smuzhiyun                    ui.progress(event.processname, 100)
313*4882a593Smuzhiyun                    ui.block_end()
314*4882a593Smuzhiyun            if isinstance(event, bb.event.ProcessProgress):
315*4882a593Smuzhiyun                if event.processname in ["Initialising tasks",
316*4882a593Smuzhiyun                                         "Checking sstate mirror object availability"] and active_process_total != 0:
317*4882a593Smuzhiyun                    ui.progress(event.processname, event.progress * 100 / active_process_total)
318*4882a593Smuzhiyun            if isinstance(event, bb.event.CacheLoadStarted):
319*4882a593Smuzhiyun                ui.block_start("Loading cache")
320*4882a593Smuzhiyun            if isinstance(event, bb.event.CacheLoadProgress):
321*4882a593Smuzhiyun                if event.total != 0:
322*4882a593Smuzhiyun                    ui.progress("Loading cache", math.floor(event.current * 100 / event.total))
323*4882a593Smuzhiyun            if isinstance(event, bb.event.CacheLoadCompleted):
324*4882a593Smuzhiyun                ui.progress("Loading cache", 100)
325*4882a593Smuzhiyun                ui.block_end()
326*4882a593Smuzhiyun            if isinstance(event, bb.event.ParseStarted):
327*4882a593Smuzhiyun                ui.block_start("Parsing recipes and checking upstream revisions")
328*4882a593Smuzhiyun            if isinstance(event, bb.event.ParseProgress):
329*4882a593Smuzhiyun                if event.total != 0:
330*4882a593Smuzhiyun                    ui.progress("Parsing recipes", math.floor(event.current * 100 / event.total))
331*4882a593Smuzhiyun            if isinstance(event, bb.event.ParseCompleted):
332*4882a593Smuzhiyun                ui.progress("Parsing recipes", 100)
333*4882a593Smuzhiyun                ui.block_end()
334*4882a593Smuzhiyun            if isinstance(event, bb.command.CommandCompleted):
335*4882a593Smuzhiyun                return
336*4882a593Smuzhiyun            if isinstance(event, bb.command.CommandFailed):
337*4882a593Smuzhiyun                logger.error(str(event))
338*4882a593Smuzhiyun                return 1
339*4882a593Smuzhiyun            if isinstance(event, bb.event.MultipleProviders):
340*4882a593Smuzhiyun                logger.warning(str(event))
341*4882a593Smuzhiyun                continue
342*4882a593Smuzhiyun            if isinstance(event, bb.event.NoProvider):
343*4882a593Smuzhiyun                logger.error(str(event))
344*4882a593Smuzhiyun                continue
345*4882a593Smuzhiyun            if isinstance(event, bb.command.CommandExit):
346*4882a593Smuzhiyun                return
347*4882a593Smuzhiyun            if isinstance(event, bb.cooker.CookerExit):
348*4882a593Smuzhiyun                return
349*4882a593Smuzhiyun            if isinstance(event, bb.runqueue.sceneQueueTaskStarted):
350*4882a593Smuzhiyun                if not is_tasks_running:
351*4882a593Smuzhiyun                    is_tasks_running = True
352*4882a593Smuzhiyun                    ui.block_start("Running tasks")
353*4882a593Smuzhiyun                if event.stats.total != 0:
354*4882a593Smuzhiyun                    ui.progress("Running setscene tasks", (
355*4882a593Smuzhiyun                            event.stats.completed + event.stats.active + event.stats.failed + 1) * 100 / event.stats.total)
356*4882a593Smuzhiyun            if isinstance(event, bb.runqueue.runQueueTaskStarted):
357*4882a593Smuzhiyun                if not is_tasks_running:
358*4882a593Smuzhiyun                    is_tasks_running = True
359*4882a593Smuzhiyun                    ui.block_start("Running tasks")
360*4882a593Smuzhiyun                if event.stats.total != 0:
361*4882a593Smuzhiyun                    pseudo_total = event.stats.total - event.stats.skipped
362*4882a593Smuzhiyun                    pseudo_complete = event.stats.completed + event.stats.active - event.stats.skipped + event.stats.failed + 1
363*4882a593Smuzhiyun                    # TODO: sometimes this gives over 100%
364*4882a593Smuzhiyun                    ui.progress("Running runqueue tasks", (pseudo_complete) * 100 / pseudo_total,
365*4882a593Smuzhiyun                            " ({0}/{1})".format(pseudo_complete, pseudo_total))
366*4882a593Smuzhiyun            if isinstance(event, bb.runqueue.sceneQueueTaskFailed):
367*4882a593Smuzhiyun                logger.warning(str(event))
368*4882a593Smuzhiyun                continue
369*4882a593Smuzhiyun            if isinstance(event, bb.runqueue.runQueueTaskFailed):
370*4882a593Smuzhiyun                logger.error(str(event))
371*4882a593Smuzhiyun                return 1
372*4882a593Smuzhiyun            if isinstance(event, bb.event.LogExecTTY):
373*4882a593Smuzhiyun                pass
374*4882a593Smuzhiyun        except EnvironmentError as ioerror:
375*4882a593Smuzhiyun            # ignore interrupted io
376*4882a593Smuzhiyun            if ioerror.args[0] == 4:
377*4882a593Smuzhiyun                pass
378*4882a593Smuzhiyun        except Exception as ex:
379*4882a593Smuzhiyun            logger.error(str(ex))
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun        # except KeyboardInterrupt:
382*4882a593Smuzhiyun        #     if shutdown == 2:
383*4882a593Smuzhiyun        #         mw.appendText("Third Keyboard Interrupt, exit.\n")
384*4882a593Smuzhiyun        #         exitflag = True
385*4882a593Smuzhiyun        #     if shutdown == 1:
386*4882a593Smuzhiyun        #         mw.appendText("Second Keyboard Interrupt, stopping...\n")
387*4882a593Smuzhiyun        #         _, error = server.runCommand(["stateForceShutdown"])
388*4882a593Smuzhiyun        #         if error:
389*4882a593Smuzhiyun        #             print("Unable to cleanly stop: %s" % error)
390*4882a593Smuzhiyun        #     if shutdown == 0:
391*4882a593Smuzhiyun        #         mw.appendText("Keyboard Interrupt, closing down...\n")
392*4882a593Smuzhiyun        #         _, error = server.runCommand(["stateShutdown"])
393*4882a593Smuzhiyun        #         if error:
394*4882a593Smuzhiyun        #             print("Unable to cleanly shutdown: %s" % error)
395*4882a593Smuzhiyun        #     shutdown = shutdown + 1
396*4882a593Smuzhiyun        #     pass
397