[bitbake-devel] [PATCH 3/3] ui/json: Add JSON UI

Tomasz Meresiński tomasz.meresinski at comarch.pl
Mon Oct 29 16:39:33 UTC 2018


Knotty UI output is difficult to parse correctly in other apps.
Add JSON UI that print to stdout all events as JSON.
This will make it easier to integrate bitbake with other systems.

Signed-off-by: Tomasz Meresiński <tomasz.meresinski at comarch.com>
---
 lib/bb/ui/json.py | 275 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 275 insertions(+)
 create mode 100644 lib/bb/ui/json.py

diff --git a/lib/bb/ui/json.py b/lib/bb/ui/json.py
new file mode 100644
index 00000000..069dcb4f
--- /dev/null
+++ b/lib/bb/ui/json.py
@@ -0,0 +1,275 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# Bitbake Json UI
+#
+# Copyright (C) 2018 Tomasz Meresiński
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import json
+import logging
+import os
+
+import bb
+
+def generate_type(event):
+    c = event.__class__
+
+    return "{c.__module__}.{c.__name__}".format(c=c)
+
+json_types = {
+    logging.LogRecord: lambda event: {
+        **event.__dict__
+    },
+    bb.event.Event: lambda event: {
+        "pid": event.pid,
+    },
+    bb.event.HeartbeatEvent: lambda event: {
+        "time": event.time,
+    },
+    bb.event.OperationStarted: lambda event: {
+        "msg": event.msg,
+    },
+    bb.event.OperationCompleted: lambda event: {
+        "total": event.total,
+        "msg": event.msg,
+    },
+    bb.event.OperationProgress: lambda event: {
+        "current": event.current,
+        "total": event.total,
+        "msg": event.msg,
+    },
+    bb.event.MultiConfigParsed: lambda event: {
+        "mcdata": event.mcdata,
+    },
+    bb.event.RecipeEvent: lambda event: {
+        "fn": event.fn,
+    },
+    bb.event.RecipeTaskPreProcess: lambda event: {
+        "fn": event.fn,
+        "tasklist": event.tasklist,
+    },
+    bb.event.StampUpdate: lambda event: {
+        "stampPrefix": event.stampPrefix,
+        "targets": event.targets,
+    },
+    bb.event.BuildBase: lambda event: {
+        "pkgs": event.pkgs,
+        "name": event.name,
+        "failures": event.getFailures(),
+    },
+    bb.event.MonitorDiskEvent: lambda event: {
+        "disk_usage": event.disk_usage,
+    },
+    bb.event.DiskUsageSample: lambda sample: {
+        "available_bytes": sample.available_bytes,
+        "free_bytes": sample.free_bytes,
+        "total_bytes": sample.total_bytes,
+    },
+    bb.event.NoProvider: lambda event: {
+        "item": event.getItem(),
+        "isRuntime": event.isRuntime(),
+        "msg": str(event),
+    },
+    bb.event.MultipleProviders: lambda event: {
+        "isRuntime": event.isRuntime(),
+        "item": event.getItem(),
+        "candidates": event.getCandidates(),
+        "msg": str(event),
+    },
+    bb.event.ParseStarted: lambda event: {
+        "total": event.total,
+    },
+    bb.event.ParseCompleted: lambda event: {
+        "cached": event.cached,
+        "parsed": event.parsed,
+        "skipped": event.skipped,
+        "virtuals": event.virtuals,
+        "masked": event.masked,
+        "errors": event.errors,
+        "sofar": event.sofar,
+    },
+    bb.event.CacheLoadStarted: lambda event: {
+        "total": event.total,
+    },
+    bb.event.CacheLoadCompleted: lambda event: {
+        "num_entries": event.num_entries,
+    },
+    bb.event.ReachableStamps: lambda event: {
+        "stamps": event.stamps,
+    },
+    bb.event.LogExecTTY: lambda event: {
+        "msg": event.msg,
+        "prog": event.prog,
+        "sleep_delay": event.sleep_delay,
+        "retries": event.retries,
+    },
+    bb.event.MetadataEvent: lambda event: {
+        "type": event.type,
+    },
+    bb.event.ProcessStarted: lambda event: {
+        "processname": event.processname,
+        "total": event.total,
+    },
+    bb.event.ProcessProgress: lambda event: {
+        "processname": event.processname,
+        "progress": event.progress,
+    },
+    bb.event.ProcessFinished: lambda event: {
+        "processname": event.processname,
+    },
+    bb.event.SanityCheck: lambda event: {
+        "generateevents": event.generateevents,
+    },
+    bb.event.NetworkTest: lambda event: {
+        "generateevents": event.generateevents,
+    },
+    bb.event.FindSigInfoResult: lambda event: {
+        "result": event.result,
+    },
+    bb.build.TaskBase: lambda event: {
+        "taskfile": event.taskfile,
+        "taskname": event.taskname,
+        "logfile": event.logfile,
+        "time": event.time,
+        "task": event.task,
+        "displayName": event.getDisplayName(),
+        "package": event.package,
+    },
+    bb.build.TaskStarted: lambda event: {
+        "taskflags": event.taskflags,
+    },
+    bb.build.TaskFailed: lambda event: {
+        "errprinted": event.errprinted,
+    },
+    bb.build.TaskProgress: lambda event: {
+        "progress": event.progress,
+        "rate": event.rate,
+    },
+    bb.runqueue.runQueueExitWait: lambda event: {
+        "remain": event.remain,
+        "message": event.message,
+    },
+    bb.runqueue.runQueueEvent: lambda event: {
+        "taskid": event.taskid,
+        "taskstring": event.taskstring,
+        "taskname": event.taskname,
+        "taskfile": event.taskfile,
+        "taskhash": event.taskhash,
+        "stats": event.stats,
+    },
+    bb.runqueue.RunQueueStats: lambda stats: {
+        "completed": stats.completed,
+        "skipped": stats.skipped,
+        "failed": stats.failed,
+        "active": stats.active,
+        "total": stats.total,
+    },
+    bb.runqueue.runQueueTaskStarted: lambda event: {
+        "noexec": event.noexec,
+    },
+    bb.runqueue.sceneQueueTaskStarted: lambda event: {
+        "noexec": event.noexec,
+    },
+    bb.runqueue.runQueueTaskFailed: lambda event: {
+        "exitcode": event.exitcode,
+        "msg": str(event),
+    },
+    bb.runqueue.sceneQueueTaskFailed: lambda event: {
+        "exitcode": event.exitcode,
+        "msg": str(event),
+    },
+    bb.runqueue.sceneQueueComplete: lambda event: {
+        "stats": event.stats,
+    },
+    bb.runqueue.runQueueTaskSkipped: lambda event: {
+        "reason": event.reason,
+    },
+    bb.command.CommandExit: lambda event: {
+        "exitcode": event.exitcode,
+    },
+    bb.command.CommandFailed: lambda event: {
+        "error": event.error,
+    },
+}
+
+class EventEncoder(json.JSONEncoder):
+    def default(self, event):
+        result = dict()
+
+        for (event_type, callback) in json_types.items():
+            if isinstance(event, event_type):
+                result.update(callback(event))
+
+        if len(result) > 0:
+            #We handled that type
+            result["type"] = generate_type(event)
+            return result
+
+        return json.JSONEncoder.default(self, event)
+
+def main(server, eventHandler, params):
+    # llevel, debug_domains = bb.msg.constructLogOptions()
+    # server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
+
+    if not params.observe_only:
+        params.updateToServer(server, os.environ.copy())
+
+    bb.utils.set_process_name("JSON-UI")
+
+    if params.options.remote_server and params.options.kill_server:
+        server.terminateServer()
+        return 0
+
+    if not params.observe_only:
+        params.updateFromServer(server)
+        cmdline = params.parseActions()
+        if not cmdline:
+            #Nothing to do
+            return 0
+
+        if 'msg' in cmdline and cmdline['msg']:
+            record = logging.makeLogRecord({'level': logging.ERROR, 'msg': cmdline['msg']})
+            print(json.dumps(record, cls=EventEncoder), flush=True)
+            return 0
+
+        action = cmdline['action']
+
+        ret, error = server.runCommand(action)
+        if ret != True:
+            error = "Cannot get default commandline"
+
+        if error:
+            record = logging.makeLogRecord({'level': logging.ERROR, 'msg': "Error running command '%s': %s" % (action, error)})
+
+    try:
+        return_value = 0
+        while True:
+            event = eventHandler.waitEvent(None)
+
+            print(json.dumps(event, cls=EventEncoder), flush=True)
+
+            if isinstance(event, bb.command.CommandFailed):
+                return 1
+
+            if isinstance(event, bb.build.TaskFailed):
+                return_value = 1
+
+            if isinstance(event, (bb.command.CommandCompleted, bb.cooker.CookerExit)):
+                return return_value
+
+    except Exception:
+        server.runCommand(["stateForceShutdown"])
+        return 1
-- 
2.17.1




More information about the bitbake-devel mailing list