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)
downloadpoky-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 a83a176ddc..d9829861bb 100644
--- a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py
+++ b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py
@@ -20,6 +20,58 @@
20 20
21import gtk 21import gtk
22import gobject 22import gobject
23import re
24
25class BuildRep(gobject.GObject):
26
27 def __init__(self, userpkgs, allpkgs, base_image=None):
28 gobject.GObject.__init__(self)
29 self.base_image = base_image
30 self.allpkgs = allpkgs
31 self.userpkgs = userpkgs
32
33 def loadRecipe(self, pathname):
34 contents = []
35 packages = ""
36 base_image = ""
37
38 with open(pathname, 'r') as f:
39 contents = f.readlines()
40
41 pkg_pattern = "^\s*(IMAGE_INSTALL)\s*([+=.?]+)\s*(\"\S*\")"
42 img_pattern = "^\s*(require)\s+(\S+.bb)"
43
44 for line in contents:
45 matchpkg = re.search(pkg_pattern, line)
46 matchimg = re.search(img_pattern, line)
47 if matchpkg:
48 packages = packages + matchpkg.group(3).strip('"')
49 if matchimg:
50 base_image = os.path.basename(matchimg.group(2)).split(".")[0]
51
52 self.base_image = base_image
53 self.userpkgs = packages
54
55 def writeRecipe(self, writepath, model):
56 # FIXME: Need a better way to determine meta_path...
57 template = """
58# Recipe generated by the HOB
59
60require %s.bb
61
62IMAGE_INSTALL += "%s"
63"""
64 meta_path = model.find_image_path(self.base_image)
65
66 recipe = template % (meta_path, self.userpkgs)
67
68 if os.path.exists(writepath):
69 os.rename(writepath, "%s~" % writepath)
70
71 with open(writepath, 'w') as r:
72 r.write(recipe)
73
74 return writepath
23 75
24class TaskListModel(gtk.ListStore): 76class TaskListModel(gtk.ListStore):
25 """ 77 """
@@ -28,12 +80,18 @@ class TaskListModel(gtk.ListStore):
28 providing convenience functions to access gtk.TreeModel subclasses which 80 providing convenience functions to access gtk.TreeModel subclasses which
29 provide filtered views of the data. 81 provide filtered views of the data.
30 """ 82 """
31 (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC) = range(8) 83 (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_PATH) = range(10)
32 84
33 __gsignals__ = { 85 __gsignals__ = {
34 "tasklist-populated" : (gobject.SIGNAL_RUN_LAST, 86 "tasklist-populated" : (gobject.SIGNAL_RUN_LAST,
35 gobject.TYPE_NONE, 87 gobject.TYPE_NONE,
36 ()) 88 ()),
89 "contents-changed" : (gobject.SIGNAL_RUN_LAST,
90 gobject.TYPE_NONE,
91 (gobject.TYPE_INT,)),
92 "image-changed" : (gobject.SIGNAL_RUN_LAST,
93 gobject.TYPE_NONE,
94 (gobject.TYPE_STRING,)),
37 } 95 }
38 96
39 """ 97 """
@@ -43,6 +101,7 @@ class TaskListModel(gtk.ListStore):
43 self.tasks = None 101 self.tasks = None
44 self.packages = None 102 self.packages = None
45 self.images = None 103 self.images = None
104 self.selected_image = None
46 105
47 gtk.ListStore.__init__ (self, 106 gtk.ListStore.__init__ (self,
48 gobject.TYPE_STRING, 107 gobject.TYPE_STRING,
@@ -52,7 +111,22 @@ class TaskListModel(gtk.ListStore):
52 gobject.TYPE_STRING, 111 gobject.TYPE_STRING,
53 gobject.TYPE_STRING, 112 gobject.TYPE_STRING,
54 gobject.TYPE_STRING, 113 gobject.TYPE_STRING,
55 gobject.TYPE_BOOLEAN) 114 gobject.TYPE_BOOLEAN,
115 gobject.TYPE_BOOLEAN,
116 gobject.TYPE_STRING)
117
118 def contents_changed_cb(self, tree_model, path, it=None):
119 pkg_cnt = self.contents.iter_n_children(None)
120 self.emit("contents-changed", pkg_cnt)
121
122 def contents_model_filter(self, model, it):
123 if not model.get_value(it, self.COL_INC) or model.get_value(it, self.COL_TYPE) == 'image':
124 return False
125 name = model.get_value(it, self.COL_NAME)
126 if name.endswith('-native') or name.endswith('-cross'):
127 return False
128 else:
129 return True
56 130
57 """ 131 """
58 Create, if required, and return a filtered gtk.TreeModel 132 Create, if required, and return a filtered gtk.TreeModel
@@ -62,7 +136,9 @@ class TaskListModel(gtk.ListStore):
62 def contents_model(self): 136 def contents_model(self):
63 if not self.contents: 137 if not self.contents:
64 self.contents = self.filter_new() 138 self.contents = self.filter_new()
65 self.contents.set_visible_column(self.COL_INC) 139 self.contents.set_visible_func(self.contents_model_filter)
140 self.contents.connect("row-inserted", self.contents_changed_cb)
141 self.contents.connect("row-deleted", self.contents_changed_cb)
66 return self.contents 142 return self.contents
67 143
68 """ 144 """
@@ -107,10 +183,10 @@ class TaskListModel(gtk.ListStore):
107 Helper function to determine whether an item is a package 183 Helper function to determine whether an item is a package
108 """ 184 """
109 def package_model_filter(self, model, it): 185 def package_model_filter(self, model, it):
110 if model.get_value(it, self.COL_TYPE) == 'package': 186 if model.get_value(it, self.COL_TYPE) != 'package':
111 return True
112 else:
113 return False 187 return False
188 else:
189 return True
114 190
115 """ 191 """
116 Create, if required, and return a filtered gtk.TreeModel 192 Create, if required, and return a filtered gtk.TreeModel
@@ -129,33 +205,78 @@ class TaskListModel(gtk.ListStore):
129 to notify any listeners that the model is ready 205 to notify any listeners that the model is ready
130 """ 206 """
131 def populate(self, event_model): 207 def populate(self, event_model):
208 # First clear the model, in case repopulating
209 self.clear()
132 for item in event_model["pn"]: 210 for item in event_model["pn"]:
133 atype = 'package' 211 atype = 'package'
134 name = item 212 name = item
135 summary = event_model["pn"][item]["summary"] 213 summary = event_model["pn"][item]["summary"]
136 license = event_model["pn"][item]["license"] 214 lic = event_model["pn"][item]["license"]
137 group = event_model["pn"][item]["section"] 215 group = event_model["pn"][item]["section"]
138 216 filename = event_model["pn"][item]["filename"]
139 depends = event_model["depends"].get(item, "") 217 depends = event_model["depends"].get(item, "")
140 rdepends = event_model["rdepends-pn"].get(item, "") 218 rdepends = event_model["rdepends-pn"].get(item, "")
141 depends = depends + rdepends 219 if rdepends:
220 for rdep in rdepends:
221 if event_model["packages"].get(rdep, ""):
222 pn = event_model["packages"][rdep].get("pn", "")
223 if pn:
224 depends.append(pn)
225
142 self.squish(depends) 226 self.squish(depends)
143 deps = " ".join(depends) 227 deps = " ".join(depends)
144 228
145 if name.count('task-') > 0: 229 if name.count('task-') > 0:
146 atype = 'task' 230 atype = 'task'
147 elif name.count('-image-') > 0: 231 elif name.count('-image-') > 0:
148 atype = 'image' 232 atype = 'image'
149 233
150 self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary, 234 self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary,
151 self.COL_LIC, license, self.COL_GROUP, group, 235 self.COL_LIC, lic, self.COL_GROUP, group,
152 self.COL_DEPS, deps, self.COL_BINB, "", 236 self.COL_DEPS, deps, self.COL_BINB, "",
153 self.COL_TYPE, atype, self.COL_INC, False) 237 self.COL_TYPE, atype, self.COL_INC, False,
154 238 self.COL_IMG, False, self.COL_PATH, filename)
239
155 self.emit("tasklist-populated") 240 self.emit("tasklist-populated")
156 241
157 """ 242 """
158 squish lst so that it doesn't contain any duplicates 243 Load a BuildRep into the model
244 """
245 def load_image_rep(self, rep):
246 # Unset everything
247 it = self.get_iter_first()
248 while it:
249 path = self.get_path(it)
250 self[path][self.COL_INC] = False
251 self[path][self.COL_IMG] = False
252 it = self.iter_next(it)
253
254 # Iterate the images and disable them all
255 it = self.images.get_iter_first()
256 while it:
257 path = self.images.convert_path_to_child_path(self.images.get_path(it))
258 name = self[path][self.COL_NAME]
259 if name == rep.base_image:
260 self.include_item(path, image_contents=True)
261 else:
262 self[path][self.COL_INC] = False
263 it = self.images.iter_next(it)
264
265 # Mark all of the additional packages for inclusion
266 packages = rep.packages.split(" ")
267 it = self.get_iter_first()
268 while it:
269 path = self.get_path(it)
270 name = self[path][self.COL_NAME]
271 if name in packages:
272 self.include_item(path)
273 packages.remove(name)
274 it = self.iter_next(it)
275
276 self.emit("image-changed", rep.base_image)
277
278 """
279 squish lst so that it doesn't contain any duplicate entries
159 """ 280 """
160 def squish(self, lst): 281 def squish(self, lst):
161 seen = {} 282 seen = {}
@@ -173,56 +294,59 @@ class TaskListModel(gtk.ListStore):
173 self[path][self.COL_INC] = False 294 self[path][self.COL_INC] = False
174 295
175 """ 296 """
297 recursively called to mark the item at opath and any package which
298 depends on it for removal
176 """ 299 """
177 def mark(self, path): 300 def mark(self, opath):
178 name = self[path][self.COL_NAME]
179 it = self.get_iter_first()
180 removals = [] 301 removals = []
181 #print("Removing %s" % name) 302 it = self.get_iter_first()
303 name = self[opath][self.COL_NAME]
182 304
183 self.remove_item_path(path) 305 self.remove_item_path(opath)
184 306
185 # Remove all dependent packages, update binb 307 # Remove all dependent packages, update binb
186 while it: 308 while it:
187 path = self.get_path(it) 309 path = self.get_path(it)
188 # FIXME: need to ensure partial name matching doesn't happen, regexp? 310 inc = self[path][self.COL_INC]
189 if self[path][self.COL_INC] and self[path][self.COL_DEPS].count(name): 311 deps = self[path][self.COL_DEPS]
190 #print("%s depended on %s, marking for removal" % (self[path][self.COL_NAME], name)) 312 binb = self[path][self.COL_BINB]
313
314 # FIXME: need to ensure partial name matching doesn't happen
315 if inc and deps.count(name):
191 # found a dependency, remove it 316 # found a dependency, remove it
192 self.mark(path) 317 self.mark(path)
193 if self[path][self.COL_INC] and self[path][self.COL_BINB].count(name): 318 if inc and binb.count(name):
194 binb = self.find_alt_dependency(self[path][self.COL_NAME]) 319 bib = self.find_alt_dependency(name)
195 #print("%s was brought in by %s, binb set to %s" % (self[path][self.COL_NAME], name, binb)) 320 self[path][self.COL_BINB] = bib
196 self[path][self.COL_BINB] = binb 321
197 it = self.iter_next(it) 322 it = self.iter_next(it)
198 323
199 """ 324 """
325 Remove items from contents if the have an empty COL_BINB (brought in by)
326 caused by all packages they are a dependency of being removed.
327 If the item isn't a package we leave it included.
200 """ 328 """
201 def sweep_up(self): 329 def sweep_up(self):
330 model = self.contents
202 removals = [] 331 removals = []
203 it = self.get_iter_first() 332 it = self.contents.get_iter_first()
204 333
205 while it: 334 while it:
206 path = self.get_path(it) 335 binb = model.get_value(it, self.COL_BINB)
207 binb = self[path][self.COL_BINB] 336 itype = model.get_value(it, self.COL_TYPE)
208 if binb == "" or binb is None: 337
209 #print("Sweeping up %s" % self[path][self.COL_NAME]) 338 if itype == 'package' and not binb:
210 if not path in removals: 339 opath = model.convert_path_to_child_path(model.get_path(it))
211 removals.extend(path) 340 if not opath in removals:
212 it = self.iter_next(it) 341 removals.extend(opath)
342
343 it = model.iter_next(it)
213 344
214 while removals: 345 while removals:
215 path = removals.pop() 346 path = removals.pop()
216 self.mark(path) 347 self.mark(path)
217 348
218 """ 349 """
219 Remove an item from the contents
220 """
221 def remove_item(self, path):
222 self.mark(path)
223 self.sweep_up()
224
225 """
226 Find the name of an item in the image contents which depends on the item 350 Find the name of an item in the image contents which depends on the item
227 at contents_path returns either an item name (str) or None 351 at contents_path returns either an item name (str) or None
228 NOTE: 352 NOTE:
@@ -238,18 +362,11 @@ class TaskListModel(gtk.ListStore):
238 inc = self[path][self.COL_INC] 362 inc = self[path][self.COL_INC]
239 if itname != name and inc and deps.count(name) > 0: 363 if itname != name and inc and deps.count(name) > 0:
240 # if this item depends on the item, return this items name 364 # if this item depends on the item, return this items name
241 #print("%s depends on %s" % (itname, name))
242 return itname 365 return itname
243 it = self.iter_next(it) 366 it = self.iter_next(it)
244 return "" 367 return ""
245 368
246 """ 369 """
247 Convert a path in self to a path in the filtered contents model
248 """
249 def contents_path_for_path(self, path):
250 return self.contents.convert_child_path_to_path(path)
251
252 """
253 Check the self.contents gtk.TreeModel for an item 370 Check the self.contents gtk.TreeModel for an item
254 where COL_NAME matches item_name 371 where COL_NAME matches item_name
255 Returns True if a match is found, False otherwise 372 Returns True if a match is found, False otherwise
@@ -266,25 +383,30 @@ class TaskListModel(gtk.ListStore):
266 """ 383 """
267 Add this item, and any of its dependencies, to the image contents 384 Add this item, and any of its dependencies, to the image contents
268 """ 385 """
269 def include_item(self, item_path, binb=""): 386 def include_item(self, item_path, binb="", image_contents=False):
270 name = self[item_path][self.COL_NAME] 387 name = self[item_path][self.COL_NAME]
271 deps = self[item_path][self.COL_DEPS] 388 deps = self[item_path][self.COL_DEPS]
272 cur_inc = self[item_path][self.COL_INC] 389 cur_inc = self[item_path][self.COL_INC]
273 #print("Adding %s for %s dependency" % (name, binb))
274 if not cur_inc: 390 if not cur_inc:
275 self[item_path][self.COL_INC] = True 391 self[item_path][self.COL_INC] = True
276 self[item_path][self.COL_BINB] = binb 392 self[item_path][self.COL_BINB] = binb
393 # We want to do some magic with things which are brought in by the base
394 # image so tag them as so
395 if image_contents:
396 self[item_path][self.COL_IMG] = True
397 if self[item_path][self.COL_TYPE] == 'image':
398 self.selected_image = name
399
277 if deps: 400 if deps:
278 #print("Dependencies of %s are %s" % (name, deps))
279 # add all of the deps and set their binb to this item 401 # add all of the deps and set their binb to this item
280 for dep in deps.split(" "): 402 for dep in deps.split(" "):
281 # FIXME: this skipping virtuals can't be right? Unless we choose only to show target
282 # packages? In which case we should handle this server side...
283 # If the contents model doesn't already contain dep, add it 403 # If the contents model doesn't already contain dep, add it
284 if not dep.startswith("virtual") and not self.contents_includes_name(dep): 404 # We only care to show things which will end up in the
405 # resultant image, so filter cross and native recipes
406 if not self.contents_includes_name(dep) and not dep.endswith("-native") and not dep.endswith("-cross"):
285 path = self.find_path_for_item(dep) 407 path = self.find_path_for_item(dep)
286 if path: 408 if path:
287 self.include_item(path, name) 409 self.include_item(path, name, image_contents)
288 else: 410 else:
289 pass 411 pass
290 412
@@ -317,30 +439,78 @@ class TaskListModel(gtk.ListStore):
317 it = self.contents.get_iter_first() 439 it = self.contents.get_iter_first()
318 440
319 """ 441 """
320 Returns True if one of the selected tasks is an image, False otherwise 442 Returns two lists. One of user selected packages and the other containing
443 all selected packages
321 """ 444 """
322 def targets_contains_image(self): 445 def get_selected_packages(self):
323 it = self.images.get_iter_first() 446 allpkgs = []
447 userpkgs = []
448
449 it = self.contents.get_iter_first()
324 while it: 450 while it:
325 path = self.images.get_path(it) 451 sel = self.contents.get_value(it, self.COL_BINB) == "User Selected"
326 inc = self.images[path][self.COL_INC] 452 name = self.contents.get_value(it, self.COL_NAME)
327 if inc: 453 allpkgs.append(name)
328 return True 454 if sel:
329 it = self.images.iter_next(it) 455 userpkgs.append(name)
330 return False 456 it = self.contents.iter_next(it)
457 return userpkgs, allpkgs
331 458
332 """ 459 def get_build_rep(self):
333 Return a list of all selected items which are not -native or -cross 460 userpkgs, allpkgs = self.get_selected_packages()
334 """ 461 image = self.selected_image
335 def get_targets(self): 462
336 tasks = [] 463 return BuildRep(" ".join(userpkgs), " ".join(allpkgs), image)
337 464
465 def find_reverse_depends(self, pn):
466 revdeps = []
338 it = self.contents.get_iter_first() 467 it = self.contents.get_iter_first()
468
339 while it: 469 while it:
340 path = self.contents.get_path(it) 470 if self.contents.get_value(it, self.COL_DEPS).count(pn) != 0:
341 name = self.contents[path][self.COL_NAME] 471 revdeps.append(self.contents.get_value(it, self.COL_NAME))
342 stype = self.contents[path][self.COL_TYPE]
343 if not name.count('-native') and not name.count('-cross'):
344 tasks.append(name)
345 it = self.contents.iter_next(it) 472 it = self.contents.iter_next(it)
346 return tasks 473
474 if pn in revdeps:
475 revdeps.remove(pn)
476 return revdeps
477
478 def set_selected_image(self, img):
479 self.selected_image = img
480 path = self.find_path_for_item(img)
481 self.include_item(item_path=path,
482 binb="User Selected",
483 image_contents=True)
484
485 self.emit("image-changed", self.selected_image)
486
487 def set_selected_packages(self, pkglist):
488 selected = pkglist
489 it = self.get_iter_first()
490
491 while it:
492 name = self.get_value(it, self.COL_NAME)
493 if name in pkglist:
494 pkglist.remove(name)
495 path = self.get_path(it)
496 self.include_item(item_path=path,
497 binb="User Selected")
498 if len(pkglist) == 0:
499 return
500 it = self.iter_next(it)
501
502 def find_image_path(self, image):
503 it = self.images.get_iter_first()
504
505 while it:
506 image_name = self.images.get_value(it, self.COL_NAME)
507 if image_name == image:
508 path = self.images.get_value(it, self.COL_PATH)
509 meta_pattern = "(\S*)/(meta*/)(\S*)"
510 meta_match = re.search(meta_pattern, path)
511 if meta_match:
512 _, lyr, bbrel = path.partition(meta_match.group(2))
513 if bbrel:
514 path = bbrel
515 return path
516 it = self.images.iter_next(it)