summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbavery <brian.avery@intel.com>2016-02-29 10:26:02 -0800
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-03-02 22:41:23 +0000
commita9dc72f08bf5bdd898fba429729acee0e7305838 (patch)
treefa1e320398a1939df372363fbbfd79a5e58ae933
parent27468dbc3aa4ed71a20f706d4a65dfa29c4dd733 (diff)
downloadpoky-a9dc72f08bf5bdd898fba429729acee0e7305838.tar.gz
bitbake: hob: removal of hob ui and associated ui files
We've been gearing up the Toaster web UI to replace the Hob (GTK+ based) UI for some time now; Hob has basically been on life support for the past few releases. As of late last month in master, Toaster has the capability to select the packages in an image, removing the last thing that Hob could do that Toaster couldn't. To recap, the reasons why Hob is being removed include: - The code is tightly woven into BitBake, making it fragile. This means it needs significant QA and maintenance on an ongoing basis. - Some of the implementation is not ideal; we'll be able to remove some cruft from BitBake and OE-Core at the same time. - It's GTK+ 2 based, not the current GTK+ 3. - Toaster is now a much more capable UI and is being actively maintained The discussion about removing hob can be found at: http://lists.openembedded.org/pipermail/openembedded-architecture/2016-February/000082.html (Bitbake rev: be2cceea159c6ca9111eff3df87b98513eab6d72) Signed-off-by: bavery <brian.avery@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rwxr-xr-xbitbake/lib/bb/ui/crumbs/builddetailspage.py437
-rwxr-xr-xbitbake/lib/bb/ui/crumbs/builder.py1534
-rw-r--r--bitbake/lib/bb/ui/crumbs/hig/advancedsettingsdialog.py341
-rw-r--r--bitbake/lib/bb/ui/crumbs/hig/parsingwarningsdialog.py163
-rw-r--r--bitbake/lib/bb/ui/crumbs/hig/proxydetailsdialog.py90
-rw-r--r--bitbake/lib/bb/ui/crumbs/hig/retrieveimagedialog.py51
-rw-r--r--bitbake/lib/bb/ui/crumbs/hig/saveimagedialog.py159
-rw-r--r--bitbake/lib/bb/ui/crumbs/hig/simplesettingsdialog.py891
-rw-r--r--bitbake/lib/bb/ui/crumbs/hobeventhandler.py645
-rw-r--r--bitbake/lib/bb/ui/crumbs/hoblistmodel.py903
-rwxr-xr-xbitbake/lib/bb/ui/crumbs/hobpages.py128
-rw-r--r--bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py561
-rwxr-xr-xbitbake/lib/bb/ui/crumbs/imagedetailspage.py705
-rwxr-xr-xbitbake/lib/bb/ui/crumbs/packageselectionpage.py355
-rwxr-xr-xbitbake/lib/bb/ui/crumbs/recipeselectionpage.py335
-rw-r--r--bitbake/lib/bb/ui/crumbs/sanitycheckpage.py85
-rwxr-xr-xbitbake/lib/bb/ui/hob.py109
17 files changed, 0 insertions, 7492 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/builddetailspage.py b/bitbake/lib/bb/ui/crumbs/builddetailspage.py
deleted file mode 100755
index 7fc690e2fa..0000000000
--- a/bitbake/lib/bb/ui/crumbs/builddetailspage.py
+++ /dev/null
@@ -1,437 +0,0 @@
1#!/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")
diff --git a/bitbake/lib/bb/ui/crumbs/builder.py b/bitbake/lib/bb/ui/crumbs/builder.py
deleted file mode 100755
index 457cadc77a..0000000000
--- a/bitbake/lib/bb/ui/crumbs/builder.py
+++ /dev/null
@@ -1,1534 +0,0 @@
1#!/usr/bin/env python
2#
3# BitBake Graphical GTK User Interface
4#
5# Copyright (C) 2011-2012 Intel Corporation
6#
7# Authored by Joshua Lock <josh@linux.intel.com>
8# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
9# Authored by Shane Wang <shane.wang@intel.com>
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
24import glib
25import gtk, gobject
26import copy
27import os
28import subprocess
29import shlex
30import re
31import logging
32import sys
33import signal
34import time
35from bb.ui.crumbs.imageconfigurationpage import ImageConfigurationPage
36from bb.ui.crumbs.recipeselectionpage import RecipeSelectionPage
37from bb.ui.crumbs.packageselectionpage import PackageSelectionPage
38from bb.ui.crumbs.builddetailspage import BuildDetailsPage
39from bb.ui.crumbs.imagedetailspage import ImageDetailsPage
40from bb.ui.crumbs.sanitycheckpage import SanityCheckPage
41from bb.ui.crumbs.hobwidget import hwc, HobButton, HobAltButton
42from bb.ui.crumbs.persistenttooltip import PersistentTooltip
43import bb.ui.crumbs.utils
44from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
45from bb.ui.crumbs.hig.simplesettingsdialog import SimpleSettingsDialog
46from bb.ui.crumbs.hig.advancedsettingsdialog import AdvancedSettingsDialog
47from bb.ui.crumbs.hig.deployimagedialog import DeployImageDialog
48from bb.ui.crumbs.hig.layerselectiondialog import LayerSelectionDialog
49from bb.ui.crumbs.hig.imageselectiondialog import ImageSelectionDialog
50from bb.ui.crumbs.hig.parsingwarningsdialog import ParsingWarningsDialog
51from bb.ui.crumbs.hig.propertydialog import PropertyDialog
52
53hobVer = 20120808
54
55class Configuration:
56 '''Represents the data structure of configuration.'''
57
58 @classmethod
59 def parse_proxy_string(cls, proxy):
60 pattern = "^\s*((http|https|ftp|socks|cvs)://)?((\S+):(\S+)@)?([^\s:]+)(:(\d+))?/?"
61 match = re.search(pattern, proxy)
62 if match:
63 return match.group(2), match.group(4), match.group(5), match.group(6), match.group(8)
64 else:
65 return None, None, None, "", ""
66
67 @classmethod
68 def make_host_string(cls, prot, user, passwd, host, default_prot=""):
69 if host == None or host == "":
70 return ""
71
72 passwd = passwd or ""
73
74 if user != None and user != "":
75 if prot == None or prot == "":
76 prot = default_prot
77 return prot + "://" + user + ":" + passwd + "@" + host
78 else:
79 if prot == None or prot == "":
80 return host
81 else:
82 return prot + "://" + host
83
84 @classmethod
85 def make_port_string(cls, port):
86 port = port or ""
87 return port
88
89 @classmethod
90 def make_proxy_string(cls, prot, user, passwd, host, port, default_prot=""):
91 if host == None or host == "":# or port == None or port == "":
92 return ""
93
94 return Configuration.make_host_string(prot, user, passwd, host, default_prot) + (":" + Configuration.make_port_string(port) if port else "")
95
96 def __init__(self):
97 self.curr_mach = ""
98 self.selected_image = None
99 # settings
100 self.curr_distro = ""
101 self.dldir = self.sstatedir = self.sstatemirror = ""
102 self.pmake = self.bbthread = 0
103 self.curr_package_format = ""
104 self.image_rootfs_size = self.image_extra_size = 0
105 self.image_overhead_factor = 1
106 self.incompat_license = ""
107 self.curr_sdk_machine = ""
108 self.conf_version = self.lconf_version = ""
109 self.extra_setting = {}
110 self.toolchain_build = False
111 self.image_fstypes = ""
112 self.image_size = None
113 self.image_packages = []
114 # bblayers.conf
115 self.layers = []
116 # image/recipes/packages
117 self.clear_selection()
118
119 self.user_selected_packages = []
120
121 self.default_task = "build"
122
123 # proxy settings
124 self.enable_proxy = None
125 self.same_proxy = False
126 self.proxies = {
127 "http" : [None, None, None, "", ""], # protocol : [prot, user, passwd, host, port]
128 "https" : [None, None, None, "", ""],
129 "ftp" : [None, None, None, "", ""],
130 "socks" : [None, None, None, "", ""],
131 "cvs" : [None, None, None, "", ""],
132 }
133
134 def clear_selection(self):
135 self.selected_recipes = []
136 self.selected_packages = []
137 self.initial_selected_image = None
138 self.initial_selected_packages = []
139 self.initial_user_selected_packages = []
140
141 def split_proxy(self, protocol, proxy):
142 entry = []
143 prot, user, passwd, host, port = Configuration.parse_proxy_string(proxy)
144 entry.append(prot)
145 entry.append(user)
146 entry.append(passwd)
147 entry.append(host)
148 entry.append(port)
149 self.proxies[protocol] = entry
150
151 def combine_proxy(self, protocol):
152 entry = self.proxies[protocol]
153 return Configuration.make_proxy_string(entry[0], entry[1], entry[2], entry[3], entry[4], protocol)
154
155 def combine_host_only(self, protocol):
156 entry = self.proxies[protocol]
157 return Configuration.make_host_string(entry[0], entry[1], entry[2], entry[3], protocol)
158
159 def combine_port_only(self, protocol):
160 entry = self.proxies[protocol]
161 return Configuration.make_port_string(entry[4])
162
163 def update(self, params):
164 # settings
165 self.curr_distro = params["distro"]
166 self.dldir = params["dldir"]
167 self.sstatedir = params["sstatedir"]
168 self.sstatemirror = params["sstatemirror"]
169 self.pmake = int(params["pmake"].split()[1])
170 self.bbthread = params["bbthread"]
171 self.curr_package_format = " ".join(params["pclass"].split("package_")).strip()
172 self.image_rootfs_size = params["image_rootfs_size"]
173 self.image_extra_size = params["image_extra_size"]
174 self.image_overhead_factor = params['image_overhead_factor']
175 self.incompat_license = params["incompat_license"]
176 self.curr_sdk_machine = params["sdk_machine"]
177 self.conf_version = params["conf_version"]
178 self.lconf_version = params["lconf_version"]
179 self.image_fstypes = params["image_fstypes"]
180 # self.extra_setting/self.toolchain_build
181 # bblayers.conf
182 self.layers = params["layer"].split()
183 self.layers_non_removable = params["layers_non_removable"].split()
184 self.default_task = params["default_task"]
185
186 # proxy settings
187 self.enable_proxy = params["http_proxy"] != "" or params["https_proxy"] != "" \
188 or params["ftp_proxy"] != "" or params["socks_proxy"] != "" \
189 or params["cvs_proxy_host"] != "" or params["cvs_proxy_port"] != ""
190 self.split_proxy("http", params["http_proxy"])
191 self.split_proxy("https", params["https_proxy"])
192 self.split_proxy("ftp", params["ftp_proxy"])
193 self.split_proxy("socks", params["socks_proxy"])
194 self.split_proxy("cvs", params["cvs_proxy_host"] + ":" + params["cvs_proxy_port"])
195
196 def save(self, handler, defaults=False):
197 # bblayers.conf
198 handler.set_var_in_file("BBLAYERS", self.layers, "bblayers.conf")
199 # local.conf
200 if not defaults:
201 handler.early_assign_var_in_file("MACHINE", self.curr_mach, "local.conf")
202 handler.set_var_in_file("DISTRO", self.curr_distro, "local.conf")
203 handler.set_var_in_file("DL_DIR", self.dldir, "local.conf")
204 handler.set_var_in_file("SSTATE_DIR", self.sstatedir, "local.conf")
205 sstate_mirror_list = self.sstatemirror.split("\\n ")
206 sstate_mirror_list_modified = []
207 for mirror in sstate_mirror_list:
208 if mirror != "":
209 mirror = mirror + "\\n"
210 sstate_mirror_list_modified.append(mirror)
211 handler.set_var_in_file("SSTATE_MIRRORS", sstate_mirror_list_modified, "local.conf")
212 handler.set_var_in_file("PARALLEL_MAKE", "-j %s" % self.pmake, "local.conf")
213 handler.set_var_in_file("BB_NUMBER_THREADS", self.bbthread, "local.conf")
214 handler.set_var_in_file("PACKAGE_CLASSES", " ".join(["package_" + i for i in self.curr_package_format.split()]), "local.conf")
215 handler.set_var_in_file("IMAGE_ROOTFS_SIZE", self.image_rootfs_size, "local.conf")
216 handler.set_var_in_file("IMAGE_EXTRA_SPACE", self.image_extra_size, "local.conf")
217 handler.set_var_in_file("INCOMPATIBLE_LICENSE", self.incompat_license, "local.conf")
218 handler.set_var_in_file("SDKMACHINE", self.curr_sdk_machine, "local.conf")
219 handler.set_var_in_file("CONF_VERSION", self.conf_version, "local.conf")
220 handler.set_var_in_file("LCONF_VERSION", self.lconf_version, "bblayers.conf")
221 handler.set_extra_config(self.extra_setting)
222 handler.set_var_in_file("TOOLCHAIN_BUILD", self.toolchain_build, "local.conf")
223 handler.set_var_in_file("IMAGE_FSTYPES", self.image_fstypes, "local.conf")
224 if not defaults:
225 # image/recipes/packages
226 handler.set_var_in_file("__SELECTED_IMAGE__", self.selected_image, "local.conf")
227 handler.set_var_in_file("DEPENDS", self.selected_recipes, "local.conf")
228 handler.set_var_in_file("IMAGE_INSTALL", self.user_selected_packages, "local.conf")
229 # proxy
230 if self.enable_proxy == True:
231 handler.set_var_in_file("http_proxy", self.combine_proxy("http"), "local.conf")
232 handler.set_var_in_file("https_proxy", self.combine_proxy("https"), "local.conf")
233 handler.set_var_in_file("ftp_proxy", self.combine_proxy("ftp"), "local.conf")
234 handler.set_var_in_file("all_proxy", self.combine_proxy("socks"), "local.conf")
235 handler.set_var_in_file("CVS_PROXY_HOST", self.combine_host_only("cvs"), "local.conf")
236 handler.set_var_in_file("CVS_PROXY_PORT", self.combine_port_only("cvs"), "local.conf")
237 else:
238 handler.set_var_in_file("http_proxy", "", "local.conf")
239 handler.set_var_in_file("https_proxy", "", "local.conf")
240 handler.set_var_in_file("ftp_proxy", "", "local.conf")
241 handler.set_var_in_file("all_proxy", "", "local.conf")
242 handler.set_var_in_file("CVS_PROXY_HOST", "", "local.conf")
243 handler.set_var_in_file("CVS_PROXY_PORT", "", "local.conf")
244
245 def __str__(self):
246 s = "VERSION: '%s', BBLAYERS: '%s', MACHINE: '%s', DISTRO: '%s', DL_DIR: '%s'," % \
247 (hobVer, " ".join(self.layers), self.curr_mach, self.curr_distro, self.dldir )
248 s += "SSTATE_DIR: '%s', SSTATE_MIRROR: '%s', PARALLEL_MAKE: '-j %s', BB_NUMBER_THREADS: '%s', PACKAGE_CLASSES: '%s', " % \
249 (self.sstatedir, self.sstatemirror, self.pmake, self.bbthread, " ".join(["package_" + i for i in self.curr_package_format.split()]))
250 s += "IMAGE_ROOTFS_SIZE: '%s', IMAGE_EXTRA_SPACE: '%s', INCOMPATIBLE_LICENSE: '%s', SDKMACHINE: '%s', CONF_VERSION: '%s', " % \
251 (self.image_rootfs_size, self.image_extra_size, self.incompat_license, self.curr_sdk_machine, self.conf_version)
252 s += "LCONF_VERSION: '%s', EXTRA_SETTING: '%s', TOOLCHAIN_BUILD: '%s', IMAGE_FSTYPES: '%s', __SELECTED_IMAGE__: '%s', " % \
253 (self.lconf_version, self.extra_setting, self.toolchain_build, self.image_fstypes, self.selected_image)
254 s += "DEPENDS: '%s', IMAGE_INSTALL: '%s', enable_proxy: '%s', use_same_proxy: '%s', http_proxy: '%s', " % \
255 (self.selected_recipes, self.user_selected_packages, self.enable_proxy, self.same_proxy, self.combine_proxy("http"))
256 s += "https_proxy: '%s', ftp_proxy: '%s', all_proxy: '%s', CVS_PROXY_HOST: '%s', CVS_PROXY_PORT: '%s'" % \
257 (self.combine_proxy("https"), self.combine_proxy("ftp"), self.combine_proxy("socks"),
258 self.combine_host_only("cvs"), self.combine_port_only("cvs"))
259 return s
260
261class Parameters:
262 '''Represents other variables like available machines, etc.'''
263
264 def __init__(self):
265 # Variables
266 self.max_threads = 65535
267 self.core_base = ""
268 self.image_addr = ""
269 self.image_types = []
270 self.runnable_image_types = []
271 self.runnable_machine_patterns = []
272 self.deployable_image_types = []
273 self.tmpdir = ""
274
275 self.all_machines = []
276 self.all_package_formats = []
277 self.all_distros = []
278 self.all_sdk_machines = []
279 self.all_layers = []
280 self.image_names = []
281 self.image_white_pattern = ""
282 self.image_black_pattern = ""
283
284 # for build log to show
285 self.bb_version = ""
286 self.target_arch = ""
287 self.target_os = ""
288 self.distro_version = ""
289 self.tune_pkgarch = ""
290
291 def update(self, params):
292 self.max_threads = params["max_threads"]
293 self.core_base = params["core_base"]
294 self.image_addr = params["image_addr"]
295 self.image_types = params["image_types"].split()
296 self.runnable_image_types = params["runnable_image_types"].split()
297 self.runnable_machine_patterns = params["runnable_machine_patterns"].split()
298 self.deployable_image_types = params["deployable_image_types"].split()
299 self.tmpdir = params["tmpdir"]
300 self.image_white_pattern = params["image_white_pattern"]
301 self.image_black_pattern = params["image_black_pattern"]
302 self.kernel_image_type = params["kernel_image_type"]
303 # for build log to show
304 self.bb_version = params["bb_version"]
305 self.target_arch = params["target_arch"]
306 self.target_os = params["target_os"]
307 self.distro_version = params["distro_version"]
308 self.tune_pkgarch = params["tune_pkgarch"]
309
310def hob_conf_filter(fn, data):
311 if fn.endswith("/local.conf"):
312 distro = data.getVar("DISTRO_HOB", False)
313 if distro:
314 if distro != "defaultsetup":
315 data.setVar("DISTRO", distro)
316 else:
317 data.delVar("DISTRO")
318
319 keys = ["MACHINE_HOB", "SDKMACHINE_HOB", "PACKAGE_CLASSES_HOB", \
320 "BB_NUMBER_THREADS_HOB", "PARALLEL_MAKE_HOB", "DL_DIR_HOB", \
321 "SSTATE_DIR_HOB", "SSTATE_MIRRORS_HOB", "INCOMPATIBLE_LICENSE_HOB"]
322 for key in keys:
323 var_hob = data.getVar(key, False)
324 if var_hob:
325 data.setVar(key.split("_HOB")[0], var_hob)
326 return
327
328 if fn.endswith("/bblayers.conf"):
329 layers = data.getVar("BBLAYERS_HOB", False)
330 if layers:
331 data.setVar("BBLAYERS", layers)
332 return
333
334class Builder(gtk.Window):
335
336 (INITIAL_CHECKS,
337 MACHINE_SELECTION,
338 RCPPKGINFO_POPULATING,
339 RCPPKGINFO_POPULATED,
340 BASEIMG_SELECTED,
341 RECIPE_SELECTION,
342 PACKAGE_GENERATING,
343 PACKAGE_GENERATED,
344 PACKAGE_SELECTION,
345 FAST_IMAGE_GENERATING,
346 IMAGE_GENERATING,
347 IMAGE_GENERATED,
348 MY_IMAGE_OPENED,
349 BACK,
350 END_NOOP) = range(15)
351
352 (SANITY_CHECK,
353 IMAGE_CONFIGURATION,
354 RECIPE_DETAILS,
355 BUILD_DETAILS,
356 PACKAGE_DETAILS,
357 IMAGE_DETAILS,
358 END_TAB) = range(7)
359
360 __step2page__ = {
361 INITIAL_CHECKS : SANITY_CHECK,
362 MACHINE_SELECTION : IMAGE_CONFIGURATION,
363 RCPPKGINFO_POPULATING : IMAGE_CONFIGURATION,
364 RCPPKGINFO_POPULATED : IMAGE_CONFIGURATION,
365 BASEIMG_SELECTED : IMAGE_CONFIGURATION,
366 RECIPE_SELECTION : RECIPE_DETAILS,
367 PACKAGE_GENERATING : BUILD_DETAILS,
368 PACKAGE_GENERATED : PACKAGE_DETAILS,
369 PACKAGE_SELECTION : PACKAGE_DETAILS,
370 FAST_IMAGE_GENERATING : BUILD_DETAILS,
371 IMAGE_GENERATING : BUILD_DETAILS,
372 IMAGE_GENERATED : IMAGE_DETAILS,
373 MY_IMAGE_OPENED : IMAGE_DETAILS,
374 END_NOOP : None,
375 }
376
377 SANITY_CHECK_MIN_DISPLAY_TIME = 5
378
379 def __init__(self, hobHandler, recipe_model, package_model):
380 super(Builder, self).__init__()
381
382 self.hob_image = "hob-image"
383
384 # handler
385 self.handler = hobHandler
386
387 # logger
388 self.logger = logging.getLogger("BitBake")
389 self.consolelog = None
390 self.current_logfile = None
391
392 # configuration and parameters
393 self.configuration = Configuration()
394 self.parameters = Parameters()
395
396 # build step
397 self.current_step = None
398 self.previous_step = None
399
400 self.stopping = False
401
402 # recipe model and package model
403 self.recipe_model = recipe_model
404 self.package_model = package_model
405
406 # Indicate whether user has customized the image
407 self.customized = False
408
409 # Indicate whether the UI is working
410 self.sensitive = True
411
412 # Indicate whether the sanity check ran
413 self.sanity_checked = False
414
415 # save parsing warnings
416 self.parsing_warnings = []
417
418 # create visual elements
419 self.create_visual_elements()
420
421 # connect the signals to functions
422 self.connect("delete-event", self.destroy_window_cb)
423 self.recipe_model.connect ("recipe-selection-changed", self.recipelist_changed_cb)
424 self.package_model.connect("package-selection-changed", self.packagelist_changed_cb)
425 self.handler.connect("config-updated", self.handler_config_updated_cb)
426 self.handler.connect("package-formats-updated", self.handler_package_formats_updated_cb)
427 self.handler.connect("parsing-started", self.handler_parsing_started_cb)
428 self.handler.connect("parsing", self.handler_parsing_cb)
429 self.handler.connect("parsing-completed", self.handler_parsing_completed_cb)
430 self.handler.build.connect("build-started", self.handler_build_started_cb)
431 self.handler.build.connect("build-succeeded", self.handler_build_succeeded_cb)
432 self.handler.build.connect("build-failed", self.handler_build_failed_cb)
433 self.handler.build.connect("build-aborted", self.handler_build_aborted_cb)
434 self.handler.build.connect("task-started", self.handler_task_started_cb)
435 self.handler.build.connect("disk-full", self.handler_disk_full_cb)
436 self.handler.build.connect("log-error", self.handler_build_failure_cb)
437 self.handler.build.connect("log-warning", self.handler_build_failure_cb)
438 self.handler.build.connect("log", self.handler_build_log_cb)
439 self.handler.build.connect("no-provider", self.handler_no_provider_cb)
440 self.handler.connect("generating-data", self.handler_generating_data_cb)
441 self.handler.connect("data-generated", self.handler_data_generated_cb)
442 self.handler.connect("command-succeeded", self.handler_command_succeeded_cb)
443 self.handler.connect("command-failed", self.handler_command_failed_cb)
444 self.handler.connect("parsing-warning", self.handler_parsing_warning_cb)
445 self.handler.connect("sanity-failed", self.handler_sanity_failed_cb)
446 self.handler.connect("recipe-populated", self.handler_recipe_populated_cb)
447 self.handler.connect("package-populated", self.handler_package_populated_cb)
448
449 self.handler.append_to_bbfiles("${TOPDIR}/recipes/images/custom/*.bb")
450 self.handler.append_to_bbfiles("${TOPDIR}/recipes/images/*.bb")
451 self.initiate_new_build_async()
452
453 signal.signal(signal.SIGINT, self.event_handle_SIGINT)
454
455 def create_visual_elements(self):
456 self.set_title("Hob")
457 self.set_icon_name("applications-development")
458 self.set_resizable(True)
459
460 try:
461 window_width = self.get_screen().get_width()
462 window_height = self.get_screen().get_height()
463 except AttributeError:
464 print "Please set DISPLAY variable before running Hob."
465 sys.exit(1)
466
467 if window_width >= hwc.MAIN_WIN_WIDTH:
468 window_width = hwc.MAIN_WIN_WIDTH
469 window_height = hwc.MAIN_WIN_HEIGHT
470 self.set_size_request(window_width, window_height)
471
472 self.vbox = gtk.VBox(False, 0)
473 self.vbox.set_border_width(0)
474 self.add(self.vbox)
475
476 # create pages
477 self.image_configuration_page = ImageConfigurationPage(self)
478 self.recipe_details_page = RecipeSelectionPage(self)
479 self.build_details_page = BuildDetailsPage(self)
480 self.package_details_page = PackageSelectionPage(self)
481 self.image_details_page = ImageDetailsPage(self)
482 self.sanity_check_page = SanityCheckPage(self)
483 self.display_sanity_check = False
484 self.sanity_check_post_func = False
485 self.had_network_error = False
486
487 self.nb = gtk.Notebook()
488 self.nb.set_show_tabs(False)
489 self.nb.insert_page(self.sanity_check_page, None, self.SANITY_CHECK)
490 self.nb.insert_page(self.image_configuration_page, None, self.IMAGE_CONFIGURATION)
491 self.nb.insert_page(self.recipe_details_page, None, self.RECIPE_DETAILS)
492 self.nb.insert_page(self.build_details_page, None, self.BUILD_DETAILS)
493 self.nb.insert_page(self.package_details_page, None, self.PACKAGE_DETAILS)
494 self.nb.insert_page(self.image_details_page, None, self.IMAGE_DETAILS)
495 self.vbox.pack_start(self.nb, expand=True, fill=True)
496
497 self.show_all()
498 self.nb.set_current_page(0)
499
500 def sanity_check_timeout(self):
501 # The minimum time for showing the 'sanity check' page has passe
502 # If someone set the 'sanity_check_post_step' meanwhile, execute it now
503 self.display_sanity_check = False
504 if self.sanity_check_post_func:
505 temp = self.sanity_check_post_func
506 self.sanity_check_post_func = None
507 temp()
508 return False
509
510 def show_sanity_check_page(self):
511 # This window must stay on screen for at least 5 seconds, according to the design document
512 self.nb.set_current_page(self.SANITY_CHECK)
513 self.sanity_check_post_step = None
514 self.display_sanity_check = True
515 self.sanity_check_page.start()
516 gobject.timeout_add(self.SANITY_CHECK_MIN_DISPLAY_TIME * 1000, self.sanity_check_timeout)
517
518 def execute_after_sanity_check(self, func):
519 if not self.display_sanity_check:
520 func()
521 else:
522 self.sanity_check_post_func = func
523
524 def generate_configuration(self):
525 if not self.sanity_checked:
526 self.show_sanity_check_page()
527 self.handler.generate_configuration()
528
529 def initiate_new_build_async(self):
530 self.configuration.selected_image = None
531 self.switch_page(self.MACHINE_SELECTION)
532 self.handler.init_cooker()
533 self.handler.set_extra_inherit("image_types")
534 self.generate_configuration()
535
536 def update_config_async(self):
537 self.set_user_config()
538 self.generate_configuration()
539 self.switch_page(self.MACHINE_SELECTION)
540
541 def sanity_check(self):
542 self.handler.trigger_sanity_check()
543
544 def populate_recipe_package_info_async(self):
545 self.switch_page(self.RCPPKGINFO_POPULATING)
546 # Parse recipes
547 self.set_user_config()
548 self.handler.generate_recipes()
549
550 def generate_packages_async(self, log = False):
551 self.switch_page(self.PACKAGE_GENERATING)
552 if log:
553 self.current_logfile = self.handler.get_logfile()
554 self.do_log(self.current_logfile)
555 # Build packages
556 _, all_recipes = self.recipe_model.get_selected_recipes()
557 self.set_user_config()
558 self.handler.reset_build()
559 self.handler.generate_packages(all_recipes, self.configuration.default_task)
560
561 def restore_initial_selected_packages(self):
562 self.package_model.set_selected_packages(self.configuration.initial_user_selected_packages, True)
563 self.package_model.set_selected_packages(self.configuration.initial_selected_packages)
564 for package in self.configuration.selected_packages:
565 if package not in self.configuration.initial_selected_packages:
566 self.package_model.exclude_item(self.package_model.find_path_for_item(package))
567
568 def fast_generate_image_async(self, log = False):
569 self.switch_page(self.FAST_IMAGE_GENERATING)
570 if log:
571 self.current_logfile = self.handler.get_logfile()
572 self.do_log(self.current_logfile)
573 # Build packages
574 _, all_recipes = self.recipe_model.get_selected_recipes()
575 self.set_user_config()
576 self.handler.reset_build()
577 self.handler.generate_packages(all_recipes, self.configuration.default_task)
578
579 def generate_image_async(self, cont = False):
580 self.switch_page(self.IMAGE_GENERATING)
581 self.handler.reset_build()
582 if not cont:
583 self.current_logfile = self.handler.get_logfile()
584 self.do_log(self.current_logfile)
585 # Build image
586 self.set_user_config()
587 toolchain_packages = []
588 base_image = None
589 if self.configuration.toolchain_build:
590 toolchain_packages = self.package_model.get_selected_packages_toolchain()
591 if self.configuration.selected_image == self.recipe_model.__custom_image__:
592 packages = self.package_model.get_selected_packages()
593 image = self.hob_image
594 base_image = self.configuration.initial_selected_image
595 else:
596 packages = []
597 image = self.configuration.selected_image
598 self.handler.generate_image(image,
599 base_image,
600 packages,
601 toolchain_packages,
602 self.configuration.default_task)
603
604 def generate_new_image(self, image, description):
605 base_image = self.configuration.initial_selected_image
606 if base_image == self.recipe_model.__custom_image__:
607 base_image = None
608 packages = self.package_model.get_selected_packages()
609 self.handler.generate_new_image(image, base_image, packages, description)
610
611 def ensure_dir(self, directory):
612 self.handler.ensure_dir(directory)
613
614 def get_parameters_sync(self):
615 return self.handler.get_parameters()
616
617 def request_package_info_async(self):
618 self.handler.request_package_info()
619
620 def cancel_build_sync(self, force=False):
621 self.handler.cancel_build(force)
622
623 def cancel_parse_sync(self):
624 self.handler.cancel_parse()
625
626 def switch_page(self, next_step):
627 # Main Workflow (Business Logic)
628 self.nb.set_current_page(self.__step2page__[next_step])
629
630 if next_step == self.MACHINE_SELECTION: # init step
631 self.image_configuration_page.show_machine()
632
633 elif next_step == self.RCPPKGINFO_POPULATING:
634 # MACHINE CHANGED action or SETTINGS CHANGED
635 # show the progress bar
636 self.image_configuration_page.show_info_populating()
637
638 elif next_step == self.RCPPKGINFO_POPULATED:
639 self.image_configuration_page.show_info_populated()
640
641 elif next_step == self.BASEIMG_SELECTED:
642 self.image_configuration_page.show_baseimg_selected()
643
644 elif next_step == self.RECIPE_SELECTION:
645 if self.recipe_model.get_selected_image() == self.recipe_model.__custom_image__:
646 self.recipe_details_page.set_recipe_curr_tab(self.recipe_details_page.ALL)
647 else:
648 self.recipe_details_page.set_recipe_curr_tab(self.recipe_details_page.INCLUDED)
649
650 elif next_step == self.PACKAGE_SELECTION:
651 self.configuration.initial_selected_packages = self.configuration.selected_packages
652 self.configuration.initial_user_selected_packages = self.configuration.user_selected_packages
653 self.package_details_page.set_title("Edit packages")
654 if self.recipe_model.get_selected_image() == self.recipe_model.__custom_image__:
655 self.package_details_page.set_packages_curr_tab(self.package_details_page.ALL)
656 else:
657 self.package_details_page.set_packages_curr_tab(self.package_details_page.INCLUDED)
658 self.package_details_page.show_page(self.current_logfile)
659
660
661 elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING:
662 # both PACKAGE_GENERATING and FAST_IMAGE_GENERATING share the same page
663 self.build_details_page.show_page(next_step)
664
665 elif next_step == self.PACKAGE_GENERATED:
666 self.package_details_page.set_title("Step 2 of 2: Edit packages")
667 if self.recipe_model.get_selected_image() == self.recipe_model.__custom_image__:
668 self.package_details_page.set_packages_curr_tab(self.package_details_page.ALL)
669 else:
670 self.package_details_page.set_packages_curr_tab(self.package_details_page.INCLUDED)
671 self.package_details_page.show_page(self.current_logfile)
672
673 elif next_step == self.IMAGE_GENERATING:
674 # after packages are generated, selected_packages need to
675 # be updated in package_model per selected_image in recipe_model
676 self.build_details_page.show_page(next_step)
677
678 elif next_step == self.IMAGE_GENERATED:
679 self.image_details_page.show_page(next_step)
680
681 elif next_step == self.MY_IMAGE_OPENED:
682 self.image_details_page.show_page(next_step)
683
684 self.previous_step = self.current_step
685 self.current_step = next_step
686
687 def set_user_config_proxies(self):
688 if self.configuration.enable_proxy == True:
689 self.handler.set_http_proxy(self.configuration.combine_proxy("http"))
690 self.handler.set_https_proxy(self.configuration.combine_proxy("https"))
691 self.handler.set_ftp_proxy(self.configuration.combine_proxy("ftp"))
692 self.handler.set_socks_proxy(self.configuration.combine_proxy("socks"))
693 self.handler.set_cvs_proxy(self.configuration.combine_host_only("cvs"), self.configuration.combine_port_only("cvs"))
694 elif self.configuration.enable_proxy == False:
695 self.handler.set_http_proxy("")
696 self.handler.set_https_proxy("")
697 self.handler.set_ftp_proxy("")
698 self.handler.set_socks_proxy("")
699 self.handler.set_cvs_proxy("", "")
700
701 def set_user_config_extra(self):
702 self.handler.set_rootfs_size(self.configuration.image_rootfs_size)
703 self.handler.set_extra_size(self.configuration.image_extra_size)
704 self.handler.set_incompatible_license(self.configuration.incompat_license)
705 self.handler.set_sdk_machine(self.configuration.curr_sdk_machine)
706 self.handler.set_image_fstypes(self.configuration.image_fstypes)
707 self.handler.set_extra_config(self.configuration.extra_setting)
708 self.handler.set_extra_inherit("packageinfo image_types")
709 self.set_user_config_proxies()
710
711 def set_user_config(self):
712 # set bb layers
713 self.handler.set_bblayers(self.configuration.layers)
714 # set local configuration
715 self.handler.set_machine(self.configuration.curr_mach)
716 self.handler.set_package_format(self.configuration.curr_package_format)
717 self.handler.set_distro(self.configuration.curr_distro)
718 self.handler.set_dl_dir(self.configuration.dldir)
719 self.handler.set_sstate_dir(self.configuration.sstatedir)
720 self.handler.set_sstate_mirrors(self.configuration.sstatemirror)
721 self.handler.set_pmake(self.configuration.pmake)
722 self.handler.set_bbthreads(self.configuration.bbthread)
723 self.set_user_config_extra()
724
725 def update_recipe_model(self, selected_image, selected_recipes):
726 self.recipe_model.set_selected_image(selected_image)
727 self.recipe_model.set_selected_recipes(selected_recipes)
728
729 def update_package_model(self, selected_packages, user_selected_packages=None):
730 if user_selected_packages:
731 left = self.package_model.set_selected_packages(user_selected_packages, True)
732 self.configuration.user_selected_packages += left
733 left = self.package_model.set_selected_packages(selected_packages)
734 self.configuration.selected_packages += left
735
736 def update_configuration_parameters(self, params):
737 if params:
738 self.configuration.update(params)
739 self.parameters.update(params)
740
741 def set_base_image(self):
742 self.configuration.initial_selected_image = self.configuration.selected_image
743 if self.configuration.selected_image != self.recipe_model.__custom_image__:
744 self.hob_image = self.configuration.selected_image + "-edited"
745
746 def reset(self):
747 self.configuration.curr_mach = ""
748 self.configuration.clear_selection()
749 self.image_configuration_page.switch_machine_combo()
750 self.switch_page(self.MACHINE_SELECTION)
751
752 # Callback Functions
753 def handler_config_updated_cb(self, handler, which, values):
754 if which == "distro":
755 self.parameters.all_distros = values
756 elif which == "machine":
757 self.parameters.all_machines = values
758 self.image_configuration_page.update_machine_combo()
759 elif which == "machine-sdk":
760 self.parameters.all_sdk_machines = values
761
762 def handler_package_formats_updated_cb(self, handler, formats):
763 self.parameters.all_package_formats = formats
764
765 def switch_to_image_configuration_helper(self):
766 self.sanity_check_page.stop()
767 self.switch_page(self.IMAGE_CONFIGURATION)
768 self.image_configuration_page.switch_machine_combo()
769
770 def show_network_error_dialog_helper(self):
771 self.sanity_check_page.stop()
772 self.show_network_error_dialog()
773
774 def handler_command_succeeded_cb(self, handler, initcmd):
775 if initcmd == self.handler.GENERATE_CONFIGURATION:
776 if not self.configuration.curr_mach:
777 self.configuration.curr_mach = self.handler.runCommand(["getVariable", "HOB_MACHINE"]) or ""
778 self.update_configuration_parameters(self.get_parameters_sync())
779 if not self.sanity_checked:
780 self.sanity_check()
781 self.sanity_checked = True
782 elif initcmd == self.handler.SANITY_CHECK:
783 if self.had_network_error:
784 self.had_network_error = False
785 self.execute_after_sanity_check(self.show_network_error_dialog_helper)
786 else:
787 # Switch to the 'image configuration' page now, but we might need
788 # to wait for the minimum display time of the sanity check page
789 self.execute_after_sanity_check(self.switch_to_image_configuration_helper)
790 elif initcmd in [self.handler.GENERATE_RECIPES,
791 self.handler.GENERATE_PACKAGES,
792 self.handler.GENERATE_IMAGE]:
793 self.update_configuration_parameters(self.get_parameters_sync())
794 self.request_package_info_async()
795 elif initcmd == self.handler.POPULATE_PACKAGEINFO:
796 if self.current_step == self.RCPPKGINFO_POPULATING:
797 self.switch_page(self.RCPPKGINFO_POPULATED)
798 self.rcppkglist_populated()
799 return
800
801 self.rcppkglist_populated()
802 if self.current_step == self.FAST_IMAGE_GENERATING:
803 self.generate_image_async(True)
804
805 def show_error_dialog(self, msg):
806 lbl = "<b>Hob found an error</b>"
807 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_ERROR, msg)
808 button = dialog.add_button("Close", gtk.RESPONSE_OK)
809 HobButton.style_button(button)
810 response = dialog.run()
811 dialog.destroy()
812
813 def show_warning_dialog(self):
814 dialog = ParsingWarningsDialog(title = "View warnings",
815 warnings = self.parsing_warnings,
816 parent = None,
817 flags = gtk.DIALOG_DESTROY_WITH_PARENT
818 | gtk.DIALOG_NO_SEPARATOR)
819 response = dialog.run()
820 dialog.destroy()
821
822 def show_network_error_dialog(self):
823 lbl = "<b>Hob cannot connect to the network</b>"
824 msg = msg + "Please check your network connection. If you are using a proxy server, please make sure it is configured correctly."
825 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_ERROR, msg)
826 button = dialog.add_button("Close", gtk.RESPONSE_OK)
827 HobButton.style_button(button)
828 button = dialog.add_button("Proxy settings", gtk.RESPONSE_CANCEL)
829 HobButton.style_button(button)
830 res = dialog.run()
831 dialog.destroy()
832 if res == gtk.RESPONSE_CANCEL:
833 res, settings_changed = self.show_simple_settings_dialog(SimpleSettingsDialog.PROXIES_PAGE_ID)
834 if not res:
835 return
836 if settings_changed:
837 self.reparse_post_adv_settings()
838
839 def handler_command_failed_cb(self, handler, msg):
840 if msg:
841 self.show_error_dialog(msg)
842 self.reset()
843
844 def handler_parsing_warning_cb(self, handler, warn_msg):
845 self.parsing_warnings.append(warn_msg)
846
847 def handler_sanity_failed_cb(self, handler, msg, network_error):
848 self.reset()
849 if network_error:
850 # Mark this in an internal field. The "network error" dialog will be
851 # shown later, when a SanityCheckPassed event will be handled
852 # (as sent by sanity.bbclass)
853 self.had_network_error = True
854 else:
855 msg = msg.replace("your local.conf", "Settings")
856 self.show_error_dialog(msg)
857 self.reset()
858
859 def window_sensitive(self, sensitive):
860 self.image_configuration_page.machine_combo.set_sensitive(sensitive)
861 self.image_configuration_page.machine_combo.child.set_sensitive(sensitive)
862 self.image_configuration_page.image_combo.set_sensitive(sensitive)
863 self.image_configuration_page.image_combo.child.set_sensitive(sensitive)
864 self.image_configuration_page.layer_button.set_sensitive(sensitive)
865 self.image_configuration_page.layer_info_icon.set_sensitive(sensitive)
866 self.image_configuration_page.toolbar.set_sensitive(sensitive)
867 self.image_configuration_page.view_adv_configuration_button.set_sensitive(sensitive)
868 self.image_configuration_page.config_build_button.set_sensitive(sensitive)
869
870 self.recipe_details_page.set_sensitive(sensitive)
871 self.package_details_page.set_sensitive(sensitive)
872 self.build_details_page.set_sensitive(sensitive)
873 self.image_details_page.set_sensitive(sensitive)
874
875 if sensitive:
876 self.window.set_cursor(None)
877 else:
878 self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
879 self.sensitive = sensitive
880
881
882 def handler_generating_data_cb(self, handler):
883 self.window_sensitive(False)
884
885 def handler_data_generated_cb(self, handler):
886 self.window_sensitive(True)
887
888 def rcppkglist_populated(self):
889 selected_image = self.configuration.selected_image
890 selected_recipes = self.configuration.selected_recipes[:]
891 selected_packages = self.configuration.selected_packages[:]
892 user_selected_packages = self.configuration.user_selected_packages[:]
893
894 self.image_configuration_page.update_image_combo(self.recipe_model, selected_image)
895 self.image_configuration_page.update_image_desc()
896 self.update_recipe_model(selected_image, selected_recipes)
897 self.update_package_model(selected_packages, user_selected_packages)
898
899 def recipelist_changed_cb(self, recipe_model):
900 self.recipe_details_page.refresh_selection()
901
902 def packagelist_changed_cb(self, package_model):
903 self.package_details_page.refresh_selection()
904
905 def handler_recipe_populated_cb(self, handler):
906 self.image_configuration_page.update_progress_bar("Populating recipes", 0.99)
907
908 def handler_package_populated_cb(self, handler):
909 self.image_configuration_page.update_progress_bar("Populating packages", 1.0)
910
911 def handler_parsing_started_cb(self, handler, message):
912 if self.current_step != self.RCPPKGINFO_POPULATING:
913 return
914
915 fraction = 0
916 if message["eventname"] == "TreeDataPreparationStarted":
917 fraction = 0.6 + fraction
918 self.image_configuration_page.stop_button.set_sensitive(False)
919 self.image_configuration_page.update_progress_bar("Generating dependency tree", fraction)
920 else:
921 self.image_configuration_page.stop_button.set_sensitive(True)
922 self.image_configuration_page.update_progress_bar(message["title"], fraction)
923
924 def handler_parsing_cb(self, handler, message):
925 if self.current_step != self.RCPPKGINFO_POPULATING:
926 return
927
928 fraction = message["current"] * 1.0/message["total"]
929 if message["eventname"] == "TreeDataPreparationProgress":
930 fraction = 0.6 + 0.38 * fraction
931 self.image_configuration_page.update_progress_bar("Generating dependency tree", fraction)
932 else:
933 fraction = 0.6 * fraction
934 self.image_configuration_page.update_progress_bar(message["title"], fraction)
935
936 def handler_parsing_completed_cb(self, handler, message):
937 if self.current_step != self.RCPPKGINFO_POPULATING:
938 return
939
940 if message["eventname"] == "TreeDataPreparationCompleted":
941 fraction = 0.98
942 else:
943 fraction = 0.6
944 self.image_configuration_page.update_progress_bar("Generating dependency tree", fraction)
945
946 def handler_build_started_cb(self, running_build):
947 if self.current_step == self.FAST_IMAGE_GENERATING:
948 fraction = 0
949 elif self.current_step == self.IMAGE_GENERATING:
950 if self.previous_step == self.FAST_IMAGE_GENERATING:
951 fraction = 0.9
952 else:
953 fraction = 0
954 elif self.current_step == self.PACKAGE_GENERATING:
955 fraction = 0
956 self.build_details_page.update_progress_bar("Build Started: ", fraction)
957 self.build_details_page.show_configurations(self.configuration, self.parameters)
958
959 def build_succeeded(self):
960 if self.current_step == self.FAST_IMAGE_GENERATING:
961 fraction = 0.9
962 elif self.current_step == self.IMAGE_GENERATING:
963 fraction = 1.0
964 version = ""
965 self.parameters.image_names = []
966 selected_image = self.recipe_model.get_selected_image()
967 if selected_image == self.recipe_model.__custom_image__:
968 if self.configuration.initial_selected_image != selected_image:
969 version = self.recipe_model.get_custom_image_version()
970 linkname = self.hob_image + version + "-" + self.configuration.curr_mach
971 else:
972 linkname = selected_image + '-' + self.configuration.curr_mach
973 image_extension = self.get_image_extension()
974 for image_type in self.parameters.image_types:
975 if image_type in image_extension:
976 real_types = image_extension[image_type]
977 else:
978 real_types = [image_type]
979 for real_image_type in real_types:
980 linkpath = self.parameters.image_addr + '/' + linkname + '.' + real_image_type
981 if os.path.exists(linkpath):
982 self.parameters.image_names.append(os.readlink(linkpath))
983 elif self.current_step == self.PACKAGE_GENERATING:
984 fraction = 1.0
985 self.build_details_page.update_progress_bar("Build Completed: ", fraction)
986 self.handler.build_succeeded_async()
987 self.stopping = False
988
989 if self.current_step == self.PACKAGE_GENERATING:
990 self.switch_page(self.PACKAGE_GENERATED)
991 elif self.current_step == self.IMAGE_GENERATING:
992 self.switch_page(self.IMAGE_GENERATED)
993
994 def build_failed(self):
995 if self.stopping:
996 status = "stop"
997 message = "Build stopped: "
998 fraction = self.build_details_page.progress_bar.get_fraction()
999 stop_to_next_edit = ""
1000 if self.current_step == self.FAST_IMAGE_GENERATING:
1001 stop_to_next_edit = "image configuration"
1002 elif self.current_step == self.IMAGE_GENERATING:
1003 if self.previous_step == self.FAST_IMAGE_GENERATING:
1004 stop_to_next_edit = "image configuration"
1005 else:
1006 stop_to_next_edit = "packages"
1007 elif self.current_step == self.PACKAGE_GENERATING:
1008 stop_to_next_edit = "recipes"
1009 button = self.build_details_page.show_stop_page(stop_to_next_edit.split(' ')[0])
1010 self.set_default(button)
1011 else:
1012 fail_to_next_edit = ""
1013 if self.current_step == self.FAST_IMAGE_GENERATING:
1014 fail_to_next_edit = "image configuration"
1015 fraction = 0.9
1016 elif self.current_step == self.IMAGE_GENERATING:
1017 if self.previous_step == self.FAST_IMAGE_GENERATING:
1018 fail_to_next_edit = "image configuration"
1019 else:
1020 fail_to_next_edit = "packages"
1021 fraction = 1.0
1022 elif self.current_step == self.PACKAGE_GENERATING:
1023 fail_to_next_edit = "recipes"
1024 fraction = 1.0
1025 self.build_details_page.show_fail_page(fail_to_next_edit.split(' ')[0])
1026 status = "fail"
1027 message = "Build failed: "
1028 self.build_details_page.update_progress_bar(message, fraction, status)
1029 self.build_details_page.show_back_button()
1030 self.build_details_page.hide_stop_button()
1031 self.handler.build_failed_async()
1032 self.stopping = False
1033
1034 def handler_build_succeeded_cb(self, running_build):
1035 if not self.stopping:
1036 self.build_succeeded()
1037 else:
1038 self.build_failed()
1039
1040
1041 def handler_build_failed_cb(self, running_build):
1042 self.build_failed()
1043
1044 def handler_build_aborted_cb(self, running_build):
1045 self.build_failed()
1046
1047 def handler_no_provider_cb(self, running_build, msg):
1048 dialog = CrumbsMessageDialog(self, glib.markup_escape_text(msg), gtk.MESSAGE_INFO)
1049 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1050 HobButton.style_button(button)
1051 dialog.run()
1052 dialog.destroy()
1053 self.build_failed()
1054
1055 def handler_task_started_cb(self, running_build, message):
1056 fraction = message["current"] * 1.0/message["total"]
1057 title = "Build packages"
1058 if self.current_step == self.FAST_IMAGE_GENERATING:
1059 if message["eventname"] == "sceneQueueTaskStarted":
1060 fraction = 0.27 * fraction
1061 elif message["eventname"] == "runQueueTaskStarted":
1062 fraction = 0.27 + 0.63 * fraction
1063 elif self.current_step == self.IMAGE_GENERATING:
1064 title = "Build image"
1065 if self.previous_step == self.FAST_IMAGE_GENERATING:
1066 if message["eventname"] == "sceneQueueTaskStarted":
1067 fraction = 0.27 + 0.63 + 0.03 * fraction
1068 elif message["eventname"] == "runQueueTaskStarted":
1069 fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction
1070 else:
1071 if message["eventname"] == "sceneQueueTaskStarted":
1072 fraction = 0.2 * fraction
1073 elif message["eventname"] == "runQueueTaskStarted":
1074 fraction = 0.2 + 0.8 * fraction
1075 elif self.current_step == self.PACKAGE_GENERATING:
1076 if message["eventname"] == "sceneQueueTaskStarted":
1077 fraction = 0.2 * fraction
1078 elif message["eventname"] == "runQueueTaskStarted":
1079 fraction = 0.2 + 0.8 * fraction
1080 self.build_details_page.update_progress_bar(title + ": ", fraction)
1081 self.build_details_page.update_build_status(message["current"], message["total"], message["task"])
1082
1083 def handler_disk_full_cb(self, running_build):
1084 self.disk_full = True
1085
1086 def handler_build_failure_cb(self, running_build):
1087 self.build_details_page.show_issues()
1088
1089 def handler_build_log_cb(self, running_build, func, obj):
1090 if hasattr(self.logger, func):
1091 getattr(self.logger, func)(obj)
1092
1093 def destroy_window_cb(self, widget, event):
1094 if not self.sensitive:
1095 return True
1096 elif self.handler.building:
1097 self.stop_build()
1098 return True
1099 else:
1100 gtk.main_quit()
1101
1102 def event_handle_SIGINT(self, signal, frame):
1103 for w in gtk.window_list_toplevels():
1104 if w.get_modal():
1105 w.response(gtk.RESPONSE_DELETE_EVENT)
1106 sys.exit(0)
1107
1108 def build_packages(self):
1109 _, all_recipes = self.recipe_model.get_selected_recipes()
1110 if not all_recipes:
1111 lbl = "<b>No selections made</b>"
1112 msg = "You have not made any selections"
1113 msg = msg + " so there isn't anything to bake at this time."
1114 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
1115 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1116 HobButton.style_button(button)
1117 dialog.run()
1118 dialog.destroy()
1119 return
1120 self.generate_packages_async(True)
1121
1122 def build_image(self):
1123 selected_packages = self.package_model.get_selected_packages()
1124 if not selected_packages:
1125 lbl = "<b>No selections made</b>"
1126 msg = "You have not made any selections"
1127 msg = msg + " so there isn't anything to bake at this time."
1128 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
1129 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1130 HobButton.style_button(button)
1131 dialog.run()
1132 dialog.destroy()
1133 return
1134 self.generate_image_async(True)
1135
1136 def just_bake(self):
1137 selected_image = self.recipe_model.get_selected_image()
1138 selected_packages = self.package_model.get_selected_packages() or []
1139
1140 # If no base image and no selected packages don't build anything
1141 if not (selected_packages or selected_image != self.recipe_model.__custom_image__):
1142 lbl = "<b>No selections made</b>"
1143 msg = "You have not made any selections"
1144 msg = msg + " so there isn't anything to bake at this time."
1145 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
1146 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1147 HobButton.style_button(button)
1148 dialog.run()
1149 dialog.destroy()
1150 return
1151
1152 self.fast_generate_image_async(True)
1153
1154 def show_recipe_property_dialog(self, properties):
1155 information = {}
1156 dialog = PropertyDialog(title = properties["name"] +' '+ "properties",
1157 parent = self,
1158 information = properties,
1159 flags = gtk.DIALOG_DESTROY_WITH_PARENT
1160 | gtk.DIALOG_NO_SEPARATOR)
1161
1162 dialog.set_modal(False)
1163
1164 button = dialog.add_button("Close", gtk.RESPONSE_NO)
1165 HobAltButton.style_button(button)
1166 button.connect("clicked", lambda w: dialog.destroy())
1167
1168 dialog.run()
1169
1170 def show_packages_property_dialog(self, properties):
1171 information = {}
1172 dialog = PropertyDialog(title = properties["name"] +' '+ "properties",
1173 parent = self,
1174 information = properties,
1175 flags = gtk.DIALOG_DESTROY_WITH_PARENT
1176 | gtk.DIALOG_NO_SEPARATOR)
1177
1178 dialog.set_modal(False)
1179
1180 button = dialog.add_button("Close", gtk.RESPONSE_NO)
1181 HobAltButton.style_button(button)
1182 button.connect("clicked", lambda w: dialog.destroy())
1183
1184 dialog.run()
1185
1186 def show_layer_selection_dialog(self):
1187 dialog = LayerSelectionDialog(title = "Layers",
1188 layers = copy.deepcopy(self.configuration.layers),
1189 layers_non_removable = copy.deepcopy(self.configuration.layers_non_removable),
1190 all_layers = self.parameters.all_layers,
1191 parent = self,
1192 flags = gtk.DIALOG_MODAL
1193 | gtk.DIALOG_DESTROY_WITH_PARENT
1194 | gtk.DIALOG_NO_SEPARATOR)
1195 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1196 HobAltButton.style_button(button)
1197 button = dialog.add_button("OK", gtk.RESPONSE_YES)
1198 HobButton.style_button(button)
1199 response = dialog.run()
1200 if response == gtk.RESPONSE_YES:
1201 self.configuration.layers = dialog.layers
1202 # DO refresh layers
1203 if dialog.layers_changed:
1204 self.update_config_async()
1205 dialog.destroy()
1206
1207 def get_image_extension(self):
1208 image_extension = {}
1209 for type in self.parameters.image_types:
1210 ext = self.handler.runCommand(["getVariable", "IMAGE_EXTENSION_%s" % type])
1211 if ext:
1212 image_extension[type] = ext.split(' ')
1213
1214 return image_extension
1215
1216 def show_load_my_images_dialog(self):
1217 image_extension = self.get_image_extension()
1218 dialog = ImageSelectionDialog(self.parameters.image_addr, self.parameters.image_types,
1219 "Open My Images", self,
1220 gtk.FILE_CHOOSER_ACTION_SAVE, None,
1221 image_extension)
1222 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1223 HobAltButton.style_button(button)
1224 button = dialog.add_button("Open", gtk.RESPONSE_YES)
1225 HobButton.style_button(button)
1226 response = dialog.run()
1227 if response == gtk.RESPONSE_YES:
1228 if not dialog.image_names:
1229 lbl = "<b>No selections made</b>"
1230 msg = "You have not made any selections"
1231 crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
1232 button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
1233 HobButton.style_button(button)
1234 crumbs_dialog.run()
1235 crumbs_dialog.destroy()
1236 dialog.destroy()
1237 return
1238
1239 self.parameters.image_addr = dialog.image_folder
1240 self.parameters.image_names = dialog.image_names[:]
1241 self.switch_page(self.MY_IMAGE_OPENED)
1242
1243 dialog.destroy()
1244
1245 def show_adv_settings_dialog(self, tab=None):
1246 dialog = AdvancedSettingsDialog(title = "Advanced configuration",
1247 configuration = copy.deepcopy(self.configuration),
1248 all_image_types = self.parameters.image_types,
1249 all_package_formats = self.parameters.all_package_formats,
1250 all_distros = self.parameters.all_distros,
1251 all_sdk_machines = self.parameters.all_sdk_machines,
1252 max_threads = self.parameters.max_threads,
1253 parent = self,
1254 flags = gtk.DIALOG_MODAL
1255 | gtk.DIALOG_DESTROY_WITH_PARENT
1256 | gtk.DIALOG_NO_SEPARATOR)
1257 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1258 HobAltButton.style_button(button)
1259 button = dialog.add_button("Save", gtk.RESPONSE_YES)
1260 HobButton.style_button(button)
1261 dialog.set_save_button(button)
1262 response = dialog.run()
1263 settings_changed = False
1264 if response == gtk.RESPONSE_YES:
1265 self.configuration = dialog.configuration
1266 self.configuration.save(self.handler, True) # remember settings
1267 settings_changed = dialog.settings_changed
1268 dialog.destroy()
1269 return response == gtk.RESPONSE_YES, settings_changed
1270
1271 def show_simple_settings_dialog(self, tab=None):
1272 dialog = SimpleSettingsDialog(title = "Settings",
1273 configuration = copy.deepcopy(self.configuration),
1274 all_image_types = self.parameters.image_types,
1275 all_package_formats = self.parameters.all_package_formats,
1276 all_distros = self.parameters.all_distros,
1277 all_sdk_machines = self.parameters.all_sdk_machines,
1278 max_threads = self.parameters.max_threads,
1279 parent = self,
1280 flags = gtk.DIALOG_MODAL
1281 | gtk.DIALOG_DESTROY_WITH_PARENT
1282 | gtk.DIALOG_NO_SEPARATOR,
1283 handler = self.handler)
1284 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1285 HobAltButton.style_button(button)
1286 button = dialog.add_button("Save", gtk.RESPONSE_YES)
1287 HobButton.style_button(button)
1288 if tab:
1289 dialog.switch_to_page(tab)
1290 response = dialog.run()
1291 settings_changed = False
1292 if response == gtk.RESPONSE_YES:
1293 self.configuration = dialog.configuration
1294 self.configuration.save(self.handler, True) # remember settings
1295 settings_changed = dialog.settings_changed
1296 if dialog.proxy_settings_changed:
1297 self.set_user_config_proxies()
1298 elif dialog.proxy_test_ran:
1299 # The user might have modified the proxies in the "Proxy"
1300 # tab, which in turn made the proxy settings modify in bb.
1301 # If "Cancel" was pressed, restore the previous proxy
1302 # settings inside bb.
1303 self.set_user_config_proxies()
1304 dialog.destroy()
1305 return response == gtk.RESPONSE_YES, settings_changed
1306
1307 def reparse_post_adv_settings(self):
1308 if not self.configuration.curr_mach:
1309 self.update_config_async()
1310 else:
1311 self.configuration.clear_selection()
1312 # DO reparse recipes
1313 self.populate_recipe_package_info_async()
1314
1315 def deploy_image(self, image_name):
1316 if not image_name:
1317 lbl = "<b>Please select an image to deploy.</b>"
1318 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO)
1319 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1320 HobButton.style_button(button)
1321 dialog.run()
1322 dialog.destroy()
1323 return
1324
1325 image_path = os.path.join(self.parameters.image_addr, image_name)
1326 dialog = DeployImageDialog(title = "Usb Image Maker",
1327 image_path = image_path,
1328 parent = self,
1329 flags = gtk.DIALOG_MODAL
1330 | gtk.DIALOG_DESTROY_WITH_PARENT
1331 | gtk.DIALOG_NO_SEPARATOR)
1332 button = dialog.add_button("Close", gtk.RESPONSE_NO)
1333 HobAltButton.style_button(button)
1334 button = dialog.add_button("Make usb image", gtk.RESPONSE_YES)
1335 HobButton.style_button(button)
1336 response = dialog.run()
1337 dialog.destroy()
1338
1339 def show_load_kernel_dialog(self):
1340 dialog = gtk.FileChooserDialog("Load Kernel Files", self,
1341 gtk.FILE_CHOOSER_ACTION_SAVE)
1342 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1343 HobAltButton.style_button(button)
1344 button = dialog.add_button("Open", gtk.RESPONSE_YES)
1345 HobButton.style_button(button)
1346 filter = gtk.FileFilter()
1347 filter.set_name("Kernel Files")
1348 filter.add_pattern("*.bin")
1349 dialog.add_filter(filter)
1350
1351 dialog.set_current_folder(self.parameters.image_addr)
1352
1353 response = dialog.run()
1354 kernel_path = ""
1355 if response == gtk.RESPONSE_YES:
1356 kernel_path = dialog.get_filename()
1357
1358 dialog.destroy()
1359
1360 return kernel_path
1361
1362 def show_load_run_script_dialog(self):
1363 dialog = gtk.FileChooserDialog("Select Run Script", self,
1364 gtk.FILE_CHOOSER_ACTION_OPEN)
1365 button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1366 HobAltButton.style_button(button)
1367 button = dialog.add_button("Select", gtk.RESPONSE_YES)
1368 HobButton.style_button(button)
1369
1370 dialog.set_current_folder(self.parameters.image_addr)
1371
1372 response = dialog.run()
1373 run_script_path = ""
1374 if response == gtk.RESPONSE_YES:
1375 run_script_path = dialog.get_filename()
1376
1377 dialog.destroy()
1378
1379 return run_script_path
1380
1381 def runqemu_image(self, image_name, kernel_name):
1382 if not image_name or not kernel_name:
1383 lbl = "<b>Please select %s to launch in QEMU.</b>" % ("a kernel" if image_name else "an image")
1384 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO)
1385 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1386 HobButton.style_button(button)
1387 dialog.run()
1388 dialog.destroy()
1389 return
1390
1391 kernel_path = os.path.join(self.parameters.image_addr, kernel_name)
1392 image_path = os.path.join(self.parameters.image_addr, image_name)
1393
1394 source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env")
1395 tmp_path = self.parameters.tmpdir
1396 cmdline = bb.ui.crumbs.utils.which_terminal()
1397 if os.path.exists(image_path) and os.path.exists(kernel_path) \
1398 and os.path.exists(source_env_path) and os.path.exists(tmp_path) \
1399 and cmdline:
1400 cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; "
1401 cmdline += "source " + source_env_path + " " + os.getcwd() + "; "
1402 cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'"
1403 subprocess.Popen(shlex.split(cmdline))
1404 else:
1405 lbl = "<b>Path error</b>"
1406 msg = "One of your paths is wrong,"
1407 msg = msg + " please make sure the following paths exist:\n"
1408 msg = msg + "image path:" + image_path + "\n"
1409 msg = msg + "kernel path:" + kernel_path + "\n"
1410 msg = msg + "source environment path:" + source_env_path + "\n"
1411 msg = msg + "tmp path: " + tmp_path + "."
1412 msg = msg + "You may be missing either xterm or vte for terminal services."
1413 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_ERROR, msg)
1414 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1415 HobButton.style_button(button)
1416 dialog.run()
1417 dialog.destroy()
1418
1419 def run_custom_image(self, image_name, custom_sim_path):
1420 if not image_name or not custom_sim_path:
1421 if not image_name:
1422 lbl = "<b>Please select an image to launch in the custom simulator.</b>"
1423 else:
1424 lbl = "<b>Please select a custom simulator for launching the selected image.</b>"
1425 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO)
1426 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1427 HobButton.style_button(button)
1428 dialog.run()
1429 dialog.destroy()
1430 return
1431
1432 image_path = os.path.join(self.parameters.image_addr, image_name)
1433
1434 source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env")
1435 tmp_path = self.parameters.tmpdir
1436 cmdline = bb.ui.crumbs.utils.which_terminal()
1437 if os.path.exists(image_path) and os.path.exists(custom_sim_path) \
1438 and os.path.exists(source_env_path) and os.path.exists(tmp_path) \
1439 and cmdline:
1440 cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; "
1441 cmdline += "source " + source_env_path + " " + os.getcwd() + "; "
1442 cmdline += custom_sim_path + " " + image_path + "\"\'"
1443 subprocess.Popen(shlex.split(cmdline))
1444 else:
1445 lbl = "<b>Path error</b>"
1446 msg = "One of your paths is wrong,"
1447 msg = msg + " please make sure the following paths exist:\n"
1448 msg = msg + "image path:" + image_path + "\n"
1449 msg = msg + "custom simulator path:" + custom_sim_path + "\n"
1450 msg = msg + "source environment path:" + source_env_path + "\n"
1451 msg = msg + "tmp path: " + tmp_path + "."
1452 msg = msg + "You may be missing either xterm or vte for terminal services."
1453 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_ERROR, msg)
1454 button = dialog.add_button("Close", gtk.RESPONSE_OK)
1455 HobButton.style_button(button)
1456 dialog.run()
1457 dialog.destroy()
1458
1459 def show_packages(self):
1460 self.package_details_page.refresh_tables()
1461 self.switch_page(self.PACKAGE_SELECTION)
1462
1463 def show_recipes(self):
1464 self.switch_page(self.RECIPE_SELECTION)
1465
1466 def show_image_details(self):
1467 self.switch_page(self.IMAGE_GENERATED)
1468
1469 def show_configuration(self):
1470 self.switch_page(self.BASEIMG_SELECTED)
1471
1472 def stop_build(self):
1473 if self.stopping:
1474 lbl = "<b>Force Stop build?</b>"
1475 msg = "You've already selected Stop once,"
1476 msg = msg + " would you like to 'Force Stop' the build?\n\n"
1477 msg = msg + "This will stop the build as quickly as possible but may"
1478 msg = msg + " well leave your build directory in an unusable state"
1479 msg = msg + " that requires manual steps to fix."
1480 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_WARNING, msg)
1481 button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1482 HobAltButton.style_button(button)
1483 button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
1484 HobButton.style_button(button)
1485 else:
1486 lbl = "<b>Stop build?</b>"
1487 msg = "Are you sure you want to stop this"
1488 msg = msg + " build?\n\n'Stop' will stop the build as soon as all in"
1489 msg = msg + " progress build tasks are finished. However if a"
1490 msg = msg + " lengthy compilation phase is in progress this may take"
1491 msg = msg + " some time.\n\n"
1492 msg = msg + "'Force Stop' will stop the build as quickly as"
1493 msg = msg + " possible but may well leave your build directory in an"
1494 msg = msg + " unusable state that requires manual steps to fix."
1495 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_WARNING, msg)
1496 button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1497 HobAltButton.style_button(button)
1498 button = dialog.add_button("Force stop", gtk.RESPONSE_YES)
1499 HobAltButton.style_button(button)
1500 button = dialog.add_button("Stop", gtk.RESPONSE_OK)
1501 HobButton.style_button(button)
1502 response = dialog.run()
1503 dialog.destroy()
1504 if response != gtk.RESPONSE_CANCEL:
1505 self.stopping = True
1506 if response == gtk.RESPONSE_OK:
1507 self.build_details_page.progress_bar.set_stop_title("Stopping the build....")
1508 self.build_details_page.progress_bar.set_rcstyle("stop")
1509 self.cancel_build_sync()
1510 elif response == gtk.RESPONSE_YES:
1511 self.cancel_build_sync(True)
1512
1513 def do_log(self, consolelogfile = None):
1514 if consolelogfile:
1515 bb.utils.mkdirhier(os.path.dirname(consolelogfile))
1516 if self.consolelog:
1517 self.logger.removeHandler(self.consolelog)
1518 self.consolelog = None
1519 self.consolelog = logging.FileHandler(consolelogfile)
1520 bb.msg.addDefaultlogFilter(self.consolelog)
1521 format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
1522 self.consolelog.setFormatter(format)
1523
1524 self.logger.addHandler(self.consolelog)
1525
1526 def get_topdir(self):
1527 return self.handler.get_topdir()
1528
1529 def wait(self, delay):
1530 time_start = time.time()
1531 time_end = time_start + delay
1532 while time_end > time.time():
1533 while gtk.events_pending():
1534 gtk.main_iteration()
diff --git a/bitbake/lib/bb/ui/crumbs/hig/advancedsettingsdialog.py b/bitbake/lib/bb/ui/crumbs/hig/advancedsettingsdialog.py
deleted file mode 100644
index e0b3553c2f..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hig/advancedsettingsdialog.py
+++ /dev/null
@@ -1,341 +0,0 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011-2012 Intel Corporation
5#
6# Authored by Joshua Lock <josh@linux.intel.com>
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 hashlib
25from bb.ui.crumbs.hobwidget import HobInfoButton, HobButton
26from bb.ui.crumbs.progressbar import HobProgressBar
27from bb.ui.crumbs.hig.settingsuihelper import SettingsUIHelper
28from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
29from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
30from bb.ui.crumbs.hig.proxydetailsdialog import ProxyDetailsDialog
31
32"""
33The following are convenience classes for implementing GNOME HIG compliant
34BitBake GUI's
35In summary: spacing = 12px, border-width = 6px
36"""
37
38class AdvancedSettingsDialog (CrumbsDialog, SettingsUIHelper):
39
40 def details_cb(self, button, parent, protocol):
41 dialog = ProxyDetailsDialog(title = protocol.upper() + " Proxy Details",
42 user = self.configuration.proxies[protocol][1],
43 passwd = self.configuration.proxies[protocol][2],
44 parent = parent,
45 flags = gtk.DIALOG_MODAL
46 | gtk.DIALOG_DESTROY_WITH_PARENT
47 | gtk.DIALOG_NO_SEPARATOR)
48 dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
49 response = dialog.run()
50 if response == gtk.RESPONSE_OK:
51 self.configuration.proxies[protocol][1] = dialog.user
52 self.configuration.proxies[protocol][2] = dialog.passwd
53 self.refresh_proxy_components()
54 dialog.destroy()
55
56 def set_save_button(self, button):
57 self.save_button = button
58
59 def rootfs_combo_changed_cb(self, rootfs_combo, all_package_format, check_hbox):
60 combo_item = self.rootfs_combo.get_active_text()
61 modified = False
62 for child in check_hbox.get_children():
63 if isinstance(child, gtk.CheckButton):
64 check_hbox.remove(child)
65 modified = True
66 for format in all_package_format:
67 if format != combo_item:
68 check_button = gtk.CheckButton(format)
69 check_hbox.pack_start(check_button, expand=False, fill=False)
70 modified = True
71 if modified:
72 check_hbox.remove(self.pkgfmt_info)
73 check_hbox.pack_start(self.pkgfmt_info, expand=False, fill=False)
74 check_hbox.show_all()
75
76 def gen_pkgfmt_widget(self, curr_package_format, all_package_format, tooltip_combo="", tooltip_extra=""):
77 pkgfmt_vbox = gtk.VBox(False, 6)
78
79 label = self.gen_label_widget("Root file system package format")
80 pkgfmt_vbox.pack_start(label, expand=False, fill=False)
81
82 rootfs_format = ""
83 if curr_package_format:
84 rootfs_format = curr_package_format.split()[0]
85
86 rootfs_format_widget, rootfs_combo = self.gen_combo_widget(rootfs_format, all_package_format, tooltip_combo)
87 pkgfmt_vbox.pack_start(rootfs_format_widget, expand=False, fill=False)
88
89 label = self.gen_label_widget("Additional package formats")
90 pkgfmt_vbox.pack_start(label, expand=False, fill=False)
91
92 check_hbox = gtk.HBox(False, 12)
93 pkgfmt_vbox.pack_start(check_hbox, expand=False, fill=False)
94 for format in all_package_format:
95 if format != rootfs_format:
96 check_button = gtk.CheckButton(format)
97 is_active = (format in curr_package_format.split())
98 check_button.set_active(is_active)
99 check_hbox.pack_start(check_button, expand=False, fill=False)
100
101 self.pkgfmt_info = HobInfoButton(tooltip_extra, self)
102 check_hbox.pack_start(self.pkgfmt_info, expand=False, fill=False)
103
104 rootfs_combo.connect("changed", self.rootfs_combo_changed_cb, all_package_format, check_hbox)
105
106 pkgfmt_vbox.show_all()
107
108 return pkgfmt_vbox, rootfs_combo, check_hbox
109
110 def __init__(self, title, configuration, all_image_types,
111 all_package_formats, all_distros, all_sdk_machines,
112 max_threads, parent, flags, buttons=None):
113 super(AdvancedSettingsDialog, self).__init__(title, parent, flags, buttons)
114
115 # class members from other objects
116 # bitbake settings from Builder.Configuration
117 self.configuration = configuration
118 self.image_types = all_image_types
119 self.all_package_formats = all_package_formats
120 self.all_distros = all_distros[:]
121 self.all_sdk_machines = all_sdk_machines
122 self.max_threads = max_threads
123
124 # class members for internal use
125 self.distro_combo = None
126 self.dldir_text = None
127 self.sstatedir_text = None
128 self.sstatemirror_text = None
129 self.bb_spinner = None
130 self.pmake_spinner = None
131 self.rootfs_size_spinner = None
132 self.extra_size_spinner = None
133 self.gplv3_checkbox = None
134 self.sdk_checkbox = None
135 self.image_types_checkbuttons = {}
136
137 self.md5 = self.config_md5()
138 self.settings_changed = False
139
140 # create visual elements on the dialog
141 self.save_button = None
142 self.create_visual_elements()
143 self.connect("response", self.response_cb)
144
145 def _get_sorted_value(self, var):
146 return " ".join(sorted(str(var).split())) + "\n"
147
148 def config_md5(self):
149 data = ""
150 data += ("PACKAGE_CLASSES: " + self.configuration.curr_package_format + '\n')
151 data += ("DISTRO: " + self._get_sorted_value(self.configuration.curr_distro))
152 data += ("IMAGE_ROOTFS_SIZE: " + self._get_sorted_value(self.configuration.image_rootfs_size))
153 data += ("IMAGE_EXTRA_SIZE: " + self._get_sorted_value(self.configuration.image_extra_size))
154 data += ("INCOMPATIBLE_LICENSE: " + self._get_sorted_value(self.configuration.incompat_license))
155 data += ("SDK_MACHINE: " + self._get_sorted_value(self.configuration.curr_sdk_machine))
156 data += ("TOOLCHAIN_BUILD: " + self._get_sorted_value(self.configuration.toolchain_build))
157 data += ("IMAGE_FSTYPES: " + self._get_sorted_value(self.configuration.image_fstypes))
158 return hashlib.md5(data).hexdigest()
159
160 def create_visual_elements(self):
161 self.nb = gtk.Notebook()
162 self.nb.set_show_tabs(True)
163 self.nb.append_page(self.create_image_types_page(), gtk.Label("Image types"))
164 self.nb.append_page(self.create_output_page(), gtk.Label("Output"))
165 self.nb.set_current_page(0)
166 self.vbox.pack_start(self.nb, expand=True, fill=True)
167 self.vbox.pack_end(gtk.HSeparator(), expand=True, fill=True)
168
169 self.show_all()
170
171 def get_num_checked_image_types(self):
172 total = 0
173 for b in self.image_types_checkbuttons.values():
174 if b.get_active():
175 total = total + 1
176 return total
177
178 def set_save_button_state(self):
179 if self.save_button:
180 self.save_button.set_sensitive(self.get_num_checked_image_types() > 0)
181
182 def image_type_checkbutton_clicked_cb(self, button):
183 self.set_save_button_state()
184 if self.get_num_checked_image_types() == 0:
185 # Show an error dialog
186 lbl = "<b>Select an image type</b>"
187 msg = "You need to select at least one image type."
188 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_WARNING, msg)
189 button = dialog.add_button("OK", gtk.RESPONSE_OK)
190 HobButton.style_button(button)
191 response = dialog.run()
192 dialog.destroy()
193
194 def create_image_types_page(self):
195 main_vbox = gtk.VBox(False, 16)
196 main_vbox.set_border_width(6)
197
198 advanced_vbox = gtk.VBox(False, 6)
199 advanced_vbox.set_border_width(6)
200
201 distro_vbox = gtk.VBox(False, 6)
202 label = self.gen_label_widget("Distro:")
203 tooltip = "Selects the Yocto Project distribution you want"
204 try:
205 i = self.all_distros.index( "defaultsetup" )
206 except ValueError:
207 i = -1
208 if i != -1:
209 self.all_distros[ i ] = "Default"
210 if self.configuration.curr_distro == "defaultsetup":
211 self.configuration.curr_distro = "Default"
212 distro_widget, self.distro_combo = self.gen_combo_widget(self.configuration.curr_distro, self.all_distros,"<b>Distro</b>" + "*" + tooltip)
213 distro_vbox.pack_start(label, expand=False, fill=False)
214 distro_vbox.pack_start(distro_widget, expand=False, fill=False)
215 main_vbox.pack_start(distro_vbox, expand=False, fill=False)
216
217
218 rows = (len(self.image_types)+1)/3
219 table = gtk.Table(rows + 1, 10, True)
220 advanced_vbox.pack_start(table, expand=False, fill=False)
221
222 tooltip = "Image file system types you want."
223 info = HobInfoButton("<b>Image types</b>" + "*" + tooltip, self)
224 label = self.gen_label_widget("Image types:")
225 align = gtk.Alignment(0, 0.5, 0, 0)
226 table.attach(align, 0, 4, 0, 1)
227 align.add(label)
228 table.attach(info, 4, 5, 0, 1)
229
230 i = 1
231 j = 1
232 for image_type in sorted(self.image_types):
233 self.image_types_checkbuttons[image_type] = gtk.CheckButton(image_type)
234 self.image_types_checkbuttons[image_type].connect("toggled", self.image_type_checkbutton_clicked_cb)
235 article = ""
236 if image_type.startswith(("a", "e", "i", "o", "u")):
237 article = "n"
238 if image_type == "live":
239 self.image_types_checkbuttons[image_type].set_tooltip_text("Build iso and hddimg images")
240 else:
241 self.image_types_checkbuttons[image_type].set_tooltip_text("Build a%s %s image" % (article, image_type))
242 table.attach(self.image_types_checkbuttons[image_type], j - 1, j + 3, i, i + 1)
243 if image_type in self.configuration.image_fstypes.split():
244 self.image_types_checkbuttons[image_type].set_active(True)
245 i += 1
246 if i > rows:
247 i = 1
248 j = j + 4
249
250 main_vbox.pack_start(advanced_vbox, expand=False, fill=False)
251 self.set_save_button_state()
252
253 return main_vbox
254
255 def create_output_page(self):
256 advanced_vbox = gtk.VBox(False, 6)
257 advanced_vbox.set_border_width(6)
258
259 advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Package format</span>'), expand=False, fill=False)
260 sub_vbox = gtk.VBox(False, 6)
261 advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
262 tooltip_combo = "Selects the package format used to generate rootfs."
263 tooltip_extra = "Selects extra package formats to build"
264 pkgfmt_widget, self.rootfs_combo, self.check_hbox = self.gen_pkgfmt_widget(self.configuration.curr_package_format, self.all_package_formats,"<b>Root file system package format</b>" + "*" + tooltip_combo,"<b>Additional package formats</b>" + "*" + tooltip_extra)
265 sub_vbox.pack_start(pkgfmt_widget, expand=False, fill=False)
266
267 advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Image size</span>'), expand=False, fill=False)
268 sub_vbox = gtk.VBox(False, 6)
269 advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
270 label = self.gen_label_widget("Image basic size (in MB)")
271 tooltip = "Defines the size for the generated image. The OpenEmbedded build system determines the final size for the generated image using an algorithm that takes into account the initial disk space used for the generated image, the Image basic size value, and the Additional free space value.\n\nFor more information, check the <a href=\"http://www.yoctoproject.org/docs/current/poky-ref-manual/poky-ref-manual.html#var-IMAGE_ROOTFS_SIZE\">Yocto Project Reference Manual</a>."
272 rootfs_size_widget, self.rootfs_size_spinner = self.gen_spinner_widget(int(self.configuration.image_rootfs_size*1.0/1024), 0, 65536,"<b>Image basic size</b>" + "*" + tooltip)
273 sub_vbox.pack_start(label, expand=False, fill=False)
274 sub_vbox.pack_start(rootfs_size_widget, expand=False, fill=False)
275
276 sub_vbox = gtk.VBox(False, 6)
277 advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
278 label = self.gen_label_widget("Additional free space (in MB)")
279 tooltip = "Sets extra free disk space to be added to the generated image. Use this variable when you want to ensure that a specific amount of free disk space is available on a device after an image is installed and running."
280 extra_size_widget, self.extra_size_spinner = self.gen_spinner_widget(int(self.configuration.image_extra_size*1.0/1024), 0, 65536,"<b>Additional free space</b>" + "*" + tooltip)
281 sub_vbox.pack_start(label, expand=False, fill=False)
282 sub_vbox.pack_start(extra_size_widget, expand=False, fill=False)
283
284 advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Licensing</span>'), expand=False, fill=False)
285 self.gplv3_checkbox = gtk.CheckButton("Exclude GPLv3 packages")
286 self.gplv3_checkbox.set_tooltip_text("Check this box to prevent GPLv3 packages from being included in your image")
287 if "GPLv3" in self.configuration.incompat_license.split():
288 self.gplv3_checkbox.set_active(True)
289 else:
290 self.gplv3_checkbox.set_active(False)
291 advanced_vbox.pack_start(self.gplv3_checkbox, expand=False, fill=False)
292
293 advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">SDK</span>'), expand=False, fill=False)
294 sub_hbox = gtk.HBox(False, 6)
295 advanced_vbox.pack_start(sub_hbox, expand=False, fill=False)
296 self.sdk_checkbox = gtk.CheckButton("Populate SDK")
297 tooltip = "Check this box to generate an SDK tarball that consists of the cross-toolchain and a sysroot that contains development packages for your image."
298 self.sdk_checkbox.set_tooltip_text(tooltip)
299 self.sdk_checkbox.set_active(self.configuration.toolchain_build)
300 sub_hbox.pack_start(self.sdk_checkbox, expand=False, fill=False)
301
302 tooltip = "Select the host platform for which you want to run the toolchain contained in the SDK tarball."
303 sdk_machine_widget, self.sdk_machine_combo = self.gen_combo_widget(self.configuration.curr_sdk_machine, self.all_sdk_machines,"<b>Populate SDK</b>" + "*" + tooltip)
304 sub_hbox.pack_start(sdk_machine_widget, expand=False, fill=False)
305
306 return advanced_vbox
307
308 def response_cb(self, dialog, response_id):
309 package_format = []
310 package_format.append(self.rootfs_combo.get_active_text())
311 for child in self.check_hbox:
312 if isinstance(child, gtk.CheckButton) and child.get_active():
313 package_format.append(child.get_label())
314 self.configuration.curr_package_format = " ".join(package_format)
315
316 distro = self.distro_combo.get_active_text()
317 if distro == "Default":
318 distro = "defaultsetup"
319 self.configuration.curr_distro = distro
320 self.configuration.image_rootfs_size = self.rootfs_size_spinner.get_value_as_int() * 1024
321 self.configuration.image_extra_size = self.extra_size_spinner.get_value_as_int() * 1024
322
323 self.configuration.image_fstypes = ""
324 for image_type in self.image_types:
325 if self.image_types_checkbuttons[image_type].get_active():
326 self.configuration.image_fstypes += (" " + image_type)
327 self.configuration.image_fstypes.strip()
328
329 if self.gplv3_checkbox.get_active():
330 if "GPLv3" not in self.configuration.incompat_license.split():
331 self.configuration.incompat_license += " GPLv3"
332 else:
333 if "GPLv3" in self.configuration.incompat_license.split():
334 self.configuration.incompat_license = self.configuration.incompat_license.split().remove("GPLv3")
335 self.configuration.incompat_license = " ".join(self.configuration.incompat_license or [])
336 self.configuration.incompat_license = self.configuration.incompat_license.strip()
337
338 self.configuration.toolchain_build = self.sdk_checkbox.get_active()
339 self.configuration.curr_sdk_machine = self.sdk_machine_combo.get_active_text()
340 md5 = self.config_md5()
341 self.settings_changed = (self.md5 != md5)
diff --git a/bitbake/lib/bb/ui/crumbs/hig/parsingwarningsdialog.py b/bitbake/lib/bb/ui/crumbs/hig/parsingwarningsdialog.py
deleted file mode 100644
index 33bac39db8..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hig/parsingwarningsdialog.py
+++ /dev/null
@@ -1,163 +0,0 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011-2012 Intel Corporation
5#
6# Authored by Cristiana Voicu <cristiana.voicu@intel.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import gtk
22import gobject
23from bb.ui.crumbs.hobwidget import HobAltButton
24from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
25
26"""
27The following are convenience classes for implementing GNOME HIG compliant
28BitBake GUI's
29In summary: spacing = 12px, border-width = 6px
30"""
31
32#
33# ParsingWarningsDialog
34#
35class ParsingWarningsDialog (CrumbsDialog):
36
37 def __init__(self, title, warnings, parent, flags, buttons=None):
38 super(ParsingWarningsDialog, self).__init__(title, parent, flags, buttons)
39
40 self.warnings = warnings
41 self.warning_on = 0
42 self.warn_nb = len(warnings)
43
44 # create visual elements on the dialog
45 self.create_visual_elements()
46
47 def cancel_button_cb(self, button):
48 self.destroy()
49
50 def previous_button_cb(self, button):
51 self.warning_on = self.warning_on - 1
52 self.refresh_components()
53
54 def next_button_cb(self, button):
55 self.warning_on = self.warning_on + 1
56 self.refresh_components()
57
58 def refresh_components(self):
59 lbl = self.warnings[self.warning_on]
60 #when the warning text has more than 400 chars, it uses a scroll bar
61 if 0<= len(lbl) < 400:
62 self.warning_label.set_size_request(320, 230)
63 self.warning_label.set_use_markup(True)
64 self.warning_label.set_line_wrap(True)
65 self.warning_label.set_markup(lbl)
66 self.warning_label.set_property("yalign", 0.00)
67 else:
68 self.textWindow.set_shadow_type(gtk.SHADOW_IN)
69 self.textWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
70 self.msgView = gtk.TextView()
71 self.msgView.set_editable(False)
72 self.msgView.set_wrap_mode(gtk.WRAP_WORD)
73 self.msgView.set_cursor_visible(False)
74 self.msgView.set_size_request(320, 230)
75 self.buf = gtk.TextBuffer()
76 self.buf.set_text(lbl)
77 self.msgView.set_buffer(self.buf)
78 self.textWindow.add(self.msgView)
79 self.msgView.show()
80
81 if self.warning_on==0:
82 self.previous_button.set_sensitive(False)
83 else:
84 self.previous_button.set_sensitive(True)
85
86 if self.warning_on==self.warn_nb-1:
87 self.next_button.set_sensitive(False)
88 else:
89 self.next_button.set_sensitive(True)
90
91 if self.warn_nb>1:
92 self.heading = "Warning " + str(self.warning_on + 1) + " of " + str(self.warn_nb)
93 self.heading_label.set_markup('<span weight="bold">%s</span>' % self.heading)
94 else:
95 self.heading = "Warning"
96 self.heading_label.set_markup('<span weight="bold">%s</span>' % self.heading)
97
98 self.show_all()
99
100 if 0<= len(lbl) < 400:
101 self.textWindow.hide()
102 else:
103 self.warning_label.hide()
104
105 def create_visual_elements(self):
106 self.set_size_request(350, 350)
107 self.heading_label = gtk.Label()
108 self.heading_label.set_alignment(0, 0)
109 self.warning_label = gtk.Label()
110 self.warning_label.set_selectable(True)
111 self.warning_label.set_alignment(0, 0)
112 self.textWindow = gtk.ScrolledWindow()
113
114 table = gtk.Table(1, 10, False)
115
116 cancel_button = gtk.Button()
117 cancel_button.set_label("Close")
118 cancel_button.connect("clicked", self.cancel_button_cb)
119 cancel_button.set_size_request(110, 30)
120
121 self.previous_button = gtk.Button()
122 image1 = gtk.image_new_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON)
123 image1.show()
124 box = gtk.HBox(False, 6)
125 box.show()
126 self.previous_button.add(box)
127 lbl = gtk.Label("Previous")
128 lbl.show()
129 box.pack_start(image1, expand=False, fill=False, padding=3)
130 box.pack_start(lbl, expand=True, fill=True, padding=3)
131 self.previous_button.connect("clicked", self.previous_button_cb)
132 self.previous_button.set_size_request(110, 30)
133
134 self.next_button = gtk.Button()
135 image2 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_BUTTON)
136 image2.show()
137 box = gtk.HBox(False, 6)
138 box.show()
139 self.next_button.add(box)
140 lbl = gtk.Label("Next")
141 lbl.show()
142 box.pack_start(lbl, expand=True, fill=True, padding=3)
143 box.pack_start(image2, expand=False, fill=False, padding=3)
144 self.next_button.connect("clicked", self.next_button_cb)
145 self.next_button.set_size_request(110, 30)
146
147 #when there more than one warning, we need "previous" and "next" button
148 if self.warn_nb>1:
149 self.vbox.pack_start(self.heading_label, expand=False, fill=False)
150 self.vbox.pack_start(self.warning_label, expand=False, fill=False)
151 self.vbox.pack_start(self.textWindow, expand=False, fill=False)
152 table.attach(cancel_button, 6, 7, 0, 1, xoptions=gtk.SHRINK)
153 table.attach(self.previous_button, 7, 8, 0, 1, xoptions=gtk.SHRINK)
154 table.attach(self.next_button, 8, 9, 0, 1, xoptions=gtk.SHRINK)
155 self.vbox.pack_end(table, expand=False, fill=False)
156 else:
157 self.vbox.pack_start(self.heading_label, expand=False, fill=False)
158 self.vbox.pack_start(self.warning_label, expand=False, fill=False)
159 self.vbox.pack_start(self.textWindow, expand=False, fill=False)
160 cancel_button = self.add_button("Close", gtk.RESPONSE_CANCEL)
161 HobAltButton.style_button(cancel_button)
162
163 self.refresh_components()
diff --git a/bitbake/lib/bb/ui/crumbs/hig/proxydetailsdialog.py b/bitbake/lib/bb/ui/crumbs/hig/proxydetailsdialog.py
deleted file mode 100644
index 69e7dffb6d..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hig/proxydetailsdialog.py
+++ /dev/null
@@ -1,90 +0,0 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011-2012 Intel Corporation
5#
6# Authored by Joshua Lock <josh@linux.intel.com>
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
24from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
25
26"""
27The following are convenience classes for implementing GNOME HIG compliant
28BitBake GUI's
29In summary: spacing = 12px, border-width = 6px
30"""
31
32class ProxyDetailsDialog (CrumbsDialog):
33
34 def __init__(self, title, user, passwd, parent, flags, buttons=None):
35 super(ProxyDetailsDialog, self).__init__(title, parent, flags, buttons)
36 self.connect("response", self.response_cb)
37
38 self.auth = not (user == None or passwd == None or user == "")
39 self.user = user or ""
40 self.passwd = passwd or ""
41
42 # create visual elements on the dialog
43 self.create_visual_elements()
44
45 def create_visual_elements(self):
46 self.auth_checkbox = gtk.CheckButton("Use authentication")
47 self.auth_checkbox.set_tooltip_text("Check this box to set the username and the password")
48 self.auth_checkbox.set_active(self.auth)
49 self.auth_checkbox.connect("toggled", self.auth_checkbox_toggled_cb)
50 self.vbox.pack_start(self.auth_checkbox, expand=False, fill=False)
51
52 hbox = gtk.HBox(False, 6)
53 self.user_label = gtk.Label("Username:")
54 self.user_text = gtk.Entry()
55 self.user_text.set_text(self.user)
56 hbox.pack_start(self.user_label, expand=False, fill=False)
57 hbox.pack_end(self.user_text, expand=False, fill=False)
58 self.vbox.pack_start(hbox, expand=False, fill=False)
59
60 hbox = gtk.HBox(False, 6)
61 self.passwd_label = gtk.Label("Password:")
62 self.passwd_text = gtk.Entry()
63 self.passwd_text.set_text(self.passwd)
64 hbox.pack_start(self.passwd_label, expand=False, fill=False)
65 hbox.pack_end(self.passwd_text, expand=False, fill=False)
66 self.vbox.pack_start(hbox, expand=False, fill=False)
67
68 self.refresh_auth_components()
69 self.show_all()
70
71 def refresh_auth_components(self):
72 self.user_label.set_sensitive(self.auth)
73 self.user_text.set_editable(self.auth)
74 self.user_text.set_sensitive(self.auth)
75 self.passwd_label.set_sensitive(self.auth)
76 self.passwd_text.set_editable(self.auth)
77 self.passwd_text.set_sensitive(self.auth)
78
79 def auth_checkbox_toggled_cb(self, button):
80 self.auth = self.auth_checkbox.get_active()
81 self.refresh_auth_components()
82
83 def response_cb(self, dialog, response_id):
84 if response_id == gtk.RESPONSE_OK:
85 if self.auth:
86 self.user = self.user_text.get_text()
87 self.passwd = self.passwd_text.get_text()
88 else:
89 self.user = None
90 self.passwd = None
diff --git a/bitbake/lib/bb/ui/crumbs/hig/retrieveimagedialog.py b/bitbake/lib/bb/ui/crumbs/hig/retrieveimagedialog.py
deleted file mode 100644
index 9017139850..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hig/retrieveimagedialog.py
+++ /dev/null
@@ -1,51 +0,0 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2013 Intel Corporation
5#
6# Authored by Cristiana Voicu <cristiana.voicu@intel.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import gtk
22
23class RetrieveImageDialog (gtk.FileChooserDialog):
24 """
25 This class is used to create a dialog that permits to retrieve
26 a custom image saved previously from Hob.
27 """
28 def __init__(self, directory,title, parent, flags, buttons=None):
29 super(RetrieveImageDialog, self).__init__(title, None, gtk.FILE_CHOOSER_ACTION_OPEN,
30 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN, gtk.RESPONSE_OK))
31 self.directory = directory
32
33 # create visual elements on the dialog
34 self.create_visual_elements()
35
36 def create_visual_elements(self):
37 self.set_show_hidden(True)
38 self.set_default_response(gtk.RESPONSE_OK)
39 self.set_current_folder(self.directory)
40
41 vbox = self.get_children()[0].get_children()[0].get_children()[0]
42 for child in vbox.get_children()[0].get_children()[0].get_children()[0].get_children():
43 vbox.get_children()[0].get_children()[0].get_children()[0].remove(child)
44
45 label1 = gtk.Label()
46 label1.set_text("File system" + self.directory)
47 label1.show()
48 vbox.get_children()[0].get_children()[0].get_children()[0].pack_start(label1, expand=False, fill=False, padding=0)
49 vbox.get_children()[0].get_children()[1].get_children()[0].hide()
50
51 self.get_children()[0].get_children()[1].get_children()[0].set_label("Select")
diff --git a/bitbake/lib/bb/ui/crumbs/hig/saveimagedialog.py b/bitbake/lib/bb/ui/crumbs/hig/saveimagedialog.py
deleted file mode 100644
index 4195f70e1e..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hig/saveimagedialog.py
+++ /dev/null
@@ -1,159 +0,0 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2013 Intel Corporation
5#
6# Authored by Cristiana Voicu <cristiana.voicu@intel.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import gtk
22import glib
23from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
24from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
25from bb.ui.crumbs.hobwidget import HobButton
26
27class SaveImageDialog (CrumbsDialog):
28 """
29 This class is used to create a dialog that permits to save
30 a custom image in a predefined directory.
31 """
32 def __init__(self, directory, name, description, title, parent, flags, buttons=None):
33 super(SaveImageDialog, self).__init__(title, parent, flags, buttons)
34 self.directory = directory
35 self.builder = parent
36 self.name_field = name
37 self.description_field = description
38
39 # create visual elements on the dialog
40 self.create_visual_elements()
41
42 def create_visual_elements(self):
43 self.set_default_response(gtk.RESPONSE_OK)
44 self.vbox.set_border_width(6)
45
46 sub_vbox = gtk.VBox(False, 12)
47 self.vbox.pack_start(sub_vbox, expand=False, fill=False)
48 label = gtk.Label()
49 label.set_alignment(0, 0)
50 label.set_markup("<b>Name</b>")
51 sub_label = gtk.Label()
52 sub_label.set_alignment(0, 0)
53 content = "Image recipe names should be all lowercase and include only alphanumeric\n"
54 content += "characters. The only special character you can use is the ASCII hyphen (-)."
55 sub_label.set_markup(content)
56 self.name_entry = gtk.Entry()
57 self.name_entry.set_text(self.name_field)
58 self.name_entry.set_size_request(350,30)
59 self.name_entry.connect("changed", self.name_entry_changed)
60 sub_vbox.pack_start(label, expand=False, fill=False)
61 sub_vbox.pack_start(sub_label, expand=False, fill=False)
62 sub_vbox.pack_start(self.name_entry, expand=False, fill=False)
63
64 sub_vbox = gtk.VBox(False, 12)
65 self.vbox.pack_start(sub_vbox, expand=False, fill=False)
66 label = gtk.Label()
67 label.set_alignment(0, 0)
68 label.set_markup("<b>Description</b> (optional)")
69 sub_label = gtk.Label()
70 sub_label.set_alignment(0, 0)
71 sub_label.set_markup("The description should be less than 150 characters long.")
72 self.description_entry = gtk.TextView()
73 description_buffer = self.description_entry.get_buffer()
74 description_buffer.set_text(self.description_field)
75 description_buffer.connect("insert-text", self.limit_description_length)
76 self.description_entry.set_wrap_mode(gtk.WRAP_WORD)
77 self.description_entry.set_size_request(350,50)
78 sub_vbox.pack_start(label, expand=False, fill=False)
79 sub_vbox.pack_start(sub_label, expand=False, fill=False)
80 sub_vbox.pack_start(self.description_entry, expand=False, fill=False)
81
82 sub_vbox = gtk.VBox(False, 12)
83 self.vbox.pack_start(sub_vbox, expand=False, fill=False)
84 label = gtk.Label()
85 label.set_alignment(0, 0)
86 label.set_markup("Your image recipe will be saved to:")
87 sub_label = gtk.Label()
88 sub_label.set_alignment(0, 0)
89 sub_label.set_markup(self.directory)
90 sub_vbox.pack_start(label, expand=False, fill=False)
91 sub_vbox.pack_start(sub_label, expand=False, fill=False)
92
93 table = gtk.Table(1, 4, True)
94
95 cancel_button = gtk.Button()
96 cancel_button.set_label("Cancel")
97 cancel_button.connect("clicked", self.cancel_button_cb)
98 cancel_button.set_size_request(110, 30)
99
100 self.save_button = gtk.Button()
101 self.save_button.set_label("Save")
102 self.save_button.connect("clicked", self.save_button_cb)
103 self.save_button.set_size_request(110, 30)
104 if self.name_entry.get_text() == '':
105 self.save_button.set_sensitive(False)
106
107 table.attach(cancel_button, 2, 3, 0, 1)
108 table.attach(self.save_button, 3, 4, 0, 1)
109 self.vbox.pack_end(table, expand=False, fill=False)
110
111 self.show_all()
112
113 def limit_description_length(self, textbuffer, iter, text, length):
114 buffer_bounds = textbuffer.get_bounds()
115 entire_text = textbuffer.get_text(*buffer_bounds)
116 entire_text += text
117 if len(entire_text)>150 or text=="\n":
118 textbuffer.emit_stop_by_name("insert-text")
119
120 def name_entry_changed(self, entry):
121 text = entry.get_text()
122 if text == '':
123 self.save_button.set_sensitive(False)
124 else:
125 self.save_button.set_sensitive(True)
126
127 def cancel_button_cb(self, button):
128 self.destroy()
129
130 def save_button_cb(self, button):
131 text = self.name_entry.get_text()
132 new_text = text.replace("-","")
133 description_buffer = self.description_entry.get_buffer()
134 description = description_buffer.get_text(description_buffer.get_start_iter(),description_buffer.get_end_iter())
135 if new_text.islower() and new_text.isalnum():
136 self.builder.image_details_page.image_saved = True
137 self.builder.customized = False
138 self.builder.generate_new_image(self.directory+text, description)
139 self.builder.recipe_model.set_in_list(text, description)
140 self.builder.recipe_model.set_selected_image(text)
141 self.builder.image_details_page.show_page(self.builder.IMAGE_GENERATED)
142 self.builder.image_details_page.name_field_template = text
143 self.builder.image_details_page.description_field_template = description
144 self.destroy()
145 else:
146 self.show_invalid_input_error_dialog()
147
148 def show_invalid_input_error_dialog(self):
149 lbl = "<b>Invalid characters in image recipe name</b>"
150 msg = "Image recipe names should be all lowercase and\n"
151 msg += "include only alphanumeric characters. The only\n"
152 msg += "special character you can use is the ASCII hyphen (-)."
153 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_ERROR, msg)
154 button = dialog.add_button("Close", gtk.RESPONSE_OK)
155 HobButton.style_button(button)
156
157 res = dialog.run()
158 self.name_entry.grab_focus()
159 dialog.destroy()
diff --git a/bitbake/lib/bb/ui/crumbs/hig/simplesettingsdialog.py b/bitbake/lib/bb/ui/crumbs/hig/simplesettingsdialog.py
deleted file mode 100644
index b5eb3d8738..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hig/simplesettingsdialog.py
+++ /dev/null
@@ -1,891 +0,0 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011-2012 Intel Corporation
5#
6# Authored by Joshua Lock <josh@linux.intel.com>
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 gobject
25import hashlib
26from bb.ui.crumbs.hobwidget import hic, HobInfoButton, HobButton, HobAltButton
27from bb.ui.crumbs.progressbar import HobProgressBar
28from bb.ui.crumbs.hig.settingsuihelper import SettingsUIHelper
29from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
30from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
31from bb.ui.crumbs.hig.proxydetailsdialog import ProxyDetailsDialog
32
33"""
34The following are convenience classes for implementing GNOME HIG compliant
35BitBake GUI's
36In summary: spacing = 12px, border-width = 6px
37"""
38
39class SimpleSettingsDialog (CrumbsDialog, SettingsUIHelper):
40
41 (BUILD_ENV_PAGE_ID,
42 SHARED_STATE_PAGE_ID,
43 PROXIES_PAGE_ID,
44 OTHERS_PAGE_ID) = range(4)
45
46 (TEST_NETWORK_NONE,
47 TEST_NETWORK_INITIAL,
48 TEST_NETWORK_RUNNING,
49 TEST_NETWORK_PASSED,
50 TEST_NETWORK_FAILED,
51 TEST_NETWORK_CANCELED) = range(6)
52
53 TARGETS = [
54 ("MY_TREE_MODEL_ROW", gtk.TARGET_SAME_WIDGET, 0),
55 ("text/plain", 0, 1),
56 ("TEXT", 0, 2),
57 ("STRING", 0, 3),
58 ]
59
60 def __init__(self, title, configuration, all_image_types,
61 all_package_formats, all_distros, all_sdk_machines,
62 max_threads, parent, flags, handler, buttons=None):
63 super(SimpleSettingsDialog, self).__init__(title, parent, flags, buttons)
64
65 # class members from other objects
66 # bitbake settings from Builder.Configuration
67 self.configuration = configuration
68 self.image_types = all_image_types
69 self.all_package_formats = all_package_formats
70 self.all_distros = all_distros
71 self.all_sdk_machines = all_sdk_machines
72 self.max_threads = max_threads
73
74 # class members for internal use
75 self.dldir_text = None
76 self.sstatedir_text = None
77 self.sstatemirrors_list = []
78 self.sstatemirrors_changed = 0
79 self.bb_spinner = None
80 self.pmake_spinner = None
81 self.rootfs_size_spinner = None
82 self.extra_size_spinner = None
83 self.gplv3_checkbox = None
84 self.toolchain_checkbox = None
85 self.setting_store = None
86 self.image_types_checkbuttons = {}
87
88 self.md5 = self.config_md5()
89 self.proxy_md5 = self.config_proxy_md5()
90 self.settings_changed = False
91 self.proxy_settings_changed = False
92 self.handler = handler
93 self.proxy_test_ran = False
94 self.selected_mirror_row = 0
95 self.new_mirror = False
96
97 # create visual elements on the dialog
98 self.create_visual_elements()
99 self.connect("response", self.response_cb)
100
101 def _get_sorted_value(self, var):
102 return " ".join(sorted(str(var).split())) + "\n"
103
104 def config_proxy_md5(self):
105 data = ("ENABLE_PROXY: " + self._get_sorted_value(self.configuration.enable_proxy))
106 if self.configuration.enable_proxy:
107 for protocol in self.configuration.proxies.keys():
108 data += (protocol + ": " + self._get_sorted_value(self.configuration.combine_proxy(protocol)))
109 return hashlib.md5(data).hexdigest()
110
111 def config_md5(self):
112 data = ""
113 for key in self.configuration.extra_setting.keys():
114 data += (key + ": " + self._get_sorted_value(self.configuration.extra_setting[key]))
115 return hashlib.md5(data).hexdigest()
116
117 def gen_proxy_entry_widget(self, protocol, parent, need_button=True, line=0):
118 label = gtk.Label(protocol.upper() + " proxy")
119 self.proxy_table.attach(label, 0, 1, line, line+1, xpadding=24)
120
121 proxy_entry = gtk.Entry()
122 proxy_entry.set_size_request(300, -1)
123 self.proxy_table.attach(proxy_entry, 1, 2, line, line+1, ypadding=4)
124
125 self.proxy_table.attach(gtk.Label(":"), 2, 3, line, line+1, xpadding=12, ypadding=4)
126
127 port_entry = gtk.Entry()
128 port_entry.set_size_request(60, -1)
129 self.proxy_table.attach(port_entry, 3, 4, line, line+1, ypadding=4)
130
131 details_button = HobAltButton("Details")
132 details_button.connect("clicked", self.details_cb, parent, protocol)
133 self.proxy_table.attach(details_button, 4, 5, line, line+1, xpadding=4, yoptions=gtk.EXPAND)
134
135 return proxy_entry, port_entry, details_button
136
137 def refresh_proxy_components(self):
138 self.same_checkbox.set_sensitive(self.configuration.enable_proxy)
139
140 self.http_proxy.set_text(self.configuration.combine_host_only("http"))
141 self.http_proxy.set_editable(self.configuration.enable_proxy)
142 self.http_proxy.set_sensitive(self.configuration.enable_proxy)
143 self.http_proxy_port.set_text(self.configuration.combine_port_only("http"))
144 self.http_proxy_port.set_editable(self.configuration.enable_proxy)
145 self.http_proxy_port.set_sensitive(self.configuration.enable_proxy)
146 self.http_proxy_details.set_sensitive(self.configuration.enable_proxy)
147
148 self.https_proxy.set_text(self.configuration.combine_host_only("https"))
149 self.https_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
150 self.https_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
151 self.https_proxy_port.set_text(self.configuration.combine_port_only("https"))
152 self.https_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
153 self.https_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
154 self.https_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
155
156 self.ftp_proxy.set_text(self.configuration.combine_host_only("ftp"))
157 self.ftp_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
158 self.ftp_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
159 self.ftp_proxy_port.set_text(self.configuration.combine_port_only("ftp"))
160 self.ftp_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
161 self.ftp_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
162 self.ftp_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
163
164 self.socks_proxy.set_text(self.configuration.combine_host_only("socks"))
165 self.socks_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
166 self.socks_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
167 self.socks_proxy_port.set_text(self.configuration.combine_port_only("socks"))
168 self.socks_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
169 self.socks_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
170 self.socks_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
171
172 self.cvs_proxy.set_text(self.configuration.combine_host_only("cvs"))
173 self.cvs_proxy.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
174 self.cvs_proxy.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
175 self.cvs_proxy_port.set_text(self.configuration.combine_port_only("cvs"))
176 self.cvs_proxy_port.set_editable(self.configuration.enable_proxy and (not self.configuration.same_proxy))
177 self.cvs_proxy_port.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
178 self.cvs_proxy_details.set_sensitive(self.configuration.enable_proxy and (not self.configuration.same_proxy))
179
180 if self.configuration.same_proxy:
181 if self.http_proxy.get_text():
182 [w.set_text(self.http_proxy.get_text()) for w in self.same_proxy_addresses]
183 if self.http_proxy_port.get_text():
184 [w.set_text(self.http_proxy_port.get_text()) for w in self.same_proxy_ports]
185
186 def proxy_checkbox_toggled_cb(self, button):
187 self.configuration.enable_proxy = self.proxy_checkbox.get_active()
188 if not self.configuration.enable_proxy:
189 self.configuration.same_proxy = False
190 self.same_checkbox.set_active(self.configuration.same_proxy)
191 self.save_proxy_data()
192 self.refresh_proxy_components()
193
194 def same_checkbox_toggled_cb(self, button):
195 self.configuration.same_proxy = self.same_checkbox.get_active()
196 self.save_proxy_data()
197 self.refresh_proxy_components()
198
199 def save_proxy_data(self):
200 self.configuration.split_proxy("http", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
201 if self.configuration.same_proxy:
202 self.configuration.split_proxy("https", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
203 self.configuration.split_proxy("ftp", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
204 self.configuration.split_proxy("socks", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
205 self.configuration.split_proxy("cvs", self.http_proxy.get_text() + ":" + self.http_proxy_port.get_text())
206 else:
207 self.configuration.split_proxy("https", self.https_proxy.get_text() + ":" + self.https_proxy_port.get_text())
208 self.configuration.split_proxy("ftp", self.ftp_proxy.get_text() + ":" + self.ftp_proxy_port.get_text())
209 self.configuration.split_proxy("socks", self.socks_proxy.get_text() + ":" + self.socks_proxy_port.get_text())
210 self.configuration.split_proxy("cvs", self.cvs_proxy.get_text() + ":" + self.cvs_proxy_port.get_text())
211
212 def response_cb(self, dialog, response_id):
213 if response_id == gtk.RESPONSE_YES:
214 if self.proxy_checkbox.get_active():
215 # Check that all proxy entries have a corresponding port
216 for proxy, port in zip(self.all_proxy_addresses, self.all_proxy_ports):
217 if proxy.get_text() and not port.get_text():
218 lbl = "<b>Enter all port numbers</b>"
219 msg = "Proxy servers require a port number. Please make sure you have entered a port number for each proxy server."
220 dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_WARNING, msg)
221 button = dialog.add_button("Close", gtk.RESPONSE_OK)
222 HobButton.style_button(button)
223 response = dialog.run()
224 dialog.destroy()
225 self.emit_stop_by_name("response")
226 return
227
228 self.configuration.dldir = self.dldir_text.get_text()
229 self.configuration.sstatedir = self.sstatedir_text.get_text()
230 self.configuration.sstatemirror = ""
231 for mirror in self.sstatemirrors_list:
232 if mirror[1] != "" and mirror[2].startswith("file://"):
233 smirror = mirror[2] + " " + mirror[1] + " \\n "
234 self.configuration.sstatemirror += smirror
235 self.configuration.bbthread = self.bb_spinner.get_value_as_int()
236 self.configuration.pmake = self.pmake_spinner.get_value_as_int()
237 self.save_proxy_data()
238 self.configuration.extra_setting = {}
239 it = self.setting_store.get_iter_first()
240 while it:
241 key = self.setting_store.get_value(it, 0)
242 value = self.setting_store.get_value(it, 1)
243 self.configuration.extra_setting[key] = value
244 it = self.setting_store.iter_next(it)
245
246 md5 = self.config_md5()
247 self.settings_changed = (self.md5 != md5)
248 self.proxy_settings_changed = (self.proxy_md5 != self.config_proxy_md5())
249
250 def create_build_environment_page(self):
251 advanced_vbox = gtk.VBox(False, 6)
252 advanced_vbox.set_border_width(6)
253
254 advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Parallel threads</span>'), expand=False, fill=False)
255 sub_vbox = gtk.VBox(False, 6)
256 advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
257 label = self.gen_label_widget("BitBake parallel threads")
258 tooltip = "Sets the number of threads that BitBake tasks can simultaneously run. See the <a href=\""
259 tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
260 tooltip += "poky-ref-manual.html#var-BB_NUMBER_THREADS\">Poky reference manual</a> for information"
261 bbthread_widget, self.bb_spinner = self.gen_spinner_widget(self.configuration.bbthread, 1, self.max_threads,"<b>BitBake prallalel threads</b>" + "*" + tooltip)
262 sub_vbox.pack_start(label, expand=False, fill=False)
263 sub_vbox.pack_start(bbthread_widget, expand=False, fill=False)
264
265 sub_vbox = gtk.VBox(False, 6)
266 advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
267 label = self.gen_label_widget("Make parallel threads")
268 tooltip = "Sets the maximum number of threads the host can use during the build. See the <a href=\""
269 tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
270 tooltip += "poky-ref-manual.html#var-PARALLEL_MAKE\">Poky reference manual</a> for information"
271 pmake_widget, self.pmake_spinner = self.gen_spinner_widget(self.configuration.pmake, 1, self.max_threads,"<b>Make parallel threads</b>" + "*" + tooltip)
272 sub_vbox.pack_start(label, expand=False, fill=False)
273 sub_vbox.pack_start(pmake_widget, expand=False, fill=False)
274
275 advanced_vbox.pack_start(self.gen_label_widget('<span weight="bold">Downloaded source code</span>'), expand=False, fill=False)
276 sub_vbox = gtk.VBox(False, 6)
277 advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
278 label = self.gen_label_widget("Downloads directory")
279 tooltip = "Select a folder that caches the upstream project source code"
280 dldir_widget, self.dldir_text = self.gen_entry_widget(self.configuration.dldir, self,"<b>Downloaded source code</b>" + "*" + tooltip)
281 sub_vbox.pack_start(label, expand=False, fill=False)
282 sub_vbox.pack_start(dldir_widget, expand=False, fill=False)
283
284 return advanced_vbox
285
286 def create_shared_state_page(self):
287 advanced_vbox = gtk.VBox(False)
288 advanced_vbox.set_border_width(12)
289
290 sub_vbox = gtk.VBox(False)
291 advanced_vbox.pack_start(sub_vbox, expand=False, fill=False, padding=24)
292 content = "<span>Shared state directory</span>"
293 tooltip = "Select a folder that caches your prebuilt results"
294 label = self.gen_label_info_widget(content,"<b>Shared state directory</b>" + "*" + tooltip)
295 sstatedir_widget, self.sstatedir_text = self.gen_entry_widget(self.configuration.sstatedir, self)
296 sub_vbox.pack_start(label, expand=False, fill=False)
297 sub_vbox.pack_start(sstatedir_widget, expand=False, fill=False, padding=6)
298
299 content = "<span weight=\"bold\">Shared state mirrors</span>"
300 tooltip = "URLs pointing to pre-built mirrors that will speed your build. "
301 tooltip += "Select the \'Standard\' configuration if the structure of your "
302 tooltip += "mirror replicates the structure of your local shared state directory. "
303 tooltip += "For more information on shared state mirrors, check the <a href=\""
304 tooltip += "http://www.yoctoproject.org/docs/current/poky-ref-manual/"
305 tooltip += "poky-ref-manual.html#shared-state\">Yocto Project Reference Manual</a>."
306 table = self.gen_label_info_widget(content,"<b>Shared state mirrors</b>" + "*" + tooltip)
307 advanced_vbox.pack_start(table, expand=False, fill=False, padding=6)
308
309 sub_vbox = gtk.VBox(False)
310 advanced_vbox.pack_start(sub_vbox, gtk.TRUE, gtk.TRUE, 0)
311
312 if self.sstatemirrors_changed == 0:
313 self.sstatemirrors_changed = 1
314 sstatemirrors = self.configuration.sstatemirror
315 if sstatemirrors == "":
316 sm_list = ["Standard", "", "file://(.*)"]
317 self.sstatemirrors_list.append(sm_list)
318 else:
319 sstatemirrors = [x for x in sstatemirrors.split('\\n')]
320 for sstatemirror in sstatemirrors:
321 sstatemirror_fields = [x for x in sstatemirror.split(' ') if x.strip()]
322 if len(sstatemirror_fields) == 2:
323 if sstatemirror_fields[0] == "file://(.*)" or sstatemirror_fields[0] == "file://.*":
324 sm_list = ["Standard", sstatemirror_fields[1], sstatemirror_fields[0]]
325 else:
326 sm_list = ["Custom", sstatemirror_fields[1], sstatemirror_fields[0]]
327 self.sstatemirrors_list.append(sm_list)
328
329 sstatemirrors_widget, sstatemirrors_store = self.gen_shared_sstate_widget(self.sstatemirrors_list, self)
330 sub_vbox.pack_start(sstatemirrors_widget, expand=True, fill=True)
331
332 table = gtk.Table(1, 10, False)
333 table.set_col_spacings(6)
334 add_mirror_button = HobAltButton("Add mirror")
335 add_mirror_button.connect("clicked", self.add_mirror)
336 add_mirror_button.set_size_request(120,30)
337 table.attach(add_mirror_button, 1, 2, 0, 1, xoptions=gtk.SHRINK)
338
339 self.delete_button = HobAltButton("Delete mirror")
340 self.delete_button.connect("clicked", self.delete_cb)
341 self.delete_button.set_size_request(120, 30)
342 table.attach(self.delete_button, 3, 4, 0, 1, xoptions=gtk.SHRINK)
343
344 advanced_vbox.pack_start(table, expand=False, fill=False, padding=6)
345
346 return advanced_vbox
347
348 def gen_shared_sstate_widget(self, sstatemirrors_list, window):
349 hbox = gtk.HBox(False)
350
351 sstatemirrors_store = gtk.ListStore(str, str, str)
352 for sstatemirror in sstatemirrors_list:
353 sstatemirrors_store.append(sstatemirror)
354
355 self.sstatemirrors_tv = gtk.TreeView()
356 self.sstatemirrors_tv.set_rules_hint(True)
357 self.sstatemirrors_tv.set_headers_visible(True)
358 tree_selection = self.sstatemirrors_tv.get_selection()
359 tree_selection.set_mode(gtk.SELECTION_SINGLE)
360
361 # Allow enable drag and drop of rows including row move
362 self.sstatemirrors_tv.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
363 self.TARGETS,
364 gtk.gdk.ACTION_DEFAULT|
365 gtk.gdk.ACTION_MOVE)
366 self.sstatemirrors_tv.enable_model_drag_dest(self.TARGETS,
367 gtk.gdk.ACTION_DEFAULT)
368 self.sstatemirrors_tv.connect("drag_data_get", self.drag_data_get_cb)
369 self.sstatemirrors_tv.connect("drag_data_received", self.drag_data_received_cb)
370
371
372 self.scroll = gtk.ScrolledWindow()
373 self.scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
374 self.scroll.set_shadow_type(gtk.SHADOW_IN)
375 self.scroll.connect('size-allocate', self.scroll_changed)
376 self.scroll.add(self.sstatemirrors_tv)
377
378 #list store for cell renderer
379 m = gtk.ListStore(gobject.TYPE_STRING)
380 m.append(["Standard"])
381 m.append(["Custom"])
382
383 cell0 = gtk.CellRendererCombo()
384 cell0.set_property("model",m)
385 cell0.set_property("text-column", 0)
386 cell0.set_property("editable", True)
387 cell0.set_property("has-entry", False)
388 col0 = gtk.TreeViewColumn("Configuration")
389 col0.pack_start(cell0, False)
390 col0.add_attribute(cell0, "text", 0)
391 col0.set_cell_data_func(cell0, self.configuration_field)
392 self.sstatemirrors_tv.append_column(col0)
393
394 cell0.connect("edited", self.combo_changed, sstatemirrors_store)
395
396 self.cell1 = gtk.CellRendererText()
397 self.cell1.set_padding(5,2)
398 col1 = gtk.TreeViewColumn('Regex', self.cell1)
399 col1.set_cell_data_func(self.cell1, self.regex_field)
400 self.sstatemirrors_tv.append_column(col1)
401
402 self.cell1.connect("edited", self.regex_changed, sstatemirrors_store)
403
404 cell2 = gtk.CellRendererText()
405 cell2.set_padding(5,2)
406 cell2.set_property("editable", True)
407 col2 = gtk.TreeViewColumn('URL', cell2)
408 col2.set_cell_data_func(cell2, self.url_field)
409 self.sstatemirrors_tv.append_column(col2)
410
411 cell2.connect("edited", self.url_changed, sstatemirrors_store)
412
413 self.sstatemirrors_tv.set_model(sstatemirrors_store)
414 self.sstatemirrors_tv.set_cursor(self.selected_mirror_row)
415 hbox.pack_start(self.scroll, expand=True, fill=True)
416 hbox.show_all()
417
418 return hbox, sstatemirrors_store
419
420 def drag_data_get_cb(self, treeview, context, selection, target_id, etime):
421 treeselection = treeview.get_selection()
422 model, iter = treeselection.get_selected()
423 data = model.get_string_from_iter(iter)
424 selection.set(selection.target, 8, data)
425
426 def drag_data_received_cb(self, treeview, context, x, y, selection, info, etime):
427 model = treeview.get_model()
428 data = []
429 tree_iter = model.get_iter_from_string(selection.data)
430 data.append(model.get_value(tree_iter, 0))
431 data.append(model.get_value(tree_iter, 1))
432 data.append(model.get_value(tree_iter, 2))
433
434 drop_info = treeview.get_dest_row_at_pos(x, y)
435 if drop_info:
436 path, position = drop_info
437 iter = model.get_iter(path)
438 if (position == gtk.TREE_VIEW_DROP_BEFORE or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
439 model.insert_before(iter, data)
440 else:
441 model.insert_after(iter, data)
442 else:
443 model.append(data)
444 if context.action == gtk.gdk.ACTION_MOVE:
445 context.finish(True, True, etime)
446 return
447
448 def delete_cb(self, button):
449 selection = self.sstatemirrors_tv.get_selection()
450 tree_model, tree_iter = selection.get_selected()
451 index = int(tree_model.get_string_from_iter(tree_iter))
452 if index == 0:
453 self.selected_mirror_row = index
454 else:
455 self.selected_mirror_row = index - 1
456 self.sstatemirrors_list.pop(index)
457 self.refresh_shared_state_page()
458 if not self.sstatemirrors_list:
459 self.delete_button.set_sensitive(False)
460
461 def add_mirror(self, button):
462 self.new_mirror = True
463 tooltip = "Select the pre-built mirror that will speed your build"
464 index = len(self.sstatemirrors_list)
465 self.selected_mirror_row = index
466 sm_list = ["Standard", "", "file://(.*)"]
467 self.sstatemirrors_list.append(sm_list)
468 self.refresh_shared_state_page()
469
470 def scroll_changed(self, widget, event, data=None):
471 if self.new_mirror == True:
472 adj = widget.get_vadjustment()
473 adj.set_value(adj.upper - adj.page_size)
474 self.new_mirror = False
475
476 def combo_changed(self, widget, path, text, model):
477 model[path][0] = text
478 selection = self.sstatemirrors_tv.get_selection()
479 tree_model, tree_iter = selection.get_selected()
480 index = int(tree_model.get_string_from_iter(tree_iter))
481 self.sstatemirrors_list[index][0] = text
482
483 def regex_changed(self, cell, path, new_text, user_data):
484 user_data[path][2] = new_text
485 selection = self.sstatemirrors_tv.get_selection()
486 tree_model, tree_iter = selection.get_selected()
487 index = int(tree_model.get_string_from_iter(tree_iter))
488 self.sstatemirrors_list[index][2] = new_text
489 return
490
491 def url_changed(self, cell, path, new_text, user_data):
492 if new_text!="Enter the mirror URL" and new_text!="Match regex and replace it with this URL":
493 user_data[path][1] = new_text
494 selection = self.sstatemirrors_tv.get_selection()
495 tree_model, tree_iter = selection.get_selected()
496 index = int(tree_model.get_string_from_iter(tree_iter))
497 self.sstatemirrors_list[index][1] = new_text
498 return
499
500 def configuration_field(self, column, cell, model, iter):
501 cell.set_property('text', model.get_value(iter, 0))
502 if model.get_value(iter, 0) == "Standard":
503 self.cell1.set_property("sensitive", False)
504 self.cell1.set_property("editable", False)
505 else:
506 self.cell1.set_property("sensitive", True)
507 self.cell1.set_property("editable", True)
508 return
509
510 def regex_field(self, column, cell, model, iter):
511 cell.set_property('text', model.get_value(iter, 2))
512 return
513
514 def url_field(self, column, cell, model, iter):
515 text = model.get_value(iter, 1)
516 if text == "":
517 if model.get_value(iter, 0) == "Standard":
518 text = "Enter the mirror URL"
519 else:
520 text = "Match regex and replace it with this URL"
521 cell.set_property('text', text)
522 return
523
524 def refresh_shared_state_page(self):
525 page_num = self.nb.get_current_page()
526 self.nb.remove_page(page_num);
527 self.nb.insert_page(self.create_shared_state_page(), gtk.Label("Shared state"),page_num)
528 self.show_all()
529 self.nb.set_current_page(page_num)
530
531 def test_proxy_ended(self, passed):
532 self.proxy_test_running = False
533 self.set_test_proxy_state(self.TEST_NETWORK_PASSED if passed else self.TEST_NETWORK_FAILED)
534 self.set_sensitive(True)
535 self.refresh_proxy_components()
536
537 def timer_func(self):
538 self.test_proxy_progress.pulse()
539 return self.proxy_test_running
540
541 def test_network_button_cb(self, b):
542 self.set_test_proxy_state(self.TEST_NETWORK_RUNNING)
543 self.set_sensitive(False)
544 self.save_proxy_data()
545 if self.configuration.enable_proxy == True:
546 self.handler.set_http_proxy(self.configuration.combine_proxy("http"))
547 self.handler.set_https_proxy(self.configuration.combine_proxy("https"))
548 self.handler.set_ftp_proxy(self.configuration.combine_proxy("ftp"))
549 self.handler.set_socks_proxy(self.configuration.combine_proxy("socks"))
550 self.handler.set_cvs_proxy(self.configuration.combine_host_only("cvs"), self.configuration.combine_port_only("cvs"))
551 elif self.configuration.enable_proxy == False:
552 self.handler.set_http_proxy("")
553 self.handler.set_https_proxy("")
554 self.handler.set_ftp_proxy("")
555 self.handler.set_socks_proxy("")
556 self.handler.set_cvs_proxy("", "")
557 self.proxy_test_ran = True
558 self.proxy_test_running = True
559 gobject.timeout_add(100, self.timer_func)
560 self.handler.trigger_network_test()
561
562 def test_proxy_focus_event(self, w, direction):
563 if self.test_proxy_state in [self.TEST_NETWORK_PASSED, self.TEST_NETWORK_FAILED]:
564 self.set_test_proxy_state(self.TEST_NETWORK_INITIAL)
565 return False
566
567 def http_proxy_changed(self, e):
568 if not self.configuration.same_proxy:
569 return
570 if e == self.http_proxy:
571 [w.set_text(self.http_proxy.get_text()) for w in self.same_proxy_addresses]
572 else:
573 [w.set_text(self.http_proxy_port.get_text()) for w in self.same_proxy_ports]
574
575 def proxy_address_focus_out_event(self, w, direction):
576 text = w.get_text()
577 if not text:
578 return False
579 if text.find("//") == -1:
580 w.set_text("http://" + text)
581 return False
582
583 def set_test_proxy_state(self, state):
584 if self.test_proxy_state == state:
585 return
586 [self.proxy_table.remove(w) for w in self.test_gui_elements]
587 if state == self.TEST_NETWORK_INITIAL:
588 self.proxy_table.attach(self.test_network_button, 1, 2, 5, 6)
589 self.test_network_button.show()
590 elif state == self.TEST_NETWORK_RUNNING:
591 self.test_proxy_progress.set_rcstyle("running")
592 self.test_proxy_progress.set_text("Testing network configuration")
593 self.proxy_table.attach(self.test_proxy_progress, 0, 5, 5, 6, xpadding=4)
594 self.test_proxy_progress.show()
595 else: # passed or failed
596 self.dummy_progress.update(1.0)
597 if state == self.TEST_NETWORK_PASSED:
598 self.dummy_progress.set_text("Your network is properly configured")
599 self.dummy_progress.set_rcstyle("running")
600 else:
601 self.dummy_progress.set_text("Network test failed")
602 self.dummy_progress.set_rcstyle("fail")
603 self.proxy_table.attach(self.dummy_progress, 0, 4, 5, 6)
604 self.proxy_table.attach(self.retest_network_button, 4, 5, 5, 6, xpadding=4)
605 self.dummy_progress.show()
606 self.retest_network_button.show()
607 self.test_proxy_state = state
608
609 def create_network_page(self):
610 advanced_vbox = gtk.VBox(False, 6)
611 advanced_vbox.set_border_width(6)
612 self.same_proxy_addresses = []
613 self.same_proxy_ports = []
614 self.all_proxy_ports = []
615 self.all_proxy_addresses = []
616
617 sub_vbox = gtk.VBox(False, 6)
618 advanced_vbox.pack_start(sub_vbox, expand=False, fill=False)
619 label = self.gen_label_widget("<span weight=\"bold\">Set the proxies used when fetching source code</span>")
620 tooltip = "Set the proxies used when fetching source code. A blank field uses a direct internet connection."
621 info = HobInfoButton("<span weight=\"bold\">Set the proxies used when fetching source code</span>" + "*" + tooltip, self)
622 hbox = gtk.HBox(False, 12)
623 hbox.pack_start(label, expand=True, fill=True)
624 hbox.pack_start(info, expand=False, fill=False)
625 sub_vbox.pack_start(hbox, expand=False, fill=False)
626
627 proxy_test_focus = []
628 self.direct_checkbox = gtk.RadioButton(None, "Direct network connection")
629 proxy_test_focus.append(self.direct_checkbox)
630 self.direct_checkbox.set_tooltip_text("Check this box to use a direct internet connection with no proxy")
631 self.direct_checkbox.set_active(not self.configuration.enable_proxy)
632 sub_vbox.pack_start(self.direct_checkbox, expand=False, fill=False)
633
634 self.proxy_checkbox = gtk.RadioButton(self.direct_checkbox, "Manual proxy configuration")
635 proxy_test_focus.append(self.proxy_checkbox)
636 self.proxy_checkbox.set_tooltip_text("Check this box to manually set up a specific proxy")
637 self.proxy_checkbox.set_active(self.configuration.enable_proxy)
638 sub_vbox.pack_start(self.proxy_checkbox, expand=False, fill=False)
639
640 self.same_checkbox = gtk.CheckButton("Use the HTTP proxy for all protocols")
641 proxy_test_focus.append(self.same_checkbox)
642 self.same_checkbox.set_tooltip_text("Check this box to use the HTTP proxy for all five proxies")
643 self.same_checkbox.set_active(self.configuration.same_proxy)
644 hbox = gtk.HBox(False, 12)
645 hbox.pack_start(self.same_checkbox, expand=False, fill=False, padding=24)
646 sub_vbox.pack_start(hbox, expand=False, fill=False)
647
648 self.proxy_table = gtk.Table(6, 5, False)
649 self.http_proxy, self.http_proxy_port, self.http_proxy_details = self.gen_proxy_entry_widget(
650 "http", self, True, 0)
651 proxy_test_focus +=[self.http_proxy, self.http_proxy_port]
652 self.http_proxy.connect("changed", self.http_proxy_changed)
653 self.http_proxy_port.connect("changed", self.http_proxy_changed)
654
655 self.https_proxy, self.https_proxy_port, self.https_proxy_details = self.gen_proxy_entry_widget(
656 "https", self, True, 1)
657 proxy_test_focus += [self.https_proxy, self.https_proxy_port]
658 self.same_proxy_addresses.append(self.https_proxy)
659 self.same_proxy_ports.append(self.https_proxy_port)
660
661 self.ftp_proxy, self.ftp_proxy_port, self.ftp_proxy_details = self.gen_proxy_entry_widget(
662 "ftp", self, True, 2)
663 proxy_test_focus += [self.ftp_proxy, self.ftp_proxy_port]
664 self.same_proxy_addresses.append(self.ftp_proxy)
665 self.same_proxy_ports.append(self.ftp_proxy_port)
666
667 self.socks_proxy, self.socks_proxy_port, self.socks_proxy_details = self.gen_proxy_entry_widget(
668 "socks", self, True, 3)
669 proxy_test_focus += [self.socks_proxy, self.socks_proxy_port]
670 self.same_proxy_addresses.append(self.socks_proxy)
671 self.same_proxy_ports.append(self.socks_proxy_port)
672
673 self.cvs_proxy, self.cvs_proxy_port, self.cvs_proxy_details = self.gen_proxy_entry_widget(
674 "cvs", self, True, 4)
675 proxy_test_focus += [self.cvs_proxy, self.cvs_proxy_port]
676 self.same_proxy_addresses.append(self.cvs_proxy)
677 self.same_proxy_ports.append(self.cvs_proxy_port)
678 self.all_proxy_ports = self.same_proxy_ports + [self.http_proxy_port]
679 self.all_proxy_addresses = self.same_proxy_addresses + [self.http_proxy]
680 sub_vbox.pack_start(self.proxy_table, expand=False, fill=False)
681 self.proxy_table.show_all()
682
683 # Create the graphical elements for the network test feature, but don't display them yet
684 self.test_network_button = HobAltButton("Test network configuration")
685 self.test_network_button.connect("clicked", self.test_network_button_cb)
686 self.test_proxy_progress = HobProgressBar()
687 self.dummy_progress = HobProgressBar()
688 self.retest_network_button = HobAltButton("Retest")
689 self.retest_network_button.connect("clicked", self.test_network_button_cb)
690 self.test_gui_elements = [self.test_network_button, self.test_proxy_progress, self.dummy_progress, self.retest_network_button]
691 # Initialize the network tester
692 self.test_proxy_state = self.TEST_NETWORK_NONE
693 self.set_test_proxy_state(self.TEST_NETWORK_INITIAL)
694 self.proxy_test_passed_id = self.handler.connect("network-passed", lambda h:self.test_proxy_ended(True))
695 self.proxy_test_failed_id = self.handler.connect("network-failed", lambda h:self.test_proxy_ended(False))
696 [w.connect("focus-in-event", self.test_proxy_focus_event) for w in proxy_test_focus]
697 [w.connect("focus-out-event", self.proxy_address_focus_out_event) for w in self.all_proxy_addresses]
698
699 self.direct_checkbox.connect("toggled", self.proxy_checkbox_toggled_cb)
700 self.proxy_checkbox.connect("toggled", self.proxy_checkbox_toggled_cb)
701 self.same_checkbox.connect("toggled", self.same_checkbox_toggled_cb)
702
703 self.refresh_proxy_components()
704 return advanced_vbox
705
706 def switch_to_page(self, page_id):
707 self.nb.set_current_page(page_id)
708
709 def details_cb(self, button, parent, protocol):
710 self.save_proxy_data()
711 dialog = ProxyDetailsDialog(title = protocol.upper() + " Proxy Details",
712 user = self.configuration.proxies[protocol][1],
713 passwd = self.configuration.proxies[protocol][2],
714 parent = parent,
715 flags = gtk.DIALOG_MODAL
716 | gtk.DIALOG_DESTROY_WITH_PARENT
717 | gtk.DIALOG_NO_SEPARATOR)
718 dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
719 response = dialog.run()
720 if response == gtk.RESPONSE_OK:
721 self.configuration.proxies[protocol][1] = dialog.user
722 self.configuration.proxies[protocol][2] = dialog.passwd
723 self.refresh_proxy_components()
724 dialog.destroy()
725
726 def rootfs_combo_changed_cb(self, rootfs_combo, all_package_format, check_hbox):
727 combo_item = self.rootfs_combo.get_active_text()
728 for child in check_hbox.get_children():
729 if isinstance(child, gtk.CheckButton):
730 check_hbox.remove(child)
731 for format in all_package_format:
732 if format != combo_item:
733 check_button = gtk.CheckButton(format)
734 check_hbox.pack_start(check_button, expand=False, fill=False)
735 check_hbox.show_all()
736
737 def gen_pkgfmt_widget(self, curr_package_format, all_package_format, tooltip_combo="", tooltip_extra=""):
738 pkgfmt_hbox = gtk.HBox(False, 24)
739
740 rootfs_vbox = gtk.VBox(False, 6)
741 pkgfmt_hbox.pack_start(rootfs_vbox, expand=False, fill=False)
742
743 label = self.gen_label_widget("Root file system package format")
744 rootfs_vbox.pack_start(label, expand=False, fill=False)
745
746 rootfs_format = ""
747 if curr_package_format:
748 rootfs_format = curr_package_format.split()[0]
749
750 rootfs_format_widget, rootfs_combo = self.gen_combo_widget(rootfs_format, all_package_format, tooltip_combo)
751 rootfs_vbox.pack_start(rootfs_format_widget, expand=False, fill=False)
752
753 extra_vbox = gtk.VBox(False, 6)
754 pkgfmt_hbox.pack_start(extra_vbox, expand=False, fill=False)
755
756 label = self.gen_label_widget("Additional package formats")
757 extra_vbox.pack_start(label, expand=False, fill=False)
758
759 check_hbox = gtk.HBox(False, 12)
760 extra_vbox.pack_start(check_hbox, expand=False, fill=False)
761 for format in all_package_format:
762 if format != rootfs_format:
763 check_button = gtk.CheckButton(format)
764 is_active = (format in curr_package_format.split())
765 check_button.set_active(is_active)
766 check_hbox.pack_start(check_button, expand=False, fill=False)
767
768 info = HobInfoButton(tooltip_extra, self)
769 check_hbox.pack_end(info, expand=False, fill=False)
770
771 rootfs_combo.connect("changed", self.rootfs_combo_changed_cb, all_package_format, check_hbox)
772
773 pkgfmt_hbox.show_all()
774
775 return pkgfmt_hbox, rootfs_combo, check_hbox
776
777 def editable_settings_cell_edited(self, cell, path_string, new_text, model):
778 it = model.get_iter_from_string(path_string)
779 column = cell.get_data("column")
780 model.set(it, column, new_text)
781
782 def editable_settings_add_item_clicked(self, button, model):
783 new_item = ["##KEY##", "##VALUE##"]
784
785 iter = model.append()
786 model.set (iter,
787 0, new_item[0],
788 1, new_item[1],
789 )
790
791 def editable_settings_remove_item_clicked(self, button, treeview):
792 selection = treeview.get_selection()
793 model, iter = selection.get_selected()
794
795 if iter:
796 path = model.get_path(iter)[0]
797 model.remove(iter)
798
799 def gen_editable_settings(self, setting, tooltip=""):
800 setting_hbox = gtk.HBox(False, 12)
801
802 vbox = gtk.VBox(False, 12)
803 setting_hbox.pack_start(vbox, expand=True, fill=True)
804
805 setting_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
806 for key in setting.keys():
807 setting_store.set(setting_store.append(), 0, key, 1, setting[key])
808
809 setting_tree = gtk.TreeView(setting_store)
810 setting_tree.set_headers_visible(True)
811 setting_tree.set_size_request(300, 100)
812
813 col = gtk.TreeViewColumn('Key')
814 col.set_min_width(100)
815 col.set_max_width(150)
816 col.set_resizable(True)
817 col1 = gtk.TreeViewColumn('Value')
818 col1.set_min_width(100)
819 col1.set_max_width(150)
820 col1.set_resizable(True)
821 setting_tree.append_column(col)
822 setting_tree.append_column(col1)
823 cell = gtk.CellRendererText()
824 cell.set_property('width-chars', 10)
825 cell.set_property('editable', True)
826 cell.set_data("column", 0)
827 cell.connect("edited", self.editable_settings_cell_edited, setting_store)
828 cell1 = gtk.CellRendererText()
829 cell1.set_property('width-chars', 10)
830 cell1.set_property('editable', True)
831 cell1.set_data("column", 1)
832 cell1.connect("edited", self.editable_settings_cell_edited, setting_store)
833 col.pack_start(cell, True)
834 col1.pack_end(cell1, True)
835 col.set_attributes(cell, text=0)
836 col1.set_attributes(cell1, text=1)
837
838 scroll = gtk.ScrolledWindow()
839 scroll.set_shadow_type(gtk.SHADOW_IN)
840 scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
841 scroll.add(setting_tree)
842 vbox.pack_start(scroll, expand=True, fill=True)
843
844 # some buttons
845 hbox = gtk.HBox(True, 6)
846 vbox.pack_start(hbox, False, False)
847
848 button = gtk.Button(stock=gtk.STOCK_ADD)
849 button.connect("clicked", self.editable_settings_add_item_clicked, setting_store)
850 hbox.pack_start(button)
851
852 button = gtk.Button(stock=gtk.STOCK_REMOVE)
853 button.connect("clicked", self.editable_settings_remove_item_clicked, setting_tree)
854 hbox.pack_start(button)
855
856 info = HobInfoButton(tooltip, self)
857 setting_hbox.pack_start(info, expand=False, fill=False)
858
859 return setting_hbox, setting_store
860
861 def create_others_page(self):
862 advanced_vbox = gtk.VBox(False, 6)
863 advanced_vbox.set_border_width(6)
864
865 sub_vbox = gtk.VBox(False, 6)
866 advanced_vbox.pack_start(sub_vbox, expand=True, fill=True)
867 label = self.gen_label_widget("<span weight=\"bold\">Add your own variables:</span>")
868 tooltip = "These are key/value pairs for your extra settings. Click \'Add\' and then directly edit the key and the value"
869 setting_widget, self.setting_store = self.gen_editable_settings(self.configuration.extra_setting,"<b>Add your own variables</b>" + "*" + tooltip)
870 sub_vbox.pack_start(label, expand=False, fill=False)
871 sub_vbox.pack_start(setting_widget, expand=True, fill=True)
872
873 return advanced_vbox
874
875 def create_visual_elements(self):
876 self.nb = gtk.Notebook()
877 self.nb.set_show_tabs(True)
878 self.nb.append_page(self.create_build_environment_page(), gtk.Label("Build environment"))
879 self.nb.append_page(self.create_shared_state_page(), gtk.Label("Shared state"))
880 self.nb.append_page(self.create_network_page(), gtk.Label("Network"))
881 self.nb.append_page(self.create_others_page(), gtk.Label("Others"))
882 self.nb.set_current_page(0)
883 self.vbox.pack_start(self.nb, expand=True, fill=True)
884 self.vbox.pack_end(gtk.HSeparator(), expand=True, fill=True)
885
886 self.show_all()
887
888 def destroy(self):
889 self.handler.disconnect(self.proxy_test_passed_id)
890 self.handler.disconnect(self.proxy_test_failed_id)
891 super(SimpleSettingsDialog, self).destroy()
diff --git a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py
deleted file mode 100644
index b71fb33d30..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py
+++ /dev/null
@@ -1,645 +0,0 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011 Intel Corporation
5#
6# Authored by Joshua Lock <josh@linux.intel.com>
7# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License version 2 as
11# published by the Free Software Foundation.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License along
19# with this program; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22import gobject
23import logging
24import ast
25from bb.ui.crumbs.runningbuild import RunningBuild
26
27class HobHandler(gobject.GObject):
28
29 """
30 This object does BitBake event handling for the hob gui.
31 """
32 __gsignals__ = {
33 "package-formats-updated" : (gobject.SIGNAL_RUN_LAST,
34 gobject.TYPE_NONE,
35 (gobject.TYPE_PYOBJECT,)),
36 "config-updated" : (gobject.SIGNAL_RUN_LAST,
37 gobject.TYPE_NONE,
38 (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT,)),
39 "command-succeeded" : (gobject.SIGNAL_RUN_LAST,
40 gobject.TYPE_NONE,
41 (gobject.TYPE_INT,)),
42 "command-failed" : (gobject.SIGNAL_RUN_LAST,
43 gobject.TYPE_NONE,
44 (gobject.TYPE_STRING,)),
45 "parsing-warning" : (gobject.SIGNAL_RUN_LAST,
46 gobject.TYPE_NONE,
47 (gobject.TYPE_STRING,)),
48 "sanity-failed" : (gobject.SIGNAL_RUN_LAST,
49 gobject.TYPE_NONE,
50 (gobject.TYPE_STRING, gobject.TYPE_INT)),
51 "generating-data" : (gobject.SIGNAL_RUN_LAST,
52 gobject.TYPE_NONE,
53 ()),
54 "data-generated" : (gobject.SIGNAL_RUN_LAST,
55 gobject.TYPE_NONE,
56 ()),
57 "parsing-started" : (gobject.SIGNAL_RUN_LAST,
58 gobject.TYPE_NONE,
59 (gobject.TYPE_PYOBJECT,)),
60 "parsing" : (gobject.SIGNAL_RUN_LAST,
61 gobject.TYPE_NONE,
62 (gobject.TYPE_PYOBJECT,)),
63 "parsing-completed" : (gobject.SIGNAL_RUN_LAST,
64 gobject.TYPE_NONE,
65 (gobject.TYPE_PYOBJECT,)),
66 "recipe-populated" : (gobject.SIGNAL_RUN_LAST,
67 gobject.TYPE_NONE,
68 ()),
69 "package-populated" : (gobject.SIGNAL_RUN_LAST,
70 gobject.TYPE_NONE,
71 ()),
72 "network-passed" : (gobject.SIGNAL_RUN_LAST,
73 gobject.TYPE_NONE,
74 ()),
75 "network-failed" : (gobject.SIGNAL_RUN_LAST,
76 gobject.TYPE_NONE,
77 ()),
78 }
79
80 (GENERATE_CONFIGURATION, GENERATE_RECIPES, GENERATE_PACKAGES, GENERATE_IMAGE, POPULATE_PACKAGEINFO, SANITY_CHECK, NETWORK_TEST) = range(7)
81 (SUB_PATH_LAYERS, SUB_FILES_DISTRO, SUB_FILES_MACH, SUB_FILES_SDKMACH, SUB_MATCH_CLASS, SUB_PARSE_CONFIG, SUB_SANITY_CHECK,
82 SUB_GNERATE_TGTS, SUB_GENERATE_PKGINFO, SUB_BUILD_RECIPES, SUB_BUILD_IMAGE, SUB_NETWORK_TEST) = range(12)
83
84 def __init__(self, server, recipe_model, package_model):
85 super(HobHandler, self).__init__()
86
87 self.build = RunningBuild(sequential=True)
88
89 self.recipe_model = recipe_model
90 self.package_model = package_model
91
92 self.commands_async = []
93 self.generating = False
94 self.current_phase = None
95 self.building = False
96 self.recipe_queue = []
97 self.package_queue = []
98
99 self.server = server
100 self.error_msg = ""
101 self.initcmd = None
102 self.parsing = False
103
104 def set_busy(self):
105 if not self.generating:
106 self.emit("generating-data")
107 self.generating = True
108
109 def clear_busy(self):
110 if self.generating:
111 self.emit("data-generated")
112 self.generating = False
113
114 def runCommand(self, commandline):
115 try:
116 result, error = self.server.runCommand(commandline)
117 if error:
118 raise Exception("Error running command '%s': %s" % (commandline, error))
119 return result
120 except Exception as e:
121 self.commands_async = []
122 self.clear_busy()
123 self.emit("command-failed", "Hob Exception - %s" % (str(e)))
124 return None
125
126 def run_next_command(self, initcmd=None):
127 if initcmd != None:
128 self.initcmd = initcmd
129
130 if self.commands_async:
131 self.set_busy()
132 next_command = self.commands_async.pop(0)
133 else:
134 self.clear_busy()
135 if self.initcmd != None:
136 self.emit("command-succeeded", self.initcmd)
137 return
138
139 if next_command == self.SUB_PATH_LAYERS:
140 self.runCommand(["findConfigFilePath", "bblayers.conf"])
141 elif next_command == self.SUB_FILES_DISTRO:
142 self.runCommand(["findConfigFiles", "DISTRO"])
143 elif next_command == self.SUB_FILES_MACH:
144 self.runCommand(["findConfigFiles", "MACHINE"])
145 elif next_command == self.SUB_FILES_SDKMACH:
146 self.runCommand(["findConfigFiles", "MACHINE-SDK"])
147 elif next_command == self.SUB_MATCH_CLASS:
148 self.runCommand(["findFilesMatchingInDir", "rootfs_", "classes"])
149 elif next_command == self.SUB_PARSE_CONFIG:
150 self.runCommand(["resetCooker"])
151 elif next_command == self.SUB_GNERATE_TGTS:
152 self.runCommand(["generateTargetsTree", "classes/image.bbclass", []])
153 elif next_command == self.SUB_GENERATE_PKGINFO:
154 self.runCommand(["triggerEvent", "bb.event.RequestPackageInfo()"])
155 elif next_command == self.SUB_SANITY_CHECK:
156 self.runCommand(["triggerEvent", "bb.event.SanityCheck()"])
157 elif next_command == self.SUB_NETWORK_TEST:
158 self.runCommand(["triggerEvent", "bb.event.NetworkTest()"])
159 elif next_command == self.SUB_BUILD_RECIPES:
160 self.clear_busy()
161 self.building = True
162 self.runCommand(["buildTargets", self.recipe_queue, self.default_task])
163 self.recipe_queue = []
164 elif next_command == self.SUB_BUILD_IMAGE:
165 self.clear_busy()
166 self.building = True
167 target = self.image
168
169 if self.base_image:
170 # Request the build of a custom image
171 self.generate_hob_base_image(target)
172 self.set_var_in_file("LINGUAS_INSTALL", "", "local.conf")
173 hobImage = self.runCommand(["matchFile", target + ".bb"])
174 if self.base_image != self.recipe_model.__custom_image__:
175 baseImage = self.runCommand(["matchFile", self.base_image + ".bb"])
176 version = self.runCommand(["generateNewImage", hobImage, baseImage, self.package_queue, True, ""])
177 target += version
178 self.recipe_model.set_custom_image_version(version)
179
180 targets = [target]
181 if self.toolchain_packages:
182 self.set_var_in_file("TOOLCHAIN_TARGET_TASK", " ".join(self.toolchain_packages), "local.conf")
183 targets.append(target + ":do_populate_sdk")
184
185 self.runCommand(["buildTargets", targets, self.default_task])
186
187 def display_error(self):
188 self.clear_busy()
189 self.emit("command-failed", self.error_msg)
190 self.error_msg = ""
191 if self.building:
192 self.building = False
193
194 def handle_event(self, event):
195 if not event:
196 return
197 if self.building:
198 self.current_phase = "building"
199 self.build.handle_event(event)
200
201 if isinstance(event, bb.event.PackageInfo):
202 self.package_model.populate(event._pkginfolist)
203 self.emit("package-populated")
204 self.run_next_command()
205
206 elif isinstance(event, bb.event.SanityCheckPassed):
207 reparse = self.runCommand(["getVariable", "BB_INVALIDCONF"]) or None
208 if reparse is True:
209 self.set_var_in_file("BB_INVALIDCONF", False, "local.conf")
210 self.runCommand(["setPrePostConfFiles", "conf/.hob.conf", ""])
211 self.commands_async.prepend(self.SUB_PARSE_CONFIG)
212 self.run_next_command()
213
214 elif isinstance(event, bb.event.SanityCheckFailed):
215 self.emit("sanity-failed", event._msg, event._network_error)
216
217 elif isinstance(event, logging.LogRecord):
218 if not self.building:
219 if event.levelno >= logging.ERROR:
220 formatter = bb.msg.BBLogFormatter()
221 msg = formatter.format(event)
222 self.error_msg += msg + '\n'
223 elif event.levelno >= logging.WARNING and self.parsing == True:
224 formatter = bb.msg.BBLogFormatter()
225 msg = formatter.format(event)
226 warn_msg = msg + '\n'
227 self.emit("parsing-warning", warn_msg)
228
229 elif isinstance(event, bb.event.TargetsTreeGenerated):
230 self.current_phase = "data generation"
231 if event._model:
232 self.recipe_model.populate(event._model)
233 self.emit("recipe-populated")
234 elif isinstance(event, bb.event.ConfigFilesFound):
235 self.current_phase = "configuration lookup"
236 var = event._variable
237 values = event._values
238 values.sort()
239 self.emit("config-updated", var, values)
240 elif isinstance(event, bb.event.ConfigFilePathFound):
241 self.current_phase = "configuration lookup"
242 elif isinstance(event, bb.event.FilesMatchingFound):
243 self.current_phase = "configuration lookup"
244 # FIXME: hard coding, should at least be a variable shared between
245 # here and the caller
246 if event._pattern == "rootfs_":
247 formats = []
248 for match in event._matches:
249 classname, sep, cls = match.rpartition(".")
250 fs, sep, format = classname.rpartition("_")
251 formats.append(format)
252 formats.sort()
253 self.emit("package-formats-updated", formats)
254 elif isinstance(event, bb.command.CommandCompleted):
255 self.current_phase = None
256 self.run_next_command()
257 elif isinstance(event, bb.command.CommandFailed):
258 if event.error not in ("Forced shutdown", "Stopped build"):
259 self.error_msg += event.error
260 self.commands_async = []
261 self.display_error()
262 elif isinstance(event, (bb.event.ParseStarted,
263 bb.event.CacheLoadStarted,
264 bb.event.TreeDataPreparationStarted,
265 )):
266 message = {}
267 message["eventname"] = bb.event.getName(event)
268 message["current"] = 0
269 message["total"] = None
270 message["title"] = "Parsing recipes"
271 self.emit("parsing-started", message)
272 if isinstance(event, bb.event.ParseStarted):
273 self.parsing = True
274 elif isinstance(event, (bb.event.ParseProgress,
275 bb.event.CacheLoadProgress,
276 bb.event.TreeDataPreparationProgress)):
277 message = {}
278 message["eventname"] = bb.event.getName(event)
279 message["current"] = event.current
280 message["total"] = event.total
281 message["title"] = "Parsing recipes"
282 self.emit("parsing", message)
283 elif isinstance(event, (bb.event.ParseCompleted,
284 bb.event.CacheLoadCompleted,
285 bb.event.TreeDataPreparationCompleted)):
286 message = {}
287 message["eventname"] = bb.event.getName(event)
288 message["current"] = event.total
289 message["total"] = event.total
290 message["title"] = "Parsing recipes"
291 self.emit("parsing-completed", message)
292 if isinstance(event, bb.event.ParseCompleted):
293 self.parsing = False
294 elif isinstance(event, bb.event.NetworkTestFailed):
295 self.emit("network-failed")
296 self.run_next_command()
297 elif isinstance(event, bb.event.NetworkTestPassed):
298 self.emit("network-passed")
299 self.run_next_command()
300
301 if self.error_msg and not self.commands_async:
302 self.display_error()
303
304 return
305
306 def init_cooker(self):
307 self.runCommand(["createConfigFile", ".hob.conf"])
308
309 def set_extra_inherit(self, bbclass):
310 self.append_var_in_file("INHERIT", bbclass, ".hob.conf")
311
312 def set_bblayers(self, bblayers):
313 self.set_var_in_file("BBLAYERS", " ".join(bblayers), "bblayers.conf")
314
315 def set_machine(self, machine):
316 if machine:
317 self.early_assign_var_in_file("MACHINE", machine, "local.conf")
318
319 def set_sdk_machine(self, sdk_machine):
320 self.set_var_in_file("SDKMACHINE", sdk_machine, "local.conf")
321
322 def set_image_fstypes(self, image_fstypes):
323 self.set_var_in_file("IMAGE_FSTYPES", image_fstypes, "local.conf")
324
325 def set_distro(self, distro):
326 self.set_var_in_file("DISTRO", distro, "local.conf")
327
328 def set_package_format(self, format):
329 package_classes = ""
330 for pkgfmt in format.split():
331 package_classes += ("package_%s" % pkgfmt + " ")
332 self.set_var_in_file("PACKAGE_CLASSES", package_classes, "local.conf")
333
334 def set_bbthreads(self, threads):
335 self.set_var_in_file("BB_NUMBER_THREADS", threads, "local.conf")
336
337 def set_pmake(self, threads):
338 pmake = "-j %s" % threads
339 self.set_var_in_file("PARALLEL_MAKE", pmake, "local.conf")
340
341 def set_dl_dir(self, directory):
342 self.set_var_in_file("DL_DIR", directory, "local.conf")
343
344 def set_sstate_dir(self, directory):
345 self.set_var_in_file("SSTATE_DIR", directory, "local.conf")
346
347 def set_sstate_mirrors(self, url):
348 self.set_var_in_file("SSTATE_MIRRORS", url, "local.conf")
349
350 def set_extra_size(self, image_extra_size):
351 self.set_var_in_file("IMAGE_ROOTFS_EXTRA_SPACE", str(image_extra_size), "local.conf")
352
353 def set_rootfs_size(self, image_rootfs_size):
354 self.set_var_in_file("IMAGE_ROOTFS_SIZE", str(image_rootfs_size), "local.conf")
355
356 def set_incompatible_license(self, incompat_license):
357 self.set_var_in_file("INCOMPATIBLE_LICENSE", incompat_license, "local.conf")
358
359 def set_extra_setting(self, extra_setting):
360 self.set_var_in_file("EXTRA_SETTING", extra_setting, "local.conf")
361
362 def set_extra_config(self, extra_setting):
363 old_extra_setting = self.runCommand(["getVariable", "EXTRA_SETTING"]) or {}
364 old_extra_setting = str(old_extra_setting)
365
366 old_extra_setting = ast.literal_eval(old_extra_setting)
367 if not type(old_extra_setting) == dict:
368 old_extra_setting = {}
369
370 # settings not changed
371 if old_extra_setting == extra_setting:
372 return
373
374 # remove the old EXTRA SETTING variable
375 self.remove_var_from_file("EXTRA_SETTING")
376
377 # remove old settings from conf
378 for key in old_extra_setting.keys():
379 if key not in extra_setting:
380 self.remove_var_from_file(key)
381
382 # add new settings
383 for key, value in extra_setting.iteritems():
384 self.set_var_in_file(key, value, "local.conf")
385
386 if extra_setting:
387 self.set_var_in_file("EXTRA_SETTING", extra_setting, "local.conf")
388
389 def set_http_proxy(self, http_proxy):
390 self.set_var_in_file("http_proxy", http_proxy, "local.conf")
391
392 def set_https_proxy(self, https_proxy):
393 self.set_var_in_file("https_proxy", https_proxy, "local.conf")
394
395 def set_ftp_proxy(self, ftp_proxy):
396 self.set_var_in_file("ftp_proxy", ftp_proxy, "local.conf")
397
398 def set_socks_proxy(self, socks_proxy):
399 self.set_var_in_file("all_proxy", socks_proxy, "local.conf")
400
401 def set_cvs_proxy(self, host, port):
402 self.set_var_in_file("CVS_PROXY_HOST", host, "local.conf")
403 self.set_var_in_file("CVS_PROXY_PORT", port, "local.conf")
404
405 def request_package_info(self):
406 self.commands_async.append(self.SUB_GENERATE_PKGINFO)
407 self.run_next_command(self.POPULATE_PACKAGEINFO)
408
409 def trigger_sanity_check(self):
410 self.commands_async.append(self.SUB_SANITY_CHECK)
411 self.run_next_command(self.SANITY_CHECK)
412
413 def trigger_network_test(self):
414 self.commands_async.append(self.SUB_NETWORK_TEST)
415 self.run_next_command(self.NETWORK_TEST)
416
417 def generate_configuration(self):
418 self.runCommand(["setPrePostConfFiles", "conf/.hob.conf", ""])
419 self.commands_async.append(self.SUB_PARSE_CONFIG)
420 self.commands_async.append(self.SUB_PATH_LAYERS)
421 self.commands_async.append(self.SUB_FILES_DISTRO)
422 self.commands_async.append(self.SUB_FILES_MACH)
423 self.commands_async.append(self.SUB_FILES_SDKMACH)
424 self.commands_async.append(self.SUB_MATCH_CLASS)
425 self.run_next_command(self.GENERATE_CONFIGURATION)
426
427 def generate_recipes(self):
428 self.runCommand(["setPrePostConfFiles", "conf/.hob.conf", ""])
429 self.commands_async.append(self.SUB_PARSE_CONFIG)
430 self.commands_async.append(self.SUB_GNERATE_TGTS)
431 self.run_next_command(self.GENERATE_RECIPES)
432
433 def generate_packages(self, tgts, default_task="build"):
434 targets = []
435 targets.extend(tgts)
436 self.recipe_queue = targets
437 self.default_task = default_task
438 self.runCommand(["setPrePostConfFiles", "conf/.hob.conf", ""])
439 self.commands_async.append(self.SUB_PARSE_CONFIG)
440 self.commands_async.append(self.SUB_BUILD_RECIPES)
441 self.run_next_command(self.GENERATE_PACKAGES)
442
443 def generate_image(self, image, base_image, image_packages=None, toolchain_packages=None, default_task="build"):
444 self.image = image
445 self.base_image = base_image
446 if image_packages:
447 self.package_queue = image_packages
448 else:
449 self.package_queue = []
450 if toolchain_packages:
451 self.toolchain_packages = toolchain_packages
452 else:
453 self.toolchain_packages = []
454 self.default_task = default_task
455 self.runCommand(["setPrePostConfFiles", "conf/.hob.conf", ""])
456 self.commands_async.append(self.SUB_PARSE_CONFIG)
457 self.commands_async.append(self.SUB_BUILD_IMAGE)
458 self.run_next_command(self.GENERATE_IMAGE)
459
460 def generate_new_image(self, image, base_image, package_queue, description):
461 if base_image:
462 base_image = self.runCommand(["matchFile", self.base_image + ".bb"])
463 self.runCommand(["generateNewImage", image, base_image, package_queue, False, description])
464
465 def generate_hob_base_image(self, hob_image):
466 image_dir = self.get_topdir() + "/recipes/images/"
467 recipe_name = hob_image + ".bb"
468 self.ensure_dir(image_dir)
469 self.generate_new_image(image_dir + recipe_name, None, [], "")
470
471 def ensure_dir(self, directory):
472 self.runCommand(["ensureDir", directory])
473
474 def build_succeeded_async(self):
475 self.building = False
476
477 def build_failed_async(self):
478 self.initcmd = None
479 self.commands_async = []
480 self.building = False
481
482 def cancel_parse(self):
483 self.runCommand(["stateForceShutdown"])
484
485 def cancel_build(self, force=False):
486 if force:
487 # Force the cooker to stop as quickly as possible
488 self.runCommand(["stateForceShutdown"])
489 else:
490 # Wait for tasks to complete before shutting down, this helps
491 # leave the workdir in a usable state
492 self.runCommand(["stateShutdown"])
493
494 def reset_build(self):
495 self.build.reset()
496
497 def get_logfile(self):
498 return self.server.runCommand(["getVariable", "BB_CONSOLELOG"])[0]
499
500 def get_topdir(self):
501 return self.runCommand(["getVariable", "TOPDIR"]) or ""
502
503 def _remove_redundant(self, string):
504 ret = []
505 for i in string.split():
506 if i not in ret:
507 ret.append(i)
508 return " ".join(ret)
509
510 def set_var_in_file(self, var, val, default_file=None):
511 self.runCommand(["enableDataTracking"])
512 self.server.runCommand(["setVarFile", var, val, default_file, "set"])
513 self.runCommand(["disableDataTracking"])
514
515 def early_assign_var_in_file(self, var, val, default_file=None):
516 self.runCommand(["enableDataTracking"])
517 self.server.runCommand(["setVarFile", var, val, default_file, "earlyAssign"])
518 self.runCommand(["disableDataTracking"])
519
520 def remove_var_from_file(self, var):
521 self.server.runCommand(["removeVarFile", var])
522
523 def append_var_in_file(self, var, val, default_file=None):
524 self.server.runCommand(["setVarFile", var, val, default_file, "append"])
525
526 def append_to_bbfiles(self, val):
527 bbfiles = self.runCommand(["getVariable", "BBFILES", "False"]) or ""
528 bbfiles = bbfiles.split()
529 if val not in bbfiles:
530 self.append_var_in_file("BBFILES", val, "bblayers.conf")
531
532 def get_parameters(self):
533 # retrieve the parameters from bitbake
534 params = {}
535 params["core_base"] = self.runCommand(["getVariable", "COREBASE"]) or ""
536 params["layer"] = self.runCommand(["getVariable", "BBLAYERS"]) or ""
537 params["layers_non_removable"] = self.runCommand(["getVariable", "BBLAYERS_NON_REMOVABLE"]) or ""
538 params["dldir"] = self.runCommand(["getVariable", "DL_DIR"]) or ""
539 params["machine"] = self.runCommand(["getVariable", "MACHINE"]) or ""
540 params["distro"] = self.runCommand(["getVariable", "DISTRO"]) or "defaultsetup"
541 params["pclass"] = self.runCommand(["getVariable", "PACKAGE_CLASSES"]) or ""
542 params["sstatedir"] = self.runCommand(["getVariable", "SSTATE_DIR"]) or ""
543 params["sstatemirror"] = self.runCommand(["getVariable", "SSTATE_MIRRORS"]) or ""
544
545 num_threads = self.runCommand(["getCpuCount"])
546 if not num_threads:
547 num_threads = 1
548 max_threads = 65536
549 else:
550 try:
551 num_threads = int(num_threads)
552 max_threads = 16 * num_threads
553 except:
554 num_threads = 1
555 max_threads = 65536
556 params["max_threads"] = max_threads
557
558 bbthread = self.runCommand(["getVariable", "BB_NUMBER_THREADS"])
559 if not bbthread:
560 bbthread = num_threads
561 else:
562 try:
563 bbthread = int(bbthread)
564 except:
565 bbthread = num_threads
566 params["bbthread"] = bbthread
567
568 pmake = self.runCommand(["getVariable", "PARALLEL_MAKE"])
569 if not pmake:
570 pmake = num_threads
571 elif isinstance(pmake, int):
572 pass
573 else:
574 try:
575 pmake = int(pmake.lstrip("-j "))
576 except:
577 pmake = num_threads
578 params["pmake"] = "-j %s" % pmake
579
580 params["image_addr"] = self.runCommand(["getVariable", "DEPLOY_DIR_IMAGE"]) or ""
581
582 image_extra_size = self.runCommand(["getVariable", "IMAGE_ROOTFS_EXTRA_SPACE"])
583 if not image_extra_size:
584 image_extra_size = 0
585 else:
586 try:
587 image_extra_size = int(image_extra_size)
588 except:
589 image_extra_size = 0
590 params["image_extra_size"] = image_extra_size
591
592 image_rootfs_size = self.runCommand(["getVariable", "IMAGE_ROOTFS_SIZE"])
593 if not image_rootfs_size:
594 image_rootfs_size = 0
595 else:
596 try:
597 image_rootfs_size = int(image_rootfs_size)
598 except:
599 image_rootfs_size = 0
600 params["image_rootfs_size"] = image_rootfs_size
601
602 image_overhead_factor = self.runCommand(["getVariable", "IMAGE_OVERHEAD_FACTOR"])
603 if not image_overhead_factor:
604 image_overhead_factor = 1
605 else:
606 try:
607 image_overhead_factor = float(image_overhead_factor)
608 except:
609 image_overhead_factor = 1
610 params['image_overhead_factor'] = image_overhead_factor
611
612 params["incompat_license"] = self._remove_redundant(self.runCommand(["getVariable", "INCOMPATIBLE_LICENSE"]) or "")
613 params["sdk_machine"] = self.runCommand(["getVariable", "SDKMACHINE"]) or self.runCommand(["getVariable", "SDK_ARCH"]) or ""
614
615 params["image_fstypes"] = self._remove_redundant(self.runCommand(["getVariable", "IMAGE_FSTYPES"]) or "")
616
617 params["image_types"] = self._remove_redundant(self.runCommand(["getVariable", "IMAGE_TYPES"]) or "")
618
619 params["conf_version"] = self.runCommand(["getVariable", "CONF_VERSION"]) or ""
620 params["lconf_version"] = self.runCommand(["getVariable", "LCONF_VERSION"]) or ""
621
622 params["runnable_image_types"] = self._remove_redundant(self.runCommand(["getVariable", "RUNNABLE_IMAGE_TYPES"]) or "")
623 params["runnable_machine_patterns"] = self._remove_redundant(self.runCommand(["getVariable", "RUNNABLE_MACHINE_PATTERNS"]) or "")
624 params["deployable_image_types"] = self._remove_redundant(self.runCommand(["getVariable", "DEPLOYABLE_IMAGE_TYPES"]) or "")
625 params["kernel_image_type"] = self.runCommand(["getVariable", "KERNEL_IMAGETYPE"]) or ""
626 params["tmpdir"] = self.runCommand(["getVariable", "TMPDIR"]) or ""
627 params["distro_version"] = self.runCommand(["getVariable", "DISTRO_VERSION"]) or ""
628 params["target_os"] = self.runCommand(["getVariable", "TARGET_OS"]) or ""
629 params["target_arch"] = self.runCommand(["getVariable", "TARGET_ARCH"]) or ""
630 params["tune_pkgarch"] = self.runCommand(["getVariable", "TUNE_PKGARCH"]) or ""
631 params["bb_version"] = self.runCommand(["getVariable", "BB_MIN_VERSION"]) or ""
632
633 params["default_task"] = self.runCommand(["getVariable", "BB_DEFAULT_TASK"]) or "build"
634
635 params["socks_proxy"] = self.runCommand(["getVariable", "all_proxy"]) or ""
636 params["http_proxy"] = self.runCommand(["getVariable", "http_proxy"]) or ""
637 params["ftp_proxy"] = self.runCommand(["getVariable", "ftp_proxy"]) or ""
638 params["https_proxy"] = self.runCommand(["getVariable", "https_proxy"]) or ""
639
640 params["cvs_proxy_host"] = self.runCommand(["getVariable", "CVS_PROXY_HOST"]) or ""
641 params["cvs_proxy_port"] = self.runCommand(["getVariable", "CVS_PROXY_PORT"]) or ""
642
643 params["image_white_pattern"] = self.runCommand(["getVariable", "BBUI_IMAGE_WHITE_PATTERN"]) or ""
644 params["image_black_pattern"] = self.runCommand(["getVariable", "BBUI_IMAGE_BLACK_PATTERN"]) or ""
645 return params
diff --git a/bitbake/lib/bb/ui/crumbs/hoblistmodel.py b/bitbake/lib/bb/ui/crumbs/hoblistmodel.py
deleted file mode 100644
index 50df156f4d..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hoblistmodel.py
+++ /dev/null
@@ -1,903 +0,0 @@
1#
2# BitBake Graphical GTK User Interface
3#
4# Copyright (C) 2011 Intel Corporation
5#
6# Authored by Joshua Lock <josh@linux.intel.com>
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 gobject
25from bb.ui.crumbs.hobpages import HobPage
26
27#
28# PackageListModel
29#
30class PackageListModel(gtk.ListStore):
31 """
32 This class defines an gtk.ListStore subclass which will convert the output
33 of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also
34 providing convenience functions to access gtk.TreeModel subclasses which
35 provide filtered views of the data.
36 """
37
38 (COL_NAME, COL_VER, COL_REV, COL_RNM, COL_SEC, COL_SUM, COL_RDEP, COL_RPROV, COL_SIZE, COL_RCP, COL_BINB, COL_INC, COL_FADE_INC, COL_FONT, COL_FLIST) = range(15)
39
40 __gsignals__ = {
41 "package-selection-changed" : (gobject.SIGNAL_RUN_LAST,
42 gobject.TYPE_NONE,
43 ()),
44 }
45
46 __toolchain_required_packages__ = ["packagegroup-core-standalone-sdk-target", "packagegroup-core-standalone-sdk-target-dbg"]
47
48 def __init__(self):
49 self.rprov_pkg = {}
50 gtk.ListStore.__init__ (self,
51 gobject.TYPE_STRING,
52 gobject.TYPE_STRING,
53 gobject.TYPE_STRING,
54 gobject.TYPE_STRING,
55 gobject.TYPE_STRING,
56 gobject.TYPE_STRING,
57 gobject.TYPE_STRING,
58 gobject.TYPE_STRING,
59 gobject.TYPE_STRING,
60 gobject.TYPE_STRING,
61 gobject.TYPE_STRING,
62 gobject.TYPE_BOOLEAN,
63 gobject.TYPE_BOOLEAN,
64 gobject.TYPE_STRING,
65 gobject.TYPE_STRING)
66 self.sort_column_id, self.sort_order = PackageListModel.COL_NAME, gtk.SORT_ASCENDING
67
68 """
69 Find the model path for the item_name
70 Returns the path in the model or None
71 """
72 def find_path_for_item(self, item_name):
73 pkg = item_name
74 if item_name not in self.pn_path.keys():
75 if item_name not in self.rprov_pkg.keys():
76 return None
77 pkg = self.rprov_pkg[item_name]
78 if pkg not in self.pn_path.keys():
79 return None
80
81 return self.pn_path[pkg]
82
83 def find_item_for_path(self, item_path):
84 return self[item_path][self.COL_NAME]
85
86 """
87 Helper function to determine whether an item is an item specified by filter
88 """
89 def tree_model_filter(self, model, it, filter):
90 name = model.get_value(it, self.COL_NAME)
91
92 for key in filter.keys():
93 if key == self.COL_NAME:
94 if filter[key] != 'Search packages by name':
95 if name and filter[key] not in name:
96 return False
97 else:
98 if model.get_value(it, key) not in filter[key]:
99 return False
100 self.filtered_nb += 1
101 return True
102
103 """
104 Create, if required, and return a filtered gtk.TreeModelSort
105 containing only the items specified by filter
106 """
107 def tree_model(self, filter, excluded_items_ahead=False, included_items_ahead=False, search_data=None, initial=False):
108 model = self.filter_new()
109 self.filtered_nb = 0
110 model.set_visible_func(self.tree_model_filter, filter)
111
112 sort = gtk.TreeModelSort(model)
113 sort.connect ('sort-column-changed', self.sort_column_changed_cb)
114 if initial:
115 sort.set_sort_column_id(PackageListModel.COL_NAME, gtk.SORT_ASCENDING)
116 sort.set_default_sort_func(None)
117 elif excluded_items_ahead:
118 sort.set_default_sort_func(self.exclude_item_sort_func, search_data)
119 elif included_items_ahead:
120 sort.set_default_sort_func(self.include_item_sort_func, search_data)
121 else:
122 if search_data and search_data!='Search recipes by name' and search_data!='Search package groups by name':
123 sort.set_default_sort_func(self.sort_func, search_data)
124 else:
125 sort.set_sort_column_id(self.sort_column_id, self.sort_order)
126 sort.set_default_sort_func(None)
127
128 sort.set_sort_func(PackageListModel.COL_INC, self.sort_column, PackageListModel.COL_INC)
129 sort.set_sort_func(PackageListModel.COL_SIZE, self.sort_column, PackageListModel.COL_SIZE)
130 sort.set_sort_func(PackageListModel.COL_BINB, self.sort_binb_column)
131 sort.set_sort_func(PackageListModel.COL_RCP, self.sort_column, PackageListModel.COL_RCP)
132 return sort
133
134 def sort_column_changed_cb (self, data):
135 self.sort_column_id, self.sort_order = data.get_sort_column_id ()
136
137 def sort_column(self, model, row1, row2, col):
138 value1 = model.get_value(row1, col)
139 value2 = model.get_value(row2, col)
140 if col==PackageListModel.COL_SIZE:
141 value1 = HobPage._string_to_size(value1)
142 value2 = HobPage._string_to_size(value2)
143
144 cmp_res = cmp(value1, value2)
145 if cmp_res!=0:
146 if col==PackageListModel.COL_INC:
147 return -cmp_res
148 else:
149 return cmp_res
150 else:
151 name1 = model.get_value(row1, PackageListModel.COL_NAME)
152 name2 = model.get_value(row2, PackageListModel.COL_NAME)
153 return cmp(name1,name2)
154
155 def sort_binb_column(self, model, row1, row2):
156 value1 = model.get_value(row1, PackageListModel.COL_BINB)
157 value2 = model.get_value(row2, PackageListModel.COL_BINB)
158 value1_list = value1.split(', ')
159 value2_list = value2.split(', ')
160
161 value1 = value1_list[0]
162 value2 = value2_list[0]
163
164 cmp_res = cmp(value1, value2)
165 if cmp_res==0:
166 cmp_size = cmp(len(value1_list), len(value2_list))
167 if cmp_size==0:
168 name1 = model.get_value(row1, PackageListModel.COL_NAME)
169 name2 = model.get_value(row2, PackageListModel.COL_NAME)
170 return cmp(name1,name2)
171 else:
172 return cmp_size
173 else:
174 return cmp_res
175
176 def exclude_item_sort_func(self, model, iter1, iter2, user_data=None):
177 if user_data:
178 val1 = model.get_value(iter1, PackageListModel.COL_NAME)
179 val2 = model.get_value(iter2, PackageListModel.COL_NAME)
180 return self.cmp_vals(val1, val2, user_data)
181 else:
182 val1 = model.get_value(iter1, PackageListModel.COL_FADE_INC)
183 val2 = model.get_value(iter2, PackageListModel.COL_INC)
184 return ((val1 == True) and (val2 == False))
185
186 def include_item_sort_func(self, model, iter1, iter2, user_data=None):
187 if user_data:
188 val1 = model.get_value(iter1, PackageListModel.COL_NAME)
189 val2 = model.get_value(iter2, PackageListModel.COL_NAME)
190 return self.cmp_vals(val1, val2, user_data)
191 else:
192 val1 = model.get_value(iter1, PackageListModel.COL_INC)
193 val2 = model.get_value(iter2, PackageListModel.COL_INC)
194 return ((val1 == False) and (val2 == True))
195
196 def sort_func(self, model, iter1, iter2, user_data):
197 val1 = model.get_value(iter1, PackageListModel.COL_NAME)
198 val2 = model.get_value(iter2, PackageListModel.COL_NAME)
199 return self.cmp_vals(val1, val2, user_data)
200
201 def cmp_vals(self, val1, val2, user_data):
202 if val1 is None or val2 is None:
203 return 0
204 elif val1.startswith(user_data) and not val2.startswith(user_data):
205 return -1
206 elif not val1.startswith(user_data) and val2.startswith(user_data):
207 return 1
208 else:
209 return cmp(val1, val2)
210
211 def convert_vpath_to_path(self, view_model, view_path):
212 # view_model is the model sorted
213 # get the path of the model filtered
214 filtered_model_path = view_model.convert_path_to_child_path(view_path)
215 # get the model filtered
216 filtered_model = view_model.get_model()
217 # get the path of the original model
218 path = filtered_model.convert_path_to_child_path(filtered_model_path)
219 return path
220
221 def convert_path_to_vpath(self, view_model, path):
222 it = view_model.get_iter_first()
223 while it:
224 name = self.find_item_for_path(path)
225 view_name = view_model.get_value(it, PackageListModel.COL_NAME)
226 if view_name == name:
227 view_path = view_model.get_path(it)
228 return view_path
229 it = view_model.iter_next(it)
230 return None
231
232 """
233 The populate() function takes as input the data from a
234 bb.event.PackageInfo event and populates the package list.
235 """
236 def populate(self, pkginfolist):
237 # First clear the model, in case repopulating
238 self.clear()
239
240 def getpkgvalue(pkgdict, key, pkgname, defaultval = None):
241 value = pkgdict.get('%s_%s' % (key, pkgname), None)
242 if not value:
243 value = pkgdict.get(key, defaultval)
244 return value
245
246 for pkginfo in pkginfolist:
247 pn = pkginfo['PN']
248 pv = pkginfo['PV']
249 pr = pkginfo['PR']
250 pkg = pkginfo['PKG']
251 pkgv = getpkgvalue(pkginfo, 'PKGV', pkg)
252 pkgr = getpkgvalue(pkginfo, 'PKGR', pkg)
253 # PKGSIZE is artificial, will always be overridden with the package name if present
254 pkgsize = int(pkginfo.get('PKGSIZE_%s' % pkg, "0"))
255 # PKG_%s is the renamed version
256 pkg_rename = pkginfo.get('PKG_%s' % pkg, "")
257 # The rest may be overridden or not
258 section = getpkgvalue(pkginfo, 'SECTION', pkg, "")
259 summary = getpkgvalue(pkginfo, 'SUMMARY', pkg, "")
260 rdep = getpkgvalue(pkginfo, 'RDEPENDS', pkg, "")
261 rrec = getpkgvalue(pkginfo, 'RRECOMMENDS', pkg, "")
262 rprov = getpkgvalue(pkginfo, 'RPROVIDES', pkg, "")
263 files_list = getpkgvalue(pkginfo, 'FILES_INFO', pkg, "")
264 for i in rprov.split():
265 self.rprov_pkg[i] = pkg
266
267 recipe = pn + '-' + pv + '-' + pr
268
269 allow_empty = getpkgvalue(pkginfo, 'ALLOW_EMPTY', pkg, "")
270
271 if pkgsize == 0 and not allow_empty:
272 continue
273
274 size = HobPage._size_to_string(pkgsize)
275 self.set(self.append(), self.COL_NAME, pkg, self.COL_VER, pkgv,
276 self.COL_REV, pkgr, self.COL_RNM, pkg_rename,
277 self.COL_SEC, section, self.COL_SUM, summary,
278 self.COL_RDEP, rdep + ' ' + rrec,
279 self.COL_RPROV, rprov, self.COL_SIZE, size,
280 self.COL_RCP, recipe, self.COL_BINB, "",
281 self.COL_INC, False, self.COL_FONT, '10', self.COL_FLIST, files_list)
282
283 self.pn_path = {}
284 it = self.get_iter_first()
285 while it:
286 pn = self.get_value(it, self.COL_NAME)
287 path = self.get_path(it)
288 self.pn_path[pn] = path
289 it = self.iter_next(it)
290
291 """
292 Update the model, send out the notification.
293 """
294 def selection_change_notification(self):
295 self.emit("package-selection-changed")
296
297 """
298 Check whether the item at item_path is included or not
299 """
300 def path_included(self, item_path):
301 return self[item_path][self.COL_INC]
302
303 """
304 Add this item, and any of its dependencies, to the image contents
305 """
306 def include_item(self, item_path, binb=""):
307 if self.path_included(item_path):
308 return
309
310 item_name = self[item_path][self.COL_NAME]
311 item_deps = self[item_path][self.COL_RDEP]
312
313 self[item_path][self.COL_INC] = True
314
315 item_bin = self[item_path][self.COL_BINB].split(', ')
316 if binb and not binb in item_bin:
317 item_bin.append(binb)
318 self[item_path][self.COL_BINB] = ', '.join(item_bin).lstrip(', ')
319
320 if item_deps:
321 # Ensure all of the items deps are included and, where appropriate,
322 # add this item to their COL_BINB
323 for dep in item_deps.split(" "):
324 if dep.startswith('('):
325 continue
326 # If the contents model doesn't already contain dep, add it
327 dep_path = self.find_path_for_item(dep)
328 if not dep_path:
329 continue
330 dep_included = self.path_included(dep_path)
331
332 if dep_included and not dep in item_bin:
333 # don't set the COL_BINB to this item if the target is an
334 # item in our own COL_BINB
335 dep_bin = self[dep_path][self.COL_BINB].split(', ')
336 if not item_name in dep_bin:
337 dep_bin.append(item_name)
338 self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ')
339 elif not dep_included:
340 self.include_item(dep_path, binb=item_name)
341
342 def exclude_item(self, item_path):
343 if not self.path_included(item_path):
344 return
345
346 self[item_path][self.COL_INC] = False
347
348 item_name = self[item_path][self.COL_NAME]
349 item_deps = self[item_path][self.COL_RDEP]
350 if item_deps:
351 for dep in item_deps.split(" "):
352 if dep.startswith('('):
353 continue
354 dep_path = self.find_path_for_item(dep)
355 if not dep_path:
356 continue
357 dep_bin = self[dep_path][self.COL_BINB].split(', ')
358 if item_name in dep_bin:
359 dep_bin.remove(item_name)
360 self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ')
361
362 item_bin = self[item_path][self.COL_BINB].split(', ')
363 if item_bin:
364 for binb in item_bin:
365 binb_path = self.find_path_for_item(binb)
366 if not binb_path:
367 continue
368 self.exclude_item(binb_path)
369
370 """
371 Empty self.contents by setting the include of each entry to None
372 """
373 def reset(self):
374 it = self.get_iter_first()
375 while it:
376 self.set(it,
377 self.COL_INC, False,
378 self.COL_BINB, "")
379 it = self.iter_next(it)
380
381 self.selection_change_notification()
382
383 def get_selected_packages(self):
384 packagelist = []
385
386 it = self.get_iter_first()
387 while it:
388 if self.get_value(it, self.COL_INC):
389 name = self.get_value(it, self.COL_NAME)
390 packagelist.append(name)
391 it = self.iter_next(it)
392
393 return packagelist
394
395 def get_user_selected_packages(self):
396 packagelist = []
397
398 it = self.get_iter_first()
399 while it:
400 if self.get_value(it, self.COL_INC):
401 binb = self.get_value(it, self.COL_BINB)
402 if binb == "User Selected":
403 name = self.get_value(it, self.COL_NAME)
404 packagelist.append(name)
405 it = self.iter_next(it)
406
407 return packagelist
408
409 def get_selected_packages_toolchain(self):
410 packagelist = []
411
412 it = self.get_iter_first()
413 while it:
414 if self.get_value(it, self.COL_INC):
415 name = self.get_value(it, self.COL_NAME)
416 if name.endswith("-dev") or name.endswith("-dbg"):
417 packagelist.append(name)
418 it = self.iter_next(it)
419
420 return list(set(packagelist + self.__toolchain_required_packages__));
421
422 """
423 Package model may be incomplete, therefore when calling the
424 set_selected_packages(), some packages will not be set included.
425 Return the un-set packages list.
426 """
427 def set_selected_packages(self, packagelist, user_selected=False):
428 left = []
429 binb = 'User Selected' if user_selected else ''
430 for pn in packagelist:
431 if pn in self.pn_path.keys():
432 path = self.pn_path[pn]
433 self.include_item(item_path=path, binb=binb)
434 else:
435 left.append(pn)
436
437 self.selection_change_notification()
438 return left
439
440 """
441 Return the selected package size, unit is B.
442 """
443 def get_packages_size(self):
444 packages_size = 0
445 it = self.get_iter_first()
446 while it:
447 if self.get_value(it, self.COL_INC):
448 str_size = self.get_value(it, self.COL_SIZE)
449 if not str_size:
450 continue
451
452 packages_size += HobPage._string_to_size(str_size)
453
454 it = self.iter_next(it)
455 return packages_size
456
457 """
458 Resync the state of included items to a backup column before performing the fadeout visible effect
459 """
460 def resync_fadeout_column(self, model_first_iter=None):
461 it = model_first_iter
462 while it:
463 active = self.get_value(it, self.COL_INC)
464 self.set(it, self.COL_FADE_INC, active)
465 it = self.iter_next(it)
466
467#
468# RecipeListModel
469#
470class RecipeListModel(gtk.ListStore):
471 """
472 This class defines an gtk.ListStore subclass which will convert the output
473 of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also
474 providing convenience functions to access gtk.TreeModel subclasses which
475 provide filtered views of the data.
476 """
477 (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_INSTALL, COL_PN, COL_FADE_INC, COL_SUMMARY, COL_VERSION,
478 COL_REVISION, COL_HOMEPAGE, COL_BUGTRACKER, COL_FILE) = range(18)
479
480 __custom_image__ = "Start with an empty image recipe"
481
482 __gsignals__ = {
483 "recipe-selection-changed" : (gobject.SIGNAL_RUN_LAST,
484 gobject.TYPE_NONE,
485 ()),
486 }
487
488 """
489 """
490 def __init__(self):
491 gtk.ListStore.__init__ (self,
492 gobject.TYPE_STRING,
493 gobject.TYPE_STRING,
494 gobject.TYPE_STRING,
495 gobject.TYPE_STRING,
496 gobject.TYPE_STRING,
497 gobject.TYPE_STRING,
498 gobject.TYPE_STRING,
499 gobject.TYPE_BOOLEAN,
500 gobject.TYPE_BOOLEAN,
501 gobject.TYPE_STRING,
502 gobject.TYPE_STRING,
503 gobject.TYPE_BOOLEAN,
504 gobject.TYPE_STRING,
505 gobject.TYPE_STRING,
506 gobject.TYPE_STRING,
507 gobject.TYPE_STRING,
508 gobject.TYPE_STRING,
509 gobject.TYPE_STRING)
510 self.sort_column_id, self.sort_order = RecipeListModel.COL_NAME, gtk.SORT_ASCENDING
511
512 """
513 Find the model path for the item_name
514 Returns the path in the model or None
515 """
516 def find_path_for_item(self, item_name):
517 if self.non_target_name(item_name) or item_name not in self.pn_path.keys():
518 return None
519 else:
520 return self.pn_path[item_name]
521
522 def find_item_for_path(self, item_path):
523 return self[item_path][self.COL_NAME]
524
525 """
526 Helper method to determine whether name is a target pn
527 """
528 def non_target_name(self, name):
529 if name and ('-native' in name):
530 return True
531 return False
532
533 """
534 Helper function to determine whether an item is an item specified by filter
535 """
536 def tree_model_filter(self, model, it, filter):
537 name = model.get_value(it, self.COL_NAME)
538 if self.non_target_name(name):
539 return False
540
541 for key in filter.keys():
542 if key == self.COL_NAME:
543 if filter[key] != 'Search recipes by name' and filter[key] != 'Search package groups by name':
544 if filter[key] not in name:
545 return False
546 else:
547 if model.get_value(it, key) not in filter[key]:
548 return False
549 self.filtered_nb += 1
550
551 return True
552
553 def exclude_item_sort_func(self, model, iter1, iter2, user_data=None):
554 if user_data:
555 val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
556 val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
557 return self.cmp_vals(val1, val2, user_data)
558 else:
559 val1 = model.get_value(iter1, RecipeListModel.COL_FADE_INC)
560 val2 = model.get_value(iter2, RecipeListModel.COL_INC)
561 return ((val1 == True) and (val2 == False))
562
563 def include_item_sort_func(self, model, iter1, iter2, user_data=None):
564 if user_data:
565 val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
566 val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
567 return self.cmp_vals(val1, val2, user_data)
568 else:
569 val1 = model.get_value(iter1, RecipeListModel.COL_INC)
570 val2 = model.get_value(iter2, RecipeListModel.COL_INC)
571 return ((val1 == False) and (val2 == True))
572
573 def sort_func(self, model, iter1, iter2, user_data):
574 val1 = model.get_value(iter1, RecipeListModel.COL_NAME)
575 val2 = model.get_value(iter2, RecipeListModel.COL_NAME)
576 return self.cmp_vals(val1, val2, user_data)
577
578 def cmp_vals(self, val1, val2, user_data):
579 if val1 is None or val2 is None:
580 return 0
581 elif val1.startswith(user_data) and not val2.startswith(user_data):
582 return -1
583 elif not val1.startswith(user_data) and val2.startswith(user_data):
584 return 1
585 else:
586 return cmp(val1, val2)
587
588 """
589 Create, if required, and return a filtered gtk.TreeModelSort
590 containing only the items specified by filter
591 """
592 def tree_model(self, filter, excluded_items_ahead=False, included_items_ahead=False, search_data=None, initial=False):
593 model = self.filter_new()
594 self.filtered_nb = 0
595 model.set_visible_func(self.tree_model_filter, filter)
596
597 sort = gtk.TreeModelSort(model)
598 sort.connect ('sort-column-changed', self.sort_column_changed_cb)
599 if initial:
600 sort.set_sort_column_id(RecipeListModel.COL_NAME, gtk.SORT_ASCENDING)
601 sort.set_default_sort_func(None)
602 elif excluded_items_ahead:
603 sort.set_default_sort_func(self.exclude_item_sort_func, search_data)
604 elif included_items_ahead:
605 sort.set_default_sort_func(self.include_item_sort_func, search_data)
606 else:
607 if search_data and search_data!='Search recipes by name' and search_data!='Search package groups by name':
608 sort.set_default_sort_func(self.sort_func, search_data)
609 else:
610 sort.set_sort_column_id(self.sort_column_id, self.sort_order)
611 sort.set_default_sort_func(None)
612
613 sort.set_sort_func(RecipeListModel.COL_INC, self.sort_column, RecipeListModel.COL_INC)
614 sort.set_sort_func(RecipeListModel.COL_GROUP, self.sort_column, RecipeListModel.COL_GROUP)
615 sort.set_sort_func(RecipeListModel.COL_BINB, self.sort_binb_column)
616 sort.set_sort_func(RecipeListModel.COL_LIC, self.sort_column, RecipeListModel.COL_LIC)
617 return sort
618
619 def sort_column_changed_cb (self, data):
620 self.sort_column_id, self.sort_order = data.get_sort_column_id ()
621
622 def sort_column(self, model, row1, row2, col):
623 value1 = model.get_value(row1, col)
624 value2 = model.get_value(row2, col)
625 cmp_res = cmp(value1, value2)
626 if cmp_res!=0:
627 if col==RecipeListModel.COL_INC:
628 return -cmp_res
629 else:
630 return cmp_res
631 else:
632 name1 = model.get_value(row1, RecipeListModel.COL_NAME)
633 name2 = model.get_value(row2, RecipeListModel.COL_NAME)
634 return cmp(name1,name2)
635
636 def sort_binb_column(self, model, row1, row2):
637 value1 = model.get_value(row1, RecipeListModel.COL_BINB)
638 value2 = model.get_value(row2, RecipeListModel.COL_BINB)
639 value1_list = value1.split(', ')
640 value2_list = value2.split(', ')
641
642 value1 = value1_list[0]
643 value2 = value2_list[0]
644
645 cmp_res = cmp(value1, value2)
646 if cmp_res==0:
647 cmp_size = cmp(len(value1_list), len(value2_list))
648 if cmp_size==0:
649 name1 = model.get_value(row1, RecipeListModel.COL_NAME)
650 name2 = model.get_value(row2, RecipeListModel.COL_NAME)
651 return cmp(name1,name2)
652 else:
653 return cmp_size
654 else:
655 return cmp_res
656
657 def convert_vpath_to_path(self, view_model, view_path):
658 filtered_model_path = view_model.convert_path_to_child_path(view_path)
659 filtered_model = view_model.get_model()
660
661 # get the path of the original model
662 path = filtered_model.convert_path_to_child_path(filtered_model_path)
663 return path
664
665 def convert_path_to_vpath(self, view_model, path):
666 it = view_model.get_iter_first()
667 while it:
668 name = self.find_item_for_path(path)
669 view_name = view_model.get_value(it, RecipeListModel.COL_NAME)
670 if view_name == name:
671 view_path = view_model.get_path(it)
672 return view_path
673 it = view_model.iter_next(it)
674 return None
675
676 """
677 The populate() function takes as input the data from a
678 bb.event.TargetsTreeGenerated event and populates the RecipeList.
679 """
680 def populate(self, event_model):
681 # First clear the model, in case repopulating
682 self.clear()
683
684 # dummy image for prompt
685 self.set_in_list(self.__custom_image__, "Use 'Edit image recipe' to customize recipes and packages " \
686 "to be included in your image ")
687
688 for item in event_model["pn"]:
689 name = item
690 desc = event_model["pn"][item]["description"]
691 lic = event_model["pn"][item]["license"]
692 group = event_model["pn"][item]["section"]
693 inherits = event_model["pn"][item]["inherits"]
694 summary = event_model["pn"][item]["summary"]
695 version = event_model["pn"][item]["version"]
696 revision = event_model["pn"][item]["prevision"]
697 homepage = event_model["pn"][item]["homepage"]
698 bugtracker = event_model["pn"][item]["bugtracker"]
699 filename = event_model["pn"][item]["filename"]
700 install = []
701
702 depends = event_model["depends"].get(item, []) + event_model["rdepends-pn"].get(item, [])
703
704 if ('packagegroup.bbclass' in " ".join(inherits)):
705 atype = 'packagegroup'
706 elif ('/image.bbclass' in " ".join(inherits)):
707 if "edited" not in name:
708 atype = 'image'
709 install = event_model["rdepends-pkg"].get(item, []) + event_model["rrecs-pkg"].get(item, [])
710 elif ('meta-' in name):
711 atype = 'toolchain'
712 elif (name == 'dummy-image' or name == 'dummy-toolchain'):
713 atype = 'dummy'
714 else:
715 atype = 'recipe'
716
717 self.set(self.append(), self.COL_NAME, item, self.COL_DESC, desc,
718 self.COL_LIC, lic, self.COL_GROUP, group,
719 self.COL_DEPS, " ".join(depends), self.COL_BINB, "",
720 self.COL_TYPE, atype, self.COL_INC, False,
721 self.COL_IMG, False, self.COL_INSTALL, " ".join(install), self.COL_PN, item,
722 self.COL_SUMMARY, summary, self.COL_VERSION, version, self.COL_REVISION, revision,
723 self.COL_HOMEPAGE, homepage, self.COL_BUGTRACKER, bugtracker,
724 self.COL_FILE, filename)
725
726 self.pn_path = {}
727 it = self.get_iter_first()
728 while it:
729 pn = self.get_value(it, self.COL_NAME)
730 path = self.get_path(it)
731 self.pn_path[pn] = path
732 it = self.iter_next(it)
733
734 def set_in_list(self, item, desc):
735 self.set(self.append(), self.COL_NAME, item,
736 self.COL_DESC, desc,
737 self.COL_LIC, "", self.COL_GROUP, "",
738 self.COL_DEPS, "", self.COL_BINB, "",
739 self.COL_TYPE, "image", self.COL_INC, False,
740 self.COL_IMG, False, self.COL_INSTALL, "", self.COL_PN, item,
741 self.COL_SUMMARY, "", self.COL_VERSION, "", self.COL_REVISION, "",
742 self.COL_HOMEPAGE, "", self.COL_BUGTRACKER, "")
743 self.pn_path = {}
744 it = self.get_iter_first()
745 while it:
746 pn = self.get_value(it, self.COL_NAME)
747 path = self.get_path(it)
748 self.pn_path[pn] = path
749 it = self.iter_next(it)
750
751 """
752 Update the model, send out the notification.
753 """
754 def selection_change_notification(self):
755 self.emit("recipe-selection-changed")
756
757 def path_included(self, item_path):
758 return self[item_path][self.COL_INC]
759
760 """
761 Add this item, and any of its dependencies, to the image contents
762 """
763 def include_item(self, item_path, binb="", image_contents=False):
764 if self.path_included(item_path):
765 return
766
767 item_name = self[item_path][self.COL_NAME]
768 item_deps = self[item_path][self.COL_DEPS]
769
770 self[item_path][self.COL_INC] = True
771
772 item_bin = self[item_path][self.COL_BINB].split(', ')
773 if binb and not binb in item_bin:
774 item_bin.append(binb)
775 self[item_path][self.COL_BINB] = ', '.join(item_bin).lstrip(', ')
776
777 # We want to do some magic with things which are brought in by the
778 # base image so tag them as so
779 if image_contents:
780 self[item_path][self.COL_IMG] = True
781
782 if item_deps:
783 # Ensure all of the items deps are included and, where appropriate,
784 # add this item to their COL_BINB
785 for dep in item_deps.split(" "):
786 # If the contents model doesn't already contain dep, add it
787 dep_path = self.find_path_for_item(dep)
788 if not dep_path:
789 continue
790 dep_included = self.path_included(dep_path)
791
792 if dep_included and not dep in item_bin:
793 # don't set the COL_BINB to this item if the target is an
794 # item in our own COL_BINB
795 dep_bin = self[dep_path][self.COL_BINB].split(', ')
796 if not item_name in dep_bin:
797 dep_bin.append(item_name)
798 self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ')
799 elif not dep_included:
800 self.include_item(dep_path, binb=item_name, image_contents=image_contents)
801 dep_bin = self[item_path][self.COL_BINB].split(', ')
802 if self[item_path][self.COL_NAME] in dep_bin:
803 dep_bin.remove(self[item_path][self.COL_NAME])
804 self[item_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ')
805
806 def exclude_item(self, item_path):
807 if not self.path_included(item_path):
808 return
809
810 self[item_path][self.COL_INC] = False
811
812 item_name = self[item_path][self.COL_NAME]
813 item_deps = self[item_path][self.COL_DEPS]
814 if item_deps:
815 for dep in item_deps.split(" "):
816 dep_path = self.find_path_for_item(dep)
817 if not dep_path:
818 continue
819 dep_bin = self[dep_path][self.COL_BINB].split(', ')
820 if item_name in dep_bin:
821 dep_bin.remove(item_name)
822 self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ')
823
824 item_bin = self[item_path][self.COL_BINB].split(', ')
825 if item_bin:
826 for binb in item_bin:
827 binb_path = self.find_path_for_item(binb)
828 if not binb_path:
829 continue
830 self.exclude_item(binb_path)
831
832 def reset(self):
833 it = self.get_iter_first()
834 while it:
835 self.set(it,
836 self.COL_INC, False,
837 self.COL_BINB, "",
838 self.COL_IMG, False)
839 it = self.iter_next(it)
840
841 self.selection_change_notification()
842
843 """
844 Returns two lists. One of user selected recipes and the other containing
845 all selected recipes
846 """
847 def get_selected_recipes(self):
848 allrecipes = []
849 userrecipes = []
850
851 it = self.get_iter_first()
852 while it:
853 if self.get_value(it, self.COL_INC):
854 name = self.get_value(it, self.COL_PN)
855 type = self.get_value(it, self.COL_TYPE)
856 if type != "image":
857 allrecipes.append(name)
858 sel = "User Selected" in self.get_value(it, self.COL_BINB)
859 if sel:
860 userrecipes.append(name)
861 it = self.iter_next(it)
862
863 return list(set(userrecipes)), list(set(allrecipes))
864
865 def set_selected_recipes(self, recipelist):
866 for pn in recipelist:
867 if pn in self.pn_path.keys():
868 path = self.pn_path[pn]
869 self.include_item(item_path=path,
870 binb="User Selected")
871 self.selection_change_notification()
872
873 def get_selected_image(self):
874 it = self.get_iter_first()
875 while it:
876 if self.get_value(it, self.COL_INC):
877 name = self.get_value(it, self.COL_PN)
878 type = self.get_value(it, self.COL_TYPE)
879 if type == "image":
880 sel = "User Selected" in self.get_value(it, self.COL_BINB)
881 if sel:
882 return name
883 it = self.iter_next(it)
884 return None
885
886 def set_selected_image(self, img):
887 if not img:
888 return
889 self.reset()
890 path = self.find_path_for_item(img)
891 self.include_item(item_path=path,
892 binb="User Selected",
893 image_contents=True)
894 self.selection_change_notification()
895
896 def set_custom_image_version(self, version):
897 self.custom_image_version = version
898
899 def get_custom_image_version(self):
900 return self.custom_image_version
901
902 def is_custom_image(self):
903 return self.get_selected_image() == self.__custom_image__
diff --git a/bitbake/lib/bb/ui/crumbs/hobpages.py b/bitbake/lib/bb/ui/crumbs/hobpages.py
deleted file mode 100755
index 0fd3598c3a..0000000000
--- a/bitbake/lib/bb/ui/crumbs/hobpages.py
+++ /dev/null
@@ -1,128 +0,0 @@
1#!/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
24from bb.ui.crumbs.hobcolor import HobColors
25from bb.ui.crumbs.hobwidget import hwc
26
27#
28# HobPage: the super class for all Hob-related pages
29#
30class HobPage (gtk.VBox):
31
32 def __init__(self, builder, title = None):
33 super(HobPage, self).__init__(False, 0)
34 self.builder = builder
35 self.builder_width, self.builder_height = self.builder.size_request()
36
37 if not title:
38 self.title = "Hob -- Image Creator"
39 else:
40 self.title = title
41 self.title_label = gtk.Label()
42
43 self.box_group_area = gtk.VBox(False, 12)
44 self.box_group_area.set_size_request(self.builder_width - 73 - 73, self.builder_height - 88 - 15 - 15)
45 self.group_align = gtk.Alignment(xalign = 0, yalign=0.5, xscale=1, yscale=1)
46 self.group_align.set_padding(15, 15, 73, 73)
47 self.group_align.add(self.box_group_area)
48 self.box_group_area.set_homogeneous(False)
49
50 def set_title(self, title):
51 self.title = title
52 self.title_label.set_markup("<span size='x-large'>%s</span>" % self.title)
53
54 def add_onto_top_bar(self, widget = None, padding = 0):
55 # the top button occupies 1/7 of the page height
56 # setup an event box
57 eventbox = gtk.EventBox()
58 style = eventbox.get_style().copy()
59 style.bg[gtk.STATE_NORMAL] = eventbox.get_colormap().alloc_color(HobColors.LIGHT_GRAY, False, False)
60 eventbox.set_style(style)
61 eventbox.set_size_request(-1, 88)
62
63 hbox = gtk.HBox()
64
65 self.title_label = gtk.Label()
66 self.title_label.set_markup("<span size='x-large'>%s</span>" % self.title)
67 hbox.pack_start(self.title_label, expand=False, fill=False, padding=20)
68
69 if widget:
70 # add the widget in the event box
71 hbox.pack_end(widget, expand=False, fill=False, padding=padding)
72 eventbox.add(hbox)
73
74 return eventbox
75
76 def span_tag(self, size="medium", weight="normal", forground="#1c1c1c"):
77 span_tag = "weight='%s' foreground='%s' size='%s'" % (weight, forground, size)
78 return span_tag
79
80 def append_toolbar_button(self, toolbar, buttonname, icon_disp, icon_hovor, tip, cb):
81 # Create a button and append it on the toolbar according to button name
82 icon = gtk.Image()
83 icon_display = icon_disp
84 icon_hover = icon_hovor
85 pix_buffer = gtk.gdk.pixbuf_new_from_file(icon_display)
86 icon.set_from_pixbuf(pix_buffer)
87 tip_text = tip
88 button = toolbar.append_item(buttonname, tip, None, icon, cb)
89 return button
90
91 @staticmethod
92 def _size_to_string(size):
93 try:
94 if not size:
95 size_str = "0 B"
96 else:
97 if len(str(int(size))) > 6:
98 size_str = '%.1f' % (size*1.0/(1024*1024)) + ' MB'
99 elif len(str(int(size))) > 3:
100 size_str = '%.1f' % (size*1.0/1024) + ' KB'
101 else:
102 size_str = str(size) + ' B'
103 except:
104 size_str = "0 B"
105 return size_str
106
107 @staticmethod
108 def _string_to_size(str_size):
109 try:
110 if not str_size:
111 size = 0
112 else:
113 unit = str_size.split()
114 if len(unit) > 1:
115 if unit[1] == 'MB':
116 size = float(unit[0])*1024*1024
117 elif unit[1] == 'KB':
118 size = float(unit[0])*1024
119 elif unit[1] == 'B':
120 size = float(unit[0])
121 else:
122 size = 0
123 else:
124 size = float(unit[0])
125 except:
126 size = 0
127 return size
128
diff --git a/bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py b/bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py
deleted file mode 100644
index 2766bea8c7..0000000000
--- a/bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py
+++ /dev/null
@@ -1,561 +0,0 @@
1#!/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 glib
25import re
26from bb.ui.crumbs.progressbar import HobProgressBar
27from bb.ui.crumbs.hobcolor import HobColors
28from bb.ui.crumbs.hobwidget import hic, HobImageButton, HobInfoButton, HobAltButton, HobButton
29from bb.ui.crumbs.hoblistmodel import RecipeListModel
30from bb.ui.crumbs.hobpages import HobPage
31from bb.ui.crumbs.hig.retrieveimagedialog import RetrieveImageDialog
32
33#
34# ImageConfigurationPage
35#
36class ImageConfigurationPage (HobPage):
37
38 __dummy_machine__ = "--select a machine--"
39 __dummy_image__ = "--select an image recipe--"
40 __custom_image__ = "Select from my image recipes"
41
42 def __init__(self, builder):
43 super(ImageConfigurationPage, self).__init__(builder, "Image configuration")
44
45 self.image_combo_id = None
46 # we use machine_combo_changed_by_manual to identify the machine is changed by code
47 # or by manual. If by manual, all user's recipe selection and package selection are
48 # cleared.
49 self.machine_combo_changed_by_manual = True
50 self.stopping = False
51 self.warning_shift = 0
52 self.custom_image_selected = None
53 self.create_visual_elements()
54
55 def create_visual_elements(self):
56 # create visual elements
57 self.toolbar = gtk.Toolbar()
58 self.toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
59 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
60
61 my_images_button = self.append_toolbar_button(self.toolbar,
62 "Images",
63 hic.ICON_IMAGES_DISPLAY_FILE,
64 hic.ICON_IMAGES_HOVER_FILE,
65 "Open previously built images",
66 self.my_images_button_clicked_cb)
67 settings_button = self.append_toolbar_button(self.toolbar,
68 "Settings",
69 hic.ICON_SETTINGS_DISPLAY_FILE,
70 hic.ICON_SETTINGS_HOVER_FILE,
71 "View additional build settings",
72 self.settings_button_clicked_cb)
73
74 self.config_top_button = self.add_onto_top_bar(self.toolbar)
75
76 self.gtable = gtk.Table(40, 40, True)
77 self.create_config_machine()
78 self.create_config_baseimg()
79 self.config_build_button = self.create_config_build_button()
80
81 def _remove_all_widget(self):
82 children = self.gtable.get_children() or []
83 for child in children:
84 self.gtable.remove(child)
85 children = self.box_group_area.get_children() or []
86 for child in children:
87 self.box_group_area.remove(child)
88 children = self.get_children() or []
89 for child in children:
90 self.remove(child)
91
92 def _pack_components(self, pack_config_build_button = False):
93 self._remove_all_widget()
94 self.pack_start(self.config_top_button, expand=False, fill=False)
95 self.pack_start(self.group_align, expand=True, fill=True)
96
97 self.box_group_area.pack_start(self.gtable, expand=True, fill=True)
98 if pack_config_build_button:
99 self.box_group_area.pack_end(self.config_build_button, expand=False, fill=False)
100 else:
101 box = gtk.HBox(False, 6)
102 box.show()
103 subbox = gtk.HBox(False, 0)
104 subbox.set_size_request(205, 49)
105 subbox.show()
106 box.add(subbox)
107 self.box_group_area.pack_end(box, False, False)
108
109 def show_machine(self):
110 self.progress_bar.reset()
111 self._pack_components(pack_config_build_button = False)
112 self.set_config_machine_layout(show_progress_bar = False)
113 self.show_all()
114
115 def update_progress_bar(self, title, fraction, status=None):
116 if self.stopping == False:
117 self.progress_bar.update(fraction)
118 self.progress_bar.set_text(title)
119 self.progress_bar.set_rcstyle(status)
120
121 def show_info_populating(self):
122 self._pack_components(pack_config_build_button = False)
123 self.set_config_machine_layout(show_progress_bar = True)
124 self.show_all()
125
126 def show_info_populated(self):
127 self.progress_bar.reset()
128 self._pack_components(pack_config_build_button = False)
129 self.set_config_machine_layout(show_progress_bar = False)
130 self.set_config_baseimg_layout()
131 self.show_all()
132
133 def show_baseimg_selected(self):
134 self.progress_bar.reset()
135 self._pack_components(pack_config_build_button = True)
136 self.set_config_machine_layout(show_progress_bar = False)
137 self.set_config_baseimg_layout()
138 self.show_all()
139 if self.builder.recipe_model.get_selected_image() == self.builder.recipe_model.__custom_image__:
140 self.just_bake_button.hide()
141
142 def add_warnings_bar(self):
143 #create the warnings bar shown when recipes parsing generates warnings
144 color = HobColors.KHAKI
145 warnings_bar = gtk.EventBox()
146 warnings_bar.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
147 warnings_bar.set_flags(gtk.CAN_DEFAULT)
148 warnings_bar.grab_default()
149
150 build_stop_tab = gtk.Table(10, 20, True)
151 warnings_bar.add(build_stop_tab)
152
153 icon = gtk.Image()
154 icon_pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_INDI_ALERT_FILE)
155 icon.set_from_pixbuf(icon_pix_buffer)
156 build_stop_tab.attach(icon, 0, 2, 0, 10)
157
158 label = gtk.Label()
159 label.set_alignment(0.0, 0.5)
160 warnings_nb = len(self.builder.parsing_warnings)
161 if warnings_nb == 1:
162 label.set_markup("<span size='x-large'><b>1 recipe parsing warning</b></span>")
163 else:
164 label.set_markup("<span size='x-large'><b>%s recipe parsing warnings</b></span>" % warnings_nb)
165 build_stop_tab.attach(label, 2, 12, 0, 10)
166
167 view_warnings_button = HobButton("View warnings")
168 view_warnings_button.connect('clicked', self.view_warnings_button_clicked_cb)
169 build_stop_tab.attach(view_warnings_button, 15, 19, 1, 9)
170
171 return warnings_bar
172
173 def disable_warnings_bar(self):
174 if self.builder.parsing_warnings:
175 if hasattr(self, 'warnings_bar'):
176 self.warnings_bar.hide_all()
177 self.builder.parsing_warnings = []
178
179 def create_config_machine(self):
180 self.machine_title = gtk.Label()
181 self.machine_title.set_alignment(0.0, 0.5)
182 mark = "<span %s>Select a machine</span>" % self.span_tag('x-large', 'bold')
183 self.machine_title.set_markup(mark)
184
185 self.machine_title_desc = gtk.Label()
186 self.machine_title_desc.set_alignment(0.0, 0.5)
187 mark = ("<span %s>Your selection is the profile of the target machine for which you"
188 " are building the image.\n</span>") % (self.span_tag('medium'))
189 self.machine_title_desc.set_markup(mark)
190
191 self.machine_combo = gtk.combo_box_new_text()
192 self.machine_combo.connect("changed", self.machine_combo_changed_cb)
193
194 icon_file = hic.ICON_LAYERS_DISPLAY_FILE
195 hover_file = hic.ICON_LAYERS_HOVER_FILE
196 self.layer_button = HobImageButton("Layers", "Add support for machines, software, etc.",
197 icon_file, hover_file)
198 self.layer_button.connect("clicked", self.layer_button_clicked_cb)
199
200 markup = "Layers are a powerful mechanism to extend the Yocto Project "
201 markup += "with your own functionality.\n"
202 markup += "For more on layers, check the <a href=\""
203 markup += "http://www.yoctoproject.org/docs/current/dev-manual/"
204 markup += "dev-manual.html#understanding-and-using-layers\">reference manual</a>."
205 self.layer_info_icon = HobInfoButton("<b>Layers</b>" + "*" + markup, self.get_parent())
206 self.progress_bar = HobProgressBar()
207 self.stop_button = HobAltButton("Stop")
208 self.stop_button.connect("clicked", self.stop_button_clicked_cb)
209 self.machine_separator = gtk.HSeparator()
210
211 def set_config_machine_layout(self, show_progress_bar = False):
212 self.gtable.attach(self.machine_title, 0, 40, 0, 4)
213 self.gtable.attach(self.machine_title_desc, 0, 40, 4, 6)
214 self.gtable.attach(self.machine_combo, 0, 12, 7, 10)
215 self.gtable.attach(self.layer_button, 14, 36, 7, 12)
216 self.gtable.attach(self.layer_info_icon, 36, 40, 7, 11)
217 if show_progress_bar:
218 #self.gtable.attach(self.progress_box, 0, 40, 15, 18)
219 self.gtable.attach(self.progress_bar, 0, 37, 15, 18)
220 self.gtable.attach(self.stop_button, 37, 40, 15, 18, 0, 0)
221 if self.builder.parsing_warnings:
222 self.warnings_bar = self.add_warnings_bar()
223 self.gtable.attach(self.warnings_bar, 0, 40, 14, 18)
224 self.warning_shift = 4
225 else:
226 self.warning_shift = 0
227 self.gtable.attach(self.machine_separator, 0, 40, 13, 14)
228
229 def create_config_baseimg(self):
230 self.image_title = gtk.Label()
231 self.image_title.set_alignment(0, 1.0)
232 mark = "<span %s>Select an image recipe</span>" % self.span_tag('x-large', 'bold')
233 self.image_title.set_markup(mark)
234
235 self.image_title_desc = gtk.Label()
236 self.image_title_desc.set_alignment(0, 0.5)
237
238 mark = ("<span %s>Image recipes are a starting point for the type of image you want. "
239 "You can build them as \n"
240 "they are or edit them to suit your needs.\n</span>") % self.span_tag('medium')
241 self.image_title_desc.set_markup(mark)
242
243 self.image_combo = gtk.combo_box_new_text()
244 self.image_combo.set_row_separator_func(self.combo_separator_func, None)
245 self.image_combo_id = self.image_combo.connect("changed", self.image_combo_changed_cb)
246
247 self.image_desc = gtk.Label()
248 self.image_desc.set_alignment(0.0, 0.5)
249 self.image_desc.set_size_request(256, -1)
250 self.image_desc.set_justify(gtk.JUSTIFY_LEFT)
251 self.image_desc.set_line_wrap(True)
252
253 # button to view recipes
254 icon_file = hic.ICON_RCIPE_DISPLAY_FILE
255 hover_file = hic.ICON_RCIPE_HOVER_FILE
256 self.view_adv_configuration_button = HobImageButton("Advanced configuration",
257 "Select image types, package formats, etc",
258 icon_file, hover_file)
259 self.view_adv_configuration_button.connect("clicked", self.view_adv_configuration_button_clicked_cb)
260
261 self.image_separator = gtk.HSeparator()
262
263 def combo_separator_func(self, model, iter, user_data):
264 name = model.get_value(iter, 0)
265 if name == "--Separator--":
266 return True
267
268 def set_config_baseimg_layout(self):
269 self.gtable.attach(self.image_title, 0, 40, 15+self.warning_shift, 17+self.warning_shift)
270 self.gtable.attach(self.image_title_desc, 0, 40, 18+self.warning_shift, 22+self.warning_shift)
271 self.gtable.attach(self.image_combo, 0, 12, 23+self.warning_shift, 26+self.warning_shift)
272 self.gtable.attach(self.image_desc, 0, 12, 27+self.warning_shift, 33+self.warning_shift)
273 self.gtable.attach(self.view_adv_configuration_button, 14, 36, 23+self.warning_shift, 28+self.warning_shift)
274 self.gtable.attach(self.image_separator, 0, 40, 35+self.warning_shift, 36+self.warning_shift)
275
276 def create_config_build_button(self):
277 # Create the "Build packages" and "Build image" buttons at the bottom
278 button_box = gtk.HBox(False, 6)
279
280 # create button "Build image"
281 self.just_bake_button = HobButton("Build image")
282 self.just_bake_button.set_tooltip_text("Build the image recipe as it is")
283 self.just_bake_button.connect("clicked", self.just_bake_button_clicked_cb)
284 button_box.pack_end(self.just_bake_button, expand=False, fill=False)
285
286 # create button "Edit image recipe"
287 self.edit_image_button = HobAltButton("Edit image recipe")
288 self.edit_image_button.set_tooltip_text("Customize the recipes and packages to be included in your image")
289 self.edit_image_button.connect("clicked", self.edit_image_button_clicked_cb)
290 button_box.pack_end(self.edit_image_button, expand=False, fill=False)
291
292 return button_box
293
294 def stop_button_clicked_cb(self, button):
295 self.stopping = True
296 self.progress_bar.set_text("Stopping recipe parsing")
297 self.progress_bar.set_rcstyle("stop")
298 self.builder.cancel_parse_sync()
299
300 def view_warnings_button_clicked_cb(self, button):
301 self.builder.show_warning_dialog()
302
303 def machine_combo_changed_idle_cb(self):
304 self.builder.window.set_cursor(None)
305
306 def machine_combo_changed_cb(self, machine_combo):
307 self.stopping = False
308 self.builder.parsing_warnings = []
309 combo_item = machine_combo.get_active_text()
310 if not combo_item or combo_item == self.__dummy_machine__:
311 return
312
313 self.builder.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
314 self.builder.wait(0.1) #wait for combo and cursor to update
315
316 # remove __dummy_machine__ item from the store list after first user selection
317 # because it is no longer valid
318 combo_store = machine_combo.get_model()
319 if len(combo_store) and (combo_store[0][0] == self.__dummy_machine__):
320 machine_combo.remove_text(0)
321
322 self.builder.configuration.curr_mach = combo_item
323 if self.machine_combo_changed_by_manual:
324 self.builder.configuration.clear_selection()
325 # reset machine_combo_changed_by_manual
326 self.machine_combo_changed_by_manual = True
327
328 self.builder.configuration.selected_image = None
329
330 # Do reparse recipes
331 self.builder.populate_recipe_package_info_async()
332
333 glib.idle_add(self.machine_combo_changed_idle_cb)
334
335 def update_machine_combo(self):
336 self.disable_warnings_bar()
337 all_machines = [self.__dummy_machine__] + self.builder.parameters.all_machines
338
339 model = self.machine_combo.get_model()
340 model.clear()
341 for machine in all_machines:
342 self.machine_combo.append_text(machine)
343 self.machine_combo.set_active(0)
344
345 def switch_machine_combo(self):
346 self.disable_warnings_bar()
347 self.machine_combo_changed_by_manual = False
348 model = self.machine_combo.get_model()
349 active = 0
350 while active < len(model):
351 if model[active][0] == self.builder.configuration.curr_mach:
352 self.machine_combo.set_active(active)
353 return
354 active += 1
355
356 if model[0][0] != self.__dummy_machine__:
357 self.machine_combo.insert_text(0, self.__dummy_machine__)
358
359 self.machine_combo.set_active(0)
360
361 def update_image_desc(self):
362 desc = ""
363 selected_image = self.image_combo.get_active_text()
364 if selected_image and selected_image in self.builder.recipe_model.pn_path.keys():
365 image_path = self.builder.recipe_model.pn_path[selected_image]
366 image_iter = self.builder.recipe_model.get_iter(image_path)
367 desc = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_DESC)
368
369 mark = ("<span %s>%s</span>\n") % (self.span_tag('small'), desc)
370 self.image_desc.set_markup(mark)
371
372 def image_combo_changed_idle_cb(self, selected_image, selected_recipes, selected_packages):
373 self.builder.update_recipe_model(selected_image, selected_recipes)
374 self.builder.update_package_model(selected_packages)
375 self.builder.window_sensitive(True)
376
377 def image_combo_changed_cb(self, combo):
378 self.builder.window_sensitive(False)
379 selected_image = self.image_combo.get_active_text()
380 if selected_image == self.__custom_image__:
381 topdir = self.builder.get_topdir()
382 images_dir = topdir + "/recipes/images/custom/"
383 self.builder.ensure_dir(images_dir)
384
385 dialog = RetrieveImageDialog(images_dir, "Select from my image recipes",
386 self.builder, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
387 response = dialog.run()
388 if response == gtk.RESPONSE_OK:
389 image_name = dialog.get_filename()
390 head, tail = os.path.split(image_name)
391 selected_image = os.path.splitext(tail)[0]
392 self.custom_image_selected = selected_image
393 self.update_image_combo(self.builder.recipe_model, selected_image)
394 else:
395 selected_image = self.__dummy_image__
396 self.update_image_combo(self.builder.recipe_model, None)
397 dialog.destroy()
398 else:
399 if self.custom_image_selected:
400 self.custom_image_selected = None
401 self.update_image_combo(self.builder.recipe_model, selected_image)
402
403 if not selected_image or (selected_image == self.__dummy_image__):
404 self.builder.window_sensitive(True)
405 self.just_bake_button.hide()
406 self.edit_image_button.hide()
407 return
408
409 # remove __dummy_image__ item from the store list after first user selection
410 # because it is no longer valid
411 combo_store = combo.get_model()
412 if len(combo_store) and (combo_store[0][0] == self.__dummy_image__):
413 combo.remove_text(0)
414
415 self.builder.customized = False
416
417 selected_recipes = []
418
419 image_path = self.builder.recipe_model.pn_path[selected_image]
420 image_iter = self.builder.recipe_model.get_iter(image_path)
421 selected_packages = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_INSTALL).split()
422 self.update_image_desc()
423
424 self.builder.recipe_model.reset()
425 self.builder.package_model.reset()
426
427 self.show_baseimg_selected()
428
429 if selected_image == self.builder.recipe_model.__custom_image__:
430 self.just_bake_button.hide()
431
432 glib.idle_add(self.image_combo_changed_idle_cb, selected_image, selected_recipes, selected_packages)
433
434 def _image_combo_connect_signal(self):
435 if not self.image_combo_id:
436 self.image_combo_id = self.image_combo.connect("changed", self.image_combo_changed_cb)
437
438 def _image_combo_disconnect_signal(self):
439 if self.image_combo_id:
440 self.image_combo.disconnect(self.image_combo_id)
441 self.image_combo_id = None
442
443 def update_image_combo(self, recipe_model, selected_image):
444 # Update the image combo according to the images in the recipe_model
445 # populate image combo
446 filter = {RecipeListModel.COL_TYPE : ['image']}
447 image_model = recipe_model.tree_model(filter)
448 image_model.set_sort_column_id(recipe_model.COL_NAME, gtk.SORT_ASCENDING)
449 active = 0
450 cnt = 0
451
452 white_pattern = []
453 if self.builder.parameters.image_white_pattern:
454 for i in self.builder.parameters.image_white_pattern.split():
455 white_pattern.append(re.compile(i))
456
457 black_pattern = []
458 if self.builder.parameters.image_black_pattern:
459 for i in self.builder.parameters.image_black_pattern.split():
460 black_pattern.append(re.compile(i))
461 black_pattern.append(re.compile("hob-image"))
462 black_pattern.append(re.compile("edited(-[0-9]*)*.bb$"))
463
464 it = image_model.get_iter_first()
465 self._image_combo_disconnect_signal()
466 model = self.image_combo.get_model()
467 model.clear()
468 # Set a indicator text to combo store when first open
469 if not selected_image:
470 self.image_combo.append_text(self.__dummy_image__)
471 cnt = cnt + 1
472
473 self.image_combo.append_text(self.__custom_image__)
474 self.image_combo.append_text("--Separator--")
475 cnt = cnt + 2
476
477 topdir = self.builder.get_topdir()
478 # append and set active
479 while it:
480 path = image_model.get_path(it)
481 it = image_model.iter_next(it)
482 image_name = image_model[path][recipe_model.COL_NAME]
483 if image_name == self.builder.recipe_model.__custom_image__:
484 continue
485
486 if black_pattern:
487 allow = True
488 for pattern in black_pattern:
489 if pattern.search(image_name):
490 allow = False
491 break
492 elif white_pattern:
493 allow = False
494 for pattern in white_pattern:
495 if pattern.search(image_name):
496 allow = True
497 break
498 else:
499 allow = True
500
501 file_name = image_model[path][recipe_model.COL_FILE]
502 if file_name and topdir in file_name:
503 allow = False
504
505 if allow:
506 self.image_combo.append_text(image_name)
507 if image_name == selected_image:
508 active = cnt
509 cnt = cnt + 1
510 self.image_combo.append_text(self.builder.recipe_model.__custom_image__)
511
512 if selected_image == self.builder.recipe_model.__custom_image__:
513 active = cnt
514
515 if self.custom_image_selected:
516 self.image_combo.append_text("--Separator--")
517 self.image_combo.append_text(self.custom_image_selected)
518 cnt = cnt + 2
519 if self.custom_image_selected == selected_image:
520 active = cnt
521
522 self.image_combo.set_active(active)
523
524 if active != 0:
525 self.show_baseimg_selected()
526
527 self._image_combo_connect_signal()
528
529 def layer_button_clicked_cb(self, button):
530 # Create a layer selection dialog
531 self.builder.show_layer_selection_dialog()
532
533 def view_adv_configuration_button_clicked_cb(self, button):
534 # Create an advanced settings dialog
535 response, settings_changed = self.builder.show_adv_settings_dialog()
536 if not response:
537 return
538 if settings_changed:
539 self.builder.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
540 self.builder.wait(0.1) #wait for adv_settings_dialog to terminate
541 self.builder.reparse_post_adv_settings()
542 self.builder.window.set_cursor(None)
543
544 def just_bake_button_clicked_cb(self, button):
545 self.builder.parsing_warnings = []
546 self.builder.just_bake()
547
548 def edit_image_button_clicked_cb(self, button):
549 self.builder.set_base_image()
550 self.builder.show_recipes()
551
552 def my_images_button_clicked_cb(self, button):
553 self.builder.show_load_my_images_dialog()
554
555 def settings_button_clicked_cb(self, button):
556 # Create an advanced settings dialog
557 response, settings_changed = self.builder.show_simple_settings_dialog()
558 if not response:
559 return
560 if settings_changed:
561 self.builder.reparse_post_adv_settings()
diff --git a/bitbake/lib/bb/ui/crumbs/imagedetailspage.py b/bitbake/lib/bb/ui/crumbs/imagedetailspage.py
deleted file mode 100755
index 32d4854166..0000000000
--- a/bitbake/lib/bb/ui/crumbs/imagedetailspage.py
+++ /dev/null
@@ -1,705 +0,0 @@
1#!/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 gobject
24import gtk
25from bb.ui.crumbs.hobcolor import HobColors
26from bb.ui.crumbs.hobwidget import hic, HobViewTable, HobAltButton, HobButton
27from bb.ui.crumbs.hobpages import HobPage
28import subprocess
29from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
30from bb.ui.crumbs.hig.saveimagedialog import SaveImageDialog
31
32#
33# ImageDetailsPage
34#
35class ImageDetailsPage (HobPage):
36
37 class DetailBox (gtk.EventBox):
38 def __init__(self, widget = None, varlist = None, vallist = None, icon = None, button = None, button2=None, color = HobColors.LIGHT_GRAY):
39 gtk.EventBox.__init__(self)
40
41 # set color
42 style = self.get_style().copy()
43 style.bg[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(color, False, False)
44 self.set_style(style)
45
46 self.row = gtk.Table(1, 2, False)
47 self.row.set_border_width(10)
48 self.add(self.row)
49
50 total_rows = 0
51 if widget:
52 total_rows = 10
53 if varlist and vallist:
54 # pack the icon and the text on the left
55 total_rows += len(varlist)
56 self.table = gtk.Table(total_rows, 20, True)
57 self.table.set_row_spacings(6)
58 self.table.set_size_request(100, -1)
59 self.row.attach(self.table, 0, 1, 0, 1, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL)
60
61 colid = 0
62 rowid = 0
63 self.line_widgets = {}
64 if icon:
65 self.table.attach(icon, colid, colid + 2, 0, 1)
66 colid = colid + 2
67 if widget:
68 self.table.attach(widget, colid, 20, 0, 10)
69 rowid = 10
70 if varlist and vallist:
71 for row in range(rowid, total_rows):
72 index = row - rowid
73 self.line_widgets[varlist[index]] = self.text2label(varlist[index], vallist[index])
74 self.table.attach(self.line_widgets[varlist[index]], colid, 20, row, row + 1)
75 # pack the button on the right
76 if button:
77 self.bbox = gtk.VBox()
78 self.bbox.pack_start(button, expand=True, fill=False)
79 if button2:
80 self.bbox.pack_start(button2, expand=True, fill=False)
81 self.bbox.set_size_request(150,-1)
82 self.row.attach(self.bbox, 1, 2, 0, 1, xoptions=gtk.FILL, yoptions=gtk.EXPAND)
83
84 def update_line_widgets(self, variable, value):
85 if len(self.line_widgets) == 0:
86 return
87 if not isinstance(self.line_widgets[variable], gtk.Label):
88 return
89 self.line_widgets[variable].set_markup(self.format_line(variable, value))
90
91 def wrap_line(self, inputs):
92 # wrap the long text of inputs
93 wrap_width_chars = 75
94 outputs = ""
95 tmps = inputs
96 less_chars = len(inputs)
97 while (less_chars - wrap_width_chars) > 0:
98 less_chars -= wrap_width_chars
99 outputs += tmps[:wrap_width_chars] + "\n "
100 tmps = inputs[less_chars:]
101 outputs += tmps
102 return outputs
103
104 def format_line(self, variable, value):
105 wraped_value = self.wrap_line(value)
106 markup = "<span weight=\'bold\'>%s</span>" % variable
107 markup += "<span weight=\'normal\' foreground=\'#1c1c1c\' font_desc=\'14px\'>%s</span>" % wraped_value
108 return markup
109
110 def text2label(self, variable, value):
111 # append the name:value to the left box
112 # such as "Name: hob-core-minimal-variant-2011-12-15-beagleboard"
113 label = gtk.Label()
114 label.set_alignment(0.0, 0.5)
115 label.set_markup(self.format_line(variable, value))
116 return label
117
118 class BuildDetailBox (gtk.EventBox):
119 def __init__(self, varlist = None, vallist = None, icon = None, color = HobColors.LIGHT_GRAY):
120 gtk.EventBox.__init__(self)
121
122 # set color
123 style = self.get_style().copy()
124 style.bg[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(color, False, False)
125 self.set_style(style)
126
127 self.hbox = gtk.HBox()
128 self.hbox.set_border_width(10)
129 self.add(self.hbox)
130
131 total_rows = 0
132 if varlist and vallist:
133 # pack the icon and the text on the left
134 total_rows += len(varlist)
135 self.table = gtk.Table(total_rows, 20, True)
136 self.table.set_row_spacings(6)
137 self.table.set_size_request(100, -1)
138 self.hbox.pack_start(self.table, expand=True, fill=True, padding=15)
139
140 colid = 0
141 rowid = 0
142 self.line_widgets = {}
143 if icon:
144 self.table.attach(icon, colid, colid + 2, 0, 1)
145 colid = colid + 2
146 if varlist and vallist:
147 for row in range(rowid, total_rows):
148 index = row - rowid
149 self.line_widgets[varlist[index]] = self.text2label(varlist[index], vallist[index])
150 self.table.attach(self.line_widgets[varlist[index]], colid, 20, row, row + 1)
151
152 def update_line_widgets(self, variable, value):
153 if len(self.line_widgets) == 0:
154 return
155 if not isinstance(self.line_widgets[variable], gtk.Label):
156 return
157 self.line_widgets[variable].set_markup(self.format_line(variable, value))
158
159 def wrap_line(self, inputs):
160 # wrap the long text of inputs
161 wrap_width_chars = 75
162 outputs = ""
163 tmps = inputs
164 less_chars = len(inputs)
165 while (less_chars - wrap_width_chars) > 0:
166 less_chars -= wrap_width_chars
167 outputs += tmps[:wrap_width_chars] + "\n "
168 tmps = inputs[less_chars:]
169 outputs += tmps
170 return outputs
171
172 def format_line(self, variable, value):
173 wraped_value = self.wrap_line(value)
174 markup = "<span weight=\'bold\'>%s</span>" % variable
175 markup += "<span weight=\'normal\' foreground=\'#1c1c1c\' font_desc=\'14px\'>%s</span>" % wraped_value
176 return markup
177
178 def text2label(self, variable, value):
179 # append the name:value to the left box
180 # such as "Name: hob-core-minimal-variant-2011-12-15-beagleboard"
181 label = gtk.Label()
182 label.set_alignment(0.0, 0.5)
183 label.set_markup(self.format_line(variable, value))
184 return label
185
186 def __init__(self, builder):
187 super(ImageDetailsPage, self).__init__(builder, "Image details")
188
189 self.image_store = []
190 self.button_ids = {}
191 self.details_bottom_buttons = gtk.HBox(False, 6)
192 self.image_saved = False
193 self.create_visual_elements()
194 self.name_field_template = ""
195 self.description_field_template = ""
196
197 def create_visual_elements(self):
198 # create visual elements
199 # create the toolbar
200 self.toolbar = gtk.Toolbar()
201 self.toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
202 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
203
204 my_images_button = self.append_toolbar_button(self.toolbar,
205 "Images",
206 hic.ICON_IMAGES_DISPLAY_FILE,
207 hic.ICON_IMAGES_HOVER_FILE,
208 "Open previously built images",
209 self.my_images_button_clicked_cb)
210 settings_button = self.append_toolbar_button(self.toolbar,
211 "Settings",
212 hic.ICON_SETTINGS_DISPLAY_FILE,
213 hic.ICON_SETTINGS_HOVER_FILE,
214 "View additional build settings",
215 self.settings_button_clicked_cb)
216
217 self.details_top_buttons = self.add_onto_top_bar(self.toolbar)
218
219 def _remove_all_widget(self):
220 children = self.get_children() or []
221 for child in children:
222 self.remove(child)
223 children = self.box_group_area.get_children() or []
224 for child in children:
225 self.box_group_area.remove(child)
226 children = self.details_bottom_buttons.get_children() or []
227 for child in children:
228 self.details_bottom_buttons.remove(child)
229
230 def show_page(self, step):
231 self.build_succeeded = (step == self.builder.IMAGE_GENERATED)
232 image_addr = self.builder.parameters.image_addr
233 image_names = self.builder.parameters.image_names
234 if self.build_succeeded:
235 machine = self.builder.configuration.curr_mach
236 base_image = self.builder.recipe_model.get_selected_image()
237 layers = self.builder.configuration.layers
238 pkg_num = "%s" % len(self.builder.package_model.get_selected_packages())
239 log_file = self.builder.current_logfile
240 else:
241 pkg_num = "N/A"
242 log_file = None
243
244 # remove
245 for button_id, button in self.button_ids.items():
246 button.disconnect(button_id)
247 self._remove_all_widget()
248
249 # repack
250 self.pack_start(self.details_top_buttons, expand=False, fill=False)
251 self.pack_start(self.group_align, expand=True, fill=True)
252
253 self.build_result = None
254 if self.image_saved or (self.build_succeeded and self.builder.current_step == self.builder.IMAGE_GENERATING):
255 # building is the previous step
256 icon = gtk.Image()
257 pixmap_path = hic.ICON_INDI_CONFIRM_FILE
258 color = HobColors.RUNNING
259 pix_buffer = gtk.gdk.pixbuf_new_from_file(pixmap_path)
260 icon.set_from_pixbuf(pix_buffer)
261 varlist = [""]
262 if self.image_saved:
263 vallist = ["Your image recipe has been saved"]
264 else:
265 vallist = ["Your image is ready"]
266 self.build_result = self.BuildDetailBox(varlist=varlist, vallist=vallist, icon=icon, color=color)
267 self.box_group_area.pack_start(self.build_result, expand=False, fill=False)
268
269 self.buttonlist = ["Build new image", "Save image recipe", "Run image", "Deploy image"]
270
271 # Name
272 self.image_store = []
273 self.toggled_image = ""
274 default_image_size = 0
275 self.num_toggled = 0
276 i = 0
277 for image_name in image_names:
278 image_size = HobPage._size_to_string(os.stat(os.path.join(image_addr, image_name)).st_size)
279
280 image_attr = ("run" if (self.test_type_runnable(image_name) and self.test_mach_runnable(image_name)) else \
281 ("deploy" if self.test_deployable(image_name) else ""))
282 is_toggled = (image_attr != "")
283
284 if not self.toggled_image:
285 if i == (len(image_names) - 1):
286 is_toggled = True
287 if is_toggled:
288 default_image_size = image_size
289 self.toggled_image = image_name
290
291 split_stuff = image_name.split('.')
292 if "rootfs" in split_stuff:
293 image_type = image_name[(len(split_stuff[0]) + len(".rootfs") + 1):]
294 else:
295 image_type = image_name[(len(split_stuff[0]) + 1):]
296
297 self.image_store.append({'name': image_name,
298 'type': image_type,
299 'size': image_size,
300 'is_toggled': is_toggled,
301 'action_attr': image_attr,})
302
303 i = i + 1
304 self.num_toggled += is_toggled
305
306 self.is_runnable = self.create_bottom_buttons(self.buttonlist, self.toggled_image)
307
308 # Generated image files info
309 varlist = ["Name: ", "Files created: ", "Directory: "]
310 vallist = []
311
312 vallist.append(image_name.split('.')[0])
313 vallist.append(', '.join(fileitem['type'] for fileitem in self.image_store))
314 vallist.append(image_addr)
315
316 view_files_button = HobAltButton("View files")
317 view_files_button.connect("clicked", self.view_files_clicked_cb, image_addr)
318 view_files_button.set_tooltip_text("Open the directory containing the image files")
319 open_log_button = None
320 if log_file:
321 open_log_button = HobAltButton("Open log")
322 open_log_button.connect("clicked", self.open_log_clicked_cb, log_file)
323 open_log_button.set_tooltip_text("Open the build's log file")
324 self.image_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=view_files_button, button2=open_log_button)
325 self.box_group_area.pack_start(self.image_detail, expand=False, fill=True)
326
327 # The default script path to run the image (runqemu)
328 self.run_script_path = os.path.join(self.builder.parameters.core_base, "scripts/runqemu")
329 self.run_script_detail = None
330 varlist = ["Run script: "]
331 vallist = []
332 vallist.append(self.run_script_path)
333
334 change_run_script_button = HobAltButton("Change")
335 change_run_script_button.connect("clicked", self.change_run_script_cb)
336 change_run_script_button.set_tooltip_text("Change run script")
337 self.run_script_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=change_run_script_button)
338 self.box_group_area.pack_start(self.run_script_detail, expand=False, fill=True)
339
340 # The default kernel box for the qemu images
341 self.sel_kernel = ""
342 self.kernel_detail = None
343 if self.test_mach_runnable(image_name):
344 self.sel_kernel = self.get_kernel_file_name()
345
346 # varlist = ["Kernel: "]
347 # vallist = []
348 # vallist.append(self.sel_kernel)
349
350 # change_kernel_button = HobAltButton("Change")
351 # change_kernel_button.connect("clicked", self.change_kernel_cb)
352 # change_kernel_button.set_tooltip_text("Change qemu kernel file")
353 # self.kernel_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=change_kernel_button)
354 # self.box_group_area.pack_start(self.kernel_detail, expand=True, fill=True)
355
356 # Machine, Image recipe and Layers
357 layer_num_limit = 15
358 varlist = ["Machine: ", "Image recipe: ", "Layers: "]
359 vallist = []
360 self.setting_detail = None
361 if self.build_succeeded:
362 vallist.append(machine)
363 if self.builder.recipe_model.is_custom_image():
364 if self.builder.configuration.initial_selected_image == self.builder.recipe_model.__custom_image__:
365 base_image ="New image recipe"
366 else:
367 base_image = self.builder.configuration.initial_selected_image + " (edited)"
368 vallist.append(base_image)
369 i = 0
370 for layer in layers:
371 if i > layer_num_limit:
372 break
373 varlist.append(" - ")
374 i += 1
375 vallist.append("")
376 i = 0
377 for layer in layers:
378 if i > layer_num_limit:
379 break
380 elif i == layer_num_limit:
381 vallist.append("and more...")
382 else:
383 vallist.append(layer)
384 i += 1
385
386 edit_config_button = HobAltButton("Edit configuration")
387 edit_config_button.set_tooltip_text("Edit machine and image recipe")
388 edit_config_button.connect("clicked", self.edit_config_button_clicked_cb)
389 self.setting_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=edit_config_button)
390 self.box_group_area.pack_start(self.setting_detail, expand=True, fill=True)
391
392 # Packages included, and Total image size
393 varlist = ["Packages included: ", "Total image size: "]
394 vallist = []
395 vallist.append(pkg_num)
396 vallist.append(default_image_size)
397 self.builder.configuration.image_size = default_image_size
398 self.builder.configuration.image_packages = self.builder.configuration.selected_packages
399 if self.build_succeeded:
400 edit_packages_button = HobAltButton("Edit packages")
401 edit_packages_button.set_tooltip_text("Edit the packages included in your image")
402 edit_packages_button.connect("clicked", self.edit_packages_button_clicked_cb)
403 else: # get to this page from "My images"
404 edit_packages_button = None
405 self.package_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=edit_packages_button)
406 self.box_group_area.pack_start(self.package_detail, expand=True, fill=True)
407
408 # pack the buttons at the bottom, at this time they are already created.
409 if self.build_succeeded:
410 self.box_group_area.pack_end(self.details_bottom_buttons, expand=False, fill=False)
411 else: # for "My images" page
412 self.details_separator = gtk.HSeparator()
413 self.box_group_area.pack_start(self.details_separator, expand=False, fill=False)
414 self.box_group_area.pack_start(self.details_bottom_buttons, expand=False, fill=False)
415
416 self.show_all()
417 if self.kernel_detail and (not self.is_runnable):
418 self.kernel_detail.hide()
419 if self.run_script_detail and self.is_runnable:
420 self.run_script_detail.hide()
421 self.image_saved = False
422
423 def view_files_clicked_cb(self, button, image_addr):
424 subprocess.call("xdg-open /%s" % image_addr, shell=True)
425
426 def open_log_clicked_cb(self, button, log_file):
427 if log_file:
428 log_file = "file:///" + log_file
429 gtk.show_uri(screen=button.get_screen(), uri=log_file, timestamp=0)
430
431 def refresh_package_detail_box(self, image_size):
432 self.package_detail.update_line_widgets("Total image size: ", image_size)
433
434 def test_type_runnable(self, image_name):
435 type_runnable = False
436 for t in self.builder.parameters.runnable_image_types:
437 if image_name.endswith(t):
438 type_runnable = True
439 break
440 return type_runnable
441
442 def test_mach_runnable(self, image_name):
443 mach_runnable = False
444 for t in self.builder.parameters.runnable_machine_patterns:
445 if t in image_name:
446 mach_runnable = True
447 break
448 return mach_runnable
449
450 def test_deployable(self, image_name):
451 if self.builder.configuration.curr_mach.startswith("qemu"):
452 return False
453 deployable = False
454 for t in self.builder.parameters.deployable_image_types:
455 if image_name.endswith(t):
456 deployable = True
457 break
458 return deployable
459
460 def get_kernel_file_name(self, kernel_addr=""):
461 kernel_name = ""
462
463 if not kernel_addr:
464 kernel_addr = self.builder.parameters.image_addr
465
466 files = [f for f in os.listdir(kernel_addr) if f[0] <> '.']
467 for check_file in files:
468 if check_file.endswith(".bin"):
469 name_splits = check_file.split(".")[0]
470 if self.builder.parameters.kernel_image_type in name_splits.split("-"):
471 kernel_name = check_file
472 break
473
474 return kernel_name
475
476 def show_builded_images_dialog(self, widget, primary_action=""):
477 title = primary_action if primary_action else "Your builded images"
478 dialog = CrumbsDialog(title, self.builder,
479 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
480 dialog.set_border_width(12)
481
482 label = gtk.Label()
483 label.set_use_markup(True)
484 label.set_alignment(0.0, 0.5)
485 label.set_padding(12,0)
486 if primary_action == "Run image":
487 label.set_markup("<span font_desc='12'>Select the image file you want to run:</span>")
488 elif primary_action == "Deploy image":
489 label.set_markup("<span font_desc='12'>Select the image file you want to deploy:</span>")
490 else:
491 label.set_markup("<span font_desc='12'>Select the image file you want to %s</span>" % primary_action)
492 dialog.vbox.pack_start(label, expand=False, fill=False)
493
494 # filter created images as action attribution (deploy or run)
495 action_attr = ""
496 action_images = []
497 for fileitem in self.image_store:
498 action_attr = fileitem['action_attr']
499 if (action_attr == 'run' and primary_action == "Run image") \
500 or (action_attr == 'deploy' and primary_action == "Deploy image"):
501 action_images.append(fileitem)
502 if (len(action_images) == 0 and primary_action == "Run image"):
503 # if there are no qemu runnable images, let the user choose any of the existing images for custom run
504 action_images = self.image_store
505 # pack the corresponding 'runnable' or 'deploy' radio_buttons, if there has no more than one file.
506 # assume that there does not both have 'deploy' and 'runnable' files in the same building result
507 # in possible as design.
508 curr_row = 0
509 rows = (len(action_images)) if len(action_images) < 10 else 10
510 table = gtk.Table(rows, 10, True)
511 table.set_row_spacings(6)
512 table.set_col_spacing(0, 12)
513 table.set_col_spacing(5, 12)
514
515 sel_parent_btn = None
516 for fileitem in action_images:
517 sel_btn = gtk.RadioButton(sel_parent_btn, fileitem['type'])
518 sel_parent_btn = sel_btn if not sel_parent_btn else sel_parent_btn
519 if curr_row == 0:
520 sel_btn.set_active(True)
521 self.toggled_image = fileitem['name']
522 sel_btn.connect('toggled', self.table_selected_cb, fileitem)
523 if curr_row < 10:
524 table.attach(sel_btn, 0, 4, curr_row, curr_row + 1, xpadding=24)
525 else:
526 table.attach(sel_btn, 5, 9, curr_row - 10, curr_row - 9, xpadding=24)
527 curr_row += 1
528
529 dialog.vbox.pack_start(table, expand=False, fill=False, padding=6)
530
531 button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
532 HobAltButton.style_button(button)
533
534 if primary_action:
535 button = dialog.add_button(primary_action, gtk.RESPONSE_YES)
536 HobButton.style_button(button)
537
538 dialog.show_all()
539
540 response = dialog.run()
541 dialog.destroy()
542
543 if response != gtk.RESPONSE_YES:
544 return
545 # perform the primary action on the image selected by the user
546 for fileitem in self.image_store:
547 if fileitem['name'] == self.toggled_image:
548 if (fileitem['action_attr'] == 'run' and primary_action == "Run image"):
549 self.builder.runqemu_image(fileitem['name'], self.sel_kernel)
550 elif (fileitem['action_attr'] == 'deploy' and primary_action == "Deploy image"):
551 self.builder.deploy_image(fileitem['name'])
552 elif (primary_action == "Run image"):
553 self.builder.run_custom_image(fileitem['name'], self.run_script_path)
554
555 def table_selected_cb(self, tbutton, image):
556 image['is_toggled'] = tbutton.get_active()
557 if image['is_toggled']:
558 self.toggled_image = image['name']
559
560 def change_kernel_cb(self, widget):
561 kernel_path = self.builder.show_load_kernel_dialog()
562 if kernel_path and self.kernel_detail:
563 import os.path
564 self.sel_kernel = os.path.basename(kernel_path)
565 self.kernel_detail.update_line_widgets("Kernel: ", self.sel_kernel);
566
567 def change_run_script_cb(self, widget):
568 self.run_script_path = self.builder.show_load_run_script_dialog()
569 if self.run_script_path and self.run_script_detail:
570 import os.path
571 self.sel_run_script = os.path.basename(self.run_script_path)
572 self.run_script_detail.update_line_widgets("Run script: ", self.run_script_path)
573
574 def create_bottom_buttons(self, buttonlist, image_name):
575 # Create the buttons at the bottom
576 created = False
577 packed = False
578 self.button_ids = {}
579 is_runnable = False
580
581 # create button "Deploy image"
582 name = "Deploy image"
583 if name in buttonlist and self.test_deployable(image_name):
584 deploy_button = HobButton('Deploy image')
585 #deploy_button.set_size_request(205, 49)
586 deploy_button.set_tooltip_text("Burn a live image to a USB drive or flash memory")
587 deploy_button.set_flags(gtk.CAN_DEFAULT)
588 button_id = deploy_button.connect("clicked", self.deploy_button_clicked_cb)
589 self.button_ids[button_id] = deploy_button
590 self.details_bottom_buttons.pack_end(deploy_button, expand=False, fill=False)
591 created = True
592 packed = True
593
594 name = "Run image"
595 if name in buttonlist:
596 name = "Run qemu image"
597 is_runnable = True
598 if not (self.test_type_runnable(image_name) and self.test_mach_runnable(image_name)):
599 name = "Run custom image"
600 is_runnable = False
601 if created == True:
602 # separator
603 #label = gtk.Label(" or ")
604 #self.details_bottom_buttons.pack_end(label, expand=False, fill=False)
605
606 # create button "Run image"
607 run_button = HobAltButton(name)
608 else:
609 # create button "Run image" as the primary button
610 run_button = HobButton(name)
611 #run_button.set_size_request(205, 49)
612 run_button.set_flags(gtk.CAN_DEFAULT)
613 packed = True
614 if is_runnable:
615 run_button.set_tooltip_text("Start up an image with qemu emulator")
616 else:
617 run_button.set_tooltip_text("Start up an image with custom simulator")
618 button_id = run_button.connect("clicked", self.run_button_clicked_cb)
619 self.button_ids[button_id] = run_button
620 self.details_bottom_buttons.pack_end(run_button, expand=False, fill=False)
621 created = True
622
623 name = "Save image recipe"
624 if name in buttonlist and self.builder.recipe_model.is_custom_image():
625 save_button = HobAltButton("Save image recipe")
626 save_button.set_tooltip_text("Keep your changes saving them as an image recipe")
627 save_button.set_sensitive(not self.image_saved)
628 button_id = save_button.connect("clicked", self.save_button_clicked_cb)
629 self.button_ids[button_id] = save_button
630 self.details_bottom_buttons.pack_end(save_button, expand=False, fill=False)
631
632 name = "Build new image"
633 if name in buttonlist:
634 # create button "Build new image"
635 if packed:
636 build_new_button = HobAltButton("Build new image")
637 else:
638 build_new_button = HobButton("Build new image")
639 build_new_button.set_flags(gtk.CAN_DEFAULT)
640 #build_new_button.set_size_request(205, 49)
641 self.details_bottom_buttons.pack_end(build_new_button, expand=False, fill=False)
642 build_new_button.set_tooltip_text("Create a new image from scratch")
643 button_id = build_new_button.connect("clicked", self.build_new_button_clicked_cb)
644 self.button_ids[button_id] = build_new_button
645
646 return is_runnable
647
648 def deploy_button_clicked_cb(self, button):
649 if self.toggled_image:
650 if self.num_toggled > 1:
651 self.set_sensitive(False)
652 self.show_builded_images_dialog(None, "Deploy image")
653 self.set_sensitive(True)
654 else:
655 self.builder.deploy_image(self.toggled_image)
656
657 def run_button_clicked_cb(self, button):
658 if self.toggled_image:
659 if self.num_toggled > 1:
660 self.set_sensitive(False)
661 self.show_builded_images_dialog(None, "Run image")
662 self.set_sensitive(True)
663 else:
664 if self.is_runnable:
665 self.builder.runqemu_image(self.toggled_image, self.sel_kernel)
666 else:
667 self.builder.run_custom_image(self.toggled_image, self.run_script_path)
668
669 def save_button_clicked_cb(self, button):
670 topdir = self.builder.get_topdir()
671 images_dir = topdir + "/recipes/images/custom/"
672 self.builder.ensure_dir(images_dir)
673
674 self.name_field_template = self.builder.image_configuration_page.custom_image_selected
675 if self.name_field_template:
676 image_path = self.builder.recipe_model.pn_path[self.name_field_template]
677 image_iter = self.builder.recipe_model.get_iter(image_path)
678 self.description_field_template = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_DESC)
679 else:
680 self.name_field_template = ""
681
682 dialog = SaveImageDialog(images_dir, self.name_field_template, self.description_field_template,
683 "Save image recipe", self.builder, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
684 response = dialog.run()
685 dialog.destroy()
686
687 def build_new_button_clicked_cb(self, button):
688 self.builder.initiate_new_build_async()
689
690 def edit_config_button_clicked_cb(self, button):
691 self.builder.show_configuration()
692
693 def edit_packages_button_clicked_cb(self, button):
694 self.builder.show_packages()
695
696 def my_images_button_clicked_cb(self, button):
697 self.builder.show_load_my_images_dialog()
698
699 def settings_button_clicked_cb(self, button):
700 # Create an advanced settings dialog
701 response, settings_changed = self.builder.show_simple_settings_dialog()
702 if not response:
703 return
704 if settings_changed:
705 self.builder.reparse_post_adv_settings()
diff --git a/bitbake/lib/bb/ui/crumbs/packageselectionpage.py b/bitbake/lib/bb/ui/crumbs/packageselectionpage.py
deleted file mode 100755
index 7c62b36e6b..0000000000
--- a/bitbake/lib/bb/ui/crumbs/packageselectionpage.py
+++ /dev/null
@@ -1,355 +0,0 @@
1#!/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 glib
25from bb.ui.crumbs.hobcolor import HobColors
26from bb.ui.crumbs.hobwidget import HobViewTable, HobNotebook, HobAltButton, HobButton
27from bb.ui.crumbs.hoblistmodel import PackageListModel
28from bb.ui.crumbs.hobpages import HobPage
29
30#
31# PackageSelectionPage
32#
33class PackageSelectionPage (HobPage):
34
35 pages = [
36 {
37 'name' : 'Included packages',
38 'tooltip' : 'The packages currently included for your image',
39 'filter' : { PackageListModel.COL_INC : [True] },
40 'search' : 'Search packages by name',
41 'searchtip' : 'Enter a package name to find it',
42 'columns' : [{
43 'col_name' : 'Package name',
44 'col_id' : PackageListModel.COL_NAME,
45 'col_style': 'text',
46 'col_min' : 100,
47 'col_max' : 300,
48 'expand' : 'True'
49 }, {
50 'col_name' : 'Size',
51 'col_id' : PackageListModel.COL_SIZE,
52 'col_style': 'text',
53 'col_min' : 100,
54 'col_max' : 300,
55 'expand' : 'True'
56 }, {
57 'col_name' : 'Recipe',
58 'col_id' : PackageListModel.COL_RCP,
59 'col_style': 'text',
60 'col_min' : 100,
61 'col_max' : 250,
62 'expand' : 'True'
63 }, {
64 'col_name' : 'Brought in by (+others)',
65 'col_id' : PackageListModel.COL_BINB,
66 'col_style': 'binb',
67 'col_min' : 100,
68 'col_max' : 350,
69 'expand' : 'True'
70 }, {
71 'col_name' : 'Included',
72 'col_id' : PackageListModel.COL_INC,
73 'col_style': 'check toggle',
74 'col_min' : 100,
75 'col_max' : 100
76 }]
77 }, {
78 'name' : 'All packages',
79 'tooltip' : 'All packages that have been built',
80 'filter' : {},
81 'search' : 'Search packages by name',
82 'searchtip' : 'Enter a package name to find it',
83 'columns' : [{
84 'col_name' : 'Package name',
85 'col_id' : PackageListModel.COL_NAME,
86 'col_style': 'text',
87 'col_min' : 100,
88 'col_max' : 400,
89 'expand' : 'True'
90 }, {
91 'col_name' : 'Size',
92 'col_id' : PackageListModel.COL_SIZE,
93 'col_style': 'text',
94 'col_min' : 100,
95 'col_max' : 500,
96 'expand' : 'True'
97 }, {
98 'col_name' : 'Recipe',
99 'col_id' : PackageListModel.COL_RCP,
100 'col_style': 'text',
101 'col_min' : 100,
102 'col_max' : 250,
103 'expand' : 'True'
104 }, {
105 'col_name' : 'Included',
106 'col_id' : PackageListModel.COL_INC,
107 'col_style': 'check toggle',
108 'col_min' : 100,
109 'col_max' : 100
110 }]
111 }
112 ]
113
114 (INCLUDED,
115 ALL) = range(2)
116
117 def __init__(self, builder):
118 super(PackageSelectionPage, self).__init__(builder, "Edit packages")
119
120 # set invisible members
121 self.recipe_model = self.builder.recipe_model
122 self.package_model = self.builder.package_model
123
124 # create visual elements
125 self.create_visual_elements()
126
127 def included_clicked_cb(self, button):
128 self.ins.set_current_page(self.INCLUDED)
129
130 def create_visual_elements(self):
131 self.label = gtk.Label("Packages included: 0\nSelected packages size: 0 MB")
132 self.eventbox = self.add_onto_top_bar(self.label, 73)
133 self.pack_start(self.eventbox, expand=False, fill=False)
134 self.pack_start(self.group_align, expand=True, fill=True)
135
136 # set visible members
137 self.ins = HobNotebook()
138 self.tables = [] # we need to modify table when the dialog is shown
139
140 search_names = []
141 search_tips = []
142 # append the tab
143 for page in self.pages:
144 columns = page['columns']
145 name = page['name']
146 tab = HobViewTable(columns, name)
147 search_names.append(page['search'])
148 search_tips.append(page['searchtip'])
149 filter = page['filter']
150 sort_model = self.package_model.tree_model(filter, initial=True)
151 tab.set_model(sort_model)
152 tab.connect("toggled", self.table_toggled_cb, name)
153 tab.connect("button-release-event", self.button_click_cb)
154 tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include, filter)
155 self.ins.append_page(tab, page['name'], page['tooltip'])
156 self.tables.append(tab)
157
158 self.ins.set_entry(search_names, search_tips)
159 self.ins.search.connect("changed", self.search_entry_changed)
160
161 # add all into the dialog
162 self.box_group_area.pack_start(self.ins, expand=True, fill=True)
163
164 self.button_box = gtk.HBox(False, 6)
165 self.box_group_area.pack_start(self.button_box, expand=False, fill=False)
166
167 self.build_image_button = HobButton('Build image')
168 #self.build_image_button.set_size_request(205, 49)
169 self.build_image_button.set_tooltip_text("Build target image")
170 self.build_image_button.set_flags(gtk.CAN_DEFAULT)
171 self.build_image_button.grab_default()
172 self.build_image_button.connect("clicked", self.build_image_clicked_cb)
173 self.button_box.pack_end(self.build_image_button, expand=False, fill=False)
174
175 self.back_button = HobAltButton('Cancel')
176 self.back_button.connect("clicked", self.back_button_clicked_cb)
177 self.button_box.pack_end(self.back_button, expand=False, fill=False)
178
179 def search_entry_changed(self, entry):
180 text = entry.get_text()
181 if self.ins.search_focus:
182 self.ins.search_focus = False
183 elif self.ins.page_changed:
184 self.ins.page_change = False
185 self.filter_search(entry)
186 elif text not in self.ins.search_names:
187 self.filter_search(entry)
188
189 def filter_search(self, entry):
190 text = entry.get_text()
191 current_tab = self.ins.get_current_page()
192 filter = self.pages[current_tab]['filter']
193 filter[PackageListModel.COL_NAME] = text
194 self.tables[current_tab].set_model(self.package_model.tree_model(filter, search_data=text))
195 if self.package_model.filtered_nb == 0:
196 if not self.ins.get_nth_page(current_tab).top_bar:
197 self.ins.get_nth_page(current_tab).add_no_result_bar(entry)
198 self.ins.get_nth_page(current_tab).top_bar.set_no_show_all(True)
199 self.ins.get_nth_page(current_tab).top_bar.show()
200 self.ins.get_nth_page(current_tab).scroll.hide()
201 else:
202 if self.ins.get_nth_page(current_tab).top_bar:
203 self.ins.get_nth_page(current_tab).top_bar.hide()
204 self.ins.get_nth_page(current_tab).scroll.show()
205 if entry.get_text() == '':
206 entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
207 else:
208 entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, True)
209
210 def button_click_cb(self, widget, event):
211 path, col = widget.table_tree.get_cursor()
212 tree_model = widget.table_tree.get_model()
213 if path and col.get_title() != 'Included': # else activation is likely a removal
214 properties = {'binb': '' , 'name': '', 'size':'', 'recipe':'', 'files_list':''}
215 properties['binb'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_BINB)
216 properties['name'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_NAME)
217 properties['size'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_SIZE)
218 properties['recipe'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_RCP)
219 properties['files_list'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_FLIST)
220
221 self.builder.show_recipe_property_dialog(properties)
222
223 def open_log_clicked_cb(self, button, log_file):
224 if log_file:
225 log_file = "file:///" + log_file
226 gtk.show_uri(screen=button.get_screen(), uri=log_file, timestamp=0)
227
228 def show_page(self, log_file):
229 children = self.button_box.get_children() or []
230 for child in children:
231 self.button_box.remove(child)
232 # re-packed the buttons as request, add the 'open log' button if build success
233 self.button_box.pack_end(self.build_image_button, expand=False, fill=False)
234 if log_file:
235 open_log_button = HobAltButton("Open log")
236 open_log_button.connect("clicked", self.open_log_clicked_cb, log_file)
237 open_log_button.set_tooltip_text("Open the build's log file")
238 self.button_box.pack_end(open_log_button, expand=False, fill=False)
239 self.button_box.pack_end(self.back_button, expand=False, fill=False)
240 self.show_all()
241
242 def build_image_clicked_cb(self, button):
243 self.builder.parsing_warnings = []
244 self.builder.build_image()
245
246 def refresh_tables(self):
247 self.ins.reset_entry(self.ins.search, 0)
248 for tab in self.tables:
249 index = self.tables.index(tab)
250 filter = self.pages[index]['filter']
251 tab.set_model(self.package_model.tree_model(filter, initial=True))
252
253 def back_button_clicked_cb(self, button):
254 if self.builder.previous_step == self.builder.IMAGE_GENERATED:
255 self.builder.restore_initial_selected_packages()
256 self.refresh_selection()
257 self.builder.show_image_details()
258 else:
259 self.builder.show_configuration()
260 self.refresh_tables()
261
262 def refresh_selection(self):
263 self.builder.configuration.selected_packages = self.package_model.get_selected_packages()
264 self.builder.configuration.user_selected_packages = self.package_model.get_user_selected_packages()
265 selected_packages_num = len(self.builder.configuration.selected_packages)
266 selected_packages_size = self.package_model.get_packages_size()
267 selected_packages_size_str = HobPage._size_to_string(selected_packages_size)
268
269 if self.builder.configuration.image_packages == self.builder.configuration.selected_packages:
270 image_total_size_str = self.builder.configuration.image_size
271 else:
272 image_overhead_factor = self.builder.configuration.image_overhead_factor
273 image_rootfs_size = self.builder.configuration.image_rootfs_size / 1024 # image_rootfs_size is KB
274 image_extra_size = self.builder.configuration.image_extra_size / 1024 # image_extra_size is KB
275 base_size = image_overhead_factor * selected_packages_size
276 image_total_size = max(base_size, image_rootfs_size) + image_extra_size
277 if "zypper" in self.builder.configuration.selected_packages:
278 image_total_size += (51200 * 1024)
279 image_total_size_str = HobPage._size_to_string(image_total_size)
280
281 self.label.set_label("Packages included: %s\nSelected packages size: %s\nEstimated image size: %s" %
282 (selected_packages_num, selected_packages_size_str, image_total_size_str))
283 self.ins.show_indicator_icon("Included packages", selected_packages_num)
284
285 def toggle_item_idle_cb(self, path, view_tree, cell, pagename):
286 if not self.package_model.path_included(path):
287 self.package_model.include_item(item_path=path, binb="User Selected")
288 else:
289 self.pre_fadeout_checkout_include(view_tree)
290 self.package_model.exclude_item(item_path=path)
291 self.render_fadeout(view_tree, cell)
292
293 self.refresh_selection()
294 if not self.builder.customized:
295 self.builder.customized = True
296 self.builder.set_base_image()
297 self.builder.configuration.selected_image = self.recipe_model.__custom_image__
298 self.builder.rcppkglist_populated()
299
300 self.builder.window_sensitive(True)
301 view_model = view_tree.get_model()
302 vpath = self.package_model.convert_path_to_vpath(view_model, path)
303 view_tree.set_cursor(vpath)
304
305 def table_toggled_cb(self, table, cell, view_path, toggled_columnid, view_tree, pagename):
306 # Click to include a package
307 self.builder.window_sensitive(False)
308 view_model = view_tree.get_model()
309 path = self.package_model.convert_vpath_to_path(view_model, view_path)
310 glib.idle_add(self.toggle_item_idle_cb, path, view_tree, cell, pagename)
311
312 def pre_fadeout_checkout_include(self, tree):
313 #after the fadeout the table will be sorted as before
314 self.sort_column_id = self.package_model.sort_column_id
315 self.sort_order = self.package_model.sort_order
316
317 self.package_model.resync_fadeout_column(self.package_model.get_iter_first())
318 # Check out a model which base on the column COL_FADE_INC,
319 # it's save the prev state of column COL_INC before do exclude_item
320 filter = { PackageListModel.COL_FADE_INC : [True]}
321 new_model = self.package_model.tree_model(filter, excluded_items_ahead=True)
322 tree.set_model(new_model)
323 tree.expand_all()
324
325 def get_excluded_rows(self, to_render_cells, model, it):
326 while it:
327 path = model.get_path(it)
328 prev_cell_is_active = model.get_value(it, PackageListModel.COL_FADE_INC)
329 curr_cell_is_active = model.get_value(it, PackageListModel.COL_INC)
330 if (prev_cell_is_active == True) and (curr_cell_is_active == False):
331 to_render_cells.append(path)
332 if model.iter_has_child(it):
333 self.get_excluded_rows(to_render_cells, model, model.iter_children(it))
334 it = model.iter_next(it)
335
336 return to_render_cells
337
338 def render_fadeout(self, tree, cell):
339 if (not cell) or (not tree):
340 return
341 to_render_cells = []
342 view_model = tree.get_model()
343 self.get_excluded_rows(to_render_cells, view_model, view_model.get_iter_first())
344
345 cell.fadeout(tree, 1000, to_render_cells)
346
347 def after_fadeout_checkin_include(self, table, ctrl, cell, tree, filter):
348 self.package_model.sort_column_id = self.sort_column_id
349 self.package_model.sort_order = self.sort_order
350 tree.set_model(self.package_model.tree_model(filter))
351 tree.expand_all()
352
353 def set_packages_curr_tab(self, curr_page):
354 self.ins.set_current_page(curr_page)
355
diff --git a/bitbake/lib/bb/ui/crumbs/recipeselectionpage.py b/bitbake/lib/bb/ui/crumbs/recipeselectionpage.py
deleted file mode 100755
index 58db43f706..0000000000
--- a/bitbake/lib/bb/ui/crumbs/recipeselectionpage.py
+++ /dev/null
@@ -1,335 +0,0 @@
1#!/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 glib
25from bb.ui.crumbs.hobcolor import HobColors
26from bb.ui.crumbs.hobwidget import HobViewTable, HobNotebook, HobAltButton, HobButton
27from bb.ui.crumbs.hoblistmodel import RecipeListModel
28from bb.ui.crumbs.hobpages import HobPage
29
30#
31# RecipeSelectionPage
32#
33class RecipeSelectionPage (HobPage):
34 pages = [
35 {
36 'name' : 'Included recipes',
37 'tooltip' : 'The recipes currently included for your image',
38 'filter' : { RecipeListModel.COL_INC : [True],
39 RecipeListModel.COL_TYPE : ['recipe', 'packagegroup'] },
40 'search' : 'Search recipes by name',
41 'searchtip' : 'Enter a recipe name to find it',
42 'columns' : [{
43 'col_name' : 'Recipe name',
44 'col_id' : RecipeListModel.COL_NAME,
45 'col_style': 'text',
46 'col_min' : 100,
47 'col_max' : 400,
48 'expand' : 'True'
49 }, {
50 'col_name' : 'Group',
51 'col_id' : RecipeListModel.COL_GROUP,
52 'col_style': 'text',
53 'col_min' : 100,
54 'col_max' : 300,
55 'expand' : 'True'
56 }, {
57 'col_name' : 'Brought in by (+others)',
58 'col_id' : RecipeListModel.COL_BINB,
59 'col_style': 'binb',
60 'col_min' : 100,
61 'col_max' : 500,
62 'expand' : 'True'
63 }, {
64 'col_name' : 'Included',
65 'col_id' : RecipeListModel.COL_INC,
66 'col_style': 'check toggle',
67 'col_min' : 100,
68 'col_max' : 100
69 }]
70 }, {
71 'name' : 'All recipes',
72 'tooltip' : 'All recipes in your configured layers',
73 'filter' : { RecipeListModel.COL_TYPE : ['recipe'] },
74 'search' : 'Search recipes by name',
75 'searchtip' : 'Enter a recipe name to find it',
76 'columns' : [{
77 'col_name' : 'Recipe name',
78 'col_id' : RecipeListModel.COL_NAME,
79 'col_style': 'text',
80 'col_min' : 100,
81 'col_max' : 400,
82 'expand' : 'True'
83 }, {
84 'col_name' : 'Group',
85 'col_id' : RecipeListModel.COL_GROUP,
86 'col_style': 'text',
87 'col_min' : 100,
88 'col_max' : 400,
89 'expand' : 'True'
90 }, {
91 'col_name' : 'License',
92 'col_id' : RecipeListModel.COL_LIC,
93 'col_style': 'text',
94 'col_min' : 100,
95 'col_max' : 400,
96 'expand' : 'True'
97 }, {
98 'col_name' : 'Included',
99 'col_id' : RecipeListModel.COL_INC,
100 'col_style': 'check toggle',
101 'col_min' : 100,
102 'col_max' : 100
103 }]
104 }, {
105 'name' : 'Package Groups',
106 'tooltip' : 'All package groups in your configured layers',
107 'filter' : { RecipeListModel.COL_TYPE : ['packagegroup'] },
108 'search' : 'Search package groups by name',
109 'searchtip' : 'Enter a package group name to find it',
110 'columns' : [{
111 'col_name' : 'Package group name',
112 'col_id' : RecipeListModel.COL_NAME,
113 'col_style': 'text',
114 'col_min' : 100,
115 'col_max' : 400,
116 'expand' : 'True'
117 }, {
118 'col_name' : 'Included',
119 'col_id' : RecipeListModel.COL_INC,
120 'col_style': 'check toggle',
121 'col_min' : 100,
122 'col_max' : 100
123 }]
124 }
125 ]
126
127 (INCLUDED,
128 ALL,
129 TASKS) = range(3)
130
131 def __init__(self, builder = None):
132 super(RecipeSelectionPage, self).__init__(builder, "Step 1 of 2: Edit recipes")
133
134 # set invisible members
135 self.recipe_model = self.builder.recipe_model
136
137 # create visual elements
138 self.create_visual_elements()
139
140 def included_clicked_cb(self, button):
141 self.ins.set_current_page(self.INCLUDED)
142
143 def create_visual_elements(self):
144 self.eventbox = self.add_onto_top_bar(None, 73)
145 self.pack_start(self.eventbox, expand=False, fill=False)
146 self.pack_start(self.group_align, expand=True, fill=True)
147
148 # set visible members
149 self.ins = HobNotebook()
150 self.tables = [] # we need modify table when the dialog is shown
151
152 search_names = []
153 search_tips = []
154 # append the tabs in order
155 for page in self.pages:
156 columns = page['columns']
157 name = page['name']
158 tab = HobViewTable(columns, name)
159 search_names.append(page['search'])
160 search_tips.append(page['searchtip'])
161 filter = page['filter']
162 sort_model = self.recipe_model.tree_model(filter, initial=True)
163 tab.set_model(sort_model)
164 tab.connect("toggled", self.table_toggled_cb, name)
165 tab.connect("button-release-event", self.button_click_cb)
166 tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include, filter)
167 self.ins.append_page(tab, page['name'], page['tooltip'])
168 self.tables.append(tab)
169
170 self.ins.set_entry(search_names, search_tips)
171 self.ins.search.connect("changed", self.search_entry_changed)
172
173 # add all into the window
174 self.box_group_area.pack_start(self.ins, expand=True, fill=True)
175
176 button_box = gtk.HBox(False, 6)
177 self.box_group_area.pack_end(button_box, expand=False, fill=False)
178
179 self.build_packages_button = HobButton('Build packages')
180 #self.build_packages_button.set_size_request(205, 49)
181 self.build_packages_button.set_tooltip_text("Build selected recipes into packages")
182 self.build_packages_button.set_flags(gtk.CAN_DEFAULT)
183 self.build_packages_button.grab_default()
184 self.build_packages_button.connect("clicked", self.build_packages_clicked_cb)
185 button_box.pack_end(self.build_packages_button, expand=False, fill=False)
186
187 self.back_button = HobAltButton('Cancel')
188 self.back_button.connect("clicked", self.back_button_clicked_cb)
189 button_box.pack_end(self.back_button, expand=False, fill=False)
190
191 def search_entry_changed(self, entry):
192 text = entry.get_text()
193 if self.ins.search_focus:
194 self.ins.search_focus = False
195 elif self.ins.page_changed:
196 self.ins.page_change = False
197 self.filter_search(entry)
198 elif text not in self.ins.search_names:
199 self.filter_search(entry)
200
201 def filter_search(self, entry):
202 text = entry.get_text()
203 current_tab = self.ins.get_current_page()
204 filter = self.pages[current_tab]['filter']
205 filter[RecipeListModel.COL_NAME] = text
206 self.tables[current_tab].set_model(self.recipe_model.tree_model(filter, search_data=text))
207 if self.recipe_model.filtered_nb == 0:
208 if not self.ins.get_nth_page(current_tab).top_bar:
209 self.ins.get_nth_page(current_tab).add_no_result_bar(entry)
210 self.ins.get_nth_page(current_tab).top_bar.set_no_show_all(True)
211 self.ins.get_nth_page(current_tab).top_bar.show()
212 self.ins.get_nth_page(current_tab).scroll.hide()
213 else:
214 if self.ins.get_nth_page(current_tab).top_bar:
215 self.ins.get_nth_page(current_tab).top_bar.hide()
216 self.ins.get_nth_page(current_tab).scroll.show()
217 if entry.get_text() == '':
218 entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
219 else:
220 entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, True)
221
222 def button_click_cb(self, widget, event):
223 path, col = widget.table_tree.get_cursor()
224 tree_model = widget.table_tree.get_model()
225 if path and col.get_title() != 'Included': # else activation is likely a removal
226 properties = {'summary': '', 'name': '', 'version': '', 'revision': '', 'binb': '', 'group': '', 'license': '', 'homepage': '', 'bugtracker': '', 'description': ''}
227 properties['summary'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_SUMMARY)
228 properties['name'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_NAME)
229 properties['version'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_VERSION)
230 properties['revision'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_REVISION)
231 properties['binb'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_BINB)
232 properties['group'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_GROUP)
233 properties['license'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_LIC)
234 properties['homepage'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_HOMEPAGE)
235 properties['bugtracker'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_BUGTRACKER)
236 properties['description'] = tree_model.get_value(tree_model.get_iter(path), RecipeListModel.COL_DESC)
237 self.builder.show_recipe_property_dialog(properties)
238
239 def build_packages_clicked_cb(self, button):
240 self.refresh_tables()
241 self.builder.build_packages()
242
243 def refresh_tables(self):
244 self.ins.reset_entry(self.ins.search, 0)
245 for tab in self.tables:
246 index = self.tables.index(tab)
247 filter = self.pages[index]['filter']
248 tab.set_model(self.recipe_model.tree_model(filter, search_data="", initial=True))
249
250 def back_button_clicked_cb(self, button):
251 self.builder.recipe_model.set_selected_image(self.builder.configuration.initial_selected_image)
252 self.builder.image_configuration_page.update_image_combo(self.builder.recipe_model, self.builder.configuration.initial_selected_image)
253 self.builder.image_configuration_page.update_image_desc()
254 self.builder.show_configuration()
255 self.refresh_tables()
256
257 def refresh_selection(self):
258 self.builder.configuration.selected_image = self.recipe_model.get_selected_image()
259 _, self.builder.configuration.selected_recipes = self.recipe_model.get_selected_recipes()
260 self.ins.show_indicator_icon("Included recipes", len(self.builder.configuration.selected_recipes))
261
262 def toggle_item_idle_cb(self, path, view_tree, cell, pagename):
263 if not self.recipe_model.path_included(path):
264 self.recipe_model.include_item(item_path=path, binb="User Selected", image_contents=False)
265 else:
266 self.pre_fadeout_checkout_include(view_tree, pagename)
267 self.recipe_model.exclude_item(item_path=path)
268 self.render_fadeout(view_tree, cell)
269
270 self.refresh_selection()
271 if not self.builder.customized:
272 self.builder.customized = True
273 self.builder.configuration.selected_image = self.recipe_model.__custom_image__
274 self.builder.rcppkglist_populated()
275
276 self.builder.window_sensitive(True)
277
278 view_model = view_tree.get_model()
279 vpath = self.recipe_model.convert_path_to_vpath(view_model, path)
280 view_tree.set_cursor(vpath)
281
282 def table_toggled_cb(self, table, cell, view_path, toggled_columnid, view_tree, pagename):
283 # Click to include a recipe
284 self.builder.window_sensitive(False)
285 view_model = view_tree.get_model()
286 path = self.recipe_model.convert_vpath_to_path(view_model, view_path)
287 glib.idle_add(self.toggle_item_idle_cb, path, view_tree, cell, pagename)
288
289 def pre_fadeout_checkout_include(self, tree, pagename):
290 #after the fadeout the table will be sorted as before
291 self.sort_column_id = self.recipe_model.sort_column_id
292 self.sort_order = self.recipe_model.sort_order
293
294 #resync the included items to a backup fade include column
295 it = self.recipe_model.get_iter_first()
296 while it:
297 active = self.recipe_model.get_value(it, self.recipe_model.COL_INC)
298 self.recipe_model.set(it, self.recipe_model.COL_FADE_INC, active)
299 it = self.recipe_model.iter_next(it)
300 # Check out a model which base on the column COL_FADE_INC,
301 # it's save the prev state of column COL_INC before do exclude_item
302 filter = { RecipeListModel.COL_FADE_INC:[True] }
303 if pagename == "Included recipes":
304 filter[RecipeListModel.COL_TYPE] = ['recipe', 'packagegroup']
305 elif pagename == "All recipes":
306 filter[RecipeListModel.COL_TYPE] = ['recipe']
307 else:
308 filter[RecipeListModel.COL_TYPE] = ['packagegroup']
309
310 new_model = self.recipe_model.tree_model(filter, excluded_items_ahead=True)
311 tree.set_model(new_model)
312
313 def render_fadeout(self, tree, cell):
314 if (not cell) or (not tree):
315 return
316 to_render_cells = []
317 model = tree.get_model()
318 it = model.get_iter_first()
319 while it:
320 path = model.get_path(it)
321 prev_cell_is_active = model.get_value(it, RecipeListModel.COL_FADE_INC)
322 curr_cell_is_active = model.get_value(it, RecipeListModel.COL_INC)
323 if (prev_cell_is_active == True) and (curr_cell_is_active == False):
324 to_render_cells.append(path)
325 it = model.iter_next(it)
326
327 cell.fadeout(tree, 1000, to_render_cells)
328
329 def after_fadeout_checkin_include(self, table, ctrl, cell, tree, filter):
330 self.recipe_model.sort_column_id = self.sort_column_id
331 self.recipe_model.sort_order = self.sort_order
332 tree.set_model(self.recipe_model.tree_model(filter))
333
334 def set_recipe_curr_tab(self, curr_page):
335 self.ins.set_current_page(curr_page)
diff --git a/bitbake/lib/bb/ui/crumbs/sanitycheckpage.py b/bitbake/lib/bb/ui/crumbs/sanitycheckpage.py
deleted file mode 100644
index 76ce2ecc23..0000000000
--- a/bitbake/lib/bb/ui/crumbs/sanitycheckpage.py
+++ /dev/null
@@ -1,85 +0,0 @@
1#!/usr/bin/env python
2#
3# BitBake Graphical GTK User Interface
4#
5# Copyright (C) 2012 Intel Corporation
6#
7# Authored by Bogdan Marinescu <bogdan.a.marinescu@intel.com>
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License version 2 as
11# published by the Free Software Foundation.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License along
19# with this program; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22import gtk, gobject
23from bb.ui.crumbs.progressbar import HobProgressBar
24from bb.ui.crumbs.hobwidget import hic
25from bb.ui.crumbs.hobpages import HobPage
26
27#
28# SanityCheckPage
29#
30class SanityCheckPage (HobPage):
31
32 def __init__(self, builder):
33 super(SanityCheckPage, self).__init__(builder)
34 self.running = False
35 self.create_visual_elements()
36 self.show_all()
37
38 def make_label(self, text, bold=True):
39 label = gtk.Label()
40 label.set_alignment(0.0, 0.5)
41 mark = "<span %s>%s</span>" % (self.span_tag('x-large', 'bold') if bold else self.span_tag('medium'), text)
42 label.set_markup(mark)
43 return label
44
45 def start(self):
46 if not self.running:
47 self.running = True
48 gobject.timeout_add(100, self.timer_func)
49
50 def stop(self):
51 self.running = False
52
53 def is_running(self):
54 return self.running
55
56 def timer_func(self):
57 self.progress_bar.pulse()
58 return self.running
59
60 def create_visual_elements(self):
61 # Table'd layout. 'rows' and 'cols' give the table size
62 rows, cols = 30, 50
63 self.table = gtk.Table(rows, cols, True)
64 self.pack_start(self.table, expand=False, fill=False)
65 sx, sy = 2, 2
66 # 'info' icon
67 image = gtk.Image()
68 image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
69 self.table.attach(image, sx, sx + 2, sy, sy + 3 )
70 image.show()
71 # 'Checking' message
72 label = self.make_label('Hob is checking for correct build system setup')
73 self.table.attach(label, sx + 2, cols, sy, sy + 3, xpadding=5 )
74 label.show()
75 # 'Shouldn't take long' message.
76 label = self.make_label("The check shouldn't take long.", False)
77 self.table.attach(label, sx + 2, cols, sy + 3, sy + 4, xpadding=5)
78 label.show()
79 # Progress bar
80 self.progress_bar = HobProgressBar()
81 self.table.attach(self.progress_bar, sx + 2, cols - 3, sy + 5, sy + 7, xpadding=5)
82 self.progress_bar.show()
83 # All done
84 self.table.show()
85
diff --git a/bitbake/lib/bb/ui/hob.py b/bitbake/lib/bb/ui/hob.py
deleted file mode 100755
index da5b411891..0000000000
--- a/bitbake/lib/bb/ui/hob.py
+++ /dev/null
@@ -1,109 +0,0 @@
1#!/usr/bin/env python
2#
3# BitBake Graphical GTK User Interface
4#
5# Copyright (C) 2011 Intel Corporation
6#
7# Authored by Joshua Lock <josh@linux.intel.com>
8# Authored by Dongxiao Xu <dongxiao.xu@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 sys
24import os
25requirements = "FATAL: Hob requires Gtk+ 2.20.0 or higher, PyGtk 2.21.0 or higher"
26try:
27 import gobject
28 import gtk
29 import pygtk
30 pygtk.require('2.0') # to be certain we don't have gtk+ 1.x !?!
31 gtkver = gtk.gtk_version
32 pygtkver = gtk.pygtk_version
33 if gtkver < (2, 20, 0) or pygtkver < (2, 21, 0):
34 sys.exit("%s,\nYou have Gtk+ %s and PyGtk %s." % (requirements,
35 ".".join(map(str, gtkver)),
36 ".".join(map(str, pygtkver))))
37except ImportError as exc:
38 sys.exit("%s (%s)." % (requirements, str(exc)))
39sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
40try:
41 import bb
42except RuntimeError as exc:
43 sys.exit(str(exc))
44from bb.ui import uihelper
45from bb.ui.crumbs.hoblistmodel import RecipeListModel, PackageListModel
46from bb.ui.crumbs.hobeventhandler import HobHandler
47from bb.ui.crumbs.builder import Builder
48
49featureSet = [bb.cooker.CookerFeatures.HOB_EXTRA_CACHES, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
50
51def event_handle_idle_func(eventHandler, hobHandler):
52 # Consume as many messages as we can in the time available to us
53 if not eventHandler:
54 return False
55 event = eventHandler.getEvent()
56 while event:
57 hobHandler.handle_event(event)
58 event = eventHandler.getEvent()
59 return True
60
61_evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.LogRecord",
62 "bb.build.TaskFailed", "bb.build.TaskBase", "bb.event.ParseStarted",
63 "bb.event.ParseProgress", "bb.event.ParseCompleted", "bb.event.CacheLoadStarted",
64 "bb.event.CacheLoadProgress", "bb.event.CacheLoadCompleted", "bb.command.CommandFailed",
65 "bb.command.CommandExit", "bb.command.CommandCompleted", "bb.cooker.CookerExit",
66 "bb.event.MultipleProviders", "bb.event.NoProvider", "bb.runqueue.sceneQueueTaskStarted",
67 "bb.runqueue.runQueueTaskStarted", "bb.runqueue.runQueueTaskFailed", "bb.runqueue.sceneQueueTaskFailed",
68 "bb.event.BuildBase", "bb.build.TaskStarted", "bb.build.TaskSucceeded", "bb.build.TaskFailedSilent",
69 "bb.event.SanityCheckPassed", "bb.event.SanityCheckFailed", "bb.event.PackageInfo",
70 "bb.event.TargetsTreeGenerated", "bb.event.ConfigFilesFound", "bb.event.ConfigFilePathFound",
71 "bb.event.FilesMatchingFound", "bb.event.NetworkTestFailed", "bb.event.NetworkTestPassed",
72 "bb.event.BuildStarted", "bb.event.BuildCompleted", "bb.event.DiskFull"]
73
74def main (server, eventHandler, params):
75 params.updateFromServer(server)
76 gobject.threads_init()
77
78 # That indicates whether the Hob and the bitbake server are
79 # running on different machines
80 # recipe model and package model
81 recipe_model = RecipeListModel()
82 package_model = PackageListModel()
83
84 llevel, debug_domains = bb.msg.constructLogOptions()
85 server.runCommand(["setEventMask", server.getEventHandle(), llevel, debug_domains, _evt_list])
86 hobHandler = HobHandler(server, recipe_model, package_model)
87 builder = Builder(hobHandler, recipe_model, package_model)
88
89 # This timeout function regularly probes the event queue to find out if we
90 # have any messages waiting for us.
91 gobject.timeout_add(10, event_handle_idle_func, eventHandler, hobHandler)
92
93 try:
94 gtk.main()
95 except EnvironmentError as ioerror:
96 # ignore interrupted io
97 if ioerror.args[0] == 4:
98 pass
99 finally:
100 hobHandler.cancel_build(force = True)
101
102if __name__ == "__main__":
103 try:
104 ret = main()
105 except Exception:
106 ret = 1
107 import traceback
108 traceback.print_exc(15)
109 sys.exit(ret)