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