blob: 240aafc3e74ccb4e35ee69318359705d57b720a9 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
2# BitBake Graphical GTK based Dependency Explorer
3#
4# Copyright (C) 2007 Ross Burton
5# Copyright (C) 2007 - 2008 Richard Purdie
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20import sys
21import gobject
22import gtk
23import Queue
24import threading
25import xmlrpclib
26import bb
27import bb.event
28from bb.ui.crumbs.progressbar import HobProgressBar
29
30# Package Model
31(COL_PKG_NAME) = (0)
32
33# Dependency Model
34(TYPE_DEP, TYPE_RDEP) = (0, 1)
35(COL_DEP_TYPE, COL_DEP_PARENT, COL_DEP_PACKAGE) = (0, 1, 2)
36
37
38class PackageDepView(gtk.TreeView):
39 def __init__(self, model, dep_type, label):
40 gtk.TreeView.__init__(self)
41 self.current = None
42 self.dep_type = dep_type
43 self.filter_model = model.filter_new()
44 self.filter_model.set_visible_func(self._filter)
45 self.set_model(self.filter_model)
46 #self.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
47 self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PACKAGE))
48
49 def _filter(self, model, iter):
50 (this_type, package) = model.get(iter, COL_DEP_TYPE, COL_DEP_PARENT)
51 if this_type != self.dep_type: return False
52 return package == self.current
53
54 def set_current_package(self, package):
55 self.current = package
56 self.filter_model.refilter()
57
58
59class PackageReverseDepView(gtk.TreeView):
60 def __init__(self, model, label):
61 gtk.TreeView.__init__(self)
62 self.current = None
63 self.filter_model = model.filter_new()
64 self.filter_model.set_visible_func(self._filter)
65 self.set_model(self.filter_model)
66 self.append_column(gtk.TreeViewColumn(label, gtk.CellRendererText(), text=COL_DEP_PARENT))
67
68 def _filter(self, model, iter):
69 package = model.get_value(iter, COL_DEP_PACKAGE)
70 return package == self.current
71
72 def set_current_package(self, package):
73 self.current = package
74 self.filter_model.refilter()
75
76
77class DepExplorer(gtk.Window):
78 def __init__(self):
79 gtk.Window.__init__(self)
80 self.set_title("Dependency Explorer")
81 self.set_default_size(500, 500)
82 self.connect("delete-event", gtk.main_quit)
83
84 # Create the data models
85 self.pkg_model = gtk.ListStore(gobject.TYPE_STRING)
86 self.pkg_model.set_sort_column_id(COL_PKG_NAME, gtk.SORT_ASCENDING)
87 self.depends_model = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING)
88 self.depends_model.set_sort_column_id(COL_DEP_PACKAGE, gtk.SORT_ASCENDING)
89
90 pane = gtk.HPaned()
91 pane.set_position(250)
92 self.add(pane)
93
94 # The master list of packages
95 scrolled = gtk.ScrolledWindow()
96 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
97 scrolled.set_shadow_type(gtk.SHADOW_IN)
98
99 self.pkg_treeview = gtk.TreeView(self.pkg_model)
100 self.pkg_treeview.get_selection().connect("changed", self.on_cursor_changed)
101 column = gtk.TreeViewColumn("Package", gtk.CellRendererText(), text=COL_PKG_NAME)
102 self.pkg_treeview.append_column(column)
103 pane.add1(scrolled)
104 scrolled.add(self.pkg_treeview)
105
106 box = gtk.VBox(homogeneous=True, spacing=4)
107
108 # Runtime Depends
109 scrolled = gtk.ScrolledWindow()
110 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
111 scrolled.set_shadow_type(gtk.SHADOW_IN)
112 self.rdep_treeview = PackageDepView(self.depends_model, TYPE_RDEP, "Runtime Depends")
113 self.rdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
114 scrolled.add(self.rdep_treeview)
115 box.add(scrolled)
116
117 # Build Depends
118 scrolled = gtk.ScrolledWindow()
119 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
120 scrolled.set_shadow_type(gtk.SHADOW_IN)
121 self.dep_treeview = PackageDepView(self.depends_model, TYPE_DEP, "Build Depends")
122 self.dep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
123 scrolled.add(self.dep_treeview)
124 box.add(scrolled)
125 pane.add2(box)
126
127 # Reverse Depends
128 scrolled = gtk.ScrolledWindow()
129 scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
130 scrolled.set_shadow_type(gtk.SHADOW_IN)
131 self.revdep_treeview = PackageReverseDepView(self.depends_model, "Reverse Depends")
132 self.revdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PARENT)
133 scrolled.add(self.revdep_treeview)
134 box.add(scrolled)
135 pane.add2(box)
136
137 self.show_all()
138
139 def on_package_activated(self, treeview, path, column, data_col):
140 model = treeview.get_model()
141 package = model.get_value(model.get_iter(path), data_col)
142
143 pkg_path = []
144 def finder(model, path, iter, needle):
145 package = model.get_value(iter, COL_PKG_NAME)
146 if package == needle:
147 pkg_path.append(path)
148 return True
149 else:
150 return False
151 self.pkg_model.foreach(finder, package)
152 if pkg_path:
153 self.pkg_treeview.get_selection().select_path(pkg_path[0])
154 self.pkg_treeview.scroll_to_cell(pkg_path[0])
155
156 def on_cursor_changed(self, selection):
157 (model, it) = selection.get_selected()
158 if it is None:
159 current_package = None
160 else:
161 current_package = model.get_value(it, COL_PKG_NAME)
162 self.rdep_treeview.set_current_package(current_package)
163 self.dep_treeview.set_current_package(current_package)
164 self.revdep_treeview.set_current_package(current_package)
165
166
167 def parse(self, depgraph):
168 for package in depgraph["pn"]:
169 self.pkg_model.insert(0, (package,))
170
171 for package in depgraph["depends"]:
172 for depend in depgraph["depends"][package]:
173 self.depends_model.insert (0, (TYPE_DEP, package, depend))
174
175 for package in depgraph["rdepends-pn"]:
176 for rdepend in depgraph["rdepends-pn"][package]:
177 self.depends_model.insert (0, (TYPE_RDEP, package, rdepend))
178
179
180class gtkthread(threading.Thread):
181 quit = threading.Event()
182 def __init__(self, shutdown):
183 threading.Thread.__init__(self)
184 self.setDaemon(True)
185 self.shutdown = shutdown
186
187 def run(self):
188 gobject.threads_init()
189 gtk.gdk.threads_init()
190 gtk.main()
191 gtkthread.quit.set()
192
193
194def main(server, eventHandler, params):
195 try:
196 params.updateFromServer(server)
197 cmdline = params.parseActions()
198 if not cmdline:
199 print("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
200 return 1
201 if 'msg' in cmdline and cmdline['msg']:
202 print(cmdline['msg'])
203 return 1
204 cmdline = cmdline['action']
205 if not cmdline or cmdline[0] != "generateDotGraph":
206 print("This UI requires the -g option")
207 return 1
208 ret, error = server.runCommand(["generateDepTreeEvent", cmdline[1], cmdline[2]])
209 if error:
210 print("Error running command '%s': %s" % (cmdline, error))
211 return 1
212 elif ret != True:
213 print("Error running command '%s': returned %s" % (cmdline, ret))
214 return 1
215 except xmlrpclib.Fault as x:
216 print("XMLRPC Fault getting commandline:\n %s" % x)
217 return
218
219 try:
220 gtk.init_check()
221 except RuntimeError:
222 sys.stderr.write("Please set DISPLAY variable before running this command \n")
223 return
224
225 shutdown = 0
226
227 gtkgui = gtkthread(shutdown)
228 gtkgui.start()
229
230 gtk.gdk.threads_enter()
231 dep = DepExplorer()
232 bardialog = gtk.Dialog(parent=dep,
233 flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
234 bardialog.set_default_size(400, 50)
235 pbar = HobProgressBar()
236 bardialog.vbox.pack_start(pbar)
237 bardialog.show_all()
238 bardialog.connect("delete-event", gtk.main_quit)
239 gtk.gdk.threads_leave()
240
241 progress_total = 0
242 while True:
243 try:
244 event = eventHandler.waitEvent(0.25)
245 if gtkthread.quit.isSet():
246 _, error = server.runCommand(["stateForceShutdown"])
247 if error:
248 print('Unable to cleanly stop: %s' % error)
249 break
250
251 if event is None:
252 continue
253
254 if isinstance(event, bb.event.CacheLoadStarted):
255 progress_total = event.total
256 gtk.gdk.threads_enter()
257 bardialog.set_title("Loading Cache")
258 pbar.update(0)
259 gtk.gdk.threads_leave()
260
261 if isinstance(event, bb.event.CacheLoadProgress):
262 x = event.current
263 gtk.gdk.threads_enter()
264 pbar.update(x * 1.0 / progress_total)
265 pbar.set_title('')
266 gtk.gdk.threads_leave()
267 continue
268
269 if isinstance(event, bb.event.CacheLoadCompleted):
270 bardialog.hide()
271 continue
272
273 if isinstance(event, bb.event.ParseStarted):
274 progress_total = event.total
275 if progress_total == 0:
276 continue
277 gtk.gdk.threads_enter()
278 pbar.update(0)
279 bardialog.set_title("Processing recipes")
280
281 gtk.gdk.threads_leave()
282
283 if isinstance(event, bb.event.ParseProgress):
284 x = event.current
285 gtk.gdk.threads_enter()
286 pbar.update(x * 1.0 / progress_total)
287 pbar.set_title('')
288 gtk.gdk.threads_leave()
289 continue
290
291 if isinstance(event, bb.event.ParseCompleted):
292 bardialog.hide()
293 continue
294
295 if isinstance(event, bb.event.DepTreeGenerated):
296 gtk.gdk.threads_enter()
297 dep.parse(event._depgraph)
298 gtk.gdk.threads_leave()
299
300 if isinstance(event, bb.command.CommandCompleted):
301 continue
302
303 if isinstance(event, bb.command.CommandFailed):
304 print("Command execution failed: %s" % event.error)
305 return event.exitcode
306
307 if isinstance(event, bb.command.CommandExit):
308 return event.exitcode
309
310 if isinstance(event, bb.cooker.CookerExit):
311 break
312
313 continue
314 except EnvironmentError as ioerror:
315 # ignore interrupted io
316 if ioerror.args[0] == 4:
317 pass
318 except KeyboardInterrupt:
319 if shutdown == 2:
320 print("\nThird Keyboard Interrupt, exit.\n")
321 break
322 if shutdown == 1:
323 print("\nSecond Keyboard Interrupt, stopping...\n")
324 _, error = server.runCommand(["stateForceShutdown"])
325 if error:
326 print('Unable to cleanly stop: %s' % error)
327 if shutdown == 0:
328 print("\nKeyboard Interrupt, closing down...\n")
329 _, error = server.runCommand(["stateShutdown"])
330 if error:
331 print('Unable to cleanly shutdown: %s' % error)
332 shutdown = shutdown + 1
333 pass