Hob: fixed issue about 'select a base image' combo not sensitive
[bitbake.git] / lib / bb / ui / crumbs / builder.py
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
24 import gtk
25 import copy
26 import os
27 import subprocess
28 import shlex
29 import re
30 from bb.ui.crumbs.template import TemplateMgr
31 from bb.ui.crumbs.imageconfigurationpage import ImageConfigurationPage
32 from bb.ui.crumbs.recipeselectionpage import RecipeSelectionPage
33 from bb.ui.crumbs.packageselectionpage import PackageSelectionPage
34 from bb.ui.crumbs.builddetailspage import BuildDetailsPage
35 from bb.ui.crumbs.imagedetailspage import ImageDetailsPage
36 from bb.ui.crumbs.hobwidget import hwc, HobButton, HobAltButton, hcc
37 from bb.ui.crumbs.hig import CrumbsMessageDialog, ImageSelectionDialog, \
38                              AdvancedSettingDialog, LayerSelectionDialog, \
39                              DeployImageDialog
40 from bb.ui.crumbs.persistenttooltip import PersistentTooltip
41 import bb.ui.crumbs.utils
42
43 hobVer = 20120530
44
45 class Configuration:
46     '''Represents the data structure of configuration.'''
47
48     @classmethod
49     def parse_proxy_string(cls, proxy):
50         pattern = "^\s*((http|https|ftp|git|cvs)://)?((\S+):(\S+)@)?(\S+):(\d+)/?"
51         match = re.search(pattern, proxy)
52         if match:
53             return match.group(2), match.group(4), match.group(5), match.group(6), match.group(7)
54         else:
55             return None, None, None, "", ""
56
57     @classmethod
58     def make_host_string(cls, prot, user, passwd, host, default_prot=""):
59         if host == None or host == "":
60             return ""
61
62         passwd = passwd or ""
63
64         if user != None and user != "":
65             if prot == None or prot == "":
66                 prot = default_prot
67             return prot + "://" + user + ":" + passwd + "@" + host
68         else:
69             if prot == None or prot == "":
70                 return host
71             else:
72                 return prot + "://" + host
73
74     @classmethod
75     def make_port_string(cls, port):
76         port = port or ""
77         return port
78
79     @classmethod
80     def make_proxy_string(cls, prot, user, passwd, host, port, default_prot=""):
81         if host == None or host == "" or port == None or port == "":
82             return ""
83
84         return Configuration.make_host_string(prot, user, passwd, host, default_prot) + ":" + Configuration.make_port_string(port)
85
86     def __init__(self):
87         self.curr_mach = ""
88         # settings
89         self.curr_distro = ""
90         self.dldir = self.sstatedir = self.sstatemirror = ""
91         self.pmake = self.bbthread = 0
92         self.curr_package_format = ""
93         self.image_rootfs_size = self.image_extra_size = 0
94         self.image_overhead_factor = 1
95         self.incompat_license = ""
96         self.curr_sdk_machine = ""
97         self.conf_version = self.lconf_version = ""
98         self.extra_setting = {}
99         self.toolchain_build = False
100         self.image_fstypes = ""
101         # bblayers.conf
102         self.layers = []
103         # image/recipes/packages
104         self.clear_selection()
105
106         self.user_selected_packages = []
107
108         self.default_task = "build"
109
110         # proxy settings
111         self.enable_proxy = None
112         self.same_proxy = False
113         self.proxies = {
114             "http"  : [None, None, None, "", ""],  # protocol : [prot, user, passwd, host, port]
115             "https" : [None, None, None, "", ""],
116             "ftp"   : [None, None, None, "", ""],
117             "git"   : [None, None, None, "", ""],
118             "cvs"   : [None, None, None, "", ""],
119         }
120
121     def clear_selection(self):
122         self.selected_image = None
123         self.selected_recipes = []
124         self.selected_packages = []
125
126     def split_proxy(self, protocol, proxy):
127         entry = []
128         prot, user, passwd, host, port = Configuration.parse_proxy_string(proxy)
129         entry.append(prot)
130         entry.append(user)
131         entry.append(passwd)
132         entry.append(host)
133         entry.append(port)
134         self.proxies[protocol] = entry
135
136     def combine_proxy(self, protocol):
137         entry = self.proxies[protocol]
138         return Configuration.make_proxy_string(entry[0], entry[1], entry[2], entry[3], entry[4], protocol)
139
140     def combine_host_only(self, protocol):
141         entry = self.proxies[protocol]
142         return Configuration.make_host_string(entry[0], entry[1], entry[2], entry[3], protocol)
143
144     def combine_port_only(self, protocol):
145         entry = self.proxies[protocol]
146         return Configuration.make_port_string(entry[4])
147
148     def update(self, params):
149         # settings
150         self.curr_distro = params["distro"]
151         self.dldir = params["dldir"]
152         self.sstatedir = params["sstatedir"]
153         self.sstatemirror = params["sstatemirror"]
154         self.pmake = int(params["pmake"].split()[1])
155         self.bbthread = params["bbthread"]
156         self.curr_package_format = " ".join(params["pclass"].split("package_")).strip()
157         self.image_rootfs_size = params["image_rootfs_size"]
158         self.image_extra_size = params["image_extra_size"]
159         self.image_overhead_factor = params['image_overhead_factor']
160         self.incompat_license = params["incompat_license"]
161         self.curr_sdk_machine = params["sdk_machine"]
162         self.conf_version = params["conf_version"]
163         self.lconf_version = params["lconf_version"]
164         self.image_fstypes = params["image_fstypes"]
165         # self.extra_setting/self.toolchain_build
166         # bblayers.conf
167         self.layers = params["layer"].split()
168         self.default_task = params["default_task"]
169
170         # proxy settings
171         self.enable_proxy = params["http_proxy"] != "" or params["https_proxy"] != "" or params["ftp_proxy"] != "" \
172             or params["git_proxy_host"] != "" or params["git_proxy_port"] != ""                                    \
173             or params["cvs_proxy_host"] != "" or params["cvs_proxy_port"] != ""
174         self.split_proxy("http", params["http_proxy"])
175         self.split_proxy("https", params["https_proxy"])
176         self.split_proxy("ftp", params["ftp_proxy"])
177         self.split_proxy("git", params["git_proxy_host"] + ":" + params["git_proxy_port"])
178         self.split_proxy("cvs", params["cvs_proxy_host"] + ":" + params["cvs_proxy_port"])
179
180     def load(self, template):
181         self.curr_mach = template.getVar("MACHINE")
182         self.curr_package_format = " ".join(template.getVar("PACKAGE_CLASSES").split("package_")).strip()
183         self.curr_distro = template.getVar("DISTRO")
184         self.dldir = template.getVar("DL_DIR")
185         self.sstatedir = template.getVar("SSTATE_DIR")
186         self.sstatemirror = template.getVar("SSTATE_MIRROR")
187         try:
188             self.pmake = int(template.getVar("PARALLEL_MAKE").split()[1])
189         except:
190             pass
191         try:
192             self.bbthread = int(template.getVar("BB_NUMBER_THREADS"))
193         except:
194             pass
195         try:
196             self.image_rootfs_size = int(template.getVar("IMAGE_ROOTFS_SIZE"))
197         except:
198             pass
199         try:
200             self.image_extra_size = int(template.getVar("IMAGE_EXTRA_SPACE"))
201         except:
202             pass
203         # image_overhead_factor is read-only.
204         self.incompat_license = template.getVar("INCOMPATIBLE_LICENSE")
205         self.curr_sdk_machine = template.getVar("SDKMACHINE")
206         self.conf_version = template.getVar("CONF_VERSION")
207         self.lconf_version = template.getVar("LCONF_VERSION")
208         self.extra_setting = eval(template.getVar("EXTRA_SETTING"))
209         self.toolchain_build = eval(template.getVar("TOOLCHAIN_BUILD"))
210         self.image_fstypes = template.getVar("IMAGE_FSTYPES")
211         # bblayers.conf
212         self.layers = template.getVar("BBLAYERS").split()
213         # image/recipes/packages
214         self.selected_image = template.getVar("__SELECTED_IMAGE__")
215         self.selected_recipes = template.getVar("DEPENDS").split()
216         self.selected_packages = template.getVar("IMAGE_INSTALL").split()
217         # proxy
218         self.enable_proxy = eval(template.getVar("enable_proxy"))
219         self.same_proxy = eval(template.getVar("use_same_proxy"))
220         self.split_proxy("http", template.getVar("http_proxy"))
221         self.split_proxy("https", template.getVar("https_proxy"))
222         self.split_proxy("ftp", template.getVar("ftp_proxy"))
223         self.split_proxy("git", template.getVar("GIT_PROXY_HOST") + ":" + template.getVar("GIT_PROXY_PORT"))
224         self.split_proxy("cvs", template.getVar("CVS_PROXY_HOST") + ":" + template.getVar("CVS_PROXY_PORT"))
225
226     def save(self, template, defaults=False):
227         template.setVar("VERSION", "%s" % hobVer)
228         # bblayers.conf
229         template.setVar("BBLAYERS", " ".join(self.layers))
230         # local.conf
231         if not defaults:
232             template.setVar("MACHINE", self.curr_mach)
233         template.setVar("DISTRO", self.curr_distro)
234         template.setVar("DL_DIR", self.dldir)
235         template.setVar("SSTATE_DIR", self.sstatedir)
236         template.setVar("SSTATE_MIRROR", self.sstatemirror)
237         template.setVar("PARALLEL_MAKE", "-j %s" % self.pmake)
238         template.setVar("BB_NUMBER_THREADS", self.bbthread)
239         template.setVar("PACKAGE_CLASSES", " ".join(["package_" + i for i in self.curr_package_format.split()]))
240         template.setVar("IMAGE_ROOTFS_SIZE", self.image_rootfs_size)
241         template.setVar("IMAGE_EXTRA_SPACE", self.image_extra_size)
242         template.setVar("INCOMPATIBLE_LICENSE", self.incompat_license)
243         template.setVar("SDKMACHINE", self.curr_sdk_machine)
244         template.setVar("CONF_VERSION", self.conf_version)
245         template.setVar("LCONF_VERSION", self.lconf_version)
246         template.setVar("EXTRA_SETTING", self.extra_setting)
247         template.setVar("TOOLCHAIN_BUILD", self.toolchain_build)
248         template.setVar("IMAGE_FSTYPES", self.image_fstypes)
249         if not defaults:
250             # image/recipes/packages
251             template.setVar("__SELECTED_IMAGE__", self.selected_image)
252             template.setVar("DEPENDS", self.selected_recipes)
253             template.setVar("IMAGE_INSTALL", self.user_selected_packages)
254         # proxy
255         template.setVar("enable_proxy", self.enable_proxy)
256         template.setVar("use_same_proxy", self.same_proxy)
257         template.setVar("http_proxy", self.combine_proxy("http"))
258         template.setVar("https_proxy", self.combine_proxy("https"))
259         template.setVar("ftp_proxy", self.combine_proxy("ftp"))
260         template.setVar("GIT_PROXY_HOST", self.combine_host_only("git"))
261         template.setVar("GIT_PROXY_PORT", self.combine_port_only("git"))
262         template.setVar("CVS_PROXY_HOST", self.combine_host_only("cvs"))
263         template.setVar("CVS_PROXY_PORT", self.combine_port_only("cvs"))
264
265 class Parameters:
266     '''Represents other variables like available machines, etc.'''
267
268     def __init__(self):
269         # Variables
270         self.max_threads = 65535
271         self.core_base = ""
272         self.image_addr = ""
273         self.image_types = []
274         self.runnable_image_types = []
275         self.runnable_machine_patterns = []
276         self.deployable_image_types = []
277         self.tmpdir = ""
278
279         self.all_machines = []
280         self.all_package_formats = []
281         self.all_distros = []
282         self.all_sdk_machines = []
283         self.all_layers = []
284         self.image_names = []
285         self.image_white_pattern = ""
286         self.image_black_pattern = ""
287
288         # for build log to show
289         self.bb_version = ""
290         self.target_arch = ""
291         self.target_os = ""
292         self.distro_version = ""
293         self.tune_pkgarch = ""
294
295     def update(self, params):
296         self.max_threads = params["max_threads"]
297         self.core_base = params["core_base"]
298         self.image_addr = params["image_addr"]
299         self.image_types = params["image_types"].split()
300         self.runnable_image_types = params["runnable_image_types"].split()
301         self.runnable_machine_patterns = params["runnable_machine_patterns"].split()
302         self.deployable_image_types = params["deployable_image_types"].split()
303         self.tmpdir = params["tmpdir"]
304         self.image_white_pattern = params["image_white_pattern"]
305         self.image_black_pattern = params["image_black_pattern"]
306         self.kernel_image_type = params["kernel_image_type"]
307         # for build log to show
308         self.bb_version = params["bb_version"]
309         self.target_arch = params["target_arch"]
310         self.target_os = params["target_os"]
311         self.distro_version = params["distro_version"]
312         self.tune_pkgarch = params["tune_pkgarch"]
313
314 def hob_conf_filter(fn, data):
315     if fn.endswith("/local.conf"):
316         distro = data.getVar("DISTRO_HOB")
317         if distro:
318             if distro != "defaultsetup":
319                 data.setVar("DISTRO", distro)
320             else:
321                 data.delVar("DISTRO")
322
323         keys = ["MACHINE_HOB", "SDKMACHINE_HOB", "PACKAGE_CLASSES_HOB", \
324                 "BB_NUMBER_THREADS_HOB", "PARALLEL_MAKE_HOB", "DL_DIR_HOB", \
325                 "SSTATE_DIR_HOB", "SSTATE_MIRROR_HOB", "INCOMPATIBLE_LICENSE_HOB"]
326         for key in keys:
327             var_hob = data.getVar(key)
328             if var_hob:
329                 data.setVar(key.split("_HOB")[0], var_hob)
330         return
331
332     if fn.endswith("/bblayers.conf"):
333         layers = data.getVar("BBLAYERS_HOB")
334         if layers:
335             data.setVar("BBLAYERS", layers)
336         return
337
338 class Builder(gtk.Window):
339
340     (MACHINE_SELECTION,
341      RCPPKGINFO_POPULATING,
342      RCPPKGINFO_POPULATED,
343      BASEIMG_SELECTED,
344      RECIPE_SELECTION,
345      PACKAGE_GENERATING,
346      PACKAGE_GENERATED,
347      PACKAGE_SELECTION,
348      FAST_IMAGE_GENERATING,
349      IMAGE_GENERATING,
350      IMAGE_GENERATED,
351      MY_IMAGE_OPENED,
352      BACK,
353      END_NOOP) = range(14)
354
355     (IMAGE_CONFIGURATION,
356      RECIPE_DETAILS,
357      BUILD_DETAILS,
358      PACKAGE_DETAILS,
359      IMAGE_DETAILS,
360      END_TAB) = range(6)
361
362     __step2page__ = {
363         MACHINE_SELECTION     : IMAGE_CONFIGURATION,
364         RCPPKGINFO_POPULATING : IMAGE_CONFIGURATION,
365         RCPPKGINFO_POPULATED  : IMAGE_CONFIGURATION,
366         BASEIMG_SELECTED      : IMAGE_CONFIGURATION,
367         RECIPE_SELECTION      : RECIPE_DETAILS,
368         PACKAGE_GENERATING    : BUILD_DETAILS,
369         PACKAGE_GENERATED     : PACKAGE_DETAILS,
370         PACKAGE_SELECTION     : PACKAGE_DETAILS,
371         FAST_IMAGE_GENERATING : BUILD_DETAILS,
372         IMAGE_GENERATING      : BUILD_DETAILS,
373         IMAGE_GENERATED       : IMAGE_DETAILS,
374         MY_IMAGE_OPENED       : IMAGE_DETAILS,
375         END_NOOP              : None,
376     }
377
378     @classmethod
379     def interpret_markup(cls, msg):
380         msg = msg.replace('&', '&amp;')
381         msg = msg.replace('<', '&lt;')
382         msg = msg.replace('>', '&gt;')
383         msg = msg.replace('"', '&quot;')
384         msg = msg.replace("'", "&acute;")
385         return msg
386
387     def __init__(self, hobHandler, recipe_model, package_model):
388         super(Builder, self).__init__()
389
390         self.hob_image = "hob-image"
391         self.hob_toolchain = "hob-toolchain"
392
393         # handler
394         self.handler = hobHandler
395
396         self.template = None
397
398         # configuration and parameters
399         self.configuration = Configuration()
400         self.parameters = Parameters()
401
402         # build step
403         self.current_step = None
404         self.previous_step = None
405
406         self.stopping = False
407
408         # recipe model and package model
409         self.recipe_model = recipe_model
410         self.package_model = package_model
411
412         # Indicate whether user has customized the image
413         self.customized = False
414
415         # Indicate whether the UI is working
416         self.sensitive = True
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("task-started",       self.handler_task_started_cb)
434         self.handler.build.connect("log-error",          self.handler_build_failure_cb)
435         self.handler.build.connect("no-provider",        self.handler_no_provider_cb)
436         self.handler.connect("generating-data",          self.handler_generating_data_cb)
437         self.handler.connect("data-generated",           self.handler_data_generated_cb)
438         self.handler.connect("command-succeeded",        self.handler_command_succeeded_cb)
439         self.handler.connect("command-failed",           self.handler_command_failed_cb)
440         self.handler.connect("sanity-failed",            self.handler_sanity_failed_cb)
441         self.handler.connect("recipe-populated",         self.handler_recipe_populated_cb)
442         self.handler.connect("package-populated",        self.handler_package_populated_cb)
443
444         self.handler.set_config_filter(hob_conf_filter)
445
446         self.initiate_new_build_async()
447
448     def create_visual_elements(self):
449         self.set_title("Hob")
450         self.set_icon_name("applications-development")
451         self.set_resizable(True)
452         window_width = self.get_screen().get_width()
453         window_height = self.get_screen().get_height()
454         if window_width >= hwc.MAIN_WIN_WIDTH:
455             window_width = hwc.MAIN_WIN_WIDTH
456             window_height = hwc.MAIN_WIN_HEIGHT
457         self.set_size_request(window_width, window_height)
458
459         self.vbox = gtk.VBox(False, 0)
460         self.vbox.set_border_width(0)
461         self.add(self.vbox)
462
463         # create pages
464         self.image_configuration_page = ImageConfigurationPage(self)
465         self.recipe_details_page      = RecipeSelectionPage(self)
466         self.build_details_page       = BuildDetailsPage(self)
467         self.package_details_page     = PackageSelectionPage(self)
468         self.image_details_page       = ImageDetailsPage(self)
469
470         self.nb = gtk.Notebook()
471         self.nb.set_show_tabs(False)
472         self.nb.insert_page(self.image_configuration_page, None, self.IMAGE_CONFIGURATION)
473         self.nb.insert_page(self.recipe_details_page,      None, self.RECIPE_DETAILS)
474         self.nb.insert_page(self.build_details_page,       None, self.BUILD_DETAILS)
475         self.nb.insert_page(self.package_details_page,     None, self.PACKAGE_DETAILS)
476         self.nb.insert_page(self.image_details_page,       None, self.IMAGE_DETAILS)
477         self.vbox.pack_start(self.nb, expand=True, fill=True)
478
479         self.show_all()
480         self.nb.set_current_page(0)
481
482     def initiate_new_build_async(self):
483         self.switch_page(self.MACHINE_SELECTION)
484         if self.load_template(TemplateMgr.convert_to_template_pathfilename("default", ".hob/")) == False:
485             self.handler.init_cooker()
486             self.handler.set_extra_inherit("image_types")
487             self.handler.generate_configuration()
488
489     def update_config_async(self):
490         self.switch_page(self.MACHINE_SELECTION)
491         self.set_user_config()
492         self.handler.generate_configuration()
493
494     def sanity_check(self):
495         self.handler.trigger_sanity_check()
496
497     def populate_recipe_package_info_async(self):
498         self.switch_page(self.RCPPKGINFO_POPULATING)
499         # Parse recipes
500         self.set_user_config()
501         self.handler.generate_recipes()
502
503     def generate_packages_async(self):
504         self.switch_page(self.PACKAGE_GENERATING)
505         # Build packages
506         _, all_recipes = self.recipe_model.get_selected_recipes()
507         self.set_user_config()
508         self.handler.reset_build()
509         self.handler.generate_packages(all_recipes, self.configuration.default_task)
510
511     def fast_generate_image_async(self):
512         self.switch_page(self.FAST_IMAGE_GENERATING)
513         # Build packages
514         _, all_recipes = self.recipe_model.get_selected_recipes()
515         self.set_user_config()
516         self.handler.reset_build()
517         self.handler.generate_packages(all_recipes, self.configuration.default_task)
518
519     def generate_image_async(self):
520         self.switch_page(self.IMAGE_GENERATING)
521         self.handler.reset_build()
522         # Build image
523         self.set_user_config()
524         toolchain_packages = []
525         if self.configuration.toolchain_build:
526             toolchain_packages = self.package_model.get_selected_packages_toolchain()
527         if self.configuration.selected_image == self.recipe_model.__custom_image__:
528             packages = self.package_model.get_selected_packages()
529             image = self.hob_image
530         else:
531             packages = []
532             image = self.configuration.selected_image
533         self.handler.generate_image(image,
534                                     self.hob_toolchain,
535                                     packages,
536                                     toolchain_packages,
537                                     self.configuration.default_task)
538
539     def get_parameters_sync(self):
540         return self.handler.get_parameters()
541
542     def request_package_info_async(self):
543         self.handler.request_package_info()
544
545     def cancel_build_sync(self, force=False):
546         self.handler.cancel_build(force)
547
548     def cancel_parse_sync(self):
549         self.handler.cancel_parse()
550
551     def load_template(self, path):
552         if not os.path.isfile(path):
553             return False
554
555         self.template = TemplateMgr()
556         # check compatibility
557         tempVer = self.template.getVersion(path)
558         if not tempVer or int(tempVer) < hobVer:
559             self.template.destroy()
560             self.template = None
561             return False
562
563         try:
564             self.template.load(path)
565             self.configuration.load(self.template)
566         except Exception as e:
567             self.show_error_dialog("Hob Exception - %s" % (str(e)))
568             self.reset()
569         finally:
570             self.template.destroy()
571             self.template = None
572
573         for layer in self.configuration.layers:
574             if not os.path.exists(layer+'/conf/layer.conf'):
575                 return False
576
577         self.save_defaults() # remember layers and settings
578         self.update_config_async()
579         return True
580
581     def save_template(self, path, defaults=False):
582         if path.rfind("/") == -1:
583             filename = "default"
584             path = "."
585         else:
586             filename = path[path.rfind("/") + 1:len(path)]
587             path = path[0:path.rfind("/")]
588
589         self.template = TemplateMgr()
590         try:
591             self.template.open(filename, path)
592             self.configuration.save(self.template, defaults)
593
594             self.template.save()
595         except Exception as e:
596             self.show_error_dialog("Hob Exception - %s" % (str(e)))
597             self.reset()
598         finally:
599             self.template.destroy()
600             self.template = None
601
602     def save_defaults(self):
603         if not os.path.exists(".hob/"):
604             os.mkdir(".hob/")
605         self.save_template(".hob/default", True)
606
607     def switch_page(self, next_step):
608         # Main Workflow (Business Logic)
609         self.nb.set_current_page(self.__step2page__[next_step])
610
611         if next_step == self.MACHINE_SELECTION: # init step
612             self.image_configuration_page.show_machine()
613
614         elif next_step == self.RCPPKGINFO_POPULATING:
615             # MACHINE CHANGED action or SETTINGS CHANGED
616             # show the progress bar
617             self.image_configuration_page.show_info_populating()
618
619         elif next_step == self.RCPPKGINFO_POPULATED:
620             self.image_configuration_page.show_info_populated()
621
622         elif next_step == self.BASEIMG_SELECTED:
623             self.image_configuration_page.show_baseimg_selected()
624
625         elif next_step == self.RECIPE_SELECTION:
626             pass
627
628         elif next_step == self.PACKAGE_SELECTION:
629             pass
630
631         elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING:
632             # both PACKAGE_GENEATING and FAST_IMAGE_GENERATING share the same page
633             self.build_details_page.show_page(next_step)
634
635         elif next_step == self.PACKAGE_GENERATED:
636             pass
637
638         elif next_step == self.IMAGE_GENERATING:
639             # after packages are generated, selected_packages need to
640             # be updated in package_model per selected_image in recipe_model
641             self.build_details_page.show_page(next_step)
642
643         elif next_step == self.IMAGE_GENERATED:
644             self.image_details_page.show_page(next_step)
645
646         elif next_step == self.MY_IMAGE_OPENED:
647             self.image_details_page.show_page(next_step)
648
649         self.previous_step = self.current_step
650         self.current_step = next_step
651
652     def set_user_config(self):
653         self.handler.init_cooker()
654         # set bb layers
655         self.handler.set_bblayers(self.configuration.layers)
656         # set local configuration
657         self.handler.set_machine(self.configuration.curr_mach)
658         self.handler.set_package_format(self.configuration.curr_package_format)
659         self.handler.set_distro(self.configuration.curr_distro)
660         self.handler.set_dl_dir(self.configuration.dldir)
661         self.handler.set_sstate_dir(self.configuration.sstatedir)
662         self.handler.set_sstate_mirror(self.configuration.sstatemirror)
663         self.handler.set_pmake(self.configuration.pmake)
664         self.handler.set_bbthreads(self.configuration.bbthread)
665         self.handler.set_rootfs_size(self.configuration.image_rootfs_size)
666         self.handler.set_extra_size(self.configuration.image_extra_size)
667         self.handler.set_incompatible_license(self.configuration.incompat_license)
668         self.handler.set_sdk_machine(self.configuration.curr_sdk_machine)
669         self.handler.set_image_fstypes(self.configuration.image_fstypes)
670         self.handler.set_extra_config(self.configuration.extra_setting)
671         self.handler.set_extra_inherit("packageinfo")
672         self.handler.set_extra_inherit("image_types")
673         # set proxies
674         if self.configuration.enable_proxy == True:
675             self.handler.set_http_proxy(self.configuration.combine_proxy("http"))
676             self.handler.set_https_proxy(self.configuration.combine_proxy("https"))
677             self.handler.set_ftp_proxy(self.configuration.combine_proxy("ftp"))
678             self.handler.set_git_proxy(self.configuration.combine_host_only("git"), self.configuration.combine_port_only("git"))
679             self.handler.set_cvs_proxy(self.configuration.combine_host_only("cvs"), self.configuration.combine_port_only("cvs"))
680         elif self.configuration.enable_proxy == False:
681             self.handler.set_http_proxy("")
682             self.handler.set_https_proxy("")
683             self.handler.set_ftp_proxy("")
684             self.handler.set_git_proxy("", "")
685             self.handler.set_cvs_proxy("", "")
686
687     def update_recipe_model(self, selected_image, selected_recipes):
688         self.recipe_model.set_selected_image(selected_image)
689         self.recipe_model.set_selected_recipes(selected_recipes)
690
691     def update_package_model(self, selected_packages):
692         left = self.package_model.set_selected_packages(selected_packages)
693         self.configuration.selected_packages += left
694
695     def update_configuration_parameters(self, params):
696         if params:
697             self.configuration.update(params)
698             self.parameters.update(params)
699
700     def reset(self):
701         self.configuration.curr_mach = ""
702         self.configuration.clear_selection()
703         self.image_configuration_page.switch_machine_combo()
704         self.switch_page(self.MACHINE_SELECTION)
705
706     # Callback Functions
707     def handler_config_updated_cb(self, handler, which, values):
708         if which == "distro":
709             self.parameters.all_distros = values
710         elif which == "machine":
711             self.parameters.all_machines = values
712             self.image_configuration_page.update_machine_combo()
713         elif which == "machine-sdk":
714             self.parameters.all_sdk_machines = values
715
716     def handler_package_formats_updated_cb(self, handler, formats):
717         self.parameters.all_package_formats = formats
718
719     def handler_command_succeeded_cb(self, handler, initcmd):
720         if initcmd == self.handler.GENERATE_CONFIGURATION:
721             self.update_configuration_parameters(self.get_parameters_sync())
722             self.sanity_check()
723         elif initcmd == self.handler.SANITY_CHECK:
724             self.image_configuration_page.switch_machine_combo()
725         elif initcmd in [self.handler.GENERATE_RECIPES,
726                          self.handler.GENERATE_PACKAGES,
727                          self.handler.GENERATE_IMAGE]:
728             self.update_configuration_parameters(self.get_parameters_sync())
729             self.request_package_info_async()
730         elif initcmd == self.handler.POPULATE_PACKAGEINFO:
731             if self.current_step == self.RCPPKGINFO_POPULATING:
732                 self.switch_page(self.RCPPKGINFO_POPULATED)
733                 self.rcppkglist_populated()
734                 return
735
736             self.rcppkglist_populated()
737             if self.current_step == self.FAST_IMAGE_GENERATING:
738                 self.generate_image_async()
739
740     def show_error_dialog(self, msg):
741         lbl = "<b>Error</b>\n"
742         lbl = lbl + "%s\n\n" % Builder.interpret_markup(msg)
743         dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
744         button = dialog.add_button("Close", gtk.RESPONSE_OK)
745         HobButton.style_button(button)
746         response = dialog.run()
747         dialog.destroy()
748
749     def handler_command_failed_cb(self, handler, msg):
750         if msg:
751             self.show_error_dialog(msg)
752         self.reset()
753
754     def handler_sanity_failed_cb(self, handler, msg):
755         msg = msg.replace("your local.conf", "Settings")
756         self.show_error_dialog(msg)
757         self.reset()
758
759     def window_sensitive(self, sensitive):
760         self.image_configuration_page.machine_combo.set_sensitive(sensitive)
761         self.image_configuration_page.machine_combo.child.set_sensitive(sensitive)
762         self.image_configuration_page.image_combo.set_sensitive(sensitive)
763         self.image_configuration_page.image_combo.child.set_sensitive(sensitive)
764         self.image_configuration_page.layer_button.set_sensitive(sensitive)
765         self.image_configuration_page.layer_info_icon.set_sensitive(sensitive)
766         self.image_configuration_page.toolbar.set_sensitive(sensitive)
767         self.image_configuration_page.view_recipes_button.set_sensitive(sensitive)
768         self.image_configuration_page.view_packages_button.set_sensitive(sensitive)
769         self.image_configuration_page.config_build_button.set_sensitive(sensitive)
770
771         self.recipe_details_page.set_sensitive(sensitive)
772         self.package_details_page.set_sensitive(sensitive)
773         self.build_details_page.set_sensitive(sensitive)
774         self.image_details_page.set_sensitive(sensitive)
775
776         if sensitive:
777             self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
778         else:
779             self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
780         self.sensitive = sensitive
781
782
783     def handler_generating_data_cb(self, handler):
784         self.window_sensitive(False)
785
786     def handler_data_generated_cb(self, handler):
787         self.window_sensitive(True)
788
789     def rcppkglist_populated(self):
790         selected_image = self.configuration.selected_image
791         selected_recipes = self.configuration.selected_recipes[:]
792         selected_packages = self.configuration.selected_packages[:]
793
794         self.image_configuration_page.update_image_combo(self.recipe_model, selected_image)
795         self.image_configuration_page.update_image_desc()
796         self.update_recipe_model(selected_image, selected_recipes)
797         self.update_package_model(selected_packages)
798
799     def recipelist_changed_cb(self, recipe_model):
800         self.recipe_details_page.refresh_selection()
801
802     def packagelist_changed_cb(self, package_model):
803         self.package_details_page.refresh_selection()
804
805     def handler_recipe_populated_cb(self, handler):
806         self.image_configuration_page.update_progress_bar("Populated recipes", 0.99)
807
808     def handler_package_populated_cb(self, handler):
809         self.image_configuration_page.update_progress_bar("Populated packages", 1.0)
810
811     def handler_parsing_started_cb(self, handler, message):
812         if self.current_step != self.RCPPKGINFO_POPULATING:
813             return
814
815         fraction = 0
816         if message["eventname"] == "TreeDataPreparationStarted":
817             fraction = 0.6 + fraction
818             self.image_configuration_page.stop_button.set_sensitive(False)
819         else:
820             self.image_configuration_page.stop_button.set_sensitive(True)
821
822         self.image_configuration_page.update_progress_bar(message["title"], fraction)
823
824     def handler_parsing_cb(self, handler, message):
825         if self.current_step != self.RCPPKGINFO_POPULATING:
826             return
827
828         fraction = message["current"] * 1.0/message["total"]
829         if message["eventname"] == "TreeDataPreparationProgress":
830             fraction = 0.6 + 0.38 * fraction
831         else:
832             fraction = 0.6 * fraction
833         self.image_configuration_page.update_progress_bar(message["title"], fraction)
834
835     def handler_parsing_completed_cb(self, handler, message):
836         if self.current_step != self.RCPPKGINFO_POPULATING:
837             return
838
839         if message["eventname"] == "TreeDataPreparationCompleted":
840             fraction = 0.98
841         else:
842             fraction = 0.6
843         self.image_configuration_page.update_progress_bar(message["title"], fraction)
844
845     def handler_build_started_cb(self, running_build):
846         if self.current_step == self.FAST_IMAGE_GENERATING:
847             fraction = 0
848         elif self.current_step == self.IMAGE_GENERATING:
849             if self.previous_step == self.FAST_IMAGE_GENERATING:
850                 fraction = 0.9
851             else:
852                 fraction = 0
853         elif self.current_step == self.PACKAGE_GENERATING:
854             fraction = 0
855         self.build_details_page.update_progress_bar("Build Started: ", fraction)
856         self.build_details_page.show_configurations(self.configuration, self.parameters)
857
858     def build_succeeded(self):
859         if self.current_step == self.FAST_IMAGE_GENERATING:
860             fraction = 0.9
861         elif self.current_step == self.IMAGE_GENERATING:
862             fraction = 1.0
863             self.parameters.image_names = []
864             selected_image = self.recipe_model.get_selected_image()
865             if selected_image == self.recipe_model.__custom_image__:
866                 linkname = 'hob-image-' + self.configuration.curr_mach
867             else:
868                 linkname = selected_image + '-' + self.configuration.curr_mach
869             for image_type in self.parameters.image_types:
870                 for real_image_type in hcc.SUPPORTED_IMAGE_TYPES[image_type]:
871                     linkpath = self.parameters.image_addr + '/' + linkname + '.' + real_image_type
872                     if os.path.exists(linkpath):
873                         self.parameters.image_names.append(os.readlink(linkpath))
874         elif self.current_step == self.PACKAGE_GENERATING:
875             fraction = 1.0
876         self.build_details_page.update_progress_bar("Build Completed: ", fraction)
877         self.handler.build_succeeded_async()
878         self.stopping = False
879
880         if self.current_step == self.PACKAGE_GENERATING:
881             self.switch_page(self.PACKAGE_GENERATED)
882         elif self.current_step == self.IMAGE_GENERATING:
883             self.switch_page(self.IMAGE_GENERATED)
884
885     def build_failed(self):
886         if self.stopping:
887             status = "stop"
888             message = "Build stopped: "
889             fraction = self.build_details_page.progress_bar.get_fraction()
890         else:
891             fail_to_next_edit = ""
892             if self.current_step == self.FAST_IMAGE_GENERATING:
893                 fail_to_next_edit = "image configuration"
894                 fraction = 0.9
895             elif self.current_step == self.IMAGE_GENERATING:
896                 if self.previous_step == self.FAST_IMAGE_GENERATING:
897                     fail_to_next_edit = "image configuration"
898                 else:
899                     fail_to_next_edit = "packages"
900                 fraction = 1.0
901             elif self.current_step == self.PACKAGE_GENERATING:
902                 fail_to_next_edit = "recipes"
903                 fraction = 1.0
904             self.build_details_page.show_fail_page(fail_to_next_edit.split(' ')[0], fail_to_next_edit)
905             status = "fail"
906             message = "Build failed: "
907         self.build_details_page.update_progress_bar(message, fraction, status)
908         self.build_details_page.show_back_button()
909         self.build_details_page.hide_stop_button()
910         self.handler.build_failed_async()
911         self.stopping = False
912
913     def handler_build_succeeded_cb(self, running_build):
914         if not self.stopping:
915             self.build_succeeded()
916         else:
917             self.build_failed()
918
919
920     def handler_build_failed_cb(self, running_build):
921         self.build_failed()
922
923     def handler_no_provider_cb(self, running_build, msg):
924         dialog = CrumbsMessageDialog(self, Builder.interpret_markup(msg), gtk.STOCK_DIALOG_INFO)
925         button = dialog.add_button("Close", gtk.RESPONSE_OK)
926         HobButton.style_button(button)
927         dialog.run()
928         dialog.destroy()
929         self.build_failed()
930
931     def handler_task_started_cb(self, running_build, message): 
932         fraction = message["current"] * 1.0/message["total"]
933         title = "Build packages"
934         if self.current_step == self.FAST_IMAGE_GENERATING:
935             if message["eventname"] == "sceneQueueTaskStarted":
936                 fraction = 0.27 * fraction
937             elif message["eventname"] == "runQueueTaskStarted":
938                 fraction = 0.27 + 0.63 * fraction
939         elif self.current_step == self.IMAGE_GENERATING:
940             title = "Build image"
941             if self.previous_step == self.FAST_IMAGE_GENERATING:
942                 if message["eventname"] == "sceneQueueTaskStarted":
943                     fraction = 0.27 + 0.63 + 0.03 * fraction
944                 elif message["eventname"] == "runQueueTaskStarted":
945                     fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction
946             else:
947                 if message["eventname"] == "sceneQueueTaskStarted":
948                     fraction = 0.2 * fraction
949                 elif message["eventname"] == "runQueueTaskStarted":
950                     fraction = 0.2 + 0.8 * fraction
951         elif self.current_step == self.PACKAGE_GENERATING:
952             if message["eventname"] == "sceneQueueTaskStarted":
953                 fraction = 0.2 * fraction
954             elif message["eventname"] == "runQueueTaskStarted":
955                 fraction = 0.2 + 0.8 * fraction
956         self.build_details_page.update_progress_bar(title + ": ", fraction)
957         self.build_details_page.update_build_status(message["current"], message["total"], message["task"])
958
959     def handler_build_failure_cb(self, running_build):
960         self.build_details_page.show_issues()
961
962     def destroy_window_cb(self, widget, event):
963         if not self.sensitive:
964             return True
965         lbl = "<b>Do you really want to exit the Hob image creator?</b>"
966         dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
967         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
968         HobAltButton.style_button(button)
969         button = dialog.add_button("Exit Hob", gtk.RESPONSE_YES)
970         HobButton.style_button(button)
971         dialog.set_default_response(gtk.RESPONSE_YES)
972         response = dialog.run()
973         dialog.destroy()
974         if response == gtk.RESPONSE_YES:
975             gtk.main_quit()
976             return False
977         else:
978             return True
979
980     def build_packages(self):
981         _, all_recipes = self.recipe_model.get_selected_recipes()
982         if not all_recipes:
983             lbl = "<b>No selections made</b>\nYou have not made any selections"
984             lbl = lbl + " so there isn't anything to bake at this time."
985             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
986             button = dialog.add_button("Close", gtk.RESPONSE_OK)
987             HobButton.style_button(button)
988             dialog.run()
989             dialog.destroy()
990             return
991         self.generate_packages_async()
992
993     def build_image(self):
994         selected_packages = self.package_model.get_selected_packages()
995         if not selected_packages:      
996             lbl = "<b>No selections made</b>\nYou have not made any selections"
997             lbl = lbl + " so there isn't anything to bake at this time."
998             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
999             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1000             HobButton.style_button(button)
1001             dialog.run()
1002             dialog.destroy()
1003             return
1004         self.generate_image_async()
1005
1006     def just_bake(self):
1007         selected_image = self.recipe_model.get_selected_image()
1008         selected_packages = self.package_model.get_selected_packages() or []
1009
1010         # If no base image and no selected packages don't build anything
1011         if not (selected_packages or selected_image != self.recipe_model.__custom_image__):
1012             lbl = "<b>No selections made</b>\nYou have not made any selections"
1013             lbl = lbl + " so there isn't anything to bake at this time."
1014             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1015             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1016             HobButton.style_button(button)
1017             dialog.run()
1018             dialog.destroy()
1019             return
1020
1021         self.fast_generate_image_async()
1022
1023     def show_binb_dialog(self, binb):
1024         markup = "<b>Brought in by:</b>\n%s" % binb
1025         ptip = PersistentTooltip(markup, self)
1026
1027         ptip.show()
1028
1029     def show_layer_selection_dialog(self):
1030         dialog = LayerSelectionDialog(title = "Layers",
1031                      layers = copy.deepcopy(self.configuration.layers),
1032                      all_layers = self.parameters.all_layers,
1033                      parent = self,
1034                      flags = gtk.DIALOG_MODAL
1035                          | gtk.DIALOG_DESTROY_WITH_PARENT
1036                          | gtk.DIALOG_NO_SEPARATOR)
1037         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1038         HobAltButton.style_button(button)
1039         button = dialog.add_button("OK", gtk.RESPONSE_YES)
1040         HobButton.style_button(button)
1041         response = dialog.run()
1042         if response == gtk.RESPONSE_YES:
1043             self.configuration.layers = dialog.layers
1044             self.save_defaults() # remember layers
1045             # DO refresh layers
1046             if dialog.layers_changed:
1047                 self.update_config_async()
1048         dialog.destroy()
1049
1050     def show_load_template_dialog(self):
1051         dialog = gtk.FileChooserDialog("Load Template Files", self,
1052                                        gtk.FILE_CHOOSER_ACTION_OPEN)
1053         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1054         HobAltButton.style_button(button)
1055         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1056         HobButton.style_button(button)
1057         filter = gtk.FileFilter()
1058         filter.set_name("Hob Files")
1059         filter.add_pattern("*.hob")
1060         dialog.add_filter(filter)
1061
1062         response = dialog.run()
1063         path = None
1064         if response == gtk.RESPONSE_YES:
1065             path = dialog.get_filename()
1066         dialog.destroy()
1067         return response == gtk.RESPONSE_YES, path
1068
1069     def show_save_template_dialog(self):
1070         dialog = gtk.FileChooserDialog("Save Template Files", self,
1071                                        gtk.FILE_CHOOSER_ACTION_SAVE)
1072         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1073         HobAltButton.style_button(button)
1074         button = dialog.add_button("Save", gtk.RESPONSE_YES)
1075         HobButton.style_button(button)
1076         dialog.set_current_name("hob")
1077         response = dialog.run()
1078         if response == gtk.RESPONSE_YES:
1079             path = dialog.get_filename()
1080             self.save_template(path)
1081         dialog.destroy()
1082
1083     def show_load_my_images_dialog(self):
1084         dialog = ImageSelectionDialog(self.parameters.image_addr, self.parameters.image_types,
1085                                       "Open My Images", self,
1086                                        gtk.FILE_CHOOSER_ACTION_SAVE)
1087         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1088         HobAltButton.style_button(button)
1089         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1090         HobButton.style_button(button)
1091         response = dialog.run()
1092         if response == gtk.RESPONSE_YES:
1093             if not dialog.image_names:
1094                 lbl = "<b>No selections made</b>\nYou have not made any selections"
1095                 crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1096                 button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
1097                 HobButton.style_button(button)
1098                 crumbs_dialog.run()
1099                 crumbs_dialog.destroy()
1100                 dialog.destroy()
1101                 return
1102
1103             self.parameters.image_addr = dialog.image_folder
1104             self.parameters.image_names = dialog.image_names[:]
1105             self.switch_page(self.MY_IMAGE_OPENED)
1106
1107         dialog.destroy()
1108
1109     def show_adv_settings_dialog(self):
1110         dialog = AdvancedSettingDialog(title = "Settings",
1111             configuration = copy.deepcopy(self.configuration),
1112             all_image_types = self.parameters.image_types,
1113             all_package_formats = self.parameters.all_package_formats,
1114             all_distros = self.parameters.all_distros,
1115             all_sdk_machines = self.parameters.all_sdk_machines,
1116             max_threads = self.parameters.max_threads,
1117             parent = self,
1118             flags = gtk.DIALOG_MODAL
1119                     | gtk.DIALOG_DESTROY_WITH_PARENT
1120                     | gtk.DIALOG_NO_SEPARATOR)
1121         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1122         HobAltButton.style_button(button)
1123         button = dialog.add_button("Save", gtk.RESPONSE_YES)
1124         HobButton.style_button(button)
1125         response = dialog.run()
1126         settings_changed = False
1127         if response == gtk.RESPONSE_YES:
1128             self.configuration = dialog.configuration
1129             self.save_defaults() # remember settings
1130             settings_changed = dialog.settings_changed
1131         dialog.destroy()
1132         return response == gtk.RESPONSE_YES, settings_changed
1133
1134     def reparse_post_adv_settings(self):
1135         if not self.configuration.curr_mach:
1136             self.update_config_async()
1137         else:
1138             self.configuration.clear_selection()
1139             # DO reparse recipes
1140             self.populate_recipe_package_info_async()
1141
1142     def deploy_image(self, image_name):
1143         if not image_name:
1144             lbl = "<b>Please select an image to deploy.</b>"
1145             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1146             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1147             HobButton.style_button(button)
1148             dialog.run()
1149             dialog.destroy()
1150             return
1151
1152         image_path = os.path.join(self.parameters.image_addr, image_name)
1153         dialog = DeployImageDialog(title = "Usb Image Maker",
1154             image_path = image_path,
1155             parent = self,
1156             flags = gtk.DIALOG_MODAL
1157                     | gtk.DIALOG_DESTROY_WITH_PARENT
1158                     | gtk.DIALOG_NO_SEPARATOR)
1159         button = dialog.add_button("Close", gtk.RESPONSE_NO)
1160         HobAltButton.style_button(button)
1161         button = dialog.add_button("Make usb image", gtk.RESPONSE_YES)
1162         HobButton.style_button(button)
1163         response = dialog.run()
1164         dialog.destroy()
1165
1166     def show_load_kernel_dialog(self):
1167         dialog = gtk.FileChooserDialog("Load Kernel Files", self,
1168                                        gtk.FILE_CHOOSER_ACTION_SAVE)
1169         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1170         HobAltButton.style_button(button)
1171         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1172         HobButton.style_button(button)
1173         filter = gtk.FileFilter()
1174         filter.set_name("Kernel Files")
1175         filter.add_pattern("*.bin")
1176         dialog.add_filter(filter)
1177
1178         dialog.set_current_folder(self.parameters.image_addr)
1179
1180         response = dialog.run()
1181         kernel_path = ""
1182         if response == gtk.RESPONSE_YES:
1183             kernel_path = dialog.get_filename()
1184
1185         dialog.destroy()
1186
1187         return kernel_path
1188
1189     def runqemu_image(self, image_name, kernel_name):
1190         if not image_name or not kernel_name:
1191             lbl = "<b>Please select an %s to launch in QEMU.</b>" % ("kernel" if image_name else "image")
1192             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1193             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1194             HobButton.style_button(button)
1195             dialog.run()
1196             dialog.destroy()
1197             return
1198
1199         kernel_path = os.path.join(self.parameters.image_addr, kernel_name)
1200         image_path = os.path.join(self.parameters.image_addr, image_name)
1201
1202         source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env")
1203         tmp_path = self.parameters.tmpdir
1204         cmdline = bb.ui.crumbs.utils.which_terminal()
1205         if os.path.exists(image_path) and os.path.exists(kernel_path) \
1206            and os.path.exists(source_env_path) and os.path.exists(tmp_path) \
1207            and cmdline:
1208             cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; "
1209             cmdline += "source " + source_env_path + " " + os.getcwd() + "; "
1210             cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'"
1211             subprocess.Popen(shlex.split(cmdline))
1212         else:
1213             lbl = "<b>Path error</b>\nOne of your paths is wrong,"
1214             lbl = lbl + " please make sure the following paths exist:\n"
1215             lbl = lbl + "image path:" + image_path + "\n"
1216             lbl = lbl + "kernel path:" + kernel_path + "\n"
1217             lbl = lbl + "source environment path:" + source_env_path + "\n"
1218             lbl = lbl + "tmp path: " + tmp_path + "."
1219             lbl = lbl + "You may be missing either xterm or vte for terminal services."
1220             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
1221             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1222             HobButton.style_button(button)
1223             dialog.run()
1224             dialog.destroy()
1225
1226     def show_packages(self, ask=True):
1227         _, selected_recipes = self.recipe_model.get_selected_recipes()
1228         if selected_recipes and ask:
1229             lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes"
1230             lbl = lbl + " to get a full list or just view the existing packages?"
1231             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1232             button = dialog.add_button("View packages", gtk.RESPONSE_NO)
1233             HobAltButton.style_button(button)
1234             button = dialog.add_button("Build packages", gtk.RESPONSE_YES)
1235             HobButton.style_button(button)
1236             dialog.set_default_response(gtk.RESPONSE_YES)
1237             response = dialog.run()
1238             dialog.destroy()
1239             if response == gtk.RESPONSE_YES:
1240                 self.generate_packages_async()
1241             else:
1242                 self.switch_page(self.PACKAGE_SELECTION)
1243         else:
1244             self.switch_page(self.PACKAGE_SELECTION)
1245
1246     def show_recipes(self):
1247         self.switch_page(self.RECIPE_SELECTION)
1248
1249     def show_configuration(self):
1250         self.switch_page(self.BASEIMG_SELECTED)
1251
1252     def stop_build(self):
1253         if self.stopping:
1254             lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once,"
1255             lbl = lbl + " would you like to 'Force Stop' the build?\n\n"
1256             lbl = lbl + "This will stop the build as quickly as possible but may"
1257             lbl = lbl + " well leave your build directory in an  unusable state"
1258             lbl = lbl + " that requires manual steps to fix.\n"
1259             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
1260             button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1261             HobAltButton.style_button(button)
1262             button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
1263             HobButton.style_button(button)
1264         else:
1265             lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this"
1266             lbl = lbl + " build?\n\n'Force Stop' will stop the build as quickly as"
1267             lbl = lbl + " possible but may well leave your build directory in an"
1268             lbl = lbl + " unusable state that requires manual steps to fix.\n\n"
1269             lbl = lbl + "'Stop' will stop the build as soon as all in"
1270             lbl = lbl + " progress build tasks are finished. However if a"
1271             lbl = lbl + " lengthy compilation phase is in progress this may take"
1272             lbl = lbl + " some time."
1273             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
1274             button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1275             HobAltButton.style_button(button)
1276             button = dialog.add_button("Stop", gtk.RESPONSE_OK)
1277             HobAltButton.style_button(button)
1278             button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
1279             HobButton.style_button(button)
1280         response = dialog.run()
1281         dialog.destroy()
1282         if response != gtk.RESPONSE_CANCEL:
1283             self.stopping = True
1284         if response == gtk.RESPONSE_OK:
1285             self.cancel_build_sync()
1286         elif response == gtk.RESPONSE_YES:
1287             self.cancel_build_sync(True)