2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
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
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
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.
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.
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'))
28 from bb import utils, data, parse, event, cache, providers, taskdata, runqueue
30 import itertools, optparse
32 parsespin = itertools.cycle( r'|/-\\' )
36 #============================================================================#
38 #============================================================================#
39 class BBParsingStatus:
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
48 Direct cache variables
53 self.packages_dynamic = {}
54 self.possible_world = []
60 self.all_depends = Set()
71 Indirect Cache variables
73 self.ignored_dependencies = []
74 self.world_target = Set()
75 self.bbfile_priority = {}
76 self.bbfile_config_priorities = []
79 def handle_bb_data(self, file_name, bb_cache, cached):
81 We will fill the dictionaries with the stuff we
82 need for building the tree more fast
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()
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)
98 # build PackageName to FileName lookup table
99 if pn not in self.pkg_pn:
101 self.pkg_pn[pn].append(file_name)
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)
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
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
118 for provide in provides:
119 if provide not in self.providers:
120 self.providers[provide] = []
121 self.providers[provide].append(file_name)
123 self.deps[file_name] = Set()
125 self.all_depends.add(dep)
126 self.deps[file_name].add(dep)
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()
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)
141 for rprovide in rprovides:
142 if not rprovide in self.rproviders:
143 self.rproviders[rprovide] = []
144 self.rproviders[rprovide].append(file_name)
146 # Build hash of runtime depends and rececommends
148 def add_dep(deplist, deps):
150 if not dep in deplist:
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] = {}
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] = {}
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 ""))
169 # Collect files we may need for possible world-dep
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)
175 #============================================================================#
177 #============================================================================#
180 Manage build statistics for one run
189 print "Build statistics:"
190 print " Attempted builds: %d" % self.attempt
192 print " Failed builds: %d" % self.fail
194 print " Dependencies not satisfied: %d" % self.deps
195 if self.fail or self.deps: return 1
199 #============================================================================#
201 #============================================================================#
202 class BBConfiguration( object ):
204 Manages build options and configurations for one run
206 def __init__( self, options ):
207 for key, val in options.__dict__.items():
208 setattr( self, key, val )
210 #============================================================================#
212 #============================================================================#
215 Manages one bitbake build run
218 ParsingStatus = BBParsingStatus # make it visible from the shell
219 Statistics = BBStatistics # make it visible from the shell
221 def __init__( self ):
222 self.build_cache_fail = []
223 self.build_cache = []
224 self.stats = BBStatistics()
230 def tryBuildPackage(self, fn, item, task, the_data, build_depends):
232 Build one task of a package, optionally build following task depends
234 bb.event.fire(bb.event.PkgStarted(item, the_data))
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)
246 except bb.build.FuncFailed:
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)
252 except bb.build.EventException, e:
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)
260 def tryBuild( self, fn, build_depends):
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
267 the_data = self.bb_cache.loadDataFull(fn, self)
269 item = self.status.pkg_fn[fn]
271 if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
272 self.build_cache.append(fn)
275 return self.tryBuildPackage(fn, item, self.configuration.cmd, the_data, build_depends)
277 def showVersions( self ):
278 pkg_pn = self.status.pkg_pn
279 preferred_versions = {}
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)
288 pkg_list = pkg_pn.keys()
292 pref = preferred_versions[p]
293 latest = latest_versions[p]
296 prefstr = pref[0][0] + "-" + pref[0][1]
300 print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
304 def showEnvironment( self ):
305 """Show the outer or per-package environment"""
306 if self.configuration.buildfile:
308 self.bb_cache = bb.cache.init(self)
310 self.configuration.data = self.bb_cache.loadDataFull(self.configuration.buildfile, self)
312 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to read %s: %s" % ( self.configuration.buildfile, e ))
314 bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e)
315 # emit variables and shell functions
317 data.update_data( self.configuration.data )
318 data.emit_env(sys.__stdout__, self.configuration.data, True)
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)))
326 def generateDotGraph( self, pkgs_to_build, ignore_deps ):
328 Generate two graphs one for the DEPENDS and RDEPENDS. The current
329 implementation creates crappy graphs ;)
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
336 def myFilterProvider( providers, item):
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.
343 preferred_versions = {}
345 # Collate providers by PN
348 pn = self.status.pkg_fn[p]
353 bb.msg.debug(1, bb.msg.domain.Provider, "providers for %s are: %s" % (item, pkg_pn.keys()))
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])
360 if p in self.build_cache_fail:
361 bb.msg.debug(1, bb.msg.domain.Provider, "rejecting already-failed %s" % p)
364 if len(eligible) == 0:
365 bb.msg.error(bb.msg.domain.Provider, "no eligible providers for %s" % item)
368 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
370 # try the preferred provider first
373 if prefervar == self.status.pkg_fn[p]:
374 bb.msg.note(1, bb.msg.domain.Provider, "Selecting PREFERRED_PROVIDER %s" % prefervar)
376 eligible = [p] + eligible
381 # try to avoid adding the same rdepends over an over again
386 def add_depends(package_list):
388 Add all depends of all packages from this list
390 for package in package_list:
391 if package in seen_depends or package in ignore_deps:
394 seen_depends.append( package )
395 if not package in self.status.providers:
397 We have not seen this name -> error in
400 bb.note( "ERROR with provider: %(package)s" % vars() )
401 print >> depends_file, '"%(package)s" -> ERROR' % vars()
404 # get all providers for this package
405 providers = self.status.providers[package]
407 # now let us find the bestProvider for it
408 fn = myFilterProvider(providers, package)[0]
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 )
414 # now create the node
415 print >> depends_file, '"%(package)s" [label="%(package)s\\n%(version)s"]' % vars()
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()
422 def add_all_depends( the_depends, the_rdepends ):
424 Add both DEPENDS and RDEPENDS. RDEPENDS will get dashed
427 package_list = the_depends + the_rdepends
428 for package in package_list:
429 if package in seen_rdepends or package in ignore_deps:
432 seen_rdepends.append( package )
434 # Let us find out if the package is a DEPENDS or RDEPENDS
435 # and we will set 'providers' with the avilable providers
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()
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()
450 providers = bb.providers.getRuntimeProviders(self.status, package)
452 # something went wrong...
453 print "Complete ERROR! %s" % package
456 # now let us find the bestProvider for it
457 fn = myFilterProvider(providers, package)[0]
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()
465 version = self.bb_cache.getVar('PV', fn, True ) + '-' + self.bb_cache.getVar('PR', fn, True)
467 # handle all the depends and rdepends of package
468 add_all_depends ( depends, rdepends )
470 # now create the node using package name
471 print >> alldepends_file, '"%(package)s" [label="%(package)s\\n%(version)s"]' % vars()
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()
483 depends_file = file('depends.dot', 'w' )
484 print >> depends_file, "digraph depends {"
485 add_depends( pkgs_to_build )
486 print >> depends_file, "}"
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, "}"
494 def buildProvider( self, item , buildAllDeps , build_depends = [] ):
496 Build something to provide a named build requirement
497 (takes item names from DEPENDS namespace)
500 taskdata = bb.taskdata.TaskData()
503 taskdata.add_provider(self.configuration.data, self.status, item)
504 except bb.providers.NoProvider:
507 providers = taskdata.get_provider(item)
509 if len(providers) == 0:
513 if p in self.build_cache:
514 bb.msg.debug(1, bb.msg.domain.Provider, "already built %s in this run" % p)
517 taskdata.add_unresolved(self.configuration.data, self.status)
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)
524 #taskdata.dump_data()
525 #rq.dump_data(taskdata)
529 def buildDepgraph( self ):
530 all_depends = self.status.all_depends
531 pn_provides = self.status.pn_provides
533 localdata = data.createCopy(self.configuration.data)
534 bb.data.update_data(localdata)
536 def calc_bbfile_priority(filename):
537 for (regex, pri) in self.status.bbfile_config_priorities:
538 if regex.match(filename):
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
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)
553 def buildWorldTargetList(self):
555 Build package list for "bitbake world"
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:
562 pn = self.status.pkg_fn[f]
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))
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))
575 self.status.world_target.add(pn)
577 # drop reference count now
578 self.status.possible_world = None
579 self.status.all_depends = None
581 def myProgressCallback( self, x, y, f, bb_cache, from_cache ):
582 # feed the status with new input
584 self.status.handle_bb_data(f, bb_cache, from_cache)
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 ) )
591 sys.stdout.write("Parsing .bb files, please wait...")
594 sys.stdout.write("done.")
597 def interactiveMode( self ):
598 """Drop off into a shell"""
601 except ImportError, details:
602 bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details )
604 bb.data.update_data( self.configuration.data )
608 def parseConfigurationFile( self, afile ):
610 self.configuration.data = bb.parse.handle( afile, self.configuration.data )
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 )
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))
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) )
631 def handleCollections( self, collections ):
632 """Handle 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)
638 bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s not defined" % c)
640 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
642 bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PRIORITY_%s not defined" % c)
645 cre = re.compile(regex)
647 bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
651 self.status.bbfile_config_priorities.append((cre, pri))
653 bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
656 def cook( self, configuration, args ):
658 We are building stuff here. We do the building
659 from here. By default we try to execute task
663 self.configuration = configuration
665 if self.configuration.verbose:
666 bb.msg.set_verbose(True)
668 if self.configuration.debug:
669 bb.msg.set_debug_level(self.configuration.debug)
671 self.configuration.data = bb.data.init()
673 for f in self.configuration.file:
674 self.parseConfigurationFile( f )
676 self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
678 if not self.configuration.cmd:
679 self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data)
681 # For backwards compatibility - REMOVE ME
682 if not self.configuration.cmd:
683 self.configuration.cmd = "build"
686 # Special updated configuration we use for firing events
688 self.configuration.event_data = bb.data.createCopy(self.configuration.data)
689 bb.data.update_data(self.configuration.event_data)
691 if self.configuration.show_environment:
692 self.showEnvironment()
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)
700 buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
702 if self.configuration.interactive:
703 self.interactiveMode()
705 if self.configuration.buildfile is not None:
706 bf = os.path.abspath( self.configuration.buildfile )
708 bbfile_data = bb.parse.handle(bf, self.configuration.data)
710 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to open %s" % bf)
712 item = bb.data.getVar('PN', bbfile_data, 1)
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 )
718 sys.exit( self.stats.show() )
720 # initialise the parsing status now we know we will need deps
721 self.status = BBParsingStatus()
723 ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
724 self.status.ignored_dependencies = Set( ignore.split() )
726 self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
730 if not 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)
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."
744 # Import Psyco if available and not disabled
745 if not self.configuration.disable_psyco:
749 bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
751 psyco.bind( self.collect_bbfiles )
753 bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
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")
760 if self.configuration.parse_only:
761 bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only. Exiting.")
767 if self.configuration.show_versions:
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)
776 if self.configuration.dot_graph:
777 self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps )
780 bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data))
782 taskdata = bb.taskdata.TaskData()
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:
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)
797 bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures))
799 sys.exit( self.stats.show() )
801 except KeyboardInterrupt:
802 bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.")
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)
810 (root, ext) = os.path.splitext(f)
812 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
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/'
819 finddata = os.popen(findcmd)
822 return finddata.readlines()
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)
830 files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
831 data.setVar("BBFILES", " ".join(files), self.configuration.data)
834 files = self.get_bbfiles()
837 bb.msg.error(bb.msg.domain.Collection, "no files to build.")
842 dirfiles = self.find_bbfiles(f)
846 newfiles += glob.glob(f) or [ f ]
848 bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
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.")
854 for i in xrange( len( newfiles ) ):
856 if bbmask and bbmask_compiled.search(f):
857 bb.msg.debug(1, bb.msg.domain.Collection, "bbmake: skipping %s" % f, f)
860 bb.msg.debug(1, bb.msg.domain.Collection, "bbmake: parsing %s" % f, f)
862 # read a file's metadata
864 fromCache, skip = self.bb_cache.loadData(f, self)
867 bb.msg.debug(2, bb.msg.domain.Collection, "Skipping %s" % f, f)
868 self.bb_cache.skip(f)
870 elif fromCache: cached += 1
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
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)
884 # now inform the caller
885 if self.cb is not None:
886 self.cb( i + 1, len( newfiles ), f, self.bb_cache, fromCache )
889 self.bb_cache.remove(f)
890 bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e), f)
892 except KeyboardInterrupt:
896 self.bb_cache.remove(f)
897 bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f), f)
899 self.bb_cache.remove(f)
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 ))
908 #============================================================================#
910 #============================================================================#
913 parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
914 usage = """%prog [options] [package ...]
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.""" )
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 )
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 )
927 parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
928 action = "store_true", dest = "force", default = False )
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 )
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" )
936 parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
937 action = "append", dest = "file", default = [] )
939 parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
940 action = "store_true", dest = "verbose", default = False )
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)
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 )
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 )
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 )
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 )
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 )
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 = [] )
966 options, args = parser.parse_args( sys.argv )
969 cooker.cook( BBConfiguration( options ), args[1:] )
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."""