bitbake/bin/bitbake:
[bitbake.git] / bin / bitbake
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 #
5 # Copyright (C) 2003, 2004  Chris Larson
6 # Copyright (C) 2003, 2004  Phil Blundell
7 # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
8 # Copyright (C) 2005        Holger Hans Peter Freyther
9 # Copyright (C) 2005        ROAD GmbH
10 #
11 # This program is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 #
16 # This program is distributed in the hope that it will be useful, but WITHOUT
17 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along with
21 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22 # Place, Suite 330, Boston, MA 02111-1307 USA.
23
24 import sys, os, getopt, glob, copy, os.path, re, time
25 sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
26 import bb
27 from bb import utils, data, parse, debug, event, fatal, cache
28 from sets import Set
29 import itertools, optparse
30
31 parsespin = itertools.cycle( r'|/-\\' )
32 bbdebug = 0
33
34 __version__ = "1.3.9"
35
36 #============================================================================#
37 # BBParsingStatus
38 #============================================================================#
39 class BBParsingStatus:
40     """
41     The initial idea for this status class is to use the data when it is
42     already loaded instead of loading it from various place over and over
43     again.
44     """
45
46     def __init__(self):
47         self.providers   = {}
48         self.rproviders = {}
49         self.packages = {}
50         self.packages_dynamic = {}
51         self.bbfile_priority = {}
52         self.bbfile_config_priorities = []
53         self.ignored_dependencies = None
54         self.possible_world = []
55         self.world_target = Set()
56         self.pkg_pn = {}
57         self.pkg_fn = {}
58         self.pkg_pvpr = {}
59         self.pkg_dp = {}
60         self.pn_provides = {}
61         self.all_depends = Set()
62         self.build_all = {}
63         self.rundeps = {}
64         self.runrecs = {}
65         self.stamp = {}
66
67     def handle_bb_data(self, file_name, bb_cache, cached):
68         """
69         We will fill the dictionaries with the stuff we
70         need for building the tree more fast
71         """
72
73         pn       = bb_cache.getVar('PN', file_name, True)
74         pv       = bb_cache.getVar('PV', file_name, True)
75         pr       = bb_cache.getVar('PR', file_name, True)
76         dp       = int(bb_cache.getVar('DEFAULT_PREFERENCE', file_name, True) or "0")
77         provides  = Set([pn] + (bb_cache.getVar("PROVIDES", file_name, True) or "").split())
78         depends   = (bb_cache.getVar("DEPENDS", file_name, True) or "").split()
79         packages  = (bb_cache.getVar('PACKAGES', file_name, True) or "").split()
80         packages_dynamic = (bb_cache.getVar('PACKAGES_DYNAMIC', file_name, True) or "").split()
81         rprovides = (bb_cache.getVar("RPROVIDES", file_name, True) or "").split()
82
83         # build PackageName to FileName lookup table
84         if pn not in self.pkg_pn:
85             self.pkg_pn[pn] = []
86         self.pkg_pn[pn].append(file_name)
87
88         self.build_all[file_name] = int(bb_cache.getVar('BUILD_ALL_DEPS', file_name, True) or "0")
89         self.stamp[file_name] = bb_cache.getVar('STAMP', file_name, True)
90
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
95
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
102
103         for provide in provides:
104             if provide not in self.providers:
105                 self.providers[provide] = []
106             self.providers[provide].append(file_name)
107
108         for dep in depends:
109             self.all_depends.add(dep)
110
111         # Build reverse hash for PACKAGES, so runtime dependencies 
112         # can be be resolved (RDEPENDS, RRECOMMENDS etc.)
113         for package in packages:
114             if not package in self.packages:
115                 self.packages[package] = []
116             self.packages[package].append(file_name)
117             rprovides += (bb_cache.getVar("RPROVIDES_%s" % package, file_name, 1) or "").split() 
118
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)
123
124         for rprovide in rprovides:
125             if not rprovide in self.rproviders:
126                 self.rproviders[rprovide] = []
127             self.rproviders[rprovide].append(file_name)
128
129         # Build hash of runtime depeneds and rececommends
130
131         def add_dep(deplist, deps):
132             for dep in deps:
133                 if not dep in deplist:
134                     deplist[dep] = ""
135
136         if not file_name in self.rundeps:
137             self.rundeps[file_name] = {}
138         if not file_name in self.runrecs:
139             self.runrecs[file_name] = {}
140
141         for package in packages + [pn]:
142             if not package in self.rundeps[file_name]:
143                 self.rundeps[file_name][package] = {}
144             if not package in self.runrecs[file_name]:
145                 self.runrecs[file_name][package] = {}
146
147             add_dep(self.rundeps[file_name][package], bb.utils.explode_deps(bb_cache.getVar('RDEPENDS', file_name, True) or ""))
148             add_dep(self.runrecs[file_name][package], bb.utils.explode_deps(bb_cache.getVar('RRECOMMENDS', file_name, True) or ""))
149             add_dep(self.rundeps[file_name][package], bb.utils.explode_deps(bb_cache.getVar("RDEPENDS_%s" % package, file_name, True) or ""))
150             add_dep(self.runrecs[file_name][package], bb.utils.explode_deps(bb_cache.getVar("RRECOMMENDS_%s" % package, file_name, True) or ""))
151
152         # Collect files we may need for possible world-dep
153         # calculations
154         if not bb_cache.getVar('BROKEN', file_name, True) and not bb_cache.getVar('EXCLUDE_FROM_WORLD', file_name, True):
155             self.possible_world.append(file_name)
156
157
158 #============================================================================#
159 # BBStatistics
160 #============================================================================#
161 class BBStatistics:
162     """
163     Manage build statistics for one run
164     """
165     def __init__(self ):
166         self.attempt = 0
167         self.success = 0
168         self.fail = 0
169         self.deps = 0
170
171     def show( self ):
172         print "Build statistics:"
173         print "  Attempted builds: %d" % self.attempt
174         if self.fail:
175             print "  Failed builds: %d" % self.fail
176         if self.deps:
177             print "  Dependencies not satisfied: %d" % self.deps
178         if self.fail or self.deps: return 1
179         else: return 0
180
181
182 #============================================================================#
183 # BBOptions
184 #============================================================================#
185 class BBConfiguration( object ):
186     """
187     Manages build options and configurations for one run
188     """
189     def __init__( self, options ):
190         for key, val in options.__dict__.items():
191             setattr( self, key, val )
192         self.data = data.init()
193
194 #============================================================================#
195 # BBCooker
196 #============================================================================#
197 class BBCooker:
198     """
199     Manages one bitbake build run
200     """
201
202     ParsingStatus = BBParsingStatus     # make it visible from the shell
203     Statistics = BBStatistics           # make it visible from the shell
204
205     def __init__( self ):
206         self.build_cache_fail = []
207         self.build_cache = []
208         self.rbuild_cache = []
209         self.building_list = []
210         self.build_path = []
211         self.consider_msgs_cache = []
212         self.preferred = {}
213         self.stats = BBStatistics()
214         self.status = None
215
216         self.cache = None
217         self.bb_cache = None
218
219     def tryBuildPackage( self, fn, item, the_data ):
220         """Build one package"""
221         bb.event.fire(bb.event.PkgStarted(item, the_data))
222         try:
223             self.stats.attempt += 1
224             if self.configuration.force:
225                 bb.data.setVarFlag('do_%s' % self.configuration.cmd, 'force', 1, the_data)
226             if not self.configuration.dry_run:
227                 bb.build.exec_task('do_%s' % self.configuration.cmd, the_data)
228             bb.event.fire(bb.event.PkgSucceeded(item, the_data))
229             self.build_cache.append(fn)
230             return True
231         except bb.build.FuncFailed:
232             self.stats.fail += 1
233             bb.error("task stack execution failed")
234             bb.event.fire(bb.event.PkgFailed(item, the_data))
235             self.build_cache_fail.append(fn)
236             raise
237         except bb.build.EventException, e:
238             self.stats.fail += 1
239             event = e.args[1]
240             bb.error("%s event exception, aborting" % bb.event.getName(event))
241             bb.event.fire(bb.event.PkgFailed(item, the_data))
242             self.build_cache_fail.append(fn)
243             raise
244
245     def tryBuild( self, fn, virtual , buildAllDeps , build_depends = []):
246         """
247         Build a provider and its dependencies. 
248         build_depends is a list of previous build dependencies (not runtime)
249         If build_depends is empty, we're dealing with a runtime depends
250         """
251
252         the_data = self.bb_cache.loadDataFull(fn, self)
253
254         if not buildAllDeps:
255             buildAllDeps = self.status.build_all[fn]
256
257         # Error on build time dependency loops
258         if build_depends and build_depends.count(fn) > 1:
259             bb.error("%s depends on itself (eventually)" % fn)
260             bb.error("upwards chain is: %s" % (" -> ".join(self.build_path)))
261             return False
262
263         # See if this is a runtime dependency we've already built
264         # Or a build dependency being handled in a different build chain
265         if fn in self.building_list:
266             return self.addRunDeps(fn, virtual , buildAllDeps)
267
268         item = self.status.pkg_fn[fn]
269
270         self.building_list.append(fn)
271
272         pathstr = "%s (%s)" % (item, virtual)
273         self.build_path.append(pathstr)
274
275         depends_list = (bb.data.getVar('DEPENDS', the_data, True) or "").split()
276
277         if self.configuration.verbose:
278             bb.note("current path: %s" % (" -> ".join(self.build_path)))
279             bb.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
280
281         try:
282             failed = False
283
284             depcmd = self.configuration.cmd
285             bbdepcmd = bb.data.getVarFlag('do_%s' % self.configuration.cmd, 'bbdepcmd', the_data)
286             if bbdepcmd is not None:
287                 if bbdepcmd == "":
288                     depcmd = None
289                 else:
290                     depcmd = bbdepcmd
291
292             if depcmd:
293                 oldcmd = self.configuration.cmd
294                 self.configuration.cmd = depcmd
295
296             for dependency in depends_list:
297                 if dependency in self.status.ignored_dependencies:
298                     continue
299                 if not depcmd:
300                     continue
301                 if self.buildProvider( dependency , buildAllDeps , build_depends ) == 0:
302                     bb.error("dependency %s (for %s) not satisfied" % (dependency,item))
303                     failed = True
304                     if self.configuration.abort:
305                         break
306
307             if depcmd:
308                 self.configuration.cmd = oldcmd
309
310             if failed:
311                 self.stats.deps += 1
312                 return False
313
314             if not self.addRunDeps(fn, virtual , buildAllDeps):
315                 return False
316
317             if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
318                 self.build_cache.append(fn)
319                 return True
320
321             return self.tryBuildPackage( fn, item, the_data )
322
323         finally:
324             self.building_list.remove(fn)
325             self.build_path.remove(pathstr)
326
327     def findBestProvider( self, pn, pkg_pn = None):
328         """
329         If there is a PREFERRED_VERSION, find the highest-priority bbfile
330         providing that version.  If not, find the latest version provided by
331         an bbfile in the highest-priority set.
332         """
333         if not pkg_pn:
334             pkg_pn = self.status.pkg_pn
335
336         files = pkg_pn[pn]
337         priorities = {}
338         for f in files:
339             priority = self.status.bbfile_priority[f]
340             if priority not in priorities:
341                 priorities[priority] = []
342             priorities[priority].append(f)
343         p_list = priorities.keys()
344         p_list.sort(lambda a, b: a - b)
345         tmp_pn = []
346         for p in p_list:
347             tmp_pn = [priorities[p]] + tmp_pn
348
349         preferred_file = None
350
351         localdata = data.createCopy(self.configuration.data)
352         bb.data.setVar('OVERRIDES', "%s:%s" % (pn, data.getVar('OVERRIDES', localdata)), localdata)
353         bb.data.update_data(localdata)
354
355         preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, True)
356         if preferred_v:
357             m = re.match('(.*)_(.*)', preferred_v)
358             if m:
359                 preferred_v = m.group(1)
360                 preferred_r = m.group(2)
361             else:
362                 preferred_r = None
363
364             for file_set in tmp_pn:
365                 for f in file_set:
366                     pv,pr = self.status.pkg_pvpr[f]
367                     if preferred_v == pv and (preferred_r == pr or preferred_r == None):
368                         preferred_file = f
369                         preferred_ver = (pv, pr)
370                         break
371                 if preferred_file:
372                     break;
373             if preferred_r:
374                 pv_str = '%s-%s' % (preferred_v, preferred_r)
375             else:
376                 pv_str = preferred_v
377             if preferred_file is None:
378                 bb.note("preferred version %s of %s not available" % (pv_str, pn))
379             else:
380                 bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
381
382         del localdata
383
384         # get highest priority file set
385         files = tmp_pn[0]
386         latest = None
387         latest_p = 0
388         latest_f = None
389         for file_name in files:
390             pv,pr = self.status.pkg_pvpr[file_name]
391             dp = self.status.pkg_dp[file_name]
392
393             if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
394                 latest = (pv, pr)
395                 latest_f = file_name
396                 latest_p = dp
397         if preferred_file is None:
398             preferred_file = latest_f
399             preferred_ver = latest
400
401         return (latest,latest_f,preferred_ver, preferred_file)
402
403     def showVersions( self ):
404         pkg_pn = self.status.pkg_pn
405         preferred_versions = {}
406         latest_versions = {}
407
408         # Sort by priority
409         for pn in pkg_pn.keys():
410             (last_ver,last_file,pref_ver,pref_file) = self.findBestProvider(pn)
411             preferred_versions[pn] = (pref_ver, pref_file)
412             latest_versions[pn] = (last_ver, last_file)
413
414         pkg_list = pkg_pn.keys()
415         pkg_list.sort()
416
417         for p in pkg_list:
418             pref = preferred_versions[p]
419             latest = latest_versions[p]
420
421             if pref != latest:
422                 prefstr = pref[0][0] + "-" + pref[0][1]
423             else:
424                 prefstr = ""
425
426             print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
427                                         prefstr)
428         
429
430     def showEnvironment( self ):
431         """Show the outer or per-package environment"""
432         if self.configuration.buildfile:
433             self.bb_cache = bb.cache.init(self)
434             try:
435                 self.configuration.data, fromCache = self.bb_cache.loadDataFull(self.configuration.buildfile, self)
436             except IOError, e:
437                 fatal("Unable to read %s: %s" % ( self.configuration.buildfile, e ))
438             except Exception, e:
439                 fatal("%s" % e)
440         # emit variables and shell functions
441         try:
442             data.update_data( self.configuration.data )
443             data.emit_env(sys.__stdout__, self.configuration.data, True)
444         except Exception, e:
445             fatal("%s" % e)
446         # emit the metadata which isnt valid shell
447         for e in self.configuration.data.keys():
448             if data.getVarFlag( e, 'python', self.configuration.data ):
449                 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
450
451     def filterProviders(self, providers, item):
452         """
453         Take a list of providers and filter/reorder according to the 
454         environment variables and previous build results
455         """
456         eligible = []
457         preferred_versions = {}
458
459         # Collate providers by PN
460         pkg_pn = {}
461         for p in providers:
462             pn = self.status.pkg_fn[p]
463             if pn not in pkg_pn:
464                 pkg_pn[pn] = []
465             pkg_pn[pn].append(p)
466
467         bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
468
469         for pn in pkg_pn.keys():
470             preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
471             eligible.append(preferred_versions[pn][1])
472
473         for p in eligible:
474             if p in self.build_cache_fail:
475                 bb.debug(1, "rejecting already-failed %s" % p)
476                 eligible.remove(p)
477
478         if len(eligible) == 0:
479             bb.error("no eligible providers for %s" % item)
480             return 0
481
482         # look to see if one of them is already staged, or marked as preferred.
483         # if so, bump it to the head of the queue
484         for p in providers:
485             pn = self.status.pkg_fn[p]
486             pv, pr = self.status.pkg_pvpr[p]
487
488             stamp = '%s.do_populate_staging' % self.status.stamp[p]
489             if os.path.exists(stamp):
490                 (newvers, fn) = preferred_versions[pn]
491                 if not fn in eligible:
492                     # package was made ineligible by already-failed check
493                     continue
494                 oldver = "%s-%s" % (pv, pr)
495                 newver = '-'.join(newvers)
496                 if (newver != oldver):
497                     extra_chat = "%s (%s) already staged but upgrading to %s to satisfy %s" % (pn, oldver, newver, item)
498                 else:
499                     extra_chat = "Selecting already-staged %s (%s) to satisfy %s" % (pn, oldver, item)
500                 if self.configuration.verbose:
501                     bb.note("%s" % extra_chat)
502                 eligible.remove(fn)
503                 eligible = [fn] + eligible
504                 discriminated = True
505                 break
506
507         return eligible
508
509     def buildProvider( self, item , buildAllDeps , build_depends = [] ):
510         """
511         Build something to provide a named build requirement
512         (takes item names from DEPENDS namespace)
513         """
514
515         fn = None
516         discriminated = False
517
518         if not item in self.status.providers:
519             bb.error("Nothing provides dependency %s" % item)
520             bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
521             return 0
522
523         all_p = self.status.providers[item]
524
525         for p in all_p:
526             if p in self.build_cache:
527                 bb.debug(1, "already built %s in this run\n" % p)
528                 return 1
529
530         eligible = self.filterProviders(all_p, item)
531
532         if not eligible:
533             return 0
534
535         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
536         if prefervar:
537             self.preferred[item] = prefervar
538
539         if item in self.preferred:
540             for p in eligible:
541                 pn = self.status.pkg_fn[p]
542                 if self.preferred[item] == pn:
543                     if self.configuration.verbose:
544                         bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
545                     eligible.remove(p)
546                     eligible = [p] + eligible
547                     discriminated = True
548                     break
549
550         if len(eligible) > 1 and discriminated == False:
551             if item not in self.consider_msgs_cache:
552                 providers_list = []
553                 for fn in eligible:
554                     providers_list.append(self.status.pkg_fn[fn])
555                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
556                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
557                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data))
558             self.consider_msgs_cache.append(item)
559
560
561         # run through the list until we find one that we can build
562         for fn in eligible:
563             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
564             if self.tryBuild(fn, item, buildAllDeps, build_depends + [fn]):
565                 return 1
566
567         bb.note("no buildable providers for %s" % item)
568         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
569         return 0
570
571     def buildRProvider( self, item , buildAllDeps ):
572         """
573         Build something to provide a named runtime requirement
574         (takes item names from RDEPENDS/PACKAGES namespace)
575         """
576
577         fn = None
578         all_p = []
579         discriminated = False
580
581         if not buildAllDeps:
582             return True
583
584         all_p = self.getProvidersRun(item)
585
586         if not all_p:
587             bb.error("Nothing provides runtime dependency %s" % (item))
588             bb.event.fire(bb.event.NoProvider(item,self.configuration.data,runtime=True))
589             return False
590
591         for p in all_p:
592             if p in self.rbuild_cache:
593                 bb.debug(2, "Already built %s providing runtime %s\n" % (p,item))
594                 return True
595             if p in self.build_cache:
596                 bb.debug(2, "Already built %s but adding any further RDEPENDS for %s\n" % (p, item))
597                 return self.addRunDeps(p, item , buildAllDeps)
598
599         eligible = self.filterProviders(all_p, item)
600         if not eligible:
601             return 0
602
603         preferred = []
604         for p in eligible:
605             pn = self.status.pkg_fn[p]
606             provides = self.status.pn_provides[pn]
607             for provide in provides:
608                 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, self.configuration.data, 1)
609                 if prefervar == pn:
610                     if self.configuration.verbose:
611                         bb.note("selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
612                     eligible.remove(p)
613                     eligible = [p] + eligible
614                     preferred.append(p)
615
616         if len(eligible) > 1 and len(preferred) == 0:
617             if item not in self.consider_msgs_cache:
618                 providers_list = []
619                 for fn in eligible:
620                     providers_list.append(self.status.pkg_fn[fn])
621                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
622                 bb.note("consider defining a PREFERRED_PROVIDER to match runtime %s" % item)
623                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
624             self.consider_msgs_cache.append(item)
625
626         if len(preferred) > 1:
627             if item not in self.consider_msgs_cache:
628                 providers_list = []
629                 for fn in preferred:
630                     providers_list.append(self.status.pkg_fn[fn])
631                 bb.note("multiple preferred providers are available (%s);" % ", ".join(providers_list))
632                 bb.note("consider defining only one PREFERRED_PROVIDER to match runtime %s" % item)
633                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
634             self.consider_msgs_cache.append(item)
635
636         # run through the list until we find one that we can build
637         for fn in eligible:
638             bb.debug(2, "selecting %s to satisfy runtime %s" % (fn, item))
639             if self.tryBuild(fn, item, buildAllDeps):
640                 return True
641
642         bb.error("No buildable providers for runtime %s" % item)
643         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
644         return False
645
646     def getProvidersRun(self, rdepend):
647         """
648         Return any potential providers of runtime rdepend
649         """
650         rproviders = []
651
652         if rdepend in self.status.rproviders:
653             rproviders += self.status.rproviders[rdepend]
654
655         if rdepend in self.status.packages:
656             rproviders += self.status.packages[rdepend]
657
658         if rproviders:
659             return rproviders
660
661         # Only search dynamic packages if we can't find anything in other variables
662         for pattern in self.status.packages_dynamic:
663             regexp = re.compile(pattern)
664             if regexp.match(rdepend):
665                 rproviders += self.status.packages_dynamic[pattern]
666
667         return rproviders
668
669     def addRunDeps(self , fn, item , buildAllDeps):
670         """
671         Add any runtime dependencies of runtime item provided by fn 
672         as long as item has't previously been processed by this function.
673         """
674
675         if item in self.rbuild_cache:
676             return True
677
678         if not buildAllDeps:
679             return True
680
681         rdepends = []
682         self.rbuild_cache.append(item)
683
684         if fn in self.status.rundeps and item in self.status.rundeps[fn]:
685             rdepends += self.status.rundeps[fn][item].keys()
686         if fn in self.status.runrecs and item in self.status.runrecs[fn]:
687             rdepends += self.status.runrecs[fn][item].keys()
688
689         bb.debug(2, "Additional runtime dependencies for %s are: %s" % (item, " ".join(rdepends)))
690
691         for rdepend in rdepends:
692             if rdepend in self.status.ignored_dependencies:
693                 continue
694             if not self.buildRProvider(rdepend, buildAllDeps):
695                 return False
696         return True
697
698     def buildDepgraph( self ):
699         all_depends = self.status.all_depends
700         pn_provides = self.status.pn_provides
701
702         def calc_bbfile_priority(filename):
703             for (regex, pri) in self.status.bbfile_config_priorities:
704                 if regex.match(filename):
705                     return pri
706             return 0
707
708         # Handle PREFERRED_PROVIDERS
709         for p in (bb.data.getVar('PREFERRED_PROVIDERS', self.configuration.data, 1) or "").split():
710             (providee, provider) = p.split(':')
711             if providee in self.preferred and self.preferred[providee] != provider:
712                 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
713             self.preferred[providee] = provider
714
715         # Calculate priorities for each file
716         for p in self.status.pkg_fn.keys():
717             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
718
719     def buildWorldTargetList(self):
720         """
721          Build package list for "bitbake world"
722         """
723         all_depends = self.status.all_depends
724         pn_provides = self.status.pn_provides
725         bb.debug(1, "collating packages for \"world\"")
726         for f in self.status.possible_world:
727             terminal = True
728             pn = self.status.pkg_fn[f]
729
730             for p in pn_provides[pn]:
731                 if p.startswith('virtual/'):
732                     bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
733                     terminal = False
734                     break
735                 for pf in self.status.providers[p]:
736                     if self.status.pkg_fn[pf] != pn:
737                         bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
738                         terminal = False
739                         break
740             if terminal:
741                 self.status.world_target.add(pn)
742
743             # drop reference count now
744             self.status.possible_world = None
745             self.status.all_depends    = None
746
747     def myProgressCallback( self, x, y, f, bb_cache, from_cache ):
748         # feed the status with new input
749
750         self.status.handle_bb_data(f, bb_cache, from_cache)
751
752         if bbdebug > 0:
753             return
754         if os.isatty(sys.stdout.fileno()):
755             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
756             sys.stdout.flush()
757         else:
758             if x == 1:
759                 sys.stdout.write("Parsing .bb files, please wait...")
760                 sys.stdout.flush()
761             if x == y:
762                 sys.stdout.write("done.")
763                 sys.stdout.flush()
764
765     def interactiveMode( self ):
766         """Drop off into a shell"""
767         try:
768             from bb import shell
769         except ImportError, details:
770             bb.fatal("Sorry, shell not available (%s)" % details )
771         else:
772             bb.data.update_data( self.configuration.data )
773             shell.start( self )
774             sys.exit( 0 )
775
776     def parseConfigurationFile( self, afile ):
777         try:
778             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
779         except IOError:
780             bb.fatal( "Unable to open %s" % afile )
781         except bb.parse.ParseError, details:
782             bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
783
784     def handleCollections( self, collections ):
785         """Handle collections"""
786         if collections:
787             collection_list = collections.split()
788             for c in collection_list:
789                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
790                 if regex == None:
791                     bb.error("BBFILE_PATTERN_%s not defined" % c)
792                     continue
793                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
794                 if priority == None:
795                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
796                     continue
797                 try:
798                     cre = re.compile(regex)
799                 except re.error:
800                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
801                     continue
802                 try:
803                     pri = int(priority)
804                     self.status.bbfile_config_priorities.append((cre, pri))
805                 except ValueError:
806                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
807
808
809     def cook( self, configuration, args ):
810         self.configuration = configuration
811
812         if not self.configuration.cmd:
813             self.configuration.cmd = "build"
814
815         if self.configuration.debug:
816             bb.debug_level = self.configuration.debug
817
818         self.configuration.data = bb.data.init()
819
820         for f in self.configuration.file:
821             self.parseConfigurationFile( f )
822
823         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
824
825         if self.configuration.show_environment:
826             self.showEnvironment()
827             sys.exit( 0 )
828
829         # inject custom variables
830         if not bb.data.getVar("BUILDNAME", self.configuration.data):
831             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
832         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
833
834         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
835
836         if self.configuration.interactive:
837             self.interactiveMode()
838
839         if self.configuration.buildfile is not None:
840             bf = os.path.abspath( self.configuration.buildfile )
841             try:
842                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
843             except IOError:
844                 bb.fatal("Unable to open %s" % bf)
845
846             item = bb.data.getVar('PN', bbfile_data, 1)
847             try:
848                 self.tryBuildPackage( bf, item, bbfile_data )
849             except bb.build.EventException:
850                 bb.error( "Build of '%s' failed" % item )
851
852             sys.exit( self.stats.show() )
853
854         # initialise the parsing status now we know we will need deps
855         self.status = BBParsingStatus()
856
857         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
858         self.status.ignored_dependencies = Set( ignore.split() )
859
860         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
861
862         pkgs_to_build = None
863         if args:
864             if not pkgs_to_build:
865                 pkgs_to_build = []
866             pkgs_to_build.extend(args)
867         if not pkgs_to_build:
868                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
869                 if bbpkgs:
870                         pkgs_to_build = bbpkgs.split()
871         if not pkgs_to_build and not self.configuration.show_versions \
872                              and not self.configuration.interactive \
873                              and not self.configuration.show_environment:
874                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
875                 print "for usage information."
876                 sys.exit(0)
877
878         # Import Psyco if available and not disabled
879         if not self.configuration.disable_psyco:
880             try:
881                 import psyco
882             except ImportError:
883                 if bbdebug == 0:
884                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
885             else:
886                 psyco.bind( self.collect_bbfiles )
887         else:
888             bb.note("You have disabled Psyco. This decreases performance.")
889
890         try:
891             bb.debug(1, "collecting .bb files")
892             self.collect_bbfiles( self.myProgressCallback )
893             bb.debug(1, "parsing complete")
894             if bbdebug == 0:
895                 print
896             if self.configuration.parse_only:
897                 print "Requested parsing .bb files only.  Exiting."
898                 return
899
900             bb.data.update_data( self.configuration.data )
901             self.buildDepgraph()
902
903             if self.configuration.show_versions:
904                 self.showVersions()
905                 sys.exit( 0 )
906             if 'world' in pkgs_to_build:
907                 self.buildWorldTargetList()
908                 pkgs_to_build.remove('world')
909                 for t in self.status.world_target:
910                     pkgs_to_build.append(t)
911
912             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.data))
913
914             failures = 0
915             for k in pkgs_to_build:
916                 failed = False
917                 try:
918                     if self.buildProvider( k , False ) == 0:
919                         # already diagnosed
920                         failed = True
921                 except bb.build.EventException:
922                     bb.error("Build of " + k + " failed")
923                     failed = True
924
925                 if failed:
926                     failures += failures
927                     if self.configuration.abort:
928                         sys.exit(1)
929
930             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data, failures))
931
932             sys.exit( self.stats.show() )
933
934         except KeyboardInterrupt:
935             print "\nNOTE: KeyboardInterrupt - Build not completed."
936             sys.exit(1)
937
938     def get_bbfiles( self, path = os.getcwd() ):
939         """Get list of default .bb files by reading out the current directory"""
940         contents = os.listdir(path)
941         bbfiles = []
942         for f in contents:
943             (root, ext) = os.path.splitext(f)
944             if ext == ".bb":
945                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
946         return bbfiles
947
948     def find_bbfiles( self, path ):
949         """Find all the .bb files in a directory (uses find)"""
950         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
951         try:
952             finddata = os.popen(findcmd)
953         except OSError:
954             return []
955         return finddata.readlines()
956
957     def collect_bbfiles( self, progressCallback ):
958         """Collect all available .bb build files"""
959         self.cb = progressCallback
960         parsed, cached, skipped, masked = 0, 0, 0, 0
961         self.bb_cache = bb.cache.init(self)
962
963         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
964         data.setVar("BBFILES", " ".join(files), self.configuration.data)
965
966         if not len(files):
967             files = self.get_bbfiles()
968
969         if not len(files):
970             bb.error("no files to build.")
971
972         newfiles = []
973         for f in files:
974             if os.path.isdir(f):
975                 dirfiles = self.find_bbfiles(f)
976                 if dirfiles:
977                     newfiles += dirfiles
978                     continue
979             newfiles += glob.glob(f) or [ f ]
980
981         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
982         try:
983             bbmask_compiled = re.compile(bbmask)
984         except sre_constants.error:
985             bb.fatal("BBMASK is not a valid regular expression.")
986
987         for i in xrange( len( newfiles ) ):
988             f = newfiles[i]
989             if bbmask and bbmask_compiled.search(f):
990                 bb.debug(1, "bbmake: skipping %s" % f)
991                 masked += 1
992                 continue
993             debug(1, "bbmake: parsing %s" % f)
994
995             # read a file's metadata
996             try:
997                 fromCache, skip = self.bb_cache.loadData(f, self)
998                 if skip:
999                     skipped += 1
1000                     #bb.note("Skipping %s" % f)
1001                     self.bb_cache.skip(f)
1002                     continue
1003                 elif fromCache: cached += 1
1004                 else: parsed += 1
1005                 deps = None
1006
1007                 # allow metadata files to add items to BBFILES
1008                 #data.update_data(self.pkgdata[f])
1009                 addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
1010                 if addbbfiles:
1011                     for aof in addbbfiles.split():
1012                         if not files.count(aof):
1013                             if not os.path.isabs(aof):
1014                                 aof = os.path.join(os.path.dirname(f),aof)
1015                             files.append(aof)
1016
1017                 # now inform the caller
1018                 if self.cb is not None:
1019                     self.cb( i + 1, len( newfiles ), f, self.bb_cache, fromCache )
1020
1021             except IOError, e:
1022                 self.bb_cache.remove(f)
1023                 bb.error("opening %s: %s" % (f, e))
1024                 pass
1025             except KeyboardInterrupt:
1026                 self.bb_cache.sync()
1027                 raise
1028             except Exception, e:
1029                 self.bb_cache.remove(f)
1030                 bb.error("%s while parsing %s" % (e, f))
1031             except:
1032                 self.bb_cache.remove(f)
1033                 raise
1034
1035         if self.cb is not None:
1036             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
1037
1038         self.bb_cache.sync()
1039
1040 #============================================================================#
1041 # main
1042 #============================================================================#
1043
1044 def main():
1045     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
1046     usage = """%prog [options] [package ...]
1047
1048 Executes the specified task (default is 'build') for a given set of BitBake files.
1049 It expects that BBFILES is defined, which is a space seperated list of files to
1050 be executed.  BBFILES does support wildcards.
1051 Default BBFILES are the .bb files in the current directory.""" )
1052
1053     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
1054                action = "store", dest = "buildfile", default = None )
1055
1056     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.",
1057                action = "store_false", dest = "abort", default = True )
1058
1059     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
1060                action = "store_true", dest = "force", default = False )
1061
1062     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
1063                action = "store_true", dest = "interactive", default = False )
1064
1065     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)",
1066                action = "store", dest = "cmd", default = "build" )
1067
1068     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
1069                action = "append", dest = "file", default = [] )
1070
1071     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
1072                action = "store_true", dest = "verbose", default = False )
1073
1074     parser.add_option( "-D", "--debug", help = "Increase the debug level",
1075                action = "count", dest="debug", default = 0)
1076
1077     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
1078                action = "store_true", dest = "dry_run", default = False )
1079
1080     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
1081                action = "store_true", dest = "parse_only", default = False )
1082
1083     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
1084                action = "store_true", dest = "disable_psyco", default = False )
1085
1086     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
1087                action = "store_true", dest = "show_versions", default = False )
1088
1089     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
1090                action = "store_true", dest = "show_environment", default = False )
1091
1092     options, args = parser.parse_args( sys.argv )
1093
1094     cooker = BBCooker()
1095     cooker.cook( BBConfiguration( options ), args[1:] )
1096
1097
1098
1099 if __name__ == "__main__":
1100     main()