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