Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
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)