bin/bitbake:
[vuplus_bitbake] / bin / bitbake
index a62811a..ac5a8b7 100755 (executable)
@@ -22,7 +22,7 @@
 # Place, Suite 330, Boston, MA 02111-1307 USA.
 
 import sys, os, getopt, glob, copy, os.path, re, time
-sys.path.append(os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
+sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
 import bb
 from bb import utils, data, parse, debug, event, fatal
 from sets import Set
@@ -31,7 +31,7 @@ import itertools, optparse
 parsespin = itertools.cycle( r'|/-\\' )
 bbdebug = 0
 
-__version__ = "1.3.2"
+__version__ = "1.3.3.2"
 
 #============================================================================#
 # BBParsingStatus
@@ -46,6 +46,7 @@ class BBParsingStatus:
     def __init__(self):
         self.cache_dirty = False
         self.providers   = {}
+        self.rproviders = {}
         self.packages = {}
         self.packages_dynamic = {}
         self.bbfile_priority = {}
@@ -79,6 +80,7 @@ class BBParsingStatus:
         depends  = (bb.data.getVar("DEPENDS", bb_data, True) or "").split()
         packages = (bb.data.getVar('PACKAGES', bb_data, True) or "").split()
         packages_dynamic = (bb.data.getVar('PACKAGES_DYNAMIC', bb_data, True) or "").split()
+        rprovides = (bb.data.getVar("RPROVIDES", bb_data, 1) or "").split()
 
 
         # build PackageName to FileName lookup table
@@ -108,17 +110,22 @@ class BBParsingStatus:
 
         # Build reverse hash for PACKAGES, so runtime dependencies 
         # can be be resolved (RDEPENDS, RRECOMMENDS etc.)
-    
         for package in packages:
             if not package in self.packages:
                 self.packages[package] = []
             self.packages[package].append(file_name)
+            rprovides += (bb.data.getVar("RPROVIDES_%s" % package, bb_data, 1) or "").split() 
 
         for package in packages_dynamic:
             if not package in self.packages_dynamic:
                 self.packages_dynamic[package] = []
             self.packages_dynamic[package].append(file_name)
 
+        for rprovide in rprovides:
+            if not rprovide in self.rproviders:
+                self.rproviders[rprovide] = []
+            self.rproviders[rprovide].append(file_name)
+
         # Collect files we may need for possible world-dep
         # calculations
         if not bb.data.getVar('BROKEN', bb_data, True) and not bb.data.getVar('EXCLUDE_FROM_WORLD', bb_data, True):
@@ -175,6 +182,7 @@ class BBCooker:
     def __init__( self ):
         self.build_cache_fail = []
         self.build_cache = []
+        self.rbuild_cache = []
         self.building_list = []
         self.build_path = []
         self.consider_msgs_cache = []
@@ -211,14 +219,29 @@ class BBCooker:
             self.build_cache_fail.append(fn)
             raise
 
-    def tryBuild( self, fn, virtual , buildAllDeps ):
-        """Build a provider and its dependencies"""
-        if fn in self.building_list:
+    def tryBuild( self, fn, virtual , buildAllDeps , build_depends = []):
+        """
+        Build a provider and its dependencies. 
+        build_depends is a list of previous build dependencies (not runtime)
+        If build_depends is empty, we're dealing with a runtime depends
+        """
+
+        the_data = self.pkgdata[fn]
+
+        if not buildAllDeps:
+            buildAllDeps = bb.data.getVar('BUILD_ALL_DEPS', the_data, True) or False
+
+        # Error on build time dependency loops
+        if build_depends and build_depends.count(fn) > 1:
             bb.error("%s depends on itself (eventually)" % fn)
             bb.error("upwards chain is: %s" % (" -> ".join(self.build_path)))
             return False
 
-        the_data = self.pkgdata[fn]
+        # See if this is a runtime dependency we've already built
+       # Or a build dependency being handled in a different build chain
+        if fn in self.building_list:
+            return self.addRunDeps(fn, virtual , buildAllDeps)
+
         item = self.status.pkg_fn[fn]
 
         self.building_list.append(fn)
@@ -226,18 +249,11 @@ class BBCooker:
         pathstr = "%s (%s)" % (item, virtual)
         self.build_path.append(pathstr)
 
-        depends_list = (bb.data.getVar('DEPENDS', the_data, 1) or "")
-        if not buildAllDeps:
-            buildAllDeps = bb.data.getVar('BUILD_ALL_DEPS', the_data, 1) or False
-
-        if buildAllDeps:
-            depends_list = "%s %s %s" % (depends_list, (bb.data.getVar('RDEPENDS', the_data, 1) or ""), (bb.data.getVar('RRECOMMENDS', the_data, 1) or ""))
+        depends_list = (bb.data.getVar('DEPENDS', the_data, True) or "").split()
 
         if self.configuration.verbose:
             bb.note("current path: %s" % (" -> ".join(self.build_path)))
-            bb.note("dependencies for %s are: %s" % (item, depends_list))
-
-        depends_list = depends_list.split()
+            bb.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
 
         try:
             failed = False
@@ -259,7 +275,7 @@ class BBCooker:
                     continue
                 if not depcmd:
                     continue
-                if self.buildProvider( dependency , buildAllDeps ) == 0:
+                if self.buildProvider( dependency , buildAllDeps , build_depends ) == 0:
                     bb.error("dependency %s (for %s) not satisfied" % (dependency,item))
                     failed = True
                     if self.configuration.abort:
@@ -272,6 +288,9 @@ class BBCooker:
                 self.stats.deps += 1
                 return False
 
+            if not self.addRunDeps(fn, virtual , buildAllDeps):
+                return False
+
             if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
                 self.build_cache.append(fn)
                 return True
@@ -310,7 +329,7 @@ class BBCooker:
         bb.data.setVar('OVERRIDES', "%s:%s" % (pn, data.getVar('OVERRIDES', localdata)), localdata)
         bb.data.update_data(localdata)
 
-        preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, 1)
+        preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, True)
         if preferred_v:
             m = re.match('(.*)_(.*)', preferred_v)
             if m:
@@ -404,51 +423,17 @@ class BBCooker:
             if data.getVarFlag( e, 'python', self.configuration.data ):
                 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
 
-    def getProviders(self, item, buildAllDeps):
-
-        if item in self.status.providers:
-            return self.status.providers[item]
-
-        if not buildAllDeps:
-            return False
-
-        if item in self.status.packages:
-            return self.status.packages[item]
-
-        matches = []
-        for pattern in self.status.packages_dynamic:
-            regexp = re.compile(pattern)
-            if regexp.match(item):
-                for fn in self.status.packages_dynamic[pattern]:
-                    matches.append(fn)
-
-        if matches:
-            return matches
-
-        return False
-
-    def buildProvider( self, item , buildAllDeps ):
-        fn = None
-
-        discriminated = False
-
-        all_p = self.getProviders(item, buildAllDeps)
-
-        if not all_p:
-            bb.error("Nothing provides %s" % item)
-            return 0
-
-        for p in all_p:
-            if p in self.build_cache:
-                bb.debug(1, "already built %s in this run\n" % p)
-                return 1
-
+    def filterProviders(self, providers, item):
+        """
+        Take a list of providers and filter/reorder according to the 
+        environment variables and previous build results
+        """
         eligible = []
         preferred_versions = {}
 
         # Collate providers by PN
         pkg_pn = {}
-        for p in all_p:
+        for p in providers:
             pn = self.status.pkg_fn[p]
             if pn not in pkg_pn:
                 pkg_pn[pn] = []
@@ -471,13 +456,12 @@ class BBCooker:
 
         # look to see if one of them is already staged, or marked as preferred.
         # if so, bump it to the head of the queue
-        for p in all_p:
+        for p in providers:
             the_data = self.pkgdata[p]
             pn = bb.data.getVar('PN', the_data, 1)
             pv = bb.data.getVar('PV', the_data, 1)
             pr = bb.data.getVar('PR', the_data, 1)
-            tmpdir = bb.data.getVar('TMPDIR', the_data, 1)
-            stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
+            stamp = '%s.do_populate_staging' % bb.data.getVar('STAMP', the_data, 1)
             if os.path.exists(stamp):
                 (newvers, fn) = preferred_versions[pn]
                 if not fn in eligible:
@@ -496,6 +480,34 @@ class BBCooker:
                 discriminated = True
                 break
 
+        return eligible
+
+    def buildProvider( self, item , buildAllDeps , build_depends = [] ):
+        """
+        Build something to provide a named build requirement
+        (takes item names from DEPENDS namespace)
+        """
+
+        fn = None
+        discriminated = False
+
+        if not item in self.status.providers:
+            bb.error("Nothing provides dependency %s" % item)
+            bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
+            return 0
+
+        all_p = self.status.providers[item]
+
+        for p in all_p:
+            if p in self.build_cache:
+                bb.debug(1, "already built %s in this run\n" % p)
+                return 1
+
+        eligible = self.filterProviders(all_p, item)
+
+        if not eligible:
+            return 0
+
         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
         if prefervar:
             self.preferred[item] = prefervar
@@ -518,18 +530,156 @@ class BBCooker:
                     providers_list.append(self.status.pkg_fn[fn])
                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
+                bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data))
             self.consider_msgs_cache.append(item)
 
 
         # run through the list until we find one that we can build
         for fn in eligible:
             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
-            if self.tryBuild(fn, item, buildAllDeps):
+            if self.tryBuild(fn, item, buildAllDeps, build_depends + [fn]):
                 return 1
 
         bb.note("no buildable providers for %s" % item)
+        bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
         return 0
 
+    def buildRProvider( self, item , buildAllDeps ):
+        """
+        Build something to provide a named runtime requirement
+        (takes item names from RDEPENDS/PACKAGES namespace)
+        """
+
+        fn = None
+        all_p = []
+        discriminated = False
+
+        if not buildAllDeps:
+            return True
+
+        all_p = self.getProvidersRun(item)
+
+        if not all_p:
+            bb.error("Nothing provides runtime dependency %s" % (item))
+            bb.event.fire(bb.event.NoProvider(item,self.configuration.data,runtime=True))
+            return False
+
+        for p in all_p:
+            if p in self.rbuild_cache:
+                bb.debug(2, "Already built %s providing runtime %s\n" % (p,item))
+                return True
+            if p in self.build_cache:
+                bb.debug(2, "Already built %s but adding any further RDEPENDS for %s\n" % (p, item))
+                return self.addRunDeps(p, item , buildAllDeps)
+
+        eligible = self.filterProviders(all_p, item)
+        if not eligible:
+            return 0
+
+        preferred = []
+        for p in eligible:
+            pn = self.status.pkg_fn[p]
+            provides = self.status.pn_provides[pn]
+            for provide in provides:
+                prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, self.configuration.data, 1)
+                if prefervar == pn:
+                    if self.configuration.verbose:
+                        bb.note("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 item not in self.consider_msgs_cache:
+                providers_list = []
+                for fn in eligible:
+                    providers_list.append(self.status.pkg_fn[fn])
+                bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
+                bb.note("consider defining a PREFERRED_PROVIDER to match runtime %s" % item)
+                bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
+            self.consider_msgs_cache.append(item)
+
+        if len(preferred) > 1:
+            if item not in self.consider_msgs_cache:
+                providers_list = []
+                for fn in preferred:
+                    providers_list.append(self.status.pkg_fn[fn])
+                bb.note("multiple preferred providers are available (%s);" % ", ".join(providers_list))
+                bb.note("consider defining only one PREFERRED_PROVIDER to match runtime %s" % item)
+                bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
+            self.consider_msgs_cache.append(item)
+
+        # run through the list until we find one that we can build
+        for fn in eligible:
+            bb.debug(2, "selecting %s to satisfy runtime %s" % (fn, item))
+            if self.tryBuild(fn, item, buildAllDeps):
+                return True
+
+        bb.error("No buildable providers for runtime %s" % item)
+        bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
+        return False
+
+    def getProvidersRun(self, rdepend):
+        """
+        Return any potential providers of runtime rdepend
+        """
+        rproviders = []
+
+        if rdepend in self.status.rproviders:
+            rproviders += self.status.rproviders[rdepend]
+
+        if rdepend in self.status.packages:
+            rproviders += self.status.packages[rdepend]
+
+        if rproviders:
+            return rproviders
+
+        # Only search dynamic packages if we can't find anything in other variables
+        for pattern in self.status.packages_dynamic:
+            regexp = re.compile(pattern)
+            if regexp.match(rdepend):
+                rproviders += self.status.packages_dynamic[pattern]
+
+        return rproviders
+
+    def addRunDeps(self , fn, item , buildAllDeps):
+        """
+        Add any runtime dependencies of runtime item provided by fn 
+        as long as item has't previously been processed by this function.
+        """
+
+        if item in self.rbuild_cache:
+            return True
+
+        if not buildAllDeps:
+            return True
+
+        rdepends = []
+        self.rbuild_cache.append(item)
+        the_data = self.pkgdata[fn]
+        pn = self.status.pkg_fn[fn]
+
+        if (item == pn):
+            rdepends += bb.utils.explode_deps(bb.data.getVar('RDEPENDS', the_data, True) or "")
+            rdepends += bb.utils.explode_deps(bb.data.getVar('RRECOMMENDS', the_data, True) or "")
+            rdepends += bb.utils.explode_deps(bb.data.getVar("RDEPENDS_%s" % pn, the_data, True) or "")
+            rdepends += bb.utils.explode_deps(bb.data.getVar('RRECOMMENDS_%s' % pn, the_data, True) or "")
+        else:
+            packages = (bb.data.getVar('PACKAGES', the_data, 1).split() or "")
+            for package in packages:
+                if package == item:
+                    rdepends += bb.utils.explode_deps(bb.data.getVar("RDEPENDS_%s" % package, the_data, True) or "")
+                    rdepends += bb.utils.explode_deps(bb.data.getVar("RRECOMMENDS_%s" % package, the_data, True) or "")
+
+        bb.debug(2, "Additional runtime dependencies for %s are: %s" % (item, " ".join(rdepends)))
+
+        for rdepend in rdepends:
+            if rdepend in self.status.ignored_dependencies:
+                continue
+            if not self.buildRProvider(rdepend, buildAllDeps):
+                return False
+        return True
+
     def buildDepgraph( self ):
         all_depends = self.status.all_depends
         pn_provides = self.status.pn_provides
@@ -551,7 +701,12 @@ class BBCooker:
         for p in self.pkgdata.keys():
             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
 
-        # Build package list for "bitbake world"
+    def buildWorldTargetList(self):
+        """
+         Build package list for "bitbake world"
+        """
+        all_depends = self.status.all_depends
+        pn_provides = self.status.pn_provides
         bb.debug(1, "collating packages for \"world\"")
         for f in self.status.possible_world:
             terminal = True
@@ -733,12 +888,14 @@ class BBCooker:
                 self.showVersions()
                 sys.exit( 0 )
             if 'world' in pkgs_to_build:
+                self.buildWorldTargetList()
                 pkgs_to_build.remove('world')
                 for t in self.status.world_target:
                     pkgs_to_build.append(t)
 
             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.data))
 
+            failures = 0
             for k in pkgs_to_build:
                 failed = False
                 try:
@@ -750,10 +907,11 @@ class BBCooker:
                     failed = True
 
                 if failed:
+                    failures += failures
                     if self.configuration.abort:
                         sys.exit(1)
 
-            bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data))
+            bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data, failures))
 
             sys.exit( self.stats.show() )
 
@@ -899,9 +1057,6 @@ class BBCooker:
                                 if not os.path.isabs(aof):
                                     aof = os.path.join(os.path.dirname(f),aof)
                                 files.append(aof)
-                    for var in bb_data.keys():
-                        if data.getVarFlag(var, "handler", bb_data) and data.getVar(var, bb_data):
-                            event.register(data.getVar(var, bb_data))
                     self.pkgdata[f] = bb_data
 
                 # now inform the caller
@@ -926,8 +1081,7 @@ class BBCooker:
 # main
 #============================================================================#
 
-if __name__ == "__main__":
-
+def main():
     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
     usage = """%prog [options] [package ...]
 
@@ -979,3 +1133,8 @@ Default BBFILES are the .bb files in the current directory.""" )
 
     cooker = BBCooker()
     cooker.cook( BBConfiguration( options ), args[1:] )
+
+
+
+if __name__ == "__main__":
+    main()