bump 1.7 versions
[vuplus_bitbake] / 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 # Copyright (C) 2006        Richard Purdie
11 #
12 # This program is free software; you can redistribute it and/or modify it under
13 # the terms of the GNU General Public License as published by the Free Software
14 # Foundation; either version 2 of the License, or (at your option) any later
15 # version.
16 #
17 # This program is distributed in the hope that it will be useful, but WITHOUT
18 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License along with
22 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 # Place, Suite 330, Boston, MA 02111-1307 USA.
24
25 import sys, os, getopt, glob, copy, os.path, re, time
26 sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
27 import bb
28 from bb import utils, data, parse, event, cache, providers, taskdata, runqueue
29 from sets import Set
30 import itertools, optparse
31
32 parsespin = itertools.cycle( r'|/-\\' )
33
34 __version__ = "1.7.1"
35
36 #============================================================================#
37 # BBStatistics
38 #============================================================================#
39 class BBStatistics:
40     """
41     Manage build statistics for one run
42     """
43     def __init__(self ):
44         self.attempt = 0
45         self.success = 0
46         self.fail = 0
47         self.deps = 0
48
49     def show( self ):
50         print "Build statistics:"
51         print "  Attempted builds: %d" % self.attempt
52         if self.fail:
53             print "  Failed builds: %d" % self.fail
54         if self.deps:
55             print "  Dependencies not satisfied: %d" % self.deps
56         if self.fail or self.deps: return 1
57         else: return 0
58
59
60 #============================================================================#
61 # BBOptions
62 #============================================================================#
63 class BBConfiguration( object ):
64     """
65     Manages build options and configurations for one run
66     """
67     def __init__( self, options ):
68         for key, val in options.__dict__.items():
69             setattr( self, key, val )
70
71 #============================================================================#
72 # BBCooker
73 #============================================================================#
74 class BBCooker:
75     """
76     Manages one bitbake build run
77     """
78
79     Statistics = BBStatistics           # make it visible from the shell
80
81     def __init__( self ):
82         self.build_cache_fail = []
83         self.build_cache = []
84         self.stats = BBStatistics()
85         self.status = None
86
87         self.cache = None
88         self.bb_cache = None
89
90     def tryBuildPackage(self, fn, item, task, the_data, build_depends):
91         """
92         Build one task of a package, optionally build following task depends
93         """
94         bb.event.fire(bb.event.PkgStarted(item, the_data))
95         try:
96             self.stats.attempt += 1
97             if self.configuration.force:
98                 bb.data.setVarFlag('do_%s' % task, 'force', 1, the_data)
99             if not build_depends:
100                 bb.data.setVarFlag('do_%s' % task, 'dontrundeps', 1, the_data)
101             if not self.configuration.dry_run:
102                 bb.build.exec_task('do_%s' % task, the_data)
103             bb.event.fire(bb.event.PkgSucceeded(item, the_data))
104             self.build_cache.append(fn)
105             return True
106         except bb.build.FuncFailed:
107             self.stats.fail += 1
108             bb.msg.error(bb.msg.domain.Build, "task stack execution failed")
109             bb.event.fire(bb.event.PkgFailed(item, the_data))
110             self.build_cache_fail.append(fn)
111             raise
112         except bb.build.EventException, e:
113             self.stats.fail += 1
114             event = e.args[1]
115             bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event))
116             bb.event.fire(bb.event.PkgFailed(item, the_data))
117             self.build_cache_fail.append(fn)
118             raise
119
120     def tryBuild( self, fn, build_depends):
121         """
122         Build a provider and its dependencies. 
123         build_depends is a list of previous build dependencies (not runtime)
124         If build_depends is empty, we're dealing with a runtime depends
125         """
126
127         the_data = self.bb_cache.loadDataFull(fn, self.configuration.data)
128
129         item = self.status.pkg_fn[fn]
130
131         if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data) and not self.configuration.force:
132             self.build_cache.append(fn)
133             return True
134
135         return self.tryBuildPackage(fn, item, self.configuration.cmd, the_data, build_depends)
136
137     def showVersions( self ):
138         pkg_pn = self.status.pkg_pn
139         preferred_versions = {}
140         latest_versions = {}
141
142         # Sort by priority
143         for pn in pkg_pn.keys():
144             (last_ver,last_file,pref_ver,pref_file) = bb.providers.findBestProvider(pn, self.configuration.data, self.status)
145             preferred_versions[pn] = (pref_ver, pref_file)
146             latest_versions[pn] = (last_ver, last_file)
147
148         pkg_list = pkg_pn.keys()
149         pkg_list.sort()
150
151         for p in pkg_list:
152             pref = preferred_versions[p]
153             latest = latest_versions[p]
154
155             if pref != latest:
156                 prefstr = pref[0][0] + "-" + pref[0][1]
157             else:
158                 prefstr = ""
159
160             print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
161                                         prefstr)
162
163
164     def showEnvironment( self ):
165         """Show the outer or per-package environment"""
166         if self.configuration.buildfile:
167             self.cb = None
168             self.bb_cache = bb.cache.init(self)
169             try:
170                 self.configuration.data = self.bb_cache.loadDataFull(self.configuration.buildfile, self.configuration.data)
171             except IOError, e:
172                 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to read %s: %s" % ( self.configuration.buildfile, e ))
173             except Exception, e:
174                 bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e)
175         # emit variables and shell functions
176         try:
177             data.update_data( self.configuration.data )
178             data.emit_env(sys.__stdout__, self.configuration.data, True)
179         except Exception, e:
180             bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e)
181         # emit the metadata which isnt valid shell
182         for e in self.configuration.data.keys():
183             if data.getVarFlag( e, 'python', self.configuration.data ):
184                 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
185
186     def generateDotGraph( self, pkgs_to_build, ignore_deps ):
187         """
188         Generate two graphs one for the DEPENDS and RDEPENDS. The current
189         implementation creates crappy graphs ;)
190
191         pkgs_to_build A list of packages that needs to be built
192         ignore_deps   A list of names where processing of dependencies
193                       should be stopped. e.g. dependencies that get
194         """
195
196         def myFilterProvider( providers, item):
197             """
198             Take a list of providers and filter according to environment
199             variables. In contrast to filterProviders we do not discriminate
200             and take PREFERRED_PROVIDER into account.
201             """
202             eligible = []
203             preferred_versions = {}
204
205             # Collate providers by PN
206             pkg_pn = {}
207             for p in providers:
208                 pn = self.status.pkg_fn[p]
209                 if pn not in pkg_pn:
210                     pkg_pn[pn] = []
211                 pkg_pn[pn].append(p)
212
213             bb.msg.debug(1, bb.msg.domain.Provider, "providers for %s are: %s" % (item, pkg_pn.keys()))
214
215             for pn in pkg_pn.keys():
216                 preferred_versions[pn] = bb.providers.findBestProvider(pn, self.configuration.data, self.status, pkg_pn)[2:4]
217                 eligible.append(preferred_versions[pn][1])
218
219             for p in eligible:
220                 if p in self.build_cache_fail:
221                     bb.msg.debug(1, bb.msg.domain.Provider, "rejecting already-failed %s" % p)
222                     eligible.remove(p)
223
224             if len(eligible) == 0:
225                 bb.msg.error(bb.msg.domain.Provider, "no eligible providers for %s" % item)
226                 return 0
227
228             prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
229
230             # try the preferred provider first
231             if prefervar:
232                 for p in eligible:
233                     if prefervar == self.status.pkg_fn[p]:
234                         bb.msg.note(1, bb.msg.domain.Provider, "Selecting PREFERRED_PROVIDER %s" % prefervar)
235                         eligible.remove(p)
236                         eligible = [p] + eligible
237
238             return eligible
239
240
241         # try to avoid adding the same rdepends over an over again
242         seen_depends  = []
243         seen_rdepends = []
244
245
246         def add_depends(package_list):
247             """
248             Add all depends of all packages from this list
249             """
250             for package in package_list:
251                 if package in seen_depends or package in ignore_deps:
252                     continue
253
254                 seen_depends.append( package )
255                 if not package in self.status.providers:
256                     """
257                     We have not seen this name -> error in
258                     dependency handling
259                     """
260                     bb.msg.note(1, bb.msg.domain.Depends, "ERROR with provider: %(package)s" % vars() )
261                     print >> depends_file, '"%(package)s" -> ERROR' % vars()
262                     continue
263
264                 # get all providers for this package
265                 providers = self.status.providers[package]
266
267                 # now let us find the bestProvider for it
268                 fn = myFilterProvider(providers, package)[0]
269
270                 depends  = bb.utils.explode_deps(self.bb_cache.getVar('DEPENDS', fn, True) or "")
271                 version  = self.bb_cache.getVar('PV', fn, True ) + '-' + self.bb_cache.getVar('PR', fn, True)
272                 add_depends ( depends )
273
274                 # now create the node
275                 print >> depends_file, '"%(package)s" [label="%(package)s\\n%(version)s"]' % vars()
276
277                 depends = filter( (lambda x: x not in ignore_deps), depends )
278                 for depend in depends:
279                     print >> depends_file, '"%(package)s" -> "%(depend)s"' % vars()
280
281
282         def add_all_depends( the_depends, the_rdepends ):
283             """
284             Add both DEPENDS and RDEPENDS. RDEPENDS will get dashed
285             lines
286             """
287             package_list = the_depends + the_rdepends
288             for package in package_list:
289                 if package in seen_rdepends or package in ignore_deps:
290                     continue
291
292                 seen_rdepends.append( package )
293
294                 # Let us find out if the package is a DEPENDS or RDEPENDS
295                 # and we will set 'providers' with the avilable providers
296                 # for the package.
297                 if package in the_depends:
298                     if not package in self.status.providers:
299                         bb.msg.note(1, bb.msg.domain.Depends, "ERROR with provider: %(package)s" % vars() )
300                         print >> alldepends_file, '"%(package)s" -> ERROR' % vars()
301                         continue
302
303                     providers = self.status.providers[package]
304                 elif package in the_rdepends:
305                     if len(bb.providers.getRuntimeProviders(self.status, package)) == 0:
306                         bb.msg.note(1, bb.msg.domain.Depends, "ERROR with rprovider: %(package)s" % vars() )
307                         print >> alldepends_file, '"%(package)s" -> ERROR [style="dashed"]' % vars()
308                         continue
309
310                     providers = bb.providers.getRuntimeProviders(self.status, package)
311                 else:
312                     # something went wrong...
313                     print "Complete ERROR! %s" % package
314                     continue
315
316                 # now let us find the bestProvider for it
317                 fn = myFilterProvider(providers, package)[0]
318
319                 # Now we have a filename let us get the depends and RDEPENDS of it
320                 depends  = bb.utils.explode_deps(self.bb_cache.getVar('DEPENDS', fn, True) or "")
321                 if fn in self.status.rundeps and package in self.status.rundeps[fn]:
322                     rdepends= self.status.rundeps[fn][package].keys()
323                 else:
324                     rdepends = []
325                 version  = self.bb_cache.getVar('PV', fn, True ) + '-' + self.bb_cache.getVar('PR', fn, True)
326
327                 # handle all the depends and rdepends of package
328                 add_all_depends ( depends, rdepends )
329
330                 # now create the node using package name
331                 print >> alldepends_file, '"%(package)s" [label="%(package)s\\n%(version)s"]' % vars()
332
333                 # remove the stuff we want to ignore and add the edges
334                 depends = filter( (lambda x: x not in ignore_deps), depends )
335                 rdepends = filter( (lambda x: x not in ignore_deps), rdepends )
336                 for depend in depends:
337                     print >> alldepends_file, '"%(package)s" -> "%(depend)s"' % vars()
338                 for depend in rdepends:
339                     print >> alldepends_file, '"%(package)s" -> "%(depend)s" [style=dashed]' % vars()
340
341
342         # Add depends now
343         depends_file = file('depends.dot', 'w' )
344         print >> depends_file, "digraph depends {"
345         add_depends( pkgs_to_build )
346         print >> depends_file,  "}"
347
348         # Add all depends now
349         alldepends_file = file('alldepends.dot', 'w' )
350         print >> alldepends_file, "digraph alldepends {"
351         add_all_depends( pkgs_to_build, [] )
352         print >> alldepends_file, "}"
353
354     def buildDepgraph( self ):
355         all_depends = self.status.all_depends
356         pn_provides = self.status.pn_provides
357
358         localdata = data.createCopy(self.configuration.data)
359         bb.data.update_data(localdata)
360
361         def calc_bbfile_priority(filename):
362             for (regex, pri) in self.status.bbfile_config_priorities:
363                 if regex.match(filename):
364                     return pri
365             return 0
366
367         # Handle PREFERRED_PROVIDERS
368         for p in (bb.data.getVar('PREFERRED_PROVIDERS', localdata, 1) or "").split():
369             (providee, provider) = p.split(':')
370             if providee in self.status.preferred and self.status.preferred[providee] != provider:
371                 bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee]))
372             self.status.preferred[providee] = provider
373
374         # Calculate priorities for each file
375         for p in self.status.pkg_fn.keys():
376             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
377
378     def buildWorldTargetList(self):
379         """
380          Build package list for "bitbake world"
381         """
382         all_depends = self.status.all_depends
383         pn_provides = self.status.pn_provides
384         bb.msg.debug(1, bb.msg.domain.Parsing, "collating packages for \"world\"")
385         for f in self.status.possible_world:
386             terminal = True
387             pn = self.status.pkg_fn[f]
388
389             for p in pn_provides[pn]:
390                 if p.startswith('virtual/'):
391                     bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to %s provider starting with virtual/" % (f, p))
392                     terminal = False
393                     break
394                 for pf in self.status.providers[p]:
395                     if self.status.pkg_fn[pf] != pn:
396                         bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to both us and %s providing %s" % (f, pf, p))
397                         terminal = False
398                         break
399             if terminal:
400                 self.status.world_target.add(pn)
401
402             # drop reference count now
403             self.status.possible_world = None
404             self.status.all_depends    = None
405
406     def myProgressCallback( self, x, y, f, from_cache ):
407         """Update any tty with the progress change"""
408         if os.isatty(sys.stdout.fileno()):
409             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
410             sys.stdout.flush()
411         else:
412             if x == 1:
413                 sys.stdout.write("Parsing .bb files, please wait...")
414                 sys.stdout.flush()
415             if x == y:
416                 sys.stdout.write("done.")
417                 sys.stdout.flush()
418
419     def interactiveMode( self ):
420         """Drop off into a shell"""
421         try:
422             from bb import shell
423         except ImportError, details:
424             bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details )
425         else:
426             bb.data.update_data( self.configuration.data )
427             shell.start( self )
428             sys.exit( 0 )
429
430     def parseConfigurationFile( self, afile ):
431         try:
432             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
433
434             # Add the handlers we inherited by INHERIT
435             # we need to do this manually as it is not guranteed
436             # we will pick up these classes... as we only INHERIT
437             # on .inc and .bb files but not on .conf
438             data = bb.data.createCopy( self.configuration.data )
439             inherits  = ["base"] + (bb.data.getVar('INHERIT', data, True ) or "").split()
440             for inherit in inherits:
441                 data = bb.parse.handle( os.path.join('classes', '%s.bbclass' % inherit ), data, True )
442
443             # FIXME: This assumes that we included at least one .inc file
444             for var in bb.data.keys(data):
445                 if bb.data.getVarFlag(var, 'handler', data):
446                     bb.event.register(var,bb.data.getVar(var, data))
447
448         except IOError:
449             bb.msg.fatal(bb.msg.domain.Parsing, "Unable to open %s" % afile )
450         except bb.parse.ParseError, details:
451             bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (afile, details) )
452
453     def handleCollections( self, collections ):
454         """Handle collections"""
455         if collections:
456             collection_list = collections.split()
457             for c in collection_list:
458                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
459                 if regex == None:
460                     bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s not defined" % c)
461                     continue
462                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
463                 if priority == None:
464                     bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PRIORITY_%s not defined" % c)
465                     continue
466                 try:
467                     cre = re.compile(regex)
468                 except re.error:
469                     bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
470                     continue
471                 try:
472                     pri = int(priority)
473                     self.status.bbfile_config_priorities.append((cre, pri))
474                 except ValueError:
475                     bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
476
477
478     def cook( self, configuration, args ):
479         """
480         We are building stuff here. We do the building
481         from here. By default we try to execute task
482         build.
483         """
484
485         self.configuration = configuration
486
487         if self.configuration.verbose:
488             bb.msg.set_verbose(True)
489
490         if self.configuration.debug:
491             bb.msg.set_debug_level(self.configuration.debug)
492         else:
493             bb.msg.set_debug_level(0)
494
495         if self.configuration.debug_domains:
496             bb.msg.set_debug_domains(self.configuration.debug_domains)
497
498         self.configuration.data = bb.data.init()
499
500         for f in self.configuration.file:
501             self.parseConfigurationFile( f )
502
503         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
504
505         if not self.configuration.cmd:
506             self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data)
507
508         # For backwards compatibility - REMOVE ME
509         if not self.configuration.cmd:
510             self.configuration.cmd = "build"
511
512         #
513         # Special updated configuration we use for firing events
514         #
515         self.configuration.event_data = bb.data.createCopy(self.configuration.data)
516         bb.data.update_data(self.configuration.event_data)
517
518         if self.configuration.show_environment:
519             self.showEnvironment()
520             sys.exit( 0 )
521
522         # inject custom variables
523         if not bb.data.getVar("BUILDNAME", self.configuration.data):
524             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
525         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
526
527         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
528
529         if self.configuration.interactive:
530             self.interactiveMode()
531
532         if self.configuration.buildfile is not None:
533             bf = os.path.abspath( self.configuration.buildfile )
534             try:
535                 os.stat(bf)
536             except OSError:
537                 (filelist, masked) = self.collect_bbfiles()
538                 regexp = re.compile(self.configuration.buildfile)
539                 matches = 0
540                 for f in filelist:
541                     if regexp.search(f):
542                         bf = f
543                         matches = matches + 1
544                 if matches != 1:
545                     bb.msg.fatal(bb.msg.domain.Parsing, "Unable to match %s (%s matches found)" % (self.configuration.buildfile, matches))
546
547             bbfile_data = bb.parse.handle(bf, self.configuration.data)
548
549             item = bb.data.getVar('PN', bbfile_data, 1)
550             try:
551                 self.tryBuildPackage(bf, item, self.configuration.cmd, bbfile_data, True)
552             except bb.build.EventException:
553                 bb.msg.error(bb.msg.domain.Build,  "Build of '%s' failed" % item )
554
555             sys.exit( self.stats.show() )
556
557         # initialise the parsing status now we know we will need deps
558         self.status = bb.cache.CacheData()
559
560         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
561         self.status.ignored_dependencies = Set( ignore.split() )
562
563         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
564
565         pkgs_to_build = None
566         if args:
567             if not pkgs_to_build:
568                 pkgs_to_build = []
569             pkgs_to_build.extend(args)
570         if not pkgs_to_build:
571                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
572                 if bbpkgs:
573                         pkgs_to_build = bbpkgs.split()
574         if not pkgs_to_build and not self.configuration.show_versions \
575                              and not self.configuration.interactive \
576                              and not self.configuration.show_environment:
577                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
578                 print "for usage information."
579                 sys.exit(0)
580
581         # Import Psyco if available and not disabled
582         if not self.configuration.disable_psyco:
583             try:
584                 import psyco
585             except ImportError:
586                 bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
587             else:
588                 psyco.bind( self.parse_bbfiles )
589         else:
590             bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
591
592         try:
593             bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files")
594             (filelist, masked) = self.collect_bbfiles()
595             self.parse_bbfiles(filelist, masked, self.myProgressCallback)
596             bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete")
597             print
598             if self.configuration.parse_only:
599                 bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only.  Exiting.")
600                 return
601
602
603             self.buildDepgraph()
604
605             if self.configuration.show_versions:
606                 self.showVersions()
607                 sys.exit( 0 )
608             if 'world' in pkgs_to_build:
609                 self.buildWorldTargetList()
610                 pkgs_to_build.remove('world')
611                 for t in self.status.world_target:
612                     pkgs_to_build.append(t)
613
614             if self.configuration.dot_graph:
615                 self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps )
616                 sys.exit( 0 )
617
618             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data))
619
620             taskdata = bb.taskdata.TaskData()
621
622             runlist = []
623             try:
624                 for k in pkgs_to_build:
625                     taskdata.add_provider(self.configuration.data, self.status, k)
626                     runlist.append([k, "do_%s" % self.configuration.cmd])
627                 taskdata.add_unresolved(self.configuration.data, self.status)
628             except bb.providers.NoProvider:
629                 sys.exit(1)
630
631             rq = bb.runqueue.RunQueue()
632             rq.prepare_runqueue(self.configuration.data, self.status, taskdata, runlist)
633             try:
634                 failures = rq.execute_runqueue(self, self.configuration.data, self.status, taskdata, runlist)
635             except runqueue.TaskFailure, (fnid, fn, taskname):
636                 bb.msg.error(bb.msg.domain.Build, "'%s, %s' failed" % (fn, taskname))
637                 sys.exit(1)
638
639             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures))
640
641             sys.exit( self.stats.show() )
642
643         except KeyboardInterrupt:
644             bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.")
645             sys.exit(1)
646
647     def get_bbfiles( self, path = os.getcwd() ):
648         """Get list of default .bb files by reading out the current directory"""
649         contents = os.listdir(path)
650         bbfiles = []
651         for f in contents:
652             (root, ext) = os.path.splitext(f)
653             if ext == ".bb":
654                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
655         return bbfiles
656
657     def find_bbfiles( self, path ):
658         """Find all the .bb files in a directory (uses find)"""
659         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
660         try:
661             finddata = os.popen(findcmd)
662         except OSError:
663             return []
664         return finddata.readlines()
665
666     def collect_bbfiles( self ):
667         """Collect all available .bb build files"""
668         parsed, cached, skipped, masked = 0, 0, 0, 0
669         self.bb_cache = bb.cache.init(self)
670
671         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
672         data.setVar("BBFILES", " ".join(files), self.configuration.data)
673
674         if not len(files):
675             files = self.get_bbfiles()
676
677         if not len(files):
678             bb.msg.error(bb.msg.domain.Collection, "no files to build.")
679
680         newfiles = []
681         for f in files:
682             if os.path.isdir(f):
683                 dirfiles = self.find_bbfiles(f)
684                 if dirfiles:
685                     newfiles += dirfiles
686                     continue
687             newfiles += glob.glob(f) or [ f ]
688
689         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1)
690
691         if not bbmask:
692             return (newfiles, 0)
693
694         try:
695             bbmask_compiled = re.compile(bbmask)
696         except sre_constants.error:
697             bb.msg.fatal(bb.msg.domain.Collection, "BBMASK is not a valid regular expression.")
698
699         finalfiles = []
700         for i in xrange( len( newfiles ) ):
701             f = newfiles[i]
702             if bbmask and bbmask_compiled.search(f):
703                 bb.msg.debug(1, bb.msg.domain.Collection, "skipping masked file %s" % f)
704                 masked += 1
705                 continue
706             finalfiles.append(f)
707
708         return (finalfiles, masked)
709
710     def parse_bbfiles(self, filelist, masked, progressCallback = None):
711         parsed, cached, skipped = 0, 0, 0
712         for i in xrange( len( filelist ) ):
713             f = filelist[i]
714
715             bb.msg.debug(1, bb.msg.domain.Collection, "parsing %s" % f)
716
717             # read a file's metadata
718             try:
719                 fromCache, skip = self.bb_cache.loadData(f, self.configuration.data)
720                 if skip:
721                     skipped += 1
722                     bb.msg.debug(2, bb.msg.domain.Collection, "skipping %s" % f)
723                     self.bb_cache.skip(f)
724                     continue
725                 elif fromCache: cached += 1
726                 else: parsed += 1
727                 deps = None
728
729                 # Disabled by RP as was no longer functional
730                 # allow metadata files to add items to BBFILES
731                 #data.update_data(self.pkgdata[f])
732                 #addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
733                 #if addbbfiles:
734                 #    for aof in addbbfiles.split():
735                 #        if not files.count(aof):
736                 #            if not os.path.isabs(aof):
737                 #                aof = os.path.join(os.path.dirname(f),aof)
738                 #            files.append(aof)
739
740                 self.bb_cache.handle_data(f, self.status)
741
742                 # now inform the caller
743                 if progressCallback is not None:
744                     progressCallback( i + 1, len( filelist ), f, fromCache )
745
746             except IOError, e:
747                 self.bb_cache.remove(f)
748                 bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e))
749                 pass
750             except KeyboardInterrupt:
751                 self.bb_cache.sync()
752                 raise
753             except Exception, e:
754                 self.bb_cache.remove(f)
755                 bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f))
756             except:
757                 self.bb_cache.remove(f)
758                 raise
759
760         if progressCallback is not None:
761             print "\r" # need newline after Handling Bitbake files message
762             bb.msg.note(1, bb.msg.domain.Collection, "Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ))
763
764         self.bb_cache.sync()
765
766 #============================================================================#
767 # main
768 #============================================================================#
769
770 def main():
771     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
772     usage = """%prog [options] [package ...]
773
774 Executes the specified task (default is 'build') for a given set of BitBake files.
775 It expects that BBFILES is defined, which is a space seperated list of files to
776 be executed.  BBFILES does support wildcards.
777 Default BBFILES are the .bb files in the current directory.""" )
778
779     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
780                action = "store", dest = "buildfile", default = None )
781
782     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.",
783                action = "store_false", dest = "abort", default = True )
784
785     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
786                action = "store_true", dest = "force", default = False )
787
788     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode also called the BitBake shell.",
789                action = "store_true", dest = "interactive", default = False )
790
791     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). Depending on the base.bbclass a listtaks tasks is defined and will show available tasks",
792                action = "store", dest = "cmd" )
793
794     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
795                action = "append", dest = "file", default = [] )
796
797     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
798                action = "store_true", dest = "verbose", default = False )
799
800     parser.add_option( "-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
801                action = "count", dest="debug", default = 0)
802
803     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
804                action = "store_true", dest = "dry_run", default = False )
805
806     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
807                action = "store_true", dest = "parse_only", default = False )
808
809     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
810                action = "store_true", dest = "disable_psyco", default = False )
811
812     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
813                action = "store_true", dest = "show_versions", default = False )
814
815     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
816                action = "store_true", dest = "show_environment", default = False )
817
818     parser.add_option( "-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax",
819                 action = "store_true", dest = "dot_graph", default = False )
820
821     parser.add_option( "-I", "--ignore-deps", help = """Stop processing at the given list of dependencies when generating dependency graphs. This can help to make the graph more appealing""",
822                 action = "append", dest = "ignored_dot_deps", default = [] )
823
824     parser.add_option( "-l", "--log-domains", help = """Show debug logging for the specified logging domains""",
825                 action = "append", dest = "debug_domains", default = [] )
826
827
828     options, args = parser.parse_args( sys.argv )
829
830     cooker = BBCooker()
831     cooker.cook( BBConfiguration( options ), args[1:] )
832
833
834
835 if __name__ == "__main__":
836     print """WARNING, WARNING, WARNING
837 This is a Bitbake from the Unstable/Development Branch.
838 You might want to use the bitbake-1.6 stable branch (if you are not a BitBake developer or tester). I'm going to sleep 5 seconds now to make sure you see that."""
839     import time
840     time.sleep(5)
841     main()