bitbake/bin/bitbake: DOT code cleanup
[bitbake.git] / bin / bitbake
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 # Copyright (C) 2003, 2004  Chris Larson
6 # Copyright (C) 2003, 2004  Phil Blundell
7 # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
8 # Copyright (C) 2005        Holger Hans Peter Freyther
9 # Copyright (C) 2005        ROAD GmbH
10 #
11 # This program is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 #
16 # This program is distributed in the hope that it will be useful, but WITHOUT
17 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along with
21 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22 # Place, Suite 330, Boston, MA 02111-1307 USA.
23
24 import sys, os, getopt, glob, copy, os.path, re, time
25 sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
26 import bb
27 from bb import utils, data, parse, debug, event, fatal, cache
28 from sets import Set
29 import itertools, optparse
30
31 parsespin = itertools.cycle( r'|/-\\' )
32 bbdebug = 0
33
34 __version__ = "1.5.0"
35
36 #============================================================================#
37 # BBParsingStatus
38 #============================================================================#
39 class BBParsingStatus:
40     """
41     The initial idea for this status class is to use the data when it is
42     already loaded instead of loading it from various place over and over
43     again.
44     """
45
46     def __init__(self):
47         self.providers   = {}
48         self.rproviders = {}
49         self.packages = {}
50         self.packages_dynamic = {}
51         self.bbfile_priority = {}
52         self.bbfile_config_priorities = []
53         self.ignored_dependencies = None
54         self.possible_world = []
55         self.world_target = Set()
56         self.pkg_pn = {}
57         self.pkg_fn = {}
58         self.pkg_pvpr = {}
59         self.pkg_dp = {}
60         self.pn_provides = {}
61         self.all_depends = Set()
62         self.build_all = {}
63         self.rundeps = {}
64         self.runrecs = {}
65         self.stamp = {}
66
67     def handle_bb_data(self, file_name, bb_cache, cached):
68         """
69         We will fill the dictionaries with the stuff we
70         need for building the tree more fast
71         """
72
73         pn       = bb_cache.getVar('PN', file_name, True)
74         pv       = bb_cache.getVar('PV', file_name, True)
75         pr       = bb_cache.getVar('PR', file_name, True)
76         dp       = int(bb_cache.getVar('DEFAULT_PREFERENCE', file_name, True) or "0")
77         provides  = Set([pn] + (bb_cache.getVar("PROVIDES", file_name, True) or "").split())
78         depends   = (bb_cache.getVar("DEPENDS", file_name, True) or "").split()
79         packages  = (bb_cache.getVar('PACKAGES', file_name, True) or "").split()
80         packages_dynamic = (bb_cache.getVar('PACKAGES_DYNAMIC', file_name, True) or "").split()
81         rprovides = (bb_cache.getVar("RPROVIDES", file_name, True) or "").split()
82
83         # build PackageName to FileName lookup table
84         if pn not in self.pkg_pn:
85             self.pkg_pn[pn] = []
86         self.pkg_pn[pn].append(file_name)
87
88         self.build_all[file_name] = int(bb_cache.getVar('BUILD_ALL_DEPS', file_name, True) or "0")
89         self.stamp[file_name] = bb_cache.getVar('STAMP', file_name, True)
90
91         # build FileName to PackageName lookup table
92         self.pkg_fn[file_name] = pn
93         self.pkg_pvpr[file_name] = (pv,pr)
94         self.pkg_dp[file_name] = dp
95
96         # Build forward and reverse provider hashes
97         # Forward: virtual -> [filenames]
98         # Reverse: PN -> [virtuals]
99         if pn not in self.pn_provides:
100             self.pn_provides[pn] = Set()
101         self.pn_provides[pn] |= provides
102
103         for provide in provides:
104             if provide not in self.providers:
105                 self.providers[provide] = []
106             self.providers[provide].append(file_name)
107
108         for dep in depends:
109             self.all_depends.add(dep)
110
111         # Build reverse hash for PACKAGES, so runtime dependencies 
112         # can be be resolved (RDEPENDS, RRECOMMENDS etc.)
113         for package in packages:
114             if not package in self.packages:
115                 self.packages[package] = []
116             self.packages[package].append(file_name)
117             rprovides += (bb_cache.getVar("RPROVIDES_%s" % package, file_name, 1) or "").split() 
118
119         for package in packages_dynamic:
120             if not package in self.packages_dynamic:
121                 self.packages_dynamic[package] = []
122             self.packages_dynamic[package].append(file_name)
123
124         for rprovide in rprovides:
125             if not rprovide in self.rproviders:
126                 self.rproviders[rprovide] = []
127             self.rproviders[rprovide].append(file_name)
128
129         # Build hash of runtime depeneds and rececommends
130
131         def add_dep(deplist, deps):
132             for dep in deps:
133                 if not dep in deplist:
134                     deplist[dep] = ""
135
136         if not file_name in self.rundeps:
137             self.rundeps[file_name] = {}
138         if not file_name in self.runrecs:
139             self.runrecs[file_name] = {}
140
141         for package in packages + [pn]:
142             if not package in self.rundeps[file_name]:
143                 self.rundeps[file_name][package] = {}
144             if not package in self.runrecs[file_name]:
145                 self.runrecs[file_name][package] = {}
146
147             add_dep(self.rundeps[file_name][package], bb.utils.explode_deps(bb_cache.getVar('RDEPENDS', file_name, True) or ""))
148             add_dep(self.runrecs[file_name][package], bb.utils.explode_deps(bb_cache.getVar('RRECOMMENDS', file_name, True) or ""))
149             add_dep(self.rundeps[file_name][package], bb.utils.explode_deps(bb_cache.getVar("RDEPENDS_%s" % package, file_name, True) or ""))
150             add_dep(self.runrecs[file_name][package], bb.utils.explode_deps(bb_cache.getVar("RRECOMMENDS_%s" % package, file_name, True) or ""))
151
152         # Collect files we may need for possible world-dep
153         # calculations
154         if not bb_cache.getVar('BROKEN', file_name, True) and not bb_cache.getVar('EXCLUDE_FROM_WORLD', file_name, True):
155             self.possible_world.append(file_name)
156
157
158 #============================================================================#
159 # BBStatistics
160 #============================================================================#
161 class BBStatistics:
162     """
163     Manage build statistics for one run
164     """
165     def __init__(self ):
166         self.attempt = 0
167         self.success = 0
168         self.fail = 0
169         self.deps = 0
170
171     def show( self ):
172         print "Build statistics:"
173         print "  Attempted builds: %d" % self.attempt
174         if self.fail:
175             print "  Failed builds: %d" % self.fail
176         if self.deps:
177             print "  Dependencies not satisfied: %d" % self.deps
178         if self.fail or self.deps: return 1
179         else: return 0
180
181
182 #============================================================================#
183 # BBOptions
184 #============================================================================#
185 class BBConfiguration( object ):
186     """
187     Manages build options and configurations for one run
188     """
189     def __init__( self, options ):
190         for key, val in options.__dict__.items():
191             setattr( self, key, val )
192
193 #============================================================================#
194 # BBCooker
195 #============================================================================#
196 class BBCooker:
197     """
198     Manages one bitbake build run
199     """
200
201     ParsingStatus = BBParsingStatus     # make it visible from the shell
202     Statistics = BBStatistics           # make it visible from the shell
203
204     def __init__( self ):
205         self.build_cache_fail = []
206         self.build_cache = []
207         self.rbuild_cache = []
208         self.building_list = []
209         self.build_path = []
210         self.consider_msgs_cache = []
211         self.preferred = {}
212         self.stats = BBStatistics()
213         self.status = None
214
215         self.cache = None
216         self.bb_cache = None
217
218     def tryBuildPackage( self, fn, item, the_data ):
219         """Build one package"""
220         bb.event.fire(bb.event.PkgStarted(item, the_data))
221         try:
222             self.stats.attempt += 1
223             if self.configuration.force:
224                 bb.data.setVarFlag('do_%s' % self.configuration.cmd, 'force', 1, the_data)
225             if not self.configuration.dry_run:
226                 bb.build.exec_task('do_%s' % self.configuration.cmd, the_data)
227             bb.event.fire(bb.event.PkgSucceeded(item, the_data))
228             self.build_cache.append(fn)
229             return True
230         except bb.build.FuncFailed:
231             self.stats.fail += 1
232             bb.error("task stack execution failed")
233             bb.event.fire(bb.event.PkgFailed(item, the_data))
234             self.build_cache_fail.append(fn)
235             raise
236         except bb.build.EventException, e:
237             self.stats.fail += 1
238             event = e.args[1]
239             bb.error("%s event exception, aborting" % bb.event.getName(event))
240             bb.event.fire(bb.event.PkgFailed(item, the_data))
241             self.build_cache_fail.append(fn)
242             raise
243
244     def tryBuild( self, fn, virtual , buildAllDeps , build_depends = []):
245         """
246         Build a provider and its dependencies. 
247         build_depends is a list of previous build dependencies (not runtime)
248         If build_depends is empty, we're dealing with a runtime depends
249         """
250
251         the_data = self.bb_cache.loadDataFull(fn, self)
252
253         # Only follow all (runtime) dependencies if doing a build
254         if not buildAllDeps and self.configuration.cmd is "build":
255             buildAllDeps = self.status.build_all[fn]
256
257         # Error on build time dependency loops
258         if build_depends and build_depends.count(fn) > 1:
259             bb.error("%s depends on itself (eventually)" % fn)
260             bb.error("upwards chain is: %s" % (" -> ".join(self.build_path)))
261             return False
262
263         # See if this is a runtime dependency we've already built
264         # Or a build dependency being handled in a different build chain
265         if fn in self.building_list:
266             return self.addRunDeps(fn, virtual , buildAllDeps)
267
268         item = self.status.pkg_fn[fn]
269
270         self.building_list.append(fn)
271
272         pathstr = "%s (%s)" % (item, virtual)
273         self.build_path.append(pathstr)
274
275         depends_list = (bb.data.getVar('DEPENDS', the_data, True) or "").split()
276
277         if self.configuration.verbose:
278             bb.note("current path: %s" % (" -> ".join(self.build_path)))
279             bb.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
280
281         try:
282             failed = False
283
284             depcmd = self.configuration.cmd
285             bbdepcmd = bb.data.getVarFlag('do_%s' % self.configuration.cmd, 'bbdepcmd', the_data)
286             if bbdepcmd is not None:
287                 if bbdepcmd == "":
288                     depcmd = None
289                 else:
290                     depcmd = bbdepcmd
291
292             if depcmd:
293                 oldcmd = self.configuration.cmd
294                 self.configuration.cmd = depcmd
295
296             for dependency in depends_list:
297                 if dependency in self.status.ignored_dependencies:
298                     continue
299                 if not depcmd:
300                     continue
301                 if self.buildProvider( dependency , buildAllDeps , build_depends ) == 0:
302                     bb.error("dependency %s (for %s) not satisfied" % (dependency,item))
303                     failed = True
304                     if self.configuration.abort:
305                         break
306
307             if depcmd:
308                 self.configuration.cmd = oldcmd
309
310             if failed:
311                 self.stats.deps += 1
312                 return False
313
314             if not self.addRunDeps(fn, virtual , buildAllDeps):
315                 return False
316
317             if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
318                 self.build_cache.append(fn)
319                 return True
320
321             return self.tryBuildPackage( fn, item, the_data )
322
323         finally:
324             self.building_list.remove(fn)
325             self.build_path.remove(pathstr)
326
327     def findBestProvider( self, pn, pkg_pn = None):
328         """
329         If there is a PREFERRED_VERSION, find the highest-priority bbfile
330         providing that version.  If not, find the latest version provided by
331         an bbfile in the highest-priority set.
332         """
333         if not pkg_pn:
334             pkg_pn = self.status.pkg_pn
335
336         files = pkg_pn[pn]
337         priorities = {}
338         for f in files:
339             priority = self.status.bbfile_priority[f]
340             if priority not in priorities:
341                 priorities[priority] = []
342             priorities[priority].append(f)
343         p_list = priorities.keys()
344         p_list.sort(lambda a, b: a - b)
345         tmp_pn = []
346         for p in p_list:
347             tmp_pn = [priorities[p]] + tmp_pn
348
349         preferred_file = None
350
351         localdata = data.createCopy(self.configuration.data)
352         bb.data.setVar('OVERRIDES', "%s:%s" % (pn, data.getVar('OVERRIDES', localdata)), localdata)
353         bb.data.update_data(localdata)
354
355         preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, True)
356         if preferred_v:
357             m = re.match('(.*)_(.*)', preferred_v)
358             if m:
359                 preferred_v = m.group(1)
360                 preferred_r = m.group(2)
361             else:
362                 preferred_r = None
363
364             for file_set in tmp_pn:
365                 for f in file_set:
366                     pv,pr = self.status.pkg_pvpr[f]
367                     if preferred_v == pv and (preferred_r == pr or preferred_r == None):
368                         preferred_file = f
369                         preferred_ver = (pv, pr)
370                         break
371                 if preferred_file:
372                     break;
373             if preferred_r:
374                 pv_str = '%s-%s' % (preferred_v, preferred_r)
375             else:
376                 pv_str = preferred_v
377             if preferred_file is None:
378                 bb.note("preferred version %s of %s not available" % (pv_str, pn))
379             else:
380                 bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
381
382         del localdata
383
384         # get highest priority file set
385         files = tmp_pn[0]
386         latest = None
387         latest_p = 0
388         latest_f = None
389         for file_name in files:
390             pv,pr = self.status.pkg_pvpr[file_name]
391             dp = self.status.pkg_dp[file_name]
392
393             if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
394                 latest = (pv, pr)
395                 latest_f = file_name
396                 latest_p = dp
397         if preferred_file is None:
398             preferred_file = latest_f
399             preferred_ver = latest
400
401         return (latest,latest_f,preferred_ver, preferred_file)
402
403     def showVersions( self ):
404         pkg_pn = self.status.pkg_pn
405         preferred_versions = {}
406         latest_versions = {}
407
408         # Sort by priority
409         for pn in pkg_pn.keys():
410             (last_ver,last_file,pref_ver,pref_file) = self.findBestProvider(pn)
411             preferred_versions[pn] = (pref_ver, pref_file)
412             latest_versions[pn] = (last_ver, last_file)
413
414         pkg_list = pkg_pn.keys()
415         pkg_list.sort()
416
417         for p in pkg_list:
418             pref = preferred_versions[p]
419             latest = latest_versions[p]
420
421             if pref != latest:
422                 prefstr = pref[0][0] + "-" + pref[0][1]
423             else:
424                 prefstr = ""
425
426             print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
427                                         prefstr)
428
429
430     def showEnvironment( self ):
431         """Show the outer or per-package environment"""
432         if self.configuration.buildfile:
433             self.cb = None
434             self.bb_cache = bb.cache.init(self)
435             try:
436                 self.configuration.data = self.bb_cache.loadDataFull(self.configuration.buildfile, self)
437             except IOError, e:
438                 fatal("Unable to read %s: %s" % ( self.configuration.buildfile, e ))
439             except Exception, e:
440                 fatal("%s" % e)
441         # emit variables and shell functions
442         try:
443             data.update_data( self.configuration.data )
444             data.emit_env(sys.__stdout__, self.configuration.data, True)
445         except Exception, e:
446             fatal("%s" % e)
447         # emit the metadata which isnt valid shell
448         for e in self.configuration.data.keys():
449             if data.getVarFlag( e, 'python', self.configuration.data ):
450                 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
451
452     def generateDotGraph( self, pkgs_to_build, ignore_deps ):
453         """
454         Generate two graphs one for the DEPENDS and RDEPENDS. The current
455         implementation creates crappy graphs ;)
456
457         pkgs_to_build A list of packages that needs to be built
458         ignore_deps   A list of names where processing of dependencies
459                       should be stopped. e.g. dependencies that get
460         """
461
462         def myFilterProvider( providers, item):
463             """
464             Take a list of providers and filter according to environment
465             variables. In contrast to filterProviders we do not discriminate
466             and take PREFERRED_PROVIDER into account.
467             """
468             eligible = []
469             preferred_versions = {}
470
471             # Collate providers by PN
472             pkg_pn = {}
473             for p in providers:
474                 pn = self.status.pkg_fn[p]
475                 if pn not in pkg_pn:
476                     pkg_pn[pn] = []
477                 pkg_pn[pn].append(p)
478
479             bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
480
481             for pn in pkg_pn.keys():
482                 preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
483                 eligible.append(preferred_versions[pn][1])
484
485             for p in eligible:
486                 if p in self.build_cache_fail:
487                     bb.debug(1, "rejecting already-failed %s" % p)
488                     eligible.remove(p)
489
490             if len(eligible) == 0:
491                 bb.error("no eligible providers for %s" % item)
492                 return 0
493
494             prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
495
496             # try the preferred provider first
497             if prefervar:
498                 for p in eligible:
499                     if prefervar == self.status.pkg_fn[p]:
500                         bb.note("Selecting PREFERRED_PROVIDER %s" % prefervar)
501                         eligible.remove(p)
502                         eligible = [p] + eligible
503
504             return eligible
505
506
507
508
509         # try to avoid adding the same rdepends over an over again
510         seen_depends  = []
511         seen_rdepends = []
512
513
514         def add_depends(package_list):
515             """
516             Add all depends of all packages from this list
517             """
518             for package in package_list:
519                 if package in seen_depends or package in ignore_deps:
520                     continue
521
522                 seen_depends.append( package )
523                 if not package in self.status.providers:
524                     """
525                     We have not seen this name -> error in
526                     dependency handling
527                     """
528                     bb.note( "ERROR with provider: %(package)s" % vars() )
529                     print >> depends_file, '"%(package)s" -> ERROR' % vars()
530                     continue
531
532                 # get all providers for this package
533                 providers = self.status.providers[package]
534
535                 # now let us find the bestProvider for it
536                 fn = myFilterProvider(providers, package)[0]
537
538                 depends  = bb.utils.explode_deps(self.bb_cache.getVar('DEPENDS', fn, True) or "")
539                 version  = self.bb_cache.getVar('PV', fn, True ) + '-' + self.bb_cache.getVar('PR', fn, True)
540                 add_depends ( depends )
541
542                 # now create the node
543                 print >> depends_file, '"%(package)s" [label="%(package)s\\n%(version)s"]' % vars()
544
545                 depends = filter( (lambda x: x not in ignore_deps), depends )
546                 for depend in depends:
547                     print >> depends_file, '"%(package)s" -> "%(depend)s"' % vars()
548
549
550         def add_all_depends( the_depends, the_rdepends ):
551             """
552             Add both DEPENDS and RDEPENDS. RDEPENDS will get dashed
553             lines
554             """
555             package_list = the_depends + the_rdepends
556             for package in package_list:
557                 if package in seen_rdepends or package in ignore_deps:
558                     continue
559
560                 seen_rdepends.append( package )
561
562                 # Let us find out if the package is a DEPENDS or RDEPENDS
563                 # and we will set 'providers' with the avilable providers
564                 # for the package.
565                 if package in the_depends:
566                     if not package in self.status.providers:
567                         bb.note( "ERROR with provider: %(package)s" % vars() )
568                         print >> alldepends_file, '"%(package)s" -> ERROR' % vars()
569                         continue
570
571                     providers = self.status.providers[package]
572                 elif package in the_rdepends:
573                     if len(self.getProvidersRun(package)) == 0:
574                         bb.note( "ERROR with rprovider: %(package)s" % vars() )
575                         print >> alldepends_file, '"%(package)s" -> ERROR [style="dashed"]' % vars()
576                         continue
577
578                     providers = self.getProvidersRun(package)
579                 else:
580                     # something went wrong...
581                     print "Complete ERROR! %s" % package
582                     continue
583
584                 # now let us find the bestProvider for it
585                 fn = myFilterProvider(providers, package)[0]
586
587                 # Now we have a filename let us get the depends and RDEPENDS of it
588                 depends  = bb.utils.explode_deps(self.bb_cache.getVar('DEPENDS', fn, True) or "")
589                 if fn in self.status.rundeps and package in self.status.rundeps[fn]:
590                     rdepends= self.status.rundeps[fn][package].keys()
591                 else:
592                     rdepends = []
593                 version  = self.bb_cache.getVar('PV', fn, True ) + '-' + self.bb_cache.getVar('PR', fn, True)
594
595                 # handle all the depends and rdepends of package
596                 add_all_depends ( depends, rdepends )
597
598                 # now create the node using package name
599                 print >> alldepends_file, '"%(package)s" [label="%(package)s\\n%(version)s"]' % vars()
600
601                 # remove the stuff we want to ignore and add the edges
602                 depends = filter( (lambda x: x not in ignore_deps), depends )
603                 rdepends = filter( (lambda x: x not in ignore_deps), rdepends )
604                 for depend in depends:
605                     print >> alldepends_file, '"%(package)s" -> "%(depend)s"' % vars()
606                 for depend in rdepends:
607                     print >> alldepends_file, '"%(package)s" -> "%(depend)s" [style=dashed]' % vars()
608
609
610         # Add depends now
611         depends_file = file('depends.dot', 'w' )
612         print >> depends_file, "digraph depends {"
613         add_depends( pkgs_to_build )
614         print >> depends_file,  "}"
615
616         # Add all depends now
617         alldepends_file = file('alldepends.dot', 'w' )
618         print >> alldepends_file, "digraph alldepends {"
619         add_all_depends( pkgs_to_build, [] )
620         print >> alldepends_file, "}"
621
622     def filterProviders(self, providers, item):
623         """
624         Take a list of providers and filter/reorder according to the 
625         environment variables and previous build results
626         """
627         eligible = []
628         preferred_versions = {}
629
630         # Collate providers by PN
631         pkg_pn = {}
632         for p in providers:
633             pn = self.status.pkg_fn[p]
634             if pn not in pkg_pn:
635                 pkg_pn[pn] = []
636             pkg_pn[pn].append(p)
637
638         bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
639
640         for pn in pkg_pn.keys():
641             preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
642             eligible.append(preferred_versions[pn][1])
643
644         for p in eligible:
645             if p in self.build_cache_fail:
646                 bb.debug(1, "rejecting already-failed %s" % p)
647                 eligible.remove(p)
648
649         if len(eligible) == 0:
650             bb.error("no eligible providers for %s" % item)
651             return 0
652
653         # look to see if one of them is already staged, or marked as preferred.
654         # if so, bump it to the head of the queue
655         for p in providers:
656             pn = self.status.pkg_fn[p]
657             pv, pr = self.status.pkg_pvpr[p]
658
659             stamp = '%s.do_populate_staging' % self.status.stamp[p]
660             if os.path.exists(stamp):
661                 (newvers, fn) = preferred_versions[pn]
662                 if not fn in eligible:
663                     # package was made ineligible by already-failed check
664                     continue
665                 oldver = "%s-%s" % (pv, pr)
666                 newver = '-'.join(newvers)
667                 if (newver != oldver):
668                     extra_chat = "%s (%s) already staged but upgrading to %s to satisfy %s" % (pn, oldver, newver, item)
669                 else:
670                     extra_chat = "Selecting already-staged %s (%s) to satisfy %s" % (pn, oldver, item)
671                 if self.configuration.verbose:
672                     bb.note("%s" % extra_chat)
673                 eligible.remove(fn)
674                 eligible = [fn] + eligible
675                 discriminated = True
676                 break
677
678         return eligible
679
680     def buildProvider( self, item , buildAllDeps , build_depends = [] ):
681         """
682         Build something to provide a named build requirement
683         (takes item names from DEPENDS namespace)
684         """
685
686         fn = None
687         discriminated = False
688
689         if not item in self.status.providers:
690             bb.error("Nothing provides dependency %s" % item)
691             bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
692             return 0
693
694         all_p = self.status.providers[item]
695
696         for p in all_p:
697             if p in self.build_cache:
698                 bb.debug(1, "already built %s in this run\n" % p)
699                 return 1
700
701         eligible = self.filterProviders(all_p, item)
702
703         if not eligible:
704             return 0
705
706         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
707         if prefervar:
708             self.preferred[item] = prefervar
709
710         if item in self.preferred:
711             for p in eligible:
712                 pn = self.status.pkg_fn[p]
713                 if self.preferred[item] == pn:
714                     if self.configuration.verbose:
715                         bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
716                     eligible.remove(p)
717                     eligible = [p] + eligible
718                     discriminated = True
719                     break
720
721         if len(eligible) > 1 and discriminated == False:
722             if item not in self.consider_msgs_cache:
723                 providers_list = []
724                 for fn in eligible:
725                     providers_list.append(self.status.pkg_fn[fn])
726                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
727                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
728                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data))
729             self.consider_msgs_cache.append(item)
730
731
732         # run through the list until we find one that we can build
733         for fn in eligible:
734             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
735             if self.tryBuild(fn, item, buildAllDeps, build_depends + [fn]):
736                 return 1
737
738         bb.note("no buildable providers for %s" % item)
739         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
740         return 0
741
742     def buildRProvider( self, item , buildAllDeps ):
743         """
744         Build something to provide a named runtime requirement
745         (takes item names from RDEPENDS/PACKAGES namespace)
746         """
747
748         fn = None
749         all_p = []
750         discriminated = False
751
752         if not buildAllDeps:
753             return True
754
755         all_p = self.getProvidersRun(item)
756
757         if not all_p:
758             bb.error("Nothing provides runtime dependency %s" % (item))
759             bb.event.fire(bb.event.NoProvider(item,self.configuration.data,runtime=True))
760             return False
761
762         for p in all_p:
763             if p in self.rbuild_cache:
764                 bb.debug(2, "Already built %s providing runtime %s\n" % (p,item))
765                 return True
766             if p in self.build_cache:
767                 bb.debug(2, "Already built %s but adding any further RDEPENDS for %s\n" % (p, item))
768                 return self.addRunDeps(p, item , buildAllDeps)
769
770         eligible = self.filterProviders(all_p, item)
771         if not eligible:
772             return 0
773
774         preferred = []
775         for p in eligible:
776             pn = self.status.pkg_fn[p]
777             provides = self.status.pn_provides[pn]
778             for provide in provides:
779                 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, self.configuration.data, 1)
780                 if prefervar == pn:
781                     if self.configuration.verbose:
782                         bb.note("selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
783                     eligible.remove(p)
784                     eligible = [p] + eligible
785                     preferred.append(p)
786
787         if len(eligible) > 1 and len(preferred) == 0:
788             if item not in self.consider_msgs_cache:
789                 providers_list = []
790                 for fn in eligible:
791                     providers_list.append(self.status.pkg_fn[fn])
792                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
793                 bb.note("consider defining a PREFERRED_PROVIDER to match runtime %s" % item)
794                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
795             self.consider_msgs_cache.append(item)
796
797         if len(preferred) > 1:
798             if item not in self.consider_msgs_cache:
799                 providers_list = []
800                 for fn in preferred:
801                     providers_list.append(self.status.pkg_fn[fn])
802                 bb.note("multiple preferred providers are available (%s);" % ", ".join(providers_list))
803                 bb.note("consider defining only one PREFERRED_PROVIDER to match runtime %s" % item)
804                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
805             self.consider_msgs_cache.append(item)
806
807         # run through the list until we find one that we can build
808         for fn in eligible:
809             bb.debug(2, "selecting %s to satisfy runtime %s" % (fn, item))
810             if self.tryBuild(fn, item, buildAllDeps):
811                 return True
812
813         bb.error("No buildable providers for runtime %s" % item)
814         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
815         return False
816
817     def getProvidersRun(self, rdepend):
818         """
819         Return any potential providers of runtime rdepend
820         """
821         rproviders = []
822
823         if rdepend in self.status.rproviders:
824             rproviders += self.status.rproviders[rdepend]
825
826         if rdepend in self.status.packages:
827             rproviders += self.status.packages[rdepend]
828
829         if rproviders:
830             return rproviders
831
832         # Only search dynamic packages if we can't find anything in other variables
833         for pattern in self.status.packages_dynamic:
834             regexp = re.compile(pattern)
835             if regexp.match(rdepend):
836                 rproviders += self.status.packages_dynamic[pattern]
837
838         return rproviders
839
840     def addRunDeps(self , fn, item , buildAllDeps):
841         """
842         Add any runtime dependencies of runtime item provided by fn 
843         as long as item has't previously been processed by this function.
844         """
845
846         if item in self.rbuild_cache:
847             return True
848
849         if not buildAllDeps:
850             return True
851
852         rdepends = []
853         self.rbuild_cache.append(item)
854
855         if fn in self.status.rundeps and item in self.status.rundeps[fn]:
856             rdepends += self.status.rundeps[fn][item].keys()
857         if fn in self.status.runrecs and item in self.status.runrecs[fn]:
858             rdepends += self.status.runrecs[fn][item].keys()
859
860         bb.debug(2, "Additional runtime dependencies for %s are: %s" % (item, " ".join(rdepends)))
861
862         for rdepend in rdepends:
863             if rdepend in self.status.ignored_dependencies:
864                 continue
865             if not self.buildRProvider(rdepend, buildAllDeps):
866                 return False
867         return True
868
869     def buildDepgraph( self ):
870         all_depends = self.status.all_depends
871         pn_provides = self.status.pn_provides
872
873         localdata = data.createCopy(self.configuration.data)
874         bb.data.update_data(localdata)
875
876         def calc_bbfile_priority(filename):
877             for (regex, pri) in self.status.bbfile_config_priorities:
878                 if regex.match(filename):
879                     return pri
880             return 0
881
882         # Handle PREFERRED_PROVIDERS
883         for p in (bb.data.getVar('PREFERRED_PROVIDERS', localdata, 1) or "").split():
884             (providee, provider) = p.split(':')
885             if providee in self.preferred and self.preferred[providee] != provider:
886                 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
887             self.preferred[providee] = provider
888
889         # Calculate priorities for each file
890         for p in self.status.pkg_fn.keys():
891             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
892
893     def buildWorldTargetList(self):
894         """
895          Build package list for "bitbake world"
896         """
897         all_depends = self.status.all_depends
898         pn_provides = self.status.pn_provides
899         bb.debug(1, "collating packages for \"world\"")
900         for f in self.status.possible_world:
901             terminal = True
902             pn = self.status.pkg_fn[f]
903
904             for p in pn_provides[pn]:
905                 if p.startswith('virtual/'):
906                     bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
907                     terminal = False
908                     break
909                 for pf in self.status.providers[p]:
910                     if self.status.pkg_fn[pf] != pn:
911                         bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
912                         terminal = False
913                         break
914             if terminal:
915                 self.status.world_target.add(pn)
916
917             # drop reference count now
918             self.status.possible_world = None
919             self.status.all_depends    = None
920
921     def myProgressCallback( self, x, y, f, bb_cache, from_cache ):
922         # feed the status with new input
923
924         self.status.handle_bb_data(f, bb_cache, from_cache)
925
926         if bbdebug > 0:
927             return
928         if os.isatty(sys.stdout.fileno()):
929             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
930             sys.stdout.flush()
931         else:
932             if x == 1:
933                 sys.stdout.write("Parsing .bb files, please wait...")
934                 sys.stdout.flush()
935             if x == y:
936                 sys.stdout.write("done.")
937                 sys.stdout.flush()
938
939     def interactiveMode( self ):
940         """Drop off into a shell"""
941         try:
942             from bb import shell
943         except ImportError, details:
944             bb.fatal("Sorry, shell not available (%s)" % details )
945         else:
946             bb.data.update_data( self.configuration.data )
947             shell.start( self )
948             sys.exit( 0 )
949
950     def parseConfigurationFile( self, afile ):
951         try:
952             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
953
954             # Add the handlers we inherited by INHERIT
955             # we need to do this manually as it is not guranteed
956             # we will pick up these classes... as we only INHERIT
957             # on .inc and .bb files but not on .conf
958             data = bb.data.createCopy( self.configuration.data )
959             inherits  = ["base"] + (bb.data.getVar('INHERIT', data, True ) or "").split()
960             for inherit in inherits:
961                 data = bb.parse.handle( os.path.join('classes', '%s.bbclass' % inherit ), data, True )
962
963             # FIXME: This assumes that we included at least one .inc file
964             for var in bb.data.keys(data):
965                 if bb.data.getVarFlag(var, 'handler', data):
966                     bb.event.register(var,bb.data.getVar(var, data))
967
968         except IOError:
969             bb.fatal( "Unable to open %s" % afile )
970         except bb.parse.ParseError, details:
971             bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
972
973     def handleCollections( self, collections ):
974         """Handle collections"""
975         if collections:
976             collection_list = collections.split()
977             for c in collection_list:
978                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
979                 if regex == None:
980                     bb.error("BBFILE_PATTERN_%s not defined" % c)
981                     continue
982                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
983                 if priority == None:
984                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
985                     continue
986                 try:
987                     cre = re.compile(regex)
988                 except re.error:
989                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
990                     continue
991                 try:
992                     pri = int(priority)
993                     self.status.bbfile_config_priorities.append((cre, pri))
994                 except ValueError:
995                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
996
997
998     def cook( self, configuration, args ):
999         """
1000         We are building stuff here. We do the building
1001         from here. By default we try to execute task
1002         build.
1003         """
1004
1005         self.configuration = configuration
1006
1007         if not self.configuration.cmd:
1008             self.configuration.cmd = "build"
1009
1010         if self.configuration.debug:
1011             bb.debug_level = self.configuration.debug
1012
1013         self.configuration.data = bb.data.init()
1014
1015         for f in self.configuration.file:
1016             self.parseConfigurationFile( f )
1017
1018         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
1019
1020
1021         #
1022         # Special updated configuration we use for firing events
1023         #
1024         self.configuration.event_data = bb.data.createCopy(self.configuration.data)
1025         bb.data.update_data(self.configuration.event_data)
1026
1027         if self.configuration.show_environment:
1028             self.showEnvironment()
1029             sys.exit( 0 )
1030
1031         # inject custom variables
1032         if not bb.data.getVar("BUILDNAME", self.configuration.data):
1033             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
1034         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
1035
1036         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
1037
1038         if self.configuration.interactive:
1039             self.interactiveMode()
1040
1041         if self.configuration.buildfile is not None:
1042             bf = os.path.abspath( self.configuration.buildfile )
1043             try:
1044                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
1045             except IOError:
1046                 bb.fatal("Unable to open %s" % bf)
1047
1048             item = bb.data.getVar('PN', bbfile_data, 1)
1049             try:
1050                 self.tryBuildPackage( bf, item, bbfile_data )
1051             except bb.build.EventException:
1052                 bb.error( "Build of '%s' failed" % item )
1053
1054             sys.exit( self.stats.show() )
1055
1056         # initialise the parsing status now we know we will need deps
1057         self.status = BBParsingStatus()
1058
1059         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
1060         self.status.ignored_dependencies = Set( ignore.split() )
1061
1062         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
1063
1064         pkgs_to_build = None
1065         if args:
1066             if not pkgs_to_build:
1067                 pkgs_to_build = []
1068             pkgs_to_build.extend(args)
1069         if not pkgs_to_build:
1070                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
1071                 if bbpkgs:
1072                         pkgs_to_build = bbpkgs.split()
1073         if not pkgs_to_build and not self.configuration.show_versions \
1074                              and not self.configuration.interactive \
1075                              and not self.configuration.show_environment:
1076                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
1077                 print "for usage information."
1078                 sys.exit(0)
1079
1080         # Import Psyco if available and not disabled
1081         if not self.configuration.disable_psyco:
1082             try:
1083                 import psyco
1084             except ImportError:
1085                 if bbdebug == 0:
1086                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
1087             else:
1088                 psyco.bind( self.collect_bbfiles )
1089         else:
1090             bb.note("You have disabled Psyco. This decreases performance.")
1091
1092         try:
1093             bb.debug(1, "collecting .bb files")
1094             self.collect_bbfiles( self.myProgressCallback )
1095             bb.debug(1, "parsing complete")
1096             if bbdebug == 0:
1097                 print
1098             if self.configuration.parse_only:
1099                 print "Requested parsing .bb files only.  Exiting."
1100                 return
1101
1102
1103             self.buildDepgraph()
1104
1105             if self.configuration.show_versions:
1106                 self.showVersions()
1107                 sys.exit( 0 )
1108             if 'world' in pkgs_to_build:
1109                 self.buildWorldTargetList()
1110                 pkgs_to_build.remove('world')
1111                 for t in self.status.world_target:
1112                     pkgs_to_build.append(t)
1113
1114             if self.configuration.dot_graph:
1115                 self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps )
1116                 sys.exit( 0 )
1117
1118
1119             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data))
1120
1121             failures = 0
1122             for k in pkgs_to_build:
1123                 failed = False
1124                 try:
1125                     if self.buildProvider( k , False ) == 0:
1126                         # already diagnosed
1127                         failed = True
1128                 except bb.build.EventException:
1129                     bb.error("Build of " + k + " failed")
1130                     failed = True
1131
1132                 if failed:
1133                     failures += failures
1134                     if self.configuration.abort:
1135                         sys.exit(1)
1136
1137             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures))
1138
1139             sys.exit( self.stats.show() )
1140
1141         except KeyboardInterrupt:
1142             print "\nNOTE: KeyboardInterrupt - Build not completed."
1143             sys.exit(1)
1144
1145     def get_bbfiles( self, path = os.getcwd() ):
1146         """Get list of default .bb files by reading out the current directory"""
1147         contents = os.listdir(path)
1148         bbfiles = []
1149         for f in contents:
1150             (root, ext) = os.path.splitext(f)
1151             if ext == ".bb":
1152                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
1153         return bbfiles
1154
1155     def find_bbfiles( self, path ):
1156         """Find all the .bb files in a directory (uses find)"""
1157         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
1158         try:
1159             finddata = os.popen(findcmd)
1160         except OSError:
1161             return []
1162         return finddata.readlines()
1163
1164     def collect_bbfiles( self, progressCallback ):
1165         """Collect all available .bb build files"""
1166         self.cb = progressCallback
1167         parsed, cached, skipped, masked = 0, 0, 0, 0
1168         self.bb_cache = bb.cache.init(self)
1169
1170         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
1171         data.setVar("BBFILES", " ".join(files), self.configuration.data)
1172
1173         if not len(files):
1174             files = self.get_bbfiles()
1175
1176         if not len(files):
1177             bb.error("no files to build.")
1178
1179         newfiles = []
1180         for f in files:
1181             if os.path.isdir(f):
1182                 dirfiles = self.find_bbfiles(f)
1183                 if dirfiles:
1184                     newfiles += dirfiles
1185                     continue
1186             newfiles += glob.glob(f) or [ f ]
1187
1188         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
1189         try:
1190             bbmask_compiled = re.compile(bbmask)
1191         except sre_constants.error:
1192             bb.fatal("BBMASK is not a valid regular expression.")
1193
1194         for i in xrange( len( newfiles ) ):
1195             f = newfiles[i]
1196             if bbmask and bbmask_compiled.search(f):
1197                 bb.debug(1, "bbmake: skipping %s" % f)
1198                 masked += 1
1199                 continue
1200             debug(1, "bbmake: parsing %s" % f)
1201
1202             # read a file's metadata
1203             try:
1204                 fromCache, skip = self.bb_cache.loadData(f, self)
1205                 if skip:
1206                     skipped += 1
1207                     #bb.note("Skipping %s" % f)
1208                     self.bb_cache.skip(f)
1209                     continue
1210                 elif fromCache: cached += 1
1211                 else: parsed += 1
1212                 deps = None
1213
1214                 # allow metadata files to add items to BBFILES
1215                 #data.update_data(self.pkgdata[f])
1216                 addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
1217                 if addbbfiles:
1218                     for aof in addbbfiles.split():
1219                         if not files.count(aof):
1220                             if not os.path.isabs(aof):
1221                                 aof = os.path.join(os.path.dirname(f),aof)
1222                             files.append(aof)
1223
1224                 # now inform the caller
1225                 if self.cb is not None:
1226                     self.cb( i + 1, len( newfiles ), f, self.bb_cache, fromCache )
1227
1228             except IOError, e:
1229                 self.bb_cache.remove(f)
1230                 bb.error("opening %s: %s" % (f, e))
1231                 pass
1232             except KeyboardInterrupt:
1233                 self.bb_cache.sync()
1234                 raise
1235             except Exception, e:
1236                 self.bb_cache.remove(f)
1237                 bb.error("%s while parsing %s" % (e, f))
1238             except:
1239                 self.bb_cache.remove(f)
1240                 raise
1241
1242         if self.cb is not None:
1243             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
1244
1245         self.bb_cache.sync()
1246
1247 #============================================================================#
1248 # main
1249 #============================================================================#
1250
1251 def main():
1252     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
1253     usage = """%prog [options] [package ...]
1254
1255 Executes the specified task (default is 'build') for a given set of BitBake files.
1256 It expects that BBFILES is defined, which is a space seperated list of files to
1257 be executed.  BBFILES does support wildcards.
1258 Default BBFILES are the .bb files in the current directory.""" )
1259
1260     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
1261                action = "store", dest = "buildfile", default = None )
1262
1263     parser.add_option( "-k", "--continue", help = "continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.",
1264                action = "store_false", dest = "abort", default = True )
1265
1266     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
1267                action = "store_true", dest = "force", default = False )
1268
1269     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode also called the BitBake shell.",
1270                action = "store_true", dest = "interactive", default = False )
1271
1272     parser.add_option( "-c", "--cmd", help = "Specify task to execute. Note that this only executes the specified task for the providee and the packages it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). Depending on the base.bbclass a listtaks tasks is defined and will show available tasks",
1273                action = "store", dest = "cmd", default = "build" )
1274
1275     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
1276                action = "append", dest = "file", default = [] )
1277
1278     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
1279                action = "store_true", dest = "verbose", default = False )
1280
1281     parser.add_option( "-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
1282                action = "count", dest="debug", default = 0)
1283
1284     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
1285                action = "store_true", dest = "dry_run", default = False )
1286
1287     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
1288                action = "store_true", dest = "parse_only", default = False )
1289
1290     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
1291                action = "store_true", dest = "disable_psyco", default = False )
1292
1293     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
1294                action = "store_true", dest = "show_versions", default = False )
1295
1296     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
1297                action = "store_true", dest = "show_environment", default = False )
1298
1299     parser.add_option( "-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax",
1300                 action = "store_true", dest = "dot_graph", default = False )
1301     parser.add_option( "-I", "--ignore-deps", help = """Stop processing at the given list of dependencies when generating dependency graphs. This can help to make the graph more appealing""",
1302                 action = "append", dest = "ignored_dot_deps", default = [] )
1303
1304
1305     options, args = parser.parse_args( sys.argv )
1306
1307     cooker = BBCooker()
1308     cooker.cook( BBConfiguration( options ), args[1:] )
1309
1310
1311
1312 if __name__ == "__main__":
1313     print """WARNING, WARNING, WARNING
1314 This is a Bitbake from the Unstable/Development Branch.
1315 You might want to use the bitbake-1.4 stable branch (if you are not a BitBake developer or tester). I'm going to sleep 5 seconds now to make sure you see that."""
1316     import time
1317     time.sleep(5)
1318     main()