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