Latest round of hob UI improvements
cover Latest round of hob UI improvements
Highlights are using buildTargets rather than buildFiles to build the custom image and enabling building an image without a 'Base image' set. Plus a slew of more minor fixes.
The following changes since commit d11f9dd33cdcc97a4a937e8bf7e97558d813cadd:
cooker: only append files once (2011-07-26 13:45:31 +0100)
are available in the git repository at:
git://github.com/incandescant/bitbake hob
https://github.com/incandescant/bitbake/tree/hob
Jessica Zhang (1):
ui/crumbs/tasklistmodel: fix loading a saved recipe
Joshua Lock (9):
ui/hob: replace the ugly static command map ui/crumbs/hobeventhandler: reparse files before running other
commands
ui/hob: switch from buildFile to buildTargets for custom image builds ui/hob: don't offer to show built output if build fails ui/hob: change wording in build complete dialog
ui/hob: Force the 'Base image combo' to be drawn correctly ui/hob: enable building an image with minimal contents ui/crumbs/tasklistmodel: handle items added in by base image being
removed
ui/crumbs/tasklistmodel: don't iterate whole model in
find_alt_dependency()
lib/bb/ui/crumbs/configurator.py | 18 +++++ lib/bb/ui/crumbs/hobeventhandler.py | 127 +++++++++++++++++++++++------------ lib/bb/ui/crumbs/tasklistmodel.py | 51 ++++++++++---- lib/bb/ui/hob.py | 63 +++++++++++------ 4 files changed, 179 insertions(+), 80 deletions(-)
Signature · 1 line
1.7.6
PATCH 1/10 ui/hob: replace the ugly static command map
The command_map was never a good idea, what's implemented here is a fraction less ugly but a significant factor more readable and therefore easy to maintain.
The method implemented in this patch also has the advantage of not being static meaning we can determine the desired runCommand arguments dynamically at call time.
--- lib/bb/ui/crumbs/hobeventhandler.py | 60 ++++++++++++++++++++--------------- lib/bb/ui/hob.py | 2 +- 2 files changed, 35 insertions(+), 27 deletions(-)
lib/bb/ui/crumbs/hobeventhandler.py +34 -26
@@ -61,8 +61,11 @@ class HobHandler(gobject.GObject): gobject.TYPE_STRING)), } + (CFG_PATH_LOCAL, CFG_PATH_HOB, CFG_PATH_LAYERS, CFG_FILES_DISTRO, CFG_FILES_MACH, CFG_FILES_SDK, FILES_MATCH_CLASS, GENERATE_TGTS) = range(8) + def __init__(self, taskmodel, server): gobject.GObject.__init__(self) + self.current_command = None self.building = None self.gplv3_excluded = False
@@ -76,30 +79,37 @@ class HobHandler(gobject.GObject): self.image_output_types = self.server.runCommand(["getVariable", "IMAGE_FSTYPES"]).split(" ") - self.command_map = { - "findConfigFilePathLocal" : ("findConfigFilePath", ["hob.local.conf"], "findConfigFilePathHobLocal"), - "findConfigFilePathHobLocal" : ("findConfigFilePath", ["bblayers.conf"], "findConfigFilePathLayers"), - "findConfigFilePathLayers" : ("findConfigFiles", ["DISTRO"], "findConfigFilesDistro"), - "findConfigFilesDistro" : ("findConfigFiles", ["MACHINE"], "findConfigFilesMachine"), - "findConfigFilesMachine" : ("findConfigFiles", ["MACHINE-SDK"], "findConfigFilesSdkMachine"), - "findConfigFilesSdkMachine" : ("findFilesMatchingInDir", ["rootfs_", "classes"], "findFilesMatchingPackage"), - "findFilesMatchingPackage" : ("generateTargetsTree", ["classes/image.bbclass"], None), - "generateTargetsTree" : (None, [], None), - } - def run_next_command(self): - # FIXME: this is ugly and I *will* replace it - if self.current_command: - if not self.generating: - self.emit("generating-data") - self.generating = True - next_cmd = self.command_map[self.current_command] - command = next_cmd[0] - argument = next_cmd[1] - self.current_command = next_cmd[2] - args = [command] - args.extend(argument) - self.server.runCommand(args) + if self.current_command and not self.generating: + self.emit("generating-data") + self.generating = True + + if self.current_command == self.CFG_PATH_LOCAL: + self.current_command = self.CFG_PATH_HOB + self.server.runCommand(["findConfigFilePath", "hob.local.conf"]) + elif self.current_command == self.CFG_PATH_HOB: + self.current_command = self.CFG_PATH_LAYERS + self.server.runCommand(["findConfigFilePath", "bblayers.conf"]) + elif self.current_command == self.CFG_PATH_LAYERS: + self.current_command = self.CFG_FILES_DISTRO + self.server.runCommand(["findConfigFiles", "DISTRO"]) + elif self.current_command == self.CFG_FILES_DISTRO: + self.current_command = self.CFG_FILES_MACH + self.server.runCommand(["findConfigFiles", "MACHINE"]) + elif self.current_command == self.CFG_FILES_MACH: + self.current_command = self.CFG_FILES_SDK + self.server.runCommand(["findConfigFiles", "MACHINE-SDK"]) + elif self.current_command == self.CFG_FILES_SDK: + self.current_command = self.FILES_MATCH_CLASS + self.server.runCommand(["findFilesMatchingInDir", "rootfs_", "classes"]) + elif self.current_command == self.FILES_MATCH_CLASS: + self.current_command = self.GENERATE_TGTS + self.server.runCommand(["generateTargetsTree", "classes/image.bbclass"]) + elif self.current_command == self.GENERATE_TGTS: + if self.generating: + self.emit("data-generated") + self.generating = False + self.current_command = None def handle_event(self, event, running_build, pbar): if not event:
@@ -109,8 +119,6 @@ class HobHandler(gobject.GObject): if self.building: running_build.handle_event(event) elif isinstance(event, bb.event.TargetsTreeGenerated): - self.emit("data-generated") - self.generating = False if event._model: self.model.populate(event._model) elif isinstance(event, bb.event.ConfigFilesFound):
@@ -188,7 +196,7 @@ class HobHandler(gobject.GObject): selected_packages, _ = self.model.get_selected_packages() self.emit("reload-triggered", img, " ".join(selected_packages)) self.server.runCommand(["reparseFiles"]) - self.current_command = "findConfigFilePathLayers" + self.current_command = self.CFG_PATH_LAYERS self.run_next_command() def set_bbthreads(self, threads):
lib/bb/ui/hob.py +1 -1
@@ -929,7 +929,7 @@ def main (server, eventHandler): try: # kick the while thing off - handler.current_command = "findConfigFilePathLocal" + handler.current_command = handler.CFG_PATH_LOCAL server.runCommand(["findConfigFilePath", "local.conf"]) except xmlrpclib.Fault: print("XMLRPC Fault getting commandline:\n %s" % x)
Signature · 1 line
1.7.6
PATCH 2/10 ui/crumbs/hobeventhandler: reparse files before running other commands
In reply to Joshua LockIntegrate reparseFiles into the run_next_command() method rather than calling reparseFiles on the server and immediately calling other methods.
--- lib/bb/ui/crumbs/hobeventhandler.py | 8 +++++--- 1 files changed, 5 insertions(+), 3 deletions(-)
lib/bb/ui/crumbs/hobeventhandler.py +5 -3
@@ -61,7 +61,7 @@ class HobHandler(gobject.GObject): gobject.TYPE_STRING)), } - (CFG_PATH_LOCAL, CFG_PATH_HOB, CFG_PATH_LAYERS, CFG_FILES_DISTRO, CFG_FILES_MACH, CFG_FILES_SDK, FILES_MATCH_CLASS, GENERATE_TGTS) = range(8) + (CFG_PATH_LOCAL, CFG_PATH_HOB, CFG_PATH_LAYERS, CFG_FILES_DISTRO, CFG_FILES_MACH, CFG_FILES_SDK, FILES_MATCH_CLASS, GENERATE_TGTS, REPARSE_FILES) = range(9) def __init__(self, taskmodel, server): gobject.GObject.__init__(self)
@@ -110,6 +110,9 @@ class HobHandler(gobject.GObject): self.emit("data-generated") self.generating = False self.current_command = None + elif self.current_command == self.REPARSE_FILES: + self.current_command = self.CFG_PATH_LAYERS + self.server.runCommand(["reparseFiles"]) def handle_event(self, event, running_build, pbar): if not event:
@@ -195,8 +198,7 @@ class HobHandler(gobject.GObject): img = self.model.selected_image selected_packages, _ = self.model.get_selected_packages() self.emit("reload-triggered", img, " ".join(selected_packages)) - self.server.runCommand(["reparseFiles"]) - self.current_command = self.CFG_PATH_LAYERS + self.current_command = self.REPARSE_FILES self.run_next_command() def set_bbthreads(self, threads):
Signature · 1 line
1.7.6
PATCH 3/10 ui/hob: switch from buildFile to buildTargets for custom image builds
In reply to Joshua LockWe need to use the buildTargets command to ensure dependencies, such as native tools to build the rootfs, are correctly included. This patch achieves this by modifying BBPATH and BBFILES to include matches for the location of the generated recipe file and reparsing the metadata before calling buildTargets.
Fixes [YOCTO #1228]
--- lib/bb/ui/crumbs/configurator.py | 18 +++++++++ lib/bb/ui/crumbs/hobeventhandler.py | 67 +++++++++++++++++++++++++--------- lib/bb/ui/hob.py | 24 +++++------- 3 files changed, 77 insertions(+), 32 deletions(-)
lib/bb/ui/crumbs/configurator.py +18 -0
@@ -244,6 +244,24 @@ class Configurator(gobject.GObject): del self.orig_config self.orig_config = copy.deepcopy(self.config) + def insertTempBBPath(self, bbpath, bbfiles): + # Create a backup of the local.conf + bkup = "%s~" % self.local + os.rename(self.local, bkup) + + # read the original conf into a list + with open(bkup, 'r') as config: + config_lines = config.readlines() + + if bbpath: + config_lines.append("BBPATH := \"${BBPATH}:%s\"\n" % bbpath) + if bbfiles: + config_lines.append("BBFILES := \"${BBFILES} %s\"\n" % bbfiles) + + # Write the updated lines list object to the local.conf + with open(self.local, "w") as n: + n.write("".join(config_lines)) + def writeLayerConf(self): # If we've not added/removed new layers don't write if not self._isLayerConfDirty():
lib/bb/ui/crumbs/hobeventhandler.py +49 -18
100 lines not on this page · raw message
lib/bb/ui/hob.py +10 -14
@@ -65,10 +65,8 @@ class MainWindow (gtk.Window): self.build = RunningBuild() self.build.connect("build-failed", self.running_build_failed_cb) - self.build.connect("build-complete", self.handler.build_complete_cb) self.build.connect("build-started", self.build_started_cb) - - self.handler.connect("build-complete", self.build_complete_cb) + self.build.connect("build-complete", self.build_complete_cb) vbox = gtk.VBox(False, 0) vbox.set_border_width(0)
@@ -373,16 +371,15 @@ class MainWindow (gtk.Window): dialog.destroy() if response == gtk.RESPONSE_CANCEL: return - else: - # TODO: show a confirmation dialog ? - if not self.save_path: - import tempfile, datetime - image_name = "hob-%s-variant-%s.bb" % (rep.base_image, datetime.date.today().isoformat()) - image_dir = os.path.join(tempfile.gettempdir(), 'hob-images') - bb.utils.mkdirhier(image_dir) - recipepath = os.path.join(image_dir, image_name) else: - recipepath = self.save_path + self.handler.build_packages(rep.allpkgs.split(" ")) + else: + import tempfile, datetime + image_name = "hob-%s-variant-%s" % (rep.base_image, datetime.date.today().isoformat()) + image_file = "%s.bb" % (image_name) + image_dir = os.path.join(tempfile.gettempdir(), 'hob-images') + bb.utils.mkdirhier(image_dir) + recipepath = os.path.join(image_dir, image_file) rep.writeRecipe(recipepath, self.model) # In the case where we saved the file for the purpose of building
@@ -391,9 +388,8 @@ class MainWindow (gtk.Window): if not self.save_path: self.files_to_clean.append(recipepath) - self.handler.queue_image_recipe_path(recipepath) + self.handler.build_image(image_name, image_dir, self.configurator) - self.handler.build_packages(rep.allpkgs.split(" ")) self.nb.set_current_page(1) def back_button_clicked_cb(self, button):
Signature · 1 line
1.7.6
PATCH 4/10 ui/hob: don't offer to show built output if build fails
In reply to Joshua LockThe link to open the deploy directory should only be shown if the build completed succesfully.
--- lib/bb/ui/hob.py | 10 +++++++--- 1 files changed, 7 insertions(+), 3 deletions(-)
lib/bb/ui/hob.py +7 -3
@@ -57,6 +57,7 @@ class MainWindow (gtk.Window): 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")
@@ -65,6 +66,7 @@ class MainWindow (gtk.Window): self.build = RunningBuild() 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)
@@ -113,9 +115,11 @@ class MainWindow (gtk.Window): 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): - # FIXME: handle this - print("Build failed") + self.build_succeeded = False def image_changed_string_cb(self, model, new_image): cnt = 0
@@ -408,7 +412,7 @@ class MainWindow (gtk.Window): os.remove(f) lbl = "<b>Build completed</b>\n\nClick 'Edit Image' to start another build or 'View Log' to view the build log." - if self.handler.building == "image": + if self.handler.building == "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)
Signature · 1 line
1.7.6
PATCH 5/10 ui/hob: change wording in build complete dialog
In reply to Joshua LockThe 'View Log' button is potentially confusing to existing users of the system who may be expecting to be shown the on disk logs of the build. Instead use 'View Messages'.
Addresses [YOCTO #1222]
--- lib/bb/ui/hob.py | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
lib/bb/ui/hob.py +2 -2
@@ -411,13 +411,13 @@ class MainWindow (gtk.Window): for f in self.files_to_clean: os.remove(f) - lbl = "<b>Build completed</b>\n\nClick 'Edit Image' to start another build or 'View Log' to view the build log." + 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.building == "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 Log", gtk.RESPONSE_CANCEL) + dialog.add_button("View Messages", gtk.RESPONSE_CANCEL) dialog.add_button("Edit Image", gtk.RESPONSE_OK) response = dialog.run() dialog.destroy()
Signature · 1 line
1.7.6
PATCH 6/10 ui/hob: Force the 'Base image combo' to be drawn correctly
In reply to Joshua LockAs the combo is created before its backing model it's common for the combo to be drawn at its minimum size and then grow the first time the user activates it. This slight ugly patch forces the combo to be resized as soon as the model is associated so that by the time the user interacts with the widget it is less likely to change size.
--- lib/bb/ui/hob.py | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
lib/bb/ui/hob.py +4 -0
@@ -155,6 +155,10 @@ class MainWindow (gtk.Window): def data_generated(self, handler): self.generating = False 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) self.enable_widgets()
Signature · 1 line
1.7.6
PATCH 7/10 ui/hob: enable building an image with minimal contents
In reply to Joshua LockThis patch enables a user to build a rootfs containing only the selected packages without having to have first selected a 'Base image'.
Fixes [YOCTO #1239]
--- lib/bb/ui/crumbs/tasklistmodel.py | 14 ++++++++++++-- lib/bb/ui/hob.py | 23 ++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-)
lib/bb/ui/crumbs/tasklistmodel.py +12 -2
@@ -60,9 +60,19 @@ require %s IMAGE_INSTALL += "%s" """ - meta_path = model.find_image_path(self.base_image) - recipe = template % (meta_path, self.userpkgs) + empty_template = """ +# Recipe generated by the HOB + +inherit core-image + +IMAGE_INSTALL = "%s" +""" + if self.base_image and not self.base_image == "empty": + meta_path = model.find_image_path(self.base_image) + recipe = template % (meta_path, self.userpkgs) + else: + recipe = empty_template % self.allpkgs if os.path.exists(writepath): os.rename(writepath, "%s~" % writepath)
lib/bb/ui/hob.py +18 -5
@@ -369,20 +369,31 @@ class MainWindow (gtk.Window): self.dirty = False def bake_clicked_cb(self, button): + build_image = True + rep = self.model.get_build_rep() if not rep.base_image: - lbl = "<b>Build only packages?</b>\n\nAn image has not been selected, so only the selected packages will be built." + 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("Build", gtk.RESPONSE_YES) + 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 - else: - self.handler.build_packages(rep.allpkgs.split(" ")) - else: + elif response == gtk.RESPONSE_YES: + build_image = False + elif response == gtk.RESPONSE_OK: + rep.base_image = "empty" + + if build_image: import tempfile, datetime + image_name = "hob-%s-variant-%s" % (rep.base_image, datetime.date.today().isoformat()) image_file = "%s.bb" % (image_name) image_dir = os.path.join(tempfile.gettempdir(), 'hob-images')
@@ -397,6 +408,8 @@ class MainWindow (gtk.Window): self.files_to_clean.append(recipepath) self.handler.build_image(image_name, image_dir, self.configurator) + else: + self.handler.build_packages(rep.allpkgs.split(" ")) self.nb.set_current_page(1)
Signature · 1 line
1.7.6
PATCH 8/10 ui/crumbs/tasklistmodel: handle items added in by base image being removed
In reply to Joshua LockWhen building an image based on an existing image we need to correctly handle removals from that images package set. Do so by testing if any of the items brought in by the base image are removed and, if so, building an image from scratch with all of the selected packages included.
Fixes [YOCTO #1232]
--- lib/bb/ui/crumbs/tasklistmodel.py | 16 +++++++++++++++- 1 files changed, 15 insertions(+), 1 deletions(-)
lib/bb/ui/crumbs/tasklistmodel.py +15 -1
@@ -495,9 +495,23 @@ class TaskListModel(gtk.ListStore): it = self.contents.iter_next(it) return userpkgs, allpkgs + def image_contents_removed(self): + it = self.get_iter_first() + while it: + sel = self.get_value(it, self.COL_INC) + img = self.get_value(it, self.COL_IMG) + if img and not sel: + return True + it = self.iter_next(it) + return False + def get_build_rep(self): userpkgs, allpkgs = self.get_selected_packages() - image = self.selected_image + # If base image contents have been removed start from an empty rootfs + if not self.selected_image or self.image_contents_removed(): + image = "empty" + else: + image = self.selected_image return BuildRep(" ".join(userpkgs), " ".join(allpkgs), image)
Signature · 1 line
1.7.6
PATCH 9/10 ui/crumbs/tasklistmodel: don't iterate whole model in find_alt_dependency()
In reply to Joshua LockThe method needs to find an included item anyway so rather than iterating the entire model and checking the included status of each entry iterate over the contents gtk.TreeFilter.
--- lib/bb/ui/crumbs/tasklistmodel.py | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-)
lib/bb/ui/crumbs/tasklistmodel.py +9 -10
@@ -369,22 +369,21 @@ class TaskListModel(gtk.ListStore): """ Find the name of an item in the image contents which depends on the item - at contents_path returns either an item name (str) or None - NOTE: - contents_path must be a path in the self.contents gtk.TreeModel + name. + Returns either an item name (str) or None """ def find_alt_dependency(self, name): - it = self.get_iter_first() + it = self.contents.get_iter_first() while it: - # iterate all items in the model - path = self.get_path(it) - deps = self[path][self.COL_DEPS] - itname = self[path][self.COL_NAME] - inc = self[path][self.COL_INC] + # iterate all items in the contents model + path = self.contents.get_path(it) + deps = self.contents[path][self.COL_DEPS] + itname = self.contents[path][self.COL_NAME] + inc = self.contents[path][self.COL_INC] if itname != name and inc and deps.count(name) > 0: # if this item depends on the item, return this items name return itname - it = self.iter_next(it) + it = self.contents.iter_next(it) return "" """
Signature · 1 line
1.7.6
PATCH 10/10 ui/crumbs/tasklistmodel: fix loading a saved recipe
In reply to Joshua LockFrom: Jessica Zhang <[email protected]>
use the correct variable userpkgs instead of packages during reload saved bb file that contains user customization.
Fixes [YOCTO #1289]
--- lib/bb/ui/crumbs/tasklistmodel.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
lib/bb/ui/crumbs/tasklistmodel.py +1 -1
@@ -275,7 +275,7 @@ class TaskListModel(gtk.ListStore): it = self.images.iter_next(it) # Mark all of the additional packages for inclusion - packages = rep.packages.split(" ") + packages = rep.userpkgs.split(" ") it = self.get_iter_first() while it: path = self.get_path(it)
Signature · 1 line
1.7.6
Richard Purdie
AppliedQuoted 15 lines · Joshua Lock
Highlights are using buildTargets rather than buildFiles to build the custom image and enabling building an image without a 'Base image' set. Plus a slew of more minor fixes.
The following changes since commit
d11f9dd33cdcc97a4a937e8bf7e97558d813cadd:cooker: only append files once (2011-07-26 13:45:31 +0100)
are available in the git repository at:
git://github.com/incandescant/bitbakehob
https://github.com/incandescant/bitbake/tree/hobJessica Zhang (1):
ui/crumbs/tasklistmodel: fix loading a saved recipe
Quoted 13 lines
Joshua Lock (9):
ui/hob: replace the ugly static command map ui/crumbs/hobeventhandler: reparse files before running other
commands
ui/hob: switch from buildFile to buildTargets for custom image builds ui/hob: don't offer to show built output if build fails ui/hob: change wording in build complete dialog
ui/hob: Force the 'Base image combo' to be drawn correctly ui/hob: enable building an image with minimal contents ui/crumbs/tasklistmodel: handle items added in by base image being
removed
ui/crumbs/tasklistmodel: don't iterate whole model in
find_alt_dependency()
Merged to master, thanks.
Richard