summaryrefslogtreecommitdiff
path: root/bitbake/lib/bb/ui/crumbs/runningbuild.py
blob: b4416ecbb32a0d59898659000871ae364e235202 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2008        Intel Corporation
#
# Authored by Rob Bradford <rob@linux.intel.com>
#
# 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 gtk
import gobject

class RunningBuildModel (gtk.TreeStore):
    (COL_TYPE, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_ACTIVE) = (0, 1, 2, 3, 4, 5)
    def __init__ (self):
        gtk.TreeStore.__init__ (self,
                                gobject.TYPE_STRING,
                                gobject.TYPE_STRING,
                                gobject.TYPE_STRING,
                                gobject.TYPE_STRING,
                                gobject.TYPE_STRING,
                                gobject.TYPE_BOOLEAN)

class RunningBuild (gobject.GObject):
    __gsignals__ = {
          'build-succeeded' : (gobject.SIGNAL_RUN_LAST,
                               gobject.TYPE_NONE,
                               ()),
          'build-failed' : (gobject.SIGNAL_RUN_LAST,
                            gobject.TYPE_NONE,
                            ())
          }
    pids_to_task = {}
    tasks_to_iter = {}

    def __init__ (self):
        gobject.GObject.__init__ (self)
        self.model = RunningBuildModel()

    def handle_event (self, event):
        # Handle an event from the event queue, this may result in updating
        # the model and thus the UI. Or it may be to tell us that the build
        # has finished successfully (or not, as the case may be.)

        parent = None
        pid = 0
        package = None
        task = None

        # If we have a pid attached to this message/event try and get the
        # (package, task) pair for it. If we get that then get the parent iter
        # for the message.
        if hasattr(event, 'pid'):
            pid = event.pid
            if pid in self.pids_to_task:
                (package, task) = self.pids_to_task[pid]
                parent = self.tasks_to_iter[(package, task)]

        if isinstance(event, bb.msg.Msg):
            # Set a pretty icon for the message based on it's type.
            if isinstance(event, bb.msg.MsgWarn):
                icon = "dialog-warning"
            elif isinstance(event, bb.msg.MsgErr):
                icon = "dialog-error"
            else:
                icon = None

            # Ignore the "Running task i of n .." messages
            if (event._message.startswith ("Running task")):
                return

            # Add the message to the tree either at the top level if parent is
            # None otherwise as a descendent of a task.
            self.model.append (parent,
                               (event.__name__.split()[-1], # e.g. MsgWarn, MsgError
                                package,
                                task,
                                event._message,
                                icon,
                                False))
        elif isinstance(event, bb.build.TaskStarted):
            (package, task) = (event._package, event._task)

            # Save out this PID.
            self.pids_to_task[pid] = (package, task)

            # Check if we already have this package in our model. If so then
            # that can be the parent for the task. Otherwise we create a new
            # top level for the package.
            if ((package, None) in self.tasks_to_iter):
                parent = self.tasks_to_iter[(package, None)]
            else:
                parent = self.model.append (None, (None,
                                                   package,
                                                   None,
                                                   "Package: %s" % (package),
                                                   None,
                                                   False))
                self.tasks_to_iter[(package, None)] = parent

            # Because this parent package now has an active child mark it as
            # such.
            self.model.set(parent, self.model.COL_ICON, "gtk-execute")

            # Add an entry in the model for this task
            i = self.model.append (parent, (None,
                                            package,
                                            task,
                                            "Task: %s" % (task),
                                            None,
                                            False))

            # Save out the iter so that we can find it when we have a message
            # that we need to attach to a task.
            self.tasks_to_iter[(package, task)] = i

            # Mark this task as active.
            self.model.set(i, self.model.COL_ICON, "gtk-execute")

        elif isinstance(event, bb.build.Task):

            if isinstance(event, bb.build.TaskFailed):
                # Mark the task as failed
                i = self.tasks_to_iter[(package, task)]
                self.model.set(i, self.model.COL_ICON, "dialog-error")

                # Mark the parent package as failed
                i = self.tasks_to_iter[(package, None)]
                self.model.set(i, self.model.COL_ICON, "dialog-error")
            else:
                # Mark the task as inactive
                i = self.tasks_to_iter[(package, task)]
                self.model.set(i, self.model.COL_ICON, None)

                # Mark the parent package as inactive
                i = self.tasks_to_iter[(package, None)]
                self.model.set(i, self.model.COL_ICON, None)


            # Clear the iters and the pids since when the task goes away the
            # pid will no longer be used for messages
            del self.tasks_to_iter[(package, task)]
            del self.pids_to_task[pid]

        elif isinstance(event, bb.event.BuildCompleted):
            failures = int (event._failures)

            # Emit the appropriate signal depending on the number of failures
            if (failures > 1):
                self.emit ("build-failed")
            else:
                self.emit ("build-succeeded")

class RunningBuildTreeView (gtk.TreeView):
    def __init__ (self):
        gtk.TreeView.__init__ (self)

        # The icon that indicates whether we're building or failed.
        renderer = gtk.CellRendererPixbuf ()
        col = gtk.TreeViewColumn ("Status", renderer)
        col.add_attribute (renderer, "icon-name", 4)
        self.append_column (col)

        # The message of the build.
        renderer = gtk.CellRendererText ()
        col = gtk.TreeViewColumn ("Message", renderer, text=3)
        self.append_column (col)