Hob: change the implementation way about get config info for building log
[bitbake.git] / lib / bb / ui / crumbs / builder.py
1 #!/usr/bin/env python
2 #
3 # BitBake Graphical GTK User Interface
4 #
5 # Copyright (C) 2011-2012   Intel Corporation
6 #
7 # Authored by Joshua Lock <josh@linux.intel.com>
8 # Authored by Dongxiao Xu <dongxiao.xu@intel.com>
9 # Authored by Shane Wang <shane.wang@intel.com>
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License version 2 as
13 # published by the Free Software Foundation.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along
21 # with this program; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24 import gtk
25 import copy
26 import os
27 import subprocess
28 import shlex
29 from bb.ui.crumbs.template import TemplateMgr
30 from bb.ui.crumbs.imageconfigurationpage import ImageConfigurationPage
31 from bb.ui.crumbs.recipeselectionpage import RecipeSelectionPage
32 from bb.ui.crumbs.packageselectionpage import PackageSelectionPage
33 from bb.ui.crumbs.builddetailspage import BuildDetailsPage
34 from bb.ui.crumbs.imagedetailspage import ImageDetailsPage
35 from bb.ui.crumbs.hobwidget import hwc, HobButton, HobAltButton
36 from bb.ui.crumbs.hig import CrumbsMessageDialog, ImageSelectionDialog, \
37                              AdvancedSettingDialog, LayerSelectionDialog, \
38                              DeployImageDialog
39 from bb.ui.crumbs.persistenttooltip import PersistentTooltip
40
41 class Configuration:
42     '''Represents the data structure of configuration.'''
43
44     def __init__(self, params):
45         # Settings
46         self.curr_mach = ""
47         self.curr_distro = params["distro"]
48         self.dldir = params["dldir"]
49         self.sstatedir = params["sstatedir"]
50         self.sstatemirror = params["sstatemirror"]
51         self.pmake = params["pmake"]
52         self.bbthread = params["bbthread"]
53         self.curr_package_format = " ".join(params["pclass"].split("package_")).strip()
54         self.image_rootfs_size = params["image_rootfs_size"]
55         self.image_extra_size = params["image_extra_size"]
56         self.image_overhead_factor = params['image_overhead_factor']
57         self.incompat_license = params["incompat_license"]
58         self.curr_sdk_machine = params["sdk_machine"]
59         self.conf_version = params["conf_version"]
60         self.lconf_version = params["lconf_version"]
61         self.extra_setting = {}
62         self.toolchain_build = False
63         self.image_fstypes = params["image_fstypes"].split()
64         # bblayers.conf
65         self.layers = params["layer"].split()
66         # image/recipes/packages
67         self.selected_image = None
68         self.selected_recipes = []
69         self.selected_packages = []
70
71     def load(self, template):
72         self.curr_mach = template.getVar("MACHINE")
73         self.curr_package_format = " ".join(template.getVar("PACKAGE_CLASSES").split("package_")).strip()
74         self.curr_distro = template.getVar("DISTRO")
75         self.dldir = template.getVar("DL_DIR")
76         self.sstatedir = template.getVar("SSTATE_DIR")
77         self.sstatemirror = template.getVar("SSTATE_MIRROR")
78         self.pmake = int(template.getVar("PARALLEL_MAKE").split()[1])
79         self.bbthread = int(template.getVar("BB_NUMBER_THREADS"))
80         self.image_rootfs_size = int(template.getVar("IMAGE_ROOTFS_SIZE"))
81         self.image_extra_size = int(template.getVar("IMAGE_EXTRA_SPACE"))
82         # image_overhead_factor is read-only.
83         self.incompat_license = template.getVar("INCOMPATIBLE_LICENSE")
84         self.curr_sdk_machine = template.getVar("SDKMACHINE")
85         self.conf_version = template.getVar("CONF_VERSION")
86         self.lconf_version = template.getVar("LCONF_VERSION")
87         self.extra_setting = eval(template.getVar("EXTRA_SETTING"))
88         self.toolchain_build = eval(template.getVar("TOOLCHAIN_BUILD"))
89         self.image_fstypes = template.getVar("IMAGE_FSTYPES").split()
90         # bblayers.conf
91         self.layers = template.getVar("BBLAYERS").split()
92         # image/recipes/packages
93         self.selected_image = template.getVar("__SELECTED_IMAGE__")
94         self.selected_recipes = template.getVar("DEPENDS").split()
95         self.selected_packages = template.getVar("IMAGE_INSTALL").split()
96
97     def save(self, template, filename):
98         # bblayers.conf
99         template.setVar("BBLAYERS", " ".join(self.layers))
100         # local.conf
101         template.setVar("MACHINE", self.curr_mach)
102         template.setVar("DISTRO", self.curr_distro)
103         template.setVar("DL_DIR", self.dldir)
104         template.setVar("SSTATE_DIR", self.sstatedir)
105         template.setVar("SSTATE_MIRROR", self.sstatemirror)
106         template.setVar("PARALLEL_MAKE", "-j %s" % self.pmake)
107         template.setVar("BB_NUMBER_THREADS", self.bbthread)
108         template.setVar("PACKAGE_CLASSES", " ".join(["package_" + i for i in self.curr_package_format.split()]))
109         template.setVar("IMAGE_ROOTFS_SIZE", self.image_rootfs_size)
110         template.setVar("IMAGE_EXTRA_SPACE", self.image_extra_size)
111         template.setVar("INCOMPATIBLE_LICENSE", self.incompat_license)
112         template.setVar("SDKMACHINE", self.curr_sdk_machine)
113         template.setVar("CONF_VERSION", self.conf_version)
114         template.setVar("LCONF_VERSION", self.lconf_version)
115         template.setVar("EXTRA_SETTING", self.extra_setting)
116         template.setVar("TOOLCHAIN_BUILD", self.toolchain_build)
117         template.setVar("IMAGE_FSTYPES", " ".join(self.image_fstypes).lstrip(" "))
118         # image/recipes/packages
119         self.selected_image = filename
120         template.setVar("__SELECTED_IMAGE__", self.selected_image)
121         template.setVar("DEPENDS", self.selected_recipes)
122         template.setVar("IMAGE_INSTALL", self.selected_packages)
123
124 class Parameters:
125     '''Represents other variables like available machines, etc.'''
126
127     def __init__(self, params):
128         # Variables
129         self.all_machines = []
130         self.all_package_formats = []
131         self.all_distros = []
132         self.all_sdk_machines = []
133         self.max_threads = params["max_threads"]
134         self.all_layers = []
135         self.core_base = params["core_base"]
136         self.image_names = []
137         self.image_addr = params["image_addr"]
138         self.image_types = params["image_types"].split()
139         self.runnable_image_types = params["runnable_image_types"].split()
140         self.runnable_machine_patterns = params["runnable_machine_patterns"].split()
141         self.deployable_image_types = params["deployable_image_types"].split()
142         self.tmpdir = params["tmpdir"]
143         self.distro_version = params["distro_version"]
144         self.target_os = params["target_os"]
145         self.target_arch = params["target_arch"]
146         self.tune_pkgarch = params["tune_pkgarch"]
147         self.bb_version = params["bb_version"]
148         self.tune_arch = params["tune_arch"]
149
150 class Builder(gtk.Window):
151
152     (MACHINE_SELECTION,
153      LAYER_CHANGED,
154      RCPPKGINFO_POPULATING,
155      RCPPKGINFO_POPULATED,
156      BASEIMG_SELECTED,
157      RECIPE_SELECTION,
158      PACKAGE_GENERATING,
159      PACKAGE_GENERATED,
160      PACKAGE_SELECTION,
161      FAST_IMAGE_GENERATING,
162      IMAGE_GENERATING,
163      IMAGE_GENERATED,
164      MY_IMAGE_OPENED,
165      BACK,
166      END_NOOP) = range(15)
167
168     (IMAGE_CONFIGURATION,
169      RECIPE_DETAILS,
170      BUILD_DETAILS,
171      PACKAGE_DETAILS,
172      IMAGE_DETAILS,
173      END_TAB) = range(6)
174
175     __step2page__ = {
176         MACHINE_SELECTION     : IMAGE_CONFIGURATION,
177         LAYER_CHANGED         : IMAGE_CONFIGURATION,
178         RCPPKGINFO_POPULATING : IMAGE_CONFIGURATION,
179         RCPPKGINFO_POPULATED  : IMAGE_CONFIGURATION,
180         BASEIMG_SELECTED      : IMAGE_CONFIGURATION,
181         RECIPE_SELECTION      : RECIPE_DETAILS,
182         PACKAGE_GENERATING    : BUILD_DETAILS,
183         PACKAGE_GENERATED     : PACKAGE_DETAILS,
184         PACKAGE_SELECTION     : PACKAGE_DETAILS,
185         FAST_IMAGE_GENERATING : BUILD_DETAILS,
186         IMAGE_GENERATING      : BUILD_DETAILS,
187         IMAGE_GENERATED       : IMAGE_DETAILS,
188         MY_IMAGE_OPENED       : IMAGE_DETAILS,
189         END_NOOP              : None,
190     }
191
192     def __init__(self, hobHandler, recipe_model, package_model):
193         super(Builder, self).__init__()
194
195         # handler
196         self.handler = hobHandler
197
198         self.template = None
199
200         # build step
201         self.current_step = None
202         self.previous_step = None
203
204         self.stopping = False
205
206         # recipe model and package model
207         self.recipe_model = recipe_model
208         self.package_model = package_model
209
210         # create visual elements
211         self.create_visual_elements()
212
213         # connect the signals to functions
214         self.connect("delete-event", self.destroy_window_cb)
215         self.recipe_model.connect ("recipe-selection-changed",  self.recipelist_changed_cb)
216         self.package_model.connect("package-selection-changed", self.packagelist_changed_cb)
217         self.handler.connect("config-updated",           self.handler_config_updated_cb)
218         self.handler.connect("package-formats-updated",  self.handler_package_formats_updated_cb)
219         self.handler.connect("layers-updated",           self.handler_layers_updated_cb)
220         self.handler.connect("parsing-started",          self.handler_parsing_started_cb)
221         self.handler.connect("parsing",                  self.handler_parsing_cb)
222         self.handler.connect("parsing-completed",        self.handler_parsing_completed_cb)
223         self.handler.build.connect("build-started",      self.handler_build_started_cb)
224         self.handler.build.connect("build-succeeded",    self.handler_build_succeeded_cb)
225         self.handler.build.connect("build-failed",       self.handler_build_failed_cb)
226         self.handler.build.connect("task-started",       self.handler_task_started_cb)
227         self.handler.build.connect("log-error",          self.handler_build_failure_cb)
228         self.handler.connect("generating-data",          self.handler_generating_data_cb)
229         self.handler.connect("data-generated",           self.handler_data_generated_cb)
230         self.handler.connect("command-succeeded",        self.handler_command_succeeded_cb)
231         self.handler.connect("command-failed",           self.handler_command_failed_cb)
232
233         self.handler.init_cooker()
234         self.handler.set_extra_inherit("image_types")
235         self.handler.parse_config()
236
237         self.switch_page(self.MACHINE_SELECTION)
238
239     def create_visual_elements(self):
240         self.set_title("Hob")
241         self.set_icon_name("applications-development")
242         self.set_resizable(True)
243         window_width = self.get_screen().get_width()
244         window_height = self.get_screen().get_height()
245         if window_width >= hwc.MAIN_WIN_WIDTH:
246             window_width = hwc.MAIN_WIN_WIDTH
247             window_height = hwc.MAIN_WIN_HEIGHT
248         self.set_size_request(window_width, window_height)
249
250         self.vbox = gtk.VBox(False, 0)
251         self.vbox.set_border_width(0)
252         self.add(self.vbox)
253
254         # create pages
255         self.image_configuration_page = ImageConfigurationPage(self)
256         self.recipe_details_page      = RecipeSelectionPage(self)
257         self.build_details_page       = BuildDetailsPage(self)
258         self.package_details_page     = PackageSelectionPage(self)
259         self.image_details_page       = ImageDetailsPage(self)
260
261         self.nb = gtk.Notebook()
262         self.nb.set_show_tabs(False)
263         self.nb.insert_page(self.image_configuration_page, None, self.IMAGE_CONFIGURATION)
264         self.nb.insert_page(self.recipe_details_page,      None, self.RECIPE_DETAILS)
265         self.nb.insert_page(self.build_details_page,       None, self.BUILD_DETAILS)
266         self.nb.insert_page(self.package_details_page,     None, self.PACKAGE_DETAILS)
267         self.nb.insert_page(self.image_details_page,       None, self.IMAGE_DETAILS)
268         self.vbox.pack_start(self.nb, expand=True, fill=True)
269
270         self.show_all()
271         self.nb.set_current_page(0)
272
273     def load_template(self, path):
274         self.template = TemplateMgr()
275         self.template.load(path)
276         self.configuration.load(self.template)
277
278         for layer in self.configuration.layers:
279             if not os.path.exists(layer+'/conf/layer.conf'):
280                 return False
281
282         self.switch_page(self.LAYER_CHANGED)
283
284         self.template.destroy()
285         self.template = None
286
287     def save_template(self, path):
288         if path.rfind("/") == -1:
289             filename = "default"
290             path = "."
291         else:
292             filename = path[path.rfind("/") + 1:len(path)]
293             path = path[0:path.rfind("/")]
294
295         self.template = TemplateMgr()
296         self.template.open(filename, path)
297         self.configuration.save(self.template, filename)
298
299         self.template.save()
300         self.template.destroy()
301         self.template = None
302
303     def switch_page(self, next_step):
304         # Main Workflow (Business Logic)
305         self.nb.set_current_page(self.__step2page__[next_step])
306
307         if next_step == self.MACHINE_SELECTION: # init step
308             self.image_configuration_page.show_machine()
309
310         elif next_step == self.LAYER_CHANGED:
311             # after layers is changd by users
312             self.image_configuration_page.show_machine()
313             self.handler.refresh_layers(self.configuration.layers)
314
315         elif next_step == self.RCPPKGINFO_POPULATING:
316             # MACHINE CHANGED action or SETTINGS CHANGED
317             # show the progress bar
318             self.image_configuration_page.show_info_populating()
319             self.generate_recipes()
320
321         elif next_step == self.RCPPKGINFO_POPULATED:
322             self.image_configuration_page.show_info_populated()
323
324         elif next_step == self.BASEIMG_SELECTED:
325             self.image_configuration_page.show_baseimg_selected()
326
327         elif next_step == self.RECIPE_SELECTION:
328             pass
329
330         elif next_step == self.PACKAGE_SELECTION:
331             pass
332
333         elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING:
334             # both PACKAGE_GENEATING and FAST_IMAGE_GENERATING share the same page
335             self.build_details_page.show_page(next_step)
336             self.generate_packages()
337
338         elif next_step == self.PACKAGE_GENERATED:
339             pass
340
341         elif next_step == self.IMAGE_GENERATING:
342             # after packages are generated, selected_packages need to
343             # be updated in package_model per selected_image in recipe_model
344             self.build_details_page.show_page(next_step)
345             self.generate_image()
346
347         elif next_step == self.IMAGE_GENERATED:
348             self.image_details_page.show_page(next_step)
349
350         elif next_step == self.MY_IMAGE_OPENED:
351             self.image_details_page.show_page(next_step)
352
353         self.previous_step = self.current_step
354         self.current_step = next_step
355
356     def set_user_config(self):
357         self.handler.init_cooker()
358         # set bb layers
359         self.handler.set_bblayers(self.configuration.layers)
360         # set local configuration
361         self.handler.set_machine(self.configuration.curr_mach)
362         self.handler.set_package_format(self.configuration.curr_package_format)
363         self.handler.set_distro(self.configuration.curr_distro)
364         self.handler.set_dl_dir(self.configuration.dldir)
365         self.handler.set_sstate_dir(self.configuration.sstatedir)
366         self.handler.set_sstate_mirror(self.configuration.sstatemirror)
367         self.handler.set_pmake(self.configuration.pmake)
368         self.handler.set_bbthreads(self.configuration.bbthread)
369         self.handler.set_rootfs_size(self.configuration.image_rootfs_size)
370         self.handler.set_extra_size(self.configuration.image_extra_size)
371         self.handler.set_incompatible_license(self.configuration.incompat_license)
372         self.handler.set_sdk_machine(self.configuration.curr_sdk_machine)
373         self.handler.set_image_fstypes(self.configuration.image_fstypes)
374         self.handler.set_extra_config(self.configuration.extra_setting)
375         self.handler.set_extra_inherit("packageinfo")
376
377     def update_recipe_model(self, selected_image, selected_recipes):
378         self.recipe_model.set_selected_image(selected_image)
379         self.recipe_model.set_selected_recipes(selected_recipes)
380
381     def update_package_model(self, selected_packages):
382         left = self.package_model.set_selected_packages(selected_packages)
383         self.configuration.selected_packages += left
384
385     def generate_packages(self):
386         # Build packages
387         _, all_recipes = self.recipe_model.get_selected_recipes()
388         self.set_user_config()
389         self.handler.reset_build()
390         self.handler.generate_packages(all_recipes)
391
392     def generate_recipes(self):
393         # Parse recipes
394         self.set_user_config()
395         self.handler.generate_recipes()
396
397     def generate_image(self):
398         # Build image
399         self.set_user_config()
400         all_packages = self.package_model.get_selected_packages()
401         self.handler.reset_build()
402         self.handler.generate_image(all_packages, self.configuration.toolchain_build)
403
404
405     # Callback Functions
406     def handler_config_updated_cb(self, handler, which, values):
407         if which == "distro":
408             self.parameters.all_distros = values
409         elif which == "machine":
410             self.parameters.all_machines = values
411             self.image_configuration_page.update_machine_combo()
412         elif which == "machine-sdk":
413             self.parameters.all_sdk_machines = values
414
415     def handler_package_formats_updated_cb(self, handler, formats):
416         self.parameters.all_package_formats = formats
417
418     def handler_layers_updated_cb(self, handler, layers):
419         self.parameters.all_layers = layers
420
421     def handler_command_succeeded_cb(self, handler, initcmd):
422         if initcmd == self.handler.PARSE_CONFIG:
423             # settings
424             params = self.handler.get_parameters()
425             self.configuration = Configuration(params)
426             self.parameters = Parameters(params)
427             self.handler.generate_configuration()
428         elif initcmd == self.handler.GENERATE_CONFIGURATION:
429             self.image_configuration_page.switch_machine_combo()
430         elif initcmd in [self.handler.GENERATE_RECIPES,
431                          self.handler.GENERATE_PACKAGES,
432                          self.handler.GENERATE_IMAGE]:
433             self.handler.request_package_info_async()
434         elif initcmd == self.handler.POPULATE_PACKAGEINFO:
435             if self.current_step == self.RCPPKGINFO_POPULATING:
436                 self.switch_page(self.RCPPKGINFO_POPULATED)
437                 self.rcppkglist_populated()
438                 return
439
440             self.rcppkglist_populated()
441             if self.current_step == self.FAST_IMAGE_GENERATING:
442                 self.switch_page(self.IMAGE_GENERATING)
443             elif self.current_step == self.PACKAGE_GENERATING:
444                 self.switch_page(self.PACKAGE_GENERATED)
445             elif self.current_step == self.IMAGE_GENERATING:
446                 self.switch_page(self.IMAGE_GENERATED)
447
448     def handler_command_failed_cb(self, handler, msg):
449         if msg:
450             lbl = "<b>Error</b>\n"
451             lbl = lbl + "%s\n\n" % msg
452             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
453             button = dialog.add_button("Close", gtk.RESPONSE_OK)
454             HobButton.style_button(button)
455             response = dialog.run()
456             dialog.destroy()
457         self.handler.clear_busy()
458         self.configuration.curr_mach = None
459         self.image_configuration_page.switch_machine_combo()
460         self.switch_page(self.MACHINE_SELECTION)
461
462     def window_sensitive(self, sensitive):
463         self.image_configuration_page.machine_combo.set_sensitive(sensitive)
464         self.image_configuration_page.image_combo.set_sensitive(sensitive)
465         self.image_configuration_page.layer_button.set_sensitive(sensitive)
466         self.image_configuration_page.layer_info_icon.set_sensitive(sensitive)
467         self.image_configuration_page.toolbar.set_sensitive(sensitive)
468         self.image_configuration_page.view_recipes_button.set_sensitive(sensitive)
469         self.image_configuration_page.view_packages_button.set_sensitive(sensitive)
470         self.image_configuration_page.config_build_button.set_sensitive(sensitive)
471
472         self.recipe_details_page.set_sensitive(sensitive)
473         self.package_details_page.set_sensitive(sensitive)
474         self.build_details_page.set_sensitive(sensitive)
475         self.image_details_page.set_sensitive(sensitive)
476
477         if sensitive:
478             self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
479         else:
480             self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
481
482
483     def handler_generating_data_cb(self, handler):
484         self.window_sensitive(False)
485
486     def handler_data_generated_cb(self, handler):
487         self.window_sensitive(True)
488
489     def rcppkglist_populated(self):
490         selected_image = self.configuration.selected_image
491         selected_recipes = self.configuration.selected_recipes[:]
492         selected_packages = self.configuration.selected_packages[:]
493
494         self.recipe_model.image_list_append(selected_image,
495                                             " ".join(selected_recipes),
496                                             " ".join(selected_packages))
497
498         self.image_configuration_page.update_image_combo(self.recipe_model, selected_image)
499         self.update_recipe_model(selected_image, selected_recipes)
500         self.update_package_model(selected_packages)
501
502     def recipelist_changed_cb(self, recipe_model):
503         self.recipe_details_page.refresh_selection()
504
505     def packagelist_changed_cb(self, package_model):
506         self.package_details_page.refresh_selection()
507
508     def handler_parsing_started_cb(self, handler, message):
509         if self.current_step != self.RCPPKGINFO_POPULATING:
510             return
511
512         fraction = 0
513         if message["eventname"] == "TreeDataPreparationStarted":
514             fraction = 0.6 + fraction
515             self.image_configuration_page.stop_button.set_sensitive(False)
516         else:
517             self.image_configuration_page.stop_button.set_sensitive(True)
518
519         self.image_configuration_page.update_progress_bar(message["title"], fraction)
520
521     def handler_parsing_cb(self, handler, message):
522         if self.current_step != self.RCPPKGINFO_POPULATING:
523             return
524
525         fraction = message["current"] * 1.0/message["total"]
526         if message["eventname"] == "TreeDataPreparationProgress":
527             fraction = 0.6 + 0.4 * fraction
528         else:
529             fraction = 0.6 * fraction
530         self.image_configuration_page.update_progress_bar(message["title"], fraction)
531
532     def handler_parsing_completed_cb(self, handler, message):
533         if self.current_step != self.RCPPKGINFO_POPULATING:
534             return
535
536         if message["eventname"] == "TreeDataPreparationCompleted":
537             fraction = 1.0
538         else:
539             fraction = 0.6
540         self.image_configuration_page.update_progress_bar(message["title"], fraction)
541
542     def handler_build_started_cb(self, running_build):
543         if self.current_step == self.FAST_IMAGE_GENERATING:
544             fraction = 0
545         elif self.current_step == self.IMAGE_GENERATING:
546             if self.previous_step == self.FAST_IMAGE_GENERATING:
547                 fraction = 0.9
548             else:
549                 fraction = 0
550         elif self.current_step == self.PACKAGE_GENERATING:
551             fraction = 0
552         self.build_details_page.update_progress_bar("Build Started: ", fraction)
553         self.build_details_page.reset_build_status()
554         self.build_details_page.reset_issues()
555         self.build_details_page.show_configurations(self.configuration, self.parameters)
556
557     def build_succeeded(self):
558         if self.current_step == self.FAST_IMAGE_GENERATING:
559             fraction = 0.9
560         elif self.current_step == self.IMAGE_GENERATING:
561             fraction = 1.0
562             self.parameters.image_names = []
563             linkname = 'hob-image-' + self.configuration.curr_mach
564             for image_type in self.parameters.image_types:
565                 linkpath = self.parameters.image_addr + '/' + linkname + '.' + image_type
566                 if os.path.exists(linkpath):
567                     self.parameters.image_names.append(os.readlink(linkpath))
568         elif self.current_step == self.PACKAGE_GENERATING:
569             fraction = 1.0
570         self.build_details_page.update_progress_bar("Build Completed: ", fraction)
571         self.stopping = False
572
573     def build_failed(self):
574         if self.current_step == self.FAST_IMAGE_GENERATING:
575             fraction = 0.9
576         elif self.current_step == self.IMAGE_GENERATING:
577             fraction = 1.0
578         elif self.current_step == self.PACKAGE_GENERATING:
579             fraction = 1.0
580         self.build_details_page.update_progress_bar("Build Failed: ", fraction, False)
581         self.build_details_page.show_back_button()
582         self.build_details_page.hide_stop_button()
583         self.handler.build_failed_async()
584         self.stopping = False
585
586     def handler_build_succeeded_cb(self, running_build):
587         if not self.stopping:
588             self.build_succeeded()
589         else:
590             self.build_failed()
591
592
593     def handler_build_failed_cb(self, running_build):
594         self.build_failed()
595
596     def handler_task_started_cb(self, running_build, message): 
597         fraction = message["current"] * 1.0/message["total"]
598         title = "Build packages"
599         if self.current_step == self.FAST_IMAGE_GENERATING:
600             if message["eventname"] == "sceneQueueTaskStarted":
601                 fraction = 0.27 * fraction
602             elif message["eventname"] == "runQueueTaskStarted":
603                 fraction = 0.27 + 0.63 * fraction
604         elif self.current_step == self.IMAGE_GENERATING:
605             title = "Build image"
606             if self.previous_step == self.FAST_IMAGE_GENERATING:
607                 if message["eventname"] == "sceneQueueTaskStarted":
608                     fraction = 0.27 + 0.63 + 0.03 * fraction
609                 elif message["eventname"] == "runQueueTaskStarted":
610                     fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction
611             else:
612                 if message["eventname"] == "sceneQueueTaskStarted":
613                     fraction = 0.2 * fraction
614                 elif message["eventname"] == "runQueueTaskStarted":
615                     fraction = 0.2 + 0.8 * fraction
616         elif self.current_step == self.PACKAGE_GENERATING:
617             if message["eventname"] == "sceneQueueTaskStarted":
618                 fraction = 0.2 * fraction
619             elif message["eventname"] == "runQueueTaskStarted":
620                 fraction = 0.2 + 0.8 * fraction
621         self.build_details_page.update_progress_bar(title + ": ", fraction)
622         self.build_details_page.update_build_status(message["current"], message["total"], message["task"])
623
624     def handler_build_failure_cb(self, running_build):
625         self.build_details_page.show_issues()
626
627     def destroy_window_cb(self, widget, event):
628         lbl = "<b>Do you really want to exit the Hob image creator?</b>"
629         dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
630         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
631         HobAltButton.style_button(button)
632         button = dialog.add_button("Exit Hob", gtk.RESPONSE_YES)
633         HobButton.style_button(button)
634         dialog.set_default_response(gtk.RESPONSE_YES)
635         response = dialog.run()
636         dialog.destroy()
637         if response == gtk.RESPONSE_YES:
638             gtk.main_quit()
639             return False
640         else:
641             return True
642
643     def build_packages(self):
644         _, all_recipes = self.recipe_model.get_selected_recipes()
645         if not all_recipes:
646             lbl = "<b>No selections made</b>\nYou have not made any selections"
647             lbl = lbl + " so there isn't anything to bake at this time."
648             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
649             button = dialog.add_button("Close", gtk.RESPONSE_OK)
650             HobButton.style_button(button)
651             dialog.run()
652             dialog.destroy()
653             return
654         self.switch_page(self.PACKAGE_GENERATING)
655
656     def build_image(self):
657         selected_packages = self.package_model.get_selected_packages()
658         if not selected_packages:      
659             lbl = "<b>No selections made</b>\nYou have not made any selections"
660             lbl = lbl + " so there isn't anything to bake at this time."
661             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
662             button = dialog.add_button("Close", gtk.RESPONSE_OK)
663             HobButton.style_button(button)
664             dialog.run()
665             dialog.destroy()
666             return
667         self.switch_page(self.IMAGE_GENERATING)
668
669     def just_bake(self):
670         selected_image = self.recipe_model.get_selected_image()
671         selected_packages = self.package_model.get_selected_packages() or []
672
673         # If no base image and no selected packages don't build anything
674         if not (selected_packages or selected_image != self.recipe_model.__dummy_image__):
675             lbl = "<b>No selections made</b>\nYou have not made any selections"
676             lbl = lbl + " so there isn't anything to bake at this time."
677             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
678             button = dialog.add_button("Close", gtk.RESPONSE_OK)
679             HobButton.style_button(button)
680             dialog.run()
681             dialog.destroy()
682             return
683
684         self.switch_page(self.FAST_IMAGE_GENERATING)
685
686     def show_binb_dialog(self, binb):
687         markup = "<b>Brought in by:</b>\n%s" % binb
688         ptip = PersistentTooltip(markup)
689
690         ptip.show()
691
692     def show_layer_selection_dialog(self):
693         dialog = LayerSelectionDialog(title = "Layers",
694                      layers = copy.deepcopy(self.configuration.layers),
695                      all_layers = self.parameters.all_layers,
696                      parent = self,
697                      flags = gtk.DIALOG_MODAL
698                          | gtk.DIALOG_DESTROY_WITH_PARENT
699                          | gtk.DIALOG_NO_SEPARATOR)
700         button = dialog.add_button("Close", gtk.RESPONSE_YES)
701         HobButton.style_button(button)
702         response = dialog.run()
703         if response == gtk.RESPONSE_YES:
704             self.configuration.layers = dialog.layers
705             # DO refresh layers
706             if dialog.layers_changed:
707                 self.switch_page(self.LAYER_CHANGED)
708         dialog.destroy()
709
710     def show_load_template_dialog(self):
711         dialog = gtk.FileChooserDialog("Load Template Files", self,
712                                        gtk.FILE_CHOOSER_ACTION_OPEN)
713         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
714         HobAltButton.style_button(button)
715         button = dialog.add_button("Open", gtk.RESPONSE_YES)
716         HobButton.style_button(button)
717         filter = gtk.FileFilter()
718         filter.set_name("Hob Files")
719         filter.add_pattern("*.hob")
720         dialog.add_filter(filter)
721
722         response = dialog.run()
723         if response == gtk.RESPONSE_YES:
724             path = dialog.get_filename()
725             self.load_template(path)
726         dialog.destroy()
727
728     def show_save_template_dialog(self):
729         dialog = gtk.FileChooserDialog("Save Template Files", self,
730                                        gtk.FILE_CHOOSER_ACTION_SAVE)
731         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
732         HobAltButton.style_button(button)
733         button = dialog.add_button("Save", gtk.RESPONSE_YES)
734         HobButton.style_button(button)
735         dialog.set_current_name("hob")
736         response = dialog.run()
737         if response == gtk.RESPONSE_YES:
738             path = dialog.get_filename()
739             self.save_template(path)
740         dialog.destroy()
741
742     def show_load_my_images_dialog(self):
743         dialog = ImageSelectionDialog(self.parameters.image_addr, self.parameters.image_types,
744                                       "Open My Images", self,
745                                        gtk.FILE_CHOOSER_ACTION_SAVE)
746         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
747         HobAltButton.style_button(button)
748         button = dialog.add_button("Open", gtk.RESPONSE_YES)
749         HobButton.style_button(button)
750         response = dialog.run()
751         if response == gtk.RESPONSE_YES:
752             if not dialog.image_names:
753                 lbl = "<b>No selections made</b>\nYou have not made any selections"
754                 crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
755                 button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
756                 HobButton.style_button(button)
757                 crumbs_dialog.run()
758                 crumbs_dialog.destroy()
759                 dialog.destroy()
760                 return
761
762             self.parameters.image_addr = dialog.image_folder
763             self.parameters.image_names = dialog.image_names[:]
764             self.switch_page(self.MY_IMAGE_OPENED)
765
766         dialog.destroy()
767
768     def show_adv_settings_dialog(self):
769         dialog = AdvancedSettingDialog(title = "Settings",
770             configuration = copy.deepcopy(self.configuration),
771             all_image_types = self.parameters.image_types,
772             all_package_formats = self.parameters.all_package_formats,
773             all_distros = self.parameters.all_distros,
774             all_sdk_machines = self.parameters.all_sdk_machines,
775             max_threads = self.parameters.max_threads,
776             parent = self,
777             flags = gtk.DIALOG_MODAL
778                     | gtk.DIALOG_DESTROY_WITH_PARENT
779                     | gtk.DIALOG_NO_SEPARATOR)
780         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
781         HobAltButton.style_button(button)
782         button = dialog.add_button("Save", gtk.RESPONSE_YES)
783         HobButton.style_button(button)
784         response = dialog.run()
785         if response == gtk.RESPONSE_YES:
786             self.configuration = dialog.configuration
787             # DO reparse recipes
788             if dialog.settings_changed:
789                 if self.configuration.curr_mach == "":
790                     self.switch_page(self.MACHINE_SELECTION)
791                 else:
792                     self.switch_page(self.RCPPKGINFO_POPULATING)
793         dialog.destroy()
794
795     def deploy_image(self, image_name):
796         if not image_name:
797             lbl = "<b>Please select an image to deploy.</b>"
798             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
799             button = dialog.add_button("Close", gtk.RESPONSE_OK)
800             HobButton.style_button(button)
801             dialog.run()
802             dialog.destroy()
803             return
804
805         image_path = os.path.join(self.parameters.image_addr, image_name)
806         dialog = DeployImageDialog(title = "Usb Image Maker",
807             image_path = image_path,
808             parent = self,
809             flags = gtk.DIALOG_MODAL
810                     | gtk.DIALOG_DESTROY_WITH_PARENT
811                     | gtk.DIALOG_NO_SEPARATOR)
812         button = dialog.add_button("Close", gtk.RESPONSE_NO)
813         HobAltButton.style_button(button)
814         button = dialog.add_button("Make usb image", gtk.RESPONSE_YES)
815         HobButton.style_button(button)
816         response = dialog.run()
817         dialog.destroy()
818
819     def runqemu_image(self, image_name):
820         if not image_name:
821             lbl = "<b>Please select an image to launch in QEMU.</b>"
822             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
823             button = dialog.add_button("Close", gtk.RESPONSE_OK)
824             HobButton.style_button(button)
825             dialog.run()
826             dialog.destroy()
827             return
828
829         dialog = gtk.FileChooserDialog("Load Kernel Files", self,
830                                        gtk.FILE_CHOOSER_ACTION_SAVE)
831         button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
832         HobAltButton.style_button(button)
833         button = dialog.add_button("Open", gtk.RESPONSE_YES)
834         HobButton.style_button(button)
835         filter = gtk.FileFilter()
836         filter.set_name("Kernel Files")
837         filter.add_pattern("*.bin")
838         dialog.add_filter(filter)
839
840         dialog.set_current_folder(self.parameters.image_addr)
841
842         response = dialog.run()
843         if response == gtk.RESPONSE_YES:
844             kernel_path = dialog.get_filename()
845             image_path = os.path.join(self.parameters.image_addr, image_name)
846         dialog.destroy()
847
848         if response == gtk.RESPONSE_YES:
849             source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env")
850             tmp_path = self.parameters.tmpdir
851             if os.path.exists(image_path) and os.path.exists(kernel_path) \
852                and os.path.exists(source_env_path) and os.path.exists(tmp_path):
853                 cmdline = "/usr/bin/xterm -e "
854                 cmdline += "\" export OE_TMPDIR=" + tmp_path + "; "
855                 cmdline += "source " + source_env_path + " " + os.getcwd() + "; "
856                 cmdline += "runqemu " + kernel_path + " " + image_path + "; bash\""
857                 subprocess.Popen(shlex.split(cmdline))
858             else:
859                 lbl = "<b>Path error</b>\nOne of your paths is wrong,"
860                 lbl = lbl + " please make sure the following paths exist:\n"
861                 lbl = lbl + "image path:" + image_path + "\n"
862                 lbl = lbl + "kernel path:" + kernel_path + "\n"
863                 lbl = lbl + "source environment path:" + source_env_path + "\n"
864                 lbl = lbl + "tmp path: " + tmp_path + "."
865                 dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
866                 button = dialog.add_button("Close", gtk.RESPONSE_OK)
867                 HobButton.style_button(button)
868                 dialog.run()
869                 dialog.destroy()
870
871     def show_packages(self, ask=True):
872         _, selected_recipes = self.recipe_model.get_selected_recipes()
873         if selected_recipes and ask:
874             lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes"
875             lbl = lbl + " to get a full list or just view the existing packages?"
876             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
877             button = dialog.add_button("View packages", gtk.RESPONSE_NO)
878             HobAltButton.style_button(button)
879             button = dialog.add_button("Build packages", gtk.RESPONSE_YES)
880             HobButton.style_button(button)
881             dialog.set_default_response(gtk.RESPONSE_YES)
882             response = dialog.run()
883             dialog.destroy()
884             if response == gtk.RESPONSE_YES:
885                 self.switch_page(self.PACKAGE_GENERATING)
886             else:
887                 self.switch_page(self.PACKAGE_SELECTION)
888         else:
889             self.switch_page(self.PACKAGE_SELECTION)
890
891     def show_recipes(self):
892         self.switch_page(self.RECIPE_SELECTION)
893
894     def initiate_new_build(self):
895         self.configuration.curr_mach = ""
896         self.image_configuration_page.switch_machine_combo()
897         self.switch_page(self.MACHINE_SELECTION)
898
899     def show_configuration(self):
900         self.switch_page(self.BASEIMG_SELECTED)
901
902     def stop_parse(self):
903         self.handler.cancel_parse()
904
905     def stop_build(self):
906         if self.stopping:
907             lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once,"
908             lbl = lbl + " would you like to 'Force Stop' the build?\n\n"
909             lbl = lbl + "This will stop the build as quickly as possible but may"
910             lbl = lbl + " well leave your build directory in an  unusable state"
911             lbl = lbl + " that requires manual steps to fix.\n"
912             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
913             button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
914             HobAltButton.style_button(button)
915             button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
916             HobButton.style_button(button)
917         else:
918             lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this"
919             lbl = lbl + " build?\n\n'Force Stop' will stop the build as quickly as"
920             lbl = lbl + " possible but may well leave your build directory in an"
921             lbl = lbl + " unusable state that requires manual steps to fix.\n\n"
922             lbl = lbl + "'Stop' will stop the build as soon as all in"
923             lbl = lbl + " progress build tasks are finished. However if a"
924             lbl = lbl + " lengthy compilation phase is in progress this may take"
925             lbl = lbl + " some time."
926             dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
927             button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL)
928             HobAltButton.style_button(button)
929             button = dialog.add_button("Stop", gtk.RESPONSE_OK)
930             HobAltButton.style_button(button)
931             button = dialog.add_button("Force Stop", gtk.RESPONSE_YES)
932             HobButton.style_button(button)
933         response = dialog.run()
934         dialog.destroy()
935         if response != gtk.RESPONSE_CANCEL:
936             self.stopping = True
937         if response == gtk.RESPONSE_OK:
938             self.handler.cancel_build()
939         elif response == gtk.RESPONSE_YES:
940             self.handler.cancel_build(True)