When SRCREV autorevisioning for a recipe is in use, don't cache the recipe (from...
[vuplus_bitbake] / lib / bb / taskdata.py
1 #!/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 """
5 BitBake 'TaskData' implementation
6
7 Task 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
26 from bb import data, event, mkdirhier, utils
27 import bb, os
28
29 class TaskData:
30     """
31     BitBake Task Data implementation
32     """
33     def __init__(self, abort = True):
34         self.build_names_index = []
35         self.run_names_index = []
36         self.fn_index = []
37
38         self.build_targets = {}
39         self.run_targets = {}
40
41         self.external_targets = []
42
43         self.tasks_fnid = []
44         self.tasks_name = []
45         self.tasks_tdepends = []
46         self.tasks_idepends = []
47         # Cache to speed up task ID lookups
48         self.tasks_lookup = {}
49
50         self.depids = {}
51         self.rdepids = {}
52
53         self.consider_msgs_cache = []
54
55         self.failed_deps = []
56         self.failed_rdeps = []
57         self.failed_fnids = []
58
59         self.abort = abort
60
61     def getbuild_id(self, name):
62         """
63         Return an ID number for the build target name.
64         If it doesn't exist, create one.
65         """
66         if not name in self.build_names_index:
67             self.build_names_index.append(name)
68             return len(self.build_names_index) - 1
69
70         return self.build_names_index.index(name)
71
72     def getrun_id(self, name):
73         """
74         Return an ID number for the run target name. 
75         If it doesn't exist, create one.
76         """
77         if not name in self.run_names_index:
78             self.run_names_index.append(name)
79             return len(self.run_names_index) - 1
80
81         return self.run_names_index.index(name)
82
83     def getfn_id(self, name):
84         """
85         Return an ID number for the filename. 
86         If it doesn't exist, create one.
87         """
88         if not name in self.fn_index:
89             self.fn_index.append(name)
90             return len(self.fn_index) - 1
91
92         return self.fn_index.index(name)
93
94     def gettask_ids(self, fnid):
95         """
96         Return an array of the ID numbers matching a given fnid.
97         """
98         ids = []
99         if fnid in self.tasks_lookup:
100             for task in self.tasks_lookup[fnid]:
101                 ids.append(self.tasks_lookup[fnid][task])
102         return ids
103
104     def gettask_id(self, fn, task, create = True):
105         """
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.
109         """
110         fnid = self.getfn_id(fn)
111
112         if fnid in self.tasks_lookup:
113             if task in self.tasks_lookup[fnid]:
114                 return self.tasks_lookup[fnid][task]
115
116         if not create:
117             return None
118
119         self.tasks_name.append(task)
120         self.tasks_fnid.append(fnid)
121         self.tasks_tdepends.append([])
122         self.tasks_idepends.append([])
123
124         listid = len(self.tasks_name) - 1
125
126         if fnid not in self.tasks_lookup:
127             self.tasks_lookup[fnid] = {}
128         self.tasks_lookup[fnid][task] = listid
129
130         return listid
131
132     def add_tasks(self, fn, dataCache):
133         """
134         Add tasks for a given fn to the database
135         """
136
137         task_deps = dataCache.task_deps[fn]
138
139         fnid = self.getfn_id(fn)
140
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...")
143
144         # Check if we've already seen this fn
145         if fnid in self.tasks_fnid:
146             return
147
148         for task in task_deps['tasks']:
149
150             # Work out task dependencies
151             parentids = []
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)
157
158             # Touch all intertask dependencies
159             if 'depends' in task_deps and task in task_deps['depends']:
160                 ids = []
161                 for dep in task_deps['depends'][task].split():
162                     if dep:
163                         ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
164                 self.tasks_idepends[taskid].extend(ids)
165
166         # Work out build dependencies
167         if not fnid in self.depids:
168             dependids = {}
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()
173
174         # Work out runtime dependencies
175         if not fnid in self.rdepids:
176             rdependids = {}
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()
188
189         for dep in self.depids[fnid]:
190             if dep in self.failed_deps:
191                 self.fail_fnid(fnid)
192                 return
193         for dep in self.rdepids[fnid]:
194             if dep in self.failed_rdeps:
195                 self.fail_fnid(fnid)
196                 return
197
198     def have_build_target(self, target):
199         """
200         Have we a build target matching this name?
201         """
202         targetid = self.getbuild_id(target)
203
204         if targetid in self.build_targets:
205             return True
206         return False
207
208     def have_runtime_target(self, target):
209         """
210         Have we a runtime target matching this name?
211         """
212         targetid = self.getrun_id(target)
213
214         if targetid in self.run_targets:
215             return True
216         return False
217
218     def add_build_target(self, fn, item):
219         """
220         Add a build target.
221         If already present, append the provider fn to the list
222         """
223         targetid = self.getbuild_id(item)
224         fnid = self.getfn_id(fn)
225
226         if targetid in self.build_targets:
227             if fnid in self.build_targets[targetid]:
228                 return
229             self.build_targets[targetid].append(fnid)
230             return
231         self.build_targets[targetid] = [fnid]
232
233     def add_runtime_target(self, fn, item):
234         """
235         Add a runtime target.
236         If already present, append the provider fn to the list
237         """
238         targetid = self.getrun_id(item)
239         fnid = self.getfn_id(fn)
240
241         if targetid in self.run_targets:
242             if fnid in self.run_targets[targetid]:
243                 return
244             self.run_targets[targetid].append(fnid)
245             return
246         self.run_targets[targetid] = [fnid]
247
248     def mark_external_target(self, item):
249         """
250         Mark a build target as being externally requested
251         """
252         targetid = self.getbuild_id(item)
253
254         if targetid not in self.external_targets:
255             self.external_targets.append(targetid)
256
257     def get_unresolved_build_targets(self, dataCache):
258         """
259         Return a list of build targets who's providers 
260         are unknown.
261         """
262         unresolved = []
263         for target in self.build_names_index:
264             if target in dataCache.ignored_dependencies:
265                 continue
266             if self.build_names_index.index(target) in self.failed_deps:
267                 continue
268             if not self.have_build_target(target):
269                 unresolved.append(target)
270         return unresolved
271
272     def get_unresolved_run_targets(self, dataCache):
273         """
274         Return a list of runtime targets who's providers 
275         are unknown.
276         """
277         unresolved = []
278         for target in self.run_names_index:
279             if target in dataCache.ignored_dependencies:
280                 continue
281             if self.run_names_index.index(target) in self.failed_rdeps:
282                 continue
283             if not self.have_runtime_target(target):
284                 unresolved.append(target)
285         return unresolved
286
287     def get_provider(self, item):
288         """
289         Return a list of providers of item
290         """
291         targetid = self.getbuild_id(item)
292    
293         return self.build_targets[targetid]
294
295     def get_dependees(self, itemid):
296         """
297         Return a list of targets which depend on item
298         """
299         dependees = []
300         for fnid in self.depids:
301             if itemid in self.depids[fnid]:
302                 dependees.append(fnid)
303         return dependees
304
305     def get_dependees_str(self, item):
306         """
307         Return a list of targets which depend on item as a user readable string
308         """
309         itemid = self.getbuild_id(item)
310         dependees = []
311         for fnid in self.depids:
312             if itemid in self.depids[fnid]:
313                 dependees.append(self.fn_index[fnid])
314         return dependees
315
316     def get_rdependees(self, itemid):
317         """
318         Return a list of targets which depend on runtime item
319         """
320         dependees = []
321         for fnid in self.rdepids:
322             if itemid in self.rdepids[fnid]:
323                 dependees.append(fnid)
324         return dependees
325
326     def get_rdependees_str(self, item):
327         """
328         Return a list of targets which depend on runtime item as a user readable string
329         """
330         itemid = self.getrun_id(item)
331         dependees = []
332         for fnid in self.rdepids:
333             if itemid in self.rdepids[fnid]:
334                 dependees.append(self.fn_index[fnid])
335         return dependees
336
337     def add_provider(self, cfgData, dataCache, item):
338         try:
339             self.add_provider_internal(cfgData, dataCache, item)
340         except bb.providers.NoProvider:
341             if self.abort:
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)))
344                 else:
345                     bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item))
346                 raise
347             targetid = self.getbuild_id(item)
348             self.remove_buildtarget(targetid)
349
350         self.mark_external_target(item)
351
352     def add_provider_internal(self, cfgData, dataCache, item):
353         """
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
357         """
358
359         if item in dataCache.ignored_dependencies:
360             return
361
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)))
365             else:
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)
369
370         if self.have_build_target(item):
371             return
372
373         all_p = dataCache.providers[item]
374
375         eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
376
377         for p in eligible:
378             fnid = self.getfn_id(p)
379             if fnid in self.failed_fnids:
380                 eligible.remove(p)
381
382         if not eligible:
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)
386
387         if len(eligible) > 1 and foundUnique == False:
388             if item not in self.consider_msgs_cache:
389                 providers_list = []
390                 for fn in eligible:
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)
396
397         for fn in eligible:
398             fnid = self.getfn_id(fn)
399             if fnid in self.failed_fnids:
400                 continue
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)
404
405
406             #item = dataCache.pkg_fn[fn]
407
408     def add_rprovider(self, cfgData, dataCache, item):
409         """
410         Add the runtime providers of item to the task data
411         (takes item names from RDEPENDS/PACKAGES namespace)
412         """
413
414         if item in dataCache.ignored_dependencies:
415             return
416
417         if self.have_runtime_target(item):
418             return
419
420         all_p = bb.providers.getRuntimeProviders(dataCache, item)
421
422         if not all_p:
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)
426
427         eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache)
428
429         for p in eligible:
430             fnid = self.getfn_id(p)
431             if fnid in self.failed_fnids:
432                 eligible.remove(p)
433
434         if not eligible:
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)
438
439         if len(eligible) > 1 and numberPreferred == 0:
440             if item not in self.consider_msgs_cache:
441                 providers_list = []
442                 for fn in eligible:
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)
448
449         if numberPreferred > 1:
450             if item not in self.consider_msgs_cache:
451                 providers_list = []
452                 for fn in eligible:
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)
458
459         # run through the list until we find one that we can build
460         for fn in eligible:
461             fnid = self.getfn_id(fn)
462             if fnid in self.failed_fnids:
463                 continue
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)
467
468     def fail_fnid(self, fnid, missing_list = []):
469         """
470         Mark a file as failed (unbuildable)
471         Remove any references from build and runtime provider lists
472
473         missing_list, A list of missing requirements for this target
474         """
475         if fnid in self.failed_fnids:
476             return
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)
489
490     def remove_buildtarget(self, targetid, missing_list = []):
491         """
492         Mark a build target as failed (unbuildable)
493         Trigger removal of any files that have this as a dependency
494         """
495         if not missing_list:
496             missing_list = [self.build_names_index[targetid]]
497         else:
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)
509
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
513
514     def remove_runtarget(self, targetid, missing_list = []):
515         """
516         Mark a run target as failed (unbuildable)
517         Trigger removal of any files that have this as a dependency
518         """
519         if not missing_list:
520             missing_list = [self.run_names_index[targetid]]
521         else:
522             missing_list = [self.run_names_index[targetid]] + missing_list
523
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)
529
530     def add_unresolved(self, cfgData, dataCache):
531         """
532         Resolve all unresolved build and runtime targets
533         """
534         bb.msg.note(1, bb.msg.domain.TaskData, "Resolving any missing task queue dependencies")
535         while 1:
536             added = 0
537             for target in self.get_unresolved_build_targets(dataCache):
538                 try:
539                     self.add_provider_internal(cfgData, dataCache, target)
540                     added = added + 1
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)))
546                         else:
547                             bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (target))
548                         raise
549                     self.remove_buildtarget(targetid)
550             for target in self.get_unresolved_run_targets(dataCache):
551                 try:
552                     self.add_rprovider(cfgData, dataCache, target)
553                     added = added + 1
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")
557             if added == 0:
558                 break
559         # self.dump_data()
560
561     def dump_data(self):
562         """
563         Dump some debug information on the internal data structures
564         """
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))
567
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))
570
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]
574             targets = "None"
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))
578
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]
582             targets = "None"
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))
586
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" % (
590                 task, 
591                 self.fn_index[self.tasks_fnid[task]], 
592                 self.tasks_name[task], 
593                 self.tasks_tdepends[task]))
594
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]))
598
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]))
602
603