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