summaryrefslogtreecommitdiffstats
path: root/lib/bb/ui/hob.py
diff options
context:
space:
mode:
authorDongxiao Xu <dongxiao.xu@intel.com>2011-11-28 14:32:40 +0800
committerRichard Purdie <richard.purdie@linuxfoundation.org>2012-02-24 17:51:20 +0000
commit4dacd29f9c957d20f4583330b51e5420f9c3338d (patch)
treeea13a7e1e08e68d2c6aaeafe34892a9603717307 /lib/bb/ui/hob.py
parent82482aae6f311c994275fb0b6b32d954bbfc78c3 (diff)
downloadbitbake-4dacd29f9c957d20f4583330b51e5420f9c3338d.tar.gz
Hob: A new implemetation (v2)
This commit implements a new design for hob Some of the new features: - Friendly new designed GUI. Quick response to user actions. - Two step builds support package generation and image generation. - Support running GUI seprarately from bitbake server. - Recipe/package selection and deselection. - Accurate customization for image contents and size. - Progress bars showing the parsing and build status. - Load/save user configurations from/into templates. Signed-off-by: Dongxiao Xu <dongxiao.xu@intel.com> Signed-off-by: Shane Wang <shane.wang@intel.com> Signed-off-by: Liming An <limingx.l.an@intel.com> Signed-off-by: Fengxia Hua <fengxia.hua@intel.com> Designed-by: Belen Barros Pena <belen.barros.pena@intel.com>
Diffstat (limited to 'lib/bb/ui/hob.py')
-rwxr-xr-x[-rw-r--r--]lib/bb/ui/hob.py1115
1 files changed, 48 insertions, 1067 deletions
diff --git a/lib/bb/ui/hob.py b/lib/bb/ui/hob.py
index 0fcaad54a..429bb750d 100644..100755
--- a/lib/bb/ui/hob.py
+++ b/lib/bb/ui/hob.py
@@ -1,9 +1,11 @@
+#!/usr/bin/env python
#
# BitBake Graphical GTK User Interface
#
# Copyright (C) 2011 Intel Corporation
#
# Authored by Joshua Lock <josh@linux.intel.com>
+# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
#
# 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
@@ -18,1087 +20,58 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-import glib
import gobject
import gtk
-from bb.ui.crumbs.tasklistmodel import TaskListModel, BuildRep
+import sys
+import os
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
+try:
+ import bb
+except RuntimeError as exc:
+ sys.exit(str(exc))
+from bb.ui import uihelper
+from bb.ui.crumbs.hoblistmodel import RecipeListModel, PackageListModel
from bb.ui.crumbs.hobeventhandler import HobHandler
-from bb.ui.crumbs.configurator import Configurator
-from bb.ui.crumbs.hobprefs import HobPrefs
-from bb.ui.crumbs.layereditor import LayerEditor
-from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
-from bb.ui.crumbs.hig import CrumbsDialog
-import xmlrpclib
-import logging
-import Queue
+from bb.ui.crumbs.builder import Builder
extraCaches = ['bb.cache_extra:HobRecipeInfo']
-class MainWindow (gtk.Window):
-
- def __init__(self, taskmodel, handler, configurator, prefs, layers, mach):
- gtk.Window.__init__(self)
- # global state
- self.curr_mach = mach
- self.machine_handler_id = None
- self.image_combo_id = None
- self.generating = False
- self.files_to_clean = []
- self.selected_image = None
- self.selected_packages = None
- self.stopping = False
-
- self.model = taskmodel
- self.model.connect("tasklist-populated", self.update_model)
- self.model.connect("image-changed", self.image_changed_string_cb)
- self.handler = handler
- self.configurator = configurator
- self.prefs = prefs
- self.layers = layers
- self.save_path = None
- self.dirty = False
- self.build_succeeded = False
-
- self.connect("delete-event", self.destroy_window)
- self.set_title("Image Creator")
- self.set_icon_name("applications-development")
- self.set_default_size(1000, 650)
-
- self.build = RunningBuild(sequential=True)
- self.build.connect("build-failed", self.running_build_failed_cb)
- self.build.connect("build-succeeded", self.running_build_succeeded_cb)
- self.build.connect("build-started", self.build_started_cb)
- self.build.connect("build-complete", self.build_complete_cb)
-
- vbox = gtk.VBox(False, 0)
- vbox.set_border_width(0)
- vbox.show()
- self.add(vbox)
- self.menu = self.create_menu()
- vbox.pack_start(self.menu, False)
- createview = self.create_build_gui()
- self.back = None
- self.cancel = None
- buildview = self.view_build_gui()
- self.nb = gtk.Notebook()
- self.nb.append_page(createview)
- self.nb.append_page(buildview)
- self.nb.set_current_page(0)
- self.nb.set_show_tabs(False)
- vbox.pack_start(self.nb, expand=True, fill=True)
-
- def destroy_window(self, widget, event):
- self.quit()
-
- def menu_quit(self, action):
- self.quit()
-
- def quit(self):
- if self.dirty and len(self.model.contents):
- question = "Would you like to save your customisations?"
- dialog = CrumbsDialog(self, question, gtk.STOCK_DIALOG_WARNING)
- dialog.add_buttons(gtk.STOCK_NO, gtk.RESPONSE_NO,
- gtk.STOCK_YES, gtk.RESPONSE_YES)
- resp = dialog.run()
- dialog.destroy()
- if resp == gtk.RESPONSE_YES:
- if not self.save_path:
- self.get_save_path()
-
- if self.save_path:
- self.save_recipe_file()
- rep = self.model.get_build_rep()
- rep.writeRecipe(self.save_path, self.model)
-
- # Prevent the busy cursor being shown after hob exits if quit is called
- # whilst the busy cursor is set
- self.set_busy_cursor(False)
-
- self.handler.remove_temp_dir()
-
- gtk.main_quit()
-
- """
- In the case of a fatal error give the user as much information as possible
- and then exit.
- """
- def fatal_error_cb(self, handler, errormsg, phase):
- lbl = "<b>Error!</b>\nThere was an unrecoverable error during the"
- lbl = lbl + " <i>%s</i> phase of BitBake. This must be" % phase
- lbl = lbl + " rectified before the GUI will function. The error"
- lbl = lbl + " message which which caused this is:\n\n\"%s\"" % errormsg
- dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_ERROR)
- dialog.add_button("Exit", gtk.RESPONSE_OK)
- response = dialog.run()
- dialog.destroy()
- self.set_busy_cursor(False)
- gtk.main_quit()
-
- def scroll_tv_cb(self, model, path, it, view):
- view.scroll_to_cell(path)
-
- def running_build_succeeded_cb(self, running_build):
- self.build_succeeded = True
-
- def running_build_failed_cb(self, running_build):
- self.build_succeeded = False
-
- def image_changed_string_cb(self, model, new_image):
- self.selected_image = new_image
- # disconnect the image combo's signal handler
- if self.image_combo_id:
- self.image_combo.disconnect(self.image_combo_id)
- self.image_combo_id = None
- cnt = 0
- it = self.model.images.get_iter_first()
- while it:
- path = self.model.images.get_path(it)
- if self.model.images[path][self.model.COL_NAME] == new_image:
- self.image_combo.set_active(cnt)
- break
- it = self.model.images.iter_next(it)
- cnt = cnt + 1
- # Reconnect the signal handler
- if not self.image_combo_id:
- self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
-
- def image_changed_cb(self, combo):
- model = self.image_combo.get_model()
- it = self.image_combo.get_active_iter()
- if it:
- path = model.get_path(it)
- # Firstly, deselect the previous image
- userp, _ = self.model.get_selected_packages()
- self.model.reset()
- # Now select the new image and save its path in case we
- # change the image later
- self.toggle_package(path, model, image=True)
- if len(userp):
- self.model.set_selected_packages(userp)
- self.selected_image = model[path][self.model.COL_NAME]
-
- def reload_triggered_cb(self, handler, image, packages):
- if image:
- self.selected_image = image
- if len(packages):
- self.selected_packages = packages.split()
-
- def data_generated(self, handler):
- self.generating = False
- self.enable_widgets()
-
- def machine_combo_changed_cb(self, combo, handler):
- mach = combo.get_active_text()
- if mach != self.curr_mach:
- self.curr_mach = mach
- # Flush this straight to the file as MACHINE is changed
- # independently of other 'Preferences'
- self.configurator.setConfVar('MACHINE', mach)
- self.configurator.writeConf()
- handler.set_machine(mach)
- handler.reload_data()
-
- def update_machines(self, handler, machines):
- active = 0
- # disconnect the signal handler before updating the combo model
- if self.machine_handler_id:
- self.machine_combo.disconnect(self.machine_handler_id)
- self.machine_handler_id = None
-
- model = self.machine_combo.get_model()
- if model:
- model.clear()
-
- for machine in machines:
- self.machine_combo.append_text(machine)
- if machine == self.curr_mach:
- self.machine_combo.set_active(active)
- active = active + 1
-
- self.machine_handler_id = self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler)
-
- def set_busy_cursor(self, busy=True):
- """
- Convenience method to set the cursor to a spinner when executing
- a potentially lengthy process.
- A busy value of False will set the cursor back to the default
- left pointer.
- """
- if busy:
- cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
- else:
- # TODO: presumably the default cursor is different on RTL
- # systems. Can we determine the default cursor? Or at least
- # the cursor which is set before we change it?
- cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
- window = self.get_root_window()
- window.set_cursor(cursor)
-
- def busy_idle_func(self):
- if self.generating:
- self.progress.pulse()
- return True
- else:
- if not self.image_combo_id:
- self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
- self.progress.set_text("Loaded")
- self.progress.set_fraction(0.0)
- self.set_busy_cursor(False)
- return False
-
- def busy(self, handler):
- self.generating = True
- self.progress.set_text("Loading...")
- self.set_busy_cursor()
- if self.image_combo_id:
- self.image_combo.disconnect(self.image_combo_id)
- self.image_combo_id = None
- self.progress.pulse()
- gobject.timeout_add (100, self.busy_idle_func)
- self.disable_widgets()
-
- def enable_widgets(self):
- self.menu.set_sensitive(True)
- self.machine_combo.set_sensitive(True)
- self.image_combo.set_sensitive(True)
- self.nb.set_sensitive(True)
- self.contents_tree.set_sensitive(True)
-
- def disable_widgets(self):
- self.menu.set_sensitive(False)
- self.machine_combo.set_sensitive(False)
- self.image_combo.set_sensitive(False)
- self.nb.set_sensitive(False)
- self.contents_tree.set_sensitive(False)
-
- def update_model(self, model):
- # We want the packages model to be alphabetised and sortable so create
- # a TreeModelSort to use in the view
- pkgsaz_model = gtk.TreeModelSort(self.model.packages_model())
- pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
- # Unset default sort func so that we only toggle between A-Z and
- # Z-A sorting
- pkgsaz_model.set_default_sort_func(None)
- self.pkgsaz_tree.set_model(pkgsaz_model)
-
- self.image_combo.set_model(self.model.images_model())
- # Without this the image combo is incorrectly sized on first load of the GUI
- self.image_combo.set_active(0)
- self.image_combo.set_active(-1)
-
- if not self.image_combo_id:
- self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
-
- # We want the contents to be alphabetised so create a TreeModelSort to
- # use in the view
- contents_model = gtk.TreeModelSort(self.model.contents_model())
- contents_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
- # Unset default sort func so that we only toggle between A-Z and
- # Z-A sorting
- contents_model.set_default_sort_func(None)
- self.contents_tree.set_model(contents_model)
- self.tasks_tree.set_model(self.model.tasks_model())
-
- if self.selected_image:
- if self.image_combo_id:
- self.image_combo.disconnect(self.image_combo_id)
- self.image_combo_id = None
- self.model.set_selected_image(self.selected_image)
- if not self.image_combo_id:
- self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
-
- if self.selected_packages:
- self.model.set_selected_packages(self.selected_packages)
-
- def reset_clicked_cb(self, button):
- lbl = "<b>Reset your selections?</b>\n\nAny new changes you have made will be lost"
- dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
- dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
- dialog.add_button("Reset", gtk.RESPONSE_OK)
- response = dialog.run()
- dialog.destroy()
- if response == gtk.RESPONSE_OK:
- self.reset_build()
- self.search.set_text("")
- self.selected_image = None
- return
-
- def reset_build(self):
- if self.image_combo_id:
- self.image_combo.disconnect(self.image_combo_id)
- self.image_combo_id = None
- self.image_combo.set_active(-1)
- self.model.reset()
- if not self.image_combo_id:
- self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb)
-
- def layers_cb(self, action):
- resp = self.layers.run()
- self.layers.save_current_layers()
- self.layers.hide()
-
- def add_layer_cb(self, action):
- self.layers.find_layer(self)
- self.layers.save_current_layers()
-
- def preferences_cb(self, action):
- resp = self.prefs.run()
- self.prefs.write_changes()
- self.prefs.hide()
-
- def about_cb(self, action):
- about = gtk.AboutDialog()
- about.set_name("Image Creator")
- about.set_copyright("Copyright (C) 2011 Intel Corporation")
- about.set_authors(["Joshua Lock <josh@linux.intel.com>"])
- about.set_logo_icon_name("applications-development")
- about.run()
- about.destroy()
-
- def save_recipe_file(self):
- rep = self.model.get_build_rep()
- rep.writeRecipe(self.save_path, self.model)
- self.dirty = False
-
- def get_save_path(self):
- chooser = gtk.FileChooserDialog(title=None, parent=self,
- action=gtk.FILE_CHOOSER_ACTION_SAVE,
- buttons=(gtk.STOCK_CANCEL,
- gtk.RESPONSE_CANCEL,
- gtk.STOCK_SAVE,
- gtk.RESPONSE_OK,))
- chooser.set_current_name("myimage.bb")
- response = chooser.run()
- if response == gtk.RESPONSE_OK:
- save_path = chooser.get_filename()
- else:
- save_path = None
- chooser.destroy()
- self.save_path = save_path
-
- def save_cb(self, action):
- if not self.save_path:
- self.get_save_path()
- if self.save_path:
- self.save_recipe_file()
-
- def save_as_cb(self, action):
- self.get_save_path()
- if self.save_path:
- self.save_recipe_file()
-
- def open_cb(self, action):
- chooser = gtk.FileChooserDialog(title=None, parent=self,
- action=gtk.FILE_CHOOSER_ACTION_OPEN,
- buttons=(gtk.STOCK_CANCEL,
- gtk.RESPONSE_CANCEL,
- gtk.STOCK_OPEN,
- gtk.RESPONSE_OK))
- response = chooser.run()
- rep = BuildRep(None, None, None)
- recipe = chooser.get_filename()
- if response == gtk.RESPONSE_OK:
- rep.loadRecipe(recipe)
- self.save_path = recipe
- self.model.load_image_rep(rep)
- self.dirty = False
- chooser.destroy()
-
- def bake_clicked_cb(self, button):
- build_image = True
-
- rep = self.model.get_build_rep()
-
- # If no base image and no user selected packages don't build anything
- if not self.selected_image and not len(rep.userpkgs):
- 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 = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
- dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
- dialog.run()
- dialog.destroy()
- return
- # Else if no base image, ask whether to just build packages or whether
- # to build a rootfs with the selected packages in
- elif not self.selected_image:
- lbl = "<b>Build empty image or only packages?</b>\nA base image"
- lbl = lbl + " has not been selected.\n\'Empty image' will build"
- lbl = lbl + " an image with only the selected packages as its"
- lbl = lbl + " contents.\n'Packages Only' will build only the"
- lbl = lbl + " selected packages, no image will be created"
- dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
- dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
- dialog.add_button("Empty Image", gtk.RESPONSE_OK)
- dialog.add_button("Packages Only", gtk.RESPONSE_YES)
- response = dialog.run()
- dialog.destroy()
- if response == gtk.RESPONSE_CANCEL:
- return
- elif response == gtk.RESPONSE_YES:
- build_image = False
- elif response == gtk.RESPONSE_OK:
- rep.base_image = "empty"
-
- # Ensure at least one value is set in IMAGE_FSTYPES.
- have_selected_fstype = False
- if (len(self.prefs.selected_image_types) and
- len(self.prefs.selected_image_types[0])):
- have_selected_fstype = True
-
- if build_image and not have_selected_fstype:
- lbl = "<b>No image output type selected</b>\nThere is no image output"
- lbl = lbl + " selected for the build. Please set an output image type"
- lbl = lbl + " in the preferences (Edit -> Preferences)."
- dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
- dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
- dialog.run()
- dialog.destroy()
- return
- elif build_image:
- self.handler.make_temp_dir()
- recipepath = self.handler.get_temp_recipe_path(rep.base_image)
- image_name = recipepath.rstrip(".bb")
- path, sep, image_name = image_name.rpartition("/")
-
- image = []
- image.append(image_name)
-
- rep.writeRecipe(recipepath, self.model)
- # In the case where we saved the file for the purpose of building
- # it we should then delete it so that the users workspace doesn't
- # contain files they haven't explicitly saved there.
- if not self.save_path:
- self.files_to_clean.append(recipepath)
-
- self.handler.build_targets(image, self.configurator)
- else:
- self.handler.build_targets(self.model.get_selected_pn(), self.configurator, "packages")
-
- # Disable parts of the menu which shouldn't be used whilst building
- self.set_menus_sensitive(False)
- self.nb.set_current_page(1)
-
- def set_menus_sensitive(self, sensitive):
- self.add_layers_action.set_sensitive(sensitive)
- self.layers_action.set_sensitive(sensitive)
- self.prefs_action.set_sensitive(sensitive)
- self.open_action.set_sensitive(sensitive)
-
- def back_button_clicked_cb(self, button):
- self.toggle_createview()
-
- def toggle_createview(self):
- self.set_menus_sensitive(True)
- self.build.reset()
- self.nb.set_current_page(0)
-
- def build_complete_cb(self, running_build):
- # Have the handler process BB events again
- self.handler.building = False
- self.stopping = False
- self.back.connect("clicked", self.back_button_clicked_cb)
- self.back.set_sensitive(True)
- self.cancel.set_sensitive(False)
- for f in self.files_to_clean:
- try:
- os.remove(f)
- except OSError:
- pass
- self.files_to_clean.remove(f)
- self.files_to_clean = []
-
- lbl = "<b>Build completed</b>\n\nClick 'Edit Image' to start another build or 'View Messages' to view the messages output during the build."
- if self.handler.build_type == "image" and self.build_succeeded:
- deploy = self.handler.get_image_deploy_dir()
- lbl = lbl + "\n<a href=\"file://%s\" title=\"%s\">Browse folder of built images</a>." % (deploy, deploy)
-
- dialog = CrumbsDialog(self, lbl)
- dialog.add_button("View Messages", gtk.RESPONSE_CANCEL)
- dialog.add_button("Edit Image", gtk.RESPONSE_OK)
- response = dialog.run()
- dialog.destroy()
- if response == gtk.RESPONSE_OK:
- self.toggle_createview()
-
- def build_started_cb(self, running_build):
- self.back.set_sensitive(False)
- self.cancel.set_sensitive(True)
-
- def include_gplv3_cb(self, toggle):
- excluded = toggle.get_active()
- self.handler.toggle_gplv3(excluded)
-
- def change_bb_threads(self, spinner):
- val = spinner.get_value_as_int()
- self.handler.set_bbthreads(val)
-
- def change_make_threads(self, spinner):
- val = spinner.get_value_as_int()
- self.handler.set_pmake(val)
-
- def toggle_toolchain(self, check):
- enabled = check.get_active()
- self.handler.toggle_toolchain(enabled)
-
- def toggle_headers(self, check):
- enabled = check.get_active()
- self.handler.toggle_toolchain_headers(enabled)
-
- def toggle_package_idle_cb(self, opath, image):
- """
- As the operations which we're calling on the model can take
- a significant amount of time (in the order of seconds) during which
- the GUI is unresponsive as the main loop is blocked perform them in
- an idle function which at least enables us to set the busy cursor
- before the UI is blocked giving the appearance of being responsive.
- """
- # Whether the item is currently included
- inc = self.model[opath][self.model.COL_INC]
- # FIXME: due to inpredictability of the removal of packages we are
- # temporarily disabling this feature
- # If the item is already included, mark it for removal then
- # the sweep_up() method finds affected items and marks them
- # appropriately
- # if inc:
- # self.model.mark(opath)
- # self.model.sweep_up()
- # # If the item isn't included, mark it for inclusion
- # else:
- if not inc:
- self.model.include_item(item_path=opath,
- binb="User Selected",
- image_contents=image)
-
- self.set_busy_cursor(False)
+def event_handle_idle_func(eventHandler, hobHandler):
+ # Consume as many messages as we can in the time available to us
+ if not eventHandler:
return False
+ event = eventHandler.getEvent()
+ while event:
+ hobHandler.handle_event(event)
+ event = eventHandler.getEvent()
+ return True
+
+def main (server = None, eventHandler = None):
+ bitbake_server = None
+ client_addr = None
+ server_addr = None
+
+ if not eventHandler:
+ helper = uihelper.BBUIHelper()
+ server, eventHandler, server_addr, client_addr = helper.findServerDetails()
+ bitbake_server = server
- def toggle_package(self, path, model, image=False):
- inc = model[path][self.model.COL_INC]
- # Warn user before removing included packages
- if inc:
- # FIXME: due to inpredictability of the removal of packages we are
- # temporarily disabling this feature
- return
- # pn = model[path][self.model.COL_NAME]
- # revdeps = self.model.find_reverse_depends(pn)
- # if len(revdeps):
- # lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone and all packages which depend on this will be removed\nPackages which depend on %s include %s." % (pn, pn, ", ".join(revdeps).rstrip(","))
- # else:
- # lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone." % pn
- # dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING)
- # dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
- # dialog.add_button("Remove", gtk.RESPONSE_OK)
- # response = dialog.run()
- # dialog.destroy()
- # if response == gtk.RESPONSE_CANCEL:
- # return
-
- self.set_busy_cursor()
- # Convert path to path in original model
- opath = model.convert_path_to_child_path(path)
- # This is a potentially length call which can block the
- # main loop, therefore do the work in an idle func to keep
- # the UI responsive
- glib.idle_add(self.toggle_package_idle_cb, opath, image)
-
- self.dirty = True
-
- def toggle_include_cb(self, cell, path, tv):
- model = tv.get_model()
- self.toggle_package(path, model)
-
- def toggle_pkg_include_cb(self, cell, path, tv):
- # there's an extra layer of models in the packages case.
- sort_model = tv.get_model()
- cpath = sort_model.convert_path_to_child_path(path)
- self.toggle_package(cpath, sort_model.get_model())
-
- def pkgsaz(self):
- vbox = gtk.VBox(False, 6)
- vbox.show()
- self.pkgsaz_tree = gtk.TreeView()
- self.pkgsaz_tree.set_headers_visible(True)
- self.pkgsaz_tree.set_headers_clickable(True)
- self.pkgsaz_tree.set_enable_search(True)
- self.pkgsaz_tree.set_search_column(0)
- self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
-
- col = gtk.TreeViewColumn('Package')
- col.set_clickable(True)
- col.set_sort_column_id(self.model.COL_NAME)
- col.set_min_width(220)
- col1 = gtk.TreeViewColumn('Description')
- col1.set_resizable(True)
- col1.set_min_width(360)
- col2 = gtk.TreeViewColumn('License')
- col2.set_resizable(True)
- col2.set_clickable(True)
- col2.set_sort_column_id(self.model.COL_LIC)
- col2.set_min_width(170)
- col3 = gtk.TreeViewColumn('Group')
- col3.set_clickable(True)
- col3.set_sort_column_id(self.model.COL_GROUP)
- col4 = gtk.TreeViewColumn('Included')
- col4.set_min_width(80)
- col4.set_max_width(90)
- col4.set_sort_column_id(self.model.COL_INC)
-
- self.pkgsaz_tree.append_column(col)
- self.pkgsaz_tree.append_column(col1)
- self.pkgsaz_tree.append_column(col2)
- self.pkgsaz_tree.append_column(col3)
- self.pkgsaz_tree.append_column(col4)
-
- cell = gtk.CellRendererText()
- cell1 = gtk.CellRendererText()
- cell1.set_property('width-chars', 20)
- cell2 = gtk.CellRendererText()
- cell2.set_property('width-chars', 20)
- cell3 = gtk.CellRendererText()
- cell4 = gtk.CellRendererToggle()
- cell4.set_property('activatable', True)
- cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsaz_tree)
-
- col.pack_start(cell, True)
- col1.pack_start(cell1, True)
- col2.pack_start(cell2, True)
- col3.pack_start(cell3, True)
- col4.pack_end(cell4, True)
-
- col.set_attributes(cell, text=self.model.COL_NAME)
- col1.set_attributes(cell1, text=self.model.COL_DESC)
- col2.set_attributes(cell2, text=self.model.COL_LIC)
- col3.set_attributes(cell3, text=self.model.COL_GROUP)
- col4.set_attributes(cell4, active=self.model.COL_INC)
-
- self.pkgsaz_tree.show()
-
- scroll = gtk.ScrolledWindow()
- scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
- scroll.set_shadow_type(gtk.SHADOW_IN)
- scroll.add(self.pkgsaz_tree)
- vbox.pack_start(scroll, True, True, 0)
-
- hb = gtk.HBox(False, 0)
- hb.show()
- self.search = gtk.Entry()
- self.search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
- self.search.connect("icon-release", self.search_entry_clear_cb)
- self.search.show()
- self.pkgsaz_tree.set_search_entry(self.search)
- hb.pack_end(self.search, False, False, 0)
- label = gtk.Label("Search packages:")
- label.show()
- hb.pack_end(label, False, False, 6)
- vbox.pack_start(hb, False, False, 0)
-
- return vbox
-
- def search_entry_clear_cb(self, entry, icon_pos, event):
- entry.set_text("")
-
- def tasks(self):
- vbox = gtk.VBox(False, 6)
- vbox.show()
- self.tasks_tree = gtk.TreeView()
- self.tasks_tree.set_headers_visible(True)
- self.tasks_tree.set_headers_clickable(False)
- self.tasks_tree.set_enable_search(True)
- self.tasks_tree.set_search_column(0)
- self.tasks_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
-
- col = gtk.TreeViewColumn('Package Collection')
- col.set_min_width(430)
- col1 = gtk.TreeViewColumn('Description')
- col1.set_min_width(430)
- col2 = gtk.TreeViewColumn('Include')
- col2.set_min_width(70)
- col2.set_max_width(80)
-
- self.tasks_tree.append_column(col)
- self.tasks_tree.append_column(col1)
- self.tasks_tree.append_column(col2)
-
- cell = gtk.CellRendererText()
- cell1 = gtk.CellRendererText()
- cell2 = gtk.CellRendererToggle()
- cell2.set_property('activatable', True)
- cell2.connect("toggled", self.toggle_include_cb, self.tasks_tree)
-
- col.pack_start(cell, True)
- col1.pack_start(cell1, True)
- col2.pack_end(cell2, True)
-
- col.set_attributes(cell, text=self.model.COL_NAME)
- col1.set_attributes(cell1, text=self.model.COL_DESC)
- col2.set_attributes(cell2, active=self.model.COL_INC)
-
- self.tasks_tree.show()
-
- scroll = gtk.ScrolledWindow()
- scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
- scroll.set_shadow_type(gtk.SHADOW_IN)
- scroll.add(self.tasks_tree)
- vbox.pack_start(scroll, True, True, 0)
-
- hb = gtk.HBox(False, 0)
- hb.show()
- search = gtk.Entry()
- search.show()
- self.tasks_tree.set_search_entry(search)
- hb.pack_end(search, False, False, 0)
- label = gtk.Label("Search collections:")
- label.show()
- hb.pack_end(label, False, False, 6)
- vbox.pack_start(hb, False, False, 0)
-
- return vbox
-
- def cancel_build(self, button):
- 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 = CrumbsDialog(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 = CrumbsDialog(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)
-
- def view_build_gui(self):
- vbox = gtk.VBox(False, 12)
- vbox.set_border_width(6)
- vbox.show()
- build_tv = RunningBuildTreeView(readonly=True)
- build_tv.show()
- build_tv.set_model(self.build.model)
- self.build.model.connect("row-inserted", self.scroll_tv_cb, build_tv)
- scrolled_view = gtk.ScrolledWindow ()
- scrolled_view.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- scrolled_view.add(build_tv)
- scrolled_view.show()
- vbox.pack_start(scrolled_view, expand=True, fill=True)
- hbox = gtk.HBox(False, 12)
- hbox.show()
- vbox.pack_start(hbox, expand=False, fill=False)
- self.back = gtk.Button("Back")
- self.back.show()
- self.back.set_sensitive(False)
- hbox.pack_start(self.back, expand=False, fill=False)
- self.cancel = gtk.Button("Stop Build")
- self.cancel.connect("clicked", self.cancel_build)
- self.cancel.show()
- hbox.pack_end(self.cancel, expand=False, fill=False)
-
- return vbox
-
- def create_menu(self):
- menu_items = '''<ui>
- <menubar name="MenuBar">
- <menu action="File">
- <menuitem action="Save"/>
- <menuitem action="Save As"/>
- <menuitem action="Open"/>
- <separator/>
- <menuitem action="AddLayer" label="Add Layer"/>
- <separator/>
- <menuitem action="Quit"/>
- </menu>
- <menu action="Edit">
- <menuitem action="Layers" label="Layers"/>
- <menuitem action="Preferences"/>
- </menu>
- <menu action="Help">
- <menuitem action="About"/>
- </menu>
- </menubar>
- </ui>'''
-
- uimanager = gtk.UIManager()
- accel = uimanager.get_accel_group()
- self.add_accel_group(accel)
-
- actions = gtk.ActionGroup('ImageCreator')
- self.actions = actions
- actions.add_actions([('Quit', gtk.STOCK_QUIT, None, None, None, self.menu_quit,),
- ('File', None, '_File'),
- ('Save', gtk.STOCK_SAVE, None, None, None, self.save_cb),
- ('Save As', gtk.STOCK_SAVE_AS, None, None, None, self.save_as_cb),
- ('Edit', None, '_Edit'),
- ('Help', None, '_Help'),
- ('About', gtk.STOCK_ABOUT, None, None, None, self.about_cb)])
-
- self.add_layers_action = gtk.Action('AddLayer', 'Add Layer', None, None)
- self.add_layers_action.connect("activate", self.add_layer_cb)
- self.actions.add_action(self.add_layers_action)
- self.layers_action = gtk.Action('Layers', 'Layers', None, None)
- self.layers_action.connect("activate", self.layers_cb)
- self.actions.add_action(self.layers_action)
- self.prefs_action = gtk.Action('Preferences', 'Preferences', None, None)
- self.prefs_action.connect("activate", self.preferences_cb)
- self.actions.add_action(self.prefs_action)
- self.open_action = gtk.Action('Open', 'Open', None, None)
- self.open_action.connect("activate", self.open_cb)
- self.actions.add_action(self.open_action)
-
- uimanager.insert_action_group(actions, 0)
- uimanager.add_ui_from_string(menu_items)
-
- menubar = uimanager.get_widget('/MenuBar')
- menubar.show_all()
-
- return menubar
-
- def info_button_clicked_cb(self, button):
- info = "We cannot accurately predict the image contents before they are built so instead a best"
- info = info + " attempt at estimating what the image will contain is listed."
- dialog = CrumbsDialog(self, info, gtk.STOCK_DIALOG_INFO)
- dialog.add_buttons(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
- resp = dialog.run()
- dialog.destroy()
-
- def create_build_gui(self):
- vbox = gtk.VBox(False, 12)
- vbox.set_border_width(6)
- vbox.show()
-
- hbox = gtk.HBox(False, 12)
- hbox.show()
- vbox.pack_start(hbox, expand=False, fill=False)
-
- label = gtk.Label("Machine:")
- label.show()
- hbox.pack_start(label, expand=False, fill=False, padding=6)
- self.machine_combo = gtk.combo_box_new_text()
- self.machine_combo.show()
- self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.")
- hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6)
- label = gtk.Label("Base image:")
- label.show()
- hbox.pack_start(label, expand=False, fill=False, padding=6)
- self.image_combo = gtk.ComboBox()
- self.image_combo.show()
- self.image_combo.set_tooltip_text("Selects the image on which to base the created image")
- image_combo_cell = gtk.CellRendererText()
- self.image_combo.pack_start(image_combo_cell, True)
- self.image_combo.add_attribute(image_combo_cell, 'text', self.model.COL_NAME)
- hbox.pack_start(self.image_combo, expand=False, fill=False, padding=6)
- self.progress = gtk.ProgressBar()
- self.progress.set_size_request(250, -1)
- hbox.pack_end(self.progress, expand=False, fill=False, padding=6)
-
- ins = gtk.Notebook()
- vbox.pack_start(ins, expand=True, fill=True)
- ins.set_show_tabs(True)
- label = gtk.Label("Packages")
- label.show()
- ins.append_page(self.pkgsaz(), tab_label=label)
- label = gtk.Label("Package Collections")
- label.show()
- ins.append_page(self.tasks(), tab_label=label)
- ins.set_current_page(0)
- ins.show_all()
-
- hbox = gtk.HBox(False, 1)
- hbox.show()
- label = gtk.Label("Estimated image contents:")
- self.model.connect("contents-changed", self.update_package_count_cb, label)
- label.set_property("xalign", 0.00)
- label.show()
- hbox.pack_start(label, expand=False, fill=False, padding=6)
- info = gtk.Button("?")
- info.set_tooltip_text("What does this mean?")
- info.show()
- info.connect("clicked", self.info_button_clicked_cb)
- hbox.pack_start(info, expand=False, fill=False, padding=6)
- vbox.pack_start(hbox, expand=False, fill=False, padding=6)
- con = self.contents()
- con.show()
- vbox.pack_start(con, expand=True, fill=True)
-
- bbox = gtk.HButtonBox()
- bbox.set_spacing(12)
- bbox.set_layout(gtk.BUTTONBOX_END)
- bbox.show()
- vbox.pack_start(bbox, expand=False, fill=False)
- reset = gtk.Button("Reset")
- reset.connect("clicked", self.reset_clicked_cb)
- reset.show()
- bbox.add(reset)
- bake = gtk.Button("Bake")
- bake.connect("clicked", self.bake_clicked_cb)
- bake.show()
- bbox.add(bake)
-
- return vbox
-
- def update_package_count_cb(self, model, count, label):
- lbl = "Estimated image contents (%s packages):" % count
- label.set_text(lbl)
-
- def contents(self):
- self.contents_tree = gtk.TreeView()
- self.contents_tree.set_headers_visible(True)
- self.contents_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
-
- # allow searching in the package column
- self.contents_tree.set_search_column(0)
- self.contents_tree.set_enable_search(True)
-
- col = gtk.TreeViewColumn('Package')
- col.set_sort_column_id(0)
- col.set_min_width(430)
- col1 = gtk.TreeViewColumn('Brought in by')
- col1.set_resizable(True)
- col1.set_min_width(430)
-
- self.contents_tree.append_column(col)
- self.contents_tree.append_column(col1)
-
- cell = gtk.CellRendererText()
- cell1 = gtk.CellRendererText()
- cell1.set_property('width-chars', 20)
-
- col.pack_start(cell, True)
- col1.pack_start(cell1, True)
-
- col.set_attributes(cell, text=self.model.COL_NAME)
- col1.set_attributes(cell1, text=self.model.COL_BINB)
-
- self.contents_tree.show()
-
- scroll = gtk.ScrolledWindow()
- scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
- scroll.set_shadow_type(gtk.SHADOW_IN)
- scroll.add(self.contents_tree)
-
- return scroll
-
-def main (server, eventHandler):
gobject.threads_init()
- # NOTE: For now we require that the user run with pre and post files to
- # read and store configuration set in the GUI.
- # We hope to adjust this long term as tracked in Yocto Bugzilla #1441
- # http://bugzilla.pokylinux.org/show_bug.cgi?id=1441
- reqfiles = 0
- dep_files = server.runCommand(["getVariable", "__depends"]) or set()
- dep_files.union(server.runCommand(["getVariable", "__base_depends"]) or set())
- for f in dep_files:
- if f[0].endswith("hob-pre.conf"):
- reqfiles = reqfiles + 1
- elif f[0].endswith("hob-post.conf"):
- reqfiles = reqfiles + 1
- if reqfiles == 2:
- break
- if reqfiles < 2:
- print("""The hob UI requires a pre file named hob-pre.conf and a post
-file named hob-post.conf to store and read its configuration from. Please run
-hob with these files, i.e.\n
-\bitbake -u hob -r conf/hob-pre.conf -R conf/hob-post.conf""")
- return
+ # That indicates whether the Hob and the bitbake server are
+ # running on different machines
+ # recipe model and package model
+ recipe_model = RecipeListModel()
+ package_model = PackageListModel()
- taskmodel = TaskListModel()
- configurator = Configurator()
- handler = HobHandler(taskmodel, server)
- mach = server.runCommand(["getVariable", "MACHINE"])
- sdk_mach = server.runCommand(["getVariable", "SDKMACHINE"])
- # If SDKMACHINE not set the default SDK_ARCH is used so we
- # should represent that in the GUI
- if not sdk_mach:
- sdk_mach = server.runCommand(["getVariable", "SDK_ARCH"])
- distro = server.runCommand(["getVariable", "DISTRO"])
- if not distro:
- distro = "defaultsetup"
- bbthread = server.runCommand(["getVariable", "BB_NUMBER_THREADS"])
- if not bbthread:
- bbthread = 1
- else:
- bbthread = int(bbthread)
- pmake = server.runCommand(["getVariable", "PARALLEL_MAKE"])
- if not pmake:
- pmake = 1
- else:
- # The PARALLEL_MAKE variable will be of the format: "-j 3" and we only
- # want a number for the spinner, so strip everything from the variable
- # up to and including the space
- pmake = int(pmake.lstrip("-j "))
-
- selected_image_types = server.runCommand(["getVariable", "IMAGE_FSTYPES"])
- all_image_types = server.runCommand(["getVariable", "IMAGE_TYPES"])
-
- pclasses = server.runCommand(["getVariable", "PACKAGE_CLASSES"]).split(" ")
- # NOTE: we're only supporting one value for PACKAGE_CLASSES being set
- # this seems OK because we're using the first package format set in
- # PACKAGE_CLASSES and that's the package manager used for the rootfs
- pkg, sep, pclass = pclasses[0].rpartition("_")
-
- incompatible = server.runCommand(["getVariable", "INCOMPATIBLE_LICENSE"])
- gplv3disabled = False
- if incompatible and incompatible.lower().find("gplv3") != -1:
- gplv3disabled = True
-
- build_toolchain = bool(server.runCommand(["getVariable", "HOB_BUILD_TOOLCHAIN"]))
- handler.toggle_toolchain(build_toolchain)
- build_headers = bool(server.runCommand(["getVariable", "HOB_BUILD_TOOLCHAIN_HEADERS"]))
- handler.toggle_toolchain_headers(build_headers)
-
- prefs = HobPrefs(configurator, handler, sdk_mach, distro, pclass,
- pmake, bbthread, selected_image_types, all_image_types,
- gplv3disabled, build_toolchain, build_headers)
- layers = LayerEditor(configurator, None)
- window = MainWindow(taskmodel, handler, configurator, prefs, layers, mach)
- prefs.set_parent_window(window)
- layers.set_parent_window(window)
- window.show_all ()
- handler.connect("machines-updated", window.update_machines)
- handler.connect("sdk-machines-updated", prefs.update_sdk_machines)
- handler.connect("distros-updated", prefs.update_distros)
- handler.connect("package-formats-found", prefs.update_package_formats)
- handler.connect("generating-data", window.busy)
- handler.connect("data-generated", window.data_generated)
- handler.connect("reload-triggered", window.reload_triggered_cb)
- configurator.connect("layers-loaded", layers.load_current_layers)
- configurator.connect("layers-changed", handler.reload_data)
- handler.connect("config-found", configurator.configFound)
- handler.connect("fatal-error", window.fatal_error_cb)
-
- try:
- # kick the while thing off
- handler.current_command = handler.CFG_PATH_LOCAL
- server.runCommand(["findConfigFilePath", "local.conf"])
- except xmlrpclib.Fault:
- print("XMLRPC Fault getting commandline:\n %s" % x)
+ hobHandler = HobHandler(bitbake_server, server_addr, client_addr, recipe_model, package_model)
+ if hobHandler.kick() == False:
return 1
+ builder = Builder(hobHandler, recipe_model, package_model)
# This timeout function regularly probes the event queue to find out if we
# have any messages waiting for us.
- gobject.timeout_add (100,
- handler.event_handle_idle_func,
- eventHandler,
- window.build,
- window.progress)
+ gobject.timeout_add(10, event_handle_idle_func, eventHandler, hobHandler)
try:
gtk.main()
@@ -1107,5 +80,13 @@ hob with these files, i.e.\n
if ioerror.args[0] == 4:
pass
finally:
- server.runCommand(["stateStop"])
+ hobHandler.cancel_build(force = True)
+if __name__ == "__main__":
+ try:
+ ret = main()
+ except Exception:
+ ret = 1
+ import traceback
+ traceback.print_exc(15)
+ sys.exit(ret)