diff options
Diffstat (limited to 'bitbake/lib')
| -rw-r--r-- | bitbake/lib/bb/ui/crumbs/tasklistmodel.py | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py new file mode 100644 index 0000000000..a83a176ddc --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py | |||
| @@ -0,0 +1,346 @@ | |||
| 1 | # | ||
| 2 | # BitBake Graphical GTK User Interface | ||
| 3 | # | ||
| 4 | # Copyright (C) 2011 Intel Corporation | ||
| 5 | # | ||
| 6 | # Authored by Joshua Lock <josh@linux.intel.com> | ||
| 7 | # | ||
| 8 | # This program is free software; you can redistribute it and/or modify | ||
| 9 | # it under the terms of the GNU General Public License version 2 as | ||
| 10 | # published by the Free Software Foundation. | ||
| 11 | # | ||
| 12 | # This program is distributed in the hope that it will be useful, | ||
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | # GNU General Public License for more details. | ||
| 16 | # | ||
| 17 | # You should have received a copy of the GNU General Public License along | ||
| 18 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
| 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | |||
| 21 | import gtk | ||
| 22 | import gobject | ||
| 23 | |||
| 24 | class TaskListModel(gtk.ListStore): | ||
| 25 | """ | ||
| 26 | This class defines an gtk.ListStore subclass which will convert the output | ||
| 27 | of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also | ||
| 28 | providing convenience functions to access gtk.TreeModel subclasses which | ||
| 29 | provide filtered views of the data. | ||
| 30 | """ | ||
| 31 | (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC) = range(8) | ||
| 32 | |||
| 33 | __gsignals__ = { | ||
| 34 | "tasklist-populated" : (gobject.SIGNAL_RUN_LAST, | ||
| 35 | gobject.TYPE_NONE, | ||
| 36 | ()) | ||
| 37 | } | ||
| 38 | |||
| 39 | """ | ||
| 40 | """ | ||
| 41 | def __init__(self): | ||
| 42 | self.contents = None | ||
| 43 | self.tasks = None | ||
| 44 | self.packages = None | ||
| 45 | self.images = None | ||
| 46 | |||
| 47 | gtk.ListStore.__init__ (self, | ||
| 48 | gobject.TYPE_STRING, | ||
| 49 | gobject.TYPE_STRING, | ||
| 50 | gobject.TYPE_STRING, | ||
| 51 | gobject.TYPE_STRING, | ||
| 52 | gobject.TYPE_STRING, | ||
| 53 | gobject.TYPE_STRING, | ||
| 54 | gobject.TYPE_STRING, | ||
| 55 | gobject.TYPE_BOOLEAN) | ||
| 56 | |||
| 57 | """ | ||
| 58 | Create, if required, and return a filtered gtk.TreeModel | ||
| 59 | containing only the items which are to be included in the | ||
| 60 | image | ||
| 61 | """ | ||
| 62 | def contents_model(self): | ||
| 63 | if not self.contents: | ||
| 64 | self.contents = self.filter_new() | ||
| 65 | self.contents.set_visible_column(self.COL_INC) | ||
| 66 | return self.contents | ||
| 67 | |||
| 68 | """ | ||
| 69 | Helper function to determine whether an item is a task | ||
| 70 | """ | ||
| 71 | def task_model_filter(self, model, it): | ||
| 72 | if model.get_value(it, self.COL_TYPE) == 'task': | ||
| 73 | return True | ||
| 74 | else: | ||
| 75 | return False | ||
| 76 | |||
| 77 | """ | ||
| 78 | Create, if required, and return a filtered gtk.TreeModel | ||
| 79 | containing only the items which are tasks | ||
| 80 | """ | ||
| 81 | def tasks_model(self): | ||
| 82 | if not self.tasks: | ||
| 83 | self.tasks = self.filter_new() | ||
| 84 | self.tasks.set_visible_func(self.task_model_filter) | ||
| 85 | return self.tasks | ||
| 86 | |||
| 87 | """ | ||
| 88 | Helper function to determine whether an item is an image | ||
| 89 | """ | ||
| 90 | def image_model_filter(self, model, it): | ||
| 91 | if model.get_value(it, self.COL_TYPE) == 'image': | ||
| 92 | return True | ||
| 93 | else: | ||
| 94 | return False | ||
| 95 | |||
| 96 | """ | ||
| 97 | Create, if required, and return a filtered gtk.TreeModel | ||
| 98 | containing only the items which are images | ||
| 99 | """ | ||
| 100 | def images_model(self): | ||
| 101 | if not self.images: | ||
| 102 | self.images = self.filter_new() | ||
| 103 | self.images.set_visible_func(self.image_model_filter) | ||
| 104 | return self.images | ||
| 105 | |||
| 106 | """ | ||
| 107 | Helper function to determine whether an item is a package | ||
| 108 | """ | ||
| 109 | def package_model_filter(self, model, it): | ||
| 110 | if model.get_value(it, self.COL_TYPE) == 'package': | ||
| 111 | return True | ||
| 112 | else: | ||
| 113 | return False | ||
| 114 | |||
| 115 | """ | ||
| 116 | Create, if required, and return a filtered gtk.TreeModel | ||
| 117 | containing only the items which are packages | ||
| 118 | """ | ||
| 119 | def packages_model(self): | ||
| 120 | if not self.packages: | ||
| 121 | self.packages = self.filter_new() | ||
| 122 | self.packages.set_visible_func(self.package_model_filter) | ||
| 123 | return self.packages | ||
| 124 | |||
| 125 | """ | ||
| 126 | The populate() function takes as input the data from a | ||
| 127 | bb.event.TargetsTreeGenerated event and populates the TaskList. | ||
| 128 | Once the population is done it emits gsignal tasklist-populated | ||
| 129 | to notify any listeners that the model is ready | ||
| 130 | """ | ||
| 131 | def populate(self, event_model): | ||
| 132 | for item in event_model["pn"]: | ||
| 133 | atype = 'package' | ||
| 134 | name = item | ||
| 135 | summary = event_model["pn"][item]["summary"] | ||
| 136 | license = event_model["pn"][item]["license"] | ||
| 137 | group = event_model["pn"][item]["section"] | ||
| 138 | |||
| 139 | depends = event_model["depends"].get(item, "") | ||
| 140 | rdepends = event_model["rdepends-pn"].get(item, "") | ||
| 141 | depends = depends + rdepends | ||
| 142 | self.squish(depends) | ||
| 143 | deps = " ".join(depends) | ||
| 144 | |||
| 145 | if name.count('task-') > 0: | ||
| 146 | atype = 'task' | ||
| 147 | elif name.count('-image-') > 0: | ||
| 148 | atype = 'image' | ||
| 149 | |||
| 150 | self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary, | ||
| 151 | self.COL_LIC, license, self.COL_GROUP, group, | ||
| 152 | self.COL_DEPS, deps, self.COL_BINB, "", | ||
| 153 | self.COL_TYPE, atype, self.COL_INC, False) | ||
| 154 | |||
| 155 | self.emit("tasklist-populated") | ||
| 156 | |||
| 157 | """ | ||
| 158 | squish lst so that it doesn't contain any duplicates | ||
| 159 | """ | ||
| 160 | def squish(self, lst): | ||
| 161 | seen = {} | ||
| 162 | for l in lst: | ||
| 163 | seen[l] = 1 | ||
| 164 | return seen.keys() | ||
| 165 | |||
| 166 | """ | ||
| 167 | Mark the item at path as not included | ||
| 168 | NOTE: | ||
| 169 | path should be a gtk.TreeModelPath into self (not a filtered model) | ||
| 170 | """ | ||
| 171 | def remove_item_path(self, path): | ||
| 172 | self[path][self.COL_BINB] = "" | ||
| 173 | self[path][self.COL_INC] = False | ||
| 174 | |||
| 175 | """ | ||
| 176 | """ | ||
| 177 | def mark(self, path): | ||
| 178 | name = self[path][self.COL_NAME] | ||
| 179 | it = self.get_iter_first() | ||
| 180 | removals = [] | ||
| 181 | #print("Removing %s" % name) | ||
| 182 | |||
| 183 | self.remove_item_path(path) | ||
| 184 | |||
| 185 | # Remove all dependent packages, update binb | ||
| 186 | while it: | ||
| 187 | path = self.get_path(it) | ||
| 188 | # FIXME: need to ensure partial name matching doesn't happen, regexp? | ||
| 189 | if self[path][self.COL_INC] and self[path][self.COL_DEPS].count(name): | ||
| 190 | #print("%s depended on %s, marking for removal" % (self[path][self.COL_NAME], name)) | ||
| 191 | # found a dependency, remove it | ||
| 192 | self.mark(path) | ||
| 193 | if self[path][self.COL_INC] and self[path][self.COL_BINB].count(name): | ||
| 194 | binb = self.find_alt_dependency(self[path][self.COL_NAME]) | ||
| 195 | #print("%s was brought in by %s, binb set to %s" % (self[path][self.COL_NAME], name, binb)) | ||
| 196 | self[path][self.COL_BINB] = binb | ||
| 197 | it = self.iter_next(it) | ||
| 198 | |||
| 199 | """ | ||
| 200 | """ | ||
| 201 | def sweep_up(self): | ||
| 202 | removals = [] | ||
| 203 | it = self.get_iter_first() | ||
| 204 | |||
| 205 | while it: | ||
| 206 | path = self.get_path(it) | ||
| 207 | binb = self[path][self.COL_BINB] | ||
| 208 | if binb == "" or binb is None: | ||
| 209 | #print("Sweeping up %s" % self[path][self.COL_NAME]) | ||
| 210 | if not path in removals: | ||
| 211 | removals.extend(path) | ||
| 212 | it = self.iter_next(it) | ||
| 213 | |||
| 214 | while removals: | ||
| 215 | path = removals.pop() | ||
| 216 | self.mark(path) | ||
| 217 | |||
| 218 | """ | ||
| 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 | ||
| 227 | at contents_path returns either an item name (str) or None | ||
| 228 | NOTE: | ||
| 229 | contents_path must be a path in the self.contents gtk.TreeModel | ||
| 230 | """ | ||
| 231 | def find_alt_dependency(self, name): | ||
| 232 | it = self.get_iter_first() | ||
| 233 | while it: | ||
| 234 | # iterate all items in the model | ||
| 235 | path = self.get_path(it) | ||
| 236 | deps = self[path][self.COL_DEPS] | ||
| 237 | itname = self[path][self.COL_NAME] | ||
| 238 | inc = self[path][self.COL_INC] | ||
| 239 | if itname != name and inc and deps.count(name) > 0: | ||
| 240 | # if this item depends on the item, return this items name | ||
| 241 | #print("%s depends on %s" % (itname, name)) | ||
| 242 | return itname | ||
| 243 | it = self.iter_next(it) | ||
| 244 | return "" | ||
| 245 | |||
| 246 | """ | ||
| 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 | ||
| 254 | where COL_NAME matches item_name | ||
| 255 | Returns True if a match is found, False otherwise | ||
| 256 | """ | ||
| 257 | def contents_includes_name(self, item_name): | ||
| 258 | it = self.contents.get_iter_first() | ||
| 259 | while it: | ||
| 260 | path = self.contents.get_path(it) | ||
| 261 | if self.contents[path][self.COL_NAME] == item_name: | ||
| 262 | return True | ||
| 263 | it = self.contents.iter_next(it) | ||
| 264 | return False | ||
| 265 | |||
| 266 | """ | ||
| 267 | Add this item, and any of its dependencies, to the image contents | ||
| 268 | """ | ||
| 269 | def include_item(self, item_path, binb=""): | ||
| 270 | name = self[item_path][self.COL_NAME] | ||
| 271 | deps = self[item_path][self.COL_DEPS] | ||
| 272 | cur_inc = self[item_path][self.COL_INC] | ||
| 273 | #print("Adding %s for %s dependency" % (name, binb)) | ||
| 274 | if not cur_inc: | ||
| 275 | self[item_path][self.COL_INC] = True | ||
| 276 | self[item_path][self.COL_BINB] = binb | ||
| 277 | if deps: | ||
| 278 | #print("Dependencies of %s are %s" % (name, deps)) | ||
| 279 | # add all of the deps and set their binb to this item | ||
| 280 | 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 | ||
| 284 | if not dep.startswith("virtual") and not self.contents_includes_name(dep): | ||
| 285 | path = self.find_path_for_item(dep) | ||
| 286 | if path: | ||
| 287 | self.include_item(path, name) | ||
| 288 | else: | ||
| 289 | pass | ||
| 290 | |||
| 291 | """ | ||
| 292 | Find the model path for the item_name | ||
| 293 | Returns the path in the model or None | ||
| 294 | """ | ||
| 295 | def find_path_for_item(self, item_name): | ||
| 296 | it = self.get_iter_first() | ||
| 297 | path = None | ||
| 298 | while it: | ||
| 299 | path = self.get_path(it) | ||
| 300 | if (self[path][self.COL_NAME] == item_name): | ||
| 301 | return path | ||
| 302 | else: | ||
| 303 | it = self.iter_next(it) | ||
| 304 | return None | ||
| 305 | |||
| 306 | """ | ||
| 307 | Empty self.contents by setting the include of each entry to None | ||
| 308 | """ | ||
| 309 | def reset(self): | ||
| 310 | it = self.contents.get_iter_first() | ||
| 311 | while it: | ||
| 312 | path = self.contents.get_path(it) | ||
| 313 | opath = self.contents.convert_path_to_child_path(path) | ||
| 314 | self[opath][self.COL_INC] = False | ||
| 315 | self[opath][self.COL_BINB] = "" | ||
| 316 | # As we've just removed the first item... | ||
| 317 | it = self.contents.get_iter_first() | ||
| 318 | |||
| 319 | """ | ||
| 320 | Returns True if one of the selected tasks is an image, False otherwise | ||
| 321 | """ | ||
| 322 | def targets_contains_image(self): | ||
| 323 | it = self.images.get_iter_first() | ||
| 324 | while it: | ||
| 325 | path = self.images.get_path(it) | ||
| 326 | inc = self.images[path][self.COL_INC] | ||
| 327 | if inc: | ||
| 328 | return True | ||
| 329 | it = self.images.iter_next(it) | ||
| 330 | return False | ||
| 331 | |||
| 332 | """ | ||
| 333 | Return a list of all selected items which are not -native or -cross | ||
| 334 | """ | ||
| 335 | def get_targets(self): | ||
| 336 | tasks = [] | ||
| 337 | |||
| 338 | it = self.contents.get_iter_first() | ||
| 339 | while it: | ||
| 340 | path = self.contents.get_path(it) | ||
| 341 | name = self.contents[path][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) | ||
| 346 | return tasks | ||
