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):
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 = []
61 def getbuild_id(self, name):
63 Return an ID number for the build target name.
64 If it doesn't exist, create one.
66 if not name in self.build_names_index:
67 self.build_names_index.append(name)
68 return len(self.build_names_index) - 1
70 return self.build_names_index.index(name)
72 def getrun_id(self, name):
74 Return an ID number for the run target name.
75 If it doesn't exist, create one.
77 if not name in self.run_names_index:
78 self.run_names_index.append(name)
79 return len(self.run_names_index) - 1
81 return self.run_names_index.index(name)
83 def getfn_id(self, name):
85 Return an ID number for the filename.
86 If it doesn't exist, create one.
88 if not name in self.fn_index:
89 self.fn_index.append(name)
90 return len(self.fn_index) - 1
92 return self.fn_index.index(name)
94 def gettask_ids(self, fnid):
96 Return an array of the ID numbers matching a given fnid.
99 if fnid in self.tasks_lookup:
100 for task in self.tasks_lookup[fnid]:
101 ids.append(self.tasks_lookup[fnid][task])
104 def gettask_id(self, fn, task, create = True):
106 Return an ID number for the task matching fn and task.
107 If it doesn't exist, create one by default.
108 Optionally return None instead.
110 fnid = self.getfn_id(fn)
112 if fnid in self.tasks_lookup:
113 if task in self.tasks_lookup[fnid]:
114 return self.tasks_lookup[fnid][task]
119 self.tasks_name.append(task)
120 self.tasks_fnid.append(fnid)
121 self.tasks_tdepends.append([])
122 self.tasks_idepends.append([])
124 listid = len(self.tasks_name) - 1
126 if fnid not in self.tasks_lookup:
127 self.tasks_lookup[fnid] = {}
128 self.tasks_lookup[fnid][task] = listid
132 def add_tasks(self, fn, dataCache):
134 Add tasks for a given fn to the database
137 task_deps = dataCache.task_deps[fn]
139 fnid = self.getfn_id(fn)
141 if fnid in self.failed_fnids:
142 bb.msg.fatal(bb.msg.domain.TaskData, "Trying to re-add a failed file? Something is broken...")
144 # Check if we've already seen this fn
145 if fnid in self.tasks_fnid:
148 for task in task_deps['tasks']:
150 # Work out task dependencies
152 for dep in task_deps['parents'][task]:
153 parentid = self.gettask_id(fn, dep)
154 parentids.append(parentid)
155 taskid = self.gettask_id(fn, task)
156 self.tasks_tdepends[taskid].extend(parentids)
158 # Touch all intertask dependencies
159 if 'depends' in task_deps and task in task_deps['depends']:
161 for dep in task_deps['depends'][task].split():
163 ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
164 self.tasks_idepends[taskid].extend(ids)
166 # Work out build dependencies
167 if not fnid in self.depids:
169 for depend in dataCache.deps[fn]:
170 bb.msg.debug(2, bb.msg.domain.TaskData, "Added dependency %s for %s" % (depend, fn))
171 dependids[self.getbuild_id(depend)] = None
172 self.depids[fnid] = dependids.keys()
174 # Work out runtime dependencies
175 if not fnid in self.rdepids:
177 rdepends = dataCache.rundeps[fn]
178 rrecs = dataCache.runrecs[fn]
179 for package in rdepends:
180 for rdepend in bb.utils.explode_deps(rdepends[package]):
181 bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime dependency %s for %s" % (rdepend, fn))
182 rdependids[self.getrun_id(rdepend)] = None
183 for package in rrecs:
184 for rdepend in bb.utils.explode_deps(rrecs[package]):
185 bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime recommendation %s for %s" % (rdepend, fn))
186 rdependids[self.getrun_id(rdepend)] = None
187 self.rdepids[fnid] = rdependids.keys()
189 for dep in self.depids[fnid]:
190 if dep in self.failed_deps:
193 for dep in self.rdepids[fnid]:
194 if dep in self.failed_rdeps:
198 def have_build_target(self, target):
200 Have we a build target matching this name?
202 targetid = self.getbuild_id(target)
204 if targetid in self.build_targets:
208 def have_runtime_target(self, target):
210 Have we a runtime target matching this name?
212 targetid = self.getrun_id(target)
214 if targetid in self.run_targets:
218 def add_build_target(self, fn, item):
221 If already present, append the provider fn to the list
223 targetid = self.getbuild_id(item)
224 fnid = self.getfn_id(fn)
226 if targetid in self.build_targets:
227 if fnid in self.build_targets[targetid]:
229 self.build_targets[targetid].append(fnid)
231 self.build_targets[targetid] = [fnid]
233 def add_runtime_target(self, fn, item):
235 Add a runtime target.
236 If already present, append the provider fn to the list
238 targetid = self.getrun_id(item)
239 fnid = self.getfn_id(fn)
241 if targetid in self.run_targets:
242 if fnid in self.run_targets[targetid]:
244 self.run_targets[targetid].append(fnid)
246 self.run_targets[targetid] = [fnid]
248 def mark_external_target(self, item):
250 Mark a build target as being externally requested
252 targetid = self.getbuild_id(item)
254 if targetid not in self.external_targets:
255 self.external_targets.append(targetid)
257 def get_unresolved_build_targets(self, dataCache):
259 Return a list of build targets who's providers
263 for target in self.build_names_index:
264 if target in dataCache.ignored_dependencies:
266 if self.build_names_index.index(target) in self.failed_deps:
268 if not self.have_build_target(target):
269 unresolved.append(target)
272 def get_unresolved_run_targets(self, dataCache):
274 Return a list of runtime targets who's providers
278 for target in self.run_names_index:
279 if target in dataCache.ignored_dependencies:
281 if self.run_names_index.index(target) in self.failed_rdeps:
283 if not self.have_runtime_target(target):
284 unresolved.append(target)
287 def get_provider(self, item):
289 Return a list of providers of item
291 targetid = self.getbuild_id(item)
293 return self.build_targets[targetid]
295 def get_dependees(self, itemid):
297 Return a list of targets which depend on item
300 for fnid in self.depids:
301 if itemid in self.depids[fnid]:
302 dependees.append(fnid)
305 def get_dependees_str(self, item):
307 Return a list of targets which depend on item as a user readable string
309 itemid = self.getbuild_id(item)
311 for fnid in self.depids:
312 if itemid in self.depids[fnid]:
313 dependees.append(self.fn_index[fnid])
316 def get_rdependees(self, itemid):
318 Return a list of targets which depend on runtime item
321 for fnid in self.rdepids:
322 if itemid in self.rdepids[fnid]:
323 dependees.append(fnid)
326 def get_rdependees_str(self, item):
328 Return a list of targets which depend on runtime item as a user readable string
330 itemid = self.getrun_id(item)
332 for fnid in self.rdepids:
333 if itemid in self.rdepids[fnid]:
334 dependees.append(self.fn_index[fnid])
337 def add_provider(self, cfgData, dataCache, item):
339 self.add_provider_internal(cfgData, dataCache, item)
340 except bb.providers.NoProvider:
342 if self.get_rdependees_str(item):
343 bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (item, self.get_dependees_str(item)))
345 bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item))
347 targetid = self.getbuild_id(item)
348 self.remove_buildtarget(targetid)
350 self.mark_external_target(item)
352 def add_provider_internal(self, cfgData, dataCache, item):
354 Add the providers of item to the task data
355 Mark entries were specifically added externally as against dependencies
356 added internally during dependency resolution
359 if item in dataCache.ignored_dependencies:
362 if not item in dataCache.providers:
363 if self.get_rdependees_str(item):
364 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)))
366 bb.msg.note(2, bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item))
367 bb.event.fire(bb.event.NoProvider(item, cfgData))
368 raise bb.providers.NoProvider(item)
370 if self.have_build_target(item):
373 all_p = dataCache.providers[item]
375 eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
378 fnid = self.getfn_id(p)
379 if fnid in self.failed_fnids:
383 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)))
384 bb.event.fire(bb.event.NoProvider(item, cfgData))
385 raise bb.providers.NoProvider(item)
387 if len(eligible) > 1 and foundUnique == False:
388 if item not in self.consider_msgs_cache:
391 providers_list.append(dataCache.pkg_fn[fn])
392 bb.msg.note(1, bb.msg.domain.Provider, "multiple providers are available for %s (%s);" % (item, ", ".join(providers_list)))
393 bb.msg.note(1, bb.msg.domain.Provider, "consider defining PREFERRED_PROVIDER_%s" % item)
394 bb.event.fire(bb.event.MultipleProviders(item, providers_list, cfgData))
395 self.consider_msgs_cache.append(item)
398 fnid = self.getfn_id(fn)
399 if fnid in self.failed_fnids:
401 bb.msg.debug(2, bb.msg.domain.Provider, "adding %s to satisfy %s" % (fn, item))
402 self.add_build_target(fn, item)
403 self.add_tasks(fn, dataCache)
406 #item = dataCache.pkg_fn[fn]
408 def add_rprovider(self, cfgData, dataCache, item):
410 Add the runtime providers of item to the task data
411 (takes item names from RDEPENDS/PACKAGES namespace)
414 if item in dataCache.ignored_dependencies:
417 if self.have_runtime_target(item):
420 all_p = bb.providers.getRuntimeProviders(dataCache, item)
423 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))
424 bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True))
425 raise bb.providers.NoRProvider(item)
427 eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache)
430 fnid = self.getfn_id(p)
431 if fnid in self.failed_fnids:
435 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))
436 bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True))
437 raise bb.providers.NoRProvider(item)
439 if len(eligible) > 1 and numberPreferred == 0:
440 if item not in self.consider_msgs_cache:
443 providers_list.append(dataCache.pkg_fn[fn])
444 bb.msg.note(2, bb.msg.domain.Provider, "multiple providers are available for runtime %s (%s);" % (item, ", ".join(providers_list)))
445 bb.msg.note(2, bb.msg.domain.Provider, "consider defining a PREFERRED_PROVIDER entry to match runtime %s" % item)
446 bb.event.fire(bb.event.MultipleProviders(item,providers_list, cfgData, runtime=True))
447 self.consider_msgs_cache.append(item)
449 if numberPreferred > 1:
450 if item not in self.consider_msgs_cache:
453 providers_list.append(dataCache.pkg_fn[fn])
454 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)))
455 bb.msg.note(2, bb.msg.domain.Provider, "consider defining only one PREFERRED_PROVIDER entry to match runtime %s" % item)
456 bb.event.fire(bb.event.MultipleProviders(item,providers_list, cfgData, runtime=True))
457 self.consider_msgs_cache.append(item)
459 # run through the list until we find one that we can build
461 fnid = self.getfn_id(fn)
462 if fnid in self.failed_fnids:
464 bb.msg.debug(2, bb.msg.domain.Provider, "adding '%s' to satisfy runtime '%s'" % (fn, item))
465 self.add_runtime_target(fn, item)
466 self.add_tasks(fn, dataCache)
468 def fail_fnid(self, fnid, missing_list = []):
470 Mark a file as failed (unbuildable)
471 Remove any references from build and runtime provider lists
473 missing_list, A list of missing requirements for this target
475 if fnid in self.failed_fnids:
477 bb.msg.debug(1, bb.msg.domain.Provider, "File '%s' is unbuildable, removing..." % self.fn_index[fnid])
478 self.failed_fnids.append(fnid)
479 for target in self.build_targets:
480 if fnid in self.build_targets[target]:
481 self.build_targets[target].remove(fnid)
482 if len(self.build_targets[target]) == 0:
483 self.remove_buildtarget(target, missing_list)
484 for target in self.run_targets:
485 if fnid in self.run_targets[target]:
486 self.run_targets[target].remove(fnid)
487 if len(self.run_targets[target]) == 0:
488 self.remove_runtarget(target, missing_list)
490 def remove_buildtarget(self, targetid, missing_list = []):
492 Mark a build target as failed (unbuildable)
493 Trigger removal of any files that have this as a dependency
496 missing_list = [self.build_names_index[targetid]]
498 missing_list = [self.build_names_index[targetid]] + missing_list
499 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))
500 self.failed_deps.append(targetid)
501 dependees = self.get_dependees(targetid)
502 for fnid in dependees:
503 self.fail_fnid(fnid, missing_list)
504 for taskid in range(len(self.tasks_idepends)):
505 idepends = self.tasks_idepends[taskid]
506 for (idependid, idependtask) in idepends:
507 if idependid == targetid:
508 self.fail_fnid(self.tasks_fnid[taskid], missing_list)
510 if self.abort and targetid in self.external_targets:
511 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))
512 raise bb.providers.NoProvider
514 def remove_runtarget(self, targetid, missing_list = []):
516 Mark a run target as failed (unbuildable)
517 Trigger removal of any files that have this as a dependency
520 missing_list = [self.run_names_index[targetid]]
522 missing_list = [self.run_names_index[targetid]] + missing_list
524 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))
525 self.failed_rdeps.append(targetid)
526 dependees = self.get_rdependees(targetid)
527 for fnid in dependees:
528 self.fail_fnid(fnid, missing_list)
530 def add_unresolved(self, cfgData, dataCache):
532 Resolve all unresolved build and runtime targets
534 bb.msg.note(1, bb.msg.domain.TaskData, "Resolving any missing task queue dependencies")
537 for target in self.get_unresolved_build_targets(dataCache):
539 self.add_provider_internal(cfgData, dataCache, target)
541 except bb.providers.NoProvider:
542 targetid = self.getbuild_id(target)
543 if self.abort and targetid in self.external_targets:
544 if self.get_rdependees_str(target):
545 bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (target, self.get_dependees_str(target)))
547 bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (target))
549 self.remove_buildtarget(targetid)
550 for target in self.get_unresolved_run_targets(dataCache):
552 self.add_rprovider(cfgData, dataCache, target)
554 except bb.providers.NoRProvider:
555 self.remove_runtarget(self.getrun_id(target))
556 bb.msg.debug(1, bb.msg.domain.TaskData, "Resolved " + str(added) + " extra dependecies")
563 Dump some debug information on the internal data structures
565 bb.msg.debug(3, bb.msg.domain.TaskData, "build_names:")
566 bb.msg.debug(3, bb.msg.domain.TaskData, ", ".join(self.build_names_index))
568 bb.msg.debug(3, bb.msg.domain.TaskData, "run_names:")
569 bb.msg.debug(3, bb.msg.domain.TaskData, ", ".join(self.run_names_index))
571 bb.msg.debug(3, bb.msg.domain.TaskData, "build_targets:")
572 for buildid in range(len(self.build_names_index)):
573 target = self.build_names_index[buildid]
575 if buildid in self.build_targets:
576 targets = self.build_targets[buildid]
577 bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s: %s" % (buildid, target, targets))
579 bb.msg.debug(3, bb.msg.domain.TaskData, "run_targets:")
580 for runid in range(len(self.run_names_index)):
581 target = self.run_names_index[runid]
583 if runid in self.run_targets:
584 targets = self.run_targets[runid]
585 bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s: %s" % (runid, target, targets))
587 bb.msg.debug(3, bb.msg.domain.TaskData, "tasks:")
588 for task in range(len(self.tasks_name)):
589 bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s - %s: %s" % (
591 self.fn_index[self.tasks_fnid[task]],
592 self.tasks_name[task],
593 self.tasks_tdepends[task]))
595 bb.msg.debug(3, bb.msg.domain.TaskData, "dependency ids (per fn):")
596 for fnid in self.depids:
597 bb.msg.debug(3, bb.msg.domain.TaskData, " %s %s: %s" % (fnid, self.fn_index[fnid], self.depids[fnid]))
599 bb.msg.debug(3, bb.msg.domain.TaskData, "runtime dependency ids (per fn):")
600 for fnid in self.rdepids:
601 bb.msg.debug(3, bb.msg.domain.TaskData, " %s %s: %s" % (fnid, self.fn_index[fnid], self.rdepids[fnid]))