Exemple #1
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)
Exemple #2
0
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)
Exemple #3
0
class Builder(gtk.Window):

    (MACHINE_SELECTION,
     LAYER_CHANGED,
     RCPPKGINFO_POPULATING,
     RCPPKGINFO_POPULATED,
     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,
        LAYER_CHANGED         : IMAGE_CONFIGURATION,
        RCPPKGINFO_POPULATING : IMAGE_CONFIGURATION,
        RCPPKGINFO_POPULATED  : 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__()

        # handler
        self.handler = hobHandler

        self.template = None

        # settings
        params = self.handler.get_parameters()
        self.configuration = Configuration(params)
        self.parameters = Parameters(params)

        # 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

        # 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.recipe_model.connect ("recipelist-populated",      self.recipelist_populated_cb)
        self.package_model.connect("packagelist-populated",     self.packagelist_populated_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("layers-updated",           self.handler_layers_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.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.switch_page(self.MACHINE_SELECTION)

    def create_visual_elements(self):
        self.set_title("Hob - Image Creator")
        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 get_split_model(self):
        return self.handler.split_model

    def load_template(self, path):
        self.template = TemplateMgr()
        self.template.load(path)
        self.configuration.load(self.template)

        if self.get_split_model():
            if not set(self.configuration.layers) <= set(self.parameters.all_layers):
                return False
        else:
            for layer in self.configuration.layers:
                if not os.path.exists(layer+'/conf/layer.conf'):
                    return False

        self.switch_page(self.LAYER_CHANGED)

        self.template.destroy()
        self.template = None

    def save_template(self, path):
        if path.rfind("/") == -1:
            filename = "default"
            path = "."
        else:
            filename = path[path.rfind("/") + 1:len(path)]
            path = path[0:path.rfind("/")]

        self.template = TemplateMgr()
        self.template.open(filename, path)
        self.configuration.save(self.template, filename)

        self.template.save()
        self.template.destroy()
        self.template = None

    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.LAYER_CHANGED:
            # after layers is changd by users
            self.image_configuration_page.show_machine()
            self.handler.refresh_layers(self.configuration.layers)

        elif next_step == self.RCPPKGINFO_POPULATING:
            # MACHINE CHANGED action or SETTINGS CHANGED
            # show the progress bar
            self.image_configuration_page.show_info_populating()
            self.generate_recipes()

        elif next_step == self.RCPPKGINFO_POPULATED:
            self.image_configuration_page.show_info_populated()

        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)
            self.generate_packages()

        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)
            self.generate_image()

        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")

    def reset_recipe_model(self):
        self.recipe_model.reset()

    def reset_package_model(self):
        self.package_model.reset()

    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 generate_packages(self):
        # Build packages
        _, all_recipes = self.recipe_model.get_selected_recipes()
        self.set_user_config()
        self.handler.reset_build()
        self.handler.generate_packages(all_recipes)

    def generate_recipes(self):
        # Parse recipes
        self.set_user_config()
        self.handler.generate_recipes()

    def generate_image(self):
        # Build image
        self.set_user_config()
        all_packages = self.package_model.get_selected_packages()
        self.handler.reset_build()
        self.handler.generate_image(all_packages, self.configuration.toolchain_build)


    # 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_layers_updated_cb(self, handler, layers):
        self.parameters.all_layers = layers

    def handler_command_succeeded_cb(self, handler, initcmd):
        if initcmd == self.handler.LAYERS_REFRESH:
            self.image_configuration_page.switch_machine_combo()
        elif initcmd in [self.handler.GENERATE_RECIPES,
                         self.handler.GENERATE_PACKAGES,
                         self.handler.GENERATE_IMAGE]:
            self.handler.request_package_info_async()
        elif initcmd == self.handler.POPULATE_PACKAGEINFO:
            if self.current_step == self.FAST_IMAGE_GENERATING:
                self.switch_page(self.IMAGE_GENERATING)
            elif self.current_step == self.RCPPKGINFO_POPULATING:
                self.switch_page(self.RCPPKGINFO_POPULATED)
            elif 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 handler_command_failed_cb(self, handler, msg):
        lbl = "<b>Error</b>\n"
        lbl = lbl + "%s\n\n" % msg
        dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
        dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
        response = dialog.run()
        dialog.destroy()
        self.handler.clear_busy()
        self.configuration.curr_mach = None
        self.image_configuration_page.switch_machine_combo()
        self.switch_page(self.MACHINE_SELECTION)

    def window_sensitive(self, sensitive):
        self.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))


    def handler_generating_data_cb(self, handler):
        self.window_sensitive(False)

    def handler_data_generated_cb(self, handler):
        self.window_sensitive(True)

    def recipelist_populated_cb(self, recipe_model):
        selected_image = self.configuration.selected_image
        selected_recipes = self.configuration.selected_recipes[:]
        selected_packages = self.configuration.selected_packages[:]

        self.recipe_model.image_list_append(selected_image,
                                            " ".join(selected_recipes),
                                            " ".join(selected_packages))

        self.image_configuration_page.update_image_combo(self.recipe_model, selected_image)

        self.update_recipe_model(selected_image, selected_recipes)

    def packagelist_populated_cb(self, package_model):
        selected_packages = self.configuration.selected_packages[:]
        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.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)

    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 = []
            linkname = 'hob-image-' + self.configuration.curr_mach
            for image_type in self.parameters.image_types:
                linkpath = self.parameters.image_addr + '/' + linkname + '.' + 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.stopping = False

    def build_failed(self):
        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
        self.build_details_page.update_progress_bar("Build Failed: ", fraction, False)
        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_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 destroy_window_cb(self, widget, event):
        lbl = "<b>Do you really want to exit the Hob image creator?</b>"
        dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
        dialog.add_button("Keep using Hob", gtk.RESPONSE_NO)
        dialog.add_button("Exit Hob", gtk.RESPONSE_YES)
        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)
            dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
            dialog.run()
            dialog.destroy()
            return
        self.switch_page(self.PACKAGE_GENERATING)

    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)
            dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
            dialog.run()
            dialog.destroy()
            return
        self.switch_page(self.IMAGE_GENERATING)

    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)
            dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
            dialog.run()
            dialog.destroy()
            return

        self.switch_page(self.FAST_IMAGE_GENERATING)

    def show_binb_dialog(self, binb):
        binb_dialog = BinbDialog("Brought in by:", binb, self)
        binb_dialog.run()
        binb_dialog.destroy()

    def show_layer_selection_dialog(self):
        dialog = LayerSelectionDialog(title = "Layer Selection",
                     layers = copy.deepcopy(self.configuration.layers),
                     all_layers = self.parameters.all_layers,
                     split_model = self.get_split_model(),
                     parent = self,
                     flags = gtk.DIALOG_MODAL
                         | gtk.DIALOG_DESTROY_WITH_PARENT
                         | gtk.DIALOG_NO_SEPARATOR,
                     buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
                                gtk.STOCK_OK, gtk.RESPONSE_YES))
        response = dialog.run()
        if response == gtk.RESPONSE_YES:
            self.configuration.layers = dialog.layers
            # DO refresh layers
            if dialog.layers_changed:
                self.switch_page(self.LAYER_CHANGED)
        dialog.destroy()

    def show_load_template_dialog(self):
        dialog = gtk.FileChooserDialog("Load Template Files", self,
                                       gtk.FILE_CHOOSER_ACTION_OPEN,
                                      (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
                                       gtk.STOCK_OPEN, gtk.RESPONSE_YES))
        filter = gtk.FileFilter()
        filter.set_name("Hob Files")
        filter.add_pattern("*.hob")
        dialog.add_filter(filter)

        response = dialog.run()
        if response == gtk.RESPONSE_YES:
            path = dialog.get_filename()
            self.load_template(path)
        dialog.destroy()

    def show_save_template_dialog(self):
        dialog = gtk.FileChooserDialog("Save Template Files", self,
                                       gtk.FILE_CHOOSER_ACTION_SAVE,
                                      (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
                                       gtk.STOCK_SAVE, gtk.RESPONSE_YES))
        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,
                                      (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
                                       gtk.STOCK_OPEN, gtk.RESPONSE_YES))
        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)
                crumbs_dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
                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,
            split_model = self.get_split_model(),
            parent = self,
            flags = gtk.DIALOG_MODAL
                    | gtk.DIALOG_DESTROY_WITH_PARENT
                    | gtk.DIALOG_NO_SEPARATOR,
            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
                       "Save", gtk.RESPONSE_YES))
        response = dialog.run()
        if response == gtk.RESPONSE_YES:
            self.configuration = dialog.configuration
            # DO reparse recipes
            if dialog.settings_changed:
                if self.configuration.curr_mach == "":
                    self.switch_page(self.MACHINE_SELECTION)
                else:
                    self.switch_page(self.RCPPKGINFO_POPULATING)
        dialog.destroy()

    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)
            dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
            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,
            buttons = ("Close", gtk.RESPONSE_NO,
                       "Make usb image", gtk.RESPONSE_YES))
        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)
            dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
            dialog.run()
            dialog.destroy()
            return

        dialog = gtk.FileChooserDialog("Load Kernel Files", self,
                                       gtk.FILE_CHOOSER_ACTION_SAVE,
                                      (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
                                       gtk.STOCK_OPEN, gtk.RESPONSE_YES))
        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 = os.path.join(os.getcwd(), "tmp")
            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):
                cmdline = "/usr/bin/xterm -e "
                cmdline += "\" export OE_TMPDIR=" + tmp_path + "; "
                cmdline += "source " + source_env_path + " " + os.getcwd() + "; "
                cmdline += "runqemu " + kernel_path + " " + image_path + "; bash\""
                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 + "."
                dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
                dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
                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 (Yes) or just view the existing packages (No)?"
            dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
            dialog.add_button(gtk.STOCK_NO, gtk.RESPONSE_NO)
            dialog.add_button(gtk.STOCK_YES, gtk.RESPONSE_YES)
            dialog.set_default_response(gtk.RESPONSE_YES)
            response = dialog.run()
            dialog.destroy()
            if response == gtk.RESPONSE_YES:
                self.switch_page(self.PACKAGE_GENERATING)
            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 initiate_new_build(self):
	self.configuration.curr_mach = ""
        self.image_configuration_page.switch_machine_combo()
        self.switch_page(self.MACHINE_SELECTION)

    def show_configuration(self):
        self.switch_page(self.RCPPKGINFO_POPULATED)

    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)
            dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
            dialog.add_button("Force Stop", gtk.RESPONSE_YES)
        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)
            dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
            dialog.add_button("Stop", gtk.RESPONSE_OK)
            dialog.add_button("Force Stop", gtk.RESPONSE_YES)
        response = dialog.run()
        dialog.destroy()
        if response != gtk.RESPONSE_CANCEL:
            self.stopping = True
        if response == gtk.RESPONSE_OK:
            self.handler.cancel_build()
        elif response == gtk.RESPONSE_YES:
            self.handler.cancel_build(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)
Exemple #5
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)