bitbake/bin/bitdoc:
[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_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         # inject custom variables
605         if not bb.data.getVar("BUILDNAME", self.configuration.data):
606             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
607         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.localtime()),self.configuration.data)
608
609         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
610
611         if self.configuration.interactive:
612             self.interactiveMode()
613
614         if self.configuration.buildfile is not None:
615             bf = os.path.abspath( self.configuration.buildfile )
616             try:
617                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
618             except IOError:
619                 bb.fatal("Unable to open %s" % bf)
620
621             item = bb.data.getVar('PN', bbfile_data, 1)
622             try:
623                 self.tryBuildPackage( bf, item, bbfile_data )
624             except bb.build.EventException:
625                 bb.error( "Build of '%s' failed" % item )
626
627             sys.exit( self.stats.show() )
628
629         # initialise the parsing status now we know we will need deps
630         self.status = BBParsingStatus()
631
632         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
633         self.status.ignored_dependencies = Set( ignore.split() )
634
635         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
636
637         pkgs_to_build = None
638         if args:
639             if not pkgs_to_build:
640                 pkgs_to_build = []
641             pkgs_to_build.extend(args)
642         if not pkgs_to_build:
643                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
644                 if bbpkgs:
645                         pkgs_to_build = bbpkgs.split()
646         if not pkgs_to_build and not self.configuration.show_versions \
647                              and not self.configuration.interactive \
648                              and not self.configuration.show_environment:
649                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
650                 print "for usage information."
651                 sys.exit(0)
652
653         # Import Psyco if available and not disabled
654         if not self.configuration.disable_psyco:
655             try:
656                 import psyco
657             except ImportError:
658                 if bbdebug == 0:
659                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
660             else:
661                 psyco.bind( self.collect_bbfiles )
662         else:
663             bb.note("You have disabled Psyco. This decreases performance.")
664
665         try:
666             bb.debug(1, "collecting .bb files")
667             self.collect_bbfiles( self.myProgressCallback )
668             bb.debug(1, "parsing complete")
669             if bbdebug == 0:
670                 print
671             if self.configuration.parse_only:
672                 print "Requested parsing .bb files only.  Exiting."
673                 return
674
675             bb.data.update_data( self.configuration.data )
676             self.buildDepgraph()
677
678             if self.configuration.show_versions:
679                 self.showVersions()
680                 sys.exit( 0 )
681             if 'world' in pkgs_to_build:
682                 pkgs_to_build.remove('world')
683                 for t in self.status.world_target:
684                     pkgs_to_build.append(t)
685
686             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.data))
687
688             for k in pkgs_to_build:
689                 failed = False
690                 try:
691                     if self.buildProvider( k ) == 0:
692                         # already diagnosed
693                         failed = True
694                 except bb.build.EventException:
695                     bb.error("Build of " + k + " failed")
696                     failed = True
697
698                 if failed:
699                     if self.configuration.abort:
700                         sys.exit(1)
701
702             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data))
703
704             sys.exit( self.stats.show() )
705
706         except KeyboardInterrupt:
707             print "\nNOTE: KeyboardInterrupt - Build not completed."
708             sys.exit(1)
709
710     def get_bbfiles( self, path = os.getcwd() ):
711         """Get list of default .bb files by reading out the current directory"""
712         contents = os.listdir(path)
713         bbfiles = []
714         for f in contents:
715             (root, ext) = os.path.splitext(f)
716             if ext == ".bb":
717                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
718         return bbfiles
719
720     def find_bbfiles( self, path ):
721         """Find all the .bb files in a directory (uses find)"""
722         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
723         try:
724             finddata = os.popen(findcmd)
725         except OSError:
726             return []
727         return finddata.readlines()
728
729     def deps_clean(self, d):
730         depstr = data.getVar('__depends', d)
731         if depstr:
732             deps = depstr.split(" ")
733             for dep in deps:
734                 (f,old_mtime_s) = dep.split("@")
735                 old_mtime = int(old_mtime_s)
736                 new_mtime = parse.cached_mtime(f)
737                 if (new_mtime > old_mtime):
738                     return False
739         return True
740
741     def load_bbfile( self, bbfile ):
742         """Load and parse one .bb build file"""
743
744         if not self.cache in [None, '']:
745             # get the times
746             cache_mtime = data.init_db_mtime(self.cache, bbfile)
747             file_mtime = parse.cached_mtime(bbfile)
748
749             if file_mtime > cache_mtime:
750                 #print " : '%s' dirty. reparsing..." % bbfile
751                 pass
752             else:
753                 #print " : '%s' clean. loading from cache..." % bbfile
754                 cache_data = data.init_db( self.cache, bbfile, False )
755                 if self.deps_clean(cache_data):
756                     return cache_data, True
757
758         topdir = data.getVar('TOPDIR', self.configuration.data)
759         if not topdir:
760             topdir = os.path.abspath(os.getcwd())
761             # set topdir to here
762             data.setVar('TOPDIR', topdir, self.configuration)
763         bbfile = os.path.abspath(bbfile)
764         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
765         # expand tmpdir to include this topdir
766         data.setVar('TMPDIR', data.getVar('TMPDIR', self.configuration.data, 1) or "", self.configuration.data)
767         # set topdir to location of .bb file
768         topdir = bbfile_loc
769         #data.setVar('TOPDIR', topdir, cfg)
770         # go there
771         oldpath = os.path.abspath(os.getcwd())
772         os.chdir(topdir)
773         bb = data.init_db(self.cache,bbfile, True, self.configuration.data)
774         try:
775             parse.handle(bbfile, bb) # read .bb data
776             if not self.cache in [None, '']:
777                 bb.commit(parse.cached_mtime(bbfile)) # write cache
778             os.chdir(oldpath)
779             return bb, False
780         finally:
781             os.chdir(oldpath)
782
783     def collect_bbfiles( self, progressCallback ):
784         """Collect all available .bb build files"""
785         self.cb = progressCallback
786         parsed, cached, skipped, masked = 0, 0, 0, 0
787         self.cache   = bb.data.getVar( "CACHE", self.configuration.data, 1 )
788         self.pkgdata = data.pkgdata( not self.cache in [None, ''], self.cache )
789
790         if not self.cache in [None, '']:
791             if self.cb is not None:
792                 print "NOTE: Using cache in '%s'" % self.cache
793             try:
794                 os.stat( self.cache )
795             except OSError:
796                 bb.mkdirhier( self.cache )
797         else:
798             if self.cb is not None:
799                 print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
800         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
801         data.setVar("BBFILES", " ".join(files), self.configuration.data)
802
803         if not len(files):
804             files = self.get_bbfiles()
805
806         if not len(files):
807             bb.error("no files to build.")
808
809         newfiles = []
810         for f in files:
811             if os.path.isdir(f):
812                 dirfiles = self.find_bbfiles(f)
813                 if dirfiles:
814                     newfiles += dirfiles
815                     continue
816             newfiles += glob.glob(f) or [ f ]
817
818         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
819         try:
820             bbmask_compiled = re.compile(bbmask)
821         except sre_constants.error:
822             bb.fatal("BBMASK is not a valid regular expression.")
823
824         for i in xrange( len( newfiles ) ):
825             f = newfiles[i]
826             if bbmask and bbmask_compiled.search(f):
827                 bb.debug(1, "bbmake: skipping %s" % f)
828                 masked += 1
829                 continue
830             debug(1, "bbmake: parsing %s" % f)
831
832             # read a file's metadata
833             try:
834                 bb_data, fromCache = self.load_bbfile(f)
835                 if fromCache: cached += 1
836                 else: parsed += 1
837                 deps = None
838                 if bb_data is not None:
839                     # allow metadata files to add items to BBFILES
840                     #data.update_data(self.pkgdata[f])
841                     addbbfiles = data.getVar('BBFILES', bb_data) or None
842                     if addbbfiles:
843                         for aof in addbbfiles.split():
844                             if not files.count(aof):
845                                 if not os.path.isabs(aof):
846                                     aof = os.path.join(os.path.dirname(f),aof)
847                                 files.append(aof)
848                     for var in bb_data.keys():
849                         if data.getVarFlag(var, "handler", bb_data) and data.getVar(var, bb_data):
850                             event.register(data.getVar(var, bb_data))
851                     self.pkgdata[f] = bb_data
852
853                 # now inform the caller
854                 if self.cb is not None:
855                     self.cb( i + 1, len( newfiles ), f, bb_data, fromCache )
856
857             except IOError, e:
858                 bb.error("opening %s: %s" % (f, e))
859                 pass
860             except bb.parse.SkipPackage:
861                 skipped += 1
862                 pass
863             except KeyboardInterrupt:
864                 raise
865             except Exception, e:
866                 bb.error("%s while parsing %s" % (e, f))
867
868         if self.cb is not None:
869             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
870
871 #============================================================================#
872 # main
873 #============================================================================#
874
875 if __name__ == "__main__":
876
877     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
878     usage = """%prog [options] [package ...]
879
880 Executes the specified task (default is 'build') for a given set of BitBake files.
881 It expects that BBFILES is defined, which is a space seperated list of files to
882 be executed.  BBFILES does support wildcards.
883 Default BBFILES are the .bb files in the current directory.""" )
884
885     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
886                action = "store", dest = "buildfile", default = None )
887
888     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.",
889                action = "store_false", dest = "abort", default = True )
890
891     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
892                action = "store_true", dest = "force", default = False )
893
894     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
895                action = "store_true", dest = "interactive", default = False )
896
897     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)",
898                action = "store", dest = "cmd", default = "build" )
899
900     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
901                action = "append", dest = "file", default = [] )
902
903     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
904                action = "store_true", dest = "verbose", default = False )
905
906     parser.add_option( "-D", "--debug", help = "Increase the debug level",
907                action = "count", dest="debug", default = 0)
908
909     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
910                action = "store_true", dest = "dry_run", default = False )
911
912     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
913                action = "store_true", dest = "parse_only", default = False )
914
915     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
916                action = "store_true", dest = "disable_psyco", default = False )
917
918     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
919                action = "store_true", dest = "show_versions", default = False )
920
921     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
922                action = "store_true", dest = "show_environment", default = False )
923
924     options, args = parser.parse_args( sys.argv )
925
926     cooker = BBCooker()
927     cooker.cook( BBConfiguration( options ), args[1:] )