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