blob: 7fc690e2fa7460c162b347cb76b657d98976578c [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#!/usr/bin/env python
2#
3# BitBake Graphical GTK User Interface
4#
5# Copyright (C) 2012 Intel Corporation
6#
7# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
8# Authored by Shane Wang <shane.wang@intel.com>
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License version 2 as
12# published by the Free Software Foundation.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License along
20# with this program; if not, write to the Free Software Foundation, Inc.,
21# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
23import gtk
24import pango
25import gobject
26import bb.process
27from bb.ui.crumbs.progressbar import HobProgressBar
28from bb.ui.crumbs.hobwidget import hic, HobNotebook, HobAltButton, HobWarpCellRendererText, HobButton, HobInfoButton
29from bb.ui.crumbs.runningbuild import RunningBuildTreeView
30from bb.ui.crumbs.runningbuild import BuildFailureTreeView
31from bb.ui.crumbs.hobpages import HobPage
32from bb.ui.crumbs.hobcolor import HobColors
33
34class BuildConfigurationTreeView(gtk.TreeView):
35 def __init__ (self):
36 gtk.TreeView.__init__(self)
37 self.set_rules_hint(False)
38 self.set_headers_visible(False)
39 self.set_property("hover-expand", True)
40 self.get_selection().set_mode(gtk.SELECTION_SINGLE)
41
42 # The icon that indicates whether we're building or failed.
43 renderer0 = gtk.CellRendererText()
44 renderer0.set_property('font-desc', pango.FontDescription('courier bold 12'))
45 col0 = gtk.TreeViewColumn ("Name", renderer0, text=0)
46 self.append_column (col0)
47
48 # The message of configuration.
49 renderer1 = HobWarpCellRendererText(col_number=1)
50 col1 = gtk.TreeViewColumn ("Values", renderer1, text=1)
51 self.append_column (col1)
52
53 def set_vars(self, key="", var=[""]):
54 d = {}
55 if type(var) == str:
56 d = {key: [var]}
57 elif type(var) == list and len(var) > 1:
58 #create the sub item line
59 l = []
60 text = ""
61 for item in var:
62 text = " - " + item
63 l.append(text)
64 d = {key: var}
65
66 return d
67
68 def set_config_model(self, show_vars):
69 listmodel = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
70 parent = None
71 for var in show_vars:
72 for subitem in var.items():
73 name = subitem[0]
74 is_parent = True
75 for value in subitem[1]:
76 if is_parent:
77 parent = listmodel.append(parent, (name, value))
78 is_parent = False
79 else:
80 listmodel.append(parent, (None, value))
81 name = " - "
82 parent = None
83 # renew the tree model after get the configuration messages
84 self.set_model(listmodel)
85
86 def show(self, src_config_info, src_params):
87 vars = []
88 vars.append(self.set_vars("BB version:", src_params.bb_version))
89 vars.append(self.set_vars("Target arch:", src_params.target_arch))
90 vars.append(self.set_vars("Target OS:", src_params.target_os))
91 vars.append(self.set_vars("Machine:", src_config_info.curr_mach))
92 vars.append(self.set_vars("Distro:", src_config_info.curr_distro))
93 vars.append(self.set_vars("Distro version:", src_params.distro_version))
94 vars.append(self.set_vars("SDK machine:", src_config_info.curr_sdk_machine))
95 vars.append(self.set_vars("Tune features:", src_params.tune_pkgarch))
96 vars.append(self.set_vars("Layers:", src_config_info.layers))
97
98 for path in src_config_info.layers:
99 import os, os.path
100 if os.path.exists(path):
101 branch = bb.process.run('cd %s; git branch | grep "^* " | tr -d "* "' % path)[0]
102 if branch.startswith("fatal:"):
103 branch = "(unknown)"
104 if branch:
105 branch = branch.strip('\n')
106 vars.append(self.set_vars("Branch:", branch))
107 break
108
109 self.set_config_model(vars)
110
111 def reset(self):
112 self.set_model(None)
113
114#
115# BuildDetailsPage
116#
117
118class BuildDetailsPage (HobPage):
119
120 def __init__(self, builder):
121 super(BuildDetailsPage, self).__init__(builder, "Building ...")
122
123 self.num_of_issues = 0
124 self.endpath = (0,)
125 # create visual elements
126 self.create_visual_elements()
127
128 def create_visual_elements(self):
129 # create visual elements
130 self.vbox = gtk.VBox(False, 12)
131
132 self.progress_box = gtk.VBox(False, 12)
133 self.task_status = gtk.Label("\n") # to ensure layout is correct
134 self.task_status.set_alignment(0.0, 0.5)
135 self.progress_box.pack_start(self.task_status, expand=False, fill=False)
136 self.progress_hbox = gtk.HBox(False, 6)
137 self.progress_box.pack_end(self.progress_hbox, expand=True, fill=True)
138 self.progress_bar = HobProgressBar()
139 self.progress_hbox.pack_start(self.progress_bar, expand=True, fill=True)
140 self.stop_button = HobAltButton("Stop")
141 self.stop_button.connect("clicked", self.stop_button_clicked_cb)
142 self.stop_button.set_sensitive(False)
143 self.progress_hbox.pack_end(self.stop_button, expand=False, fill=False)
144
145 self.notebook = HobNotebook()
146 self.config_tv = BuildConfigurationTreeView()
147 self.scrolled_view_config = gtk.ScrolledWindow ()
148 self.scrolled_view_config.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
149 self.scrolled_view_config.add(self.config_tv)
150 self.notebook.append_page(self.scrolled_view_config, "Build configuration")
151
152 self.failure_tv = BuildFailureTreeView()
153 self.failure_model = self.builder.handler.build.model.failure_model()
154 self.failure_tv.set_model(self.failure_model)
155 self.scrolled_view_failure = gtk.ScrolledWindow ()
156 self.scrolled_view_failure.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
157 self.scrolled_view_failure.add(self.failure_tv)
158 self.notebook.append_page(self.scrolled_view_failure, "Issues")
159
160 self.build_tv = RunningBuildTreeView(readonly=True, hob=True)
161 self.build_tv.set_model(self.builder.handler.build.model)
162 self.scrolled_view_build = gtk.ScrolledWindow ()
163 self.scrolled_view_build.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
164 self.scrolled_view_build.add(self.build_tv)
165 self.notebook.append_page(self.scrolled_view_build, "Log")
166
167 self.builder.handler.build.model.connect_after("row-changed", self.scroll_to_present_row, self.scrolled_view_build.get_vadjustment(), self.build_tv)
168
169 self.button_box = gtk.HBox(False, 6)
170 self.back_button = HobAltButton('&lt;&lt; Back')
171 self.back_button.connect("clicked", self.back_button_clicked_cb)
172 self.button_box.pack_start(self.back_button, expand=False, fill=False)
173
174 def update_build_status(self, current, total, task):
175 recipe_path, recipe_task = task.split(", ")
176 recipe = os.path.basename(recipe_path).rstrip(".bb")
177 tsk_msg = "<b>Running task %s of %s:</b> %s\n<b>Recipe:</b> %s" % (current, total, recipe_task, recipe)
178 self.task_status.set_markup(tsk_msg)
179 self.stop_button.set_sensitive(True)
180
181 def reset_build_status(self):
182 self.task_status.set_markup("\n") # to ensure layout is correct
183 self.endpath = (0,)
184
185 def show_issues(self):
186 self.num_of_issues += 1
187 self.notebook.show_indicator_icon("Issues", self.num_of_issues)
188 self.notebook.queue_draw()
189
190 def reset_issues(self):
191 self.num_of_issues = 0
192 self.notebook.hide_indicator_icon("Issues")
193
194 def _remove_all_widget(self):
195 children = self.vbox.get_children() or []
196 for child in children:
197 self.vbox.remove(child)
198 children = self.box_group_area.get_children() or []
199 for child in children:
200 self.box_group_area.remove(child)
201 children = self.get_children() or []
202 for child in children:
203 self.remove(child)
204
205 def add_build_fail_top_bar(self, actions, log_file=None):
206 primary_action = "Edit %s" % actions
207
208 color = HobColors.ERROR
209 build_fail_top = gtk.EventBox()
210 #build_fail_top.set_size_request(-1, 200)
211 build_fail_top.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
212
213 build_fail_tab = gtk.Table(14, 46, True)
214 build_fail_top.add(build_fail_tab)
215
216 icon = gtk.Image()
217 icon_pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_INDI_ERROR_FILE)
218 icon.set_from_pixbuf(icon_pix_buffer)
219 build_fail_tab.attach(icon, 1, 4, 0, 6)
220
221 label = gtk.Label()
222 label.set_alignment(0.0, 0.5)
223 label.set_markup("<span size='x-large'><b>%s</b></span>" % self.title)
224 build_fail_tab.attach(label, 4, 26, 0, 6)
225
226 label = gtk.Label()
227 label.set_alignment(0.0, 0.5)
228 # Ensure variable disk_full is defined
229 if not hasattr(self.builder, 'disk_full'):
230 self.builder.disk_full = False
231
232 if self.builder.disk_full:
233 markup = "<span size='medium'>There is no disk space left, so Hob cannot finish building your image. Free up some disk space\n"
234 markup += "and restart the build. Check the \"Issues\" tab for more details</span>"
235 label.set_markup(markup)
236 else:
237 label.set_markup("<span size='medium'>Check the \"Issues\" information for more details</span>")
238 build_fail_tab.attach(label, 4, 40, 4, 9)
239
240 # create button 'Edit packages'
241 action_button = HobButton(primary_action)
242 #action_button.set_size_request(-1, 40)
243 action_button.set_tooltip_text("Edit the %s parameters" % actions)
244 action_button.connect('clicked', self.failure_primary_action_button_clicked_cb, primary_action)
245
246 if log_file:
247 open_log_button = HobAltButton("Open log")
248 open_log_button.set_relief(gtk.RELIEF_HALF)
249 open_log_button.set_tooltip_text("Open the build's log file")
250 open_log_button.connect('clicked', self.open_log_button_clicked_cb, log_file)
251
252 attach_pos = (24 if log_file else 14)
253 file_bug_button = HobAltButton('File a bug')
254 file_bug_button.set_relief(gtk.RELIEF_HALF)
255 file_bug_button.set_tooltip_text("Open the Yocto Project bug tracking website")
256 file_bug_button.connect('clicked', self.failure_activate_file_bug_link_cb)
257
258 if not self.builder.disk_full:
259 build_fail_tab.attach(action_button, 4, 13, 9, 12)
260 if log_file:
261 build_fail_tab.attach(open_log_button, 14, 23, 9, 12)
262 build_fail_tab.attach(file_bug_button, attach_pos, attach_pos + 9, 9, 12)
263
264 else:
265 restart_build = HobButton("Restart the build")
266 restart_build.set_tooltip_text("Restart the build")
267 restart_build.connect('clicked', self.restart_build_button_clicked_cb)
268
269 build_fail_tab.attach(restart_build, 4, 13, 9, 12)
270 build_fail_tab.attach(action_button, 14, 23, 9, 12)
271 if log_file:
272 build_fail_tab.attach(open_log_button, attach_pos, attach_pos + 9, 9, 12)
273
274 self.builder.disk_full = False
275 return build_fail_top
276
277 def show_fail_page(self, title):
278 self._remove_all_widget()
279 self.title = "Hob cannot build your %s" % title
280
281 self.build_fail_bar = self.add_build_fail_top_bar(title, self.builder.current_logfile)
282
283 self.pack_start(self.group_align, expand=True, fill=True)
284 self.box_group_area.pack_start(self.build_fail_bar, expand=False, fill=False)
285 self.box_group_area.pack_start(self.vbox, expand=True, fill=True)
286
287 self.vbox.pack_start(self.notebook, expand=True, fill=True)
288 self.show_all()
289 self.notebook.set_page("Issues")
290 self.back_button.hide()
291
292 def add_build_stop_top_bar(self, action, log_file=None):
293 color = HobColors.LIGHT_GRAY
294 build_stop_top = gtk.EventBox()
295 #build_stop_top.set_size_request(-1, 200)
296 build_stop_top.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
297 build_stop_top.set_flags(gtk.CAN_DEFAULT)
298 build_stop_top.grab_default()
299
300 build_stop_tab = gtk.Table(11, 46, True)
301 build_stop_top.add(build_stop_tab)
302
303 icon = gtk.Image()
304 icon_pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_INFO_HOVER_FILE)
305 icon.set_from_pixbuf(icon_pix_buffer)
306 build_stop_tab.attach(icon, 1, 4, 0, 6)
307
308 label = gtk.Label()
309 label.set_alignment(0.0, 0.5)
310 label.set_markup("<span size='x-large'><b>%s</b></span>" % self.title)
311 build_stop_tab.attach(label, 4, 26, 0, 6)
312
313 action_button = HobButton("Edit %s" % action)
314 action_button.set_size_request(-1, 40)
315 if action == "image":
316 action_button.set_tooltip_text("Edit the image parameters")
317 elif action == "recipes":
318 action_button.set_tooltip_text("Edit the included recipes")
319 elif action == "packages":
320 action_button.set_tooltip_text("Edit the included packages")
321 action_button.connect('clicked', self.stop_primary_action_button_clicked_cb, action)
322 build_stop_tab.attach(action_button, 4, 13, 6, 9)
323
324 if log_file:
325 open_log_button = HobAltButton("Open log")
326 open_log_button.set_relief(gtk.RELIEF_HALF)
327 open_log_button.set_tooltip_text("Open the build's log file")
328 open_log_button.connect('clicked', self.open_log_button_clicked_cb, log_file)
329 build_stop_tab.attach(open_log_button, 14, 23, 6, 9)
330
331 attach_pos = (24 if log_file else 14)
332 build_button = HobAltButton("Build new image")
333 #build_button.set_size_request(-1, 40)
334 build_button.set_tooltip_text("Create a new image from scratch")
335 build_button.connect('clicked', self.new_image_button_clicked_cb)
336 build_stop_tab.attach(build_button, attach_pos, attach_pos + 9, 6, 9)
337
338 return build_stop_top, action_button
339
340 def show_stop_page(self, action):
341 self._remove_all_widget()
342 self.title = "Build stopped"
343 self.build_stop_bar, action_button = self.add_build_stop_top_bar(action, self.builder.current_logfile)
344
345 self.pack_start(self.group_align, expand=True, fill=True)
346 self.box_group_area.pack_start(self.build_stop_bar, expand=False, fill=False)
347 self.box_group_area.pack_start(self.vbox, expand=True, fill=True)
348
349 self.vbox.pack_start(self.notebook, expand=True, fill=True)
350 self.show_all()
351 self.back_button.hide()
352 return action_button
353
354 def show_page(self, step):
355 self._remove_all_widget()
356 if step == self.builder.PACKAGE_GENERATING or step == self.builder.FAST_IMAGE_GENERATING:
357 self.title = "Building packages ..."
358 else:
359 self.title = "Building image ..."
360 self.build_details_top = self.add_onto_top_bar(None)
361 self.pack_start(self.build_details_top, expand=False, fill=False)
362 self.pack_start(self.group_align, expand=True, fill=True)
363
364 self.box_group_area.pack_start(self.vbox, expand=True, fill=True)
365
366 self.progress_bar.reset()
367 self.config_tv.reset()
368 self.vbox.pack_start(self.progress_box, expand=False, fill=False)
369
370 self.vbox.pack_start(self.notebook, expand=True, fill=True)
371
372 self.box_group_area.pack_end(self.button_box, expand=False, fill=False)
373 self.show_all()
374 self.notebook.set_page("Log")
375 self.back_button.hide()
376
377 self.reset_build_status()
378 self.reset_issues()
379
380 def update_progress_bar(self, title, fraction, status=None):
381 self.progress_bar.update(fraction)
382 self.progress_bar.set_title(title)
383 self.progress_bar.set_rcstyle(status)
384
385 def back_button_clicked_cb(self, button):
386 self.builder.show_configuration()
387
388 def new_image_button_clicked_cb(self, button):
389 self.builder.reset()
390
391 def show_back_button(self):
392 self.back_button.show()
393
394 def stop_button_clicked_cb(self, button):
395 self.builder.stop_build()
396
397 def hide_stop_button(self):
398 self.stop_button.set_sensitive(False)
399 self.stop_button.hide()
400
401 def scroll_to_present_row(self, model, path, iter, v_adj, treeview):
402 if treeview and v_adj:
403 if path[0] > self.endpath[0]: # check the event is a new row append or not
404 self.endpath = path
405 # check the gtk.adjustment position is at end boundary or not
406 if (v_adj.upper <= v_adj.page_size) or (v_adj.value == v_adj.upper - v_adj.page_size):
407 treeview.scroll_to_cell(path)
408
409 def show_configurations(self, configurations, params):
410 self.config_tv.show(configurations, params)
411
412 def failure_primary_action_button_clicked_cb(self, button, action):
413 if "Edit recipes" in action:
414 self.builder.show_recipes()
415 elif "Edit packages" in action:
416 self.builder.show_packages()
417 elif "Edit image" in action:
418 self.builder.show_configuration()
419
420 def restart_build_button_clicked_cb(self, button):
421 self.builder.just_bake()
422
423 def stop_primary_action_button_clicked_cb(self, button, action):
424 if "recipes" in action:
425 self.builder.show_recipes()
426 elif "packages" in action:
427 self.builder.show_packages()
428 elif "image" in action:
429 self.builder.show_configuration()
430
431 def open_log_button_clicked_cb(self, button, log_file):
432 if log_file:
433 log_file = "file:///" + log_file
434 gtk.show_uri(screen=button.get_screen(), uri=log_file, timestamp=0)
435
436 def failure_activate_file_bug_link_cb(self, button):
437 button.child.emit('activate-link', "http://bugzilla.yoctoproject.org")