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