#!/usr/bin/env python # # BitBake Graphical GTK User Interface # # Copyright (C) 2011-2012 Intel Corporation # # Authored by Joshua Lock # Authored by Dongxiao Xu # Authored by Shane Wang # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import gtk import copy import os import subprocess import shlex import re from bb.ui.crumbs.template import TemplateMgr from bb.ui.crumbs.imageconfigurationpage import ImageConfigurationPage from bb.ui.crumbs.recipeselectionpage import RecipeSelectionPage from bb.ui.crumbs.packageselectionpage import PackageSelectionPage from bb.ui.crumbs.builddetailspage import BuildDetailsPage from bb.ui.crumbs.imagedetailspage import ImageDetailsPage from bb.ui.crumbs.hobwidget import hwc, HobButton, HobAltButton, hcc from bb.ui.crumbs.hig import CrumbsMessageDialog, ImageSelectionDialog, \ AdvancedSettingDialog, LayerSelectionDialog, \ DeployImageDialog from bb.ui.crumbs.persistenttooltip import PersistentTooltip import bb.ui.crumbs.utils hobVer = 20120530 class Configuration: '''Represents the data structure of configuration.''' @classmethod def parse_proxy_string(cls, proxy): pattern = "^\s*((http|https|ftp|git|cvs)://)?((\S+):(\S+)@)?(\S+):(\d+)/?" match = re.search(pattern, proxy) if match: return match.group(2), match.group(4), match.group(5), match.group(6), match.group(7) else: return None, None, None, "", "" @classmethod def make_host_string(cls, prot, user, passwd, host, default_prot=""): if host == None or host == "": return "" passwd = passwd or "" if user != None and user != "": if prot == None or prot == "": prot = default_prot return prot + "://" + user + ":" + passwd + "@" + host else: if prot == None or prot == "": return host else: return prot + "://" + host @classmethod def make_port_string(cls, port): port = port or "" return port @classmethod def make_proxy_string(cls, prot, user, passwd, host, port, default_prot=""): if host == None or host == "" or port == None or port == "": return "" return Configuration.make_host_string(prot, user, passwd, host, default_prot) + ":" + Configuration.make_port_string(port) def __init__(self): self.curr_mach = "" # settings self.curr_distro = "" self.dldir = self.sstatedir = self.sstatemirror = "" self.pmake = self.bbthread = 0 self.curr_package_format = "" self.image_rootfs_size = self.image_extra_size = 0 self.image_overhead_factor = 1 self.incompat_license = "" self.curr_sdk_machine = "" self.conf_version = self.lconf_version = "" self.extra_setting = {} self.toolchain_build = False self.image_fstypes = "" # bblayers.conf self.layers = [] # image/recipes/packages self.clear_selection() self.user_selected_packages = [] self.default_task = "build" # proxy settings self.enable_proxy = None self.same_proxy = False self.proxies = { "http" : [None, None, None, "", ""], # protocol : [prot, user, passwd, host, port] "https" : [None, None, None, "", ""], "ftp" : [None, None, None, "", ""], "git" : [None, None, None, "", ""], "cvs" : [None, None, None, "", ""], } def clear_selection(self): self.selected_image = None self.selected_recipes = [] self.selected_packages = [] def split_proxy(self, protocol, proxy): entry = [] prot, user, passwd, host, port = Configuration.parse_proxy_string(proxy) entry.append(prot) entry.append(user) entry.append(passwd) entry.append(host) entry.append(port) self.proxies[protocol] = entry def combine_proxy(self, protocol): entry = self.proxies[protocol] return Configuration.make_proxy_string(entry[0], entry[1], entry[2], entry[3], entry[4], protocol) def combine_host_only(self, protocol): entry = self.proxies[protocol] return Configuration.make_host_string(entry[0], entry[1], entry[2], entry[3], protocol) def combine_port_only(self, protocol): entry = self.proxies[protocol] return Configuration.make_port_string(entry[4]) def update(self, params): # settings self.curr_distro = params["distro"] self.dldir = params["dldir"] self.sstatedir = params["sstatedir"] self.sstatemirror = params["sstatemirror"] self.pmake = int(params["pmake"].split()[1]) self.bbthread = params["bbthread"] self.curr_package_format = " ".join(params["pclass"].split("package_")).strip() self.image_rootfs_size = params["image_rootfs_size"] self.image_extra_size = params["image_extra_size"] self.image_overhead_factor = params['image_overhead_factor'] self.incompat_license = params["incompat_license"] self.curr_sdk_machine = params["sdk_machine"] self.conf_version = params["conf_version"] self.lconf_version = params["lconf_version"] self.image_fstypes = params["image_fstypes"] # self.extra_setting/self.toolchain_build # bblayers.conf self.layers = params["layer"].split() self.default_task = params["default_task"] # proxy settings self.enable_proxy = params["http_proxy"] != "" or params["https_proxy"] != "" or params["ftp_proxy"] != "" \ or params["git_proxy_host"] != "" or params["git_proxy_port"] != "" \ or params["cvs_proxy_host"] != "" or params["cvs_proxy_port"] != "" self.split_proxy("http", params["http_proxy"]) self.split_proxy("https", params["https_proxy"]) self.split_proxy("ftp", params["ftp_proxy"]) self.split_proxy("git", params["git_proxy_host"] + ":" + params["git_proxy_port"]) self.split_proxy("cvs", params["cvs_proxy_host"] + ":" + params["cvs_proxy_port"]) def load(self, template): self.curr_mach = template.getVar("MACHINE") self.curr_package_format = " ".join(template.getVar("PACKAGE_CLASSES").split("package_")).strip() self.curr_distro = template.getVar("DISTRO") self.dldir = template.getVar("DL_DIR") self.sstatedir = template.getVar("SSTATE_DIR") self.sstatemirror = template.getVar("SSTATE_MIRROR") try: self.pmake = int(template.getVar("PARALLEL_MAKE").split()[1]) except: pass try: self.bbthread = int(template.getVar("BB_NUMBER_THREADS")) except: pass try: self.image_rootfs_size = int(template.getVar("IMAGE_ROOTFS_SIZE")) except: pass try: self.image_extra_size = int(template.getVar("IMAGE_EXTRA_SPACE")) except: pass # image_overhead_factor is read-only. self.incompat_license = template.getVar("INCOMPATIBLE_LICENSE") self.curr_sdk_machine = template.getVar("SDKMACHINE") self.conf_version = template.getVar("CONF_VERSION") self.lconf_version = template.getVar("LCONF_VERSION") self.extra_setting = eval(template.getVar("EXTRA_SETTING")) self.toolchain_build = eval(template.getVar("TOOLCHAIN_BUILD")) self.image_fstypes = template.getVar("IMAGE_FSTYPES") # bblayers.conf self.layers = template.getVar("BBLAYERS").split() # image/recipes/packages self.selected_image = template.getVar("__SELECTED_IMAGE__") self.selected_recipes = template.getVar("DEPENDS").split() self.selected_packages = template.getVar("IMAGE_INSTALL").split() # proxy self.enable_proxy = eval(template.getVar("enable_proxy")) self.same_proxy = eval(template.getVar("use_same_proxy")) self.split_proxy("http", template.getVar("http_proxy")) self.split_proxy("https", template.getVar("https_proxy")) self.split_proxy("ftp", template.getVar("ftp_proxy")) self.split_proxy("git", template.getVar("GIT_PROXY_HOST") + ":" + template.getVar("GIT_PROXY_PORT")) self.split_proxy("cvs", template.getVar("CVS_PROXY_HOST") + ":" + template.getVar("CVS_PROXY_PORT")) def save(self, template, defaults=False): template.setVar("VERSION", "%s" % hobVer) # bblayers.conf template.setVar("BBLAYERS", " ".join(self.layers)) # local.conf if not defaults: template.setVar("MACHINE", self.curr_mach) template.setVar("DISTRO", self.curr_distro) template.setVar("DL_DIR", self.dldir) template.setVar("SSTATE_DIR", self.sstatedir) template.setVar("SSTATE_MIRROR", self.sstatemirror) template.setVar("PARALLEL_MAKE", "-j %s" % self.pmake) template.setVar("BB_NUMBER_THREADS", self.bbthread) template.setVar("PACKAGE_CLASSES", " ".join(["package_" + i for i in self.curr_package_format.split()])) template.setVar("IMAGE_ROOTFS_SIZE", self.image_rootfs_size) template.setVar("IMAGE_EXTRA_SPACE", self.image_extra_size) template.setVar("INCOMPATIBLE_LICENSE", self.incompat_license) template.setVar("SDKMACHINE", self.curr_sdk_machine) template.setVar("CONF_VERSION", self.conf_version) template.setVar("LCONF_VERSION", self.lconf_version) template.setVar("EXTRA_SETTING", self.extra_setting) template.setVar("TOOLCHAIN_BUILD", self.toolchain_build) template.setVar("IMAGE_FSTYPES", self.image_fstypes) if not defaults: # image/recipes/packages template.setVar("__SELECTED_IMAGE__", self.selected_image) template.setVar("DEPENDS", self.selected_recipes) template.setVar("IMAGE_INSTALL", self.user_selected_packages) # proxy template.setVar("enable_proxy", self.enable_proxy) template.setVar("use_same_proxy", self.same_proxy) template.setVar("http_proxy", self.combine_proxy("http")) template.setVar("https_proxy", self.combine_proxy("https")) template.setVar("ftp_proxy", self.combine_proxy("ftp")) template.setVar("GIT_PROXY_HOST", self.combine_host_only("git")) template.setVar("GIT_PROXY_PORT", self.combine_port_only("git")) template.setVar("CVS_PROXY_HOST", self.combine_host_only("cvs")) template.setVar("CVS_PROXY_PORT", self.combine_port_only("cvs")) class Parameters: '''Represents other variables like available machines, etc.''' def __init__(self): # Variables self.max_threads = 65535 self.core_base = "" self.image_addr = "" self.image_types = [] self.runnable_image_types = [] self.runnable_machine_patterns = [] self.deployable_image_types = [] self.tmpdir = "" self.all_machines = [] self.all_package_formats = [] self.all_distros = [] self.all_sdk_machines = [] self.all_layers = [] self.image_names = [] self.image_white_pattern = "" self.image_black_pattern = "" # for build log to show self.bb_version = "" self.target_arch = "" self.target_os = "" self.distro_version = "" self.tune_pkgarch = "" def update(self, params): self.max_threads = params["max_threads"] self.core_base = params["core_base"] self.image_addr = params["image_addr"] self.image_types = params["image_types"].split() self.runnable_image_types = params["runnable_image_types"].split() self.runnable_machine_patterns = params["runnable_machine_patterns"].split() self.deployable_image_types = params["deployable_image_types"].split() self.tmpdir = params["tmpdir"] self.image_white_pattern = params["image_white_pattern"] self.image_black_pattern = params["image_black_pattern"] self.kernel_image_type = params["kernel_image_type"] # for build log to show self.bb_version = params["bb_version"] self.target_arch = params["target_arch"] self.target_os = params["target_os"] self.distro_version = params["distro_version"] self.tune_pkgarch = params["tune_pkgarch"] def hob_conf_filter(fn, data): if fn.endswith("/local.conf"): distro = data.getVar("DISTRO_HOB") if distro: if distro != "defaultsetup": data.setVar("DISTRO", distro) else: data.delVar("DISTRO") keys = ["MACHINE_HOB", "SDKMACHINE_HOB", "PACKAGE_CLASSES_HOB", \ "BB_NUMBER_THREADS_HOB", "PARALLEL_MAKE_HOB", "DL_DIR_HOB", \ "SSTATE_DIR_HOB", "SSTATE_MIRROR_HOB", "INCOMPATIBLE_LICENSE_HOB"] for key in keys: var_hob = data.getVar(key) if var_hob: data.setVar(key.split("_HOB")[0], var_hob) return if fn.endswith("/bblayers.conf"): layers = data.getVar("BBLAYERS_HOB") if layers: data.setVar("BBLAYERS", layers) return 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, } @classmethod def interpret_markup(cls, msg): msg = msg.replace('&', '&') msg = msg.replace('<', '<') msg = msg.replace('>', '>') msg = msg.replace('"', '"') msg = msg.replace("'", "´") return msg 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.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.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/")) == False: 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.__custom_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 False self.template = TemplateMgr() # check compatibility tempVer = self.template.getVersion(path) if not tempVer or int(tempVer) < hobVer: self.template.destroy() self.template = None return False 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.configuration.enable_proxy == True: self.handler.set_http_proxy(self.configuration.combine_proxy("http")) self.handler.set_https_proxy(self.configuration.combine_proxy("https")) self.handler.set_ftp_proxy(self.configuration.combine_proxy("ftp")) self.handler.set_git_proxy(self.configuration.combine_host_only("git"), self.configuration.combine_port_only("git")) self.handler.set_cvs_proxy(self.configuration.combine_host_only("cvs"), self.configuration.combine_port_only("cvs")) elif self.configuration.enable_proxy == False: self.handler.set_http_proxy("") self.handler.set_https_proxy("") self.handler.set_ftp_proxy("") self.handler.set_git_proxy("", "") self.handler.set_cvs_proxy("", "") 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 = "Error\n" lbl = lbl + "%s\n\n" % Builder.interpret_markup(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: self.show_error_dialog(msg) self.reset() def handler_sanity_failed_cb(self, handler, 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.machine_combo.child.set_sensitive(sensitive) self.image_configuration_page.image_combo.set_sensitive(sensitive) self.image_configuration_page.image_combo.child.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() 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_recipe_populated_cb(self, handler): self.image_configuration_page.update_progress_bar("Populated recipes", 0.99) def handler_package_populated_cb(self, handler): self.image_configuration_page.update_progress_bar("Populated 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.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.38 * 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(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.__custom_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: 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], fail_to_next_edit) 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, Builder.interpret_markup(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 = "Do you really want to exit the Hob image creator?" 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 = "No selections made\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 = "No selections made\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.__custom_image__): lbl = "No selections made\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 = "Brought in by:\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 = "No selections made\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, 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.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 = "Please select an image to deploy." 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 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 = "Please select an %s to launch in QEMU." % ("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 = "Path error\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 = "Package list may be incomplete!\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 = "Force Stop build?\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 = "Stop build?\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)