| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame^] | 1 | #!/usr/bin/env python | 
|  | 2 | # ex:ts=4:sw=4:sts=4:et | 
|  | 3 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | 
|  | 4 | # | 
|  | 5 | # Copyright (C) 2014        Alex Damian | 
|  | 6 | # | 
|  | 7 | # This file re-uses code spread throughout other Bitbake source files. | 
|  | 8 | # As such, all other copyrights belong to their own right holders. | 
|  | 9 | # | 
|  | 10 | # | 
|  | 11 | # This program is free software; you can redistribute it and/or modify | 
|  | 12 | # it under the terms of the GNU General Public License version 2 as | 
|  | 13 | # published by the Free Software Foundation. | 
|  | 14 | # | 
|  | 15 | # This program is distributed in the hope that it will be useful, | 
|  | 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 18 | # GNU General Public License for more details. | 
|  | 19 | # | 
|  | 20 | # You should have received a copy of the GNU General Public License along | 
|  | 21 | # with this program; if not, write to the Free Software Foundation, Inc., | 
|  | 22 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
|  | 23 |  | 
|  | 24 |  | 
|  | 25 | # This command takes a filename as a single parameter. The filename is read | 
|  | 26 | # as a build eventlog, and the ToasterUI is used to process events in the file | 
|  | 27 | # and log data in the database | 
|  | 28 |  | 
|  | 29 | from __future__ import print_function | 
|  | 30 | import os | 
|  | 31 | import sys, logging | 
|  | 32 |  | 
|  | 33 | # mangle syspath to allow easy import of modules | 
|  | 34 | sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), | 
|  | 35 | 'lib')) | 
|  | 36 |  | 
|  | 37 |  | 
|  | 38 | import bb.cooker | 
|  | 39 | from bb.ui import toasterui | 
|  | 40 | import sys | 
|  | 41 | import logging | 
|  | 42 |  | 
|  | 43 | import json, pickle | 
|  | 44 |  | 
|  | 45 |  | 
|  | 46 | class FileReadEventsServerConnection(): | 
|  | 47 | """  Emulates a connection to a bitbake server that feeds | 
|  | 48 | events coming actually read from a saved log file. | 
|  | 49 | """ | 
|  | 50 |  | 
|  | 51 | class MockConnection(): | 
|  | 52 | """ fill-in for the proxy to the server. we just return generic data | 
|  | 53 | """ | 
|  | 54 | def __init__(self, sc): | 
|  | 55 | self._sc = sc | 
|  | 56 |  | 
|  | 57 | def runCommand(self, commandArray): | 
|  | 58 | """ emulates running a command on the server; only read-only commands are accepted """ | 
|  | 59 | command_name = commandArray[0] | 
|  | 60 |  | 
|  | 61 | if command_name == "getVariable": | 
|  | 62 | if commandArray[1] in self._sc._variables: | 
|  | 63 | return (self._sc._variables[commandArray[1]]['v'], None) | 
|  | 64 | return (None, "Missing variable") | 
|  | 65 |  | 
|  | 66 | elif command_name == "getAllKeysWithFlags": | 
|  | 67 | dump = {} | 
|  | 68 | flaglist = commandArray[1] | 
|  | 69 | for k in self._sc._variables.keys(): | 
|  | 70 | try: | 
|  | 71 | if not k.startswith("__"): | 
|  | 72 | v = self._sc._variables[k]['v'] | 
|  | 73 | dump[k] = { | 
|  | 74 | 'v' : v , | 
|  | 75 | 'history' : self._sc._variables[k]['history'], | 
|  | 76 | } | 
|  | 77 | for d in flaglist: | 
|  | 78 | dump[k][d] = self._sc._variables[k][d] | 
|  | 79 | except Exception as e: | 
|  | 80 | print(e) | 
|  | 81 | return (dump, None) | 
|  | 82 | else: | 
|  | 83 | raise Exception("Command %s not implemented" % commandArray[0]) | 
|  | 84 |  | 
|  | 85 | def terminateServer(self): | 
|  | 86 | """ do not do anything """ | 
|  | 87 | pass | 
|  | 88 |  | 
|  | 89 |  | 
|  | 90 |  | 
|  | 91 | class EventReader(): | 
|  | 92 | def __init__(self, sc): | 
|  | 93 | self._sc = sc | 
|  | 94 | self.firstraise = 0 | 
|  | 95 |  | 
|  | 96 | def _create_event(self, line): | 
|  | 97 | def _import_class(name): | 
|  | 98 | assert len(name) > 0 | 
|  | 99 | assert "." in name, name | 
|  | 100 |  | 
|  | 101 | components = name.strip().split(".") | 
|  | 102 | modulename = ".".join(components[:-1]) | 
|  | 103 | moduleklass = components[-1] | 
|  | 104 |  | 
|  | 105 | module = __import__(modulename, fromlist=[str(moduleklass)]) | 
|  | 106 | return getattr(module, moduleklass) | 
|  | 107 |  | 
|  | 108 | # we build a toaster event out of current event log line | 
|  | 109 | try: | 
|  | 110 | event_data = json.loads(line.strip()) | 
|  | 111 | event_class = _import_class(event_data['class']) | 
|  | 112 | event_object = pickle.loads(json.loads(event_data['vars'])) | 
|  | 113 | except ValueError as e: | 
|  | 114 | print("Failed loading ", line) | 
|  | 115 | raise e | 
|  | 116 |  | 
|  | 117 | if not isinstance(event_object, event_class): | 
|  | 118 | raise Exception("Error loading objects %s class %s ", event_object, event_class) | 
|  | 119 |  | 
|  | 120 | return event_object | 
|  | 121 |  | 
|  | 122 | def waitEvent(self, timeout): | 
|  | 123 |  | 
|  | 124 | nextline = self._sc._eventfile.readline() | 
|  | 125 | if len(nextline) == 0: | 
|  | 126 | # the build data ended, while toasterui still waits for events. | 
|  | 127 | # this happens when the server was abruptly stopped, so we simulate this | 
|  | 128 | self.firstraise += 1 | 
|  | 129 | if self.firstraise == 1: | 
|  | 130 | raise KeyboardInterrupt() | 
|  | 131 | else: | 
|  | 132 | return None | 
|  | 133 | else: | 
|  | 134 | self._sc.lineno += 1 | 
|  | 135 | return self._create_event(nextline) | 
|  | 136 |  | 
|  | 137 |  | 
|  | 138 | def _readVariables(self, variableline): | 
|  | 139 | self._variables = json.loads(variableline.strip())['allvariables'] | 
|  | 140 |  | 
|  | 141 |  | 
|  | 142 | def __init__(self, file_name): | 
|  | 143 | self.connection = FileReadEventsServerConnection.MockConnection(self) | 
|  | 144 | self._eventfile = open(file_name, "r") | 
|  | 145 |  | 
|  | 146 | # we expect to have the variable dump at the start of the file | 
|  | 147 | self.lineno = 1 | 
|  | 148 | self._readVariables(self._eventfile.readline()) | 
|  | 149 |  | 
|  | 150 | self.events = FileReadEventsServerConnection.EventReader(self) | 
|  | 151 |  | 
|  | 152 |  | 
|  | 153 |  | 
|  | 154 |  | 
|  | 155 |  | 
|  | 156 | class MockConfigParameters(): | 
|  | 157 | """ stand-in for cookerdata.ConfigParameters; as we don't really config a cooker, this | 
|  | 158 | serves just to supply needed interfaces for the toaster ui to work """ | 
|  | 159 | def __init__(self): | 
|  | 160 | self.observe_only = True            # we can only read files | 
|  | 161 |  | 
|  | 162 |  | 
|  | 163 | # run toaster ui on our mock bitbake class | 
|  | 164 | if __name__ == "__main__": | 
|  | 165 | if len(sys.argv) < 2: | 
|  | 166 | print("Usage: %s event.log " % sys.argv[0]) | 
|  | 167 | sys.exit(1) | 
|  | 168 |  | 
|  | 169 | file_name = sys.argv[-1] | 
|  | 170 | mock_connection = FileReadEventsServerConnection(file_name) | 
|  | 171 | configParams = MockConfigParameters() | 
|  | 172 |  | 
|  | 173 | # run the main program and set exit code to the returned value | 
|  | 174 | sys.exit(toasterui.main(mock_connection.connection, mock_connection.events, configParams)) |