2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5 BitBake 'TaskData' implementation
7 Task data collection and handling
9 Copyright (C) 2006 Richard Purdie
11 This program is free software; you can redistribute it and/or modify it under
12 the terms of the GNU General Public License version 2 as published by the Free
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License along with
22 from bb import data, fetch, event, mkdirhier, utils
27 BitBake Task Data implementation
30 self.build_names_index = []
31 self.run_names_index = []
34 self.build_targets = {}
37 self.external_targets = []
41 self.tasks_tdepends = []
46 self.consider_msgs_cache = []
49 self.failed_rdeps = []
50 self.failed_fnids = []
53 def matches_in_list(self, data, substring):
55 Return a list of the positions of substring in list data
61 start = data.index(substring, start)
67 def both_contain(self, list1, list2):
69 Return the items present in both list1 and list2
78 def getbuild_id(self, name):
80 Return an ID number for the build target name.
81 If it doesn't exist, create one.
83 if not name in self.build_names_index:
84 self.build_names_index.append(name)
86 return self.build_names_index.index(name)
88 def getrun_id(self, name):
90 Return an ID number for the run target name.
91 If it doesn't exist, create one.
93 if not name in self.run_names_index:
94 self.run_names_index.append(name)
96 return self.run_names_index.index(name)
98 def getfn_id(self, name):
100 Return an ID number for the filename.
101 If it doesn't exist, create one.
103 if not name in self.fn_index:
104 self.fn_index.append(name)
106 return self.fn_index.index(name)
108 def gettask_id(self, fn, task):
110 Return an ID number for the task matching fn and task.
111 If it doesn't exist, create one.
113 fnid = self.getfn_id(fn)
115 fnids = self.matches_in_list(self.tasks_fnid, fnid)
116 names = self.matches_in_list(self.tasks_name, task)
118 listid = self.both_contain(fnids, names)
120 if listid is not None:
123 self.tasks_name.append(task)
124 self.tasks_fnid.append(fnid)
125 self.tasks_tdepends.append([])
127 return len(self.tasks_name)-1
129 def add_tasks(self, fn, dataCache):
131 Add tasks for a given fn to the database
134 task_graph = dataCache.task_queues[fn]
135 task_deps = dataCache.task_deps[fn]
137 fnid = self.getfn_id(fn)
139 if fnid in self.failed_fnids:
140 bb.msg.fatal(bb.msg.domain.TaskData, "Trying to re-add a failed file? Something is broken...")
142 # Check if we've already seen this fn
143 if fnid in self.tasks_fnid:
146 # Work out task dependencies
147 for task in task_graph.allnodes():
149 for dep in task_graph.getparents(task):
150 parentid = self.gettask_id(fn, dep)
151 parentids.append(parentid)
152 taskid = self.gettask_id(fn, task)
153 self.tasks_tdepends[taskid].extend(parentids)
155 # Work out build dependencies
156 if not fnid in self.depids:
158 for depend in dataCache.deps[fn]:
159 bb.msg.debug(2, bb.msg.domain.TaskData, "Added dependency %s for %s" % (depend, fn))
160 dependids.append(self.getbuild_id(depend))
161 self.depids[fnid] = dependids
163 # Work out runtime dependencies
164 if not fnid in self.rdepids:
166 rdepends = dataCache.rundeps[fn]
167 rrecs = dataCache.runrecs[fn]
168 for package in rdepends:
169 for rdepend in rdepends[package]:
170 bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime dependency %s for %s" % (rdepend, fn))
171 rdependids.append(self.getrun_id(rdepend))
172 for package in rrecs:
173 for rdepend in rrecs[package]:
174 bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime recommendation %s for %s" % (rdepend, fn))
175 rdependids.append(self.getrun_id(rdepend))
176 self.rdepids[fnid] = rdependids
178 def have_build_target(self, target):
180 Have we a build target matching this name?
182 targetid = self.getbuild_id(target)
184 if targetid in self.build_targets:
188 def have_runtime_target(self, target):
190 Have we a runtime target matching this name?
192 targetid = self.getrun_id(target)
194 if targetid in self.run_targets:
198 def add_build_target(self, fn, item):
201 If already present, append the provider fn to the list
203 targetid = self.getbuild_id(item)
204 fnid = self.getfn_id(fn)
206 if targetid in self.build_targets:
207 if fnid in self.build_targets[targetid]:
209 self.build_targets[targetid].append(fnid)
211 self.build_targets[targetid] = [fnid]
213 def add_runtime_target(self, fn, item):
215 Add a runtime target.
216 If already present, append the provider fn to the list
218 targetid = self.getrun_id(item)
219 fnid = self.getfn_id(fn)
221 if targetid in self.run_targets:
222 if fnid in self.run_targets[targetid]:
224 self.run_targets[targetid].append(fnid)
226 self.run_targets[targetid] = [fnid]
228 def mark_external_target(self, item):
230 Mark a build target as being externally requested
232 targetid = self.getbuild_id(item)
234 if targetid not in self.external_targets:
235 self.external_targets.append(targetid)
237 def get_unresolved_build_targets(self, dataCache):
239 Return a list of build targets who's providers
243 for target in self.build_names_index:
244 if target in dataCache.ignored_dependencies:
246 if self.build_names_index.index(target) in self.failed_deps:
248 if not self.have_build_target(target):
249 unresolved.append(target)
252 def get_unresolved_run_targets(self, dataCache):
254 Return a list of runtime targets who's providers
258 for target in self.run_names_index:
259 if target in dataCache.ignored_dependencies:
261 if self.run_names_index.index(target) in self.failed_rdeps:
263 if not self.have_runtime_target(target):
264 unresolved.append(target)
267 def get_provider(self, item):
269 Return a list of providers of item
271 targetid = self.getbuild_id(item)
273 return self.build_targets[targetid]
275 def get_dependees(self, itemid):
277 Return a list of targets which depend on item
280 for fnid in self.depids:
281 if itemid in self.depids[fnid]:
282 dependees.append(fnid)
285 def get_dependees_str(self, item):
287 Return a list of targets which depend on item as a user readable string
289 itemid = self.getbuild_id(item)
291 for fnid in self.depids:
292 if itemid in self.depids[fnid]:
293 dependees.append(self.fn_index[fnid])
296 def get_rdependees(self, itemid):
298 Return a list of targets which depend on runtime item
301 for fnid in self.rdepids:
302 if itemid in self.rdepids[fnid]:
303 dependees.append(fnid)
306 def get_rdependees_str(self, item):
308 Return a list of targets which depend on runtime item as a user readable string
310 itemid = self.getrun_id(item)
312 for fnid in self.rdepids:
313 if itemid in self.rdepids[fnid]:
314 dependees.append(self.fn_index[fnid])
317 def add_provider(self, cfgData, dataCache, item, external = True):
319 Add the providers of item to the task data
320 Mark entries were specifically added externally as against dependencies
321 added internally during dependency resolution
324 if item in dataCache.ignored_dependencies:
327 if not item in dataCache.providers:
328 msg = "No providers of build target %s (for %s)" % (item, self.get_dependees_str(item))
330 bb.msg.error(bb.msg.domain.Provider, msg)
332 bb.msg.debug(1, bb.msg.domain.Provider, msg)
333 bb.event.fire(bb.event.NoProvider(item, cfgData))
334 raise bb.providers.NoProvider(item)
336 if self.have_build_target(item):
339 all_p = dataCache.providers[item]
341 eligible = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
344 fnid = self.getfn_id(p)
345 if fnid in self.failed_fnids:
349 msg = "No providers of build target %s after filtering (for %s)" % (item, self.get_dependees_str(item))
351 bb.msg.error(bb.msg.domain.Provider, msg)
353 bb.msg.debug(1, bb.msg.domain.Provider, msg)
354 bb.event.fire(bb.event.NoProvider(item, cfgData))
355 raise bb.providers.NoProvider(item)
357 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, cfgData, 1)
359 dataCache.preferred[item] = prefervar
361 discriminated = False
362 if item in dataCache.preferred:
364 pn = dataCache.pkg_fn[p]
365 if dataCache.preferred[item] == pn:
366 bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
368 eligible = [p] + eligible
372 if len(eligible) > 1 and discriminated == False:
373 if item not in self.consider_msgs_cache:
376 providers_list.append(dataCache.pkg_fn[fn])
377 bb.msg.note(1, bb.msg.domain.Provider, "multiple providers are available (%s);" % ", ".join(providers_list))
378 bb.msg.note(1, bb.msg.domain.Provider, "consider defining PREFERRED_PROVIDER_%s" % item)
379 bb.event.fire(bb.event.MultipleProviders(item,providers_list,cfgData))
380 self.consider_msgs_cache.append(item)
383 fnid = self.getfn_id(fn)
384 if fnid in self.failed_fnids:
386 bb.msg.debug(2, bb.msg.domain.Provider, "adding %s to satisfy %s" % (fn, item))
387 self.add_tasks(fn, dataCache)
388 self.add_build_target(fn, item)
390 item = dataCache.pkg_fn[fn]
392 self.mark_external_target(item)
396 def add_rprovider(self, cfgData, dataCache, item):
398 Add the runtime providers of item to the task data
399 (takes item names from RDEPENDS/PACKAGES namespace)
402 if item in dataCache.ignored_dependencies:
405 if self.have_runtime_target(item):
408 all_p = bb.providers.getRuntimeProviders(dataCache, item)
411 bb.msg.error(bb.msg.domain.Provider, "No providers of runtime build target %s (for %s)" % (item, self.get_rdependees_str(item)))
412 bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True))
413 raise bb.providers.NoRProvider(item)
415 eligible = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
418 fnid = self.getfn_id(p)
419 if fnid in self.failed_fnids:
423 bb.msg.error(bb.msg.domain.Provider, "No providers of runtime build target %s after filtering (for %s)" % (item, self.get_rdependees_str(item)))
424 bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True))
425 raise bb.providers.NoRProvider(item)
427 # Should use dataCache.preferred here?
430 pn = dataCache.pkg_fn[p]
431 provides = dataCache.pn_provides[pn]
432 for provide in provides:
433 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, cfgData, 1)
435 bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
437 eligible = [p] + eligible
440 if len(eligible) > 1 and len(preferred) == 0:
441 if item not in self.consider_msgs_cache:
444 providers_list.append(dataCache.pkg_fn[fn])
445 bb.msg.note(2, bb.msg.domain.Provider, "multiple providers are available (%s);" % ", ".join(providers_list))
446 bb.msg.note(2, bb.msg.domain.Provider, "consider defining a PREFERRED_PROVIDER to match runtime %s" % item)
447 bb.event.fire(bb.event.MultipleProviders(item,providers_list, cfgData, runtime=True))
448 self.consider_msgs_cache.append(item)
450 if len(preferred) > 1:
451 if item not in self.consider_msgs_cache:
454 providers_list.append(dataCache.pkg_fn[fn])
455 bb.msg.note(2, bb.msg.domain.Provider, "multiple preferred providers are available (%s);" % ", ".join(providers_list))
456 bb.msg.note(2, bb.msg.domain.Provider, "consider defining only one PREFERRED_PROVIDER to match runtime %s" % item)
457 bb.event.fire(bb.event.MultipleProviders(item,providers_list, cfgData, runtime=True))
458 self.consider_msgs_cache.append(item)
460 # run through the list until we find one that we can build
462 fnid = self.getfn_id(fn)
463 if fnid in self.failed_fnids:
465 bb.msg.debug(2, bb.msg.domain.Provider, "adding %s to satisfy runtime %s" % (fn, item))
466 self.add_tasks(fn, dataCache)
467 self.add_runtime_target(fn, item)
471 def fail_fnid(self, fnid):
473 Mark a file as failed (unbuildable)
474 Remove any references from build and runtime provider lists
476 if fnid in self.failed_fnids:
478 bb.msg.debug(1, bb.msg.domain.Provider, "Removing failed file %s" % self.fn_index[fnid])
479 self.failed_fnids.append(fnid)
480 for target in self.build_targets:
481 if fnid in self.build_targets[target]:
482 self.build_targets[target].remove(fnid)
483 if len(self.build_targets[target]) == 0:
484 self.remove_buildtarget(target)
485 for target in self.run_targets:
486 if fnid in self.run_targets[target]:
487 self.run_targets[target].remove(fnid)
488 if len(self.run_targets[target]) == 0:
489 self.remove_runtarget(target)
491 def remove_buildtarget(self, targetid):
493 Mark a build target as failed (unbuildable)
494 Trigger removal of any files that have this as a dependency
496 bb.msg.debug(1, bb.msg.domain.Provider, "Removing failed build target %s" % self.build_names_index[targetid])
497 self.failed_deps.append(targetid)
498 dependees = self.get_dependees(targetid)
499 for fnid in dependees:
502 def remove_runtarget(self, targetid):
504 Mark a run target as failed (unbuildable)
505 Trigger removal of any files that have this as a dependency
507 bb.msg.note(1, bb.msg.domain.Provider, "Removing failed runtime build target %s" % self.run_names_index[targetid])
508 self.failed_rdeps.append(targetid)
509 dependees = self.get_rdependees(targetid)
510 for fnid in dependees:
513 def add_unresolved(self, cfgData, dataCache):
515 Resolve all unresolved build and runtime targets
517 bb.msg.note(1, bb.msg.domain.TaskData, "Resolving missing task queue dependencies")
520 for target in self.get_unresolved_build_targets(dataCache):
522 self.add_provider(cfgData, dataCache, target, False)
524 except bb.providers.NoProvider:
525 targetid = self.getbuild_id(target)
526 if targetid in self.external_targets:
527 # FIXME - should look at configuration.abort here and only raise if set
529 self.remove_buildtarget(targetid)
530 for target in self.get_unresolved_run_targets(dataCache):
532 self.add_rprovider(cfgData, dataCache, target)
534 except bb.providers.NoRProvider:
535 self.remove_runtarget(self.getrun_id(target))
536 bb.msg.debug(1, bb.msg.domain.TaskData, "Resolved " + str(added) + " extra dependecies")
543 Dump some debug information on the internal data structures
545 bb.msg.debug(3, bb.msg.domain.TaskData, "build_names:")
546 bb.msg.debug(3, bb.msg.domain.TaskData, self.build_names_index)
547 bb.msg.debug(3, bb.msg.domain.TaskData, "run_names:")
548 bb.msg.debug(3, bb.msg.domain.TaskData, self.run_names_index)
549 bb.msg.debug(3, bb.msg.domain.TaskData, "build_targets:")
550 for target in self.build_targets.keys():
551 bb.msg.debug(3, bb.msg.domain.TaskData, " %s: %s" % (self.build_names_index[target], self.build_targets[target]))
552 bb.msg.debug(3, bb.msg.domain.TaskData, "run_targets:")
553 for target in self.run_targets.keys():
554 bb.msg.debug(3, bb.msg.domain.TaskData, " %s: %s" % (self.run_names_index[target], self.run_targets[target]))
555 bb.msg.debug(3, bb.msg.domain.TaskData, "tasks:")
556 for task in range(len(self.tasks_name)):
557 bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s - %s: %s" % (
559 self.fn_index[self.tasks_fnid[task]],
560 self.tasks_name[task],
561 self.tasks_tdepends[task]))