bitbake/lib/bb/fetch:
[bitbake.git] / bin / bitbake
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 #
5 # Copyright (C) 2003, 2004  Chris Larson
6 # Copyright (C) 2003, 2004  Phil Blundell
7 # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
8 # Copyright (C) 2005        Holger Hans Peter Freyther
9 # Copyright (C) 2005        ROAD GmbH
10 #
11 # This program is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 #
16 # This program is distributed in the hope that it will be useful, but WITHOUT
17 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along with
21 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22 # Place, Suite 330, Boston, MA 02111-1307 USA.
23
24 import sys, os, getopt, glob, copy, os.path, re, time
25 sys.path.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_dependencies = 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         localdata = data.createCopy(self.configuration.data)
285         bb.data.setVar('OVERRIDES', "%s:%s" % (pn, data.getVar('OVERRIDES', localdata)), localdata)
286         bb.data.update_data(localdata)
287
288         preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, 1)
289         if preferred_v:
290             m = re.match('(.*)_(.*)', preferred_v)
291             if m:
292                 preferred_v = m.group(1)
293                 preferred_r = m.group(2)
294             else:
295                 preferred_r = None
296
297             for file_set in tmp_pn:
298                 for f in file_set:
299                     pv,pr = self.status.pkg_pvpr[f]
300                     if preferred_v == pv and (preferred_r == pr or preferred_r == None):
301                         preferred_file = f
302                         preferred_ver = (pv, pr)
303                         break
304                 if preferred_file:
305                     break;
306             if preferred_r:
307                 pv_str = '%s-%s' % (preferred_v, preferred_r)
308             else:
309                 pv_str = preferred_v
310             if preferred_file is None:
311                 bb.note("preferred version %s of %s not available" % (pv_str, pn))
312             else:
313                 bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
314
315         del localdata
316
317         # get highest priority file set
318         files = tmp_pn[0]
319         latest = None
320         latest_p = 0
321         latest_f = None
322         for file_name in files:
323             pv,pr = self.status.pkg_pvpr[file_name]
324             dp = self.status.pkg_dp[file_name]
325
326             if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
327                 latest = (pv, pr)
328                 latest_f = file_name
329                 latest_p = dp
330         if preferred_file is None:
331             preferred_file = latest_f
332             preferred_ver = latest
333
334         return (latest,latest_f,preferred_ver, preferred_file)
335
336     def showVersions( self ):
337         pkg_pn = self.status.pkg_pn
338         preferred_versions = {}
339         latest_versions = {}
340
341         # Sort by priority
342         for pn in pkg_pn.keys():
343             (last_ver,last_file,pref_ver,pref_file) = self.findBestProvider(pn)
344             preferred_versions[pn] = (pref_ver, pref_file)
345             latest_versions[pn] = (last_ver, last_file)
346
347         pkg_list = pkg_pn.keys()
348         pkg_list.sort()
349
350         for p in pkg_list:
351             pref = preferred_versions[p]
352             latest = latest_versions[p]
353
354             if pref != latest:
355                 prefstr = pref[0][0] + "-" + pref[0][1]
356             else:
357                 prefstr = ""
358
359             print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
360                                         prefstr)
361
362     def showEnvironment( self ):
363         """Show the outer or per-package environment"""
364         if self.configuration.buildfile:
365             try:
366                 self.configuration.data, fromCache = self.load_bbfile( self.configuration.buildfile )
367             except IOError, e:
368                 fatal("Unable to read %s: %s" % ( self.configuration.buildfile, e ))
369             except Exception, e:
370                 fatal("%s" % e)
371         # emit variables and shell functions
372         try:
373             data.update_data( self.configuration.data )
374             data.emit_env(sys.__stdout__, self.configuration.data, True)
375         except Exception, e:
376             fatal("%s" % e)
377         # emit the metadata which isnt valid shell
378         for e in self.configuration.data.keys():
379             if data.getVarFlag( e, 'python', self.configuration.data ):
380                 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
381
382     def buildProvider( self, item ):
383         fn = None
384
385         discriminated = False
386
387         if item not in self.status.providers:
388             bb.error("Nothing provides %s" % item)
389             return 0
390
391         all_p = self.status.providers[item]
392
393         for p in all_p:
394             if p in self.build_cache:
395                 bb.debug(1, "already built %s in this run\n" % p)
396                 return 1
397
398         eligible = []
399         preferred_versions = {}
400
401         # Collate providers by PN
402         pkg_pn = {}
403         for p in all_p:
404             pn = self.status.pkg_fn[p]
405             if pn not in pkg_pn:
406                 pkg_pn[pn] = []
407             pkg_pn[pn].append(p)
408
409         bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
410
411         for pn in pkg_pn.keys():
412             preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
413             eligible.append(preferred_versions[pn][1])
414
415         for p in eligible:
416             if p in self.build_cache_fail:
417                 bb.debug(1, "rejecting already-failed %s" % p)
418                 eligible.remove(p)
419
420         if len(eligible) == 0:
421             bb.error("no eligible providers for %s" % item)
422             return 0
423
424         # look to see if one of them is already staged, or marked as preferred.
425         # if so, bump it to the head of the queue
426         for p in all_p:
427             the_data = self.pkgdata[p]
428             pn = bb.data.getVar('PN', the_data, 1)
429             pv = bb.data.getVar('PV', the_data, 1)
430             pr = bb.data.getVar('PR', the_data, 1)
431             tmpdir = bb.data.getVar('TMPDIR', the_data, 1)
432             stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
433             if os.path.exists(stamp):
434                 (newvers, fn) = preferred_versions[pn]
435                 if not fn in eligible:
436                     # package was made ineligible by already-failed check
437                     continue
438                 oldver = "%s-%s" % (pv, pr)
439                 newver = '-'.join(newvers)
440                 if (newver != oldver):
441                     extra_chat = "; upgrading from %s to %s" % (oldver, newver)
442                 else:
443                     extra_chat = ""
444                 if self.configuration.verbose:
445                     bb.note("selecting already-staged %s to satisfy %s%s" % (pn, item, extra_chat))
446                 eligible.remove(fn)
447                 eligible = [fn] + eligible
448                 discriminated = True
449                 break
450
451         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
452         if prefervar:
453             self.preferred[item] = prefervar
454
455         if item in self.preferred:
456             for p in eligible:
457                 pn = self.status.pkg_fn[p]
458                 if self.preferred[item] == pn:
459                     if self.configuration.verbose:
460                         bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
461                     eligible.remove(p)
462                     eligible = [p] + eligible
463                     discriminated = True
464                     break
465
466         if len(eligible) > 1 and discriminated == False:
467             if item not in self.consider_msgs_cache:
468                 providers_list = []
469                 for fn in eligible:
470                     providers_list.append(self.status.pkg_fn[fn])
471                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
472                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
473             self.consider_msgs_cache.append(item)
474
475
476         # run through the list until we find one that we can build
477         for fn in eligible:
478             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
479             if self.tryBuild(fn, item):
480                 return 1
481
482         bb.note("no buildable providers for %s" % item)
483         return 0
484
485     def buildDepgraph( self ):
486         all_depends = self.status.all_depends
487         pn_provides = self.status.pn_provides
488
489         def calc_bbfile_priority(filename):
490             for (regex, pri) in self.status.bbfile_config_priorities:
491                 if regex.match(filename):
492                     return pri
493             return 0
494
495         # Handle PREFERRED_PROVIDERS
496         for p in (bb.data.getVar('PREFERRED_PROVIDERS', self.configuration.data, 1) or "").split():
497             (providee, provider) = p.split(':')
498             if providee in self.preferred and self.preferred[providee] != provider:
499                 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
500             self.preferred[providee] = provider
501
502         # Calculate priorities for each file
503         for p in self.pkgdata.keys():
504             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
505
506         # Build package list for "bitbake world"
507         bb.debug(1, "collating packages for \"world\"")
508         for f in self.status.possible_world:
509             terminal = True
510             pn = self.status.pkg_fn[f]
511
512             for p in pn_provides[pn]:
513                 if p.startswith('virtual/'):
514                     bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
515                     terminal = False
516                     break
517                 for pf in self.status.providers[p]:
518                     if self.status.pkg_fn[pf] != pn:
519                         bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
520                         terminal = False
521                         break
522             if terminal:
523                 self.status.world_target.add(pn)
524
525             # drop reference count now
526             self.status.possible_world = None
527             self.status.all_depends    = None
528
529     def myProgressCallback( self, x, y, f, file_data, from_cache ):
530         # feed the status with new input
531         self.status.handle_bb_data(f, file_data, from_cache)
532
533         if bbdebug > 0:
534             return
535         if os.isatty(sys.stdout.fileno()):
536             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
537             sys.stdout.flush()
538         else:
539             if x == 1:
540                 sys.stdout.write("Parsing .bb files, please wait...")
541                 sys.stdout.flush()
542             if x == y:
543                 sys.stdout.write("done.")
544                 sys.stdout.flush()
545
546     def interactiveMode( self ):
547         """Drop off into a shell"""
548         try:
549             from bb import shell
550         except ImportError, details:
551             bb.fatal("Sorry, shell not available (%s)" % details )
552         else:
553             bb.data.update_data( self.configuration.data )
554             shell.start( self )
555             sys.exit( 0 )
556
557     def parseConfigurationFile( self, afile ):
558         try:
559             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
560         except IOError:
561             bb.fatal( "Unable to open %s" % afile )
562         except bb.parse.ParseError, details:
563             bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
564
565     def handleCollections( self, collections ):
566         """Handle collections"""
567         if collections:
568             collection_list = collections.split()
569             for c in collection_list:
570                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
571                 if regex == None:
572                     bb.error("BBFILE_PATTERN_%s not defined" % c)
573                     continue
574                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
575                 if priority == None:
576                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
577                     continue
578                 try:
579                     cre = re.compile(regex)
580                 except re.error:
581                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
582                     continue
583                 try:
584                     pri = int(priority)
585                     self.status.bbfile_config_priorities.append((cre, pri))
586                 except ValueError:
587                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
588
589
590     def cook( self, configuration, args ):
591         self.configuration = configuration
592
593         if not self.configuration.cmd:
594             self.configuration.cmd = "build"
595
596         if self.configuration.debug:
597             bb.debug_level = self.configuration.debug
598
599         self.configuration.data = bb.data.init()
600
601         for f in self.configuration.file:
602             self.parseConfigurationFile( f )
603
604         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
605
606         if self.configuration.show_environment:
607             self.showEnvironment()
608             sys.exit( 0 )
609
610         # inject custom variables
611         if not bb.data.getVar("BUILDNAME", self.configuration.data):
612             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
613         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
614
615         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
616
617         if self.configuration.interactive:
618             self.interactiveMode()
619
620         if self.configuration.buildfile is not None:
621             bf = os.path.abspath( self.configuration.buildfile )
622             try:
623                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
624             except IOError:
625                 bb.fatal("Unable to open %s" % bf)
626
627             item = bb.data.getVar('PN', bbfile_data, 1)
628             try:
629                 self.tryBuildPackage( bf, item, bbfile_data )
630             except bb.build.EventException:
631                 bb.error( "Build of '%s' failed" % item )
632
633             sys.exit( self.stats.show() )
634
635         # initialise the parsing status now we know we will need deps
636         self.status = BBParsingStatus()
637
638         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
639         self.status.ignored_dependencies = Set( ignore.split() )
640
641         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
642
643         pkgs_to_build = None
644         if args:
645             if not pkgs_to_build:
646                 pkgs_to_build = []
647             pkgs_to_build.extend(args)
648         if not pkgs_to_build:
649                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
650                 if bbpkgs:
651                         pkgs_to_build = bbpkgs.split()
652         if not pkgs_to_build and not self.configuration.show_versions \
653                              and not self.configuration.interactive \
654                              and not self.configuration.show_environment:
655                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
656                 print "for usage information."
657                 sys.exit(0)
658
659         # Import Psyco if available and not disabled
660         if not self.configuration.disable_psyco:
661             try:
662                 import psyco
663             except ImportError:
664                 if bbdebug == 0:
665                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
666             else:
667                 psyco.bind( self.collect_bbfiles )
668         else:
669             bb.note("You have disabled Psyco. This decreases performance.")
670
671         try:
672             bb.debug(1, "collecting .bb files")
673             self.collect_bbfiles( self.myProgressCallback )
674             bb.debug(1, "parsing complete")
675             if bbdebug == 0:
676                 print
677             if self.configuration.parse_only:
678                 print "Requested parsing .bb files only.  Exiting."
679                 return
680
681             bb.data.update_data( self.configuration.data )
682             self.buildDepgraph()
683
684             if self.configuration.show_versions:
685                 self.showVersions()
686                 sys.exit( 0 )
687             if 'world' in pkgs_to_build:
688                 pkgs_to_build.remove('world')
689                 for t in self.status.world_target:
690                     pkgs_to_build.append(t)
691
692             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.data))
693
694             for k in pkgs_to_build:
695                 failed = False
696                 try:
697                     if self.buildProvider( k ) == 0:
698                         # already diagnosed
699                         failed = True
700                 except bb.build.EventException:
701                     bb.error("Build of " + k + " failed")
702                     failed = True
703
704                 if failed:
705                     if self.configuration.abort:
706                         sys.exit(1)
707
708             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data))
709
710             sys.exit( self.stats.show() )
711
712         except KeyboardInterrupt:
713             print "\nNOTE: KeyboardInterrupt - Build not completed."
714             sys.exit(1)
715
716     def get_bbfiles( self, path = os.getcwd() ):
717         """Get list of default .bb files by reading out the current directory"""
718         contents = os.listdir(path)
719         bbfiles = []
720         for f in contents:
721             (root, ext) = os.path.splitext(f)
722             if ext == ".bb":
723                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
724         return bbfiles
725
726     def find_bbfiles( self, path ):
727         """Find all the .bb files in a directory (uses find)"""
728         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
729         try:
730             finddata = os.popen(findcmd)
731         except OSError:
732             return []
733         return finddata.readlines()
734
735     def deps_clean(self, d):
736         depstr = data.getVar('__depends', d)
737         if depstr:
738             deps = depstr.split(" ")
739             for dep in deps:
740                 (f,old_mtime_s) = dep.split("@")
741                 old_mtime = int(old_mtime_s)
742                 new_mtime = parse.cached_mtime(f)
743                 if (new_mtime > old_mtime):
744                     return False
745         return True
746
747     def load_bbfile( self, bbfile ):
748         """Load and parse one .bb build file"""
749
750         if not self.cache in [None, '']:
751             # get the times
752             cache_mtime = data.init_db_mtime(self.cache, bbfile)
753             file_mtime = parse.cached_mtime(bbfile)
754
755             if file_mtime > cache_mtime:
756                 #print " : '%s' dirty. reparsing..." % bbfile
757                 pass
758             else:
759                 #print " : '%s' clean. loading from cache..." % bbfile
760                 cache_data = data.init_db( self.cache, bbfile, False )
761                 if self.deps_clean(cache_data):
762                     return cache_data, True
763
764         topdir = data.getVar('TOPDIR', self.configuration.data)
765         if not topdir:
766             topdir = os.path.abspath(os.getcwd())
767             # set topdir to here
768             data.setVar('TOPDIR', topdir, self.configuration)
769         bbfile = os.path.abspath(bbfile)
770         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
771         # expand tmpdir to include this topdir
772         data.setVar('TMPDIR', data.getVar('TMPDIR', self.configuration.data, 1) or "", self.configuration.data)
773         # set topdir to location of .bb file
774         topdir = bbfile_loc
775         #data.setVar('TOPDIR', topdir, cfg)
776         # go there
777         oldpath = os.path.abspath(os.getcwd())
778         os.chdir(topdir)
779         bb = data.init_db(self.cache,bbfile, True, self.configuration.data)
780         try:
781             parse.handle(bbfile, bb) # read .bb data
782             if not self.cache in [None, '']:
783                 bb.commit(parse.cached_mtime(bbfile)) # write cache
784             os.chdir(oldpath)
785             return bb, False
786         finally:
787             os.chdir(oldpath)
788
789     def collect_bbfiles( self, progressCallback ):
790         """Collect all available .bb build files"""
791         self.cb = progressCallback
792         parsed, cached, skipped, masked = 0, 0, 0, 0
793         self.cache   = bb.data.getVar( "CACHE", self.configuration.data, 1 )
794         self.pkgdata = data.pkgdata( not self.cache in [None, ''], self.cache, self.configuration.data )
795
796         if not self.cache in [None, '']:
797             if self.cb is not None:
798                 print "NOTE: Using cache in '%s'" % self.cache
799             try:
800                 os.stat( self.cache )
801             except OSError:
802                 bb.mkdirhier( self.cache )
803         else:
804             if self.cb is not None:
805                 print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
806         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
807         data.setVar("BBFILES", " ".join(files), self.configuration.data)
808
809         if not len(files):
810             files = self.get_bbfiles()
811
812         if not len(files):
813             bb.error("no files to build.")
814
815         newfiles = []
816         for f in files:
817             if os.path.isdir(f):
818                 dirfiles = self.find_bbfiles(f)
819                 if dirfiles:
820                     newfiles += dirfiles
821                     continue
822             newfiles += glob.glob(f) or [ f ]
823
824         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
825         try:
826             bbmask_compiled = re.compile(bbmask)
827         except sre_constants.error:
828             bb.fatal("BBMASK is not a valid regular expression.")
829
830         for i in xrange( len( newfiles ) ):
831             f = newfiles[i]
832             if bbmask and bbmask_compiled.search(f):
833                 bb.debug(1, "bbmake: skipping %s" % f)
834                 masked += 1
835                 continue
836             debug(1, "bbmake: parsing %s" % f)
837
838             # read a file's metadata
839             try:
840                 bb_data, fromCache = self.load_bbfile(f)
841                 if fromCache: cached += 1
842                 else: parsed += 1
843                 deps = None
844                 if bb_data is not None:
845                     # allow metadata files to add items to BBFILES
846                     #data.update_data(self.pkgdata[f])
847                     addbbfiles = data.getVar('BBFILES', bb_data) or None
848                     if addbbfiles:
849                         for aof in addbbfiles.split():
850                             if not files.count(aof):
851                                 if not os.path.isabs(aof):
852                                     aof = os.path.join(os.path.dirname(f),aof)
853                                 files.append(aof)
854                     for var in bb_data.keys():
855                         if data.getVarFlag(var, "handler", bb_data) and data.getVar(var, bb_data):
856                             event.register(data.getVar(var, bb_data))
857                     self.pkgdata[f] = bb_data
858
859                 # now inform the caller
860                 if self.cb is not None:
861                     self.cb( i + 1, len( newfiles ), f, bb_data, fromCache )
862
863             except IOError, e:
864                 bb.error("opening %s: %s" % (f, e))
865                 pass
866             except bb.parse.SkipPackage:
867                 skipped += 1
868                 pass
869             except KeyboardInterrupt:
870                 raise
871             except Exception, e:
872                 bb.error("%s while parsing %s" % (e, f))
873
874         if self.cb is not None:
875             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
876
877 #============================================================================#
878 # main
879 #============================================================================#
880
881 if __name__ == "__main__":
882
883     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
884     usage = """%prog [options] [package ...]
885
886 Executes the specified task (default is 'build') for a given set of BitBake files.
887 It expects that BBFILES is defined, which is a space seperated list of files to
888 be executed.  BBFILES does support wildcards.
889 Default BBFILES are the .bb files in the current directory.""" )
890
891     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
892                action = "store", dest = "buildfile", default = None )
893
894     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.",
895                action = "store_false", dest = "abort", default = True )
896
897     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
898                action = "store_true", dest = "force", default = False )
899
900     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
901                action = "store_true", dest = "interactive", default = False )
902
903     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)",
904                action = "store", dest = "cmd", default = "build" )
905
906     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
907                action = "append", dest = "file", default = [] )
908
909     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
910                action = "store_true", dest = "verbose", default = False )
911
912     parser.add_option( "-D", "--debug", help = "Increase the debug level",
913                action = "count", dest="debug", default = 0)
914
915     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
916                action = "store_true", dest = "dry_run", default = False )
917
918     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
919                action = "store_true", dest = "parse_only", default = False )
920
921     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
922                action = "store_true", dest = "disable_psyco", default = False )
923
924     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
925                action = "store_true", dest = "show_versions", default = False )
926
927     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
928                action = "store_true", dest = "show_environment", default = False )
929
930     options, args = parser.parse_args( sys.argv )
931
932     cooker = BBCooker()
933     cooker.cook( BBConfiguration( options ), args[1:] )