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