hob: allow configuring default machine using HOB_MACHINE
[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         lbl = "<b>Do you really want to exit the Hob image creator?</b>"
1029         dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1030         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1031         HobAltButton.style_button(button)
1032         button = dialog.add_button("Exit Hob", gtk.RESPONSE_YES)
1033         HobButton.style_button(button)
1034         dialog.set_default_response(gtk.RESPONSE_YES)
1035         response = dialog.run()
1036         dialog.destroy()
1037         if response == gtk.RESPONSE_YES:
1038             gtk.main_quit()
1039             return False
1040         else:
1041             return True
1042
1043     def build_packages(self):
1044         _, all_recipes = self.recipe_model.get_selected_recipes()
1045         if not all_recipes:
1046             lbl = "<b>No selections made</b>\nYou have not made any selections"
1047             lbl = lbl + " so there isn't anything to bake at this time."
1048             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1049             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1050             HobButton.style_button(button)
1051             dialog.run()
1052             dialog.destroy()
1053             return
1054         self.generate_packages_async(True)
1055
1056     def build_image(self):
1057         selected_packages = self.package_model.get_selected_packages()
1058         if not selected_packages:      
1059             lbl = "<b>No selections made</b>\nYou have not made any selections"
1060             lbl = lbl + " so there isn't anything to bake at this time."
1061             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1062             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1063             HobButton.style_button(button)
1064             dialog.run()
1065             dialog.destroy()
1066             return
1067         self.generate_image_async(True)
1068
1069     def just_bake(self):
1070         selected_image = self.recipe_model.get_selected_image()
1071         selected_packages = self.package_model.get_selected_packages() or []
1072
1073         # If no base image and no selected packages don't build anything
1074         if not (selected_packages or selected_image != self.recipe_model.__custom_image__):
1075             lbl = "<b>No selections made</b>\nYou have not made any selections"
1076             lbl = lbl + " so there isn't anything to bake at this time."
1077             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1078             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1079             HobButton.style_button(button)
1080             dialog.run()
1081             dialog.destroy()
1082             return
1083
1084         self.fast_generate_image_async(True)
1085
1086     def show_binb_dialog(self, binb):
1087         markup = "<b>Brought in by:</b>\n%s" % binb
1088         ptip = PersistentTooltip(markup, self)
1089
1090         ptip.show()
1091
1092     def show_layer_selection_dialog(self):
1093         dialog = LayerSelectionDialog(title = "Layers",
1094                      layers = copy.deepcopy(self.configuration.layers),
1095                      all_layers = self.parameters.all_layers,
1096                      parent = self,
1097                      flags = gtk.DIALOG_MODAL
1098                          | gtk.DIALOG_DESTROY_WITH_PARENT
1099                          | gtk.DIALOG_NO_SEPARATOR)
1100         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1101         HobAltButton.style_button(button)
1102         button = dialog.add_button("OK", gtk.RESPONSE_YES)
1103         HobButton.style_button(button)
1104         response = dialog.run()
1105         if response == gtk.RESPONSE_YES:
1106             self.configuration.layers = dialog.layers
1107             self.save_defaults() # remember layers
1108             # DO refresh layers
1109             if dialog.layers_changed:
1110                 self.update_config_async()
1111         dialog.destroy()
1112
1113     def show_load_template_dialog(self):
1114         dialog = gtk.FileChooserDialog("Load Template Files", self,
1115                                        gtk.FILE_CHOOSER_ACTION_OPEN)
1116         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1117         HobAltButton.style_button(button)
1118         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1119         HobButton.style_button(button)
1120         filter = gtk.FileFilter()
1121         filter.set_name("Hob Files")
1122         filter.add_pattern("*.hob")
1123         dialog.add_filter(filter)
1124
1125         response = dialog.run()
1126         path = None
1127         if response == gtk.RESPONSE_YES:
1128             path = dialog.get_filename()
1129         dialog.destroy()
1130         return response == gtk.RESPONSE_YES, path
1131
1132     def show_save_template_dialog(self):
1133         dialog = gtk.FileChooserDialog("Save Template Files", self,
1134                                        gtk.FILE_CHOOSER_ACTION_SAVE)
1135         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1136         HobAltButton.style_button(button)
1137         button = dialog.add_button("Save", gtk.RESPONSE_YES)
1138         HobButton.style_button(button)
1139         dialog.set_current_name("hob")
1140         response = dialog.run()
1141         if response == gtk.RESPONSE_YES:
1142             path = dialog.get_filename()
1143             self.save_template(path)
1144         dialog.destroy()
1145
1146     def get_image_extension(self):
1147         image_extension = {}
1148         for type in self.parameters.image_types:
1149             ext = self.handler.runCommand(["getVariable", "IMAGE_EXTENSION_%s" % type])
1150             if ext:
1151                 image_extension[type] = ext.split(' ')
1152
1153         return image_extension
1154
1155     def show_load_my_images_dialog(self):
1156         image_extension = self.get_image_extension()
1157         dialog = ImageSelectionDialog(self.parameters.image_addr, self.parameters.image_types,
1158                                       "Open My Images", self,
1159                                        gtk.FILE_CHOOSER_ACTION_SAVE, None,
1160                                        image_extension)
1161         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1162         HobAltButton.style_button(button)
1163         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1164         HobButton.style_button(button)
1165         response = dialog.run()
1166         if response == gtk.RESPONSE_YES:
1167             if not dialog.image_names:
1168                 lbl = "<b>No selections made</b>\nYou have not made any selections"
1169                 crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1170                 button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
1171                 HobButton.style_button(button)
1172                 crumbs_dialog.run()
1173                 crumbs_dialog.destroy()
1174                 dialog.destroy()
1175                 return
1176
1177             self.parameters.image_addr = dialog.image_folder
1178             self.parameters.image_names = dialog.image_names[:]
1179             self.switch_page(self.MY_IMAGE_OPENED)
1180
1181         dialog.destroy()
1182
1183     def show_adv_settings_dialog(self):
1184         dialog = AdvancedSettingDialog(title = "Advanced configuration",
1185             configuration = copy.deepcopy(self.configuration),
1186             all_image_types = self.parameters.image_types,
1187             all_package_formats = self.parameters.all_package_formats,
1188             all_distros = self.parameters.all_distros,
1189             all_sdk_machines = self.parameters.all_sdk_machines,
1190             max_threads = self.parameters.max_threads,
1191             parent = self,
1192             flags = gtk.DIALOG_MODAL
1193                     | gtk.DIALOG_DESTROY_WITH_PARENT
1194                     | gtk.DIALOG_NO_SEPARATOR)
1195         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1196         HobAltButton.style_button(button)
1197         button = dialog.add_button("Save", gtk.RESPONSE_YES)
1198         HobButton.style_button(button)
1199         response = dialog.run()
1200         settings_changed = False
1201         if response == gtk.RESPONSE_YES:
1202             self.configuration = dialog.configuration
1203             self.save_defaults() # remember settings
1204             settings_changed = dialog.settings_changed
1205         dialog.destroy()
1206         return response == gtk.RESPONSE_YES, settings_changed
1207
1208     def show_simple_settings_dialog(self):
1209         dialog = SimpleSettingsDialog(title = "Settings",
1210             configuration = copy.deepcopy(self.configuration),
1211             all_image_types = self.parameters.image_types,
1212             all_package_formats = self.parameters.all_package_formats,
1213             all_distros = self.parameters.all_distros,
1214             all_sdk_machines = self.parameters.all_sdk_machines,
1215             max_threads = self.parameters.max_threads,
1216             parent = self,
1217             flags = gtk.DIALOG_MODAL
1218                     | gtk.DIALOG_DESTROY_WITH_PARENT
1219                     | gtk.DIALOG_NO_SEPARATOR)
1220         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1221         HobAltButton.style_button(button)
1222         button = dialog.add_button("Save", gtk.RESPONSE_YES)
1223         HobButton.style_button(button)
1224         response = dialog.run()
1225         settings_changed = False
1226         if response == gtk.RESPONSE_YES:
1227             self.configuration = dialog.configuration
1228             self.save_defaults() # remember settings
1229             settings_changed = dialog.settings_changed
1230         dialog.destroy()
1231         return response == gtk.RESPONSE_YES, settings_changed
1232
1233     def reparse_post_adv_settings(self):
1234         if not self.configuration.curr_mach:
1235             self.update_config_async()
1236         else:
1237             self.configuration.clear_selection()
1238             # DO reparse recipes
1239             self.populate_recipe_package_info_async()
1240
1241     def deploy_image(self, image_name):
1242         if not image_name:
1243             lbl = "<b>Please select an image to deploy.</b>"
1244             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1245             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1246             HobButton.style_button(button)
1247             dialog.run()
1248             dialog.destroy()
1249             return
1250
1251         image_path = os.path.join(self.parameters.image_addr, image_name)
1252         dialog = DeployImageDialog(title = "Usb Image Maker",
1253             image_path = image_path,
1254             parent = self,
1255             flags = gtk.DIALOG_MODAL
1256                     | gtk.DIALOG_DESTROY_WITH_PARENT
1257                     | gtk.DIALOG_NO_SEPARATOR)
1258         button = dialog.add_button("Close", gtk.RESPONSE_NO)
1259         HobAltButton.style_button(button)
1260         button = dialog.add_button("Make usb image", gtk.RESPONSE_YES)
1261         HobButton.style_button(button)
1262         response = dialog.run()
1263         dialog.destroy()
1264
1265     def show_load_kernel_dialog(self):
1266         dialog = gtk.FileChooserDialog("Load Kernel Files", self,
1267                                        gtk.FILE_CHOOSER_ACTION_SAVE)
1268         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
1269         HobAltButton.style_button(button)
1270         button = dialog.add_button("Open", gtk.RESPONSE_YES)
1271         HobButton.style_button(button)
1272         filter = gtk.FileFilter()
1273         filter.set_name("Kernel Files")
1274         filter.add_pattern("*.bin")
1275         dialog.add_filter(filter)
1276
1277         dialog.set_current_folder(self.parameters.image_addr)
1278
1279         response = dialog.run()
1280         kernel_path = ""
1281         if response == gtk.RESPONSE_YES:
1282             kernel_path = dialog.get_filename()
1283
1284         dialog.destroy()
1285
1286         return kernel_path
1287
1288     def runqemu_image(self, image_name, kernel_name):
1289         if not image_name or not kernel_name:
1290             lbl = "<b>Please select an %s to launch in QEMU.</b>" % ("kernel" if image_name else "image")
1291             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1292             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1293             HobButton.style_button(button)
1294             dialog.run()
1295             dialog.destroy()
1296             return
1297
1298         kernel_path = os.path.join(self.parameters.image_addr, kernel_name)
1299         image_path = os.path.join(self.parameters.image_addr, image_name)
1300
1301         source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env")
1302         tmp_path = self.parameters.tmpdir
1303         cmdline = bb.ui.crumbs.utils.which_terminal()
1304         if os.path.exists(image_path) and os.path.exists(kernel_path) \
1305            and os.path.exists(source_env_path) and os.path.exists(tmp_path) \
1306            and cmdline:
1307             cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; "
1308             cmdline += "source " + source_env_path + " " + os.getcwd() + "; "
1309             cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'"
1310             subprocess.Popen(shlex.split(cmdline))
1311         else:
1312             lbl = "<b>Path error</b>\nOne of your paths is wrong,"
1313             lbl = lbl + " please make sure the following paths exist:\n"
1314             lbl = lbl + "image path:" + image_path + "\n"
1315             lbl = lbl + "kernel path:" + kernel_path + "\n"
1316             lbl = lbl + "source environment path:" + source_env_path + "\n"
1317             lbl = lbl + "tmp path: " + tmp_path + "."
1318             lbl = lbl + "You may be missing either xterm or vte for terminal services."
1319             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
1320             button = dialog.add_button("Close", gtk.RESPONSE_OK)
1321             HobButton.style_button(button)
1322             dialog.run()
1323             dialog.destroy()
1324
1325     def show_packages(self, ask=True):
1326         _, selected_recipes = self.recipe_model.get_selected_recipes()
1327         if selected_recipes and ask:
1328             lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes"
1329             lbl = lbl + " to get a full list or just view the existing packages?"
1330             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
1331             button = dialog.add_button("View packages", gtk.RESPONSE_NO)
1332             HobAltButton.style_button(button)
1333             button = dialog.add_button("Build packages", gtk.RESPONSE_YES)
1334             HobButton.style_button(button)
1335             dialog.set_default_response(gtk.RESPONSE_YES)
1336             response = dialog.run()
1337             dialog.destroy()
1338             if response == gtk.RESPONSE_YES:
1339                 self.generate_packages_async(True)
1340             else:
1341                 self.switch_page(self.PACKAGE_SELECTION)
1342         else:
1343             self.switch_page(self.PACKAGE_SELECTION)
1344
1345     def show_recipes(self):
1346         self.switch_page(self.RECIPE_SELECTION)
1347
1348     def show_image_details(self):
1349         self.switch_page(self.IMAGE_GENERATED)
1350
1351     def show_configuration(self):
1352         self.switch_page(self.BASEIMG_SELECTED)
1353
1354     def stop_build(self):
1355         if self.stopping:
1356             lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once,"
1357             lbl = lbl + " would you like to 'Force Stop' the build?\n\n"
1358             lbl = lbl + "This will stop the build as quickly as possible but may"
1359             lbl = lbl + " well leave your build directory in an  unusable state"
1360             lbl = lbl + " that requires manual steps to fix.\n"
1361             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
1362             button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1363             HobAltButton.style_button(button)
1364             button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
1365             HobButton.style_button(button)
1366         else:
1367             lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this"
1368             lbl = lbl + " build?\n\n'Stop' will stop the build as soon as all in"
1369             lbl = lbl + " progress build tasks are finished. However if a"
1370             lbl = lbl + " lengthy compilation phase is in progress this may take"
1371             lbl = lbl + " some time.\n\n"
1372             lbl = lbl + "'Force Stop' will stop the build as quickly as"
1373             lbl = lbl + " possible but may well leave your build directory in an"
1374             lbl = lbl + " unusable state that requires manual steps to fix."
1375             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
1376             button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
1377             HobAltButton.style_button(button)
1378             button = dialog.add_button("Force stop", gtk.RESPONSE_YES)
1379             HobAltButton.style_button(button)
1380             button = dialog.add_button("Stop", gtk.RESPONSE_OK)
1381             HobButton.style_button(button)
1382         response = dialog.run()
1383         dialog.destroy()
1384         if response != gtk.RESPONSE_CANCEL:
1385             self.stopping = True
1386         if response == gtk.RESPONSE_OK:
1387             self.cancel_build_sync()
1388         elif response == gtk.RESPONSE_YES:
1389             self.cancel_build_sync(True)
1390
1391     def do_log(self, consolelogfile = None):
1392         if consolelogfile:
1393             bb.utils.mkdirhier(os.path.dirname(consolelogfile))
1394             if self.consolelog:
1395                 self.logger.removeHandler(self.consolelog)
1396                 self.consolelog = None
1397             self.consolelog = logging.FileHandler(consolelogfile)
1398             bb.msg.addDefaultlogFilter(self.consolelog)
1399             format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
1400             self.consolelog.setFormatter(format)
1401
1402             self.logger.addHandler(self.consolelog)