hob: remove confirmation dialog on close
[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, SimpleSettingsDialog, \
42                              LayerSelectionDialog, 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             self.package_details_page.set_title("Edit packages")
657             if self.recipe_model.get_selected_image() == self.recipe_model.__custom_image__:
658                 self.package_details_page.set_packages_curr_tab(self.package_details_page.ALL)
659             else:
660                 self.package_details_page.set_packages_curr_tab(self.package_details_page.INCLUDED)
661             self.package_details_page.show_page(self.current_logfile)
662
663
664         elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING:
665             # both PACKAGE_GENERATING and FAST_IMAGE_GENERATING share the same page
666             self.build_details_page.show_page(next_step)
667
668         elif next_step == self.PACKAGE_GENERATED:
669             self.package_details_page.set_title("Step 2 of 2: Edit packages")
670             if self.recipe_model.get_selected_image() == self.recipe_model.__custom_image__:
671                 self.package_details_page.set_packages_curr_tab(self.package_details_page.ALL)
672             else:
673                 self.package_details_page.set_packages_curr_tab(self.package_details_page.INCLUDED)
674             self.package_details_page.show_page(self.current_logfile)
675
676         elif next_step == self.IMAGE_GENERATING:
677             # after packages are generated, selected_packages need to
678             # be updated in package_model per selected_image in recipe_model
679             self.build_details_page.show_page(next_step)
680
681         elif next_step == self.IMAGE_GENERATED:
682             self.image_details_page.show_page(next_step)
683
684         elif next_step == self.MY_IMAGE_OPENED:
685             self.image_details_page.show_page(next_step)
686
687         self.previous_step = self.current_step
688         self.current_step = next_step
689
690     def set_user_config(self):
691         self.handler.init_cooker()
692         # set bb layers
693         self.handler.set_bblayers(self.configuration.layers)
694         # set local configuration
695         self.handler.set_machine(self.configuration.curr_mach)
696         self.handler.set_package_format(self.configuration.curr_package_format)
697         self.handler.set_distro(self.configuration.curr_distro)
698         self.handler.set_dl_dir(self.configuration.dldir)
699         self.handler.set_sstate_dir(self.configuration.sstatedir)
700         self.handler.set_sstate_mirror(self.configuration.sstatemirror)
701         self.handler.set_pmake(self.configuration.pmake)
702         self.handler.set_bbthreads(self.configuration.bbthread)
703         self.handler.set_rootfs_size(self.configuration.image_rootfs_size)
704         self.handler.set_extra_size(self.configuration.image_extra_size)
705         self.handler.set_incompatible_license(self.configuration.incompat_license)
706         self.handler.set_sdk_machine(self.configuration.curr_sdk_machine)
707         self.handler.set_image_fstypes(self.configuration.image_fstypes)
708         self.handler.set_extra_config(self.configuration.extra_setting)
709         self.handler.set_extra_inherit("packageinfo")
710         self.handler.set_extra_inherit("image_types")
711         # set proxies
712         if self.configuration.enable_proxy == True:
713             self.handler.set_http_proxy(self.configuration.combine_proxy("http"))
714             self.handler.set_https_proxy(self.configuration.combine_proxy("https"))
715             self.handler.set_ftp_proxy(self.configuration.combine_proxy("ftp"))
716             self.handler.set_git_proxy(self.configuration.combine_host_only("git"), self.configuration.combine_port_only("git"))
717             self.handler.set_cvs_proxy(self.configuration.combine_host_only("cvs"), self.configuration.combine_port_only("cvs"))
718         elif self.configuration.enable_proxy == False:
719             self.handler.set_http_proxy("")
720             self.handler.set_https_proxy("")
721             self.handler.set_ftp_proxy("")
722             self.handler.set_git_proxy("", "")
723             self.handler.set_cvs_proxy("", "")
724
725     def update_recipe_model(self, selected_image, selected_recipes):
726         self.recipe_model.set_selected_image(selected_image)
727         self.recipe_model.set_selected_recipes(selected_recipes)
728
729     def update_package_model(self, selected_packages):
730         left = self.package_model.set_selected_packages(selected_packages)
731         self.configuration.selected_packages += left
732
733     def update_configuration_parameters(self, params):
734         if params:
735             self.configuration.update(params)
736             self.parameters.update(params)
737
738     def reset(self):
739         self.configuration.curr_mach = ""
740         self.configuration.clear_selection()
741         self.image_configuration_page.switch_machine_combo()
742         self.switch_page(self.MACHINE_SELECTION)
743
744     # Callback Functions
745     def handler_config_updated_cb(self, handler, which, values):
746         if which == "distro":
747             self.parameters.all_distros = values
748         elif which == "machine":
749             self.parameters.all_machines = values
750             self.image_configuration_page.update_machine_combo()
751         elif which == "machine-sdk":
752             self.parameters.all_sdk_machines = values
753
754     def handler_package_formats_updated_cb(self, handler, formats):
755         self.parameters.all_package_formats = formats
756
757     def handler_command_succeeded_cb(self, handler, initcmd):
758         if initcmd == self.handler.GENERATE_CONFIGURATION:
759             if not self.configuration.curr_mach:
760                 self.configuration.curr_mach = self.handler.runCommand(["getVariable", "HOB_MACHINE"]) or ""
761             self.update_configuration_parameters(self.get_parameters_sync())
762             self.sanity_check()
763         elif initcmd == self.handler.SANITY_CHECK:
764             self.image_configuration_page.switch_machine_combo()
765         elif initcmd in [self.handler.GENERATE_RECIPES,
766                          self.handler.GENERATE_PACKAGES,
767                          self.handler.GENERATE_IMAGE]:
768             self.update_configuration_parameters(self.get_parameters_sync())
769             self.request_package_info_async()
770         elif initcmd == self.handler.POPULATE_PACKAGEINFO:
771             if self.current_step == self.RCPPKGINFO_POPULATING:
772                 self.switch_page(self.RCPPKGINFO_POPULATED)
773                 self.rcppkglist_populated()
774                 return
775
776             self.rcppkglist_populated()
777             if self.current_step == self.FAST_IMAGE_GENERATING:
778                 self.generate_image_async(True)
779
780     def show_error_dialog(self, msg):
781         lbl = "<b>Error</b>\n"
782         lbl = lbl + "%s\n\n" % glib.markup_escape_text(msg)
783         dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
784         button = dialog.add_button("Close", gtk.RESPONSE_OK)
785         HobButton.style_button(button)
786         response = dialog.run()
787         dialog.destroy()
788
789     def handler_command_failed_cb(self, handler, msg):
790         if msg:
791             self.show_error_dialog(msg)
792         self.reset()
793
794     def handler_sanity_failed_cb(self, handler, msg):
795         msg = msg.replace("your local.conf", "Settings")
796         self.show_error_dialog(msg)
797         self.reset()
798
799     def window_sensitive(self, sensitive):
800         self.image_configuration_page.machine_combo.set_sensitive(sensitive)
801         self.image_configuration_page.machine_combo.child.set_sensitive(sensitive)
802         self.image_configuration_page.image_combo.set_sensitive(sensitive)
803         self.image_configuration_page.image_combo.child.set_sensitive(sensitive)
804         self.image_configuration_page.layer_button.set_sensitive(sensitive)
805         self.image_configuration_page.layer_info_icon.set_sensitive(sensitive)
806         self.image_configuration_page.toolbar.set_sensitive(sensitive)
807         self.image_configuration_page.view_adv_configuration_button.set_sensitive(sensitive)
808         self.image_configuration_page.config_build_button.set_sensitive(sensitive)
809
810         self.recipe_details_page.set_sensitive(sensitive)
811         self.package_details_page.set_sensitive(sensitive)
812         self.build_details_page.set_sensitive(sensitive)
813         self.image_details_page.set_sensitive(sensitive)
814
815         if sensitive:
816             self.window.set_cursor(None)
817         else:
818             self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
819         self.sensitive = sensitive
820
821
822     def handler_generating_data_cb(self, handler):
823         self.window_sensitive(False)
824
825     def handler_data_generated_cb(self, handler):
826         self.window_sensitive(True)
827
828     def rcppkglist_populated(self):
829         selected_image = self.configuration.selected_image
830         selected_recipes = self.configuration.selected_recipes[:]
831         selected_packages = self.configuration.selected_packages[:]
832
833         self.image_configuration_page.update_image_combo(self.recipe_model, selected_image)
834         self.image_configuration_page.update_image_desc()
835         self.update_recipe_model(selected_image, selected_recipes)
836         self.update_package_model(selected_packages)
837
838     def recipelist_changed_cb(self, recipe_model):
839         self.recipe_details_page.refresh_selection()
840
841     def packagelist_changed_cb(self, package_model):
842         self.package_details_page.refresh_selection()
843
844     def handler_recipe_populated_cb(self, handler):
845         self.image_configuration_page.update_progress_bar("Populated recipes", 0.99)
846
847     def handler_package_populated_cb(self, handler):
848         self.image_configuration_page.update_progress_bar("Populated packages", 1.0)
849
850     def handler_parsing_started_cb(self, handler, message):
851         if self.current_step != self.RCPPKGINFO_POPULATING:
852             return
853
854         fraction = 0
855         if message["eventname"] == "TreeDataPreparationStarted":
856             fraction = 0.6 + fraction
857             self.image_configuration_page.stop_button.set_sensitive(False)
858         else:
859             self.image_configuration_page.stop_button.set_sensitive(True)
860
861         self.image_configuration_page.update_progress_bar(message["title"], fraction)
862
863     def handler_parsing_cb(self, handler, message):
864         if self.current_step != self.RCPPKGINFO_POPULATING:
865             return
866
867         fraction = message["current"] * 1.0/message["total"]
868         if message["eventname"] == "TreeDataPreparationProgress":
869             fraction = 0.6 + 0.38 * fraction
870         else:
871             fraction = 0.6 * fraction
872         self.image_configuration_page.update_progress_bar(message["title"], fraction)
873
874     def handler_parsing_completed_cb(self, handler, message):
875         if self.current_step != self.RCPPKGINFO_POPULATING:
876             return
877
878         if message["eventname"] == "TreeDataPreparationCompleted":
879             fraction = 0.98
880         else:
881             fraction = 0.6
882         self.image_configuration_page.update_progress_bar(message["title"], fraction)
883
884     def handler_build_started_cb(self, running_build):
885         if self.current_step == self.FAST_IMAGE_GENERATING:
886             fraction = 0
887         elif self.current_step == self.IMAGE_GENERATING:
888             if self.previous_step == self.FAST_IMAGE_GENERATING:
889                 fraction = 0.9
890             else:
891                 fraction = 0
892         elif self.current_step == self.PACKAGE_GENERATING:
893             fraction = 0
894         self.build_details_page.update_progress_bar("Build Started: ", fraction)
895         self.build_details_page.show_configurations(self.configuration, self.parameters)
896
897     def build_succeeded(self):
898         if self.current_step == self.FAST_IMAGE_GENERATING:
899             fraction = 0.9
900         elif self.current_step == self.IMAGE_GENERATING:
901             fraction = 1.0
902             self.parameters.image_names = []
903             selected_image = self.recipe_model.get_selected_image()
904             if selected_image == self.recipe_model.__custom_image__:
905                 linkname = 'hob-image-' + self.configuration.curr_mach
906             else:
907                 linkname = selected_image + '-' + self.configuration.curr_mach
908             image_extension = self.get_image_extension()
909             for image_type in self.parameters.image_types:
910                 if image_type in image_extension:
911                     real_types = image_extension[image_type]
912                 else:
913                     real_types = [image_type]
914                 for real_image_type in real_types:
915                     linkpath = self.parameters.image_addr + '/' + linkname + '.' + real_image_type
916                     if os.path.exists(linkpath):
917                         self.parameters.image_names.append(os.readlink(linkpath))
918         elif self.current_step == self.PACKAGE_GENERATING:
919             fraction = 1.0
920         self.build_details_page.update_progress_bar("Build Completed: ", fraction)
921         self.handler.build_succeeded_async()
922         self.stopping = False
923
924         if self.current_step == self.PACKAGE_GENERATING:
925             self.switch_page(self.PACKAGE_GENERATED)
926         elif self.current_step == self.IMAGE_GENERATING:
927             self.switch_page(self.IMAGE_GENERATED)
928
929     def build_failed(self):
930         if self.stopping:
931             status = "stop"
932             message = "Build stopped: "
933             fraction = self.build_details_page.progress_bar.get_fraction()
934             stop_to_next_edit = ""
935             if self.current_step == self.FAST_IMAGE_GENERATING:
936                 stop_to_next_edit = "image configuration"
937             elif self.current_step == self.IMAGE_GENERATING:
938                 if self.previous_step == self.FAST_IMAGE_GENERATING:
939                     stop_to_next_edit = "image configuration"
940                 else:
941                     stop_to_next_edit = "packages"
942             elif self.current_step == self.PACKAGE_GENERATING:
943                 stop_to_next_edit = "recipes"
944             button = self.build_details_page.show_stop_page(stop_to_next_edit.split(' ')[0])
945             self.set_default(button)
946         else:
947             fail_to_next_edit = ""
948             if self.current_step == self.FAST_IMAGE_GENERATING:
949                 fail_to_next_edit = "image configuration"
950                 fraction = 0.9
951             elif self.current_step == self.IMAGE_GENERATING:
952                 if self.previous_step == self.FAST_IMAGE_GENERATING:
953                     fail_to_next_edit = "image configuration"
954                 else:
955                     fail_to_next_edit = "packages"
956                 fraction = 1.0
957             elif self.current_step == self.PACKAGE_GENERATING:
958                 fail_to_next_edit = "recipes"
959                 fraction = 1.0
960             self.build_details_page.show_fail_page(fail_to_next_edit.split(' ')[0])
961             status = "fail"
962             message = "Build failed: "
963         self.build_details_page.update_progress_bar(message, fraction, status)
964         self.build_details_page.show_back_button()
965         self.build_details_page.hide_stop_button()
966         self.handler.build_failed_async()
967         self.stopping = False
968
969     def handler_build_succeeded_cb(self, running_build):
970         if not self.stopping:
971             self.build_succeeded()
972         else:
973             self.build_failed()
974
975
976     def handler_build_failed_cb(self, running_build):
977         self.build_failed()
978
979     def handler_build_aborted_cb(self, running_build):
980         self.build_failed()
981
982     def handler_no_provider_cb(self, running_build, msg):
983         dialog = CrumbsMessageDialog(self, glib.markup_escape_text(msg), gtk.STOCK_DIALOG_INFO)
984         button = dialog.add_button("Close", gtk.RESPONSE_OK)
985         HobButton.style_button(button)
986         dialog.run()
987         dialog.destroy()
988         self.build_failed()
989
990     def handler_task_started_cb(self, running_build, message): 
991         fraction = message["current"] * 1.0/message["total"]
992         title = "Build packages"
993         if self.current_step == self.FAST_IMAGE_GENERATING:
994             if message["eventname"] == "sceneQueueTaskStarted":
995                 fraction = 0.27 * fraction
996             elif message["eventname"] == "runQueueTaskStarted":
997                 fraction = 0.27 + 0.63 * fraction
998         elif self.current_step == self.IMAGE_GENERATING:
999             title = "Build image"
1000             if self.previous_step == self.FAST_IMAGE_GENERATING:
1001                 if message["eventname"] == "sceneQueueTaskStarted":
1002                     fraction = 0.27 + 0.63 + 0.03 * fraction
1003                 elif message["eventname"] == "runQueueTaskStarted":
1004                     fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction
1005             else:
1006                 if message["eventname"] == "sceneQueueTaskStarted":
1007                     fraction = 0.2 * fraction
1008                 elif message["eventname"] == "runQueueTaskStarted":
1009                     fraction = 0.2 + 0.8 * fraction
1010         elif self.current_step == self.PACKAGE_GENERATING:
1011             if message["eventname"] == "sceneQueueTaskStarted":
1012                 fraction = 0.2 * fraction
1013             elif message["eventname"] == "runQueueTaskStarted":
1014                 fraction = 0.2 + 0.8 * fraction
1015         self.build_details_page.update_progress_bar(title + ": ", fraction)
1016         self.build_details_page.update_build_status(message["current"], message["total"], message["task"])
1017
1018     def handler_build_failure_cb(self, running_build):
1019         self.build_details_page.show_issues()
1020
1021     def handler_build_log_cb(self, running_build, func, obj):
1022         if hasattr(self.logger, func):
1023             getattr(self.logger, func)(obj)
1024
1025     def destroy_window_cb(self, widget, event):
1026         if not self.sensitive:
1027             return True
1028         elif self.handler.building:
1029             self.stop_build()
1030             return True
1031         else:
1032             gtk.main_quit()
1033
1034     def build_packages(self):
1035         _, all_recipes = self.recipe_model.get_selected_recipes()
1036         if not all_recipes:
1037             lbl = "<b>No selections made</b>\nYou have not made any selections"
1038             lbl = lbl + " so there isn't anything to bake at this time."
1039             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1040             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1041             HobButton.style_button(button)
1042             dialog.run()
1043             dialog.destroy()
1044             return
1045         self.generate_packages_async(True)
1046
1047     def build_image(self):
1048         selected_packages = self.package_model.get_selected_packages()
1049         if not selected_packages:      
1050             lbl = "<b>No selections made</b>\nYou have not made any selections"
1051             lbl = lbl + " so there isn't anything to bake at this time."
1052             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1053             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1054             HobButton.style_button(button)
1055             dialog.run()
1056             dialog.destroy()
1057             return
1058         self.generate_image_async(True)
1059
1060     def just_bake(self):
1061         selected_image = self.recipe_model.get_selected_image()
1062         selected_packages = self.package_model.get_selected_packages() or []
1063
1064         # If no base image and no selected packages don't build anything
1065         if not (selected_packages or selected_image != self.recipe_model.__custom_image__):
1066             lbl = "<b>No selections made</b>\nYou have not made any selections"
1067             lbl = lbl + " so there isn't anything to bake at this time."
1068             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1069             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1070             HobButton.style_button(button)
1071             dialog.run()
1072             dialog.destroy()
1073             return
1074
1075         self.fast_generate_image_async(True)
1076
1077     def show_binb_dialog(self, binb):
1078         markup = "<b>Brought in by:</b>\n%s" % binb
1079         ptip = PersistentTooltip(markup, self)
1080
1081         ptip.show()
1082
1083     def show_layer_selection_dialog(self):
1084         dialog = LayerSelectionDialog(title = "Layers",
1085                      layers = copy.deepcopy(self.configuration.layers),
1086                      all_layers = self.parameters.all_layers,
1087                      parent = self,
1088                      flags = gtk.DIALOG_MODAL
1089                          | gtk.DIALOG_DESTROY_WITH_PARENT
1090                          | gtk.DIALOG_NO_SEPARATOR)
1091         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1092         HobAltButton.style_button(button)
1093         button = dialog.add_button("OK", gtk.RESPONSE_YES)
1094         HobButton.style_button(button)
1095         response = dialog.run()
1096         if response == gtk.RESPONSE_YES:
1097             self.configuration.layers = dialog.layers
1098             self.save_defaults() # remember layers
1099             # DO refresh layers
1100             if dialog.layers_changed:
1101                 self.update_config_async()
1102         dialog.destroy()
1103
1104     def show_load_template_dialog(self):
1105         dialog = gtk.FileChooserDialog("Load Template Files", self,
1106                                        gtk.FILE_CHOOSER_ACTION_OPEN)
1107         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1108         HobAltButton.style_button(button)
1109         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1110         HobButton.style_button(button)
1111         filter = gtk.FileFilter()
1112         filter.set_name("Hob Files")
1113         filter.add_pattern("*.hob")
1114         dialog.add_filter(filter)
1115
1116         response = dialog.run()
1117         path = None
1118         if response == gtk.RESPONSE_YES:
1119             path = dialog.get_filename()
1120         dialog.destroy()
1121         return response == gtk.RESPONSE_YES, path
1122
1123     def show_save_template_dialog(self):
1124         dialog = gtk.FileChooserDialog("Save Template Files", self,
1125                                        gtk.FILE_CHOOSER_ACTION_SAVE)
1126         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1127         HobAltButton.style_button(button)
1128         button = dialog.add_button("Save", gtk.RESPONSE_YES)
1129         HobButton.style_button(button)
1130         dialog.set_current_name("hob")
1131         response = dialog.run()
1132         if response == gtk.RESPONSE_YES:
1133             path = dialog.get_filename()
1134             self.save_template(path)
1135         dialog.destroy()
1136
1137     def get_image_extension(self):
1138         image_extension = {}
1139         for type in self.parameters.image_types:
1140             ext = self.handler.runCommand(["getVariable", "IMAGE_EXTENSION_%s" % type])
1141             if ext:
1142                 image_extension[type] = ext.split(' ')
1143
1144         return image_extension
1145
1146     def show_load_my_images_dialog(self):
1147         image_extension = self.get_image_extension()
1148         dialog = ImageSelectionDialog(self.parameters.image_addr, self.parameters.image_types,
1149                                       "Open My Images", self,
1150                                        gtk.FILE_CHOOSER_ACTION_SAVE, None,
1151                                        image_extension)
1152         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1153         HobAltButton.style_button(button)
1154         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1155         HobButton.style_button(button)
1156         response = dialog.run()
1157         if response == gtk.RESPONSE_YES:
1158             if not dialog.image_names:
1159                 lbl = "<b>No selections made</b>\nYou have not made any selections"
1160                 crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1161                 button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
1162                 HobButton.style_button(button)
1163                 crumbs_dialog.run()
1164                 crumbs_dialog.destroy()
1165                 dialog.destroy()
1166                 return
1167
1168             self.parameters.image_addr = dialog.image_folder
1169             self.parameters.image_names = dialog.image_names[:]
1170             self.switch_page(self.MY_IMAGE_OPENED)
1171
1172         dialog.destroy()
1173
1174     def show_adv_settings_dialog(self):
1175         dialog = AdvancedSettingDialog(title = "Advanced configuration",
1176             configuration = copy.deepcopy(self.configuration),
1177             all_image_types = self.parameters.image_types,
1178             all_package_formats = self.parameters.all_package_formats,
1179             all_distros = self.parameters.all_distros,
1180             all_sdk_machines = self.parameters.all_sdk_machines,
1181             max_threads = self.parameters.max_threads,
1182             parent = self,
1183             flags = gtk.DIALOG_MODAL
1184                     | gtk.DIALOG_DESTROY_WITH_PARENT
1185                     | gtk.DIALOG_NO_SEPARATOR)
1186         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1187         HobAltButton.style_button(button)
1188         button = dialog.add_button("Save", gtk.RESPONSE_YES)
1189         HobButton.style_button(button)
1190         response = dialog.run()
1191         settings_changed = False
1192         if response == gtk.RESPONSE_YES:
1193             self.configuration = dialog.configuration
1194             self.save_defaults() # remember settings
1195             settings_changed = dialog.settings_changed
1196         dialog.destroy()
1197         return response == gtk.RESPONSE_YES, settings_changed
1198
1199     def show_simple_settings_dialog(self):
1200         dialog = SimpleSettingsDialog(title = "Settings",
1201             configuration = copy.deepcopy(self.configuration),
1202             all_image_types = self.parameters.image_types,
1203             all_package_formats = self.parameters.all_package_formats,
1204             all_distros = self.parameters.all_distros,
1205             all_sdk_machines = self.parameters.all_sdk_machines,
1206             max_threads = self.parameters.max_threads,
1207             parent = self,
1208             flags = gtk.DIALOG_MODAL
1209                     | gtk.DIALOG_DESTROY_WITH_PARENT
1210                     | gtk.DIALOG_NO_SEPARATOR)
1211         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1212         HobAltButton.style_button(button)
1213         button = dialog.add_button("Save", gtk.RESPONSE_YES)
1214         HobButton.style_button(button)
1215         response = dialog.run()
1216         settings_changed = False
1217         if response == gtk.RESPONSE_YES:
1218             self.configuration = dialog.configuration
1219             self.save_defaults() # remember settings
1220             settings_changed = dialog.settings_changed
1221         dialog.destroy()
1222         return response == gtk.RESPONSE_YES, settings_changed
1223
1224     def reparse_post_adv_settings(self):
1225         if not self.configuration.curr_mach:
1226             self.update_config_async()
1227         else:
1228             self.configuration.clear_selection()
1229             # DO reparse recipes
1230             self.populate_recipe_package_info_async()
1231
1232     def deploy_image(self, image_name):
1233         if not image_name:
1234             lbl = "<b>Please select an image to deploy.</b>"
1235             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1236             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1237             HobButton.style_button(button)
1238             dialog.run()
1239             dialog.destroy()
1240             return
1241
1242         image_path = os.path.join(self.parameters.image_addr, image_name)
1243         dialog = DeployImageDialog(title = "Usb Image Maker",
1244             image_path = image_path,
1245             parent = self,
1246             flags = gtk.DIALOG_MODAL
1247                     | gtk.DIALOG_DESTROY_WITH_PARENT
1248                     | gtk.DIALOG_NO_SEPARATOR)
1249         button = dialog.add_button("Close", gtk.RESPONSE_NO)
1250         HobAltButton.style_button(button)
1251         button = dialog.add_button("Make usb image", gtk.RESPONSE_YES)
1252         HobButton.style_button(button)
1253         response = dialog.run()
1254         dialog.destroy()
1255
1256     def show_load_kernel_dialog(self):
1257         dialog = gtk.FileChooserDialog("Load Kernel Files", self,
1258                                        gtk.FILE_CHOOSER_ACTION_SAVE)
1259         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1260         HobAltButton.style_button(button)
1261         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1262         HobButton.style_button(button)
1263         filter = gtk.FileFilter()
1264         filter.set_name("Kernel Files")
1265         filter.add_pattern("*.bin")
1266         dialog.add_filter(filter)
1267
1268         dialog.set_current_folder(self.parameters.image_addr)
1269
1270         response = dialog.run()
1271         kernel_path = ""
1272         if response == gtk.RESPONSE_YES:
1273             kernel_path = dialog.get_filename()
1274
1275         dialog.destroy()
1276
1277         return kernel_path
1278
1279     def runqemu_image(self, image_name, kernel_name):
1280         if not image_name or not kernel_name:
1281             lbl = "<b>Please select an %s to launch in QEMU.</b>" % ("kernel" if image_name else "image")
1282             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1283             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1284             HobButton.style_button(button)
1285             dialog.run()
1286             dialog.destroy()
1287             return
1288
1289         kernel_path = os.path.join(self.parameters.image_addr, kernel_name)
1290         image_path = os.path.join(self.parameters.image_addr, image_name)
1291
1292         source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env")
1293         tmp_path = self.parameters.tmpdir
1294         cmdline = bb.ui.crumbs.utils.which_terminal()
1295         if os.path.exists(image_path) and os.path.exists(kernel_path) \
1296            and os.path.exists(source_env_path) and os.path.exists(tmp_path) \
1297            and cmdline:
1298             cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; "
1299             cmdline += "source " + source_env_path + " " + os.getcwd() + "; "
1300             cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'"
1301             subprocess.Popen(shlex.split(cmdline))
1302         else:
1303             lbl = "<b>Path error</b>\nOne of your paths is wrong,"
1304             lbl = lbl + " please make sure the following paths exist:\n"
1305             lbl = lbl + "image path:" + image_path + "\n"
1306             lbl = lbl + "kernel path:" + kernel_path + "\n"
1307             lbl = lbl + "source environment path:" + source_env_path + "\n"
1308             lbl = lbl + "tmp path: " + tmp_path + "."
1309             lbl = lbl + "You may be missing either xterm or vte for terminal services."
1310             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
1311             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1312             HobButton.style_button(button)
1313             dialog.run()
1314             dialog.destroy()
1315
1316     def show_packages(self, ask=True):
1317         _, selected_recipes = self.recipe_model.get_selected_recipes()
1318         if selected_recipes and ask:
1319             lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes"
1320             lbl = lbl + " to get a full list or just view the existing packages?"
1321             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1322             button = dialog.add_button("View packages", gtk.RESPONSE_NO)
1323             HobAltButton.style_button(button)
1324             button = dialog.add_button("Build packages", gtk.RESPONSE_YES)
1325             HobButton.style_button(button)
1326             dialog.set_default_response(gtk.RESPONSE_YES)
1327             response = dialog.run()
1328             dialog.destroy()
1329             if response == gtk.RESPONSE_YES:
1330                 self.generate_packages_async(True)
1331             else:
1332                 self.switch_page(self.PACKAGE_SELECTION)
1333         else:
1334             self.switch_page(self.PACKAGE_SELECTION)
1335
1336     def show_recipes(self):
1337         self.switch_page(self.RECIPE_SELECTION)
1338
1339     def show_image_details(self):
1340         self.switch_page(self.IMAGE_GENERATED)
1341
1342     def show_configuration(self):
1343         self.switch_page(self.BASEIMG_SELECTED)
1344
1345     def stop_build(self):
1346         if self.stopping:
1347             lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once,"
1348             lbl = lbl + " would you like to 'Force Stop' the build?\n\n"
1349             lbl = lbl + "This will stop the build as quickly as possible but may"
1350             lbl = lbl + " well leave your build directory in an  unusable state"
1351             lbl = lbl + " that requires manual steps to fix.\n"
1352             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
1353             button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1354             HobAltButton.style_button(button)
1355             button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
1356             HobButton.style_button(button)
1357         else:
1358             lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this"
1359             lbl = lbl + " build?\n\n'Stop' will stop the build as soon as all in"
1360             lbl = lbl + " progress build tasks are finished. However if a"
1361             lbl = lbl + " lengthy compilation phase is in progress this may take"
1362             lbl = lbl + " some time.\n\n"
1363             lbl = lbl + "'Force Stop' will stop the build as quickly as"
1364             lbl = lbl + " possible but may well leave your build directory in an"
1365             lbl = lbl + " unusable state that requires manual steps to fix."
1366             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
1367             button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1368             HobAltButton.style_button(button)
1369             button = dialog.add_button("Force stop", gtk.RESPONSE_YES)
1370             HobAltButton.style_button(button)
1371             button = dialog.add_button("Stop", gtk.RESPONSE_OK)
1372             HobButton.style_button(button)
1373         response = dialog.run()
1374         dialog.destroy()
1375         if response != gtk.RESPONSE_CANCEL:
1376             self.stopping = True
1377         if response == gtk.RESPONSE_OK:
1378             self.cancel_build_sync()
1379         elif response == gtk.RESPONSE_YES:
1380             self.cancel_build_sync(True)
1381
1382     def do_log(self, consolelogfile = None):
1383         if consolelogfile:
1384             bb.utils.mkdirhier(os.path.dirname(consolelogfile))
1385             if self.consolelog:
1386                 self.logger.removeHandler(self.consolelog)
1387                 self.consolelog = None
1388             self.consolelog = logging.FileHandler(consolelogfile)
1389             bb.msg.addDefaultlogFilter(self.consolelog)
1390             format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
1391             self.consolelog.setFormatter(format)
1392
1393             self.logger.addHandler(self.consolelog)