3 # BitBake Graphical GTK User Interface
5 # Copyright (C) 2011-2012 Intel Corporation
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>
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.
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.
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.
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, \
39 from bb.ui.crumbs.persistenttooltip import PersistentTooltip
42 '''Represents the data structure of configuration.'''
44 def __init__(self, params):
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"]
65 self.layers = params["layer"].split()
66 # image/recipes/packages
67 self.selected_image = None
68 self.selected_recipes = []
69 self.selected_packages = []
71 self.user_selected_packages = []
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"]
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"]
100 self.layers = params["layer"].split()
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")
110 self.pmake = int(template.getVar("PARALLEL_MAKE").split()[1])
114 self.bbthread = int(template.getVar("BB_NUMBER_THREADS"))
118 self.image_rootfs_size = int(template.getVar("IMAGE_ROOTFS_SIZE"))
122 self.image_extra_size = int(template.getVar("IMAGE_EXTRA_SPACE"))
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")
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()
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")
149 def save(self, template, filename):
151 template.setVar("BBLAYERS", " ".join(self.layers))
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)
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)
186 '''Represents other variables like available machines, etc.'''
188 def __init__(self, params):
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"]
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
212 class Builder(gtk.Window):
216 RCPPKGINFO_POPULATING,
217 RCPPKGINFO_POPULATED,
223 FAST_IMAGE_GENERATING,
228 END_NOOP) = range(15)
230 (IMAGE_CONFIGURATION,
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,
254 def __init__(self, hobHandler, recipe_model, package_model):
255 super(Builder, self).__init__()
257 self.hob_image = "hob-image"
258 self.hob_toolchain = "hob-toolchain"
261 self.handler = hobHandler
266 self.current_step = None
267 self.previous_step = None
269 self.stopping = False
271 # recipe model and package model
272 self.recipe_model = recipe_model
273 self.package_model = package_model
275 # create visual elements
276 self.create_visual_elements()
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)
297 self.handler.init_cooker()
298 self.handler.set_extra_inherit("image_types")
299 self.handler.parse_config()
301 self.switch_page(self.MACHINE_SELECTION)
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)
314 self.vbox = gtk.VBox(False, 0)
315 self.vbox.set_border_width(0)
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)
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)
335 self.nb.set_current_page(0)
337 def load_template(self, path):
338 self.template = TemplateMgr()
339 self.template.load(path)
340 self.configuration.load(self.template)
342 for layer in self.configuration.layers:
343 if not os.path.exists(layer+'/conf/layer.conf'):
346 self.switch_page(self.CONFIG_UPDATED)
348 self.template.destroy()
351 def save_template(self, path):
352 if path.rfind("/") == -1:
356 filename = path[path.rfind("/") + 1:len(path)]
357 path = path[0:path.rfind("/")]
359 self.template = TemplateMgr()
360 self.template.open(filename, path)
361 self.configuration.save(self.template, filename)
364 self.template.destroy()
367 def switch_page(self, next_step):
368 # Main Workflow (Business Logic)
369 self.nb.set_current_page(self.__step2page__[next_step])
371 if next_step == self.MACHINE_SELECTION: # init step
372 self.image_configuration_page.show_machine()
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()
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()
386 elif next_step == self.RCPPKGINFO_POPULATED:
387 self.image_configuration_page.show_info_populated()
389 elif next_step == self.BASEIMG_SELECTED:
390 self.image_configuration_page.show_baseimg_selected()
392 elif next_step == self.RECIPE_SELECTION:
395 elif next_step == self.PACKAGE_SELECTION:
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()
403 elif next_step == self.PACKAGE_GENERATED:
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()
412 elif next_step == self.IMAGE_GENERATED:
413 self.image_details_page.show_page(next_step)
415 elif next_step == self.MY_IMAGE_OPENED:
416 self.image_details_page.show_page(next_step)
418 self.previous_step = self.current_step
419 self.current_step = next_step
421 def set_user_config(self):
422 self.handler.init_cooker()
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")
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)
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)
455 def update_package_model(self, selected_packages):
456 left = self.package_model.set_selected_packages(selected_packages)
457 self.configuration.selected_packages += left
459 def generate_packages(self):
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)
466 def generate_recipes(self):
468 self.set_user_config()
469 self.handler.generate_recipes()
471 def generate_image(self):
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,
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
494 def handler_package_formats_updated_cb(self, handler, formats):
495 self.parameters.all_package_formats = formats
497 def handler_command_succeeded_cb(self, handler, initcmd):
498 if initcmd == self.handler.PARSE_CONFIG:
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()
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)
528 def handler_command_failed_cb(self, handler, 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()
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)
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)
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)
559 self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
561 self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
564 def handler_generating_data_cb(self, handler):
565 self.window_sensitive(False)
567 def handler_data_generated_cb(self, handler):
568 self.window_sensitive(True)
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[:]
575 self.recipe_model.image_list_append(selected_image,
576 " ".join(selected_recipes),
577 " ".join(selected_packages))
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)
583 def recipelist_changed_cb(self, recipe_model):
584 self.recipe_details_page.refresh_selection()
586 def packagelist_changed_cb(self, package_model):
587 self.package_details_page.refresh_selection()
589 def handler_parsing_started_cb(self, handler, message):
590 if self.current_step != self.RCPPKGINFO_POPULATING:
594 if message["eventname"] == "TreeDataPreparationStarted":
595 fraction = 0.6 + fraction
596 self.image_configuration_page.stop_button.set_sensitive(False)
598 self.image_configuration_page.stop_button.set_sensitive(True)
600 self.image_configuration_page.update_progress_bar(message["title"], fraction)
602 def handler_parsing_cb(self, handler, message):
603 if self.current_step != self.RCPPKGINFO_POPULATING:
606 fraction = message["current"] * 1.0/message["total"]
607 if message["eventname"] == "TreeDataPreparationProgress":
608 fraction = 0.6 + 0.4 * fraction
610 fraction = 0.6 * fraction
611 self.image_configuration_page.update_progress_bar(message["title"], fraction)
613 def handler_parsing_completed_cb(self, handler, message):
614 if self.current_step != self.RCPPKGINFO_POPULATING:
617 if message["eventname"] == "TreeDataPreparationCompleted":
621 self.image_configuration_page.update_progress_bar(message["title"], fraction)
623 def handler_build_started_cb(self, running_build):
624 if self.current_step == self.FAST_IMAGE_GENERATING:
626 elif self.current_step == self.IMAGE_GENERATING:
627 if self.previous_step == self.FAST_IMAGE_GENERATING:
631 elif self.current_step == self.PACKAGE_GENERATING:
633 self.build_details_page.update_progress_bar("Build Started: ", fraction)
634 self.build_details_page.show_configurations(self.configuration, self.parameters)
636 def build_succeeded(self):
637 if self.current_step == self.FAST_IMAGE_GENERATING:
639 elif self.current_step == self.IMAGE_GENERATING:
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:
649 self.build_details_page.update_progress_bar("Build Completed: ", fraction)
650 self.handler.build_succeeded_async()
651 self.stopping = False
653 def build_failed(self):
656 message = "Build stopped: "
657 fraction = self.build_details_page.progress_bar.get_fraction()
659 if self.current_step == self.FAST_IMAGE_GENERATING:
661 elif self.current_step == self.IMAGE_GENERATING:
663 elif self.current_step == self.PACKAGE_GENERATING:
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
673 def handler_build_succeeded_cb(self, running_build):
674 if not self.stopping:
675 self.build_succeeded()
680 def handler_build_failed_cb(self, running_build):
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
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"])
711 def handler_build_failure_cb(self, running_build):
712 self.build_details_page.show_issues()
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()
724 if response == gtk.RESPONSE_YES:
730 def build_packages(self):
731 _, all_recipes = self.recipe_model.get_selected_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)
741 self.switch_page(self.PACKAGE_GENERATING)
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)
754 self.switch_page(self.IMAGE_GENERATING)
757 selected_image = self.recipe_model.get_selected_image()
758 selected_packages = self.package_model.get_selected_packages() or []
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)
771 self.switch_page(self.FAST_IMAGE_GENERATING)
773 def show_binb_dialog(self, binb):
774 markup = "<b>Brought in by:</b>\n%s" % binb
775 ptip = PersistentTooltip(markup)
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,
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
795 if dialog.layers_changed:
796 self.switch_page(self.CONFIG_UPDATED)
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)
811 response = dialog.run()
813 if response == gtk.RESPONSE_YES:
814 path = dialog.get_filename()
816 return response == gtk.RESPONSE_YES, path
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)
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)
848 crumbs_dialog.destroy()
852 self.parameters.image_addr = dialog.image_folder
853 self.parameters.image_names = dialog.image_names[:]
854 self.switch_page(self.MY_IMAGE_OPENED)
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,
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
882 return response == gtk.RESPONSE_YES, settings_changed
884 def reparse_post_adv_settings(self):
886 if not self.configuration.curr_mach:
887 self.switch_page(self.CONFIG_UPDATED)
889 self.switch_page(self.RCPPKGINFO_POPULATING)
891 def deploy_image(self, 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)
901 image_path = os.path.join(self.parameters.image_addr, image_name)
902 dialog = DeployImageDialog(title = "Usb Image Maker",
903 image_path = image_path,
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()
915 def runqemu_image(self, 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)
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)
936 dialog.set_current_folder(self.parameters.image_addr)
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)
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))
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)
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()
980 if response == gtk.RESPONSE_YES:
981 self.switch_page(self.PACKAGE_GENERATING)
983 self.switch_page(self.PACKAGE_SELECTION)
985 self.switch_page(self.PACKAGE_SELECTION)
987 def show_recipes(self):
988 self.switch_page(self.RECIPE_SELECTION)
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)
997 def show_configuration(self):
998 self.switch_page(self.BASEIMG_SELECTED)
1000 def stop_parse(self):
1001 self.handler.cancel_parse()
1003 def stop_build(self):
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)
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()
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)