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.5.0"
35
36 #============================================================================#
37 # BBParsingStatus
38 #============================================================================#
39 class BBParsingStatus:
40     """
41     The initial idea for this status class is to use the data when it is
42     already loaded instead of loading it from various place over and over
43     again.
44     """
45
46     def __init__(self):
47         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
193 #============================================================================#
194 # BBCooker
195 #============================================================================#
196 class BBCooker:
197     """
198     Manages one bitbake build run
199     """
200
201     ParsingStatus = BBParsingStatus     # make it visible from the shell
202     Statistics = BBStatistics           # make it visible from the shell
203
204     def __init__( self ):
205         self.build_cache_fail = []
206         self.build_cache = []
207         self.rbuild_cache = []
208         self.building_list = []
209         self.build_path = []
210         self.consider_msgs_cache = []
211         self.preferred = {}
212         self.stats = BBStatistics()
213         self.status = None
214
215         self.cache = None
216         self.bb_cache = None
217
218     def tryBuildPackage( self, fn, item, the_data ):
219         """Build one package"""
220         bb.event.fire(bb.event.PkgStarted(item, the_data))
221         try:
222             self.stats.attempt += 1
223             if self.configuration.force:
224                 bb.data.setVarFlag('do_%s' % self.configuration.cmd, 'force', 1, the_data)
225             if not self.configuration.dry_run:
226                 bb.build.exec_task('do_%s' % self.configuration.cmd, the_data)
227             bb.event.fire(bb.event.PkgSucceeded(item, the_data))
228             self.build_cache.append(fn)
229             return True
230         except bb.build.FuncFailed:
231             self.stats.fail += 1
232             bb.error("task stack execution failed")
233             bb.event.fire(bb.event.PkgFailed(item, the_data))
234             self.build_cache_fail.append(fn)
235             raise
236         except bb.build.EventException, e:
237             self.stats.fail += 1
238             event = e.args[1]
239             bb.error("%s event exception, aborting" % bb.event.getName(event))
240             bb.event.fire(bb.event.PkgFailed(item, the_data))
241             self.build_cache_fail.append(fn)
242             raise
243
244     def tryBuild( self, fn, virtual , buildAllDeps , build_depends = []):
245         """
246         Build a provider and its dependencies. 
247         build_depends is a list of previous build dependencies (not runtime)
248         If build_depends is empty, we're dealing with a runtime depends
249         """
250
251         the_data = self.bb_cache.loadDataFull(fn, self)
252
253         # Only follow all (runtime) dependencies if doing a build
254         if not buildAllDeps and self.configuration.cmd is "build":
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.cb = None
434             self.bb_cache = bb.cache.init(self)
435             try:
436                 self.configuration.data = self.bb_cache.loadDataFull(self.configuration.buildfile, self)
437             except IOError, e:
438                 fatal("Unable to read %s: %s" % ( self.configuration.buildfile, e ))
439             except Exception, e:
440                 fatal("%s" % e)
441         # emit variables and shell functions
442         try:
443             data.update_data( self.configuration.data )
444             data.emit_env(sys.__stdout__, self.configuration.data, True)
445         except Exception, e:
446             fatal("%s" % e)
447         # emit the metadata which isnt valid shell
448         for e in self.configuration.data.keys():
449             if data.getVarFlag( e, 'python', self.configuration.data ):
450                 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
451
452     def filterProviders(self, providers, item):
453         """
454         Take a list of providers and filter/reorder according to the 
455         environment variables and previous build results
456         """
457         eligible = []
458         preferred_versions = {}
459
460         # Collate providers by PN
461         pkg_pn = {}
462         for p in providers:
463             pn = self.status.pkg_fn[p]
464             if pn not in pkg_pn:
465                 pkg_pn[pn] = []
466             pkg_pn[pn].append(p)
467
468         bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
469
470         for pn in pkg_pn.keys():
471             preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
472             eligible.append(preferred_versions[pn][1])
473
474         for p in eligible:
475             if p in self.build_cache_fail:
476                 bb.debug(1, "rejecting already-failed %s" % p)
477                 eligible.remove(p)
478
479         if len(eligible) == 0:
480             bb.error("no eligible providers for %s" % item)
481             return 0
482
483         # look to see if one of them is already staged, or marked as preferred.
484         # if so, bump it to the head of the queue
485         for p in providers:
486             pn = self.status.pkg_fn[p]
487             pv, pr = self.status.pkg_pvpr[p]
488
489             stamp = '%s.do_populate_staging' % self.status.stamp[p]
490             if os.path.exists(stamp):
491                 (newvers, fn) = preferred_versions[pn]
492                 if not fn in eligible:
493                     # package was made ineligible by already-failed check
494                     continue
495                 oldver = "%s-%s" % (pv, pr)
496                 newver = '-'.join(newvers)
497                 if (newver != oldver):
498                     extra_chat = "%s (%s) already staged but upgrading to %s to satisfy %s" % (pn, oldver, newver, item)
499                 else:
500                     extra_chat = "Selecting already-staged %s (%s) to satisfy %s" % (pn, oldver, item)
501                 if self.configuration.verbose:
502                     bb.note("%s" % extra_chat)
503                 eligible.remove(fn)
504                 eligible = [fn] + eligible
505                 discriminated = True
506                 break
507
508         return eligible
509
510     def buildProvider( self, item , buildAllDeps , build_depends = [] ):
511         """
512         Build something to provide a named build requirement
513         (takes item names from DEPENDS namespace)
514         """
515
516         fn = None
517         discriminated = False
518
519         if not item in self.status.providers:
520             bb.error("Nothing provides dependency %s" % item)
521             bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
522             return 0
523
524         all_p = self.status.providers[item]
525
526         for p in all_p:
527             if p in self.build_cache:
528                 bb.debug(1, "already built %s in this run\n" % p)
529                 return 1
530
531         eligible = self.filterProviders(all_p, item)
532
533         if not eligible:
534             return 0
535
536         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
537         if prefervar:
538             self.preferred[item] = prefervar
539
540         if item in self.preferred:
541             for p in eligible:
542                 pn = self.status.pkg_fn[p]
543                 if self.preferred[item] == pn:
544                     if self.configuration.verbose:
545                         bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
546                     eligible.remove(p)
547                     eligible = [p] + eligible
548                     discriminated = True
549                     break
550
551         if len(eligible) > 1 and discriminated == False:
552             if item not in self.consider_msgs_cache:
553                 providers_list = []
554                 for fn in eligible:
555                     providers_list.append(self.status.pkg_fn[fn])
556                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
557                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
558                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data))
559             self.consider_msgs_cache.append(item)
560
561
562         # run through the list until we find one that we can build
563         for fn in eligible:
564             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
565             if self.tryBuild(fn, item, buildAllDeps, build_depends + [fn]):
566                 return 1
567
568         bb.note("no buildable providers for %s" % item)
569         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
570         return 0
571
572     def buildRProvider( self, item , buildAllDeps ):
573         """
574         Build something to provide a named runtime requirement
575         (takes item names from RDEPENDS/PACKAGES namespace)
576         """
577
578         fn = None
579         all_p = []
580         discriminated = False
581
582         if not buildAllDeps:
583             return True
584
585         all_p = self.getProvidersRun(item)
586
587         if not all_p:
588             bb.error("Nothing provides runtime dependency %s" % (item))
589             bb.event.fire(bb.event.NoProvider(item,self.configuration.data,runtime=True))
590             return False
591
592         for p in all_p:
593             if p in self.rbuild_cache:
594                 bb.debug(2, "Already built %s providing runtime %s\n" % (p,item))
595                 return True
596             if p in self.build_cache:
597                 bb.debug(2, "Already built %s but adding any further RDEPENDS for %s\n" % (p, item))
598                 return self.addRunDeps(p, item , buildAllDeps)
599
600         eligible = self.filterProviders(all_p, item)
601         if not eligible:
602             return 0
603
604         preferred = []
605         for p in eligible:
606             pn = self.status.pkg_fn[p]
607             provides = self.status.pn_provides[pn]
608             for provide in provides:
609                 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, self.configuration.data, 1)
610                 if prefervar == pn:
611                     if self.configuration.verbose:
612                         bb.note("selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
613                     eligible.remove(p)
614                     eligible = [p] + eligible
615                     preferred.append(p)
616
617         if len(eligible) > 1 and len(preferred) == 0:
618             if item not in self.consider_msgs_cache:
619                 providers_list = []
620                 for fn in eligible:
621                     providers_list.append(self.status.pkg_fn[fn])
622                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
623                 bb.note("consider defining a PREFERRED_PROVIDER to match runtime %s" % item)
624                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
625             self.consider_msgs_cache.append(item)
626
627         if len(preferred) > 1:
628             if item not in self.consider_msgs_cache:
629                 providers_list = []
630                 for fn in preferred:
631                     providers_list.append(self.status.pkg_fn[fn])
632                 bb.note("multiple preferred providers are available (%s);" % ", ".join(providers_list))
633                 bb.note("consider defining only one PREFERRED_PROVIDER to match runtime %s" % item)
634                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
635             self.consider_msgs_cache.append(item)
636
637         # run through the list until we find one that we can build
638         for fn in eligible:
639             bb.debug(2, "selecting %s to satisfy runtime %s" % (fn, item))
640             if self.tryBuild(fn, item, buildAllDeps):
641                 return True
642
643         bb.error("No buildable providers for runtime %s" % item)
644         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
645         return False
646
647     def getProvidersRun(self, rdepend):
648         """
649         Return any potential providers of runtime rdepend
650         """
651         rproviders = []
652
653         if rdepend in self.status.rproviders:
654             rproviders += self.status.rproviders[rdepend]
655
656         if rdepend in self.status.packages:
657             rproviders += self.status.packages[rdepend]
658
659         if rproviders:
660             return rproviders
661
662         # Only search dynamic packages if we can't find anything in other variables
663         for pattern in self.status.packages_dynamic:
664             regexp = re.compile(pattern)
665             if regexp.match(rdepend):
666                 rproviders += self.status.packages_dynamic[pattern]
667
668         return rproviders
669
670     def addRunDeps(self , fn, item , buildAllDeps):
671         """
672         Add any runtime dependencies of runtime item provided by fn 
673         as long as item has't previously been processed by this function.
674         """
675
676         if item in self.rbuild_cache:
677             return True
678
679         if not buildAllDeps:
680             return True
681
682         rdepends = []
683         self.rbuild_cache.append(item)
684
685         if fn in self.status.rundeps and item in self.status.rundeps[fn]:
686             rdepends += self.status.rundeps[fn][item].keys()
687         if fn in self.status.runrecs and item in self.status.runrecs[fn]:
688             rdepends += self.status.runrecs[fn][item].keys()
689
690         bb.debug(2, "Additional runtime dependencies for %s are: %s" % (item, " ".join(rdepends)))
691
692         for rdepend in rdepends:
693             if rdepend in self.status.ignored_dependencies:
694                 continue
695             if not self.buildRProvider(rdepend, buildAllDeps):
696                 return False
697         return True
698
699     def buildDepgraph( self ):
700         all_depends = self.status.all_depends
701         pn_provides = self.status.pn_provides
702
703         localdata = data.createCopy(self.configuration.data)
704         bb.data.update_data(localdata)
705
706         def calc_bbfile_priority(filename):
707             for (regex, pri) in self.status.bbfile_config_priorities:
708                 if regex.match(filename):
709                     return pri
710             return 0
711
712         # Handle PREFERRED_PROVIDERS
713         for p in (bb.data.getVar('PREFERRED_PROVIDERS', localdata, 1) or "").split():
714             (providee, provider) = p.split(':')
715             if providee in self.preferred and self.preferred[providee] != provider:
716                 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
717             self.preferred[providee] = provider
718
719         # Calculate priorities for each file
720         for p in self.status.pkg_fn.keys():
721             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
722
723     def buildWorldTargetList(self):
724         """
725          Build package list for "bitbake world"
726         """
727         all_depends = self.status.all_depends
728         pn_provides = self.status.pn_provides
729         bb.debug(1, "collating packages for \"world\"")
730         for f in self.status.possible_world:
731             terminal = True
732             pn = self.status.pkg_fn[f]
733
734             for p in pn_provides[pn]:
735                 if p.startswith('virtual/'):
736                     bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
737                     terminal = False
738                     break
739                 for pf in self.status.providers[p]:
740                     if self.status.pkg_fn[pf] != pn:
741                         bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
742                         terminal = False
743                         break
744             if terminal:
745                 self.status.world_target.add(pn)
746
747             # drop reference count now
748             self.status.possible_world = None
749             self.status.all_depends    = None
750
751     def myProgressCallback( self, x, y, f, bb_cache, from_cache ):
752         # feed the status with new input
753
754         self.status.handle_bb_data(f, bb_cache, from_cache)
755
756         if bbdebug > 0:
757             return
758         if os.isatty(sys.stdout.fileno()):
759             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
760             sys.stdout.flush()
761         else:
762             if x == 1:
763                 sys.stdout.write("Parsing .bb files, please wait...")
764                 sys.stdout.flush()
765             if x == y:
766                 sys.stdout.write("done.")
767                 sys.stdout.flush()
768
769     def interactiveMode( self ):
770         """Drop off into a shell"""
771         try:
772             from bb import shell
773         except ImportError, details:
774             bb.fatal("Sorry, shell not available (%s)" % details )
775         else:
776             bb.data.update_data( self.configuration.data )
777             shell.start( self )
778             sys.exit( 0 )
779
780     def parseConfigurationFile( self, afile ):
781         try:
782             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
783
784             # Add the handlers we inherited by INHERITS
785             # FIXME: This assumes that we included at least one .inc file
786             for var in bb.data.keys(self.configuration.data):
787                 if bb.data.getVarFlag(var, 'handler', self.configuration.data):
788                     bb.event.register(var,bb.data.getVar(var,self.configuration.data))
789
790         except IOError:
791             bb.fatal( "Unable to open %s" % afile )
792         except bb.parse.ParseError, details:
793             bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
794
795     def handleCollections( self, collections ):
796         """Handle collections"""
797         if collections:
798             collection_list = collections.split()
799             for c in collection_list:
800                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
801                 if regex == None:
802                     bb.error("BBFILE_PATTERN_%s not defined" % c)
803                     continue
804                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
805                 if priority == None:
806                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
807                     continue
808                 try:
809                     cre = re.compile(regex)
810                 except re.error:
811                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
812                     continue
813                 try:
814                     pri = int(priority)
815                     self.status.bbfile_config_priorities.append((cre, pri))
816                 except ValueError:
817                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
818
819
820     def cook( self, configuration, args ):
821         """
822         We are building stuff here. We do the building
823         from here. By default we try to execute task
824         build.
825         """
826
827         self.configuration = configuration
828
829         if not self.configuration.cmd:
830             self.configuration.cmd = "build"
831
832         if self.configuration.debug:
833             bb.debug_level = self.configuration.debug
834
835         self.configuration.data = bb.data.init()
836
837         for f in self.configuration.file:
838             self.parseConfigurationFile( f )
839
840         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
841
842
843         #
844         # Special updated configuration we use for firing events
845         #
846         self.configuration.event_data = bb.data.createCopy(self.configuration.data)
847         bb.data.update_data(self.configuration.event_data)
848
849         if self.configuration.show_environment:
850             self.showEnvironment()
851             sys.exit( 0 )
852
853         # inject custom variables
854         if not bb.data.getVar("BUILDNAME", self.configuration.data):
855             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
856         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
857
858         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
859
860         if self.configuration.interactive:
861             self.interactiveMode()
862
863         if self.configuration.buildfile is not None:
864             bf = os.path.abspath( self.configuration.buildfile )
865             try:
866                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
867             except IOError:
868                 bb.fatal("Unable to open %s" % bf)
869
870             item = bb.data.getVar('PN', bbfile_data, 1)
871             try:
872                 self.tryBuildPackage( bf, item, bbfile_data )
873             except bb.build.EventException:
874                 bb.error( "Build of '%s' failed" % item )
875
876             sys.exit( self.stats.show() )
877
878         # initialise the parsing status now we know we will need deps
879         self.status = BBParsingStatus()
880
881         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
882         self.status.ignored_dependencies = Set( ignore.split() )
883
884         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
885
886         pkgs_to_build = None
887         if args:
888             if not pkgs_to_build:
889                 pkgs_to_build = []
890             pkgs_to_build.extend(args)
891         if not pkgs_to_build:
892                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
893                 if bbpkgs:
894                         pkgs_to_build = bbpkgs.split()
895         if not pkgs_to_build and not self.configuration.show_versions \
896                              and not self.configuration.interactive \
897                              and not self.configuration.show_environment:
898                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
899                 print "for usage information."
900                 sys.exit(0)
901
902         # Import Psyco if available and not disabled
903         if not self.configuration.disable_psyco:
904             try:
905                 import psyco
906             except ImportError:
907                 if bbdebug == 0:
908                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
909             else:
910                 psyco.bind( self.collect_bbfiles )
911         else:
912             bb.note("You have disabled Psyco. This decreases performance.")
913
914         try:
915             bb.debug(1, "collecting .bb files")
916             self.collect_bbfiles( self.myProgressCallback )
917             bb.debug(1, "parsing complete")
918             if bbdebug == 0:
919                 print
920             if self.configuration.parse_only:
921                 print "Requested parsing .bb files only.  Exiting."
922                 return
923
924             self.buildDepgraph()
925
926             if self.configuration.show_versions:
927                 self.showVersions()
928                 sys.exit( 0 )
929             if 'world' in pkgs_to_build:
930                 self.buildWorldTargetList()
931                 pkgs_to_build.remove('world')
932                 for t in self.status.world_target:
933                     pkgs_to_build.append(t)
934
935             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data))
936
937             failures = 0
938             for k in pkgs_to_build:
939                 failed = False
940                 try:
941                     if self.buildProvider( k , False ) == 0:
942                         # already diagnosed
943                         failed = True
944                 except bb.build.EventException:
945                     bb.error("Build of " + k + " failed")
946                     failed = True
947
948                 if failed:
949                     failures += failures
950                     if self.configuration.abort:
951                         sys.exit(1)
952
953             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures))
954
955             sys.exit( self.stats.show() )
956
957         except KeyboardInterrupt:
958             print "\nNOTE: KeyboardInterrupt - Build not completed."
959             sys.exit(1)
960
961     def get_bbfiles( self, path = os.getcwd() ):
962         """Get list of default .bb files by reading out the current directory"""
963         contents = os.listdir(path)
964         bbfiles = []
965         for f in contents:
966             (root, ext) = os.path.splitext(f)
967             if ext == ".bb":
968                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
969         return bbfiles
970
971     def find_bbfiles( self, path ):
972         """Find all the .bb files in a directory (uses find)"""
973         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
974         try:
975             finddata = os.popen(findcmd)
976         except OSError:
977             return []
978         return finddata.readlines()
979
980     def collect_bbfiles( self, progressCallback ):
981         """Collect all available .bb build files"""
982         self.cb = progressCallback
983         parsed, cached, skipped, masked = 0, 0, 0, 0
984         self.bb_cache = bb.cache.init(self)
985
986         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
987         data.setVar("BBFILES", " ".join(files), self.configuration.data)
988
989         if not len(files):
990             files = self.get_bbfiles()
991
992         if not len(files):
993             bb.error("no files to build.")
994
995         newfiles = []
996         for f in files:
997             if os.path.isdir(f):
998                 dirfiles = self.find_bbfiles(f)
999                 if dirfiles:
1000                     newfiles += dirfiles
1001                     continue
1002             newfiles += glob.glob(f) or [ f ]
1003
1004         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
1005         try:
1006             bbmask_compiled = re.compile(bbmask)
1007         except sre_constants.error:
1008             bb.fatal("BBMASK is not a valid regular expression.")
1009
1010         for i in xrange( len( newfiles ) ):
1011             f = newfiles[i]
1012             if bbmask and bbmask_compiled.search(f):
1013                 bb.debug(1, "bbmake: skipping %s" % f)
1014                 masked += 1
1015                 continue
1016             debug(1, "bbmake: parsing %s" % f)
1017
1018             # read a file's metadata
1019             try:
1020                 fromCache, skip = self.bb_cache.loadData(f, self)
1021                 if skip:
1022                     skipped += 1
1023                     #bb.note("Skipping %s" % f)
1024                     self.bb_cache.skip(f)
1025                     continue
1026                 elif fromCache: cached += 1
1027                 else: parsed += 1
1028                 deps = None
1029
1030                 # allow metadata files to add items to BBFILES
1031                 #data.update_data(self.pkgdata[f])
1032                 addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
1033                 if addbbfiles:
1034                     for aof in addbbfiles.split():
1035                         if not files.count(aof):
1036                             if not os.path.isabs(aof):
1037                                 aof = os.path.join(os.path.dirname(f),aof)
1038                             files.append(aof)
1039
1040                 # now inform the caller
1041                 if self.cb is not None:
1042                     self.cb( i + 1, len( newfiles ), f, self.bb_cache, fromCache )
1043
1044             except IOError, e:
1045                 self.bb_cache.remove(f)
1046                 bb.error("opening %s: %s" % (f, e))
1047                 pass
1048             except KeyboardInterrupt:
1049                 self.bb_cache.sync()
1050                 raise
1051             except Exception, e:
1052                 self.bb_cache.remove(f)
1053                 bb.error("%s while parsing %s" % (e, f))
1054             except:
1055                 self.bb_cache.remove(f)
1056                 raise
1057
1058         if self.cb is not None:
1059             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
1060
1061         self.bb_cache.sync()
1062
1063 #============================================================================#
1064 # main
1065 #============================================================================#
1066
1067 def main():
1068     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
1069     usage = """%prog [options] [package ...]
1070
1071 Executes the specified task (default is 'build') for a given set of BitBake files.
1072 It expects that BBFILES is defined, which is a space seperated list of files to
1073 be executed.  BBFILES does support wildcards.
1074 Default BBFILES are the .bb files in the current directory.""" )
1075
1076     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
1077                action = "store", dest = "buildfile", default = None )
1078
1079     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.",
1080                action = "store_false", dest = "abort", default = True )
1081
1082     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
1083                action = "store_true", dest = "force", default = False )
1084
1085     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
1086                action = "store_true", dest = "interactive", default = False )
1087
1088     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)",
1089                action = "store", dest = "cmd", default = "build" )
1090
1091     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
1092                action = "append", dest = "file", default = [] )
1093
1094     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
1095                action = "store_true", dest = "verbose", default = False )
1096
1097     parser.add_option( "-D", "--debug", help = "Increase the debug level",
1098                action = "count", dest="debug", default = 0)
1099
1100     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
1101                action = "store_true", dest = "dry_run", default = False )
1102
1103     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
1104                action = "store_true", dest = "parse_only", default = False )
1105
1106     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
1107                action = "store_true", dest = "disable_psyco", default = False )
1108
1109     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
1110                action = "store_true", dest = "show_versions", default = False )
1111
1112     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
1113                action = "store_true", dest = "show_environment", default = False )
1114
1115     options, args = parser.parse_args( sys.argv )
1116
1117     cooker = BBCooker()
1118     cooker.cook( BBConfiguration( options ), args[1:] )
1119
1120
1121
1122 if __name__ == "__main__":
1123     print """WARNING, WARNING, WARNING
1124 This is a Bitbake from the Unstable/Development Branch.
1125 You might want to use the bitbake-1.4 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."""
1126     import time
1127     time.sleep(5)
1128     main()