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