class Builder(gtk.Window): (INITIAL_CHECKS, MACHINE_SELECTION, RCPPKGINFO_POPULATING, RCPPKGINFO_POPULATED, BASEIMG_SELECTED, RECIPE_SELECTION, PACKAGE_GENERATING, PACKAGE_GENERATED, PACKAGE_SELECTION, FAST_IMAGE_GENERATING, IMAGE_GENERATING, IMAGE_GENERATED, MY_IMAGE_OPENED, BACK, END_NOOP) = range(15) (SANITY_CHECK, IMAGE_CONFIGURATION, RECIPE_DETAILS, BUILD_DETAILS, PACKAGE_DETAILS, IMAGE_DETAILS, END_TAB) = range(7) __step2page__ = { INITIAL_CHECKS: SANITY_CHECK, MACHINE_SELECTION: IMAGE_CONFIGURATION, RCPPKGINFO_POPULATING: IMAGE_CONFIGURATION, RCPPKGINFO_POPULATED: IMAGE_CONFIGURATION, BASEIMG_SELECTED: IMAGE_CONFIGURATION, RECIPE_SELECTION: RECIPE_DETAILS, PACKAGE_GENERATING: BUILD_DETAILS, PACKAGE_GENERATED: PACKAGE_DETAILS, PACKAGE_SELECTION: PACKAGE_DETAILS, FAST_IMAGE_GENERATING: BUILD_DETAILS, IMAGE_GENERATING: BUILD_DETAILS, IMAGE_GENERATED: IMAGE_DETAILS, MY_IMAGE_OPENED: IMAGE_DETAILS, END_NOOP: None, } SANITY_CHECK_MIN_DISPLAY_TIME = 5 def __init__(self, hobHandler, recipe_model, package_model): super(Builder, self).__init__() self.hob_image = "hob-image" self.hob_toolchain = "hob-toolchain" # handler self.handler = hobHandler # logger self.logger = logging.getLogger("BitBake") self.current_logfile = None # configuration and parameters self.configuration = Configuration() self.parameters = Parameters() # build step self.current_step = None self.previous_step = None self.stopping = False self.ignore_next_completion = False # recipe model and package model self.recipe_model = recipe_model self.package_model = package_model # Indicate whether user has customized the image self.customized = False # Indicate whether the UI is working self.sensitive = True self.request_pkg_info = False # Indicate whether the sanity check ran self.sanity_checked = False # create visual elements self.create_visual_elements() # connect the signals to functions self.connect("delete-event", self.destroy_window_cb) self.recipe_model.connect("recipe-selection-changed", self.recipelist_changed_cb) self.package_model.connect("package-selection-changed", self.packagelist_changed_cb) self.handler.connect("config-updated", self.handler_config_updated_cb) self.handler.connect("package-formats-updated", self.handler_package_formats_updated_cb) self.handler.connect("parsing-started", self.handler_parsing_started_cb) self.handler.connect("parsing", self.handler_parsing_cb) self.handler.connect("parsing-completed", self.handler_parsing_completed_cb) self.handler.build.connect("build-started", self.handler_build_started_cb) self.handler.build.connect("build-succeeded", self.handler_build_succeeded_cb) self.handler.build.connect("build-failed", self.handler_build_failed_cb) self.handler.build.connect("build-aborted", self.handler_build_aborted_cb) self.handler.build.connect("task-started", self.handler_task_started_cb) self.handler.build.connect("disk-full", self.handler_disk_full_cb) self.handler.build.connect("log-error", self.handler_build_failure_cb) self.handler.build.connect("log-warning", self.handler_build_failure_cb) self.handler.build.connect("log", self.handler_build_log_cb) self.handler.build.connect("no-provider", self.handler_no_provider_cb) self.handler.connect("generating-data", self.handler_generating_data_cb) self.handler.connect("data-generated", self.handler_data_generated_cb) self.handler.connect("command-succeeded", self.handler_command_succeeded_cb) self.handler.connect("command-failed", self.handler_command_failed_cb) self.handler.connect("sanity-failed", self.handler_sanity_failed_cb) self.handler.connect("recipe-populated", self.handler_recipe_populated_cb) self.handler.connect("package-populated", self.handler_package_populated_cb) self.initiate_new_build_async() signal.signal(signal.SIGINT, self.event_handle_SIGINT) def create_visual_elements(self): self.set_title("Hob IoT") self.set_icon_name("applications-development") self.set_resizable(True) window_width = 500 window_height = 550 self.set_size_request(window_width, window_height) self.vbox = gtk.VBox(False, 0) self.vbox.set_border_width(0) self.add(self.vbox) # create pages self.image_configuration_page = ImageConfigurationPage(self) self.recipe_details_page = RecipeSelectionPage(self) self.build_details_page = BuildDetailsPage(self) self.package_details_page = PackageSelectionPage(self) self.image_details_page = ImageDetailsPage(self) self.sanity_check_page = SanityCheckPage(self) self.display_sanity_check = False self.sanity_check_post_func = False self.nb = gtk.Notebook() self.nb.set_show_tabs(False) self.nb.insert_page(self.sanity_check_page, None, self.SANITY_CHECK) self.nb.insert_page(self.image_configuration_page, None, self.IMAGE_CONFIGURATION) self.nb.insert_page(self.recipe_details_page, None, self.RECIPE_DETAILS) self.nb.insert_page(self.build_details_page, None, self.BUILD_DETAILS) self.nb.insert_page(self.package_details_page, None, self.PACKAGE_DETAILS) self.nb.insert_page(self.image_details_page, None, self.IMAGE_DETAILS) self.vbox.pack_start(self.nb, expand=True, fill=True) self.show_all() self.nb.set_current_page(0) def sanity_check_timeout(self): # The minimum time for showing the 'sanity check' page has passe # If someone set the 'sanity_check_post_step' meanwhile, execute it now self.display_sanity_check = False if self.sanity_check_post_func: temp = self.sanity_check_post_func self.sanity_check_post_func = None temp() return False def show_sanity_check_page(self): # This window must stay on screen for at least 5 seconds, according to the design document self.nb.set_current_page(self.SANITY_CHECK) self.sanity_check_post_step = None self.display_sanity_check = True self.sanity_check_page.start() gobject.timeout_add(self.SANITY_CHECK_MIN_DISPLAY_TIME * 1000, self.sanity_check_timeout) def execute_after_sanity_check(self, func): if not self.display_sanity_check: func() else: self.sanity_check_post_func = func def generate_configuration(self): if not self.sanity_checked: self.show_sanity_check_page() self.handler.generate_configuration() def initiate_new_build_async(self): self.configuration.selected_image = None self.handler.init_cooker() self.generate_configuration() def sanity_check(self): self.handler.trigger_sanity_check() def populate_recipe_package_info_async(self): self.configuration.curr_mach = self.handler.runCommand( ["getVariable", "MACHINE"]) or "clanton" self.switch_page(self.RCPPKGINFO_POPULATING) # Parse recipes self.set_user_config() self.handler.generate_recipes() def generate_packages_async(self, log=False): self.switch_page(self.PACKAGE_GENERATING) # Build packages _, all_recipes = self.recipe_model.get_selected_recipes() self.set_user_config() self.handler.reset_build() self.handler.generate_packages(all_recipes, self.configuration.default_task) def restore_initial_selected_packages(self): self.package_model.set_selected_packages( self.configuration.initial_user_selected_packages, True) self.package_model.set_selected_packages( self.configuration.initial_selected_packages) for package in self.configuration.selected_packages: if package not in self.configuration.initial_selected_packages: self.package_model.exclude_item( self.package_model.find_path_for_item(package)) def fast_generate_image_async(self, log=False): self.switch_page(self.FAST_IMAGE_GENERATING) # Build packages _, all_recipes = self.recipe_model.get_selected_recipes() self.set_user_config() self.handler.reset_build() self.handler.generate_packages(all_recipes, self.configuration.default_task) def generate_image_async(self, cont=False): self.switch_page(self.IMAGE_GENERATING) self.handler.reset_build() # Build image self.set_user_config() toolchain_packages = [] base_image = None if self.configuration.selected_image == self.recipe_model.__custom_image__: packages = self.package_model.get_selected_packages() image = self.hob_image base_image = self.configuration.initial_selected_image else: packages = [] image = self.configuration.selected_image self.handler.generate_image(image, base_image, packages, self.configuration.toolchain_build, self.configuration.default_task) def generate_new_image(self, image, description): base_image = self.configuration.initial_selected_image if base_image == self.recipe_model.__custom_image__: base_image = None packages = self.package_model.get_selected_packages() self.handler.generate_new_image(image, base_image, packages, description) def ensure_dir(self, directory): self.handler.ensure_dir(directory) def get_parameters_sync(self): return self.handler.get_parameters() def request_package_info_async(self): self.handler.request_package_info() def cancel_build_sync(self, force=False): self.handler.cancel_build(force) def cancel_parse_sync(self): self.handler.cancel_parse() def switch_page(self, next_step): # Main Workflow (Business Logic) self.nb.set_current_page(self.__step2page__[next_step]) if next_step == self.MACHINE_SELECTION: # init step self.image_configuration_page.show_machine() elif next_step == self.RCPPKGINFO_POPULATING: # MACHINE CHANGED action or SETTINGS CHANGED # show the progress bar self.image_configuration_page.show_info_populating() elif next_step == self.RCPPKGINFO_POPULATED: self.image_configuration_page.show_info_populated() elif next_step == self.BASEIMG_SELECTED: self.image_configuration_page.show_baseimg_selected() elif next_step == self.RECIPE_SELECTION: self.recipe_details_page.set_recipe_curr_tab( self.recipe_details_page.INCLUDED) elif next_step == self.PACKAGE_SELECTION: self.configuration.initial_selected_packages = self.configuration.selected_packages self.configuration.initial_user_selected_packages = self.configuration.user_selected_packages self.package_details_page.set_title("Edit packages") self.package_details_page.show_page(self.current_logfile) elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING: # both PACKAGE_GENERATING and FAST_IMAGE_GENERATING share the same page self.build_details_page.show_page(next_step) elif next_step == self.PACKAGE_GENERATED: self.package_details_page.set_title("Step 2 of 2: Edit packages") self.package_details_page.show_page(self.current_logfile) elif next_step == self.IMAGE_GENERATING: # after packages are generated, selected_packages need to # be updated in package_model per selected_image in recipe_model self.build_details_page.show_page(next_step) elif next_step == self.IMAGE_GENERATED: self.image_details_page.show_page(next_step) elif next_step == self.MY_IMAGE_OPENED: self.image_details_page.show_page(next_step) self.previous_step = self.current_step self.current_step = next_step def set_distro_packages(self, distro): ''' Set the DISTRO for the purposes of getting the cached package list''' if distro != self.parameters.distro: self.ignore_next_completion = True self.request_pkg_info = True self.handler.reset_cooker() self.parameters.distro = distro self.handler.set_distro(self.parameters.distro) self.handler.runCommand(["parseConfigurationFiles", "", ""]) self.handler.runCommand( ["triggerEvent", "bb.event.RequestPackageInfo()"]) def set_user_config(self): self.handler.reset_cooker() def update_recipe_model(self, selected_image, selected_recipes): self.recipe_model.set_selected_image(selected_image) self.recipe_model.set_selected_recipes(selected_recipes) def update_package_model(self, selected_packages, user_selected_packages=None): if user_selected_packages: left = self.package_model.set_selected_packages( user_selected_packages, True) self.configuration.user_selected_packages += left left = self.package_model.set_selected_packages(selected_packages) self.configuration.selected_packages += left def update_configuration_parameters(self, params): if params: self.configuration.update(params) self.parameters.update(params) def reset(self): self.configuration.curr_mach = "" self.configuration.clear_selection() self.initiate_new_build_async() # Callback Functions def handler_config_updated_cb(self, handler, which, values): if which == "distro": self.parameters.all_distros = values elif which == "machine": self.parameters.all_machines = values elif which == "machine-sdk": self.parameters.all_sdk_machines = values def handler_package_formats_updated_cb(self, handler, formats): self.parameters.all_package_formats = formats def handler_command_succeeded_cb(self, handler, initcmd): if self.ignore_next_completion: self.ignore_next_completion = False return if initcmd == self.handler.GENERATE_CONFIGURATION: if not self.configuration.curr_mach: self.configuration.curr_mach = self.handler.runCommand( ["getVariable", "HOB_MACHINE"]) or "" self.update_configuration_parameters(self.get_parameters_sync()) if not self.sanity_checked: self.sanity_check() self.sanity_checked = True elif initcmd == self.handler.SANITY_CHECK: self.execute_after_sanity_check( self.populate_recipe_package_info_async) elif initcmd in [ self.handler.GENERATE_RECIPES, self.handler.GENERATE_PACKAGES, self.handler.GENERATE_IMAGE ]: self.update_configuration_parameters(self.get_parameters_sync()) self.request_package_info_async() elif initcmd == self.handler.POPULATE_PACKAGEINFO: if self.current_step == self.RCPPKGINFO_POPULATING: self.switch_page(self.RCPPKGINFO_POPULATED) self.image_configuration_page.update_image_combo( self.configuration.selected_image) #self.rcppkglist_populated() return self.rcppkglist_populated() if self.current_step == self.FAST_IMAGE_GENERATING: self.generate_image_async(True) def show_error_dialog(self, msg): lbl = "<b>Hob found an error</b>\n" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR, msg) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) response = dialog.run() dialog.destroy() def handler_command_failed_cb(self, handler, msg): if msg: self.show_error_dialog(msg) self.reset() def handler_sanity_failed_cb(self, handler, msg): self.reset() msg = msg.replace("your local.conf", "Settings") self.show_error_dialog(msg) self.reset() def window_sensitive(self, sensitive): self.image_configuration_page.image_combo.set_sensitive(sensitive) self.image_configuration_page.image_combo.child.set_sensitive( sensitive) self.image_configuration_page.config_build_button.set_sensitive( sensitive) self.recipe_details_page.set_sensitive(sensitive) self.package_details_page.set_sensitive(sensitive) self.build_details_page.set_sensitive(sensitive) self.image_details_page.set_sensitive(sensitive) if sensitive: self.window.set_cursor(None) else: self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) bb.ui.crumbs.utils.wait(0.1) self.sensitive = sensitive def handler_generating_data_cb(self, handler): self.window_sensitive(False) def handler_data_generated_cb(self, handler): if not self.request_pkg_info: self.window_sensitive(True) def rcppkglist_populated(self): selected_image = self.configuration.selected_image selected_recipes = self.configuration.selected_recipes[:] selected_packages = self.configuration.selected_packages[:] user_selected_packages = self.configuration.user_selected_packages[:] self.update_recipe_model(selected_image, selected_recipes) self.update_package_model(selected_packages, user_selected_packages) self.request_pkg_info = False self.window_sensitive(True) def recipelist_changed_cb(self, recipe_model): self.recipe_details_page.refresh_selection() def packagelist_changed_cb(self, package_model): self.package_details_page.refresh_selection() def handler_recipe_populated_cb(self, handler): self.image_configuration_page.update_progress_bar( "Populating recipes", 0.99) def handler_package_populated_cb(self, handler): self.image_configuration_page.update_progress_bar( "Populating packages", 1.0) def handler_parsing_started_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return fraction = 0 if message["eventname"] == "TreeDataPreparationStarted": fraction = 0.6 + fraction self.image_configuration_page.update_progress_bar( "Generating dependency tree", fraction) else: self.image_configuration_page.update_progress_bar( message["title"], fraction) def handler_parsing_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return fraction = message["current"] * 1.0 / message["total"] if message["eventname"] == "TreeDataPreparationProgress": fraction = 0.6 + 0.38 * fraction self.image_configuration_page.update_progress_bar( "Generating dependency tree", fraction) else: fraction = 0.6 * fraction self.image_configuration_page.update_progress_bar( message["title"], fraction) def handler_parsing_completed_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return if message["eventname"] == "TreeDataPreparationCompleted": fraction = 0.98 else: fraction = 0.6 self.image_configuration_page.update_progress_bar( "Generating dependency tree", fraction) def handler_build_started_cb(self, running_build): if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0 elif self.current_step == self.IMAGE_GENERATING: if self.previous_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 else: fraction = 0 elif self.current_step == self.PACKAGE_GENERATING: fraction = 0 self.build_details_page.update_progress_bar("Build Started: ", fraction) def build_succeeded(self): if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 elif self.current_step == self.IMAGE_GENERATING: fraction = 1.0 version = "" self.parameters.image_names = [] selected_image = self.recipe_model.get_selected_image() if selected_image == self.recipe_model.__custom_image__: if self.configuration.initial_selected_image != selected_image: version = self.recipe_model.get_custom_image_version() linkname = 'hob-image' + version + "-" + self.configuration.curr_mach else: linkname = selected_image + '-' + self.configuration.curr_mach image_extension = self.get_image_extension() self.parameters.image_addr = self.handler.runCommand( ["getVariable", "DEPLOY_DIR_IMAGE"]) or "" for image_type in self.parameters.image_types: if image_type in image_extension: real_types = image_extension[image_type] else: real_types = [image_type] for real_image_type in real_types: linkpath = self.parameters.image_addr + '/' + linkname + '.' + real_image_type if os.path.exists(linkpath): self.parameters.image_names.append( os.readlink(linkpath)) elif self.current_step == self.PACKAGE_GENERATING: fraction = 1.0 self.build_details_page.update_progress_bar("Build Completed: ", fraction) self.handler.build_succeeded_async() self.stopping = False if self.current_step == self.PACKAGE_GENERATING: self.switch_page(self.PACKAGE_GENERATED) elif self.current_step == self.IMAGE_GENERATING: self.switch_page(self.IMAGE_GENERATED) def build_failed(self): if self.stopping: status = "stop" message = "Build stopped: " fraction = self.build_details_page.progress_bar.get_fraction() stop_to_next_edit = "" if self.current_step == self.FAST_IMAGE_GENERATING: stop_to_next_edit = "image configuration" elif self.current_step == self.IMAGE_GENERATING: if self.previous_step == self.FAST_IMAGE_GENERATING: stop_to_next_edit = "image configuration" else: stop_to_next_edit = "packages" elif self.current_step == self.PACKAGE_GENERATING: stop_to_next_edit = "recipes" button = self.build_details_page.show_stop_page( stop_to_next_edit.split(' ')[0]) self.set_default(button) else: fail_to_next_edit = "" if self.current_step == self.FAST_IMAGE_GENERATING: fail_to_next_edit = "image configuration" fraction = 0.9 elif self.current_step == self.IMAGE_GENERATING: if self.previous_step == self.FAST_IMAGE_GENERATING: fail_to_next_edit = "image configuration" else: fail_to_next_edit = "packages" fraction = 1.0 elif self.current_step == self.PACKAGE_GENERATING: fail_to_next_edit = "recipes" fraction = 1.0 self.build_details_page.show_fail_page( fail_to_next_edit.split(' ')[0]) status = "fail" message = "Build failed: " self.build_details_page.update_progress_bar(message, fraction, status) self.build_details_page.show_back_button() self.build_details_page.hide_stop_button() self.handler.build_failed_async() self.stopping = False def handler_build_succeeded_cb(self, running_build): if not self.stopping: self.build_succeeded() else: self.build_failed() def handler_build_failed_cb(self, running_build): self.build_failed() def handler_build_aborted_cb(self, running_build): self.build_failed() def handler_no_provider_cb(self, running_build, msg): dialog = CrumbsMessageDialog(self, glib.markup_escape_text(msg), gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() self.build_failed() def handler_task_started_cb(self, running_build, message): fraction = message["current"] * 1.0 / message["total"] title = "Build packages" if self.current_step == self.FAST_IMAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.27 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.27 + 0.63 * fraction elif self.current_step == self.IMAGE_GENERATING: title = "Build image" if self.previous_step == self.FAST_IMAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.27 + 0.63 + 0.03 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction else: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.2 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.2 + 0.8 * fraction elif self.current_step == self.PACKAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.2 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.2 + 0.8 * fraction self.build_details_page.update_progress_bar(title + ": ", fraction) def handler_disk_full_cb(self, running_build): self.disk_full = True def handler_build_failure_cb(self, running_build): pass def handler_build_log_cb(self, running_build, func, obj): if hasattr(self.logger, func): getattr(self.logger, func)(obj) def destroy_window_cb(self, widget, event): if not self.sensitive: return True elif self.handler.building: self.stop_build() return True else: gtk.main_quit() def event_handle_SIGINT(self, signal, frame): for w in gtk.window_list_toplevels(): if w.get_modal(): w.response(gtk.RESPONSE_DELETE_EVENT) sys.exit(0) def build_packages(self): _, all_recipes = self.recipe_model.get_selected_recipes() if not all_recipes: lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.generate_packages_async(True) def build_image(self): selected_packages = self.package_model.get_selected_packages() if not selected_packages: lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.generate_image_async(True) def just_bake(self): selected_image = self.recipe_model.get_selected_image() selected_packages = self.package_model.get_selected_packages() or [] # If no base image and no selected packages don't build anything if not (selected_packages or selected_image != self.recipe_model.__custom_image__): lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.fast_generate_image_async(True) def show_recipe_property_dialog(self, properties): information = {} dialog = PropertyDialog(title=properties["name"] + ' ' + "properties", parent=self, information=properties, flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) dialog.set_modal(False) button = dialog.add_button("Close", gtk.RESPONSE_NO) HobAltButton.style_button(button) button.connect("clicked", lambda w: dialog.destroy()) dialog.run() def show_packages_property_dialog(self, properties): information = {} dialog = PropertyDialog(title=properties["name"] + ' ' + "properties", parent=self, information=properties, flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) dialog.set_modal(False) button = dialog.add_button("Close", gtk.RESPONSE_NO) HobAltButton.style_button(button) button.connect("clicked", lambda w: dialog.destroy()) dialog.run() def get_image_extension(self): image_extension = {} for type in self.parameters.image_types: ext = self.handler.runCommand( ["getVariable", "IMAGE_EXTENSION_%s" % type]) if ext: image_extension[type] = ext.split(' ') return image_extension def deploy_image(self, image_name): if not image_name: lbl = "<b>Please select an image to deploy.</b>" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return dialog = DeployImageDialog(self, title="Hob IoT", image=image_name, parent=self, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) response = dialog.run() dialog.destroy() def show_load_kernel_dialog(self): dialog = gtk.FileChooserDialog("Load Kernel Files", self, gtk.FILE_CHOOSER_ACTION_SAVE) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Open", gtk.RESPONSE_YES) HobButton.style_button(button) filter = gtk.FileFilter() filter.set_name("Kernel Files") filter.add_pattern("*.bin") dialog.add_filter(filter) dialog.set_current_folder(self.parameters.image_addr) response = dialog.run() kernel_path = "" if response == gtk.RESPONSE_YES: kernel_path = dialog.get_filename() dialog.destroy() return kernel_path def runqemu_image(self, image_name, kernel_name): if not image_name or not kernel_name: lbl = "<b>Please select an %s to launch in QEMU.</b>" % ( "kernel" if image_name else "image") dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return kernel_path = os.path.join(self.parameters.image_addr, kernel_name) image_path = os.path.join(self.parameters.image_addr, image_name) source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env") tmp_path = self.parameters.tmpdir cmdline = bb.ui.crumbs.utils.which_terminal() if os.path.exists(image_path) and os.path.exists(kernel_path) \ and os.path.exists(source_env_path) and os.path.exists(tmp_path) \ and cmdline: cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; " cmdline += "source " + source_env_path + " " + os.getcwd() + "; " cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'" subprocess.Popen(shlex.split(cmdline)) else: lbl = "<b>Path error</b>\nOne of your paths is wrong," lbl = lbl + " please make sure the following paths exist:\n" lbl = lbl + "image path:" + image_path + "\n" lbl = lbl + "kernel path:" + kernel_path + "\n" lbl = lbl + "source environment path:" + source_env_path + "\n" lbl = lbl + "tmp path: " + tmp_path + "." lbl = lbl + "You may be missing either xterm or vte for terminal services." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() def show_packages(self, ask=True): _, selected_recipes = self.recipe_model.get_selected_recipes() if selected_recipes and ask: lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes" lbl = lbl + " to get a full list or just view the existing packages?" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("View packages", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Build packages", gtk.RESPONSE_YES) HobButton.style_button(button) dialog.set_default_response(gtk.RESPONSE_YES) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: self.generate_packages_async(True) else: self.switch_page(self.PACKAGE_SELECTION) else: self.switch_page(self.PACKAGE_SELECTION) def show_recipes(self): self.switch_page(self.RECIPE_SELECTION) def show_image_details(self): self.switch_page(self.IMAGE_GENERATED) def show_configuration(self): self.switch_page(self.BASEIMG_SELECTED) def stop_build(self): if self.stopping: lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once," lbl = lbl + " would you like to 'Force Stop' the build?\n\n" lbl = lbl + "This will stop the build as quickly as possible but may" lbl = lbl + " well leave your build directory in an unusable state" lbl = lbl + " that requires manual steps to fix.\n" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL) HobAltButton.style_button(button) button = dialog.add_button("Force Stop", gtk.RESPONSE_YES) HobButton.style_button(button) else: lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this" lbl = lbl + " build?\n\n'Stop' will stop the build as soon as all in" lbl = lbl + " progress build tasks are finished. However if a" lbl = lbl + " lengthy compilation phase is in progress this may take" lbl = lbl + " some time.\n\n" lbl = lbl + "'Force Stop' will stop the build as quickly as" lbl = lbl + " possible but may well leave your build directory in an" lbl = lbl + " unusable state that requires manual steps to fix." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL) HobAltButton.style_button(button) button = dialog.add_button("Force stop", gtk.RESPONSE_YES) HobAltButton.style_button(button) button = dialog.add_button("Stop", gtk.RESPONSE_OK) HobButton.style_button(button) response = dialog.run() dialog.destroy() if response != gtk.RESPONSE_CANCEL: self.stopping = True if response == gtk.RESPONSE_OK: self.build_details_page.progress_bar.set_stop_title( "Stopping the build....") self.build_details_page.progress_bar.set_rcstyle("stop") self.cancel_build_sync() elif response == gtk.RESPONSE_YES: self.cancel_build_sync(True)
class Builder(gtk.Window): (MACHINE_SELECTION, RCPPKGINFO_POPULATING, RCPPKGINFO_POPULATED, BASEIMG_SELECTED, RECIPE_SELECTION, PACKAGE_GENERATING, PACKAGE_GENERATED, PACKAGE_SELECTION, FAST_IMAGE_GENERATING, IMAGE_GENERATING, IMAGE_GENERATED, MY_IMAGE_OPENED, BACK, END_NOOP) = range(14) (IMAGE_CONFIGURATION, RECIPE_DETAILS, BUILD_DETAILS, PACKAGE_DETAILS, IMAGE_DETAILS, END_TAB) = range(6) __step2page__ = { MACHINE_SELECTION: IMAGE_CONFIGURATION, RCPPKGINFO_POPULATING: IMAGE_CONFIGURATION, RCPPKGINFO_POPULATED: IMAGE_CONFIGURATION, BASEIMG_SELECTED: IMAGE_CONFIGURATION, RECIPE_SELECTION: RECIPE_DETAILS, PACKAGE_GENERATING: BUILD_DETAILS, PACKAGE_GENERATED: PACKAGE_DETAILS, PACKAGE_SELECTION: PACKAGE_DETAILS, FAST_IMAGE_GENERATING: BUILD_DETAILS, IMAGE_GENERATING: BUILD_DETAILS, IMAGE_GENERATED: IMAGE_DETAILS, MY_IMAGE_OPENED: IMAGE_DETAILS, END_NOOP: None, } def __init__(self, hobHandler, recipe_model, package_model): super(Builder, self).__init__() self.hob_image = "hob-image" self.hob_toolchain = "hob-toolchain" # handler self.handler = hobHandler self.template = None # configuration and parameters self.configuration = Configuration() self.parameters = Parameters() # build step self.current_step = None self.previous_step = None self.stopping = False # recipe model and package model self.recipe_model = recipe_model self.package_model = package_model # Indicate whether user has customized the image self.customized = False # Indicate whether the UI is working self.sensitive = True # create visual elements self.create_visual_elements() # connect the signals to functions self.connect("delete-event", self.destroy_window_cb) self.recipe_model.connect("recipe-selection-changed", self.recipelist_changed_cb) self.package_model.connect("package-selection-changed", self.packagelist_changed_cb) self.handler.connect("config-updated", self.handler_config_updated_cb) self.handler.connect("package-formats-updated", self.handler_package_formats_updated_cb) self.handler.connect("parsing-started", self.handler_parsing_started_cb) self.handler.connect("parsing", self.handler_parsing_cb) self.handler.connect("parsing-completed", self.handler_parsing_completed_cb) self.handler.build.connect("build-started", self.handler_build_started_cb) self.handler.build.connect("build-succeeded", self.handler_build_succeeded_cb) self.handler.build.connect("build-failed", self.handler_build_failed_cb) self.handler.build.connect("task-started", self.handler_task_started_cb) self.handler.build.connect("log-error", self.handler_build_failure_cb) self.handler.build.connect("no-provider", self.handler_no_provider_cb) self.handler.connect("generating-data", self.handler_generating_data_cb) self.handler.connect("data-generated", self.handler_data_generated_cb) self.handler.connect("command-succeeded", self.handler_command_succeeded_cb) self.handler.connect("command-failed", self.handler_command_failed_cb) self.handler.set_config_filter(hob_conf_filter) self.initiate_new_build_async() def create_visual_elements(self): self.set_title("Hob") self.set_icon_name("applications-development") self.set_resizable(True) window_width = self.get_screen().get_width() window_height = self.get_screen().get_height() if window_width >= hwc.MAIN_WIN_WIDTH: window_width = hwc.MAIN_WIN_WIDTH window_height = hwc.MAIN_WIN_HEIGHT self.set_size_request(window_width, window_height) self.vbox = gtk.VBox(False, 0) self.vbox.set_border_width(0) self.add(self.vbox) # create pages self.image_configuration_page = ImageConfigurationPage(self) self.recipe_details_page = RecipeSelectionPage(self) self.build_details_page = BuildDetailsPage(self) self.package_details_page = PackageSelectionPage(self) self.image_details_page = ImageDetailsPage(self) self.nb = gtk.Notebook() self.nb.set_show_tabs(False) self.nb.insert_page(self.image_configuration_page, None, self.IMAGE_CONFIGURATION) self.nb.insert_page(self.recipe_details_page, None, self.RECIPE_DETAILS) self.nb.insert_page(self.build_details_page, None, self.BUILD_DETAILS) self.nb.insert_page(self.package_details_page, None, self.PACKAGE_DETAILS) self.nb.insert_page(self.image_details_page, None, self.IMAGE_DETAILS) self.vbox.pack_start(self.nb, expand=True, fill=True) self.show_all() self.nb.set_current_page(0) def initiate_new_build_async(self): self.switch_page(self.MACHINE_SELECTION) if self.load_template( TemplateMgr.convert_to_template_pathfilename( "default", ".hob/")) == None: self.handler.init_cooker() self.handler.set_extra_inherit("image_types") self.handler.generate_configuration() def update_config_async(self): self.switch_page(self.MACHINE_SELECTION) self.set_user_config() self.handler.generate_configuration() def sanity_check(self): self.handler.trigger_sanity_check() def populate_recipe_package_info_async(self): self.switch_page(self.RCPPKGINFO_POPULATING) # Parse recipes self.set_user_config() self.handler.generate_recipes() def generate_packages_async(self): self.switch_page(self.PACKAGE_GENERATING) # Build packages _, all_recipes = self.recipe_model.get_selected_recipes() self.set_user_config() self.handler.reset_build() self.handler.generate_packages(all_recipes, self.configuration.default_task) def fast_generate_image_async(self): self.switch_page(self.FAST_IMAGE_GENERATING) # Build packages _, all_recipes = self.recipe_model.get_selected_recipes() self.set_user_config() self.handler.reset_build() self.handler.generate_packages(all_recipes, self.configuration.default_task) def generate_image_async(self): self.switch_page(self.IMAGE_GENERATING) self.handler.reset_build() # Build image self.set_user_config() toolchain_packages = [] if self.configuration.toolchain_build: toolchain_packages = self.package_model.get_selected_packages_toolchain( ) if self.configuration.selected_image == self.recipe_model.__dummy_image__: packages = self.package_model.get_selected_packages() image = self.hob_image else: packages = [] image = self.configuration.selected_image self.handler.generate_image(image, self.hob_toolchain, packages, toolchain_packages, self.configuration.default_task) def get_parameters_sync(self): return self.handler.get_parameters() def request_package_info_async(self): self.handler.request_package_info() def cancel_build_sync(self, force=False): self.handler.cancel_build(force) def cancel_parse_sync(self): self.handler.cancel_parse() def load_template(self, path): if not os.path.isfile(path): return None self.template = TemplateMgr() try: self.template.load(path) self.configuration.load(self.template) except Exception as e: self.show_error_dialog("Hob Exception - %s" % (str(e))) self.reset() finally: self.template.destroy() self.template = None for layer in self.configuration.layers: if not os.path.exists(layer + '/conf/layer.conf'): return False self.save_defaults() # remember layers and settings self.update_config_async() return True def save_template(self, path, defaults=False): if path.rfind("/") == -1: filename = "default" path = "." else: filename = path[path.rfind("/") + 1:len(path)] path = path[0:path.rfind("/")] self.template = TemplateMgr() try: self.template.open(filename, path) self.configuration.save(self.template, defaults) self.template.save() except Exception as e: self.show_error_dialog("Hob Exception - %s" % (str(e))) self.reset() finally: self.template.destroy() self.template = None def save_defaults(self): if not os.path.exists(".hob/"): os.mkdir(".hob/") self.save_template(".hob/default", True) def switch_page(self, next_step): # Main Workflow (Business Logic) self.nb.set_current_page(self.__step2page__[next_step]) if next_step == self.MACHINE_SELECTION: # init step self.image_configuration_page.show_machine() elif next_step == self.RCPPKGINFO_POPULATING: # MACHINE CHANGED action or SETTINGS CHANGED # show the progress bar self.image_configuration_page.show_info_populating() elif next_step == self.RCPPKGINFO_POPULATED: self.image_configuration_page.show_info_populated() elif next_step == self.BASEIMG_SELECTED: self.image_configuration_page.show_baseimg_selected() elif next_step == self.RECIPE_SELECTION: pass elif next_step == self.PACKAGE_SELECTION: pass elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING: # both PACKAGE_GENEATING and FAST_IMAGE_GENERATING share the same page self.build_details_page.show_page(next_step) elif next_step == self.PACKAGE_GENERATED: pass elif next_step == self.IMAGE_GENERATING: # after packages are generated, selected_packages need to # be updated in package_model per selected_image in recipe_model self.build_details_page.show_page(next_step) elif next_step == self.IMAGE_GENERATED: self.image_details_page.show_page(next_step) elif next_step == self.MY_IMAGE_OPENED: self.image_details_page.show_page(next_step) self.previous_step = self.current_step self.current_step = next_step def set_user_config(self): self.handler.init_cooker() # set bb layers self.handler.set_bblayers(self.configuration.layers) # set local configuration self.handler.set_machine(self.configuration.curr_mach) self.handler.set_package_format(self.configuration.curr_package_format) self.handler.set_distro(self.configuration.curr_distro) self.handler.set_dl_dir(self.configuration.dldir) self.handler.set_sstate_dir(self.configuration.sstatedir) self.handler.set_sstate_mirror(self.configuration.sstatemirror) self.handler.set_pmake(self.configuration.pmake) self.handler.set_bbthreads(self.configuration.bbthread) self.handler.set_rootfs_size(self.configuration.image_rootfs_size) self.handler.set_extra_size(self.configuration.image_extra_size) self.handler.set_incompatible_license( self.configuration.incompat_license) self.handler.set_sdk_machine(self.configuration.curr_sdk_machine) self.handler.set_image_fstypes(self.configuration.image_fstypes) self.handler.set_extra_config(self.configuration.extra_setting) self.handler.set_extra_inherit("packageinfo") self.handler.set_extra_inherit("image_types") # set proxies if self.parameters.enable_proxy: self.handler.set_http_proxy(self.configuration.http_proxy) self.handler.set_https_proxy(self.configuration.https_proxy) self.handler.set_ftp_proxy(self.configuration.ftp_proxy) self.handler.set_all_proxy(self.configuration.all_proxy) self.handler.set_git_proxy(self.configuration.git_proxy_host, self.configuration.git_proxy_port) self.handler.set_cvs_proxy(self.configuration.cvs_proxy_host, self.configuration.cvs_proxy_port) def update_recipe_model(self, selected_image, selected_recipes): self.recipe_model.set_selected_image(selected_image) self.recipe_model.set_selected_recipes(selected_recipes) def update_package_model(self, selected_packages): left = self.package_model.set_selected_packages(selected_packages) self.configuration.selected_packages += left def update_configuration_parameters(self, params): if params: self.configuration.update(params) self.parameters.update(params) def reset(self): self.configuration.curr_mach = "" self.configuration.clear_selection() self.image_configuration_page.switch_machine_combo() self.switch_page(self.MACHINE_SELECTION) # Callback Functions def handler_config_updated_cb(self, handler, which, values): if which == "distro": self.parameters.all_distros = values elif which == "machine": self.parameters.all_machines = values self.image_configuration_page.update_machine_combo() elif which == "machine-sdk": self.parameters.all_sdk_machines = values def handler_package_formats_updated_cb(self, handler, formats): self.parameters.all_package_formats = formats def handler_command_succeeded_cb(self, handler, initcmd): if initcmd == self.handler.GENERATE_CONFIGURATION: self.update_configuration_parameters(self.get_parameters_sync()) self.sanity_check() elif initcmd == self.handler.SANITY_CHECK: self.image_configuration_page.switch_machine_combo() elif initcmd in [ self.handler.GENERATE_RECIPES, self.handler.GENERATE_PACKAGES, self.handler.GENERATE_IMAGE ]: self.update_configuration_parameters(self.get_parameters_sync()) self.request_package_info_async() elif initcmd == self.handler.POPULATE_PACKAGEINFO: if self.current_step == self.RCPPKGINFO_POPULATING: self.switch_page(self.RCPPKGINFO_POPULATED) self.rcppkglist_populated() return self.rcppkglist_populated() if self.current_step == self.FAST_IMAGE_GENERATING: self.generate_image_async() def show_error_dialog(self, msg): lbl = "<b>Error</b>\n" lbl = lbl + "%s\n\n" % msg dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) response = dialog.run() dialog.destroy() def handler_command_failed_cb(self, handler, msg): if msg: msg = msg.replace("your local.conf", "Settings") self.show_error_dialog(msg) self.reset() def window_sensitive(self, sensitive): self.image_configuration_page.machine_combo.set_sensitive(sensitive) self.image_configuration_page.image_combo.set_sensitive(sensitive) self.image_configuration_page.layer_button.set_sensitive(sensitive) self.image_configuration_page.layer_info_icon.set_sensitive(sensitive) self.image_configuration_page.toolbar.set_sensitive(sensitive) self.image_configuration_page.view_recipes_button.set_sensitive( sensitive) self.image_configuration_page.view_packages_button.set_sensitive( sensitive) self.image_configuration_page.config_build_button.set_sensitive( sensitive) self.recipe_details_page.set_sensitive(sensitive) self.package_details_page.set_sensitive(sensitive) self.build_details_page.set_sensitive(sensitive) self.image_details_page.set_sensitive(sensitive) if sensitive: self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) else: self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) self.sensitive = sensitive def handler_generating_data_cb(self, handler): self.window_sensitive(False) def handler_data_generated_cb(self, handler): self.window_sensitive(True) def rcppkglist_populated(self): selected_image = self.configuration.selected_image selected_recipes = self.configuration.selected_recipes[:] selected_packages = self.configuration.selected_packages[:] self.image_configuration_page.update_image_combo( self.recipe_model, selected_image) self.image_configuration_page.update_image_desc(selected_image) self.update_recipe_model(selected_image, selected_recipes) self.update_package_model(selected_packages) def recipelist_changed_cb(self, recipe_model): self.recipe_details_page.refresh_selection() def packagelist_changed_cb(self, package_model): self.package_details_page.refresh_selection() def handler_parsing_started_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return fraction = 0 if message["eventname"] == "TreeDataPreparationStarted": fraction = 0.6 + fraction self.image_configuration_page.stop_button.set_sensitive(False) else: self.image_configuration_page.stop_button.set_sensitive(True) self.image_configuration_page.update_progress_bar( message["title"], fraction) def handler_parsing_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return fraction = message["current"] * 1.0 / message["total"] if message["eventname"] == "TreeDataPreparationProgress": fraction = 0.6 + 0.4 * fraction else: fraction = 0.6 * fraction self.image_configuration_page.update_progress_bar( message["title"], fraction) def handler_parsing_completed_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return if message["eventname"] == "TreeDataPreparationCompleted": fraction = 1.0 else: fraction = 0.6 self.image_configuration_page.update_progress_bar( message["title"], fraction) def handler_build_started_cb(self, running_build): if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0 elif self.current_step == self.IMAGE_GENERATING: if self.previous_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 else: fraction = 0 elif self.current_step == self.PACKAGE_GENERATING: fraction = 0 self.build_details_page.update_progress_bar("Build Started: ", fraction) self.build_details_page.show_configurations(self.configuration, self.parameters) def build_succeeded(self): if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 elif self.current_step == self.IMAGE_GENERATING: fraction = 1.0 self.parameters.image_names = [] selected_image = self.recipe_model.get_selected_image() if selected_image == self.recipe_model.__dummy_image__: linkname = 'hob-image-' + self.configuration.curr_mach else: linkname = selected_image + '-' + self.configuration.curr_mach for image_type in self.parameters.image_types: for real_image_type in hcc.SUPPORTED_IMAGE_TYPES[image_type]: linkpath = self.parameters.image_addr + '/' + linkname + '.' + real_image_type if os.path.exists(linkpath): self.parameters.image_names.append( os.readlink(linkpath)) elif self.current_step == self.PACKAGE_GENERATING: fraction = 1.0 self.build_details_page.update_progress_bar("Build Completed: ", fraction) self.handler.build_succeeded_async() self.stopping = False if self.current_step == self.PACKAGE_GENERATING: self.switch_page(self.PACKAGE_GENERATED) elif self.current_step == self.IMAGE_GENERATING: self.switch_page(self.IMAGE_GENERATED) def build_failed(self): if self.stopping: status = "stop" message = "Build stopped: " fraction = self.build_details_page.progress_bar.get_fraction() else: if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 elif self.current_step == self.IMAGE_GENERATING: fraction = 1.0 elif self.current_step == self.PACKAGE_GENERATING: fraction = 1.0 status = "fail" message = "Build failed: " self.build_details_page.update_progress_bar(message, fraction, status) self.build_details_page.show_back_button() self.build_details_page.hide_stop_button() self.handler.build_failed_async() self.stopping = False def handler_build_succeeded_cb(self, running_build): if not self.stopping: self.build_succeeded() else: self.build_failed() def handler_build_failed_cb(self, running_build): self.build_failed() def handler_no_provider_cb(self, running_build, msg): dialog = CrumbsMessageDialog(self, msg, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() self.build_failed() def handler_task_started_cb(self, running_build, message): fraction = message["current"] * 1.0 / message["total"] title = "Build packages" if self.current_step == self.FAST_IMAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.27 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.27 + 0.63 * fraction elif self.current_step == self.IMAGE_GENERATING: title = "Build image" if self.previous_step == self.FAST_IMAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.27 + 0.63 + 0.03 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction else: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.2 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.2 + 0.8 * fraction elif self.current_step == self.PACKAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.2 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.2 + 0.8 * fraction self.build_details_page.update_progress_bar(title + ": ", fraction) self.build_details_page.update_build_status(message["current"], message["total"], message["task"]) def handler_build_failure_cb(self, running_build): self.build_details_page.show_issues() def destroy_window_cb(self, widget, event): if not self.sensitive: return True lbl = "<b>Do you really want to exit the Hob image creator?</b>" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Exit Hob", gtk.RESPONSE_YES) HobButton.style_button(button) dialog.set_default_response(gtk.RESPONSE_YES) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: gtk.main_quit() return False else: return True def build_packages(self): _, all_recipes = self.recipe_model.get_selected_recipes() if not all_recipes: lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.generate_packages_async() def build_image(self): selected_packages = self.package_model.get_selected_packages() if not selected_packages: lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.generate_image_async() def just_bake(self): selected_image = self.recipe_model.get_selected_image() selected_packages = self.package_model.get_selected_packages() or [] # If no base image and no selected packages don't build anything if not (selected_packages or selected_image != self.recipe_model.__dummy_image__): lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.fast_generate_image_async() def show_binb_dialog(self, binb): markup = "<b>Brought in by:</b>\n%s" % binb ptip = PersistentTooltip(markup, self) ptip.show() def show_layer_selection_dialog(self): dialog = LayerSelectionDialog(title="Layers", layers=copy.deepcopy( self.configuration.layers), all_layers=self.parameters.all_layers, parent=self, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("OK", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() if response == gtk.RESPONSE_YES: self.configuration.layers = dialog.layers self.save_defaults() # remember layers # DO refresh layers if dialog.layers_changed: self.update_config_async() dialog.destroy() def show_load_template_dialog(self): dialog = gtk.FileChooserDialog("Load Template Files", self, gtk.FILE_CHOOSER_ACTION_OPEN) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Open", gtk.RESPONSE_YES) HobButton.style_button(button) filter = gtk.FileFilter() filter.set_name("Hob Files") filter.add_pattern("*.hob") dialog.add_filter(filter) response = dialog.run() path = None if response == gtk.RESPONSE_YES: path = dialog.get_filename() dialog.destroy() return response == gtk.RESPONSE_YES, path def show_save_template_dialog(self): dialog = gtk.FileChooserDialog("Save Template Files", self, gtk.FILE_CHOOSER_ACTION_SAVE) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Save", gtk.RESPONSE_YES) HobButton.style_button(button) dialog.set_current_name("hob") response = dialog.run() if response == gtk.RESPONSE_YES: path = dialog.get_filename() self.save_template(path) dialog.destroy() def show_load_my_images_dialog(self): dialog = ImageSelectionDialog(self.parameters.image_addr, self.parameters.image_types, "Open My Images", self, gtk.FILE_CHOOSER_ACTION_SAVE) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Open", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() if response == gtk.RESPONSE_YES: if not dialog.image_names: lbl = "<b>No selections made</b>\nYou have not made any selections" crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) crumbs_dialog.run() crumbs_dialog.destroy() dialog.destroy() return self.parameters.image_addr = dialog.image_folder self.parameters.image_names = dialog.image_names[:] self.switch_page(self.MY_IMAGE_OPENED) dialog.destroy() def show_adv_settings_dialog(self): dialog = AdvancedSettingDialog( title="Settings", configuration=copy.deepcopy(self.configuration), all_image_types=self.parameters.image_types, all_package_formats=self.parameters.all_package_formats, all_distros=self.parameters.all_distros, all_sdk_machines=self.parameters.all_sdk_machines, max_threads=self.parameters.max_threads, enable_proxy=self.parameters.enable_proxy, parent=self, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Save", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() settings_changed = False if response == gtk.RESPONSE_YES: self.parameters.enable_proxy = dialog.enable_proxy self.configuration = dialog.configuration self.save_defaults() # remember settings settings_changed = dialog.settings_changed dialog.destroy() return response == gtk.RESPONSE_YES, settings_changed def reparse_post_adv_settings(self): if not self.configuration.curr_mach: self.update_config_async() else: self.configuration.clear_selection() # DO reparse recipes self.populate_recipe_package_info_async() def deploy_image(self, image_name): if not image_name: lbl = "<b>Please select an image to deploy.</b>" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return image_path = os.path.join(self.parameters.image_addr, image_name) dialog = DeployImageDialog(title="Usb Image Maker", image_path=image_path, parent=self, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) button = dialog.add_button("Close", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Make usb image", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() dialog.destroy() def runqemu_image(self, image_name): if not image_name: lbl = "<b>Please select an image to launch in QEMU.</b>" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return dialog = gtk.FileChooserDialog("Load Kernel Files", self, gtk.FILE_CHOOSER_ACTION_SAVE) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Open", gtk.RESPONSE_YES) HobButton.style_button(button) filter = gtk.FileFilter() filter.set_name("Kernel Files") filter.add_pattern("*.bin") dialog.add_filter(filter) dialog.set_current_folder(self.parameters.image_addr) response = dialog.run() if response == gtk.RESPONSE_YES: kernel_path = dialog.get_filename() image_path = os.path.join(self.parameters.image_addr, image_name) dialog.destroy() if response == gtk.RESPONSE_YES: source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env") tmp_path = self.parameters.tmpdir cmdline = bb.ui.crumbs.utils.which_terminal() if os.path.exists(image_path) and os.path.exists(kernel_path) \ and os.path.exists(source_env_path) and os.path.exists(tmp_path) \ and cmdline: cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; " cmdline += "source " + source_env_path + " " + os.getcwd( ) + "; " cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'" subprocess.Popen(shlex.split(cmdline)) else: lbl = "<b>Path error</b>\nOne of your paths is wrong," lbl = lbl + " please make sure the following paths exist:\n" lbl = lbl + "image path:" + image_path + "\n" lbl = lbl + "kernel path:" + kernel_path + "\n" lbl = lbl + "source environment path:" + source_env_path + "\n" lbl = lbl + "tmp path: " + tmp_path + "." lbl = lbl + "You may be missing either xterm or vte for terminal services." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() def show_packages(self, ask=True): _, selected_recipes = self.recipe_model.get_selected_recipes() if selected_recipes and ask: lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes" lbl = lbl + " to get a full list or just view the existing packages?" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("View packages", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Build packages", gtk.RESPONSE_YES) HobButton.style_button(button) dialog.set_default_response(gtk.RESPONSE_YES) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: self.generate_packages_async() else: self.switch_page(self.PACKAGE_SELECTION) else: self.switch_page(self.PACKAGE_SELECTION) def show_recipes(self): self.switch_page(self.RECIPE_SELECTION) def show_configuration(self): self.switch_page(self.BASEIMG_SELECTED) def stop_build(self): if self.stopping: lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once," lbl = lbl + " would you like to 'Force Stop' the build?\n\n" lbl = lbl + "This will stop the build as quickly as possible but may" lbl = lbl + " well leave your build directory in an unusable state" lbl = lbl + " that requires manual steps to fix.\n" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL) HobAltButton.style_button(button) button = dialog.add_button("Force Stop", gtk.RESPONSE_YES) HobButton.style_button(button) else: lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this" lbl = lbl + " build?\n\n'Force Stop' will stop the build as quickly as" lbl = lbl + " possible but may well leave your build directory in an" lbl = lbl + " unusable state that requires manual steps to fix.\n\n" lbl = lbl + "'Stop' will stop the build as soon as all in" lbl = lbl + " progress build tasks are finished. However if a" lbl = lbl + " lengthy compilation phase is in progress this may take" lbl = lbl + " some time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL) HobAltButton.style_button(button) button = dialog.add_button("Stop", gtk.RESPONSE_OK) HobAltButton.style_button(button) button = dialog.add_button("Force Stop", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() dialog.destroy() if response != gtk.RESPONSE_CANCEL: self.stopping = True if response == gtk.RESPONSE_OK: self.cancel_build_sync() elif response == gtk.RESPONSE_YES: self.cancel_build_sync(True)
class Builder(gtk.Window): ( MACHINE_SELECTION, RCPPKGINFO_POPULATING, RCPPKGINFO_POPULATED, BASEIMG_SELECTED, RECIPE_SELECTION, PACKAGE_GENERATING, PACKAGE_GENERATED, PACKAGE_SELECTION, FAST_IMAGE_GENERATING, IMAGE_GENERATING, IMAGE_GENERATED, MY_IMAGE_OPENED, BACK, END_NOOP, ) = range(14) (IMAGE_CONFIGURATION, RECIPE_DETAILS, BUILD_DETAILS, PACKAGE_DETAILS, IMAGE_DETAILS, END_TAB) = range(6) __step2page__ = { MACHINE_SELECTION: IMAGE_CONFIGURATION, RCPPKGINFO_POPULATING: IMAGE_CONFIGURATION, RCPPKGINFO_POPULATED: IMAGE_CONFIGURATION, BASEIMG_SELECTED: IMAGE_CONFIGURATION, RECIPE_SELECTION: RECIPE_DETAILS, PACKAGE_GENERATING: BUILD_DETAILS, PACKAGE_GENERATED: PACKAGE_DETAILS, PACKAGE_SELECTION: PACKAGE_DETAILS, FAST_IMAGE_GENERATING: BUILD_DETAILS, IMAGE_GENERATING: BUILD_DETAILS, IMAGE_GENERATED: IMAGE_DETAILS, MY_IMAGE_OPENED: IMAGE_DETAILS, END_NOOP: None, } def __init__(self, hobHandler, recipe_model, package_model): super(Builder, self).__init__() self.hob_image = "hob-image" self.hob_toolchain = "hob-toolchain" # handler self.handler = hobHandler self.template = None # configuration and parameters self.configuration = Configuration() self.parameters = Parameters() # build step self.current_step = None self.previous_step = None self.stopping = False # recipe model and package model self.recipe_model = recipe_model self.package_model = package_model # Indicate whether user has customized the image self.customized = False # Indicate whether the UI is working self.sensitive = True # create visual elements self.create_visual_elements() # connect the signals to functions self.connect("delete-event", self.destroy_window_cb) self.recipe_model.connect("recipe-selection-changed", self.recipelist_changed_cb) self.package_model.connect("package-selection-changed", self.packagelist_changed_cb) self.handler.connect("config-updated", self.handler_config_updated_cb) self.handler.connect("package-formats-updated", self.handler_package_formats_updated_cb) self.handler.connect("parsing-started", self.handler_parsing_started_cb) self.handler.connect("parsing", self.handler_parsing_cb) self.handler.connect("parsing-completed", self.handler_parsing_completed_cb) self.handler.build.connect("build-started", self.handler_build_started_cb) self.handler.build.connect("build-succeeded", self.handler_build_succeeded_cb) self.handler.build.connect("build-failed", self.handler_build_failed_cb) self.handler.build.connect("task-started", self.handler_task_started_cb) self.handler.build.connect("log-error", self.handler_build_failure_cb) self.handler.build.connect("no-provider", self.handler_no_provider_cb) self.handler.connect("generating-data", self.handler_generating_data_cb) self.handler.connect("data-generated", self.handler_data_generated_cb) self.handler.connect("command-succeeded", self.handler_command_succeeded_cb) self.handler.connect("command-failed", self.handler_command_failed_cb) self.handler.set_config_filter(hob_conf_filter) self.initiate_new_build_async() def create_visual_elements(self): self.set_title("Hob") self.set_icon_name("applications-development") self.set_resizable(True) window_width = self.get_screen().get_width() window_height = self.get_screen().get_height() if window_width >= hwc.MAIN_WIN_WIDTH: window_width = hwc.MAIN_WIN_WIDTH window_height = hwc.MAIN_WIN_HEIGHT self.set_size_request(window_width, window_height) self.vbox = gtk.VBox(False, 0) self.vbox.set_border_width(0) self.add(self.vbox) # create pages self.image_configuration_page = ImageConfigurationPage(self) self.recipe_details_page = RecipeSelectionPage(self) self.build_details_page = BuildDetailsPage(self) self.package_details_page = PackageSelectionPage(self) self.image_details_page = ImageDetailsPage(self) self.nb = gtk.Notebook() self.nb.set_show_tabs(False) self.nb.insert_page(self.image_configuration_page, None, self.IMAGE_CONFIGURATION) self.nb.insert_page(self.recipe_details_page, None, self.RECIPE_DETAILS) self.nb.insert_page(self.build_details_page, None, self.BUILD_DETAILS) self.nb.insert_page(self.package_details_page, None, self.PACKAGE_DETAILS) self.nb.insert_page(self.image_details_page, None, self.IMAGE_DETAILS) self.vbox.pack_start(self.nb, expand=True, fill=True) self.show_all() self.nb.set_current_page(0) def initiate_new_build_async(self): self.switch_page(self.MACHINE_SELECTION) if self.load_template(TemplateMgr.convert_to_template_pathfilename("default", ".hob/")) == None: self.handler.init_cooker() self.handler.set_extra_inherit("image_types") self.handler.generate_configuration() def update_config_async(self): self.switch_page(self.MACHINE_SELECTION) self.set_user_config() self.handler.generate_configuration() def sanity_check(self): self.handler.trigger_sanity_check() def populate_recipe_package_info_async(self): self.switch_page(self.RCPPKGINFO_POPULATING) # Parse recipes self.set_user_config() self.handler.generate_recipes() def generate_packages_async(self): self.switch_page(self.PACKAGE_GENERATING) # Build packages _, all_recipes = self.recipe_model.get_selected_recipes() self.set_user_config() self.handler.reset_build() self.handler.generate_packages(all_recipes, self.configuration.default_task) def fast_generate_image_async(self): self.switch_page(self.FAST_IMAGE_GENERATING) # Build packages _, all_recipes = self.recipe_model.get_selected_recipes() self.set_user_config() self.handler.reset_build() self.handler.generate_packages(all_recipes, self.configuration.default_task) def generate_image_async(self): self.switch_page(self.IMAGE_GENERATING) self.handler.reset_build() # Build image self.set_user_config() toolchain_packages = [] if self.configuration.toolchain_build: toolchain_packages = self.package_model.get_selected_packages_toolchain() if self.configuration.selected_image == self.recipe_model.__dummy_image__: packages = self.package_model.get_selected_packages() image = self.hob_image else: packages = [] image = self.configuration.selected_image self.handler.generate_image( image, self.hob_toolchain, packages, toolchain_packages, self.configuration.default_task ) def get_parameters_sync(self): return self.handler.get_parameters() def request_package_info_async(self): self.handler.request_package_info() def cancel_build_sync(self, force=False): self.handler.cancel_build(force) def cancel_parse_sync(self): self.handler.cancel_parse() def load_template(self, path): if not os.path.isfile(path): return None self.template = TemplateMgr() try: self.template.load(path) self.configuration.load(self.template) except Exception as e: self.show_error_dialog("Hob Exception - %s" % (str(e))) self.reset() finally: self.template.destroy() self.template = None for layer in self.configuration.layers: if not os.path.exists(layer + "/conf/layer.conf"): return False self.save_defaults() # remember layers and settings self.update_config_async() return True def save_template(self, path, defaults=False): if path.rfind("/") == -1: filename = "default" path = "." else: filename = path[path.rfind("/") + 1 : len(path)] path = path[0 : path.rfind("/")] self.template = TemplateMgr() try: self.template.open(filename, path) self.configuration.save(self.template, defaults) self.template.save() except Exception as e: self.show_error_dialog("Hob Exception - %s" % (str(e))) self.reset() finally: self.template.destroy() self.template = None def save_defaults(self): if not os.path.exists(".hob/"): os.mkdir(".hob/") self.save_template(".hob/default", True) def switch_page(self, next_step): # Main Workflow (Business Logic) self.nb.set_current_page(self.__step2page__[next_step]) if next_step == self.MACHINE_SELECTION: # init step self.image_configuration_page.show_machine() elif next_step == self.RCPPKGINFO_POPULATING: # MACHINE CHANGED action or SETTINGS CHANGED # show the progress bar self.image_configuration_page.show_info_populating() elif next_step == self.RCPPKGINFO_POPULATED: self.image_configuration_page.show_info_populated() elif next_step == self.BASEIMG_SELECTED: self.image_configuration_page.show_baseimg_selected() elif next_step == self.RECIPE_SELECTION: pass elif next_step == self.PACKAGE_SELECTION: pass elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING: # both PACKAGE_GENEATING and FAST_IMAGE_GENERATING share the same page self.build_details_page.show_page(next_step) elif next_step == self.PACKAGE_GENERATED: pass elif next_step == self.IMAGE_GENERATING: # after packages are generated, selected_packages need to # be updated in package_model per selected_image in recipe_model self.build_details_page.show_page(next_step) elif next_step == self.IMAGE_GENERATED: self.image_details_page.show_page(next_step) elif next_step == self.MY_IMAGE_OPENED: self.image_details_page.show_page(next_step) self.previous_step = self.current_step self.current_step = next_step def set_user_config(self): self.handler.init_cooker() # set bb layers self.handler.set_bblayers(self.configuration.layers) # set local configuration self.handler.set_machine(self.configuration.curr_mach) self.handler.set_package_format(self.configuration.curr_package_format) self.handler.set_distro(self.configuration.curr_distro) self.handler.set_dl_dir(self.configuration.dldir) self.handler.set_sstate_dir(self.configuration.sstatedir) self.handler.set_sstate_mirror(self.configuration.sstatemirror) self.handler.set_pmake(self.configuration.pmake) self.handler.set_bbthreads(self.configuration.bbthread) self.handler.set_rootfs_size(self.configuration.image_rootfs_size) self.handler.set_extra_size(self.configuration.image_extra_size) self.handler.set_incompatible_license(self.configuration.incompat_license) self.handler.set_sdk_machine(self.configuration.curr_sdk_machine) self.handler.set_image_fstypes(self.configuration.image_fstypes) self.handler.set_extra_config(self.configuration.extra_setting) self.handler.set_extra_inherit("packageinfo") self.handler.set_extra_inherit("image_types") # set proxies if self.parameters.enable_proxy: self.handler.set_http_proxy(self.configuration.http_proxy) self.handler.set_https_proxy(self.configuration.https_proxy) self.handler.set_ftp_proxy(self.configuration.ftp_proxy) self.handler.set_all_proxy(self.configuration.all_proxy) self.handler.set_git_proxy(self.configuration.git_proxy_host, self.configuration.git_proxy_port) self.handler.set_cvs_proxy(self.configuration.cvs_proxy_host, self.configuration.cvs_proxy_port) def update_recipe_model(self, selected_image, selected_recipes): self.recipe_model.set_selected_image(selected_image) self.recipe_model.set_selected_recipes(selected_recipes) def update_package_model(self, selected_packages): left = self.package_model.set_selected_packages(selected_packages) self.configuration.selected_packages += left def update_configuration_parameters(self, params): if params: self.configuration.update(params) self.parameters.update(params) def reset(self): self.configuration.curr_mach = "" self.configuration.clear_selection() self.image_configuration_page.switch_machine_combo() self.switch_page(self.MACHINE_SELECTION) # Callback Functions def handler_config_updated_cb(self, handler, which, values): if which == "distro": self.parameters.all_distros = values elif which == "machine": self.parameters.all_machines = values self.image_configuration_page.update_machine_combo() elif which == "machine-sdk": self.parameters.all_sdk_machines = values def handler_package_formats_updated_cb(self, handler, formats): self.parameters.all_package_formats = formats def handler_command_succeeded_cb(self, handler, initcmd): if initcmd == self.handler.GENERATE_CONFIGURATION: self.update_configuration_parameters(self.get_parameters_sync()) self.sanity_check() elif initcmd == self.handler.SANITY_CHECK: self.image_configuration_page.switch_machine_combo() elif initcmd in [self.handler.GENERATE_RECIPES, self.handler.GENERATE_PACKAGES, self.handler.GENERATE_IMAGE]: self.update_configuration_parameters(self.get_parameters_sync()) self.request_package_info_async() elif initcmd == self.handler.POPULATE_PACKAGEINFO: if self.current_step == self.RCPPKGINFO_POPULATING: self.switch_page(self.RCPPKGINFO_POPULATED) self.rcppkglist_populated() return self.rcppkglist_populated() if self.current_step == self.FAST_IMAGE_GENERATING: self.generate_image_async() def show_error_dialog(self, msg): lbl = "<b>Error</b>\n" lbl = lbl + "%s\n\n" % msg dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) response = dialog.run() dialog.destroy() def handler_command_failed_cb(self, handler, msg): if msg: msg = msg.replace("your local.conf", "Settings") self.show_error_dialog(msg) self.reset() def window_sensitive(self, sensitive): self.image_configuration_page.machine_combo.set_sensitive(sensitive) self.image_configuration_page.image_combo.set_sensitive(sensitive) self.image_configuration_page.layer_button.set_sensitive(sensitive) self.image_configuration_page.layer_info_icon.set_sensitive(sensitive) self.image_configuration_page.toolbar.set_sensitive(sensitive) self.image_configuration_page.view_recipes_button.set_sensitive(sensitive) self.image_configuration_page.view_packages_button.set_sensitive(sensitive) self.image_configuration_page.config_build_button.set_sensitive(sensitive) self.recipe_details_page.set_sensitive(sensitive) self.package_details_page.set_sensitive(sensitive) self.build_details_page.set_sensitive(sensitive) self.image_details_page.set_sensitive(sensitive) if sensitive: self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) else: self.get_root_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) self.sensitive = sensitive def handler_generating_data_cb(self, handler): self.window_sensitive(False) def handler_data_generated_cb(self, handler): self.window_sensitive(True) def rcppkglist_populated(self): selected_image = self.configuration.selected_image selected_recipes = self.configuration.selected_recipes[:] selected_packages = self.configuration.selected_packages[:] self.image_configuration_page.update_image_combo(self.recipe_model, selected_image) self.image_configuration_page.update_image_desc(selected_image) self.update_recipe_model(selected_image, selected_recipes) self.update_package_model(selected_packages) def recipelist_changed_cb(self, recipe_model): self.recipe_details_page.refresh_selection() def packagelist_changed_cb(self, package_model): self.package_details_page.refresh_selection() def handler_parsing_started_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return fraction = 0 if message["eventname"] == "TreeDataPreparationStarted": fraction = 0.6 + fraction self.image_configuration_page.stop_button.set_sensitive(False) else: self.image_configuration_page.stop_button.set_sensitive(True) self.image_configuration_page.update_progress_bar(message["title"], fraction) def handler_parsing_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return fraction = message["current"] * 1.0 / message["total"] if message["eventname"] == "TreeDataPreparationProgress": fraction = 0.6 + 0.4 * fraction else: fraction = 0.6 * fraction self.image_configuration_page.update_progress_bar(message["title"], fraction) def handler_parsing_completed_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return if message["eventname"] == "TreeDataPreparationCompleted": fraction = 1.0 else: fraction = 0.6 self.image_configuration_page.update_progress_bar(message["title"], fraction) def handler_build_started_cb(self, running_build): if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0 elif self.current_step == self.IMAGE_GENERATING: if self.previous_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 else: fraction = 0 elif self.current_step == self.PACKAGE_GENERATING: fraction = 0 self.build_details_page.update_progress_bar("Build Started: ", fraction) self.build_details_page.show_configurations(self.configuration, self.parameters) def build_succeeded(self): if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 elif self.current_step == self.IMAGE_GENERATING: fraction = 1.0 self.parameters.image_names = [] selected_image = self.recipe_model.get_selected_image() if selected_image == self.recipe_model.__dummy_image__: linkname = "hob-image-" + self.configuration.curr_mach else: linkname = selected_image + "-" + self.configuration.curr_mach for image_type in self.parameters.image_types: for real_image_type in hcc.SUPPORTED_IMAGE_TYPES[image_type]: linkpath = self.parameters.image_addr + "/" + linkname + "." + real_image_type if os.path.exists(linkpath): self.parameters.image_names.append(os.readlink(linkpath)) elif self.current_step == self.PACKAGE_GENERATING: fraction = 1.0 self.build_details_page.update_progress_bar("Build Completed: ", fraction) self.handler.build_succeeded_async() self.stopping = False if self.current_step == self.PACKAGE_GENERATING: self.switch_page(self.PACKAGE_GENERATED) elif self.current_step == self.IMAGE_GENERATING: self.switch_page(self.IMAGE_GENERATED) def build_failed(self): if self.stopping: status = "stop" message = "Build stopped: " fraction = self.build_details_page.progress_bar.get_fraction() else: if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 elif self.current_step == self.IMAGE_GENERATING: fraction = 1.0 elif self.current_step == self.PACKAGE_GENERATING: fraction = 1.0 status = "fail" message = "Build failed: " self.build_details_page.update_progress_bar(message, fraction, status) self.build_details_page.show_back_button() self.build_details_page.hide_stop_button() self.handler.build_failed_async() self.stopping = False def handler_build_succeeded_cb(self, running_build): if not self.stopping: self.build_succeeded() else: self.build_failed() def handler_build_failed_cb(self, running_build): self.build_failed() def handler_no_provider_cb(self, running_build, msg): dialog = CrumbsMessageDialog(self, msg, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() self.build_failed() def handler_task_started_cb(self, running_build, message): fraction = message["current"] * 1.0 / message["total"] title = "Build packages" if self.current_step == self.FAST_IMAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.27 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.27 + 0.63 * fraction elif self.current_step == self.IMAGE_GENERATING: title = "Build image" if self.previous_step == self.FAST_IMAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.27 + 0.63 + 0.03 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction else: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.2 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.2 + 0.8 * fraction elif self.current_step == self.PACKAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.2 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.2 + 0.8 * fraction self.build_details_page.update_progress_bar(title + ": ", fraction) self.build_details_page.update_build_status(message["current"], message["total"], message["task"]) def handler_build_failure_cb(self, running_build): self.build_details_page.show_issues() def destroy_window_cb(self, widget, event): if not self.sensitive: return True lbl = "<b>Do you really want to exit the Hob image creator?</b>" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Exit Hob", gtk.RESPONSE_YES) HobButton.style_button(button) dialog.set_default_response(gtk.RESPONSE_YES) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: gtk.main_quit() return False else: return True def build_packages(self): _, all_recipes = self.recipe_model.get_selected_recipes() if not all_recipes: lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.generate_packages_async() def build_image(self): selected_packages = self.package_model.get_selected_packages() if not selected_packages: lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.generate_image_async() def just_bake(self): selected_image = self.recipe_model.get_selected_image() selected_packages = self.package_model.get_selected_packages() or [] # If no base image and no selected packages don't build anything if not (selected_packages or selected_image != self.recipe_model.__dummy_image__): lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.fast_generate_image_async() def show_binb_dialog(self, binb): markup = "<b>Brought in by:</b>\n%s" % binb ptip = PersistentTooltip(markup, self) ptip.show() def show_layer_selection_dialog(self): dialog = LayerSelectionDialog( title="Layers", layers=copy.deepcopy(self.configuration.layers), all_layers=self.parameters.all_layers, parent=self, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, ) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("OK", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() if response == gtk.RESPONSE_YES: self.configuration.layers = dialog.layers self.save_defaults() # remember layers # DO refresh layers if dialog.layers_changed: self.update_config_async() dialog.destroy() def show_load_template_dialog(self): dialog = gtk.FileChooserDialog("Load Template Files", self, gtk.FILE_CHOOSER_ACTION_OPEN) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Open", gtk.RESPONSE_YES) HobButton.style_button(button) filter = gtk.FileFilter() filter.set_name("Hob Files") filter.add_pattern("*.hob") dialog.add_filter(filter) response = dialog.run() path = None if response == gtk.RESPONSE_YES: path = dialog.get_filename() dialog.destroy() return response == gtk.RESPONSE_YES, path def show_save_template_dialog(self): dialog = gtk.FileChooserDialog("Save Template Files", self, gtk.FILE_CHOOSER_ACTION_SAVE) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Save", gtk.RESPONSE_YES) HobButton.style_button(button) dialog.set_current_name("hob") response = dialog.run() if response == gtk.RESPONSE_YES: path = dialog.get_filename() self.save_template(path) dialog.destroy() def show_load_my_images_dialog(self): dialog = ImageSelectionDialog( self.parameters.image_addr, self.parameters.image_types, "Open My Images", self, gtk.FILE_CHOOSER_ACTION_SAVE, ) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Open", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() if response == gtk.RESPONSE_YES: if not dialog.image_names: lbl = "<b>No selections made</b>\nYou have not made any selections" crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) crumbs_dialog.run() crumbs_dialog.destroy() dialog.destroy() return self.parameters.image_addr = dialog.image_folder self.parameters.image_names = dialog.image_names[:] self.switch_page(self.MY_IMAGE_OPENED) dialog.destroy() def show_adv_settings_dialog(self): dialog = AdvancedSettingDialog( title="Settings", configuration=copy.deepcopy(self.configuration), all_image_types=self.parameters.image_types, all_package_formats=self.parameters.all_package_formats, all_distros=self.parameters.all_distros, all_sdk_machines=self.parameters.all_sdk_machines, max_threads=self.parameters.max_threads, enable_proxy=self.parameters.enable_proxy, parent=self, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, ) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Save", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() settings_changed = False if response == gtk.RESPONSE_YES: self.parameters.enable_proxy = dialog.enable_proxy self.configuration = dialog.configuration self.save_defaults() # remember settings settings_changed = dialog.settings_changed dialog.destroy() return response == gtk.RESPONSE_YES, settings_changed def reparse_post_adv_settings(self): if not self.configuration.curr_mach: self.update_config_async() else: self.configuration.clear_selection() # DO reparse recipes self.populate_recipe_package_info_async() def deploy_image(self, image_name): if not image_name: lbl = "<b>Please select an image to deploy.</b>" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return image_path = os.path.join(self.parameters.image_addr, image_name) dialog = DeployImageDialog( title="Usb Image Maker", image_path=image_path, parent=self, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, ) button = dialog.add_button("Close", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Make usb image", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() dialog.destroy() def runqemu_image(self, image_name): if not image_name: lbl = "<b>Please select an image to launch in QEMU.</b>" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return dialog = gtk.FileChooserDialog("Load Kernel Files", self, gtk.FILE_CHOOSER_ACTION_SAVE) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Open", gtk.RESPONSE_YES) HobButton.style_button(button) filter = gtk.FileFilter() filter.set_name("Kernel Files") filter.add_pattern("*.bin") dialog.add_filter(filter) dialog.set_current_folder(self.parameters.image_addr) response = dialog.run() if response == gtk.RESPONSE_YES: kernel_path = dialog.get_filename() image_path = os.path.join(self.parameters.image_addr, image_name) dialog.destroy() if response == gtk.RESPONSE_YES: source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env") tmp_path = self.parameters.tmpdir cmdline = bb.ui.crumbs.utils.which_terminal() if ( os.path.exists(image_path) and os.path.exists(kernel_path) and os.path.exists(source_env_path) and os.path.exists(tmp_path) and cmdline ): cmdline += "' bash -c \"export OE_TMPDIR=" + tmp_path + "; " cmdline += "source " + source_env_path + " " + os.getcwd() + "; " cmdline += "runqemu " + kernel_path + " " + image_path + "\"'" subprocess.Popen(shlex.split(cmdline)) else: lbl = "<b>Path error</b>\nOne of your paths is wrong," lbl = lbl + " please make sure the following paths exist:\n" lbl = lbl + "image path:" + image_path + "\n" lbl = lbl + "kernel path:" + kernel_path + "\n" lbl = lbl + "source environment path:" + source_env_path + "\n" lbl = lbl + "tmp path: " + tmp_path + "." lbl = lbl + "You may be missing either xterm or vte for terminal services." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() def show_packages(self, ask=True): _, selected_recipes = self.recipe_model.get_selected_recipes() if selected_recipes and ask: lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes" lbl = lbl + " to get a full list or just view the existing packages?" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("View packages", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Build packages", gtk.RESPONSE_YES) HobButton.style_button(button) dialog.set_default_response(gtk.RESPONSE_YES) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: self.generate_packages_async() else: self.switch_page(self.PACKAGE_SELECTION) else: self.switch_page(self.PACKAGE_SELECTION) def show_recipes(self): self.switch_page(self.RECIPE_SELECTION) def show_configuration(self): self.switch_page(self.BASEIMG_SELECTED) def stop_build(self): if self.stopping: lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once," lbl = lbl + " would you like to 'Force Stop' the build?\n\n" lbl = lbl + "This will stop the build as quickly as possible but may" lbl = lbl + " well leave your build directory in an unusable state" lbl = lbl + " that requires manual steps to fix.\n" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL) HobAltButton.style_button(button) button = dialog.add_button("Force Stop", gtk.RESPONSE_YES) HobButton.style_button(button) else: lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this" lbl = lbl + " build?\n\n'Force Stop' will stop the build as quickly as" lbl = lbl + " possible but may well leave your build directory in an" lbl = lbl + " unusable state that requires manual steps to fix.\n\n" lbl = lbl + "'Stop' will stop the build as soon as all in" lbl = lbl + " progress build tasks are finished. However if a" lbl = lbl + " lengthy compilation phase is in progress this may take" lbl = lbl + " some time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL) HobAltButton.style_button(button) button = dialog.add_button("Stop", gtk.RESPONSE_OK) HobAltButton.style_button(button) button = dialog.add_button("Force Stop", gtk.RESPONSE_YES) HobButton.style_button(button) response = dialog.run() dialog.destroy() if response != gtk.RESPONSE_CANCEL: self.stopping = True if response == gtk.RESPONSE_OK: self.cancel_build_sync() elif response == gtk.RESPONSE_YES: self.cancel_build_sync(True)
class Builder(gtk.Window): (INITIAL_CHECKS, MACHINE_SELECTION, RCPPKGINFO_POPULATING, RCPPKGINFO_POPULATED, BASEIMG_SELECTED, RECIPE_SELECTION, PACKAGE_GENERATING, PACKAGE_GENERATED, PACKAGE_SELECTION, FAST_IMAGE_GENERATING, IMAGE_GENERATING, IMAGE_GENERATED, MY_IMAGE_OPENED, BACK, END_NOOP) = range(15) (SANITY_CHECK, IMAGE_CONFIGURATION, RECIPE_DETAILS, BUILD_DETAILS, PACKAGE_DETAILS, IMAGE_DETAILS, END_TAB) = range(7) __step2page__ = { INITIAL_CHECKS : SANITY_CHECK, MACHINE_SELECTION : IMAGE_CONFIGURATION, RCPPKGINFO_POPULATING : IMAGE_CONFIGURATION, RCPPKGINFO_POPULATED : IMAGE_CONFIGURATION, BASEIMG_SELECTED : IMAGE_CONFIGURATION, RECIPE_SELECTION : RECIPE_DETAILS, PACKAGE_GENERATING : BUILD_DETAILS, PACKAGE_GENERATED : PACKAGE_DETAILS, PACKAGE_SELECTION : PACKAGE_DETAILS, FAST_IMAGE_GENERATING : BUILD_DETAILS, IMAGE_GENERATING : BUILD_DETAILS, IMAGE_GENERATED : IMAGE_DETAILS, MY_IMAGE_OPENED : IMAGE_DETAILS, END_NOOP : None, } SANITY_CHECK_MIN_DISPLAY_TIME = 5 def __init__(self, hobHandler, recipe_model, package_model): super(Builder, self).__init__() self.hob_image = "hob-image" self.hob_toolchain = "hob-toolchain" # handler self.handler = hobHandler # logger self.logger = logging.getLogger("BitBake") self.current_logfile = None # configuration and parameters self.configuration = Configuration() self.parameters = Parameters() # build step self.current_step = None self.previous_step = None self.stopping = False self.ignore_next_completion = False # recipe model and package model self.recipe_model = recipe_model self.package_model = package_model # Indicate whether user has customized the image self.customized = False # Indicate whether the UI is working self.sensitive = True self.request_pkg_info = False # Indicate whether the sanity check ran self.sanity_checked = False # create visual elements self.create_visual_elements() # connect the signals to functions self.connect("delete-event", self.destroy_window_cb) self.recipe_model.connect ("recipe-selection-changed", self.recipelist_changed_cb) self.package_model.connect("package-selection-changed", self.packagelist_changed_cb) self.handler.connect("config-updated", self.handler_config_updated_cb) self.handler.connect("package-formats-updated", self.handler_package_formats_updated_cb) self.handler.connect("parsing-started", self.handler_parsing_started_cb) self.handler.connect("parsing", self.handler_parsing_cb) self.handler.connect("parsing-completed", self.handler_parsing_completed_cb) self.handler.build.connect("build-started", self.handler_build_started_cb) self.handler.build.connect("build-succeeded", self.handler_build_succeeded_cb) self.handler.build.connect("build-failed", self.handler_build_failed_cb) self.handler.build.connect("build-aborted", self.handler_build_aborted_cb) self.handler.build.connect("task-started", self.handler_task_started_cb) self.handler.build.connect("disk-full", self.handler_disk_full_cb) self.handler.build.connect("log-error", self.handler_build_failure_cb) self.handler.build.connect("log-warning", self.handler_build_failure_cb) self.handler.build.connect("log", self.handler_build_log_cb) self.handler.build.connect("no-provider", self.handler_no_provider_cb) self.handler.connect("generating-data", self.handler_generating_data_cb) self.handler.connect("data-generated", self.handler_data_generated_cb) self.handler.connect("command-succeeded", self.handler_command_succeeded_cb) self.handler.connect("command-failed", self.handler_command_failed_cb) self.handler.connect("sanity-failed", self.handler_sanity_failed_cb) self.handler.connect("recipe-populated", self.handler_recipe_populated_cb) self.handler.connect("package-populated", self.handler_package_populated_cb) self.initiate_new_build_async() signal.signal(signal.SIGINT, self.event_handle_SIGINT) def create_visual_elements(self): self.set_title("Hob IoT") self.set_icon_name("applications-development") self.set_resizable(True) window_width = 500 window_height = 550 self.set_size_request(window_width, window_height) self.vbox = gtk.VBox(False, 0) self.vbox.set_border_width(0) self.add(self.vbox) # create pages self.image_configuration_page = ImageConfigurationPage(self) self.recipe_details_page = RecipeSelectionPage(self) self.build_details_page = BuildDetailsPage(self) self.package_details_page = PackageSelectionPage(self) self.image_details_page = ImageDetailsPage(self) self.sanity_check_page = SanityCheckPage(self) self.display_sanity_check = False self.sanity_check_post_func = False self.nb = gtk.Notebook() self.nb.set_show_tabs(False) self.nb.insert_page(self.sanity_check_page, None, self.SANITY_CHECK) self.nb.insert_page(self.image_configuration_page, None, self.IMAGE_CONFIGURATION) self.nb.insert_page(self.recipe_details_page, None, self.RECIPE_DETAILS) self.nb.insert_page(self.build_details_page, None, self.BUILD_DETAILS) self.nb.insert_page(self.package_details_page, None, self.PACKAGE_DETAILS) self.nb.insert_page(self.image_details_page, None, self.IMAGE_DETAILS) self.vbox.pack_start(self.nb, expand=True, fill=True) self.show_all() self.nb.set_current_page(0) def sanity_check_timeout(self): # The minimum time for showing the 'sanity check' page has passe # If someone set the 'sanity_check_post_step' meanwhile, execute it now self.display_sanity_check = False if self.sanity_check_post_func: temp = self.sanity_check_post_func self.sanity_check_post_func = None temp() return False def show_sanity_check_page(self): # This window must stay on screen for at least 5 seconds, according to the design document self.nb.set_current_page(self.SANITY_CHECK) self.sanity_check_post_step = None self.display_sanity_check = True self.sanity_check_page.start() gobject.timeout_add(self.SANITY_CHECK_MIN_DISPLAY_TIME * 1000, self.sanity_check_timeout) def execute_after_sanity_check(self, func): if not self.display_sanity_check: func() else: self.sanity_check_post_func = func def generate_configuration(self): if not self.sanity_checked: self.show_sanity_check_page() self.handler.generate_configuration() def initiate_new_build_async(self): self.configuration.selected_image = None self.handler.init_cooker() self.generate_configuration() def sanity_check(self): self.handler.trigger_sanity_check() def populate_recipe_package_info_async(self): self.configuration.curr_mach = self.handler.runCommand(["getVariable", "MACHINE"]) or "clanton" self.switch_page(self.RCPPKGINFO_POPULATING) # Parse recipes self.set_user_config() self.handler.generate_recipes() def generate_packages_async(self, log = False): self.switch_page(self.PACKAGE_GENERATING) # Build packages _, all_recipes = self.recipe_model.get_selected_recipes() self.set_user_config() self.handler.reset_build() self.handler.generate_packages(all_recipes, self.configuration.default_task) def restore_initial_selected_packages(self): self.package_model.set_selected_packages(self.configuration.initial_user_selected_packages, True) self.package_model.set_selected_packages(self.configuration.initial_selected_packages) for package in self.configuration.selected_packages: if package not in self.configuration.initial_selected_packages: self.package_model.exclude_item(self.package_model.find_path_for_item(package)) def fast_generate_image_async(self, log = False): self.switch_page(self.FAST_IMAGE_GENERATING) # Build packages _, all_recipes = self.recipe_model.get_selected_recipes() self.set_user_config() self.handler.reset_build() self.handler.generate_packages(all_recipes, self.configuration.default_task) def generate_image_async(self, cont = False): self.switch_page(self.IMAGE_GENERATING) self.handler.reset_build() # Build image self.set_user_config() toolchain_packages = [] base_image = None if self.configuration.selected_image == self.recipe_model.__custom_image__: packages = self.package_model.get_selected_packages() image = self.hob_image base_image = self.configuration.initial_selected_image else: packages = [] image = self.configuration.selected_image self.handler.generate_image(image, base_image, packages, self.configuration.toolchain_build, self.configuration.default_task) def generate_new_image(self, image, description): base_image = self.configuration.initial_selected_image if base_image == self.recipe_model.__custom_image__: base_image = None packages = self.package_model.get_selected_packages() self.handler.generate_new_image(image, base_image, packages, description) def ensure_dir(self, directory): self.handler.ensure_dir(directory) def get_parameters_sync(self): return self.handler.get_parameters() def request_package_info_async(self): self.handler.request_package_info() def cancel_build_sync(self, force=False): self.handler.cancel_build(force) def cancel_parse_sync(self): self.handler.cancel_parse() def switch_page(self, next_step): # Main Workflow (Business Logic) self.nb.set_current_page(self.__step2page__[next_step]) if next_step == self.MACHINE_SELECTION: # init step self.image_configuration_page.show_machine() elif next_step == self.RCPPKGINFO_POPULATING: # MACHINE CHANGED action or SETTINGS CHANGED # show the progress bar self.image_configuration_page.show_info_populating() elif next_step == self.RCPPKGINFO_POPULATED: self.image_configuration_page.show_info_populated() elif next_step == self.BASEIMG_SELECTED: self.image_configuration_page.show_baseimg_selected() elif next_step == self.RECIPE_SELECTION: self.recipe_details_page.set_recipe_curr_tab(self.recipe_details_page.INCLUDED) elif next_step == self.PACKAGE_SELECTION: self.configuration.initial_selected_packages = self.configuration.selected_packages self.configuration.initial_user_selected_packages = self.configuration.user_selected_packages self.package_details_page.set_title("Edit packages") self.package_details_page.show_page(self.current_logfile) elif next_step == self.PACKAGE_GENERATING or next_step == self.FAST_IMAGE_GENERATING: # both PACKAGE_GENERATING and FAST_IMAGE_GENERATING share the same page self.build_details_page.show_page(next_step) elif next_step == self.PACKAGE_GENERATED: self.package_details_page.set_title("Step 2 of 2: Edit packages") self.package_details_page.show_page(self.current_logfile) elif next_step == self.IMAGE_GENERATING: # after packages are generated, selected_packages need to # be updated in package_model per selected_image in recipe_model self.build_details_page.show_page(next_step) elif next_step == self.IMAGE_GENERATED: self.image_details_page.show_page(next_step) elif next_step == self.MY_IMAGE_OPENED: self.image_details_page.show_page(next_step) self.previous_step = self.current_step self.current_step = next_step def set_distro_packages(self, distro): ''' Set the DISTRO for the purposes of getting the cached package list''' if distro != self.parameters.distro: self.ignore_next_completion = True self.request_pkg_info = True self.handler.reset_cooker() self.parameters.distro = distro self.handler.set_distro(self.parameters.distro) self.handler.runCommand(["parseConfigurationFiles", "", ""]) self.handler.runCommand(["triggerEvent", "bb.event.RequestPackageInfo()"]) def set_user_config(self): self.handler.reset_cooker() def update_recipe_model(self, selected_image, selected_recipes): self.recipe_model.set_selected_image(selected_image) self.recipe_model.set_selected_recipes(selected_recipes) def update_package_model(self, selected_packages, user_selected_packages=None): if user_selected_packages: left = self.package_model.set_selected_packages(user_selected_packages, True) self.configuration.user_selected_packages += left left = self.package_model.set_selected_packages(selected_packages) self.configuration.selected_packages += left def update_configuration_parameters(self, params): if params: self.configuration.update(params) self.parameters.update(params) def reset(self): self.configuration.curr_mach = "" self.configuration.clear_selection() self.initiate_new_build_async() # Callback Functions def handler_config_updated_cb(self, handler, which, values): if which == "distro": self.parameters.all_distros = values elif which == "machine": self.parameters.all_machines = values elif which == "machine-sdk": self.parameters.all_sdk_machines = values def handler_package_formats_updated_cb(self, handler, formats): self.parameters.all_package_formats = formats def handler_command_succeeded_cb(self, handler, initcmd): if self.ignore_next_completion: self.ignore_next_completion = False return if initcmd == self.handler.GENERATE_CONFIGURATION: if not self.configuration.curr_mach: self.configuration.curr_mach = self.handler.runCommand(["getVariable", "HOB_MACHINE"]) or "" self.update_configuration_parameters(self.get_parameters_sync()) if not self.sanity_checked: self.sanity_check() self.sanity_checked = True elif initcmd == self.handler.SANITY_CHECK: self.execute_after_sanity_check(self.populate_recipe_package_info_async) elif initcmd in [self.handler.GENERATE_RECIPES, self.handler.GENERATE_PACKAGES, self.handler.GENERATE_IMAGE]: self.update_configuration_parameters(self.get_parameters_sync()) self.request_package_info_async() elif initcmd == self.handler.POPULATE_PACKAGEINFO: if self.current_step == self.RCPPKGINFO_POPULATING: self.switch_page(self.RCPPKGINFO_POPULATED) self.image_configuration_page.update_image_combo(self.configuration.selected_image) #self.rcppkglist_populated() return self.rcppkglist_populated() if self.current_step == self.FAST_IMAGE_GENERATING: self.generate_image_async(True) def show_error_dialog(self, msg): lbl = "<b>Hob found an error</b>\n" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR, msg) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) response = dialog.run() dialog.destroy() def handler_command_failed_cb(self, handler, msg): if msg: self.show_error_dialog(msg) self.reset() def handler_sanity_failed_cb(self, handler, msg): self.reset() msg = msg.replace("your local.conf", "Settings") self.show_error_dialog(msg) self.reset() def window_sensitive(self, sensitive): self.image_configuration_page.image_combo.set_sensitive(sensitive) self.image_configuration_page.image_combo.child.set_sensitive(sensitive) self.image_configuration_page.config_build_button.set_sensitive(sensitive) self.recipe_details_page.set_sensitive(sensitive) self.package_details_page.set_sensitive(sensitive) self.build_details_page.set_sensitive(sensitive) self.image_details_page.set_sensitive(sensitive) if sensitive: self.window.set_cursor(None) else: self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) bb.ui.crumbs.utils.wait(0.1) self.sensitive = sensitive def handler_generating_data_cb(self, handler): self.window_sensitive(False) def handler_data_generated_cb(self, handler): if not self.request_pkg_info: self.window_sensitive(True) def rcppkglist_populated(self): selected_image = self.configuration.selected_image selected_recipes = self.configuration.selected_recipes[:] selected_packages = self.configuration.selected_packages[:] user_selected_packages = self.configuration.user_selected_packages[:] self.update_recipe_model(selected_image, selected_recipes) self.update_package_model(selected_packages, user_selected_packages) self.request_pkg_info = False self.window_sensitive(True) def recipelist_changed_cb(self, recipe_model): self.recipe_details_page.refresh_selection() def packagelist_changed_cb(self, package_model): self.package_details_page.refresh_selection() def handler_recipe_populated_cb(self, handler): self.image_configuration_page.update_progress_bar("Populating recipes", 0.99) def handler_package_populated_cb(self, handler): self.image_configuration_page.update_progress_bar("Populating packages", 1.0) def handler_parsing_started_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return fraction = 0 if message["eventname"] == "TreeDataPreparationStarted": fraction = 0.6 + fraction self.image_configuration_page.update_progress_bar("Generating dependency tree", fraction) else: self.image_configuration_page.update_progress_bar(message["title"], fraction) def handler_parsing_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return fraction = message["current"] * 1.0/message["total"] if message["eventname"] == "TreeDataPreparationProgress": fraction = 0.6 + 0.38 * fraction self.image_configuration_page.update_progress_bar("Generating dependency tree", fraction) else: fraction = 0.6 * fraction self.image_configuration_page.update_progress_bar(message["title"], fraction) def handler_parsing_completed_cb(self, handler, message): if self.current_step != self.RCPPKGINFO_POPULATING: return if message["eventname"] == "TreeDataPreparationCompleted": fraction = 0.98 else: fraction = 0.6 self.image_configuration_page.update_progress_bar("Generating dependency tree", fraction) def handler_build_started_cb(self, running_build): if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0 elif self.current_step == self.IMAGE_GENERATING: if self.previous_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 else: fraction = 0 elif self.current_step == self.PACKAGE_GENERATING: fraction = 0 self.build_details_page.update_progress_bar("Build Started: ", fraction) def build_succeeded(self): if self.current_step == self.FAST_IMAGE_GENERATING: fraction = 0.9 elif self.current_step == self.IMAGE_GENERATING: fraction = 1.0 version = "" self.parameters.image_names = [] selected_image = self.recipe_model.get_selected_image() if selected_image == self.recipe_model.__custom_image__: if self.configuration.initial_selected_image != selected_image: version = self.recipe_model.get_custom_image_version() linkname = 'hob-image' + version+ "-" + self.configuration.curr_mach else: linkname = selected_image + '-' + self.configuration.curr_mach image_extension = self.get_image_extension() self.parameters.image_addr = self.handler.runCommand(["getVariable", "DEPLOY_DIR_IMAGE"]) or "" for image_type in self.parameters.image_types: if image_type in image_extension: real_types = image_extension[image_type] else: real_types = [image_type] for real_image_type in real_types: linkpath = self.parameters.image_addr + '/' + linkname + '.' + real_image_type if os.path.exists(linkpath): self.parameters.image_names.append(os.readlink(linkpath)) elif self.current_step == self.PACKAGE_GENERATING: fraction = 1.0 self.build_details_page.update_progress_bar("Build Completed: ", fraction) self.handler.build_succeeded_async() self.stopping = False if self.current_step == self.PACKAGE_GENERATING: self.switch_page(self.PACKAGE_GENERATED) elif self.current_step == self.IMAGE_GENERATING: self.switch_page(self.IMAGE_GENERATED) def build_failed(self): if self.stopping: status = "stop" message = "Build stopped: " fraction = self.build_details_page.progress_bar.get_fraction() stop_to_next_edit = "" if self.current_step == self.FAST_IMAGE_GENERATING: stop_to_next_edit = "image configuration" elif self.current_step == self.IMAGE_GENERATING: if self.previous_step == self.FAST_IMAGE_GENERATING: stop_to_next_edit = "image configuration" else: stop_to_next_edit = "packages" elif self.current_step == self.PACKAGE_GENERATING: stop_to_next_edit = "recipes" button = self.build_details_page.show_stop_page(stop_to_next_edit.split(' ')[0]) self.set_default(button) else: fail_to_next_edit = "" if self.current_step == self.FAST_IMAGE_GENERATING: fail_to_next_edit = "image configuration" fraction = 0.9 elif self.current_step == self.IMAGE_GENERATING: if self.previous_step == self.FAST_IMAGE_GENERATING: fail_to_next_edit = "image configuration" else: fail_to_next_edit = "packages" fraction = 1.0 elif self.current_step == self.PACKAGE_GENERATING: fail_to_next_edit = "recipes" fraction = 1.0 self.build_details_page.show_fail_page(fail_to_next_edit.split(' ')[0]) status = "fail" message = "Build failed: " self.build_details_page.update_progress_bar(message, fraction, status) self.build_details_page.show_back_button() self.build_details_page.hide_stop_button() self.handler.build_failed_async() self.stopping = False def handler_build_succeeded_cb(self, running_build): if not self.stopping: self.build_succeeded() else: self.build_failed() def handler_build_failed_cb(self, running_build): self.build_failed() def handler_build_aborted_cb(self, running_build): self.build_failed() def handler_no_provider_cb(self, running_build, msg): dialog = CrumbsMessageDialog(self, glib.markup_escape_text(msg), gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() self.build_failed() def handler_task_started_cb(self, running_build, message): fraction = message["current"] * 1.0/message["total"] title = "Build packages" if self.current_step == self.FAST_IMAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.27 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.27 + 0.63 * fraction elif self.current_step == self.IMAGE_GENERATING: title = "Build image" if self.previous_step == self.FAST_IMAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.27 + 0.63 + 0.03 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.27 + 0.63 + 0.03 + 0.07 * fraction else: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.2 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.2 + 0.8 * fraction elif self.current_step == self.PACKAGE_GENERATING: if message["eventname"] == "sceneQueueTaskStarted": fraction = 0.2 * fraction elif message["eventname"] == "runQueueTaskStarted": fraction = 0.2 + 0.8 * fraction self.build_details_page.update_progress_bar(title + ": ", fraction) def handler_disk_full_cb(self, running_build): self.disk_full = True def handler_build_failure_cb(self, running_build): pass def handler_build_log_cb(self, running_build, func, obj): if hasattr(self.logger, func): getattr(self.logger, func)(obj) def destroy_window_cb(self, widget, event): if not self.sensitive: return True elif self.handler.building: self.stop_build() return True else: gtk.main_quit() def event_handle_SIGINT(self, signal, frame): for w in gtk.window_list_toplevels(): if w.get_modal(): w.response(gtk.RESPONSE_DELETE_EVENT) sys.exit(0) def build_packages(self): _, all_recipes = self.recipe_model.get_selected_recipes() if not all_recipes: lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.generate_packages_async(True) def build_image(self): selected_packages = self.package_model.get_selected_packages() if not selected_packages: lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.generate_image_async(True) def just_bake(self): selected_image = self.recipe_model.get_selected_image() selected_packages = self.package_model.get_selected_packages() or [] # If no base image and no selected packages don't build anything if not (selected_packages or selected_image != self.recipe_model.__custom_image__): lbl = "<b>No selections made</b>\nYou have not made any selections" lbl = lbl + " so there isn't anything to bake at this time." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return self.fast_generate_image_async(True) def show_recipe_property_dialog(self, properties): information = {} dialog = PropertyDialog(title = properties["name"] +' '+ "properties", parent = self, information = properties, flags = gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) dialog.set_modal(False) button = dialog.add_button("Close", gtk.RESPONSE_NO) HobAltButton.style_button(button) button.connect("clicked", lambda w: dialog.destroy()) dialog.run() def show_packages_property_dialog(self, properties): information = {} dialog = PropertyDialog(title = properties["name"] +' '+ "properties", parent = self, information = properties, flags = gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) dialog.set_modal(False) button = dialog.add_button("Close", gtk.RESPONSE_NO) HobAltButton.style_button(button) button.connect("clicked", lambda w: dialog.destroy()) dialog.run() def get_image_extension(self): image_extension = {} for type in self.parameters.image_types: ext = self.handler.runCommand(["getVariable", "IMAGE_EXTENSION_%s" % type]) if ext: image_extension[type] = ext.split(' ') return image_extension def deploy_image(self, image_name): if not image_name: lbl = "<b>Please select an image to deploy.</b>" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return dialog = DeployImageDialog(self, title = "Hob IoT", image = image_name, parent = self, flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR) response = dialog.run() dialog.destroy() def show_load_kernel_dialog(self): dialog = gtk.FileChooserDialog("Load Kernel Files", self, gtk.FILE_CHOOSER_ACTION_SAVE) button = dialog.add_button("Cancel", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Open", gtk.RESPONSE_YES) HobButton.style_button(button) filter = gtk.FileFilter() filter.set_name("Kernel Files") filter.add_pattern("*.bin") dialog.add_filter(filter) dialog.set_current_folder(self.parameters.image_addr) response = dialog.run() kernel_path = "" if response == gtk.RESPONSE_YES: kernel_path = dialog.get_filename() dialog.destroy() return kernel_path def runqemu_image(self, image_name, kernel_name): if not image_name or not kernel_name: lbl = "<b>Please select an %s to launch in QEMU.</b>" % ("kernel" if image_name else "image") dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() return kernel_path = os.path.join(self.parameters.image_addr, kernel_name) image_path = os.path.join(self.parameters.image_addr, image_name) source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env") tmp_path = self.parameters.tmpdir cmdline = bb.ui.crumbs.utils.which_terminal() if os.path.exists(image_path) and os.path.exists(kernel_path) \ and os.path.exists(source_env_path) and os.path.exists(tmp_path) \ and cmdline: cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; " cmdline += "source " + source_env_path + " " + os.getcwd() + "; " cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'" subprocess.Popen(shlex.split(cmdline)) else: lbl = "<b>Path error</b>\nOne of your paths is wrong," lbl = lbl + " please make sure the following paths exist:\n" lbl = lbl + "image path:" + image_path + "\n" lbl = lbl + "kernel path:" + kernel_path + "\n" lbl = lbl + "source environment path:" + source_env_path + "\n" lbl = lbl + "tmp path: " + tmp_path + "." lbl = lbl + "You may be missing either xterm or vte for terminal services." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) dialog.run() dialog.destroy() def show_packages(self, ask=True): _, selected_recipes = self.recipe_model.get_selected_recipes() if selected_recipes and ask: lbl = "<b>Package list may be incomplete!</b>\nDo you want to build selected recipes" lbl = lbl + " to get a full list or just view the existing packages?" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("View packages", gtk.RESPONSE_NO) HobAltButton.style_button(button) button = dialog.add_button("Build packages", gtk.RESPONSE_YES) HobButton.style_button(button) dialog.set_default_response(gtk.RESPONSE_YES) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_YES: self.generate_packages_async(True) else: self.switch_page(self.PACKAGE_SELECTION) else: self.switch_page(self.PACKAGE_SELECTION) def show_recipes(self): self.switch_page(self.RECIPE_SELECTION) def show_image_details(self): self.switch_page(self.IMAGE_GENERATED) def show_configuration(self): self.switch_page(self.BASEIMG_SELECTED) def stop_build(self): if self.stopping: lbl = "<b>Force Stop build?</b>\nYou've already selected Stop once," lbl = lbl + " would you like to 'Force Stop' the build?\n\n" lbl = lbl + "This will stop the build as quickly as possible but may" lbl = lbl + " well leave your build directory in an unusable state" lbl = lbl + " that requires manual steps to fix.\n" dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL) HobAltButton.style_button(button) button = dialog.add_button("Force Stop", gtk.RESPONSE_YES) HobButton.style_button(button) else: lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this" lbl = lbl + " build?\n\n'Stop' will stop the build as soon as all in" lbl = lbl + " progress build tasks are finished. However if a" lbl = lbl + " lengthy compilation phase is in progress this may take" lbl = lbl + " some time.\n\n" lbl = lbl + "'Force Stop' will stop the build as quickly as" lbl = lbl + " possible but may well leave your build directory in an" lbl = lbl + " unusable state that requires manual steps to fix." dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) button = dialog.add_button("Cancel", gtk.RESPONSE_CANCEL) HobAltButton.style_button(button) button = dialog.add_button("Force stop", gtk.RESPONSE_YES) HobAltButton.style_button(button) button = dialog.add_button("Stop", gtk.RESPONSE_OK) HobButton.style_button(button) response = dialog.run() dialog.destroy() if response != gtk.RESPONSE_CANCEL: self.stopping = True if response == gtk.RESPONSE_OK: self.build_details_page.progress_bar.set_stop_title("Stopping the build....") self.build_details_page.progress_bar.set_rcstyle("stop") self.cancel_build_sync() elif response == gtk.RESPONSE_YES: self.cancel_build_sync(True)