xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/bb/ui/toasterui.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# BitBake ToasterUI Implementation
3*4882a593Smuzhiyun# based on (No)TTY UI Implementation by Richard Purdie
4*4882a593Smuzhiyun#
5*4882a593Smuzhiyun# Handling output to TTYs or files (no TTY)
6*4882a593Smuzhiyun#
7*4882a593Smuzhiyun# Copyright (C) 2006-2012 Richard Purdie
8*4882a593Smuzhiyun# Copyright (C) 2013      Intel Corporation
9*4882a593Smuzhiyun#
10*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
11*4882a593Smuzhiyun#
12*4882a593Smuzhiyun
13*4882a593Smuzhiyunfrom __future__ import division
14*4882a593Smuzhiyunimport time
15*4882a593Smuzhiyunimport sys
16*4882a593Smuzhiyuntry:
17*4882a593Smuzhiyun    import bb
18*4882a593Smuzhiyunexcept RuntimeError as exc:
19*4882a593Smuzhiyun    sys.exit(str(exc))
20*4882a593Smuzhiyun
21*4882a593Smuzhiyunfrom bb.ui import uihelper
22*4882a593Smuzhiyunfrom bb.ui.buildinfohelper import BuildInfoHelper
23*4882a593Smuzhiyun
24*4882a593Smuzhiyunimport bb.msg
25*4882a593Smuzhiyunimport logging
26*4882a593Smuzhiyunimport os
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun# pylint: disable=invalid-name
29*4882a593Smuzhiyun# module properties for UI modules are read by bitbake and the contract should not be broken
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun
32*4882a593SmuzhiyunfeatureSet = [bb.cooker.CookerFeatures.HOB_EXTRA_CACHES, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING, bb.cooker.CookerFeatures.SEND_SANITYEVENTS]
33*4882a593Smuzhiyun
34*4882a593Smuzhiyunlogger = logging.getLogger("ToasterLogger")
35*4882a593Smuzhiyuninteractive = sys.stdout.isatty()
36*4882a593Smuzhiyun
37*4882a593Smuzhiyundef _log_settings_from_server(server):
38*4882a593Smuzhiyun    # Get values of variables which control our output
39*4882a593Smuzhiyun    includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
40*4882a593Smuzhiyun    if error:
41*4882a593Smuzhiyun        logger.error("Unable to get the value of BBINCLUDELOGS variable: %s", error)
42*4882a593Smuzhiyun        raise BaseException(error)
43*4882a593Smuzhiyun    loglines, error = server.runCommand(["getVariable", "BBINCLUDELOGS_LINES"])
44*4882a593Smuzhiyun    if error:
45*4882a593Smuzhiyun        logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s", error)
46*4882a593Smuzhiyun        raise BaseException(error)
47*4882a593Smuzhiyun    consolelogfile, error = server.runCommand(["getVariable", "BB_CONSOLELOG"])
48*4882a593Smuzhiyun    if error:
49*4882a593Smuzhiyun        logger.error("Unable to get the value of BB_CONSOLELOG variable: %s", error)
50*4882a593Smuzhiyun        raise BaseException(error)
51*4882a593Smuzhiyun    return consolelogfile
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun# create a log file for a single build and direct the logger at it;
54*4882a593Smuzhiyun# log file name is timestamped to the millisecond (depending
55*4882a593Smuzhiyun# on system clock accuracy) to ensure it doesn't overlap with
56*4882a593Smuzhiyun# other log file names
57*4882a593Smuzhiyun#
58*4882a593Smuzhiyun# returns (log file, path to log file) for a build
59*4882a593Smuzhiyundef _open_build_log(log_dir):
60*4882a593Smuzhiyun    format_str = "%(levelname)s: %(message)s"
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun    now = time.time()
63*4882a593Smuzhiyun    now_ms = int((now - int(now)) * 1000)
64*4882a593Smuzhiyun    time_str = time.strftime('build_%Y%m%d_%H%M%S', time.localtime(now))
65*4882a593Smuzhiyun    log_file_name = time_str + ('.%d.log' % now_ms)
66*4882a593Smuzhiyun    build_log_file_path = os.path.join(log_dir, log_file_name)
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun    build_log = logging.FileHandler(build_log_file_path)
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun    logformat = bb.msg.BBLogFormatter(format_str)
71*4882a593Smuzhiyun    build_log.setFormatter(logformat)
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun    bb.msg.addDefaultlogFilter(build_log)
74*4882a593Smuzhiyun    logger.addHandler(build_log)
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun    return (build_log, build_log_file_path)
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun# stop logging to the build log if it exists
79*4882a593Smuzhiyundef _close_build_log(build_log):
80*4882a593Smuzhiyun    if build_log:
81*4882a593Smuzhiyun        build_log.flush()
82*4882a593Smuzhiyun        build_log.close()
83*4882a593Smuzhiyun        logger.removeHandler(build_log)
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun_evt_list = [
86*4882a593Smuzhiyun    "bb.build.TaskBase",
87*4882a593Smuzhiyun    "bb.build.TaskFailed",
88*4882a593Smuzhiyun    "bb.build.TaskFailedSilent",
89*4882a593Smuzhiyun    "bb.build.TaskStarted",
90*4882a593Smuzhiyun    "bb.build.TaskSucceeded",
91*4882a593Smuzhiyun    "bb.command.CommandCompleted",
92*4882a593Smuzhiyun    "bb.command.CommandExit",
93*4882a593Smuzhiyun    "bb.command.CommandFailed",
94*4882a593Smuzhiyun    "bb.cooker.CookerExit",
95*4882a593Smuzhiyun    "bb.event.BuildInit",
96*4882a593Smuzhiyun    "bb.event.BuildCompleted",
97*4882a593Smuzhiyun    "bb.event.BuildStarted",
98*4882a593Smuzhiyun    "bb.event.CacheLoadCompleted",
99*4882a593Smuzhiyun    "bb.event.CacheLoadProgress",
100*4882a593Smuzhiyun    "bb.event.CacheLoadStarted",
101*4882a593Smuzhiyun    "bb.event.ConfigParsed",
102*4882a593Smuzhiyun    "bb.event.DepTreeGenerated",
103*4882a593Smuzhiyun    "bb.event.LogExecTTY",
104*4882a593Smuzhiyun    "bb.event.MetadataEvent",
105*4882a593Smuzhiyun    "bb.event.MultipleProviders",
106*4882a593Smuzhiyun    "bb.event.NoProvider",
107*4882a593Smuzhiyun    "bb.event.ParseCompleted",
108*4882a593Smuzhiyun    "bb.event.ParseProgress",
109*4882a593Smuzhiyun    "bb.event.ParseStarted",
110*4882a593Smuzhiyun    "bb.event.RecipeParsed",
111*4882a593Smuzhiyun    "bb.event.SanityCheck",
112*4882a593Smuzhiyun    "bb.event.SanityCheckPassed",
113*4882a593Smuzhiyun    "bb.event.TreeDataPreparationCompleted",
114*4882a593Smuzhiyun    "bb.event.TreeDataPreparationStarted",
115*4882a593Smuzhiyun    "bb.runqueue.runQueueTaskCompleted",
116*4882a593Smuzhiyun    "bb.runqueue.runQueueTaskFailed",
117*4882a593Smuzhiyun    "bb.runqueue.runQueueTaskSkipped",
118*4882a593Smuzhiyun    "bb.runqueue.runQueueTaskStarted",
119*4882a593Smuzhiyun    "bb.runqueue.sceneQueueTaskCompleted",
120*4882a593Smuzhiyun    "bb.runqueue.sceneQueueTaskFailed",
121*4882a593Smuzhiyun    "bb.runqueue.sceneQueueTaskStarted",
122*4882a593Smuzhiyun    "logging.LogRecord"]
123*4882a593Smuzhiyun
124*4882a593Smuzhiyundef main(server, eventHandler, params):
125*4882a593Smuzhiyun    # set to a logging.FileHandler instance when a build starts;
126*4882a593Smuzhiyun    # see _open_build_log()
127*4882a593Smuzhiyun    build_log = None
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun    # set to the log path when a build starts
130*4882a593Smuzhiyun    build_log_file_path = None
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun    helper = uihelper.BBUIHelper()
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun    if not params.observe_only:
135*4882a593Smuzhiyun        params.updateToServer(server, os.environ.copy())
136*4882a593Smuzhiyun        params.updateFromServer(server)
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun    # TODO don't use log output to determine when bitbake has started
139*4882a593Smuzhiyun    #
140*4882a593Smuzhiyun    # WARNING: this log handler cannot be removed, as localhostbecontroller
141*4882a593Smuzhiyun    # relies on output in the toaster_ui.log file to determine whether
142*4882a593Smuzhiyun    # the bitbake server has started, which only happens if
143*4882a593Smuzhiyun    # this logger is setup here (see the TODO in the loop below)
144*4882a593Smuzhiyun    console = logging.StreamHandler(sys.stdout)
145*4882a593Smuzhiyun    format_str = "%(levelname)s: %(message)s"
146*4882a593Smuzhiyun    formatter = bb.msg.BBLogFormatter(format_str)
147*4882a593Smuzhiyun    bb.msg.addDefaultlogFilter(console)
148*4882a593Smuzhiyun    console.setFormatter(formatter)
149*4882a593Smuzhiyun    logger.addHandler(console)
150*4882a593Smuzhiyun    logger.setLevel(logging.INFO)
151*4882a593Smuzhiyun    llevel, debug_domains = bb.msg.constructLogOptions()
152*4882a593Smuzhiyun    result, error = server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
153*4882a593Smuzhiyun    if not result or error:
154*4882a593Smuzhiyun        logger.error("can't set event mask: %s", error)
155*4882a593Smuzhiyun        return 1
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun    # verify and warn
158*4882a593Smuzhiyun    build_history_enabled = True
159*4882a593Smuzhiyun    inheritlist, _ = server.runCommand(["getVariable", "INHERIT"])
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun    if not "buildhistory" in inheritlist.split(" "):
162*4882a593Smuzhiyun        logger.warning("buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details.")
163*4882a593Smuzhiyun        build_history_enabled = False
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun    if not "buildstats" in inheritlist.split(" "):
166*4882a593Smuzhiyun        logger.warning("buildstats is not enabled. Please enable INHERIT += \"buildstats\" to generate build statistics.")
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun    if not params.observe_only:
169*4882a593Smuzhiyun        cmdline = params.parseActions()
170*4882a593Smuzhiyun        if not cmdline:
171*4882a593Smuzhiyun            print("Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
172*4882a593Smuzhiyun            return 1
173*4882a593Smuzhiyun        if 'msg' in cmdline and cmdline['msg']:
174*4882a593Smuzhiyun            logger.error(cmdline['msg'])
175*4882a593Smuzhiyun            return 1
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun        ret, error = server.runCommand(cmdline['action'])
178*4882a593Smuzhiyun        if error:
179*4882a593Smuzhiyun            logger.error("Command '%s' failed: %s" % (cmdline, error))
180*4882a593Smuzhiyun            return 1
181*4882a593Smuzhiyun        elif not ret:
182*4882a593Smuzhiyun            logger.error("Command '%s' failed: returned %s" % (cmdline, ret))
183*4882a593Smuzhiyun            return 1
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun    # set to 1 when toasterui needs to shut down
186*4882a593Smuzhiyun    main.shutdown = 0
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun    interrupted = False
189*4882a593Smuzhiyun    return_value = 0
190*4882a593Smuzhiyun    errors = 0
191*4882a593Smuzhiyun    warnings = 0
192*4882a593Smuzhiyun    taskfailures = []
193*4882a593Smuzhiyun    first = True
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun    buildinfohelper = BuildInfoHelper(server, build_history_enabled,
196*4882a593Smuzhiyun                                      os.getenv('TOASTER_BRBE'))
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun    # write our own log files into bitbake's log directory;
199*4882a593Smuzhiyun    # we're only interested in the path to the parent directory of
200*4882a593Smuzhiyun    # this file, as we're writing our own logs into the same directory
201*4882a593Smuzhiyun    consolelogfile = _log_settings_from_server(server)
202*4882a593Smuzhiyun    log_dir = os.path.dirname(consolelogfile)
203*4882a593Smuzhiyun    bb.utils.mkdirhier(log_dir)
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun    while True:
206*4882a593Smuzhiyun        try:
207*4882a593Smuzhiyun            event = eventHandler.waitEvent(0.25)
208*4882a593Smuzhiyun            if first:
209*4882a593Smuzhiyun                first = False
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun                # TODO don't use log output to determine when bitbake has started
212*4882a593Smuzhiyun                #
213*4882a593Smuzhiyun                # this is the line localhostbecontroller needs to
214*4882a593Smuzhiyun                # see in toaster_ui.log which it uses to decide whether
215*4882a593Smuzhiyun                # the bitbake server has started...
216*4882a593Smuzhiyun                logger.info("ToasterUI waiting for events")
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun            if event is None:
219*4882a593Smuzhiyun                if main.shutdown > 0:
220*4882a593Smuzhiyun                    # if shutting down, close any open build log first
221*4882a593Smuzhiyun                    _close_build_log(build_log)
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun                    break
224*4882a593Smuzhiyun                continue
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun            helper.eventHandler(event)
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun            # pylint: disable=protected-access
229*4882a593Smuzhiyun            # the code will look into the protected variables of the event; no easy way around this
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun            if isinstance(event, bb.event.HeartbeatEvent):
232*4882a593Smuzhiyun                continue
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun            if isinstance(event, bb.event.ParseStarted):
235*4882a593Smuzhiyun                if not (build_log and build_log_file_path):
236*4882a593Smuzhiyun                    build_log, build_log_file_path = _open_build_log(log_dir)
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun                buildinfohelper.store_started_build()
239*4882a593Smuzhiyun                buildinfohelper.save_build_log_file_path(build_log_file_path)
240*4882a593Smuzhiyun                buildinfohelper.set_recipes_to_parse(event.total)
241*4882a593Smuzhiyun                continue
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun            # create a build object in buildinfohelper from either BuildInit
244*4882a593Smuzhiyun            # (if available) or BuildStarted (for jethro and previous versions)
245*4882a593Smuzhiyun            if isinstance(event, (bb.event.BuildStarted, bb.event.BuildInit)):
246*4882a593Smuzhiyun                if not (build_log and build_log_file_path):
247*4882a593Smuzhiyun                    build_log, build_log_file_path = _open_build_log(log_dir)
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun                buildinfohelper.save_build_targets(event)
250*4882a593Smuzhiyun                buildinfohelper.save_build_log_file_path(build_log_file_path)
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun                # get additional data from BuildStarted
253*4882a593Smuzhiyun                if isinstance(event, bb.event.BuildStarted):
254*4882a593Smuzhiyun                    buildinfohelper.save_build_layers_and_variables()
255*4882a593Smuzhiyun                continue
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun            if isinstance(event, bb.event.ParseProgress):
258*4882a593Smuzhiyun                buildinfohelper.set_recipes_parsed(event.current)
259*4882a593Smuzhiyun                continue
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun            if isinstance(event, bb.event.ParseCompleted):
262*4882a593Smuzhiyun                buildinfohelper.set_recipes_parsed(event.total)
263*4882a593Smuzhiyun                continue
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun            if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)):
266*4882a593Smuzhiyun                buildinfohelper.update_and_store_task(event)
267*4882a593Smuzhiyun                logger.info("Logfile for task %s", event.logfile)
268*4882a593Smuzhiyun                continue
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun            if isinstance(event, bb.build.TaskBase):
271*4882a593Smuzhiyun                logger.info(event._message)
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun            if isinstance(event, bb.event.LogExecTTY):
274*4882a593Smuzhiyun                logger.info(event.msg)
275*4882a593Smuzhiyun                continue
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun            if isinstance(event, logging.LogRecord):
278*4882a593Smuzhiyun                if event.levelno == -1:
279*4882a593Smuzhiyun                    event.levelno = formatter.ERROR
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun                buildinfohelper.store_log_event(event)
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun                if event.levelno >= formatter.ERROR:
284*4882a593Smuzhiyun                    errors = errors + 1
285*4882a593Smuzhiyun                elif event.levelno == formatter.WARNING:
286*4882a593Smuzhiyun                    warnings = warnings + 1
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun                # For "normal" logging conditions, don't show note logs from tasks
289*4882a593Smuzhiyun                # but do show them if the user has changed the default log level to
290*4882a593Smuzhiyun                # include verbose/debug messages
291*4882a593Smuzhiyun                if event.taskpid != 0 and event.levelno <= formatter.NOTE:
292*4882a593Smuzhiyun                    continue
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun                logger.handle(event)
295*4882a593Smuzhiyun                continue
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun            if isinstance(event, bb.build.TaskFailed):
298*4882a593Smuzhiyun                buildinfohelper.update_and_store_task(event)
299*4882a593Smuzhiyun                logfile = event.logfile
300*4882a593Smuzhiyun                if logfile and os.path.exists(logfile):
301*4882a593Smuzhiyun                    bb.error("Logfile of failure stored in: %s" % logfile)
302*4882a593Smuzhiyun                continue
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun            # these events are unprocessed now, but may be used in the future to log
305*4882a593Smuzhiyun            # timing and error informations from the parsing phase in Toaster
306*4882a593Smuzhiyun            if isinstance(event, (bb.event.SanityCheckPassed, bb.event.SanityCheck)):
307*4882a593Smuzhiyun                continue
308*4882a593Smuzhiyun            if isinstance(event, bb.event.CacheLoadStarted):
309*4882a593Smuzhiyun                continue
310*4882a593Smuzhiyun            if isinstance(event, bb.event.CacheLoadProgress):
311*4882a593Smuzhiyun                continue
312*4882a593Smuzhiyun            if isinstance(event, bb.event.CacheLoadCompleted):
313*4882a593Smuzhiyun                continue
314*4882a593Smuzhiyun            if isinstance(event, bb.event.MultipleProviders):
315*4882a593Smuzhiyun                logger.info(str(event))
316*4882a593Smuzhiyun                continue
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun            if isinstance(event, bb.event.NoProvider):
319*4882a593Smuzhiyun                errors = errors + 1
320*4882a593Smuzhiyun                text = str(event)
321*4882a593Smuzhiyun                logger.error(text)
322*4882a593Smuzhiyun                buildinfohelper.store_log_error(text)
323*4882a593Smuzhiyun                continue
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun            if isinstance(event, bb.event.ConfigParsed):
326*4882a593Smuzhiyun                continue
327*4882a593Smuzhiyun            if isinstance(event, bb.event.RecipeParsed):
328*4882a593Smuzhiyun                continue
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun            # end of saved events
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun            if isinstance(event, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted, bb.runqueue.runQueueTaskSkipped)):
333*4882a593Smuzhiyun                buildinfohelper.store_started_task(event)
334*4882a593Smuzhiyun                continue
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun            if isinstance(event, bb.runqueue.runQueueTaskCompleted):
337*4882a593Smuzhiyun                buildinfohelper.update_and_store_task(event)
338*4882a593Smuzhiyun                continue
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun            if isinstance(event, bb.runqueue.runQueueTaskFailed):
341*4882a593Smuzhiyun                buildinfohelper.update_and_store_task(event)
342*4882a593Smuzhiyun                taskfailures.append(event.taskstring)
343*4882a593Smuzhiyun                logger.error(str(event))
344*4882a593Smuzhiyun                continue
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun            if isinstance(event, (bb.runqueue.sceneQueueTaskCompleted, bb.runqueue.sceneQueueTaskFailed)):
347*4882a593Smuzhiyun                buildinfohelper.update_and_store_task(event)
348*4882a593Smuzhiyun                continue
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun            if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)):
352*4882a593Smuzhiyun                continue
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun            if isinstance(event, (bb.event.BuildCompleted, bb.command.CommandFailed)):
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun                errorcode = 0
357*4882a593Smuzhiyun                if isinstance(event, bb.command.CommandFailed):
358*4882a593Smuzhiyun                    errors += 1
359*4882a593Smuzhiyun                    errorcode = 1
360*4882a593Smuzhiyun                    logger.error(str(event))
361*4882a593Smuzhiyun                elif isinstance(event, bb.event.BuildCompleted):
362*4882a593Smuzhiyun                    buildinfohelper.scan_image_artifacts()
363*4882a593Smuzhiyun                    buildinfohelper.clone_required_sdk_artifacts()
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun                # turn off logging to the current build log
366*4882a593Smuzhiyun                _close_build_log(build_log)
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun                # reset ready for next BuildStarted
369*4882a593Smuzhiyun                build_log = None
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun                # update the build info helper on BuildCompleted, not on CommandXXX
372*4882a593Smuzhiyun                buildinfohelper.update_build_information(event, errors, warnings, taskfailures)
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun                brbe = buildinfohelper.brbe
375*4882a593Smuzhiyun                buildinfohelper.close(errorcode)
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun                # we start a new build info
378*4882a593Smuzhiyun                if params.observe_only:
379*4882a593Smuzhiyun                    logger.debug("ToasterUI prepared for new build")
380*4882a593Smuzhiyun                    errors = 0
381*4882a593Smuzhiyun                    warnings = 0
382*4882a593Smuzhiyun                    taskfailures = []
383*4882a593Smuzhiyun                    buildinfohelper = BuildInfoHelper(server, build_history_enabled)
384*4882a593Smuzhiyun                else:
385*4882a593Smuzhiyun                    main.shutdown = 1
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun                logger.info("ToasterUI build done, brbe: %s", brbe)
388*4882a593Smuzhiyun                continue
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun            if isinstance(event, (bb.command.CommandCompleted,
391*4882a593Smuzhiyun                                  bb.command.CommandFailed,
392*4882a593Smuzhiyun                                  bb.command.CommandExit)):
393*4882a593Smuzhiyun                if params.observe_only:
394*4882a593Smuzhiyun                    errorcode = 0
395*4882a593Smuzhiyun                else:
396*4882a593Smuzhiyun                    main.shutdown = 1
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun                continue
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun            if isinstance(event, bb.event.MetadataEvent):
401*4882a593Smuzhiyun                if event.type == "SinglePackageInfo":
402*4882a593Smuzhiyun                    buildinfohelper.store_build_package_information(event)
403*4882a593Smuzhiyun                elif event.type == "LayerInfo":
404*4882a593Smuzhiyun                    buildinfohelper.store_layer_info(event)
405*4882a593Smuzhiyun                elif event.type == "BuildStatsList":
406*4882a593Smuzhiyun                    buildinfohelper.store_tasks_stats(event)
407*4882a593Smuzhiyun                elif event.type == "ImagePkgList":
408*4882a593Smuzhiyun                    buildinfohelper.store_target_package_data(event)
409*4882a593Smuzhiyun                elif event.type == "MissedSstate":
410*4882a593Smuzhiyun                    buildinfohelper.store_missed_state_tasks(event)
411*4882a593Smuzhiyun                elif event.type == "SDKArtifactInfo":
412*4882a593Smuzhiyun                    buildinfohelper.scan_sdk_artifacts(event)
413*4882a593Smuzhiyun                elif event.type == "SetBRBE":
414*4882a593Smuzhiyun                    buildinfohelper.brbe = buildinfohelper._get_data_from_event(event)
415*4882a593Smuzhiyun                elif event.type == "TaskArtifacts":
416*4882a593Smuzhiyun                    buildinfohelper.scan_task_artifacts(event)
417*4882a593Smuzhiyun                elif event.type == "OSErrorException":
418*4882a593Smuzhiyun                    logger.error(event)
419*4882a593Smuzhiyun                else:
420*4882a593Smuzhiyun                    logger.error("Unprocessed MetadataEvent %s", event.type)
421*4882a593Smuzhiyun                continue
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun            if isinstance(event, bb.cooker.CookerExit):
424*4882a593Smuzhiyun                # shutdown when bitbake server shuts down
425*4882a593Smuzhiyun                main.shutdown = 1
426*4882a593Smuzhiyun                continue
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun            if isinstance(event, bb.event.DepTreeGenerated):
429*4882a593Smuzhiyun                buildinfohelper.store_dependency_information(event)
430*4882a593Smuzhiyun                continue
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun            logger.warning("Unknown event: %s", event)
433*4882a593Smuzhiyun            return_value += 1
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun        except EnvironmentError as ioerror:
436*4882a593Smuzhiyun            logger.warning("EnvironmentError: %s" % ioerror)
437*4882a593Smuzhiyun            # ignore interrupted io system calls
438*4882a593Smuzhiyun            if ioerror.args[0] == 4: # errno 4 is EINTR
439*4882a593Smuzhiyun                logger.warning("Skipped EINTR: %s" % ioerror)
440*4882a593Smuzhiyun            else:
441*4882a593Smuzhiyun                raise
442*4882a593Smuzhiyun        except KeyboardInterrupt:
443*4882a593Smuzhiyun            if params.observe_only:
444*4882a593Smuzhiyun                print("\nKeyboard Interrupt, exiting observer...")
445*4882a593Smuzhiyun                main.shutdown = 2
446*4882a593Smuzhiyun            if not params.observe_only and main.shutdown == 1:
447*4882a593Smuzhiyun                print("\nSecond Keyboard Interrupt, stopping...\n")
448*4882a593Smuzhiyun                _, error = server.runCommand(["stateForceShutdown"])
449*4882a593Smuzhiyun                if error:
450*4882a593Smuzhiyun                    logger.error("Unable to cleanly stop: %s" % error)
451*4882a593Smuzhiyun            if not params.observe_only and main.shutdown == 0:
452*4882a593Smuzhiyun                print("\nKeyboard Interrupt, closing down...\n")
453*4882a593Smuzhiyun                interrupted = True
454*4882a593Smuzhiyun                _, error = server.runCommand(["stateShutdown"])
455*4882a593Smuzhiyun                if error:
456*4882a593Smuzhiyun                    logger.error("Unable to cleanly shutdown: %s" % error)
457*4882a593Smuzhiyun            buildinfohelper.cancel_cli_build()
458*4882a593Smuzhiyun            main.shutdown = main.shutdown + 1
459*4882a593Smuzhiyun        except Exception as e:
460*4882a593Smuzhiyun            # print errors to log
461*4882a593Smuzhiyun            import traceback
462*4882a593Smuzhiyun            from pprint import pformat
463*4882a593Smuzhiyun            exception_data = traceback.format_exc()
464*4882a593Smuzhiyun            logger.error("%s\n%s" , e, exception_data)
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun            # save them to database, if possible; if it fails, we already logged to console.
467*4882a593Smuzhiyun            try:
468*4882a593Smuzhiyun                buildinfohelper.store_log_exception("%s\n%s" % (str(e), exception_data))
469*4882a593Smuzhiyun            except Exception as ce:
470*4882a593Smuzhiyun                logger.error("CRITICAL - Failed to to save toaster exception to the database: %s", str(ce))
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun            # make sure we return with an error
473*4882a593Smuzhiyun            return_value += 1
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun    if interrupted and return_value == 0:
476*4882a593Smuzhiyun        return_value += 1
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun    logger.warning("Return value is %d", return_value)
479*4882a593Smuzhiyun    return return_value
480