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
11 # Copyright (C) 2006 Richard Purdie
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License version 2 as
15 # published by the Free Software Foundation.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License along
23 # with this program; if not, write to the Free Software Foundation, Inc.,
24 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 from bb import data, event, mkdirhier, utils
31 BitBake Task Data implementation
33 def __init__(self, abort = True, tryaltconfigs = False):
34 self.build_names_index = []
35 self.run_names_index = []
38 self.build_targets = {}
41 self.external_targets = []
45 self.tasks_tdepends = []
46 self.tasks_idepends = []
47 # Cache to speed up task ID lookups
48 self.tasks_lookup = {}
53 self.consider_msgs_cache = []
56 self.failed_rdeps = []
57 self.failed_fnids = []
60 self.tryaltconfigs = tryaltconfigs
62 def getbuild_id(self, name):
64 Return an ID number for the build target name.
65 If it doesn't exist, create one.
67 if not name in self.build_names_index:
68 self.build_names_index.append(name)
69 return len(self.build_names_index) - 1
71 return self.build_names_index.index(name)
73 def getrun_id(self, name):
75 Return an ID number for the run target name.
76 If it doesn't exist, create one.
78 if not name in self.run_names_index:
79 self.run_names_index.append(name)
80 return len(self.run_names_index) - 1
82 return self.run_names_index.index(name)
84 def getfn_id(self, name):
86 Return an ID number for the filename.
87 If it doesn't exist, create one.
89 if not name in self.fn_index:
90 self.fn_index.append(name)
91 return len(self.fn_index) - 1
93 return self.fn_index.index(name)
95 def gettask_ids(self, fnid):
97 Return an array of the ID numbers matching a given fnid.
100 if fnid in self.tasks_lookup:
101 for task in self.tasks_lookup[fnid]:
102 ids.append(self.tasks_lookup[fnid][task])
105 def gettask_id(self, fn, task, create = True):
107 Return an ID number for the task matching fn and task.
108 If it doesn't exist, create one by default.
109 Optionally return None instead.
111 fnid = self.getfn_id(fn)
113 if fnid in self.tasks_lookup:
114 if task in self.tasks_lookup[fnid]:
115 return self.tasks_lookup[fnid][task]
120 self.tasks_name.append(task)
121 self.tasks_fnid.append(fnid)
122 self.tasks_tdepends.append([])
123 self.tasks_idepends.append([])
125 listid = len(self.tasks_name) - 1
127 if fnid not in self.tasks_lookup:
128 self.tasks_lookup[fnid] = {}
129 self.tasks_lookup[fnid][task] = listid
133 def add_tasks(self, fn, dataCache):
135 Add tasks for a given fn to the database
138 task_deps = dataCache.task_deps[fn]
140 fnid = self.getfn_id(fn)
142 if fnid in self.failed_fnids:
143 bb.msg.fatal(bb.msg.domain.TaskData, "Trying to re-add a failed file? Something is broken...")
145 # Check if we've already seen this fn
146 if fnid in self.tasks_fnid:
149 for task in task_deps['tasks']:
151 # Work out task dependencies
153 for dep in task_deps['parents'][task]:
154 parentid = self.gettask_id(fn, dep)
155 parentids.append(parentid)
156 taskid = self.gettask_id(fn, task)
157 self.tasks_tdepends[taskid].extend(parentids)
159 # Touch all intertask dependencies
160 if 'depends' in task_deps and task in task_deps['depends']:
162 for dep in task_deps['depends'][task].split():
164 ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
165 self.tasks_idepends[taskid].extend(ids)
167 # Work out build dependencies
168 if not fnid in self.depids:
170 for depend in dataCache.deps[fn]:
171 bb.msg.debug(2, bb.msg.domain.TaskData, "Added dependency %s for %s" % (depend, fn))
172 dependids[self.getbuild_id(depend)] = None
173 self.depids[fnid] = dependids.keys()
175 # Work out runtime dependencies
176 if not fnid in self.rdepids:
178 rdepends = dataCache.rundeps[fn]
179 rrecs = dataCache.runrecs[fn]
180 for package in rdepends:
181 for rdepend in bb.utils.explode_deps(rdepends[package]):
182 bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime dependency %s for %s" % (rdepend, fn))
183 rdependids[self.getrun_id(rdepend)] = None
184 for package in rrecs:
185 for rdepend in bb.utils.explode_deps(rrecs[package]):
186 bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime recommendation %s for %s" % (rdepend, fn))
187 rdependids[self.getrun_id(rdepend)] = None
188 self.rdepids[fnid] = rdependids.keys()
190 for dep in self.depids[fnid]:
191 if dep in self.failed_deps:
194 for dep in self.rdepids[fnid]:
195 if dep in self.failed_rdeps:
199 def have_build_target(self, target):
201 Have we a build target matching this name?
203 targetid = self.getbuild_id(target)
205 if targetid in self.build_targets:
209 def have_runtime_target(self, target):
211 Have we a runtime target matching this name?
213 targetid = self.getrun_id(target)
215 if targetid in self.run_targets:
219 def add_build_target(self, fn, item):
222 If already present, append the provider fn to the list
224 targetid = self.getbuild_id(item)
225 fnid = self.getfn_id(fn)
227 if targetid in self.build_targets:
228 if fnid in self.build_targets[targetid]:
230 self.build_targets[targetid].append(fnid)
232 self.build_targets[targetid] = [fnid]
234 def add_runtime_target(self, fn, item):
236 Add a runtime target.
237 If already present, append the provider fn to the list
239 targetid = self.getrun_id(item)
240 fnid = self.getfn_id(fn)
242 if targetid in self.run_targets:
243 if fnid in self.run_targets[targetid]:
245 self.run_targets[targetid].append(fnid)
247 self.run_targets[targetid] = [fnid]
249 def mark_external_target(self, item):
251 Mark a build target as being externally requested
253 targetid = self.getbuild_id(item)
255 if targetid not in self.external_targets:
256 self.external_targets.append(targetid)
258 def get_unresolved_build_targets(self, dataCache):
260 Return a list of build targets who's providers
264 for target in self.build_names_index:
265 if target in dataCache.ignored_dependencies:
267 if self.build_names_index.index(target) in self.failed_deps:
269 if not self.have_build_target(target):
270 unresolved.append(target)
273 def get_unresolved_run_targets(self, dataCache):
275 Return a list of runtime targets who's providers
279 for target in self.run_names_index:
280 if target in dataCache.ignored_dependencies:
282 if self.run_names_index.index(target) in self.failed_rdeps:
284 if not self.have_runtime_target(target):
285 unresolved.append(target)
288 def get_provider(self, item):
290 Return a list of providers of item
292 targetid = self.getbuild_id(item)
294 return self.build_targets[targetid]
296 def get_dependees(self, itemid):
298 Return a list of targets which depend on item
301 for fnid in self.depids:
302 if itemid in self.depids[fnid]:
303 dependees.append(fnid)
306 def get_dependees_str(self, item):
308 Return a list of targets which depend on item as a user readable string
310 itemid = self.getbuild_id(item)
312 for fnid in self.depids:
313 if itemid in self.depids[fnid]:
314 dependees.append(self.fn_index[fnid])
317 def get_rdependees(self, itemid):
319 Return a list of targets which depend on runtime item
322 for fnid in self.rdepids:
323 if itemid in self.rdepids[fnid]:
324 dependees.append(fnid)
327 def get_rdependees_str(self, item):
329 Return a list of targets which depend on runtime item as a user readable string
331 itemid = self.getrun_id(item)
333 for fnid in self.rdepids:
334 if itemid in self.rdepids[fnid]:
335 dependees.append(self.fn_index[fnid])
338 def add_provider(self, cfgData, dataCache, item):
340 self.add_provider_internal(cfgData, dataCache, item)
341 except bb.providers.NoProvider:
343 if self.get_rdependees_str(item):
344 bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (item, self.get_dependees_str(item)))
346 bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item))
348 targetid = self.getbuild_id(item)
349 self.remove_buildtarget(targetid)
351 self.mark_external_target(item)
353 def add_provider_internal(self, cfgData, dataCache, item):
355 Add the providers of item to the task data
356 Mark entries were specifically added externally as against dependencies
357 added internally during dependency resolution
360 if item in dataCache.ignored_dependencies:
363 if not item in dataCache.providers:
364 if self.get_rdependees_str(item):
365 bb.msg.note(2, bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (item, self.get_dependees_str(item)))
367 bb.msg.note(2, bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item))
368 bb.event.fire(bb.event.NoProvider(item, cfgData))
369 raise bb.providers.NoProvider(item)
371 if self.have_build_target(item):
374 all_p = dataCache.providers[item]
376 eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
379 fnid = self.getfn_id(p)
380 if fnid in self.failed_fnids:
384 bb.msg.note(2, bb.msg.domain.Provider, "No buildable provider PROVIDES '%s' but '%s' DEPENDS on or otherwise requires it. Enable debugging and see earlier logs to find unbuildable providers." % (item, self.get_dependees_str(item)))
385 bb.event.fire(bb.event.NoProvider(item, cfgData))
386 raise bb.providers.NoProvider(item)
388 if len(eligible) > 1 and foundUnique == False:
389 if item not in self.consider_msgs_cache:
392 providers_list.append(dataCache.pkg_fn[fn])
393 bb.msg.note(1, bb.msg.domain.Provider, "multiple providers are available for %s (%s);" % (item, ", ".join(providers_list)))
394 bb.msg.note(1, bb.msg.domain.Provider, "consider defining PREFERRED_PROVIDER_%s" % item)
395 bb.event.fire(bb.event.MultipleProviders(item, providers_list, cfgData))
396 self.consider_msgs_cache.append(item)
399 fnid = self.getfn_id(fn)
400 if fnid in self.failed_fnids:
402 bb.msg.debug(2, bb.msg.domain.Provider, "adding %s to satisfy %s" % (fn, item))
403 self.add_build_target(fn, item)
404 self.add_tasks(fn, dataCache)
407 #item = dataCache.pkg_fn[fn]
409 def add_rprovider(self, cfgData, dataCache, item):
411 Add the runtime providers of item to the task data
412 (takes item names from RDEPENDS/PACKAGES namespace)
415 if item in dataCache.ignored_dependencies:
418 if self.have_runtime_target(item):
421 all_p = bb.providers.getRuntimeProviders(dataCache, item)
424 bb.msg.error(bb.msg.domain.Provider, "'%s' RDEPENDS/RRECOMMENDS or otherwise requires the runtime entity '%s' but it wasn't found in any PACKAGE or RPROVIDES variables" % (self.get_rdependees_str(item), item))
425 bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True))
426 raise bb.providers.NoRProvider(item)
428 eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache)
431 fnid = self.getfn_id(p)
432 if fnid in self.failed_fnids:
436 bb.msg.error(bb.msg.domain.Provider, "'%s' RDEPENDS/RRECOMMENDS or otherwise requires the runtime entity '%s' but it wasn't found in any PACKAGE or RPROVIDES variables of any buildable targets.\nEnable debugging and see earlier logs to find unbuildable targets." % (self.get_rdependees_str(item), item))
437 bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True))
438 raise bb.providers.NoRProvider(item)
440 if len(eligible) > 1 and numberPreferred == 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 for runtime %s (%s);" % (item, ", ".join(providers_list)))
446 bb.msg.note(2, bb.msg.domain.Provider, "consider defining a PREFERRED_PROVIDER entry 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 numberPreferred > 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 providers are available for runtime %s (top %s entries preferred) (%s);" % (item, numberPreferred, ", ".join(providers_list)))
456 bb.msg.note(2, bb.msg.domain.Provider, "consider defining only one PREFERRED_PROVIDER entry 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_runtime_target(fn, item)
467 self.add_tasks(fn, dataCache)
469 def fail_fnid(self, fnid, missing_list = []):
471 Mark a file as failed (unbuildable)
472 Remove any references from build and runtime provider lists
474 missing_list, A list of missing requirements for this target
476 if fnid in self.failed_fnids:
478 bb.msg.debug(1, bb.msg.domain.Provider, "File '%s' is unbuildable, removing..." % 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, missing_list)
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, missing_list)
491 def remove_buildtarget(self, targetid, missing_list = []):
493 Mark a build target as failed (unbuildable)
494 Trigger removal of any files that have this as a dependency
497 missing_list = [self.build_names_index[targetid]]
499 missing_list = [self.build_names_index[targetid]] + missing_list
500 bb.msg.note(2, bb.msg.domain.Provider, "Target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s" % (self.build_names_index[targetid], missing_list))
501 self.failed_deps.append(targetid)
502 dependees = self.get_dependees(targetid)
503 for fnid in dependees:
504 self.fail_fnid(fnid, missing_list)
505 for taskid in range(len(self.tasks_idepends)):
506 idepends = self.tasks_idepends[taskid]
507 for (idependid, idependtask) in idepends:
508 if idependid == targetid:
509 self.fail_fnid(self.tasks_fnid[taskid], missing_list)
511 if self.abort and targetid in self.external_targets:
512 bb.msg.error(bb.msg.domain.Provider, "Required build target '%s' has no buildable providers.\nMissing or unbuildable dependency chain was: %s" % (self.build_names_index[targetid], missing_list))
513 raise bb.providers.NoProvider
515 def remove_runtarget(self, targetid, missing_list = []):
517 Mark a run target as failed (unbuildable)
518 Trigger removal of any files that have this as a dependency
521 missing_list = [self.run_names_index[targetid]]
523 missing_list = [self.run_names_index[targetid]] + missing_list
525 bb.msg.note(1, bb.msg.domain.Provider, "Runtime target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s" % (self.run_names_index[targetid], missing_list))
526 self.failed_rdeps.append(targetid)
527 dependees = self.get_rdependees(targetid)
528 for fnid in dependees:
529 self.fail_fnid(fnid, missing_list)
531 def add_unresolved(self, cfgData, dataCache):
533 Resolve all unresolved build and runtime targets
535 bb.msg.note(1, bb.msg.domain.TaskData, "Resolving any missing task queue dependencies")
538 for target in self.get_unresolved_build_targets(dataCache):
540 self.add_provider_internal(cfgData, dataCache, target)
542 except bb.providers.NoProvider:
543 targetid = self.getbuild_id(target)
544 if self.abort and targetid in self.external_targets:
545 if self.get_rdependees_str(target):
546 bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (target, self.get_dependees_str(target)))
548 bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (target))
550 self.remove_buildtarget(targetid)
551 for target in self.get_unresolved_run_targets(dataCache):
553 self.add_rprovider(cfgData, dataCache, target)
555 except bb.providers.NoRProvider:
556 self.remove_runtarget(self.getrun_id(target))
557 bb.msg.debug(1, bb.msg.domain.TaskData, "Resolved " + str(added) + " extra dependecies")
564 Dump some debug information on the internal data structures
566 bb.msg.debug(3, bb.msg.domain.TaskData, "build_names:")
567 bb.msg.debug(3, bb.msg.domain.TaskData, ", ".join(self.build_names_index))
569 bb.msg.debug(3, bb.msg.domain.TaskData, "run_names:")
570 bb.msg.debug(3, bb.msg.domain.TaskData, ", ".join(self.run_names_index))
572 bb.msg.debug(3, bb.msg.domain.TaskData, "build_targets:")
573 for buildid in range(len(self.build_names_index)):
574 target = self.build_names_index[buildid]
576 if buildid in self.build_targets:
577 targets = self.build_targets[buildid]
578 bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s: %s" % (buildid, target, targets))
580 bb.msg.debug(3, bb.msg.domain.TaskData, "run_targets:")
581 for runid in range(len(self.run_names_index)):
582 target = self.run_names_index[runid]
584 if runid in self.run_targets:
585 targets = self.run_targets[runid]
586 bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s: %s" % (runid, target, targets))
588 bb.msg.debug(3, bb.msg.domain.TaskData, "tasks:")
589 for task in range(len(self.tasks_name)):
590 bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s - %s: %s" % (
592 self.fn_index[self.tasks_fnid[task]],
593 self.tasks_name[task],
594 self.tasks_tdepends[task]))
596 bb.msg.debug(3, bb.msg.domain.TaskData, "dependency ids (per fn):")
597 for fnid in self.depids:
598 bb.msg.debug(3, bb.msg.domain.TaskData, " %s %s: %s" % (fnid, self.fn_index[fnid], self.depids[fnid]))
600 bb.msg.debug(3, bb.msg.domain.TaskData, "runtime dependency ids (per fn):")
601 for fnid in self.rdepids:
602 bb.msg.debug(3, bb.msg.domain.TaskData, " %s %s: %s" % (fnid, self.fn_index[fnid], self.rdepids[fnid]))