update configuration data before entering the shell. patch courtesy Proti - thanks.
[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 utils, data, parse, debug, event, fatal
28 from sets import Set
29 import itertools, optparse
30
31 parsespin = itertools.cycle( r'|/-\\' )
32 bbdebug = 0
33
34 __version__ = "1.3.2"
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 # BBOptions
137 #============================================================================#
138 class BBConfiguration( object ):
139     """
140     Manages build options and configurations for one run
141     """
142     def __init__( self, options ):
143         for key, val in options.__dict__.items():
144             setattr( self, key, val )
145         self.data = data.init()
146
147 #============================================================================#
148 # BBCooker
149 #============================================================================#
150 class BBCooker:
151     """
152     Manages one bitbake build run
153     """
154
155     ParsingStatus = BBParsingStatus     # make it visible from the shell
156     Statistics = BBStatistics           # make it visible from the shell
157
158     def __init__( self ):
159         self.build_cache_fail = []
160         self.build_cache = []
161         self.building_list = []
162         self.build_path = []
163         self.consider_msgs_cache = []
164         self.preferred = {}
165         self.stats = BBStatistics()
166         self.status = None
167
168         self.pkgdata = None
169         self.cache = None
170
171     def tryBuildPackage( self, fn, item, the_data ):
172         """Build one package"""
173         bb.event.fire(bb.event.PkgStarted(item, the_data))
174         try:
175             self.stats.attempt += 1
176             if self.configuration.force:
177                 bb.data.setVarFlag('do_%s' % self.configuration.cmd, 'force', 1, the_data)
178             if not self.configuration.dry_run:
179                 bb.build.exec_task('do_%s' % self.configuration.cmd, the_data)
180             bb.event.fire(bb.event.PkgSucceeded(item, the_data))
181             self.build_cache.append(fn)
182             return True
183         except bb.build.FuncFailed:
184             self.stats.fail += 1
185             bb.error("task stack execution failed")
186             bb.event.fire(bb.event.PkgFailed(item, the_data))
187             self.build_cache_fail.append(fn)
188             raise
189         except bb.build.EventException, e:
190             self.stats.fail += 1
191             event = e.args[1]
192             bb.error("%s event exception, aborting" % bb.event.getName(event))
193             bb.event.fire(bb.event.PkgFailed(item, the_data))
194             self.build_cache_fail.append(fn)
195             raise
196
197     def tryBuild( self, fn, virtual ):
198         """Build a provider and its dependencies"""
199         if fn in self.building_list:
200             bb.error("%s depends on itself (eventually)" % fn)
201             bb.error("upwards chain is: %s" % (" -> ".join(self.build_path)))
202             return False
203
204         the_data = self.pkgdata[fn]
205         item = self.status.pkg_fn[fn]
206
207         self.building_list.append(fn)
208
209         pathstr = "%s (%s)" % (item, virtual)
210         self.build_path.append(pathstr)
211
212         depends_list = (bb.data.getVar('DEPENDS', the_data, 1) or "").split()
213         if self.configuration.verbose:
214             bb.note("current path: %s" % (" -> ".join(self.build_path)))
215             bb.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
216
217         try:
218             failed = False
219
220             depcmd = self.configuration.cmd
221             bbdepcmd = bb.data.getVarFlag('do_%s' % self.configuration.cmd, 'bbdepcmd', the_data)
222             if bbdepcmd is not None:
223                 if bbdepcmd == "":
224                     depcmd = None
225                 else:
226                     depcmd = bbdepcmd
227
228             if depcmd:
229                 oldcmd = self.configuration.cmd
230                 self.configuration.cmd = depcmd
231
232             for dependency in depends_list:
233                 if dependency in self.status.ignored_dependencies:
234                     continue
235                 if not depcmd:
236                     continue
237                 if self.buildProvider( dependency ) == 0:
238                     bb.error("dependency %s (for %s) not satisfied" % (dependency,item))
239                     failed = True
240                     if self.configuration.abort:
241                         break
242
243             if depcmd:
244                 self.configuration.cmd = oldcmd
245
246             if failed:
247                 self.stats.deps += 1
248                 return False
249
250             if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
251                 self.build_cache.append(fn)
252                 return True
253
254             return self.tryBuildPackage( fn, item, the_data )
255
256         finally:
257             self.building_list.remove(fn)
258             self.build_path.remove(pathstr)
259
260     def findBestProvider( self, pn, pkg_pn = None):
261         """
262         If there is a PREFERRED_VERSION, find the highest-priority bbfile
263         providing that version.  If not, find the latest version provided by
264         an bbfile in the highest-priority set.
265         """
266         if not pkg_pn:
267             pkg_pn = self.status.pkg_pn
268
269         files = pkg_pn[pn]
270         priorities = {}
271         for f in files:
272             priority = self.status.bbfile_priority[f]
273             if priority not in priorities:
274                 priorities[priority] = []
275             priorities[priority].append(f)
276         p_list = priorities.keys()
277         p_list.sort(lambda a, b: a - b)
278         tmp_pn = []
279         for p in p_list:
280             tmp_pn = [priorities[p]] + tmp_pn
281
282         preferred_file = None
283
284         preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, self.configuration.data, 1)
285         if preferred_v:
286             m = re.match('(.*)_(.*)', preferred_v)
287             if m:
288                 preferred_v = m.group(1)
289                 preferred_r = m.group(2)
290             else:
291                 preferred_r = None
292
293             for file_set in tmp_pn:
294                 for f in file_set:
295                     pv,pr = self.status.pkg_pvpr[f]
296                     if preferred_v == pv and (preferred_r == pr or preferred_r == None):
297                         preferred_file = f
298                         preferred_ver = (pv, pr)
299                         break
300                 if preferred_file:
301                     break;
302             if preferred_r:
303                 pv_str = '%s-%s' % (preferred_v, preferred_r)
304             else:
305                 pv_str = preferred_v
306             if preferred_file is None:
307                 bb.note("preferred version %s of %s not available" % (pv_str, pn))
308             else:
309                 bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
310
311         # get highest priority file set
312         files = tmp_pn[0]
313         latest = None
314         latest_p = 0
315         latest_f = None
316         for file_name in files:
317             pv,pr = self.status.pkg_pvpr[file_name]
318             dp = self.status.pkg_dp[file_name]
319
320             if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
321                 latest = (pv, pr)
322                 latest_f = file_name
323                 latest_p = dp
324         if preferred_file is None:
325             preferred_file = latest_f
326             preferred_ver = latest
327
328         return (latest,latest_f,preferred_ver, preferred_file)
329
330     def showVersions( self ):
331         pkg_pn = self.status.pkg_pn
332         preferred_versions = {}
333         latest_versions = {}
334
335         # Sort by priority
336         for pn in pkg_pn.keys():
337             (last_ver,last_file,pref_ver,pref_file) = self.findBestProvider(pn)
338             preferred_versions[pn] = (pref_ver, pref_file)
339             latest_versions[pn] = (last_ver, last_file)
340
341         pkg_list = pkg_pn.keys()
342         pkg_list.sort()
343
344         for p in pkg_list:
345             pref = preferred_versions[p]
346             latest = latest_versions[p]
347
348             if pref != latest:
349                 prefstr = pref[0][0] + "-" + pref[0][1]
350             else:
351                 prefstr = ""
352
353             print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
354                                         prefstr)
355
356     def showEnvironment( self ):
357         """Show the outer or per-package environment"""
358         if self.configuration.buildfile:
359             try:
360                 self.configuration.data, fromCache = self.load_bbfile( self.configuration.buildfile )
361             except IOError, e:
362                 fatal("Unable to read %s: %s" % ( self.configuration.buildfile, e ))
363             except Exception, e:
364                 fatal("%s" % e)
365         # emit variables and shell functions
366         try:
367             data.update_data( self.configuration.data )
368             data.emit_env(sys.__stdout__, self.configuration.data, True)
369         except Exception, e:
370             fatal("%s" % e)
371         # emit the metadata which isnt valid shell
372         for e in self.configuration.data.keys():
373             if data.getVarFlag( e, 'python', self.configuration.data ):
374                 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
375
376     def buildProvider( self, item ):
377         fn = None
378
379         discriminated = False
380
381         if item not in self.status.providers:
382             bb.error("Nothing provides %s" % item)
383             return 0
384
385         all_p = self.status.providers[item]
386
387         for p in all_p:
388             if p in self.build_cache:
389                 bb.debug(1, "already built %s in this run\n" % p)
390                 return 1
391
392         eligible = []
393         preferred_versions = {}
394
395         # Collate providers by PN
396         pkg_pn = {}
397         for p in all_p:
398             pn = self.status.pkg_fn[p]
399             if pn not in pkg_pn:
400                 pkg_pn[pn] = []
401             pkg_pn[pn].append(p)
402
403         bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
404
405         for pn in pkg_pn.keys():
406             preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
407             eligible.append(preferred_versions[pn][1])
408
409         for p in eligible:
410             if p in self.build_cache_fail:
411                 bb.debug(1, "rejecting already-failed %s" % p)
412                 eligible.remove(p)
413
414         if len(eligible) == 0:
415             bb.error("no eligible providers for %s" % item)
416             return 0
417
418         # look to see if one of them is already staged, or marked as preferred.
419         # if so, bump it to the head of the queue
420         for p in all_p:
421             the_data = self.pkgdata[p]
422             pn = bb.data.getVar('PN', the_data, 1)
423             pv = bb.data.getVar('PV', the_data, 1)
424             pr = bb.data.getVar('PR', the_data, 1)
425             tmpdir = bb.data.getVar('TMPDIR', the_data, 1)
426             stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
427             if os.path.exists(stamp):
428                 (newvers, fn) = preferred_versions[pn]
429                 if not fn in eligible:
430                     # package was made ineligible by already-failed check
431                     continue
432                 oldver = "%s-%s" % (pv, pr)
433                 newver = '-'.join(newvers)
434                 if (newver != oldver):
435                     extra_chat = "; upgrading from %s to %s" % (oldver, newver)
436                 else:
437                     extra_chat = ""
438                 if self.configuration.verbose:
439                     bb.note("selecting already-staged %s to satisfy %s%s" % (pn, item, extra_chat))
440                 eligible.remove(fn)
441                 eligible = [fn] + eligible
442                 discriminated = True
443                 break
444
445         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
446         if prefervar:
447             self.preferred[item] = prefervar
448
449         if item in self.preferred:
450             for p in eligible:
451                 pn = self.status.pkg_fn[p]
452                 if self.preferred[item] == pn:
453                     if self.configuration.verbose:
454                         bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
455                     eligible.remove(p)
456                     eligible = [p] + eligible
457                     discriminated = True
458                     break
459
460         if len(eligible) > 1 and discriminated == False:
461             if item not in self.consider_msgs_cache:
462                 providers_list = []
463                 for fn in eligible:
464                     providers_list.append(self.status.pkg_fn[fn])
465                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
466                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
467             self.consider_msgs_cache.append(item)
468
469
470         # run through the list until we find one that we can build
471         for fn in eligible:
472             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
473             if self.tryBuild(fn, item):
474                 return 1
475
476         bb.note("no buildable providers for %s" % item)
477         return 0
478
479     def buildDepgraph( self ):
480         all_depends = self.status.all_depends
481         pn_provides = self.status.pn_provides
482
483         def calc_bbfile_priority(filename):
484             for (regex, pri) in self.status.bbfile_config_priorities:
485                 if regex.match(filename):
486                     return pri
487             return 0
488
489         # Handle PREFERRED_PROVIDERS
490         for p in (bb.data.getVar('PREFERRED_PROVIDERS', self.configuration.data, 1) or "").split():
491             (providee, provider) = p.split(':')
492             if providee in self.preferred and self.preferred[providee] != provider:
493                 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
494             self.preferred[providee] = provider
495
496         # Calculate priorities for each file
497         for p in self.pkgdata.keys():
498             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
499
500         # Build package list for "bitbake world"
501         bb.debug(1, "collating packages for \"world\"")
502         for f in self.status.possible_world:
503             terminal = True
504             pn = self.status.pkg_fn[f]
505
506             for p in pn_provides[pn]:
507                 if p.startswith('virtual/'):
508                     bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
509                     terminal = False
510                     break
511                 for pf in self.status.providers[p]:
512                     if self.status.pkg_fn[pf] != pn:
513                         bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
514                         terminal = False
515                         break
516             if terminal:
517                 self.status.world_target.add(pn)
518
519             # drop reference count now
520             self.status.possible_world = None
521             self.status.all_depends    = None
522
523     def myProgressCallback( self, x, y, f, file_data, from_cache ):
524         # feed the status with new input
525         self.status.handle_bb_data(f, file_data, from_cache)
526
527         if bbdebug > 0:
528             return
529         if os.isatty(sys.stdout.fileno()):
530             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
531             sys.stdout.flush()
532         else:
533             if x == 1:
534                 sys.stdout.write("Parsing .bb files, please wait...")
535                 sys.stdout.flush()
536             if x == y:
537                 sys.stdout.write("done.")
538                 sys.stdout.flush()
539
540     def interactiveMode( self ):
541         """Drop off into a shell"""
542         try:
543             from bb import shell
544         except ImportError, details:
545             bb.fatal("Sorry, shell not available (%s)" % details )
546         else:
547             bb.data.update_data( self.configuration.data )
548             shell.start( self )
549             sys.exit( 0 )
550
551     def parseConfigurationFile( self, afile ):
552         try:
553             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
554         except IOError:
555             bb.fatal( "Unable to open %s" % afile )
556         except bb.parse.ParseError, details:
557             bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
558
559     def handleCollections( self, collections ):
560         """Handle collections"""
561         if collections:
562             collection_list = collections.split()
563             for c in collection_list:
564                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
565                 if regex == None:
566                     bb.error("BBFILE_PATTERN_%s not defined" % c)
567                     continue
568                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
569                 if priority == None:
570                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
571                     continue
572                 try:
573                     cre = re.compile(regex)
574                 except re.error:
575                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
576                     continue
577                 try:
578                     pri = int(priority)
579                     self.status.bbfile_config_priorities.append((cre, pri))
580                 except ValueError:
581                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
582
583
584     def cook( self, configuration, args ):
585         self.configuration = configuration
586
587         if not self.configuration.cmd:
588             self.configuration.cmd = "build"
589
590         if self.configuration.debug:
591             bb.debug_level = self.configuration.debug
592
593         self.configuration.data = bb.data.init()
594
595         for f in self.configuration.file:
596             self.parseConfigurationFile( f )
597
598         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
599
600         if self.configuration.show_environment:
601             self.showEnvironment()
602             sys.exit( 0 )
603
604         if not bb.data.getVar("BUILDNAME", self.configuration.data):
605             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
606
607         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
608
609         if self.configuration.interactive:
610             self.interactiveMode()
611
612         if self.configuration.buildfile is not None:
613             bf = os.path.abspath( self.configuration.buildfile )
614             try:
615                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
616             except IOError:
617                 bb.fatal("Unable to open %s" % bf)
618
619             item = bb.data.getVar('PN', bbfile_data, 1)
620             try:
621                 self.tryBuildPackage( bf, item, bbfile_data )
622             except bb.build.EventException:
623                 bb.error( "Build of '%s' failed" % item )
624
625             sys.exit( self.stats.show() )
626
627         # initialise the parsing status now we know we will need deps
628         self.status = BBParsingStatus()
629
630         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
631         self.status.ignored_dependencies = Set( ignore.split() )
632
633         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
634
635         pkgs_to_build = None
636         if args:
637             if not pkgs_to_build:
638                 pkgs_to_build = []
639             pkgs_to_build.extend(args)
640         if not pkgs_to_build:
641                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
642                 if bbpkgs:
643                         pkgs_to_build = bbpkgs.split()
644         if not pkgs_to_build and not self.configuration.show_versions \
645                              and not self.configuration.interactive \
646                              and not self.configuration.show_environment:
647                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
648                 print "for usage information."
649                 sys.exit(0)
650
651         # Import Psyco if available and not disabled
652         if not self.configuration.disable_psyco:
653             try:
654                 import psyco
655             except ImportError:
656                 if bbdebug == 0:
657                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
658             else:
659                 psyco.bind( self.collect_bbfiles )
660         else:
661             bb.note("You have disabled Psyco. This decreases performance.")
662
663         try:
664             bb.debug(1, "collecting .bb files")
665             self.collect_bbfiles( self.myProgressCallback )
666             bb.debug(1, "parsing complete")
667             if bbdebug == 0:
668                 print
669             if self.configuration.parse_only:
670                 print "Requested parsing .bb files only.  Exiting."
671                 return
672
673             bb.data.update_data( self.configuration.data )
674             self.buildDepgraph()
675
676             if self.configuration.show_versions:
677                 self.showVersions()
678                 sys.exit( 0 )
679             if 'world' in pkgs_to_build:
680                 pkgs_to_build.remove('world')
681                 for t in self.status.world_target:
682                     pkgs_to_build.append(t)
683
684             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.data))
685
686             for k in pkgs_to_build:
687                 failed = False
688                 try:
689                     if self.buildProvider( k ) == 0:
690                         # already diagnosed
691                         failed = True
692                 except bb.build.EventException:
693                     bb.error("Build of " + k + " failed")
694                     failed = True
695
696                 if failed:
697                     if self.configuration.abort:
698                         sys.exit(1)
699
700             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data))
701
702             sys.exit( self.stats.show() )
703
704         except KeyboardInterrupt:
705             print "\nNOTE: KeyboardInterrupt - Build not completed."
706             sys.exit(1)
707
708     def get_bbfiles( self, path = os.getcwd() ):
709         """Get list of default .bb files by reading out the current directory"""
710         contents = os.listdir(path)
711         bbfiles = []
712         for f in contents:
713             (root, ext) = os.path.splitext(f)
714             if ext == ".bb":
715                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
716         return bbfiles
717
718     def find_bbfiles( self, path ):
719         """Find all the .bb files in a directory (uses find)"""
720         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
721         try:
722             finddata = os.popen(findcmd)
723         except OSError:
724             return []
725         return finddata.readlines()
726
727     def deps_clean(self, d):
728         depstr = data.getVar('__depends', d)
729         if depstr:
730             deps = depstr.split(" ")
731             for dep in deps:
732                 (f,old_mtime_s) = dep.split("@")
733                 old_mtime = int(old_mtime_s)
734                 new_mtime = parse.cached_mtime(f)
735                 if (new_mtime > old_mtime):
736                     return False
737         return True
738
739     def load_bbfile( self, bbfile ):
740         """Load and parse one .bb build file"""
741
742         if not self.cache in [None, '']:
743             # get the times
744             cache_mtime = data.init_db_mtime(self.cache, bbfile)
745             file_mtime = parse.cached_mtime(bbfile)
746
747             if file_mtime > cache_mtime:
748                 #print " : '%s' dirty. reparsing..." % bbfile
749                 pass
750             else:
751                 #print " : '%s' clean. loading from cache..." % bbfile
752                 cache_data = data.init_db( self.cache, bbfile, False )
753                 if self.deps_clean(cache_data):
754                     return cache_data, True
755
756         topdir = data.getVar('TOPDIR', self.configuration.data)
757         if not topdir:
758             topdir = os.path.abspath(os.getcwd())
759             # set topdir to here
760             data.setVar('TOPDIR', topdir, self.configuration)
761         bbfile = os.path.abspath(bbfile)
762         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
763         # expand tmpdir to include this topdir
764         data.setVar('TMPDIR', data.getVar('TMPDIR', self.configuration.data, 1) or "", self.configuration.data)
765         # set topdir to location of .bb file
766         topdir = bbfile_loc
767         #data.setVar('TOPDIR', topdir, cfg)
768         # go there
769         oldpath = os.path.abspath(os.getcwd())
770         os.chdir(topdir)
771         bb = data.init_db(self.cache,bbfile, True, self.configuration.data)
772         try:
773             parse.handle(bbfile, bb) # read .bb data
774             if not self.cache in [None, '']:
775                 bb.commit(parse.cached_mtime(bbfile)) # write cache
776             os.chdir(oldpath)
777             return bb, False
778         finally:
779             os.chdir(oldpath)
780
781     def collect_bbfiles( self, progressCallback ):
782         """Collect all available .bb build files"""
783         self.cb = progressCallback
784         parsed, cached, skipped, masked = 0, 0, 0, 0
785         self.cache   = bb.data.getVar( "CACHE", self.configuration.data, 1 )
786         self.pkgdata = data.pkgdata( not self.cache in [None, ''], self.cache )
787
788         if not self.cache in [None, '']:
789             if self.cb is not None:
790                 print "NOTE: Using cache in '%s'" % self.cache
791             try:
792                 os.stat( self.cache )
793             except OSError:
794                 bb.mkdirhier( self.cache )
795         else:
796             if self.cb is not None:
797                 print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
798         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
799         data.setVar("BBFILES", " ".join(files), self.configuration.data)
800
801         if not len(files):
802             files = self.get_bbfiles()
803
804         if not len(files):
805             bb.error("no files to build.")
806
807         newfiles = []
808         for f in files:
809             if os.path.isdir(f):
810                 dirfiles = self.find_bbfiles(f)
811                 if dirfiles:
812                     newfiles += dirfiles
813                     continue
814             newfiles += glob.glob(f) or [ f ]
815
816         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
817         try:
818             bbmask_compiled = re.compile(bbmask)
819         except sre_constants.error:
820             bb.fatal("BBMASK is not a valid regular expression.")
821
822         for i in xrange( len( newfiles ) ):
823             f = newfiles[i]
824             if bbmask and bbmask_compiled.search(f):
825                 bb.debug(1, "bbmake: skipping %s" % f)
826                 masked += 1
827                 continue
828             debug(1, "bbmake: parsing %s" % f)
829
830             # read a file's metadata
831             try:
832                 bb_data, fromCache = self.load_bbfile(f)
833                 if fromCache: cached += 1
834                 else: parsed += 1
835                 deps = None
836                 if bb_data is not None:
837                     # allow metadata files to add items to BBFILES
838                     #data.update_data(self.pkgdata[f])
839                     addbbfiles = data.getVar('BBFILES', bb_data) or None
840                     if addbbfiles:
841                         for aof in addbbfiles.split():
842                             if not files.count(aof):
843                                 if not os.path.isabs(aof):
844                                     aof = os.path.join(os.path.dirname(f),aof)
845                                 files.append(aof)
846                     for var in bb_data.keys():
847                         if data.getVarFlag(var, "handler", bb_data) and data.getVar(var, bb_data):
848                             event.register(data.getVar(var, bb_data))
849                     self.pkgdata[f] = bb_data
850
851                 # now inform the caller
852                 if self.cb is not None:
853                     self.cb( i + 1, len( newfiles ), f, bb_data, fromCache )
854
855             except IOError, e:
856                 bb.error("opening %s: %s" % (f, e))
857                 pass
858             except bb.parse.SkipPackage:
859                 skipped += 1
860                 pass
861             except KeyboardInterrupt:
862                 raise
863             except Exception, e:
864                 bb.error("%s while parsing %s" % (e, f))
865
866         if self.cb is not None:
867             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
868
869 #============================================================================#
870 # main
871 #============================================================================#
872
873 if __name__ == "__main__":
874
875     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
876     usage = """%prog [options] [package ...]
877
878 Executes the specified task (default is 'build') for a given set of BitBake files.
879 It expects that BBFILES is defined, which is a space seperated list of files to
880 be executed.  BBFILES does support wildcards.
881 Default BBFILES are the .bb files in the current directory.""" )
882
883     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
884                action = "store", dest = "buildfile", default = None )
885
886     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.",
887                action = "store_false", dest = "abort", default = True )
888
889     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
890                action = "store_true", dest = "force", default = False )
891
892     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
893                action = "store_true", dest = "interactive", default = False )
894
895     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)",
896                action = "store", dest = "cmd", default = "build" )
897
898     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
899                action = "append", dest = "file", default = [] )
900
901     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
902                action = "store_true", dest = "verbose", default = False )
903
904     parser.add_option( "-D", "--debug", help = "Increase the debug level",
905                action = "count", dest="debug", default = 0)
906
907     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
908                action = "store_true", dest = "dry_run", default = False )
909
910     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
911                action = "store_true", dest = "parse_only", default = False )
912
913     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
914                action = "store_true", dest = "disable_psyco", default = False )
915
916     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
917                action = "store_true", dest = "show_versions", default = False )
918
919     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
920                action = "store_true", dest = "show_environment", default = False )
921
922     options, args = parser.parse_args( sys.argv )
923
924     cooker = BBCooker()
925     cooker.cook( BBConfiguration( options ), args[1:] )