Initial import.
[vuplus_bitbake] / bin / bbmake
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 #
8 # This program is free software; you can redistribute it and/or modify it under
9 # the terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
12
13 # This program is distributed in the hope that it will be useful, but WITHOUT
14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License along with
18 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 # Place, Suite 330, Boston, MA 02111-1307 USA. 
20
21 import sys, os, getopt, glob, copy, os.path, re
22 sys.path.append(os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
23 import bb
24 from bb import make
25 from sets import Set
26 import itertools, optparse
27
28 parsespin = itertools.cycle( r'|/-\\' )
29
30 __version__ = 1.2
31 __build_cache_fail = []
32 __build_cache = []
33 __building_list = []
34 __build_path = []
35
36 __preferred = {}
37 __world_target = Set()
38 __ignored_dependencies = Set()
39 __depcmds = { "clean": None,
40              "mrproper": None }
41
42 __stats = {}
43
44 bbfile_config_priorities = []
45 bbfile_priority = {}
46 bbdebug = 0
47
48 def handle_options( args ):
49     parser = optparse.OptionParser( version = "BitBake Build Infrastructure Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
50     usage = """%prog [options] [package ...]
51
52 Builds specified packages, expecting that the .bb files
53 it has to work from are in BBFILES
54 Default packages are all packages in BBFILES.
55 Default BBFILES are the .bb files in the current directory.""" )
56
57     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.",
58                action = "store_false", dest = "abort", default = True )
59
60     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
61                action = "store_true", dest = "force", default = False )
62
63
64     parser.add_option( "-c", "--cmd", help = "specify command to pass to bbbuild. Valid commands are "
65                                              "'fetch' (fetch all sources), " 
66                                              "'unpack' (unpack the sources), "
67                                              "'patch' (apply the patches), "
68                                              "'configure' (configure the source tree), "
69                                              "'compile' (compile the source tree), "
70                                              "'stage' (install libraries and headers needed for subsequent packages), "
71                                              "'install' (install libraries and executables), and"
72                                              "'package' (package files into the selected package format)",
73                action = "store", dest = "cmd", default = "build" )
74
75     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
76                action = "append", dest = "file", default = [] )
77
78     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
79                action = "store_true", dest = "verbose", default = False )
80
81     parser.add_option( "-n", "--dry-run", help = "don't call bbbuild, just go through the motions",
82                action = "store_true", dest = "dry_run", default = False )
83
84     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
85                action = "store_true", dest = "parse_only", default = False )
86
87     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
88                action = "store_true", dest = "disable_psyco", default = False )
89
90     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
91                action = "store_true", dest = "show_versions", default = False )
92
93     options, args = parser.parse_args( args )
94     return options, args[1:]
95
96 def try_build(fn, virtual):
97     if fn in __building_list:
98         bb.error("%s depends on itself (eventually)" % fn)
99         bb.error("upwards chain is: %s" % (" -> ".join(__build_path)))
100         return False
101
102     __building_list.append(fn)
103
104     the_data = make.pkgdata[fn]
105     item = bb.data.getVar('PN', the_data, 1)
106     pathstr = "%s (%s)" % (item, virtual)
107     __build_path.append(pathstr)
108
109     depends_list = (bb.data.getVar('DEPENDS', the_data, 1) or "").split()
110     if make.options.verbose:
111         bb.note("current path: %s" % (" -> ".join(__build_path)))
112         bb.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
113
114     try:
115         failed = False
116
117         if __depcmd:
118             oldcmd = make.options.cmd
119             make.options.cmd = __depcmd
120
121         for d in depends_list:
122             if d in __ignored_dependencies:
123                 continue
124             if not __depcmd:
125                 continue
126             if buildPackage(d) == 0:
127                 bb.error("dependency %s (for %s) not satisfied" % (d,item))
128                 failed = True
129                 if make.options.abort:
130                     break
131
132         if __depcmd:
133             make.options.cmd = oldcmd
134
135         if failed:
136             __stats["deps"] += 1
137             return False
138
139         bb.event.fire(bb.event.PkgStarted(item, make.pkgdata[fn]))
140         try:
141             __stats["attempt"] += 1
142             if not make.options.dry_run:
143                 bb.build.exec_task('do_%s' % make.options.cmd, make.pkgdata[fn])
144             bb.event.fire(bb.event.PkgSucceeded(item, make.pkgdata[fn]))
145             __build_cache.append(fn)
146             return True
147         except bb.build.FuncFailed:
148             __stats["fail"] += 1
149             bb.error("task stack execution failed")
150             bb.event.fire(bb.event.PkgFailed(item, make.pkgdata[fn]))
151             __build_cache_fail.append(fn)
152             raise
153         except bb.build.EventException:
154             __stats["fail"] += 1
155             (type, value, traceback) = sys.exc_info()
156             e = value.event
157             bb.error("%s event exception, aborting" % bb.event.getName(e))
158             bb.event.fire(bb.event.PkgFailed(item, make.pkgdata[fn]))
159             __build_cache_fail.append(fn)
160             raise
161     finally:
162         __building_list.remove(fn)
163         __build_path.remove(pathstr)
164
165 def showVersions():
166     pkg_pn = {}
167     preferred_versions = {}
168     latest_versions = {}
169
170     for p in make.pkgdata.keys():
171         pn = bb.data.getVar('PN', make.pkgdata[p], 1)
172         if not pkg_pn.has_key(pn):
173             pkg_pn[pn] = []
174         pkg_pn[pn].append(p)
175     
176     # Sort by priority
177     for pn in pkg_pn.keys():
178         files = pkg_pn[pn]
179         priorities = {}
180         for f in files:
181             priority = bbfile_priority[f]
182             if not priorities.has_key(priority):
183                 priorities[priority] = []
184             priorities[priority].append(f)
185         p_list = priorities.keys()
186         p_list.sort(lambda a, b: a - b)
187         pkg_pn[pn] = []
188         for p in p_list:
189             pkg_pn[pn] = [ priorities[p] ] + pkg_pn[pn]
190
191     # If there is a PREFERRED_VERSION, find the highest-priority bbfile providing that
192     # version.  If not, find the latest version provided by an bbfile in the
193     # highest-priority set.
194     for pn in pkg_pn.keys():
195         preferred_file = None
196         
197         preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, make.cfg, 1)
198         if preferred_v:
199             preferred_r = None
200             m = re.match('(.*)_(.*)', preferred_v)
201             if m:
202                 preferred_v = m.group(1)
203                 preferred_r = m.group(2)
204                 
205             for file_set in pkg_pn[pn]:
206                 for f in file_set:
207                     the_data = make.pkgdata[f]
208                     pv = bb.data.getVar('PV', the_data, 1)
209                     pr = bb.data.getVar('PR', the_data, 1)
210                     if preferred_v == pv and (preferred_r == pr or preferred_r == None):
211                         preferred_file = f
212                         preferred_ver = (pv, pr)
213                         break
214                 if preferred_file:
215                     break
216             if preferred_r:
217                 pv_str = '%s-%s' % (preferred_v, preferred_r)
218             else:
219                 pv_str = preferred_v
220             if preferred_file is None:
221                 bb.note("preferred version %s of %s not available" % (pv_str, pn))
222             else:
223                 bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
224                
225         # get highest priority file set
226         files = pkg_pn[pn][0]
227         latest = None
228         latest_p = 0
229         latest_f = None
230         for f in files:
231             the_data = make.pkgdata[f]
232             pv = bb.data.getVar('PV', the_data, 1)
233             pr = bb.data.getVar('PR', the_data, 1)
234             dp = int(bb.data.getVar('DEFAULT_PREFERENCE', the_data, 1) or "0")
235
236             if (latest is None) or ((latest_p == dp) and (make.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
237                 latest = (pv, pr)
238                 latest_f = f
239                 latest_p = dp
240         if preferred_file is None:
241             preferred_file = latest_f
242             preferred_ver = latest
243             
244         preferred_versions[pn] = (preferred_ver, preferred_file)
245         latest_versions[pn] = (latest, latest_f)
246
247     pkg_list = pkg_pn.keys()
248     pkg_list.sort()
249     
250     for p in pkg_list:
251         pref = preferred_versions[p]
252         latest = latest_versions[p]
253
254         if pref != latest:
255             prefstr = pref[0][0] + "-" + pref[0][1]
256         else:
257             prefstr = ""
258
259         print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
260                                    prefstr)
261
262 def buildPackage(item):
263     fn = None
264
265     discriminated = False
266
267     if not providers.has_key(item):
268         bb.error("Nothing provides %s" % item)
269         return 0
270
271     all_p = providers[item]
272
273     for p in all_p:
274         if p in __build_cache:
275             bb.debug(1, "already built %s in this run\n" % p)
276             return 1
277
278     eligible = []
279     preferred_versions = {}
280
281     # Collate providers by PN
282     pkg_pn = {}
283     for p in all_p:
284         the_data = make.pkgdata[p]
285         pn = bb.data.getVar('PN', the_data, 1)
286         if not pkg_pn.has_key(pn):
287             pkg_pn[pn] = []
288         pkg_pn[pn].append(p)
289
290     bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
291
292     # Sort by priority
293     for pn in pkg_pn.keys():
294         files = pkg_pn[pn]
295         priorities = {}
296         for f in files:
297             priority = bbfile_priority[f]
298             if not priorities.has_key(priority):
299                 priorities[priority] = []
300             priorities[priority].append(f)
301         p_list = priorities.keys()
302         p_list.sort(lambda a, b: a - b)
303         pkg_pn[pn] = []
304         for p in p_list:
305             pkg_pn[pn] = [ priorities[p] ] + pkg_pn[pn]
306
307     # If there is a PREFERRED_VERSION, find the highest-priority bbfile providing that
308     # version.  If not, find the latest version provided by an bbfile in the
309     # highest-priority set.
310     for pn in pkg_pn.keys():
311         preferred_file = None
312         
313         preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, make.cfg, 1)
314         if preferred_v:
315             preferred_r = None
316             m = re.match('(.*)_(.*)', preferred_v)
317             if m:
318                 preferred_v = m.group(1)
319                 preferred_r = m.group(2)
320                 
321             for file_set in pkg_pn[pn]:
322                 for f in file_set:
323                     the_data = make.pkgdata[f]
324                     pv = bb.data.getVar('PV', the_data, 1)
325                     pr = bb.data.getVar('PR', the_data, 1)
326                     if preferred_v == pv and (preferred_r == pr or preferred_r == None):
327                         preferred_file = f
328                         preferred_ver = (pv, pr)
329                         break
330                 if preferred_file:
331                     break
332             if preferred_r:
333                 pv_str = '%s-%s' % (preferred_v, preferred_r)
334             else:
335                 pv_str = preferred_v
336             if preferred_file is None:
337                 bb.note("preferred version %s of %s not available" % (pv_str, pn))
338             else:
339                 bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
340                 
341         if preferred_file is None:
342             # get highest priority file set
343             files = pkg_pn[pn][0]
344             latest = None
345             latest_p = 0
346             latest_f = None
347             for f in files:
348                 the_data = make.pkgdata[f]
349                 pv = bb.data.getVar('PV', the_data, 1)
350                 pr = bb.data.getVar('PR', the_data, 1)
351                 dp = int(bb.data.getVar('DEFAULT_PREFERENCE', the_data, 1) or "0")
352
353                 if (latest is None) or ((latest_p == dp) and (make.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
354                     latest = (pv, pr)
355                     latest_f = f
356                     latest_p = dp
357             preferred_file = latest_f
358             preferred_ver = latest
359             
360             bb.debug(1, "selecting %s as latest version of provider %s" % (preferred_file, pn))
361
362         preferred_versions[pn] = (preferred_ver, preferred_file)
363         eligible.append(preferred_file)
364
365     for p in eligible:
366         if p in __build_cache_fail:
367             bb.debug(1, "rejecting already-failed %s" % p)
368             eligible.remove(p)
369
370     if len(eligible) == 0:
371         bb.error("no eligible providers for %s" % item)
372         return 0
373
374     # look to see if one of them is already staged, or marked as preferred.
375     # if so, bump it to the head of the queue
376     for p in all_p:
377         the_data = make.pkgdata[p]
378         pn = bb.data.getVar('PN', the_data, 1)
379         pv = bb.data.getVar('PV', the_data, 1)
380         pr = bb.data.getVar('PR', the_data, 1)
381         tmpdir = bb.data.getVar('TMPDIR', the_data, 1)
382         stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
383         if os.path.exists(stamp):
384             (newvers, fn) = preferred_versions[pn]
385             if not fn in eligible:
386                 # package was made ineligible by already-failed check
387                 continue
388             oldver = "%s-%s" % (pv, pr)
389             newver = '-'.join(newvers)
390             if (newver != oldver):
391                 extra_chat = "; upgrading from %s to %s" % (oldver, newver)
392             else:
393                 extra_chat = ""
394             if make.options.verbose:
395                 bb.note("selecting already-staged %s to satisfy %s%s" % (pn, item, extra_chat))
396             eligible.remove(fn)
397             eligible = [fn] + eligible
398             discriminated = True
399             break
400
401     prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, make.cfg, 1)
402     if prefervar:
403         __preferred[item] = prefervar
404
405     if __preferred.has_key(item):
406         for p in eligible:
407             the_data = make.pkgdata[p]
408             pn = bb.data.getVar('PN', the_data, 1)
409             if __preferred[item] == pn:
410                 if make.options.verbose:
411                     bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
412                 eligible.remove(p)
413                 eligible = [p] + eligible
414                 discriminated = True
415                 break
416
417     if len(eligible) > 1 and discriminated == False:
418         providers_list = []
419         for fn in eligible:
420             providers_list.append(bb.data.getVar('PN', make.pkgdata[fn], 1))
421         bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
422         bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
423
424     # run through the list until we find one that we can build
425     for fn in eligible:
426         bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
427         if try_build(fn, item):
428             return 1
429
430     bb.note("no buildable providers for %s" % item)
431     return 0
432
433 def build_depgraph():
434     all_depends = Set()
435     pn_provides = {}
436
437     def progress(p):
438         if bbdebug or progress.p == p: return 
439         progress.p = p
440         if os.isatty(sys.stdout.fileno()):
441             sys.stdout.write("\rNOTE: Building provider hash: [%s%s] (%02d%%)" % ( "#" * (p/5), " " * ( 20 - p/5 ), p ) )
442             sys.stdout.flush()
443         else:
444             if p == 0:
445                 sys.stdout.write("NOTE: Building provider hash, please wait...\n")
446             if p == 100:
447                 sys.stdout.write("done.\n")
448     progress.p = 0
449
450     def calc_bbfile_priority(filename):
451         for (regex, pri) in bbfile_config_priorities:
452             if regex.match(filename):
453                 return pri
454         return 0
455
456     # Handle PREFERRED_PROVIDERS
457     for p in (bb.data.getVar('PREFERRED_PROVIDERS', make.cfg, 1) or "").split():
458         (providee, provider) = p.split(':')
459         if __preferred.has_key(providee) and __preferred[providee] != provider:
460             bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, __preferred[providee]))
461         __preferred[providee] = provider
462
463     # Calculate priorities for each file
464     for p in make.pkgdata.keys():
465         bbfile_priority[p] = calc_bbfile_priority(p)
466     
467     n = len(make.pkgdata.keys())
468     i = 0
469
470     op = -1
471
472     bb.debug(1, "BBMAKE building providers hashes")
473
474     # Build forward and reverse provider hashes
475     # Forward: virtual -> [filenames]
476     # Reverse: PN -> [virtuals]
477     for f in make.pkgdata.keys():
478         d = make.pkgdata[f]
479
480         pn = bb.data.getVar('PN', d, 1)
481         provides = Set([pn] + (bb.data.getVar("PROVIDES", d, 1) or "").split())
482
483         if not pn_provides.has_key(pn):
484             pn_provides[pn] = Set()
485         pn_provides[pn] |= provides
486
487         for provide in provides:
488             if not providers.has_key(provide):
489                 providers[provide] = []
490             providers[provide].append(f)
491
492         deps = (bb.data.getVar("DEPENDS", d, 1) or "").split()
493         for dep in deps:
494             all_depends.add(dep)
495
496         i += 1
497         p = (100 * i) / n
498         if p != op:
499             op = p
500             progress(p)
501
502     if bbdebug == 0:
503         sys.stdout.write("\n")
504
505     # Build package list for "bbmake world"
506     bb.debug(1, "BBMAKE collating packages for \"world\"")
507     for f in make.pkgdata.keys():
508         d = make.pkgdata[f]
509         if bb.data.getVar('BROKEN', d, 1) or bb.data.getVar('EXCLUDE_FROM_WORLD', d, 1):
510             bb.debug(2, "BBMAKE skipping %s due to BROKEN/EXCLUDE_FROM_WORLD" % f)
511             continue
512         terminal = True
513         pn = bb.data.getVar('PN', d, 1)
514         for p in pn_provides[pn]:
515             if p.startswith('virtual/'):
516                 bb.debug(2, "BBMAKE skipping %s due to %s provider starting with virtual/" % (f, p))
517                 terminal = False
518                 break
519             for pf in providers[p]:
520                 if bb.data.getVar('PN', make.pkgdata[pf], 1) != pn:
521                     bb.debug(2, "BBMAKE skipping %s due to both us and %s providing %s" % (f, pf, p))
522                     terminal = False
523                     break
524         if terminal:
525             __world_target.add(pn)
526
527 def myProgressCallback( x, y, f ):
528     if bbdebug > 0:
529         return
530     if os.isatty(sys.stdout.fileno()):
531         sys.stdout.write("\rNOTE: Parsing .bb files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
532         sys.stdout.flush()
533     else:
534         if x == 1:
535             sys.stdout.write("Parsing .bb files, please wait...")
536             sys.stdout.flush()
537         if x == y:
538             sys.stdout.write("done.")
539             sys.stdout.flush()
540
541
542 #
543 # main
544 #
545
546 if __name__ == "__main__":
547
548     if "BBDEBUG" in os.environ:
549         bbdebug = int(os.environ["BBDEBUG"])
550
551     make.options, args = handle_options( sys.argv )
552
553     if not make.options.cmd:
554         make.options.cmd = "build"
555
556     if make.options.cmd in __depcmds:
557         __depcmd=__depcmds[make.options.cmd]
558     else:
559         __depcmd=make.options.cmd
560
561     make.pkgdata = {}
562     make.cfg = bb.data.init()
563     providers = {}
564
565     for f in make.options.file:
566         try:
567             make.cfg = bb.parse.handle(f, make.cfg)
568         except IOError:
569             bb.fatal("Unable to open %s" % f)
570
571     try:
572         make.cfg = bb.parse.handle(os.path.join('conf', 'bitbake.conf'), make.cfg)
573     except IOError:
574         bb.fatal("Unable to open %s" % os.path.join('conf', 'bitbake.conf'))
575
576     if not bb.data.getVar("BUILDNAME", make.cfg):
577         bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), make.cfg)
578
579     buildname = bb.data.getVar("BUILDNAME", make.cfg)
580
581     ignore = bb.data.getVar("ASSUME_PROVIDED", make.cfg, 1) or ""
582     __ignored_dependencies = ignore.split()
583
584     collections = bb.data.getVar("BBFILE_COLLECTIONS", make.cfg, 1)
585     if collections:
586         collection_list = collections.split()
587         for c in collection_list:
588             regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, make.cfg, 1)
589             if regex == None:
590                 bb.error("BBFILE_PATTERN_%s not defined" % c)
591                 continue
592             priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, make.cfg, 1)
593             if priority == None:
594                 bb.error("BBFILE_PRIORITY_%s not defined" % c)
595                 continue
596             try:
597                 cre = re.compile(regex)
598             except re.error:
599                 bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
600                 continue
601             try:
602                 pri = int(priority)
603                 bbfile_config_priorities.append((cre, pri))
604             except ValueError:
605                 bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
606
607     pkgs_to_build = None
608     if args:
609         if not pkgs_to_build:
610             pkgs_to_build = []
611         pkgs_to_build.extend(args)
612     if not pkgs_to_build:
613             bbpkgs = bb.data.getVar('BBPKGS', make.cfg, 1)
614             if bbpkgs:
615                     pkgs_to_build = bbpkgs.split()
616     if not pkgs_to_build and not make.options.show_versions:
617             print "Nothing to build. Use 'bbmake world' to build everything."
618             sys.exit(0)
619
620     __stats["attempt"] = 0
621     __stats["success"] = 0
622     __stats["fail"] = 0
623     __stats["deps"] = 0
624
625     # Import Psyco if available and not disabled
626     if not make.options.disable_psyco:
627         try:
628             import psyco
629         except ImportError:
630             if bbdebug == 0:
631                 bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
632         else:
633             psyco.bind( make.collect_bbfiles )
634     else:
635         bb.note("You have disabled Psyco. This decreases performance.")
636
637     try:
638         bb.debug(1, "BBMAKE collecting .bb files")
639         make.collect_bbfiles( myProgressCallback )
640         bb.debug(1, "BBMAKE parsing complete")
641         if bbdebug == 0:
642             print
643         if make.options.parse_only:
644             print "Requested parsing .bb files only.  Exiting."
645             sys.exit(0)
646
647         build_depgraph()
648
649         if make.options.show_versions:
650             showVersions()
651             sys.exit(0)
652             
653         if 'world' in pkgs_to_build:
654             pkgs_to_build.remove('world')
655             for t in __world_target:
656                 pkgs_to_build.append(t)
657
658         bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, make.cfg))
659
660         for k in pkgs_to_build:
661             failed = False
662             try:
663                 if buildPackage(k) == 0:
664                     # already diagnosed
665                     failed = True
666             except bb.build.EventException:
667                 bb.error("Build of " + k + " failed")
668                 failed = True
669
670             if failed:
671                 if make.options.abort:
672                     sys.exit(1)
673
674         bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, make.cfg))
675
676         print "Build statistics:"
677         print "  Attempted builds: %d" % __stats["attempt"]
678         if __stats["fail"] != 0:
679             print "  Failed builds: %d" % __stats["fail"]
680         if __stats["deps"] != 0:
681             print "  Dependencies not satisfied: %d" % __stats["deps"]
682         if __stats["fail"] != 0 or __stats["deps"] != 0:
683             sys.exit(1)
684         sys.exit(0)
685
686     except KeyboardInterrupt:
687         print "\nNOTE: KeyboardInterrupt - Build not completed."
688         sys.exit(1)