Add tryaltconfigs option to control whether bitbake trys using alternative providers...
[vuplus_bitbake] / lib / bb / taskdata.py
index 11261ed..64ab032 100644 (file)
@@ -23,14 +23,14 @@ Task data collection and handling
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-from bb import data, fetch, event, mkdirhier, utils
+from bb import data, event, mkdirhier, utils
 import bb, os
 
 class TaskData:
     """
     BitBake Task Data implementation
     """
-    def __init__(self, abort = True):
+    def __init__(self, abort = True, tryaltconfigs = False):
         self.build_names_index = []
         self.run_names_index = []
         self.fn_index = []
@@ -43,6 +43,7 @@ class TaskData:
         self.tasks_fnid = []
         self.tasks_name = []
         self.tasks_tdepends = []
+        self.tasks_idepends = []
         # Cache to speed up task ID lookups
         self.tasks_lookup = {}
 
@@ -56,6 +57,7 @@ class TaskData:
         self.failed_fnids = []
 
         self.abort = abort
+        self.tryaltconfigs = tryaltconfigs
 
     def getbuild_id(self, name):
         """
@@ -90,10 +92,21 @@ class TaskData:
 
         return self.fn_index.index(name)
 
-    def gettask_id(self, fn, task):
+    def gettask_ids(self, fnid):
+        """
+        Return an array of the ID numbers matching a given fnid.
+        """
+        ids = []
+        if fnid in self.tasks_lookup:
+            for task in self.tasks_lookup[fnid]:
+                ids.append(self.tasks_lookup[fnid][task])
+        return ids
+
+    def gettask_id(self, fn, task, create = True):
         """
         Return an ID number for the task matching fn and task.
-        If it doesn't exist, create one.
+        If it doesn't exist, create one by default.
+        Optionally return None instead.
         """
         fnid = self.getfn_id(fn)
 
@@ -101,9 +114,13 @@ class TaskData:
             if task in self.tasks_lookup[fnid]:
                 return self.tasks_lookup[fnid][task]
 
+        if not create:
+            return None
+
         self.tasks_name.append(task)
         self.tasks_fnid.append(fnid)
         self.tasks_tdepends.append([])
+        self.tasks_idepends.append([])
 
         listid = len(self.tasks_name) - 1
 
@@ -118,7 +135,6 @@ class TaskData:
         Add tasks for a given fn to the database
         """
 
-        task_graph = dataCache.task_queues[fn]
         task_deps = dataCache.task_deps[fn]
 
         fnid = self.getfn_id(fn)
@@ -130,15 +146,24 @@ class TaskData:
         if fnid in self.tasks_fnid:
             return
 
-        # Work out task dependencies
-        for task in task_graph.allnodes():
+        for task in task_deps['tasks']:
+
+            # Work out task dependencies
             parentids = []
-            for dep in task_graph.getparents(task):
+            for dep in task_deps['parents'][task]:
                 parentid = self.gettask_id(fn, dep)
                 parentids.append(parentid)
             taskid = self.gettask_id(fn, task)
             self.tasks_tdepends[taskid].extend(parentids)
 
+            # Touch all intertask dependencies
+            if 'depends' in task_deps and task in task_deps['depends']:
+                ids = []
+                for dep in task_deps['depends'][task].split():
+                    if dep:
+                        ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
+                self.tasks_idepends[taskid].extend(ids)
+
         # Work out build dependencies
         if not fnid in self.depids:
             dependids = {}
@@ -153,11 +178,11 @@ class TaskData:
             rdepends = dataCache.rundeps[fn]
             rrecs = dataCache.runrecs[fn]
             for package in rdepends:
-                for rdepend in rdepends[package]:
+                for rdepend in bb.utils.explode_deps(rdepends[package]):
                     bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime dependency %s for %s" % (rdepend, fn))
                     rdependids[self.getrun_id(rdepend)] = None
             for package in rrecs:
-                for rdepend in rrecs[package]:
+                for rdepend in bb.utils.explode_deps(rrecs[package]):
                     bb.msg.debug(2, bb.msg.domain.TaskData, "Added runtime recommendation %s for %s" % (rdepend, fn))
                     rdependids[self.getrun_id(rdepend)] = None
             self.rdepids[fnid] = rdependids.keys()
@@ -315,7 +340,10 @@ class TaskData:
             self.add_provider_internal(cfgData, dataCache, item)
         except bb.providers.NoProvider:
             if self.abort:
-                bb.msg.error(bb.msg.domain.Provider, "No providers of build target %s (for %s)" % (item, self.get_dependees_str(item)))
+                if self.get_rdependees_str(item):
+                    bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (item, self.get_dependees_str(item)))
+                else:
+                    bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item))
                 raise
             targetid = self.getbuild_id(item)
             self.remove_buildtarget(targetid)
@@ -333,7 +361,10 @@ class TaskData:
             return
 
         if not item in dataCache.providers:
-            bb.msg.debug(1, bb.msg.domain.Provider, "No providers of build target %s (for %s)" % (item, self.get_dependees_str(item)))
+            if self.get_rdependees_str(item):
+                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)))
+            else:
+                bb.msg.note(2, bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (item))
             bb.event.fire(bb.event.NoProvider(item, cfgData))
             raise bb.providers.NoProvider(item)
 
@@ -342,7 +373,7 @@ class TaskData:
 
         all_p = dataCache.providers[item]
 
-        eligible = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
+        eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
 
         for p in eligible:
             fnid = self.getfn_id(p)
@@ -350,33 +381,18 @@ class TaskData:
                 eligible.remove(p)
 
         if not eligible:
-            bb.msg.debug(1, bb.msg.domain.Provider, "No providers of build target %s after filtering (for %s)" % (item, self.get_dependees_str(item)))
+            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)))
             bb.event.fire(bb.event.NoProvider(item, cfgData))
             raise bb.providers.NoProvider(item)
 
-        prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, cfgData, 1)
-        if prefervar:
-            dataCache.preferred[item] = prefervar
-
-        discriminated = False
-        if item in dataCache.preferred:
-            for p in eligible:
-                pn = dataCache.pkg_fn[p]
-                if dataCache.preferred[item] == pn:
-                    bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
-                    eligible.remove(p)
-                    eligible = [p] + eligible
-                    discriminated = True
-                    break
-
-        if len(eligible) > 1 and discriminated == False:
+        if len(eligible) > 1 and foundUnique == False:
             if item not in self.consider_msgs_cache:
                 providers_list = []
                 for fn in eligible:
                     providers_list.append(dataCache.pkg_fn[fn])
                 bb.msg.note(1, bb.msg.domain.Provider, "multiple providers are available for %s (%s);" % (item, ", ".join(providers_list)))
                 bb.msg.note(1, bb.msg.domain.Provider, "consider defining PREFERRED_PROVIDER_%s" % item)
-                bb.event.fire(bb.event.MultipleProviders(item,providers_list,cfgData))
+                bb.event.fire(bb.event.MultipleProviders(item, providers_list, cfgData))
             self.consider_msgs_cache.append(item)
 
         for fn in eligible:
@@ -405,11 +421,11 @@ class TaskData:
         all_p = bb.providers.getRuntimeProviders(dataCache, item)
 
         if not all_p:
-            bb.msg.error(bb.msg.domain.Provider, "No providers of runtime build target %s (for %s)" % (item, self.get_rdependees_str(item)))
+            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))
             bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True))
             raise bb.providers.NoRProvider(item)
 
-        eligible = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
+        eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache)
 
         for p in eligible:
             fnid = self.getfn_id(p)
@@ -417,24 +433,11 @@ class TaskData:
                 eligible.remove(p)
 
         if not eligible:
-            bb.msg.error(bb.msg.domain.Provider, "No providers of runtime build target %s after filtering (for %s)" % (item, self.get_rdependees_str(item)))
+            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))
             bb.event.fire(bb.event.NoProvider(item, cfgData, runtime=True))
             raise bb.providers.NoRProvider(item)
 
-        # Should use dataCache.preferred here?
-        preferred = []
-        for p in eligible:
-            pn = dataCache.pkg_fn[p]
-            provides = dataCache.pn_provides[pn]
-            for provide in provides:
-                prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, cfgData, 1)
-                if prefervar == pn:
-                    bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
-                    eligible.remove(p)
-                    eligible = [p] + eligible
-                    preferred.append(p)
-
-        if len(eligible) > 1 and len(preferred) == 0:
+        if len(eligible) > 1 and numberPreferred == 0:
             if item not in self.consider_msgs_cache:
                 providers_list = []
                 for fn in eligible:
@@ -444,12 +447,12 @@ class TaskData:
                 bb.event.fire(bb.event.MultipleProviders(item,providers_list, cfgData, runtime=True))
             self.consider_msgs_cache.append(item)
 
-        if len(preferred) > 1:
+        if numberPreferred > 1:
             if item not in self.consider_msgs_cache:
                 providers_list = []
-                for fn in preferred:
+                for fn in eligible:
                     providers_list.append(dataCache.pkg_fn[fn])
-                bb.msg.note(2, bb.msg.domain.Provider, "multiple preferred providers are available for runtime %s (%s);" % (item, ", ".join(providers_list)))
+                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)))
                 bb.msg.note(2, bb.msg.domain.Provider, "consider defining only one PREFERRED_PROVIDER entry to match runtime %s" % item)
                 bb.event.fire(bb.event.MultipleProviders(item,providers_list, cfgData, runtime=True))
             self.consider_msgs_cache.append(item)
@@ -459,60 +462,77 @@ class TaskData:
             fnid = self.getfn_id(fn)
             if fnid in self.failed_fnids:
                 continue
-            bb.msg.debug(2, bb.msg.domain.Provider, "adding %s to satisfy runtime %s" % (fn, item))
+            bb.msg.debug(2, bb.msg.domain.Provider, "adding '%s' to satisfy runtime '%s'" % (fn, item))
             self.add_runtime_target(fn, item)
             self.add_tasks(fn, dataCache)
 
-    def fail_fnid(self, fnid):
+    def fail_fnid(self, fnid, missing_list = []):
         """
         Mark a file as failed (unbuildable)
         Remove any references from build and runtime provider lists
+
+        missing_list, A list of missing requirements for this target
         """
         if fnid in self.failed_fnids:
             return
-        bb.msg.debug(1, bb.msg.domain.Provider, "Removing failed file %s" % self.fn_index[fnid])
+        bb.msg.debug(1, bb.msg.domain.Provider, "File '%s' is unbuildable, removing..." % self.fn_index[fnid])
         self.failed_fnids.append(fnid)
         for target in self.build_targets:
             if fnid in self.build_targets[target]:
                 self.build_targets[target].remove(fnid)
                 if len(self.build_targets[target]) == 0:
-                    self.remove_buildtarget(target)
+                    self.remove_buildtarget(target, missing_list)
         for target in self.run_targets:
             if fnid in self.run_targets[target]:
                 self.run_targets[target].remove(fnid)
                 if len(self.run_targets[target]) == 0:
-                    self.remove_runtarget(target)
+                    self.remove_runtarget(target, missing_list)
 
-    def remove_buildtarget(self, targetid):
+    def remove_buildtarget(self, targetid, missing_list = []):
         """
         Mark a build target as failed (unbuildable)
         Trigger removal of any files that have this as a dependency
         """
-        bb.msg.debug(1, bb.msg.domain.Provider, "Removing failed build target %s" % self.build_names_index[targetid])
+        if not missing_list:
+            missing_list = [self.build_names_index[targetid]]
+        else:
+            missing_list = [self.build_names_index[targetid]] + missing_list
+        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))
         self.failed_deps.append(targetid)
         dependees = self.get_dependees(targetid)
         for fnid in dependees:
-            self.fail_fnid(fnid)
+            self.fail_fnid(fnid, missing_list)
+        for taskid in range(len(self.tasks_idepends)):
+            idepends = self.tasks_idepends[taskid]
+            for (idependid, idependtask) in idepends:
+                if idependid == targetid:
+                    self.fail_fnid(self.tasks_fnid[taskid], missing_list)
+
         if self.abort and targetid in self.external_targets:
-            bb.msg.error(bb.msg.domain.Provider, "No buildable providers available for required build target %s" % self.build_names_index[targetid])
+            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))
             raise bb.providers.NoProvider
 
-    def remove_runtarget(self, targetid):
+    def remove_runtarget(self, targetid, missing_list = []):
         """
         Mark a run target as failed (unbuildable)
         Trigger removal of any files that have this as a dependency
         """
-        bb.msg.note(1, bb.msg.domain.Provider, "Removing failed runtime build target %s" % self.run_names_index[targetid])
+        if not missing_list:
+            missing_list = [self.run_names_index[targetid]]
+        else:
+            missing_list = [self.run_names_index[targetid]] + missing_list
+
+        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))
         self.failed_rdeps.append(targetid)
         dependees = self.get_rdependees(targetid)
         for fnid in dependees:
-            self.fail_fnid(fnid)
+            self.fail_fnid(fnid, missing_list)
 
     def add_unresolved(self, cfgData, dataCache):
         """
         Resolve all unresolved build and runtime targets
         """
-        bb.msg.note(1, bb.msg.domain.TaskData, "Resolving missing task queue dependencies")
+        bb.msg.note(1, bb.msg.domain.TaskData, "Resolving any missing task queue dependencies")
         while 1:
             added = 0
             for target in self.get_unresolved_build_targets(dataCache):
@@ -522,6 +542,10 @@ class TaskData:
                 except bb.providers.NoProvider:
                     targetid = self.getbuild_id(target)
                     if self.abort and targetid in self.external_targets:
+                        if self.get_rdependees_str(target):
+                            bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s' (but '%s' DEPENDS on or otherwise requires it)" % (target, self.get_dependees_str(target)))
+                        else:
+                            bb.msg.error(bb.msg.domain.Provider, "Nothing PROVIDES '%s'" % (target))
                         raise
                     self.remove_buildtarget(targetid)
             for target in self.get_unresolved_run_targets(dataCache):
@@ -533,6 +557,7 @@ class TaskData:
             bb.msg.debug(1, bb.msg.domain.TaskData, "Resolved " + str(added) + " extra dependecies")
             if added == 0:
                 break
+        # self.dump_data()
 
     def dump_data(self):
         """
@@ -540,14 +565,26 @@ class TaskData:
         """
         bb.msg.debug(3, bb.msg.domain.TaskData, "build_names:")
         bb.msg.debug(3, bb.msg.domain.TaskData, ", ".join(self.build_names_index))
+
         bb.msg.debug(3, bb.msg.domain.TaskData, "run_names:")
         bb.msg.debug(3, bb.msg.domain.TaskData, ", ".join(self.run_names_index))
+
         bb.msg.debug(3, bb.msg.domain.TaskData, "build_targets:")
-        for target in self.build_targets.keys():
-            bb.msg.debug(3, bb.msg.domain.TaskData, " %s: %s" % (self.build_names_index[target], self.build_targets[target]))
+        for buildid in range(len(self.build_names_index)):
+            target = self.build_names_index[buildid]
+            targets = "None"
+            if buildid in self.build_targets:
+                targets = self.build_targets[buildid]
+            bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s: %s" % (buildid, target, targets))
+
         bb.msg.debug(3, bb.msg.domain.TaskData, "run_targets:")
-        for target in self.run_targets.keys():
-            bb.msg.debug(3, bb.msg.domain.TaskData, " %s: %s" % (self.run_names_index[target], self.run_targets[target]))
+        for runid in range(len(self.run_names_index)):
+            target = self.run_names_index[runid]
+            targets = "None"
+            if runid in self.run_targets:
+                targets = self.run_targets[runid]
+            bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s: %s" % (runid, target, targets))
+
         bb.msg.debug(3, bb.msg.domain.TaskData, "tasks:")
         for task in range(len(self.tasks_name)):
             bb.msg.debug(3, bb.msg.domain.TaskData, " (%s)%s - %s: %s" % (
@@ -555,7 +592,12 @@ class TaskData:
                 self.fn_index[self.tasks_fnid[task]], 
                 self.tasks_name[task], 
                 self.tasks_tdepends[task]))
-        bb.msg.debug(3, bb.msg.domain.TaskData, "runtime ids (per fn):")
+
+        bb.msg.debug(3, bb.msg.domain.TaskData, "dependency ids (per fn):")
+        for fnid in self.depids:
+            bb.msg.debug(3, bb.msg.domain.TaskData, " %s %s: %s" % (fnid, self.fn_index[fnid], self.depids[fnid]))
+
+        bb.msg.debug(3, bb.msg.domain.TaskData, "runtime dependency ids (per fn):")
         for fnid in self.rdepids:
             bb.msg.debug(3, bb.msg.domain.TaskData, " %s %s: %s" % (fnid, self.fn_index[fnid], self.rdepids[fnid]))