blob: 9ae52d77dad23b32858b8eb89753a4f2ff76f664 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#!/usr/bin/env python
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4"""
5BitBake 'TaskData' implementation
6
7Task data collection and handling
8
9"""
10
11# Copyright (C) 2006 Richard Purdie
12#
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.
16#
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.
21#
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.
25
26import logging
27import re
28import bb
29
30logger = logging.getLogger("BitBake.TaskData")
31
32def re_match_strings(target, strings):
33 """
34 Whether or not the string 'target' matches
35 any one string of the strings which can be regular expression string
36 """
37 return any(name == target or re.match(name, target)
38 for name in strings)
39
40class TaskData:
41 """
42 BitBake Task Data implementation
43 """
44 def __init__(self, abort = True, tryaltconfigs = False, skiplist = None, allowincomplete = False):
45 self.build_names_index = []
46 self.run_names_index = []
47 self.fn_index = []
48
49 self.build_targets = {}
50 self.run_targets = {}
51
52 self.external_targets = []
53
54 self.tasks_fnid = []
55 self.tasks_name = []
56 self.tasks_tdepends = []
57 self.tasks_idepends = []
58 self.tasks_irdepends = []
59 # Cache to speed up task ID lookups
60 self.tasks_lookup = {}
61
62 self.depids = {}
63 self.rdepids = {}
64
65 self.consider_msgs_cache = []
66
67 self.failed_deps = []
68 self.failed_rdeps = []
69 self.failed_fnids = []
70
71 self.abort = abort
72 self.tryaltconfigs = tryaltconfigs
73 self.allowincomplete = allowincomplete
74
75 self.skiplist = skiplist
76
77 def getbuild_id(self, name):
78 """
79 Return an ID number for the build target name.
80 If it doesn't exist, create one.
81 """
82 if not name in self.build_names_index:
83 self.build_names_index.append(name)
84 return len(self.build_names_index) - 1
85
86 return self.build_names_index.index(name)
87
88 def getrun_id(self, name):
89 """
90 Return an ID number for the run target name.
91 If it doesn't exist, create one.
92 """
93 if not name in self.run_names_index:
94 self.run_names_index.append(name)
95 return len(self.run_names_index) - 1
96
97 return self.run_names_index.index(name)
98
99 def getfn_id(self, name):
100 """
101 Return an ID number for the filename.
102 If it doesn't exist, create one.
103 """
104 if not name in self.fn_index:
105 self.fn_index.append(name)
106 return len(self.fn_index) - 1
107
108 return self.fn_index.index(name)
109
110 def gettask_ids(self, fnid):
111 """
112 Return an array of the ID numbers matching a given fnid.
113 """
114 ids = []
115 if fnid in self.tasks_lookup:
116 for task in self.tasks_lookup[fnid]:
117 ids.append(self.tasks_lookup[fnid][task])
118 return ids
119
120 def gettask_id_fromfnid(self, fnid, task):
121 """
122 Return an ID number for the task matching fnid and task.
123 """
124 if fnid in self.tasks_lookup:
125 if task in self.tasks_lookup[fnid]:
126 return self.tasks_lookup[fnid][task]
127
128 return None
129
130 def gettask_id(self, fn, task, create = True):
131 """
132 Return an ID number for the task matching fn and task.
133 If it doesn't exist, create one by default.
134 Optionally return None instead.
135 """
136 fnid = self.getfn_id(fn)
137
138 if fnid in self.tasks_lookup:
139 if task in self.tasks_lookup[fnid]:
140 return self.tasks_lookup[fnid][task]
141
142 if not create:
143 return None
144
145 self.tasks_name.append(task)
146 self.tasks_fnid.append(fnid)
147 self.tasks_tdepends.append([])
148 self.tasks_idepends.append([])
149 self.tasks_irdepends.append([])
150
151 listid = len(self.tasks_name) - 1
152
153 if fnid not in self.tasks_lookup:
154 self.tasks_lookup[fnid] = {}
155 self.tasks_lookup[fnid][task] = listid
156
157 return listid
158
159 def add_tasks(self, fn, dataCache):
160 """
161 Add tasks for a given fn to the database
162 """
163
164 task_deps = dataCache.task_deps[fn]
165
166 fnid = self.getfn_id(fn)
167
168 if fnid in self.failed_fnids:
169 bb.msg.fatal("TaskData", "Trying to re-add a failed file? Something is broken...")
170
171 # Check if we've already seen this fn
172 if fnid in self.tasks_fnid:
173 return
174
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500175 self.add_extra_deps(fn, dataCache)
176
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500177 for task in task_deps['tasks']:
178
179 # Work out task dependencies
180 parentids = []
181 for dep in task_deps['parents'][task]:
182 if dep not in task_deps['tasks']:
183 bb.debug(2, "Not adding dependeny of %s on %s since %s does not exist" % (task, dep, dep))
184 continue
185 parentid = self.gettask_id(fn, dep)
186 parentids.append(parentid)
187 taskid = self.gettask_id(fn, task)
188 self.tasks_tdepends[taskid].extend(parentids)
189
190 # Touch all intertask dependencies
191 if 'depends' in task_deps and task in task_deps['depends']:
192 ids = []
193 for dep in task_deps['depends'][task].split():
194 if dep:
195 if ":" not in dep:
196 bb.msg.fatal("TaskData", "Error for %s, dependency %s does not contain ':' character\n. Task 'depends' should be specified in the form 'packagename:task'" % (fn, dep))
197 ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
198 self.tasks_idepends[taskid].extend(ids)
199 if 'rdepends' in task_deps and task in task_deps['rdepends']:
200 ids = []
201 for dep in task_deps['rdepends'][task].split():
202 if dep:
203 if ":" not in dep:
204 bb.msg.fatal("TaskData", "Error for %s, dependency %s does not contain ':' character\n. Task 'rdepends' should be specified in the form 'packagename:task'" % (fn, dep))
205 ids.append(((self.getrun_id(dep.split(":")[0])), dep.split(":")[1]))
206 self.tasks_irdepends[taskid].extend(ids)
207
208
209 # Work out build dependencies
210 if not fnid in self.depids:
211 dependids = {}
212 for depend in dataCache.deps[fn]:
213 dependids[self.getbuild_id(depend)] = None
214 self.depids[fnid] = dependids.keys()
215 logger.debug(2, "Added dependencies %s for %s", str(dataCache.deps[fn]), fn)
216
217 # Work out runtime dependencies
218 if not fnid in self.rdepids:
219 rdependids = {}
220 rdepends = dataCache.rundeps[fn]
221 rrecs = dataCache.runrecs[fn]
222 rdependlist = []
223 rreclist = []
224 for package in rdepends:
225 for rdepend in rdepends[package]:
226 rdependlist.append(rdepend)
227 rdependids[self.getrun_id(rdepend)] = None
228 for package in rrecs:
229 for rdepend in rrecs[package]:
230 rreclist.append(rdepend)
231 rdependids[self.getrun_id(rdepend)] = None
232 if rdependlist:
233 logger.debug(2, "Added runtime dependencies %s for %s", str(rdependlist), fn)
234 if rreclist:
235 logger.debug(2, "Added runtime recommendations %s for %s", str(rreclist), fn)
236 self.rdepids[fnid] = rdependids.keys()
237
238 for dep in self.depids[fnid]:
239 if dep in self.failed_deps:
240 self.fail_fnid(fnid)
241 return
242 for dep in self.rdepids[fnid]:
243 if dep in self.failed_rdeps:
244 self.fail_fnid(fnid)
245 return
246
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500247 def add_extra_deps(self, fn, dataCache):
248 func = dataCache.extradepsfunc.get(fn, None)
249 if func:
250 bb.providers.buildWorldTargetList(dataCache)
251 pn = dataCache.pkg_fn[fn]
252 params = {'deps': dataCache.deps[fn],
253 'world_target': dataCache.world_target,
254 'pkg_pn': dataCache.pkg_pn,
255 'self_pn': pn}
256 funcname = '_%s_calculate_extra_depends' % pn.replace('-', '_')
257 paramlist = ','.join(params.keys())
258 func = 'def %s(%s):\n%s\n\n%s(%s)' % (funcname, paramlist, func, funcname, paramlist)
259 bb.utils.better_exec(func, params)
260
261
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262 def have_build_target(self, target):
263 """
264 Have we a build target matching this name?
265 """
266 targetid = self.getbuild_id(target)
267
268 if targetid in self.build_targets:
269 return True
270 return False
271
272 def have_runtime_target(self, target):
273 """
274 Have we a runtime target matching this name?
275 """
276 targetid = self.getrun_id(target)
277
278 if targetid in self.run_targets:
279 return True
280 return False
281
282 def add_build_target(self, fn, item):
283 """
284 Add a build target.
285 If already present, append the provider fn to the list
286 """
287 targetid = self.getbuild_id(item)
288 fnid = self.getfn_id(fn)
289
290 if targetid in self.build_targets:
291 if fnid in self.build_targets[targetid]:
292 return
293 self.build_targets[targetid].append(fnid)
294 return
295 self.build_targets[targetid] = [fnid]
296
297 def add_runtime_target(self, fn, item):
298 """
299 Add a runtime target.
300 If already present, append the provider fn to the list
301 """
302 targetid = self.getrun_id(item)
303 fnid = self.getfn_id(fn)
304
305 if targetid in self.run_targets:
306 if fnid in self.run_targets[targetid]:
307 return
308 self.run_targets[targetid].append(fnid)
309 return
310 self.run_targets[targetid] = [fnid]
311
312 def mark_external_target(self, item):
313 """
314 Mark a build target as being externally requested
315 """
316 targetid = self.getbuild_id(item)
317
318 if targetid not in self.external_targets:
319 self.external_targets.append(targetid)
320
321 def get_unresolved_build_targets(self, dataCache):
322 """
323 Return a list of build targets who's providers
324 are unknown.
325 """
326 unresolved = []
327 for target in self.build_names_index:
328 if re_match_strings(target, dataCache.ignored_dependencies):
329 continue
330 if self.build_names_index.index(target) in self.failed_deps:
331 continue
332 if not self.have_build_target(target):
333 unresolved.append(target)
334 return unresolved
335
336 def get_unresolved_run_targets(self, dataCache):
337 """
338 Return a list of runtime targets who's providers
339 are unknown.
340 """
341 unresolved = []
342 for target in self.run_names_index:
343 if re_match_strings(target, dataCache.ignored_dependencies):
344 continue
345 if self.run_names_index.index(target) in self.failed_rdeps:
346 continue
347 if not self.have_runtime_target(target):
348 unresolved.append(target)
349 return unresolved
350
351 def get_provider(self, item):
352 """
353 Return a list of providers of item
354 """
355 targetid = self.getbuild_id(item)
356
357 return self.build_targets[targetid]
358
359 def get_dependees(self, itemid):
360 """
361 Return a list of targets which depend on item
362 """
363 dependees = []
364 for fnid in self.depids:
365 if itemid in self.depids[fnid]:
366 dependees.append(fnid)
367 return dependees
368
369 def get_dependees_str(self, item):
370 """
371 Return a list of targets which depend on item as a user readable string
372 """
373 itemid = self.getbuild_id(item)
374 dependees = []
375 for fnid in self.depids:
376 if itemid in self.depids[fnid]:
377 dependees.append(self.fn_index[fnid])
378 return dependees
379
380 def get_rdependees(self, itemid):
381 """
382 Return a list of targets which depend on runtime item
383 """
384 dependees = []
385 for fnid in self.rdepids:
386 if itemid in self.rdepids[fnid]:
387 dependees.append(fnid)
388 return dependees
389
390 def get_rdependees_str(self, item):
391 """
392 Return a list of targets which depend on runtime item as a user readable string
393 """
394 itemid = self.getrun_id(item)
395 dependees = []
396 for fnid in self.rdepids:
397 if itemid in self.rdepids[fnid]:
398 dependees.append(self.fn_index[fnid])
399 return dependees
400
401 def get_reasons(self, item, runtime=False):
402 """
403 Get the reason(s) for an item not being provided, if any
404 """
405 reasons = []
406 if self.skiplist:
407 for fn in self.skiplist:
408 skipitem = self.skiplist[fn]
409 if skipitem.pn == item:
410 reasons.append("%s was skipped: %s" % (skipitem.pn, skipitem.skipreason))
411 elif runtime and item in skipitem.rprovides:
412 reasons.append("%s RPROVIDES %s but was skipped: %s" % (skipitem.pn, item, skipitem.skipreason))
413 elif not runtime and item in skipitem.provides:
414 reasons.append("%s PROVIDES %s but was skipped: %s" % (skipitem.pn, item, skipitem.skipreason))
415 return reasons
416
417 def get_close_matches(self, item, provider_list):
418 import difflib
419 if self.skiplist:
420 skipped = []
421 for fn in self.skiplist:
422 skipped.append(self.skiplist[fn].pn)
423 full_list = provider_list + skipped
424 else:
425 full_list = provider_list
426 return difflib.get_close_matches(item, full_list, cutoff=0.7)
427
428 def add_provider(self, cfgData, dataCache, item):
429 try:
430 self.add_provider_internal(cfgData, dataCache, item)
431 except bb.providers.NoProvider:
432 if self.abort:
433 raise
434 self.remove_buildtarget(self.getbuild_id(item))
435
436 self.mark_external_target(item)
437
438 def add_provider_internal(self, cfgData, dataCache, item):
439 """
440 Add the providers of item to the task data
441 Mark entries were specifically added externally as against dependencies
442 added internally during dependency resolution
443 """
444
445 if re_match_strings(item, dataCache.ignored_dependencies):
446 return
447
448 if not item in dataCache.providers:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500449 close_matches = self.get_close_matches(item, dataCache.providers.keys())
450 # Is it in RuntimeProviders ?
451 all_p = bb.providers.getRuntimeProviders(dataCache, item)
452 for fn in all_p:
453 new = dataCache.pkg_fn[fn] + " RPROVIDES " + item
454 if new not in close_matches:
455 close_matches.append(new)
456 bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=self.get_reasons(item), close_matches=close_matches), cfgData)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500457 raise bb.providers.NoProvider(item)
458
459 if self.have_build_target(item):
460 return
461
462 all_p = dataCache.providers[item]
463
464 eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
465 eligible = [p for p in eligible if not self.getfn_id(p) in self.failed_fnids]
466
467 if not eligible:
468 bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item), reasons=["No eligible PROVIDERs exist for '%s'" % item]), cfgData)
469 raise bb.providers.NoProvider(item)
470
471 if len(eligible) > 1 and foundUnique == False:
472 if item not in self.consider_msgs_cache:
473 providers_list = []
474 for fn in eligible:
475 providers_list.append(dataCache.pkg_fn[fn])
476 bb.event.fire(bb.event.MultipleProviders(item, providers_list), cfgData)
477 self.consider_msgs_cache.append(item)
478
479 for fn in eligible:
480 fnid = self.getfn_id(fn)
481 if fnid in self.failed_fnids:
482 continue
483 logger.debug(2, "adding %s to satisfy %s", fn, item)
484 self.add_build_target(fn, item)
485 self.add_tasks(fn, dataCache)
486
487
488 #item = dataCache.pkg_fn[fn]
489
490 def add_rprovider(self, cfgData, dataCache, item):
491 """
492 Add the runtime providers of item to the task data
493 (takes item names from RDEPENDS/PACKAGES namespace)
494 """
495
496 if re_match_strings(item, dataCache.ignored_dependencies):
497 return
498
499 if self.have_runtime_target(item):
500 return
501
502 all_p = bb.providers.getRuntimeProviders(dataCache, item)
503
504 if not all_p:
505 bb.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees_str(item), reasons=self.get_reasons(item, True)), cfgData)
506 raise bb.providers.NoRProvider(item)
507
508 eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache)
509 eligible = [p for p in eligible if not self.getfn_id(p) in self.failed_fnids]
510
511 if not eligible:
512 bb.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees_str(item), reasons=["No eligible RPROVIDERs exist for '%s'" % item]), cfgData)
513 raise bb.providers.NoRProvider(item)
514
515 if len(eligible) > 1 and numberPreferred == 0:
516 if item not in self.consider_msgs_cache:
517 providers_list = []
518 for fn in eligible:
519 providers_list.append(dataCache.pkg_fn[fn])
520 bb.event.fire(bb.event.MultipleProviders(item, providers_list, runtime=True), cfgData)
521 self.consider_msgs_cache.append(item)
522
523 if numberPreferred > 1:
524 if item not in self.consider_msgs_cache:
525 providers_list = []
526 for fn in eligible:
527 providers_list.append(dataCache.pkg_fn[fn])
528 bb.event.fire(bb.event.MultipleProviders(item, providers_list, runtime=True), cfgData)
529 self.consider_msgs_cache.append(item)
530 raise bb.providers.MultipleRProvider(item)
531
532 # run through the list until we find one that we can build
533 for fn in eligible:
534 fnid = self.getfn_id(fn)
535 if fnid in self.failed_fnids:
536 continue
537 logger.debug(2, "adding '%s' to satisfy runtime '%s'", fn, item)
538 self.add_runtime_target(fn, item)
539 self.add_tasks(fn, dataCache)
540
541 def fail_fnid(self, fnid, missing_list=None):
542 """
543 Mark a file as failed (unbuildable)
544 Remove any references from build and runtime provider lists
545
546 missing_list, A list of missing requirements for this target
547 """
548 if fnid in self.failed_fnids:
549 return
550 if not missing_list:
551 missing_list = []
552 logger.debug(1, "File '%s' is unbuildable, removing...", self.fn_index[fnid])
553 self.failed_fnids.append(fnid)
554 for target in self.build_targets:
555 if fnid in self.build_targets[target]:
556 self.build_targets[target].remove(fnid)
557 if len(self.build_targets[target]) == 0:
558 self.remove_buildtarget(target, missing_list)
559 for target in self.run_targets:
560 if fnid in self.run_targets[target]:
561 self.run_targets[target].remove(fnid)
562 if len(self.run_targets[target]) == 0:
563 self.remove_runtarget(target, missing_list)
564
565 def remove_buildtarget(self, targetid, missing_list=None):
566 """
567 Mark a build target as failed (unbuildable)
568 Trigger removal of any files that have this as a dependency
569 """
570 if not missing_list:
571 missing_list = [self.build_names_index[targetid]]
572 else:
573 missing_list = [self.build_names_index[targetid]] + missing_list
574 logger.verbose("Target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", self.build_names_index[targetid], missing_list)
575 self.failed_deps.append(targetid)
576 dependees = self.get_dependees(targetid)
577 for fnid in dependees:
578 self.fail_fnid(fnid, missing_list)
579 for taskid in xrange(len(self.tasks_idepends)):
580 idepends = self.tasks_idepends[taskid]
581 for (idependid, idependtask) in idepends:
582 if idependid == targetid:
583 self.fail_fnid(self.tasks_fnid[taskid], missing_list)
584
585 if self.abort and targetid in self.external_targets:
586 target = self.build_names_index[targetid]
587 logger.error("Required build target '%s' has no buildable providers.\nMissing or unbuildable dependency chain was: %s", target, missing_list)
588 raise bb.providers.NoProvider(target)
589
590 def remove_runtarget(self, targetid, missing_list=None):
591 """
592 Mark a run target as failed (unbuildable)
593 Trigger removal of any files that have this as a dependency
594 """
595 if not missing_list:
596 missing_list = [self.run_names_index[targetid]]
597 else:
598 missing_list = [self.run_names_index[targetid]] + missing_list
599
600 logger.info("Runtime target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", self.run_names_index[targetid], missing_list)
601 self.failed_rdeps.append(targetid)
602 dependees = self.get_rdependees(targetid)
603 for fnid in dependees:
604 self.fail_fnid(fnid, missing_list)
605 for taskid in xrange(len(self.tasks_irdepends)):
606 irdepends = self.tasks_irdepends[taskid]
607 for (idependid, idependtask) in irdepends:
608 if idependid == targetid:
609 self.fail_fnid(self.tasks_fnid[taskid], missing_list)
610
611 def add_unresolved(self, cfgData, dataCache):
612 """
613 Resolve all unresolved build and runtime targets
614 """
615 logger.info("Resolving any missing task queue dependencies")
616 while True:
617 added = 0
618 for target in self.get_unresolved_build_targets(dataCache):
619 try:
620 self.add_provider_internal(cfgData, dataCache, target)
621 added = added + 1
622 except bb.providers.NoProvider:
623 targetid = self.getbuild_id(target)
624 if self.abort and targetid in self.external_targets and not self.allowincomplete:
625 raise
626 if not self.allowincomplete:
627 self.remove_buildtarget(targetid)
628 for target in self.get_unresolved_run_targets(dataCache):
629 try:
630 self.add_rprovider(cfgData, dataCache, target)
631 added = added + 1
632 except (bb.providers.NoRProvider, bb.providers.MultipleRProvider):
633 self.remove_runtarget(self.getrun_id(target))
634 logger.debug(1, "Resolved " + str(added) + " extra dependencies")
635 if added == 0:
636 break
637 # self.dump_data()
638
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500639 def get_providermap(self, prefix=None):
640 provmap = {}
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500641 for name in self.build_names_index:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500642 if prefix and not name.startswith(prefix):
643 continue
644 if self.have_build_target(name):
645 provider = self.get_provider(name)
646 if provider:
647 provmap[name] = self.fn_index[provider[0]]
648 return provmap
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500649
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500650 def dump_data(self):
651 """
652 Dump some debug information on the internal data structures
653 """
654 logger.debug(3, "build_names:")
655 logger.debug(3, ", ".join(self.build_names_index))
656
657 logger.debug(3, "run_names:")
658 logger.debug(3, ", ".join(self.run_names_index))
659
660 logger.debug(3, "build_targets:")
661 for buildid in xrange(len(self.build_names_index)):
662 target = self.build_names_index[buildid]
663 targets = "None"
664 if buildid in self.build_targets:
665 targets = self.build_targets[buildid]
666 logger.debug(3, " (%s)%s: %s", buildid, target, targets)
667
668 logger.debug(3, "run_targets:")
669 for runid in xrange(len(self.run_names_index)):
670 target = self.run_names_index[runid]
671 targets = "None"
672 if runid in self.run_targets:
673 targets = self.run_targets[runid]
674 logger.debug(3, " (%s)%s: %s", runid, target, targets)
675
676 logger.debug(3, "tasks:")
677 for task in xrange(len(self.tasks_name)):
678 logger.debug(3, " (%s)%s - %s: %s",
679 task,
680 self.fn_index[self.tasks_fnid[task]],
681 self.tasks_name[task],
682 self.tasks_tdepends[task])
683
684 logger.debug(3, "dependency ids (per fn):")
685 for fnid in self.depids:
686 logger.debug(3, " %s %s: %s", fnid, self.fn_index[fnid], self.depids[fnid])
687
688 logger.debug(3, "runtime dependency ids (per fn):")
689 for fnid in self.rdepids:
690 logger.debug(3, " %s %s: %s", fnid, self.fn_index[fnid], self.rdepids[fnid])