diff options
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 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 | ||
21 | import gtk | 21 | import gtk |
22 | import gobject | 22 | import gobject |
23 | import re | ||
24 | |||
25 | class 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 | |||
60 | require %s.bb | ||
61 | |||
62 | IMAGE_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 | ||
24 | class TaskListModel(gtk.ListStore): | 76 | class 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) | ||