Mickey's style and performance tunings, volume I:
[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
25 sys.path.append(os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
26 import bb
27 from bb import make
28 from sets import Set
29 import itertools, optparse
30
31 parsespin = itertools.cycle( r'|/-\\' )
32 bbdebug = 0
33
34 __version__ = "1.2.9"
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.cache_dirty = False
48         self.providers   = {}
49         self.bbfile_priority = {}
50         self.bbfile_config_priorities = []
51         self.ignored_depedencies = None
52         self.possible_world = []
53         self.world_target = Set()
54         self.pkg_pn = {}
55         self.pkg_fn = {}
56         self.pkg_pvpr = {}
57         self.pkg_dp = {}
58         self.pn_provides = {}
59         self.all_depends = Set()
60
61     def handle_bb_data(self, file_name, bb_data, cached):
62         """
63         We will fill the dictionaries with the stuff we
64         need for building the tree more fast
65         """
66         if bb_data == None:
67             return
68
69         if not cached:
70             self.cache_dirty = True
71
72         pn       = bb.data.getVar('PN', bb_data, True)
73         pv       = bb.data.getVar('PV', bb_data, True)
74         pr       = bb.data.getVar('PR', bb_data, True)
75         dp       = int(bb.data.getVar('DEFAULT_PREFERENCE', bb_data, True) or "0")
76         provides = Set([pn] + (bb.data.getVar("PROVIDES", bb_data, 1) or "").split())
77         depends  = (bb.data.getVar("DEPENDS", bb_data, True) or "").split()
78
79
80         # build PackageName to FileName lookup table
81         if pn not in self.pkg_pn:
82             self.pkg_pn[pn] = []
83         self.pkg_pn[pn].append(file_name)
84
85         # build FileName to PackageName lookup table
86         self.pkg_fn[file_name] = pn
87         self.pkg_pvpr[file_name] = (pv,pr)
88         self.pkg_dp[file_name] = dp
89
90         # Build forward and reverse provider hashes
91         # Forward: virtual -> [filenames]
92         # Reverse: PN -> [virtuals]
93         if pn not in self.pn_provides:
94             self.pn_provides[pn] = Set()
95         self.pn_provides[pn] |= provides
96
97         for provide in provides:
98             if provide not in self.providers:
99                 self.providers[provide] = []
100             self.providers[provide].append(file_name)
101
102         for dep in depends:
103             self.all_depends.add(dep)
104
105         # Collect files we may need for possible world-dep
106         # calculations
107         if not bb.data.getVar('BROKEN', bb_data, True) and not bb.data.getVar('EXCLUDE_FROM_WORLD', bb_data, True):
108             self.possible_world.append(file_name)
109
110
111 #============================================================================#
112 # BBStatistics
113 #============================================================================#
114 class BBStatistics:
115     """
116     Manage build statistics for one run
117     """
118     def __init__(self ):
119         self.attempt = 0
120         self.success = 0
121         self.fail = 0
122         self.deps = 0
123
124     def show( self ):
125         print "Build statistics:"
126         print "  Attempted builds: %d" % self.attempt
127         if self.fail:
128             print "  Failed builds: %d" % self.fail
129         if self.deps:
130             print "  Dependencies not satisfied: %d" % self.deps
131         if self.fail or self.deps: return 1
132         else: return 0
133
134
135 #============================================================================#
136 # BBCooker
137 #============================================================================#
138 class BBCooker:
139     """
140     Manages one bitbake build run
141     """
142     def __init__( self ):
143         self.build_cache_fail = []
144         self.build_cache = []
145         self.building_list = []
146         self.build_path = []
147         self.consider_msgs_cache = []
148         self.preferred = {}
149         self.stats = BBStatistics()
150         self.status = None
151
152     def tryBuild( self, fn, virtual ):
153         if fn in self.building_list:
154             bb.error("%s depends on itself (eventually)" % fn)
155             bb.error("upwards chain is: %s" % (" -> ".join(self.build_path)))
156             return False
157
158         the_data = make.pkgdata[fn]
159         item = self.status.pkg_fn[fn]
160
161         self.building_list.append(fn)
162
163         pathstr = "%s (%s)" % (item, virtual)
164         self.build_path.append(pathstr)
165
166         depends_list = (bb.data.getVar('DEPENDS', the_data, 1) or "").split()
167         if make.options.verbose:
168             bb.note("current path: %s" % (" -> ".join(self.build_path)))
169             bb.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
170
171         try:
172             failed = False
173
174             depcmd = make.options.cmd
175             bbdepcmd = bb.data.getVarFlag('do_%s' % make.options.cmd, 'bbdepcmd', the_data)
176             if bbdepcmd is not None:
177                 if bbdepcmd == "":
178                     depcmd = None
179                 else:
180                     depcmd = bbdepcmd
181
182             if depcmd:
183                 oldcmd = make.options.cmd
184                 make.options.cmd = depcmd
185
186             for d in depends_list:
187                 if d in self.status.ignored_dependencies:
188                     continue
189                 if not depcmd:
190                     continue
191                 if self.buildPackage(d) == 0:
192                     bb.error("dependency %s (for %s) not satisfied" % (d,item))
193                     failed = True
194                     if make.options.abort:
195                         break
196
197             if depcmd:
198                 make.options.cmd = oldcmd
199
200             if failed:
201                 self.stats.deps += 1
202                 return False
203
204             if bb.build.stamp_is_current('do_%s' % make.options.cmd, the_data):
205                 self.build_cache.append(fn)
206                 return True
207
208             bb.event.fire(bb.event.PkgStarted(item, the_data))
209             try:
210                 self.stats.attempt += 1
211                 if not make.options.dry_run:
212                     bb.build.exec_task('do_%s' % make.options.cmd, the_data)
213                 bb.event.fire(bb.event.PkgSucceeded(item, the_data))
214                 self.build_cache.append(fn)
215                 return True
216             except bb.build.FuncFailed:
217                 self.stats.fail += 1
218                 bb.error("task stack execution failed")
219                 bb.event.fire(bb.event.PkgFailed(item, the_data))
220                 self.build_cache_fail.append(fn)
221                 raise
222             except bb.build.EventException:
223                 self.stats.fail += 1
224                 (type, value, traceback) = sys.exc_info()
225                 e = value.event
226                 bb.error("%s event exception, aborting" % bb.event.getName(e))
227                 bb.event.fire(bb.event.PkgFailed(item, the_data))
228                 self.build_cache_fail.append(fn)
229                 raise
230         finally:
231             self.building_list.remove(fn)
232             self.build_path.remove(pathstr)
233
234     def showVersions( self ):
235         pkg_pn = self.status.pkg_pn
236         preferred_versions = {}
237         latest_versions = {}
238
239         # Sort by priority
240         for pn in pkg_pn.keys():
241             files = pkg_pn[pn]
242             priorities = {}
243             for f in files:
244                 priority = self.status.bbfile_priority[f]
245                 if priority not in priorities:
246                     priorities[priority] = []
247                 priorities[priority].append(f)
248             p_list = priorities.keys()
249             p_list.sort(lambda a, b: a - b)
250             pkg_pn[pn] = []
251             for p in p_list:
252                 pkg_pn[pn] = [ priorities[p] ] + pkg_pn[pn]
253
254         # If there is a PREFERRED_VERSION, find the highest-priority bbfile providing that
255         # version.  If not, find the latest version provided by an bbfile in the
256         # highest-priority set.
257         for pn in pkg_pn.keys():
258             preferred_file = None
259
260             preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, make.cfg, 1)
261             if preferred_v:
262                 preferred_r = None
263                 m = re.match('(.*)_(.*)', preferred_v)
264                 if m:
265                     preferred_v = m.group(1)
266                     preferred_r = m.group(2)
267
268                 for file_set in pkg_pn[pn]:
269                     for f in file_set:
270                         pv,pr = self.status.pkg_pvpr[f]
271                         if preferred_v == pv and (preferred_r == pr or preferred_r == None):
272                             preferred_file = f
273                             preferred_ver = (pv, pr)
274                             break
275                     if preferred_file:
276                         break
277                 if preferred_r:
278                     pv_str = '%s-%s' % (preferred_v, preferred_r)
279                 else:
280                     pv_str = preferred_v
281                 if preferred_file is None:
282                     bb.note("preferred version %s of %s not available" % (pv_str, pn))
283                 else:
284                     bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
285
286             # get highest priority file set
287             files = pkg_pn[pn][0]
288             latest = None
289             latest_p = 0
290             latest_f = None
291             for f in files:
292                 pv,pr = self.status.pkg_pvpr[f]
293                 dp = self.status.pkg_dp[f]
294
295                 if (latest is None) or ((latest_p == dp) and (make.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
296                     latest = (pv, pr)
297                     latest_f = f
298                     latest_p = dp
299             if preferred_file is None:
300                 preferred_file = latest_f
301                 preferred_ver = latest
302
303             preferred_versions[pn] = (preferred_ver, preferred_file)
304             latest_versions[pn] = (latest, latest_f)
305
306         pkg_list = pkg_pn.keys()
307         pkg_list.sort()
308
309         for p in pkg_list:
310             pref = preferred_versions[p]
311             latest = latest_versions[p]
312
313             if pref != latest:
314                 prefstr = pref[0][0] + "-" + pref[0][1]
315             else:
316                 prefstr = ""
317
318             print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
319                                         prefstr)
320
321     def buildPackage( self, item ):
322         fn = None
323
324         discriminated = False
325
326         if item not in self.status.providers:
327             bb.error("Nothing provides %s" % item)
328             return 0
329
330         all_p = self.status.providers[item]
331
332         for p in all_p:
333             if p in self.build_cache:
334                 bb.debug(1, "already built %s in this run\n" % p)
335                 return 1
336
337         eligible = []
338         preferred_versions = {}
339
340         # Collate providers by PN
341         pkg_pn = {}
342         for p in all_p:
343             pn = self.status.pkg_fn[p]
344             if pn not in pkg_pn:
345                 pkg_pn[pn] = []
346             pkg_pn[pn].append(p)
347
348         bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
349
350         # Sort by priority
351         for pn in pkg_pn.keys():
352             files = pkg_pn[pn]
353             priorities = {}
354             for f in files:
355                 priority = self.status.bbfile_priority[f]
356                 if priority not in priorities:
357                     priorities[priority] = []
358                 priorities[priority].append(f)
359             p_list = priorities.keys()
360             p_list.sort(lambda a, b: a - b)
361             pkg_pn[pn] = []
362             for p in p_list:
363                 pkg_pn[pn] = [ priorities[p] ] + pkg_pn[pn]
364
365         # If there is a PREFERRED_VERSION, find the highest-priority bbfile providing that
366         # version.  If not, find the latest version provided by an bbfile in the
367         # highest-priority set.
368         for pn in pkg_pn.keys():
369             preferred_file = None
370
371             preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, make.cfg, 1)
372             if preferred_v:
373                 preferred_r = None
374                 m = re.match('(.*)_(.*)', preferred_v)
375                 if m:
376                     preferred_v = m.group(1)
377                     preferred_r = m.group(2)
378
379                 for file_set in pkg_pn[pn]:
380                     for f in file_set:
381                         pv,pr = self.status.pkg_pvpr[f]
382                         if preferred_v == pv and (preferred_r == pr or preferred_r == None):
383                             preferred_file = f
384                             preferred_ver = (pv, pr)
385                             break
386                     if preferred_file:
387                         break
388                 if preferred_r:
389                     pv_str = '%s-%s' % (preferred_v, preferred_r)
390                 else:
391                     pv_str = preferred_v
392                 if preferred_file is None:
393                     bb.note("preferred version %s of %s not available" % (pv_str, pn))
394                 else:
395                     bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
396
397             if preferred_file is None:
398                 # get highest priority file set
399                 files = pkg_pn[pn][0]
400                 latest = None
401                 latest_p = 0
402                 latest_f = None
403                 for f in files:
404                     pv,pr = self.status.pkg_pvpr[f]
405                     dp    = self.status.pkg_dp[f]
406
407                     if (latest is None) or ((latest_p == dp) and (make.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
408                         latest = (pv, pr)
409                         latest_f = f
410                         latest_p = dp
411                 preferred_file = latest_f
412                 preferred_ver = latest
413
414                 bb.debug(1, "selecting %s as latest version of provider %s" % (preferred_file, pn))
415
416             preferred_versions[pn] = (preferred_ver, preferred_file)
417             eligible.append(preferred_file)
418
419         for p in eligible:
420             if p in self.build_cache_fail:
421                 bb.debug(1, "rejecting already-failed %s" % p)
422                 eligible.remove(p)
423
424         if len(eligible) == 0:
425             bb.error("no eligible providers for %s" % item)
426             return 0
427
428         # look to see if one of them is already staged, or marked as preferred.
429         # if so, bump it to the head of the queue
430         for p in all_p:
431             the_data = make.pkgdata[p]
432             pn = bb.data.getVar('PN', the_data, 1)
433             pv = bb.data.getVar('PV', the_data, 1)
434             pr = bb.data.getVar('PR', the_data, 1)
435             tmpdir = bb.data.getVar('TMPDIR', the_data, 1)
436             stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
437             if os.path.exists(stamp):
438                 (newvers, fn) = preferred_versions[pn]
439                 if not fn in eligible:
440                     # package was made ineligible by already-failed check
441                     continue
442                 oldver = "%s-%s" % (pv, pr)
443                 newver = '-'.join(newvers)
444                 if (newver != oldver):
445                     extra_chat = "; upgrading from %s to %s" % (oldver, newver)
446                 else:
447                     extra_chat = ""
448                 if make.options.verbose:
449                     bb.note("selecting already-staged %s to satisfy %s%s" % (pn, item, extra_chat))
450                 eligible.remove(fn)
451                 eligible = [fn] + eligible
452                 discriminated = True
453                 break
454
455         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, make.cfg, 1)
456         if prefervar:
457             self.preferred[item] = prefervar
458
459         if item in self.preferred:
460             for p in eligible:
461                 pn = self.status.pkg_fn[p]
462                 if self.preferred[item] == pn:
463                     if make.options.verbose:
464                         bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
465                     eligible.remove(p)
466                     eligible = [p] + eligible
467                     discriminated = True
468                     break
469
470         if len(eligible) > 1 and discriminated == False:
471             if item not in self.consider_msgs_cache:
472                 providers_list = []
473                 for fn in eligible:
474                     providers_list.append(self.status.pkg_fn[fn])
475                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
476                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
477             self.consider_msgs_cache.append(item)
478
479
480         # run through the list until we find one that we can build
481         for fn in eligible:
482             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
483             if self.tryBuild(fn, item):
484                 return 1
485
486         bb.note("no buildable providers for %s" % item)
487         return 0
488
489     def buildDepgraph( self ):
490         all_depends = self.status.all_depends
491         pn_provides = self.status.pn_provides
492
493         def calc_bbfile_priority(filename):
494             for (regex, pri) in self.status.bbfile_config_priorities:
495                 if regex.match(filename):
496                     return pri
497             return 0
498
499         # Handle PREFERRED_PROVIDERS
500         for p in (bb.data.getVar('PREFERRED_PROVIDERS', make.cfg, 1) or "").split():
501             (providee, provider) = p.split(':')
502             if providee in self.preferred and self.preferred[providee] != provider:
503                 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
504             self.preferred[providee] = provider
505
506         # Calculate priorities for each file
507         for p in make.pkgdata.keys():
508             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
509
510         # Build package list for "bitbake world"
511         bb.debug(1, "collating packages for \"world\"")
512         for f in self.status.possible_world:
513             terminal = True
514             pn = self.status.pkg_fn[f]
515
516             for p in pn_provides[pn]:
517                 if p.startswith('virtual/'):
518                     bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
519                     terminal = False
520                     break
521                 for pf in self.status.providers[p]:
522                     if self.status.pkg_fn[pf] != pn:
523                         bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
524                         terminal = False
525                         break
526             if terminal:
527                 self.status.world_target.add(pn)
528
529             # drop reference count now
530             self.status.possible_world = None
531             self.status.all_depends    = None
532
533     def myProgressCallback( self, x, y, f, file_data, from_cache ):
534         # feed the status with new input
535         self.status.handle_bb_data(f, file_data, from_cache)
536
537         if bbdebug > 0:
538             return
539         if os.isatty(sys.stdout.fileno()):
540             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
541             sys.stdout.flush()
542         else:
543             if x == 1:
544                 sys.stdout.write("Parsing .bb files, please wait...")
545                 sys.stdout.flush()
546             if x == y:
547                 sys.stdout.write("done.")
548                 sys.stdout.flush()
549
550     def executeOneBB( self, fn ):
551             try:
552                 d = bb.parse.handle(fn, make.cfg)
553             except IOError:
554                 bb.fatal("Unable to open %s" % fn)
555
556             if make.options.parse_only:
557                 print "Requested parsing .bb files only.  Exiting."
558                 sys.exit(0)
559
560             name = bb.data.getVar('PN', d, 1)
561             bb.event.fire(bb.event.PkgStarted(name, d))
562             try:
563                 self.stats.attempt += 1
564                 if make.options.force:
565                     bb.data.setVarFlag('do_%s' % make.options.cmd, 'force', 1, d)
566                 if not make.options.dry_run:
567                     bb.build.exec_task('do_%s' % make.options.cmd, d)
568                 bb.event.fire(bb.event.PkgSucceeded(name, d))
569                 self.build_cache.append(fn)
570             except bb.build.FuncFailed:
571                 self.stats.fail += 1
572                 bb.error("task stack execution failed")
573                 bb.event.fire(bb.event.PkgFailed(name, d))
574                 self.build_cache_fail.append(fn)
575             except bb.build.EventException:
576                 self.stats.fail += 1
577                 (type, value, traceback) = sys.exc_info()
578                 e = value.event
579                 bb.error("%s event exception, aborting" % bb.event.getName(e))
580                 bb.event.fire(bb.event.PkgFailed(name, d))
581                 self.build_cache_fail.append(fn)
582
583     def interactiveMode( self ):
584         try:
585             from bb import shell
586         except ImportError:
587             print "Sorry, the shell is not available yet."
588             sys.exit( -1 )
589         else:
590             shell.start()
591
592     def cook( self, args ):
593         if not make.options.cmd:
594             make.options.cmd = "build"
595
596         if make.options.debug:
597             bb.debug_level = make.options.debug
598
599         make.cfg = bb.data.init()
600
601         for f in make.options.file:
602             try:
603                 make.cfg = bb.parse.handle(f, make.cfg)
604             except IOError:
605                 bb.fatal("Unable to open %s" % f)
606
607         try:
608             make.cfg = bb.parse.handle(os.path.join('conf', 'bitbake.conf'), make.cfg)
609         except IOError:
610             bb.fatal("Unable to open %s" % os.path.join('conf', 'bitbake.conf'))
611
612         if not bb.data.getVar("BUILDNAME", make.cfg):
613             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), make.cfg)
614
615         buildname = bb.data.getVar("BUILDNAME", make.cfg)
616
617         if make.options.interactive:
618             self.interactiveMode()
619
620         bf = make.options.buildfile
621         if bf:
622             self.executeOneBB( os.path.abspath(bf) )
623             sys.exit( self.stats.show() )
624
625         # initialise the parsing status now we know we will need deps
626         self.status = BBParsingStatus()
627
628         ignore = bb.data.getVar("ASSUME_PROVIDED", make.cfg, 1) or ""
629         self.status.ignored_dependencies = Set( ignore.split() )
630
631         collections = bb.data.getVar("BBFILE_COLLECTIONS", make.cfg, 1)
632         if collections:
633             collection_list = collections.split()
634             for c in collection_list:
635                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, make.cfg, 1)
636                 if regex == None:
637                     bb.error("BBFILE_PATTERN_%s not defined" % c)
638                     continue
639                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, make.cfg, 1)
640                 if priority == None:
641                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
642                     continue
643                 try:
644                     cre = re.compile(regex)
645                 except re.error:
646                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
647                     continue
648                 try:
649                     pri = int(priority)
650                     self.status.bbfile_config_priorities.append((cre, pri))
651                 except ValueError:
652                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
653
654         pkgs_to_build = None
655         if args:
656             if not pkgs_to_build:
657                 pkgs_to_build = []
658             pkgs_to_build.extend(args)
659         if not pkgs_to_build:
660                 bbpkgs = bb.data.getVar('BBPKGS', make.cfg, 1)
661                 if bbpkgs:
662                         pkgs_to_build = bbpkgs.split()
663         if not pkgs_to_build and not make.options.show_versions:
664                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
665                 print "for usage information."
666                 sys.exit(0)
667
668
669         # Import Psyco if available and not disabled
670         if not make.options.disable_psyco:
671             try:
672                 import psyco
673             except ImportError:
674                 if bbdebug == 0:
675                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
676             else:
677                 psyco.bind( make.collect_bbfiles )
678         else:
679             bb.note("You have disabled Psyco. This decreases performance.")
680
681         try:
682             bb.debug(1, "collecting .bb files")
683             make.collect_bbfiles( self.myProgressCallback )
684             bb.debug(1, "parsing complete")
685             if bbdebug == 0:
686                 print
687             if make.options.parse_only:
688                 print "Requested parsing .bb files only.  Exiting."
689                 sys.exit(0)
690
691             self.buildDepgraph()
692
693             if make.options.show_versions:
694                 self.showVersions()
695                 sys.exit( 0 )
696             if 'world' in pkgs_to_build:
697                 pkgs_to_build.remove('world')
698                 for t in self.status.world_target:
699                     pkgs_to_build.append(t)
700
701             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, make.cfg))
702
703             for k in pkgs_to_build:
704                 failed = False
705                 try:
706                     if self.buildPackage(k) == 0:
707                         # already diagnosed
708                         failed = True
709                 except bb.build.EventException:
710                     bb.error("Build of " + k + " failed")
711                     failed = True
712
713                 if failed:
714                     if make.options.abort:
715                         sys.exit(1)
716
717             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, make.cfg))
718
719             sys.exit( self.stats.show() )
720
721         except KeyboardInterrupt:
722             print "\nNOTE: KeyboardInterrupt - Build not completed."
723             sys.exit(1)
724
725 #============================================================================#
726 # main
727 #============================================================================#
728
729 if __name__ == "__main__":
730
731     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
732     usage = """%prog [options] [package ...]
733
734 Executes the specified task (default is 'build') for a given set of BitBake files.
735 It expects that BBFILES is defined, which is a space seperated list of files to
736 be executed.  BBFILES does support wildcards.
737 Default BBFILES are the .bb files in the current directory.""" )
738
739     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
740                action = "store", dest = "buildfile", default = None )
741
742     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.",
743                action = "store_false", dest = "abort", default = True )
744
745     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
746                action = "store_true", dest = "force", default = False )
747
748     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
749                action = "store_true", dest = "interactive", default = False )
750
751     parser.add_option( "-c", "--cmd", help = "Specify task to execute",
752                action = "store", dest = "cmd", default = "build" )
753
754     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
755                action = "append", dest = "file", default = [] )
756
757     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
758                action = "store_true", dest = "verbose", default = False )
759
760     parser.add_option( "-D", "--debug", help = "Increase the debug level",
761                action = "count", dest="debug", default = 0)
762
763     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
764                action = "store_true", dest = "dry_run", default = False )
765
766     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
767                action = "store_true", dest = "parse_only", default = False )
768
769     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
770                action = "store_true", dest = "disable_psyco", default = False )
771
772     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
773                action = "store_true", dest = "show_versions", default = False )
774
775     options, args = parser.parse_args( sys.argv )
776
777     make.options = options
778     cooker = BBCooker()
779     cooker.cook( args[1:] )