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