summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/ui/crumbs/tasklistmodel.py
diff options
context:
space:
mode:
authorJoshua Lock <josh@linux.intel.com>2011-07-01 15:58:50 -0700
committerRichard Purdie <richard.purdie@linuxfoundation.org>2011-07-05 14:40:30 +0100
commit4cc291c007103c19779f995e852b37dbad122293 (patch)
tree3447d62ef1ba2eca08137b8e13df58f8a337a453 /bitbake/lib/bb/ui/crumbs/tasklistmodel.py
parent7fc9c3488f7865111ec052d1cab213d216d2b414 (diff)
downloadast2050-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.py326
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)
OpenPOWER on IntegriCloud