diff options
author | Joshua Lock <josh@linux.intel.com> | 2011-07-01 15:58:50 -0700 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2011-07-05 14:40:30 +0100 |
commit | 4cc291c007103c19779f995e852b37dbad122293 (patch) | |
tree | 3447d62ef1ba2eca08137b8e13df58f8a337a453 /bitbake/lib/bb/ui/crumbs/tasklistmodel.py | |
parent | 7fc9c3488f7865111ec052d1cab213d216d2b414 (diff) | |
download | ast2050-yocto-poky-4cc291c007103c19779f995e852b37dbad122293.zip ast2050-yocto-poky-4cc291c007103c19779f995e852b37dbad122293.tar.gz |
hob: re-designed interaction and implementation
Highlights include:
* Atempted GNOME HIG compliance
* Simplified UI and interaction model
* Sorting and type to find in tree views
* Preferences dialog to modify local settings
* Dialog to add and remove layers
* Search in packages list
* Save/Load image recipes
The build model has been changed, hob will attempt to build all dependent
packages of an image and then use the buildFile server method to build the
created image.
(Bitbake rev: 48e64acaae4a741b9f5630f426fb4e6142755c2c)
Signed-off-by: Joshua Lock <josh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/ui/crumbs/tasklistmodel.py')
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/tasklistmodel.py | 326 |
1 files changed, 248 insertions, 78 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py index a83a176..d982986 100644 --- a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py +++ b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py @@ -20,6 +20,58 @@ import gtk import gobject +import re + +class BuildRep(gobject.GObject): + + def __init__(self, userpkgs, allpkgs, base_image=None): + gobject.GObject.__init__(self) + self.base_image = base_image + self.allpkgs = allpkgs + self.userpkgs = userpkgs + + def loadRecipe(self, pathname): + contents = [] + packages = "" + base_image = "" + + with open(pathname, 'r') as f: + contents = f.readlines() + + pkg_pattern = "^\s*(IMAGE_INSTALL)\s*([+=.?]+)\s*(\"\S*\")" + img_pattern = "^\s*(require)\s+(\S+.bb)" + + for line in contents: + matchpkg = re.search(pkg_pattern, line) + matchimg = re.search(img_pattern, line) + if matchpkg: + packages = packages + matchpkg.group(3).strip('"') + if matchimg: + base_image = os.path.basename(matchimg.group(2)).split(".")[0] + + self.base_image = base_image + self.userpkgs = packages + + def writeRecipe(self, writepath, model): + # FIXME: Need a better way to determine meta_path... + template = """ +# Recipe generated by the HOB + +require %s.bb + +IMAGE_INSTALL += "%s" +""" + meta_path = model.find_image_path(self.base_image) + + recipe = template % (meta_path, self.userpkgs) + + if os.path.exists(writepath): + os.rename(writepath, "%s~" % writepath) + + with open(writepath, 'w') as r: + r.write(recipe) + + return writepath class TaskListModel(gtk.ListStore): """ @@ -28,12 +80,18 @@ class TaskListModel(gtk.ListStore): providing convenience functions to access gtk.TreeModel subclasses which provide filtered views of the data. """ - (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC) = range(8) + (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_PATH) = range(10) __gsignals__ = { "tasklist-populated" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - ()) + ()), + "contents-changed" : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_INT,)), + "image-changed" : (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING,)), } """ @@ -43,6 +101,7 @@ class TaskListModel(gtk.ListStore): self.tasks = None self.packages = None self.images = None + self.selected_image = None gtk.ListStore.__init__ (self, gobject.TYPE_STRING, @@ -52,7 +111,22 @@ class TaskListModel(gtk.ListStore): gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, - gobject.TYPE_BOOLEAN) + gobject.TYPE_BOOLEAN, + gobject.TYPE_BOOLEAN, + gobject.TYPE_STRING) + + def contents_changed_cb(self, tree_model, path, it=None): + pkg_cnt = self.contents.iter_n_children(None) + self.emit("contents-changed", pkg_cnt) + + def contents_model_filter(self, model, it): + if not model.get_value(it, self.COL_INC) or model.get_value(it, self.COL_TYPE) == 'image': + return False + name = model.get_value(it, self.COL_NAME) + if name.endswith('-native') or name.endswith('-cross'): + return False + else: + return True """ Create, if required, and return a filtered gtk.TreeModel @@ -62,7 +136,9 @@ class TaskListModel(gtk.ListStore): def contents_model(self): if not self.contents: self.contents = self.filter_new() - self.contents.set_visible_column(self.COL_INC) + self.contents.set_visible_func(self.contents_model_filter) + self.contents.connect("row-inserted", self.contents_changed_cb) + self.contents.connect("row-deleted", self.contents_changed_cb) return self.contents """ @@ -107,10 +183,10 @@ class TaskListModel(gtk.ListStore): Helper function to determine whether an item is a package """ def package_model_filter(self, model, it): - if model.get_value(it, self.COL_TYPE) == 'package': - return True - else: + if model.get_value(it, self.COL_TYPE) != 'package': return False + else: + return True """ Create, if required, and return a filtered gtk.TreeModel @@ -129,33 +205,78 @@ class TaskListModel(gtk.ListStore): to notify any listeners that the model is ready """ def populate(self, event_model): + # First clear the model, in case repopulating + self.clear() for item in event_model["pn"]: atype = 'package' name = item summary = event_model["pn"][item]["summary"] - license = event_model["pn"][item]["license"] + lic = event_model["pn"][item]["license"] group = event_model["pn"][item]["section"] - - depends = event_model["depends"].get(item, "") + filename = event_model["pn"][item]["filename"] + depends = event_model["depends"].get(item, "") rdepends = event_model["rdepends-pn"].get(item, "") - depends = depends + rdepends + if rdepends: + for rdep in rdepends: + if event_model["packages"].get(rdep, ""): + pn = event_model["packages"][rdep].get("pn", "") + if pn: + depends.append(pn) + self.squish(depends) deps = " ".join(depends) - + if name.count('task-') > 0: atype = 'task' elif name.count('-image-') > 0: atype = 'image' self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary, - self.COL_LIC, license, self.COL_GROUP, group, - self.COL_DEPS, deps, self.COL_BINB, "", - self.COL_TYPE, atype, self.COL_INC, False) - + self.COL_LIC, lic, self.COL_GROUP, group, + self.COL_DEPS, deps, self.COL_BINB, "", + self.COL_TYPE, atype, self.COL_INC, False, + self.COL_IMG, False, self.COL_PATH, filename) + self.emit("tasklist-populated") """ - squish lst so that it doesn't contain any duplicates + Load a BuildRep into the model + """ + def load_image_rep(self, rep): + # Unset everything + it = self.get_iter_first() + while it: + path = self.get_path(it) + self[path][self.COL_INC] = False + self[path][self.COL_IMG] = False + it = self.iter_next(it) + + # Iterate the images and disable them all + it = self.images.get_iter_first() + while it: + path = self.images.convert_path_to_child_path(self.images.get_path(it)) + name = self[path][self.COL_NAME] + if name == rep.base_image: + self.include_item(path, image_contents=True) + else: + self[path][self.COL_INC] = False + it = self.images.iter_next(it) + + # Mark all of the additional packages for inclusion + packages = rep.packages.split(" ") + it = self.get_iter_first() + while it: + path = self.get_path(it) + name = self[path][self.COL_NAME] + if name in packages: + self.include_item(path) + packages.remove(name) + it = self.iter_next(it) + + self.emit("image-changed", rep.base_image) + + """ + squish lst so that it doesn't contain any duplicate entries """ def squish(self, lst): seen = {} @@ -173,56 +294,59 @@ class TaskListModel(gtk.ListStore): self[path][self.COL_INC] = False """ + recursively called to mark the item at opath and any package which + depends on it for removal """ - def mark(self, path): - name = self[path][self.COL_NAME] - it = self.get_iter_first() + def mark(self, opath): removals = [] - #print("Removing %s" % name) + it = self.get_iter_first() + name = self[opath][self.COL_NAME] - self.remove_item_path(path) + self.remove_item_path(opath) # Remove all dependent packages, update binb while it: path = self.get_path(it) - # FIXME: need to ensure partial name matching doesn't happen, regexp? - if self[path][self.COL_INC] and self[path][self.COL_DEPS].count(name): - #print("%s depended on %s, marking for removal" % (self[path][self.COL_NAME], name)) + inc = self[path][self.COL_INC] + deps = self[path][self.COL_DEPS] + binb = self[path][self.COL_BINB] + + # FIXME: need to ensure partial name matching doesn't happen + if inc and deps.count(name): # found a dependency, remove it self.mark(path) - if self[path][self.COL_INC] and self[path][self.COL_BINB].count(name): - binb = self.find_alt_dependency(self[path][self.COL_NAME]) - #print("%s was brought in by %s, binb set to %s" % (self[path][self.COL_NAME], name, binb)) - self[path][self.COL_BINB] = binb + if inc and binb.count(name): + bib = self.find_alt_dependency(name) + self[path][self.COL_BINB] = bib + it = self.iter_next(it) """ + Remove items from contents if the have an empty COL_BINB (brought in by) + caused by all packages they are a dependency of being removed. + If the item isn't a package we leave it included. """ def sweep_up(self): + model = self.contents removals = [] - it = self.get_iter_first() + it = self.contents.get_iter_first() while it: - path = self.get_path(it) - binb = self[path][self.COL_BINB] - if binb == "" or binb is None: - #print("Sweeping up %s" % self[path][self.COL_NAME]) - if not path in removals: - removals.extend(path) - it = self.iter_next(it) + binb = model.get_value(it, self.COL_BINB) + itype = model.get_value(it, self.COL_TYPE) + + if itype == 'package' and not binb: + opath = model.convert_path_to_child_path(model.get_path(it)) + if not opath in removals: + removals.extend(opath) + + it = model.iter_next(it) while removals: path = removals.pop() self.mark(path) """ - Remove an item from the contents - """ - def remove_item(self, path): - self.mark(path) - self.sweep_up() - - """ 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: @@ -238,18 +362,11 @@ class TaskListModel(gtk.ListStore): inc = self[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 - #print("%s depends on %s" % (itname, name)) return itname it = self.iter_next(it) return "" """ - Convert a path in self to a path in the filtered contents model - """ - def contents_path_for_path(self, path): - return self.contents.convert_child_path_to_path(path) - - """ Check the self.contents gtk.TreeModel for an item where COL_NAME matches item_name Returns True if a match is found, False otherwise @@ -266,25 +383,30 @@ class TaskListModel(gtk.ListStore): """ Add this item, and any of its dependencies, to the image contents """ - def include_item(self, item_path, binb=""): + def include_item(self, item_path, binb="", image_contents=False): name = self[item_path][self.COL_NAME] deps = self[item_path][self.COL_DEPS] cur_inc = self[item_path][self.COL_INC] - #print("Adding %s for %s dependency" % (name, binb)) if not cur_inc: self[item_path][self.COL_INC] = True self[item_path][self.COL_BINB] = binb + # We want to do some magic with things which are brought in by the base + # image so tag them as so + if image_contents: + self[item_path][self.COL_IMG] = True + if self[item_path][self.COL_TYPE] == 'image': + self.selected_image = name + if deps: - #print("Dependencies of %s are %s" % (name, deps)) # add all of the deps and set their binb to this item for dep in deps.split(" "): - # FIXME: this skipping virtuals can't be right? Unless we choose only to show target - # packages? In which case we should handle this server side... # If the contents model doesn't already contain dep, add it - if not dep.startswith("virtual") and not self.contents_includes_name(dep): + # We only care to show things which will end up in the + # resultant image, so filter cross and native recipes + if not self.contents_includes_name(dep) and not dep.endswith("-native") and not dep.endswith("-cross"): path = self.find_path_for_item(dep) if path: - self.include_item(path, name) + self.include_item(path, name, image_contents) else: pass @@ -317,30 +439,78 @@ class TaskListModel(gtk.ListStore): it = self.contents.get_iter_first() """ - Returns True if one of the selected tasks is an image, False otherwise + Returns two lists. One of user selected packages and the other containing + all selected packages """ - def targets_contains_image(self): - it = self.images.get_iter_first() + def get_selected_packages(self): + allpkgs = [] + userpkgs = [] + + it = self.contents.get_iter_first() while it: - path = self.images.get_path(it) - inc = self.images[path][self.COL_INC] - if inc: - return True - it = self.images.iter_next(it) - return False + sel = self.contents.get_value(it, self.COL_BINB) == "User Selected" + name = self.contents.get_value(it, self.COL_NAME) + allpkgs.append(name) + if sel: + userpkgs.append(name) + it = self.contents.iter_next(it) + return userpkgs, allpkgs - """ - Return a list of all selected items which are not -native or -cross - """ - def get_targets(self): - tasks = [] + def get_build_rep(self): + userpkgs, allpkgs = self.get_selected_packages() + image = self.selected_image + + return BuildRep(" ".join(userpkgs), " ".join(allpkgs), image) + def find_reverse_depends(self, pn): + revdeps = [] it = self.contents.get_iter_first() + while it: - path = self.contents.get_path(it) - name = self.contents[path][self.COL_NAME] - stype = self.contents[path][self.COL_TYPE] - if not name.count('-native') and not name.count('-cross'): - tasks.append(name) + if self.contents.get_value(it, self.COL_DEPS).count(pn) != 0: + revdeps.append(self.contents.get_value(it, self.COL_NAME)) it = self.contents.iter_next(it) - return tasks + + if pn in revdeps: + revdeps.remove(pn) + return revdeps + + def set_selected_image(self, img): + self.selected_image = img + path = self.find_path_for_item(img) + self.include_item(item_path=path, + binb="User Selected", + image_contents=True) + + self.emit("image-changed", self.selected_image) + + def set_selected_packages(self, pkglist): + selected = pkglist + it = self.get_iter_first() + + while it: + name = self.get_value(it, self.COL_NAME) + if name in pkglist: + pkglist.remove(name) + path = self.get_path(it) + self.include_item(item_path=path, + binb="User Selected") + if len(pkglist) == 0: + return + it = self.iter_next(it) + + def find_image_path(self, image): + it = self.images.get_iter_first() + + while it: + image_name = self.images.get_value(it, self.COL_NAME) + if image_name == image: + path = self.images.get_value(it, self.COL_PATH) + meta_pattern = "(\S*)/(meta*/)(\S*)" + meta_match = re.search(meta_pattern, path) + if meta_match: + _, lyr, bbrel = path.partition(meta_match.group(2)) + if bbrel: + path = bbrel + return path + it = self.images.iter_next(it) |