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
11 # This program is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
16 # This program is distributed in the hope that it will be useful, but WITHOUT
17 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License along with
21 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22 # Place, Suite 330, Boston, MA 02111-1307 USA.
24 import sys, os, getopt, glob, copy, os.path, re, time
25 sys.path.append(os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
27 from bb import utils, data, parse, debug, event, fatal
29 import itertools, optparse
31 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
47 self.cache_dirty = False
51 self.packages_dynamic = {}
52 self.bbfile_priority = {}
53 self.bbfile_config_priorities = []
54 self.ignored_dependencies = None
55 self.possible_world = []
56 self.world_target = Set()
62 self.all_depends = Set()
64 def handle_bb_data(self, file_name, bb_data, cached):
66 We will fill the dictionaries with the stuff we
67 need for building the tree more fast
73 self.cache_dirty = True
75 pn = bb.data.getVar('PN', bb_data, True)
76 pv = bb.data.getVar('PV', bb_data, True)
77 pr = bb.data.getVar('PR', bb_data, True)
78 dp = int(bb.data.getVar('DEFAULT_PREFERENCE', bb_data, True) or "0")
79 provides = Set([pn] + (bb.data.getVar("PROVIDES", bb_data, 1) or "").split())
80 rprovides = (bb.data.getVar("RPROVIDES", bb_data, 1) or "").split()
81 depends = (bb.data.getVar("DEPENDS", bb_data, True) or "").split()
82 packages = (bb.data.getVar('PACKAGES', bb_data, True) or "").split()
83 packages_dynamic = (bb.data.getVar('PACKAGES_DYNAMIC', bb_data, True) or "").split()
86 # build PackageName to FileName lookup table
87 if pn not in self.pkg_pn:
89 self.pkg_pn[pn].append(file_name)
91 # build FileName to PackageName lookup table
92 self.pkg_fn[file_name] = pn
93 self.pkg_pvpr[file_name] = (pv,pr)
94 self.pkg_dp[file_name] = dp
96 # Build forward and reverse provider hashes
97 # Forward: virtual -> [filenames]
98 # Reverse: PN -> [virtuals]
99 if pn not in self.pn_provides:
100 self.pn_provides[pn] = Set()
101 self.pn_provides[pn] |= provides
103 for provide in provides:
104 if provide not in self.providers:
105 self.providers[provide] = []
106 self.providers[provide].append(file_name)
109 self.all_depends.add(dep)
111 # Build reverse hash for PACKAGES, so runtime dependencies
112 # can be be resolved (RDEPENDS, RRECOMMENDS etc.)
114 for package in packages:
115 if not package in self.packages:
116 self.packages[package] = []
117 self.packages[package].append(file_name)
119 for package in packages_dynamic:
120 if not package in self.packages_dynamic:
121 self.packages_dynamic[package] = []
122 self.packages_dynamic[package].append(file_name)
124 for rprovide in rprovides:
125 if not rprovide in self.rproviders:
126 self.rproviders[rprovide] = []
127 self.rproviders[rprovide].append(file_name)
129 # Collect files we may need for possible world-dep
131 if not bb.data.getVar('BROKEN', bb_data, True) and not bb.data.getVar('EXCLUDE_FROM_WORLD', bb_data, True):
132 self.possible_world.append(file_name)
135 #============================================================================#
137 #============================================================================#
140 Manage build statistics for one run
149 print "Build statistics:"
150 print " Attempted builds: %d" % self.attempt
152 print " Failed builds: %d" % self.fail
154 print " Dependencies not satisfied: %d" % self.deps
155 if self.fail or self.deps: return 1
159 #============================================================================#
161 #============================================================================#
162 class BBConfiguration( object ):
164 Manages build options and configurations for one run
166 def __init__( self, options ):
167 for key, val in options.__dict__.items():
168 setattr( self, key, val )
169 self.data = data.init()
171 #============================================================================#
173 #============================================================================#
176 Manages one bitbake build run
179 ParsingStatus = BBParsingStatus # make it visible from the shell
180 Statistics = BBStatistics # make it visible from the shell
182 def __init__( self ):
183 self.build_cache_fail = []
184 self.build_cache = []
185 self.rbuild_cache = []
186 self.building_list = []
188 self.consider_msgs_cache = []
190 self.stats = BBStatistics()
196 def tryBuildPackage( self, fn, item, the_data ):
197 """Build one package"""
198 bb.event.fire(bb.event.PkgStarted(item, the_data))
200 self.stats.attempt += 1
201 if self.configuration.force:
202 bb.data.setVarFlag('do_%s' % self.configuration.cmd, 'force', 1, the_data)
203 if not self.configuration.dry_run:
204 bb.build.exec_task('do_%s' % self.configuration.cmd, the_data)
205 bb.event.fire(bb.event.PkgSucceeded(item, the_data))
206 self.build_cache.append(fn)
208 except bb.build.FuncFailed:
210 bb.error("task stack execution failed")
211 bb.event.fire(bb.event.PkgFailed(item, the_data))
212 self.build_cache_fail.append(fn)
214 except bb.build.EventException, e:
217 bb.error("%s event exception, aborting" % bb.event.getName(event))
218 bb.event.fire(bb.event.PkgFailed(item, the_data))
219 self.build_cache_fail.append(fn)
222 def tryBuild( self, fn, virtual , itemtype , buildAllDeps ):
223 """Build a provider and its dependencies"""
225 the_data = self.pkgdata[fn]
228 buildAllDeps = bb.data.getVar('BUILD_ALL_DEPS', the_data, True) or False
230 if fn in self.building_list:
231 if itemtype == "runtime":
232 return self.addRunDeps(fn, virtual , buildAllDeps)
234 bb.error("%s depends on itself (eventually)" % fn)
235 bb.error("upwards chain is: %s" % (" -> ".join(self.build_path)))
238 item = self.status.pkg_fn[fn]
240 self.building_list.append(fn)
242 pathstr = "%s (%s)" % (item, virtual)
243 self.build_path.append(pathstr)
245 depends_list = (bb.data.getVar('DEPENDS', the_data, True) or "").split()
247 if self.configuration.verbose:
248 bb.note("current path: %s" % (" -> ".join(self.build_path)))
249 bb.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
254 depcmd = self.configuration.cmd
255 bbdepcmd = bb.data.getVarFlag('do_%s' % self.configuration.cmd, 'bbdepcmd', the_data)
256 if bbdepcmd is not None:
263 oldcmd = self.configuration.cmd
264 self.configuration.cmd = depcmd
266 for dependency in depends_list:
267 if dependency in self.status.ignored_dependencies:
271 if self.buildProvider( dependency , buildAllDeps ) == 0:
272 bb.error("dependency %s (for %s) not satisfied" % (dependency,item))
274 if self.configuration.abort:
278 self.configuration.cmd = oldcmd
284 if not self.addRunDeps(fn, virtual , buildAllDeps):
287 if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
288 self.build_cache.append(fn)
291 return self.tryBuildPackage( fn, item, the_data )
294 self.building_list.remove(fn)
295 self.build_path.remove(pathstr)
297 def findBestProvider( self, pn, pkg_pn = None):
299 If there is a PREFERRED_VERSION, find the highest-priority bbfile
300 providing that version. If not, find the latest version provided by
301 an bbfile in the highest-priority set.
304 pkg_pn = self.status.pkg_pn
309 priority = self.status.bbfile_priority[f]
310 if priority not in priorities:
311 priorities[priority] = []
312 priorities[priority].append(f)
313 p_list = priorities.keys()
314 p_list.sort(lambda a, b: a - b)
317 tmp_pn = [priorities[p]] + tmp_pn
319 preferred_file = None
321 localdata = data.createCopy(self.configuration.data)
322 bb.data.setVar('OVERRIDES', "%s:%s" % (pn, data.getVar('OVERRIDES', localdata)), localdata)
323 bb.data.update_data(localdata)
325 preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, True)
327 m = re.match('(.*)_(.*)', preferred_v)
329 preferred_v = m.group(1)
330 preferred_r = m.group(2)
334 for file_set in tmp_pn:
336 pv,pr = self.status.pkg_pvpr[f]
337 if preferred_v == pv and (preferred_r == pr or preferred_r == None):
339 preferred_ver = (pv, pr)
344 pv_str = '%s-%s' % (preferred_v, preferred_r)
347 if preferred_file is None:
348 bb.note("preferred version %s of %s not available" % (pv_str, pn))
350 bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
354 # get highest priority file set
359 for file_name in files:
360 pv,pr = self.status.pkg_pvpr[file_name]
361 dp = self.status.pkg_dp[file_name]
363 if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
367 if preferred_file is None:
368 preferred_file = latest_f
369 preferred_ver = latest
371 return (latest,latest_f,preferred_ver, preferred_file)
373 def showVersions( self ):
374 pkg_pn = self.status.pkg_pn
375 preferred_versions = {}
379 for pn in pkg_pn.keys():
380 (last_ver,last_file,pref_ver,pref_file) = self.findBestProvider(pn)
381 preferred_versions[pn] = (pref_ver, pref_file)
382 latest_versions[pn] = (last_ver, last_file)
384 pkg_list = pkg_pn.keys()
388 pref = preferred_versions[p]
389 latest = latest_versions[p]
392 prefstr = pref[0][0] + "-" + pref[0][1]
396 print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
399 def showEnvironment( self ):
400 """Show the outer or per-package environment"""
401 if self.configuration.buildfile:
403 self.configuration.data, fromCache = self.load_bbfile( self.configuration.buildfile )
405 fatal("Unable to read %s: %s" % ( self.configuration.buildfile, e ))
408 # emit variables and shell functions
410 data.update_data( self.configuration.data )
411 data.emit_env(sys.__stdout__, self.configuration.data, True)
414 # emit the metadata which isnt valid shell
415 for e in self.configuration.data.keys():
416 if data.getVarFlag( e, 'python', self.configuration.data ):
417 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
419 def filterProviders(self, providers, item):
421 Take a list of providers and filter/reorder according to the
422 environment variables and previous build results
425 preferred_versions = {}
427 # Collate providers by PN
430 pn = self.status.pkg_fn[p]
435 bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
437 for pn in pkg_pn.keys():
438 preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
439 eligible.append(preferred_versions[pn][1])
442 if p in self.build_cache_fail:
443 bb.debug(1, "rejecting already-failed %s" % p)
446 if len(eligible) == 0:
447 bb.error("no eligible providers for %s" % item)
450 # look to see if one of them is already staged, or marked as preferred.
451 # if so, bump it to the head of the queue
453 the_data = self.pkgdata[p]
454 pn = bb.data.getVar('PN', the_data, 1)
455 pv = bb.data.getVar('PV', the_data, 1)
456 pr = bb.data.getVar('PR', the_data, 1)
457 tmpdir = bb.data.getVar('TMPDIR', the_data, 1)
458 stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
459 if os.path.exists(stamp):
460 (newvers, fn) = preferred_versions[pn]
461 if not fn in eligible:
462 # package was made ineligible by already-failed check
464 oldver = "%s-%s" % (pv, pr)
465 newver = '-'.join(newvers)
466 if (newver != oldver):
467 extra_chat = "; upgrading from %s to %s" % (oldver, newver)
470 if self.configuration.verbose:
471 bb.note("selecting already-staged %s to satisfy %s%s" % (pn, item, extra_chat))
473 eligible = [fn] + eligible
479 def buildProvider( self, item , buildAllDeps ):
481 Build something to provide a named build requirement
482 (takes item names from DEPENDS namespace)
486 discriminated = False
488 if not item in self.status.providers:
489 bb.error("Nothing provides dependency %s" % item)
492 all_p = self.status.providers[item]
495 if p in self.build_cache:
496 bb.debug(1, "already built %s in this run\n" % p)
499 eligible = self.filterProviders(all_p, item)
501 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
503 self.preferred[item] = prefervar
505 if item in self.preferred:
507 pn = self.status.pkg_fn[p]
508 if self.preferred[item] == pn:
509 if self.configuration.verbose:
510 bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
512 eligible = [p] + eligible
516 if len(eligible) > 1 and discriminated == False:
517 if item not in self.consider_msgs_cache:
520 providers_list.append(self.status.pkg_fn[fn])
521 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
522 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
523 self.consider_msgs_cache.append(item)
526 # run through the list until we find one that we can build
528 bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
529 if self.tryBuild(fn, item, "build", buildAllDeps):
532 bb.note("no buildable providers for %s" % item)
535 def buildRProvider( self, item , buildAllDeps ):
537 Build something to provide a named runtime requirement
538 (takes item names from RDEPENDS/PACKAGES namespace)
543 discriminated = False
548 all_p = self.getProvidersRun(item)
551 bb.error("Nothing provides runtime dependency %s" % (item))
555 if p in self.rbuild_cache:
556 bb.debug(2, "Already built %s providing runtime %s\n" % (p,item))
558 if p in self.build_cache:
559 bb.debug(2, "Already built %s but adding any further RDEPENDS for %s\n" % (p, item))
560 return self.addRunDeps(p, item , buildAllDeps)
562 eligible = self.filterProviders(all_p, item)
565 pn = self.status.pkg_fn[p]
566 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % pn, self.configuration.data, 1)
568 if self.configuration.verbose:
569 bb.note("selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
571 eligible = [p] + eligible
575 if len(eligible) > 1 and discriminated == False:
576 if item not in self.consider_msgs_cache:
579 providers_list.append(self.status.pkg_fn[fn])
580 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
581 bb.note("consider defining a PREFERRED_PROVIDER to match runtime %s" % item)
582 self.consider_msgs_cache.append(item)
584 # run through the list until we find one that we can build
586 bb.debug(2, "selecting %s to satisfy runtime %s" % (fn, item))
587 if self.tryBuild(fn, item, "runtime", buildAllDeps):
590 bb.error("No buildable providers for runtime %s" % item)
593 def getProvidersRun(self, rdepend):
595 Return any potential providers of runtime rdepend
599 if rdepend in self.status.rproviders:
600 rproviders += self.status.rproviders[rdepend]
602 if rdepend in self.status.packages:
603 rproviders += self.status.packages[rdepend]
608 # Only search dynamic packages if we can't find anything in other variables
609 for pattern in self.status.packages_dynamic:
610 regexp = re.compile(pattern)
611 if regexp.match(rdepend):
612 rproviders += self.status.packages_dynamic[pattern]
616 def addRunDeps(self , fn, item , buildAllDeps):
618 Add any runtime dependencies of runtime item provided by fn
619 as long as item has't previously been processed by this function.
622 if item in self.rbuild_cache:
629 self.rbuild_cache.append(item)
630 the_data = self.pkgdata[fn]
631 pn = self.status.pkg_fn[fn]
634 rdepends += bb.utils.explode_deps(bb.data.getVar('RDEPENDS', the_data, True) or "")
635 rdepends += bb.utils.explode_deps(bb.data.getVar('RRECOMMENDS', the_data, True) or "")
637 packages = (bb.data.getVar('PACKAGES', the_data, 1).split() or "")
638 for package in packages:
640 rdepends += bb.utils.explode_deps(bb.data.getVar("RDEPENDS_%s" % package, the_data, True) or "")
641 rdepends += bb.utils.explode_deps(bb.data.getVar("RRECOMMENDS_%s" % package, the_data, True) or "")
643 bb.debug(2, "Additional runtime dependencies for %s are: %s" % (item, " ".join(rdepends)))
645 for rdepend in rdepends:
646 if not self.buildRProvider(rdepend, buildAllDeps):
650 def buildDepgraph( self ):
651 all_depends = self.status.all_depends
652 pn_provides = self.status.pn_provides
654 def calc_bbfile_priority(filename):
655 for (regex, pri) in self.status.bbfile_config_priorities:
656 if regex.match(filename):
660 # Handle PREFERRED_PROVIDERS
661 for p in (bb.data.getVar('PREFERRED_PROVIDERS', self.configuration.data, 1) or "").split():
662 (providee, provider) = p.split(':')
663 if providee in self.preferred and self.preferred[providee] != provider:
664 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
665 self.preferred[providee] = provider
667 # Calculate priorities for each file
668 for p in self.pkgdata.keys():
669 self.status.bbfile_priority[p] = calc_bbfile_priority(p)
671 # Build package list for "bitbake world"
672 bb.debug(1, "collating packages for \"world\"")
673 for f in self.status.possible_world:
675 pn = self.status.pkg_fn[f]
677 for p in pn_provides[pn]:
678 if p.startswith('virtual/'):
679 bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
682 for pf in self.status.providers[p]:
683 if self.status.pkg_fn[pf] != pn:
684 bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
688 self.status.world_target.add(pn)
690 # drop reference count now
691 self.status.possible_world = None
692 self.status.all_depends = None
694 def myProgressCallback( self, x, y, f, file_data, from_cache ):
695 # feed the status with new input
696 self.status.handle_bb_data(f, file_data, from_cache)
700 if os.isatty(sys.stdout.fileno()):
701 sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
705 sys.stdout.write("Parsing .bb files, please wait...")
708 sys.stdout.write("done.")
711 def interactiveMode( self ):
712 """Drop off into a shell"""
715 except ImportError, details:
716 bb.fatal("Sorry, shell not available (%s)" % details )
718 bb.data.update_data( self.configuration.data )
722 def parseConfigurationFile( self, afile ):
724 self.configuration.data = bb.parse.handle( afile, self.configuration.data )
726 bb.fatal( "Unable to open %s" % afile )
727 except bb.parse.ParseError, details:
728 bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
730 def handleCollections( self, collections ):
731 """Handle collections"""
733 collection_list = collections.split()
734 for c in collection_list:
735 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
737 bb.error("BBFILE_PATTERN_%s not defined" % c)
739 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
741 bb.error("BBFILE_PRIORITY_%s not defined" % c)
744 cre = re.compile(regex)
746 bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
750 self.status.bbfile_config_priorities.append((cre, pri))
752 bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
755 def cook( self, configuration, args ):
756 self.configuration = configuration
758 if not self.configuration.cmd:
759 self.configuration.cmd = "build"
761 if self.configuration.debug:
762 bb.debug_level = self.configuration.debug
764 self.configuration.data = bb.data.init()
766 for f in self.configuration.file:
767 self.parseConfigurationFile( f )
769 self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
771 if self.configuration.show_environment:
772 self.showEnvironment()
775 # inject custom variables
776 if not bb.data.getVar("BUILDNAME", self.configuration.data):
777 bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
778 bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
780 buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
782 if self.configuration.interactive:
783 self.interactiveMode()
785 if self.configuration.buildfile is not None:
786 bf = os.path.abspath( self.configuration.buildfile )
788 bbfile_data = bb.parse.handle(bf, self.configuration.data)
790 bb.fatal("Unable to open %s" % bf)
792 item = bb.data.getVar('PN', bbfile_data, 1)
794 self.tryBuildPackage( bf, item, bbfile_data )
795 except bb.build.EventException:
796 bb.error( "Build of '%s' failed" % item )
798 sys.exit( self.stats.show() )
800 # initialise the parsing status now we know we will need deps
801 self.status = BBParsingStatus()
803 ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
804 self.status.ignored_dependencies = Set( ignore.split() )
806 self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
810 if not pkgs_to_build:
812 pkgs_to_build.extend(args)
813 if not pkgs_to_build:
814 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
816 pkgs_to_build = bbpkgs.split()
817 if not pkgs_to_build and not self.configuration.show_versions \
818 and not self.configuration.interactive \
819 and not self.configuration.show_environment:
820 print "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help'"
821 print "for usage information."
824 # Import Psyco if available and not disabled
825 if not self.configuration.disable_psyco:
830 bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
832 psyco.bind( self.collect_bbfiles )
834 bb.note("You have disabled Psyco. This decreases performance.")
837 bb.debug(1, "collecting .bb files")
838 self.collect_bbfiles( self.myProgressCallback )
839 bb.debug(1, "parsing complete")
842 if self.configuration.parse_only:
843 print "Requested parsing .bb files only. Exiting."
846 bb.data.update_data( self.configuration.data )
849 if self.configuration.show_versions:
852 if 'world' in pkgs_to_build:
853 pkgs_to_build.remove('world')
854 for t in self.status.world_target:
855 pkgs_to_build.append(t)
857 bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.data))
859 for k in pkgs_to_build:
862 if self.buildProvider( k , False ) == 0:
865 except bb.build.EventException:
866 bb.error("Build of " + k + " failed")
870 if self.configuration.abort:
873 bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data))
875 sys.exit( self.stats.show() )
877 except KeyboardInterrupt:
878 print "\nNOTE: KeyboardInterrupt - Build not completed."
881 def get_bbfiles( self, path = os.getcwd() ):
882 """Get list of default .bb files by reading out the current directory"""
883 contents = os.listdir(path)
886 (root, ext) = os.path.splitext(f)
888 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
891 def find_bbfiles( self, path ):
892 """Find all the .bb files in a directory (uses find)"""
893 findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
895 finddata = os.popen(findcmd)
898 return finddata.readlines()
900 def deps_clean(self, d):
901 depstr = data.getVar('__depends', d)
903 deps = depstr.split(" ")
905 (f,old_mtime_s) = dep.split("@")
906 old_mtime = int(old_mtime_s)
907 new_mtime = parse.cached_mtime(f)
908 if (new_mtime > old_mtime):
912 def load_bbfile( self, bbfile ):
913 """Load and parse one .bb build file"""
915 if not self.cache in [None, '']:
917 cache_mtime = data.init_db_mtime(self.cache, bbfile)
918 file_mtime = parse.cached_mtime(bbfile)
920 if file_mtime > cache_mtime:
921 #print " : '%s' dirty. reparsing..." % bbfile
924 #print " : '%s' clean. loading from cache..." % bbfile
925 cache_data = data.init_db( self.cache, bbfile, False )
926 if self.deps_clean(cache_data):
927 return cache_data, True
929 topdir = data.getVar('TOPDIR', self.configuration.data)
931 topdir = os.path.abspath(os.getcwd())
933 data.setVar('TOPDIR', topdir, self.configuration)
934 bbfile = os.path.abspath(bbfile)
935 bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
936 # expand tmpdir to include this topdir
937 data.setVar('TMPDIR', data.getVar('TMPDIR', self.configuration.data, 1) or "", self.configuration.data)
938 # set topdir to location of .bb file
940 #data.setVar('TOPDIR', topdir, cfg)
942 oldpath = os.path.abspath(os.getcwd())
944 bb = data.init_db(self.cache,bbfile, True, self.configuration.data)
946 parse.handle(bbfile, bb) # read .bb data
947 if not self.cache in [None, '']:
948 bb.commit(parse.cached_mtime(bbfile)) # write cache
954 def collect_bbfiles( self, progressCallback ):
955 """Collect all available .bb build files"""
956 self.cb = progressCallback
957 parsed, cached, skipped, masked = 0, 0, 0, 0
958 self.cache = bb.data.getVar( "CACHE", self.configuration.data, 1 )
959 self.pkgdata = data.pkgdata( not self.cache in [None, ''], self.cache, self.configuration.data )
961 if not self.cache in [None, '']:
962 if self.cb is not None:
963 print "NOTE: Using cache in '%s'" % self.cache
965 os.stat( self.cache )
967 bb.mkdirhier( self.cache )
969 if self.cb is not None:
970 print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
971 files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
972 data.setVar("BBFILES", " ".join(files), self.configuration.data)
975 files = self.get_bbfiles()
978 bb.error("no files to build.")
983 dirfiles = self.find_bbfiles(f)
987 newfiles += glob.glob(f) or [ f ]
989 bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
991 bbmask_compiled = re.compile(bbmask)
992 except sre_constants.error:
993 bb.fatal("BBMASK is not a valid regular expression.")
995 for i in xrange( len( newfiles ) ):
997 if bbmask and bbmask_compiled.search(f):
998 bb.debug(1, "bbmake: skipping %s" % f)
1001 debug(1, "bbmake: parsing %s" % f)
1003 # read a file's metadata
1005 bb_data, fromCache = self.load_bbfile(f)
1006 if fromCache: cached += 1
1009 if bb_data is not None:
1010 # allow metadata files to add items to BBFILES
1011 #data.update_data(self.pkgdata[f])
1012 addbbfiles = data.getVar('BBFILES', bb_data) or None
1014 for aof in addbbfiles.split():
1015 if not files.count(aof):
1016 if not os.path.isabs(aof):
1017 aof = os.path.join(os.path.dirname(f),aof)
1019 for var in bb_data.keys():
1020 if data.getVarFlag(var, "handler", bb_data) and data.getVar(var, bb_data):
1021 event.register(data.getVar(var, bb_data))
1022 self.pkgdata[f] = bb_data
1024 # now inform the caller
1025 if self.cb is not None:
1026 self.cb( i + 1, len( newfiles ), f, bb_data, fromCache )
1029 bb.error("opening %s: %s" % (f, e))
1031 except bb.parse.SkipPackage:
1034 except KeyboardInterrupt:
1036 except Exception, e:
1037 bb.error("%s while parsing %s" % (e, f))
1039 if self.cb is not None:
1040 print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
1042 #============================================================================#
1044 #============================================================================#
1046 if __name__ == "__main__":
1048 parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
1049 usage = """%prog [options] [package ...]
1051 Executes the specified task (default is 'build') for a given set of BitBake files.
1052 It expects that BBFILES is defined, which is a space seperated list of files to
1053 be executed. BBFILES does support wildcards.
1054 Default BBFILES are the .bb files in the current directory.""" )
1056 parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
1057 action = "store", dest = "buildfile", default = None )
1059 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.",
1060 action = "store_false", dest = "abort", default = True )
1062 parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
1063 action = "store_true", dest = "force", default = False )
1065 parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
1066 action = "store_true", dest = "interactive", default = False )
1068 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)",
1069 action = "store", dest = "cmd", default = "build" )
1071 parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
1072 action = "append", dest = "file", default = [] )
1074 parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
1075 action = "store_true", dest = "verbose", default = False )
1077 parser.add_option( "-D", "--debug", help = "Increase the debug level",
1078 action = "count", dest="debug", default = 0)
1080 parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
1081 action = "store_true", dest = "dry_run", default = False )
1083 parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
1084 action = "store_true", dest = "parse_only", default = False )
1086 parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
1087 action = "store_true", dest = "disable_psyco", default = False )
1089 parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
1090 action = "store_true", dest = "show_versions", default = False )
1092 parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
1093 action = "store_true", dest = "show_environment", default = False )
1095 options, args = parser.parse_args( sys.argv )
1098 cooker.cook( BBConfiguration( options ), args[1:] )