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.4.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         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         # Only follow all (runtime) dependencies if doing a build
255         if not buildAllDeps and self.configuration.cmd is "build":
256             buildAllDeps = self.status.build_all[fn]
257
258         # Error on build time dependency loops
259         if build_depends and build_depends.count(fn) > 1:
260             bb.error("%s depends on itself (eventually)" % fn)
261             bb.error("upwards chain is: %s" % (" -> ".join(self.build_path)))
262             return False
263
264         # See if this is a runtime dependency we've already built
265         # Or a build dependency being handled in a different build chain
266         if fn in self.building_list:
267             return self.addRunDeps(fn, virtual , buildAllDeps)
268
269         item = self.status.pkg_fn[fn]
270
271         self.building_list.append(fn)
272
273         pathstr = "%s (%s)" % (item, virtual)
274         self.build_path.append(pathstr)
275
276         depends_list = (bb.data.getVar('DEPENDS', the_data, True) or "").split()
277
278         if self.configuration.verbose:
279             bb.note("current path: %s" % (" -> ".join(self.build_path)))
280             bb.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
281
282         try:
283             failed = False
284
285             depcmd = self.configuration.cmd
286             bbdepcmd = bb.data.getVarFlag('do_%s' % self.configuration.cmd, 'bbdepcmd', the_data)
287             if bbdepcmd is not None:
288                 if bbdepcmd == "":
289                     depcmd = None
290                 else:
291                     depcmd = bbdepcmd
292
293             if depcmd:
294                 oldcmd = self.configuration.cmd
295                 self.configuration.cmd = depcmd
296
297             for dependency in depends_list:
298                 if dependency in self.status.ignored_dependencies:
299                     continue
300                 if not depcmd:
301                     continue
302                 if self.buildProvider( dependency , buildAllDeps , build_depends ) == 0:
303                     bb.error("dependency %s (for %s) not satisfied" % (dependency,item))
304                     failed = True
305                     if self.configuration.abort:
306                         break
307
308             if depcmd:
309                 self.configuration.cmd = oldcmd
310
311             if failed:
312                 self.stats.deps += 1
313                 return False
314
315             if not self.addRunDeps(fn, virtual , buildAllDeps):
316                 return False
317
318             if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
319                 self.build_cache.append(fn)
320                 return True
321
322             return self.tryBuildPackage( fn, item, the_data )
323
324         finally:
325             self.building_list.remove(fn)
326             self.build_path.remove(pathstr)
327
328     def findBestProvider( self, pn, pkg_pn = None):
329         """
330         If there is a PREFERRED_VERSION, find the highest-priority bbfile
331         providing that version.  If not, find the latest version provided by
332         an bbfile in the highest-priority set.
333         """
334         if not pkg_pn:
335             pkg_pn = self.status.pkg_pn
336
337         files = pkg_pn[pn]
338         priorities = {}
339         for f in files:
340             priority = self.status.bbfile_priority[f]
341             if priority not in priorities:
342                 priorities[priority] = []
343             priorities[priority].append(f)
344         p_list = priorities.keys()
345         p_list.sort(lambda a, b: a - b)
346         tmp_pn = []
347         for p in p_list:
348             tmp_pn = [priorities[p]] + tmp_pn
349
350         preferred_file = None
351
352         localdata = data.createCopy(self.configuration.data)
353         bb.data.setVar('OVERRIDES', "%s:%s" % (pn, data.getVar('OVERRIDES', localdata)), localdata)
354         bb.data.update_data(localdata)
355
356         preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, True)
357         if preferred_v:
358             m = re.match('(.*)_(.*)', preferred_v)
359             if m:
360                 preferred_v = m.group(1)
361                 preferred_r = m.group(2)
362             else:
363                 preferred_r = None
364
365             for file_set in tmp_pn:
366                 for f in file_set:
367                     pv,pr = self.status.pkg_pvpr[f]
368                     if preferred_v == pv and (preferred_r == pr or preferred_r == None):
369                         preferred_file = f
370                         preferred_ver = (pv, pr)
371                         break
372                 if preferred_file:
373                     break;
374             if preferred_r:
375                 pv_str = '%s-%s' % (preferred_v, preferred_r)
376             else:
377                 pv_str = preferred_v
378             if preferred_file is None:
379                 bb.note("preferred version %s of %s not available" % (pv_str, pn))
380             else:
381                 bb.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
382
383         del localdata
384
385         # get highest priority file set
386         files = tmp_pn[0]
387         latest = None
388         latest_p = 0
389         latest_f = None
390         for file_name in files:
391             pv,pr = self.status.pkg_pvpr[file_name]
392             dp = self.status.pkg_dp[file_name]
393
394             if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
395                 latest = (pv, pr)
396                 latest_f = file_name
397                 latest_p = dp
398         if preferred_file is None:
399             preferred_file = latest_f
400             preferred_ver = latest
401
402         return (latest,latest_f,preferred_ver, preferred_file)
403
404     def showVersions( self ):
405         pkg_pn = self.status.pkg_pn
406         preferred_versions = {}
407         latest_versions = {}
408
409         # Sort by priority
410         for pn in pkg_pn.keys():
411             (last_ver,last_file,pref_ver,pref_file) = self.findBestProvider(pn)
412             preferred_versions[pn] = (pref_ver, pref_file)
413             latest_versions[pn] = (last_ver, last_file)
414
415         pkg_list = pkg_pn.keys()
416         pkg_list.sort()
417
418         for p in pkg_list:
419             pref = preferred_versions[p]
420             latest = latest_versions[p]
421
422             if pref != latest:
423                 prefstr = pref[0][0] + "-" + pref[0][1]
424             else:
425                 prefstr = ""
426
427             print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
428                                         prefstr)
429         
430
431     def showEnvironment( self ):
432         """Show the outer or per-package environment"""
433         if self.configuration.buildfile:
434             self.cb = None
435             self.bb_cache = bb.cache.init(self)
436             try:
437                 self.configuration.data = self.bb_cache.loadDataFull(self.configuration.buildfile, self)
438             except IOError, e:
439                 fatal("Unable to read %s: %s" % ( self.configuration.buildfile, e ))
440             except Exception, e:
441                 fatal("%s" % e)
442         # emit variables and shell functions
443         try:
444             data.update_data( self.configuration.data )
445             data.emit_env(sys.__stdout__, self.configuration.data, True)
446         except Exception, e:
447             fatal("%s" % e)
448         # emit the metadata which isnt valid shell
449         for e in self.configuration.data.keys():
450             if data.getVarFlag( e, 'python', self.configuration.data ):
451                 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
452
453     def filterProviders(self, providers, item):
454         """
455         Take a list of providers and filter/reorder according to the 
456         environment variables and previous build results
457         """
458         eligible = []
459         preferred_versions = {}
460
461         # Collate providers by PN
462         pkg_pn = {}
463         for p in providers:
464             pn = self.status.pkg_fn[p]
465             if pn not in pkg_pn:
466                 pkg_pn[pn] = []
467             pkg_pn[pn].append(p)
468
469         bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
470
471         for pn in pkg_pn.keys():
472             preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
473             eligible.append(preferred_versions[pn][1])
474
475         for p in eligible:
476             if p in self.build_cache_fail:
477                 bb.debug(1, "rejecting already-failed %s" % p)
478                 eligible.remove(p)
479
480         if len(eligible) == 0:
481             bb.error("no eligible providers for %s" % item)
482             return 0
483
484         # look to see if one of them is already staged, or marked as preferred.
485         # if so, bump it to the head of the queue
486         for p in providers:
487             pn = self.status.pkg_fn[p]
488             pv, pr = self.status.pkg_pvpr[p]
489
490             stamp = '%s.do_populate_staging' % self.status.stamp[p]
491             if os.path.exists(stamp):
492                 (newvers, fn) = preferred_versions[pn]
493                 if not fn in eligible:
494                     # package was made ineligible by already-failed check
495                     continue
496                 oldver = "%s-%s" % (pv, pr)
497                 newver = '-'.join(newvers)
498                 if (newver != oldver):
499                     extra_chat = "%s (%s) already staged but upgrading to %s to satisfy %s" % (pn, oldver, newver, item)
500                 else:
501                     extra_chat = "Selecting already-staged %s (%s) to satisfy %s" % (pn, oldver, item)
502                 if self.configuration.verbose:
503                     bb.note("%s" % extra_chat)
504                 eligible.remove(fn)
505                 eligible = [fn] + eligible
506                 discriminated = True
507                 break
508
509         return eligible
510
511     def buildProvider( self, item , buildAllDeps , build_depends = [] ):
512         """
513         Build something to provide a named build requirement
514         (takes item names from DEPENDS namespace)
515         """
516
517         fn = None
518         discriminated = False
519
520         if not item in self.status.providers:
521             bb.error("Nothing provides dependency %s" % item)
522             bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
523             return 0
524
525         all_p = self.status.providers[item]
526
527         for p in all_p:
528             if p in self.build_cache:
529                 bb.debug(1, "already built %s in this run\n" % p)
530                 return 1
531
532         eligible = self.filterProviders(all_p, item)
533
534         if not eligible:
535             return 0
536
537         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
538         if prefervar:
539             self.preferred[item] = prefervar
540
541         if item in self.preferred:
542             for p in eligible:
543                 pn = self.status.pkg_fn[p]
544                 if self.preferred[item] == pn:
545                     if self.configuration.verbose:
546                         bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
547                     eligible.remove(p)
548                     eligible = [p] + eligible
549                     discriminated = True
550                     break
551
552         if len(eligible) > 1 and discriminated == False:
553             if item not in self.consider_msgs_cache:
554                 providers_list = []
555                 for fn in eligible:
556                     providers_list.append(self.status.pkg_fn[fn])
557                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
558                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
559                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data))
560             self.consider_msgs_cache.append(item)
561
562
563         # run through the list until we find one that we can build
564         for fn in eligible:
565             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
566             if self.tryBuild(fn, item, buildAllDeps, build_depends + [fn]):
567                 return 1
568
569         bb.note("no buildable providers for %s" % item)
570         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
571         return 0
572
573     def buildRProvider( self, item , buildAllDeps ):
574         """
575         Build something to provide a named runtime requirement
576         (takes item names from RDEPENDS/PACKAGES namespace)
577         """
578
579         fn = None
580         all_p = []
581         discriminated = False
582
583         if not buildAllDeps:
584             return True
585
586         all_p = self.getProvidersRun(item)
587
588         if not all_p:
589             bb.error("Nothing provides runtime dependency %s" % (item))
590             bb.event.fire(bb.event.NoProvider(item,self.configuration.data,runtime=True))
591             return False
592
593         for p in all_p:
594             if p in self.rbuild_cache:
595                 bb.debug(2, "Already built %s providing runtime %s\n" % (p,item))
596                 return True
597             if p in self.build_cache:
598                 bb.debug(2, "Already built %s but adding any further RDEPENDS for %s\n" % (p, item))
599                 return self.addRunDeps(p, item , buildAllDeps)
600
601         eligible = self.filterProviders(all_p, item)
602         if not eligible:
603             return 0
604
605         preferred = []
606         for p in eligible:
607             pn = self.status.pkg_fn[p]
608             provides = self.status.pn_provides[pn]
609             for provide in provides:
610                 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, self.configuration.data, 1)
611                 if prefervar == pn:
612                     if self.configuration.verbose:
613                         bb.note("selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
614                     eligible.remove(p)
615                     eligible = [p] + eligible
616                     preferred.append(p)
617
618         if len(eligible) > 1 and len(preferred) == 0:
619             if item not in self.consider_msgs_cache:
620                 providers_list = []
621                 for fn in eligible:
622                     providers_list.append(self.status.pkg_fn[fn])
623                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
624                 bb.note("consider defining a PREFERRED_PROVIDER to match runtime %s" % item)
625                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
626             self.consider_msgs_cache.append(item)
627
628         if len(preferred) > 1:
629             if item not in self.consider_msgs_cache:
630                 providers_list = []
631                 for fn in preferred:
632                     providers_list.append(self.status.pkg_fn[fn])
633                 bb.note("multiple preferred providers are available (%s);" % ", ".join(providers_list))
634                 bb.note("consider defining only one PREFERRED_PROVIDER to match runtime %s" % item)
635                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
636             self.consider_msgs_cache.append(item)
637
638         # run through the list until we find one that we can build
639         for fn in eligible:
640             bb.debug(2, "selecting %s to satisfy runtime %s" % (fn, item))
641             if self.tryBuild(fn, item, buildAllDeps):
642                 return True
643
644         bb.error("No buildable providers for runtime %s" % item)
645         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
646         return False
647
648     def getProvidersRun(self, rdepend):
649         """
650         Return any potential providers of runtime rdepend
651         """
652         rproviders = []
653
654         if rdepend in self.status.rproviders:
655             rproviders += self.status.rproviders[rdepend]
656
657         if rdepend in self.status.packages:
658             rproviders += self.status.packages[rdepend]
659
660         if rproviders:
661             return rproviders
662
663         # Only search dynamic packages if we can't find anything in other variables
664         for pattern in self.status.packages_dynamic:
665             regexp = re.compile(pattern)
666             if regexp.match(rdepend):
667                 rproviders += self.status.packages_dynamic[pattern]
668
669         return rproviders
670
671     def addRunDeps(self , fn, item , buildAllDeps):
672         """
673         Add any runtime dependencies of runtime item provided by fn 
674         as long as item has't previously been processed by this function.
675         """
676
677         if item in self.rbuild_cache:
678             return True
679
680         if not buildAllDeps:
681             return True
682
683         rdepends = []
684         self.rbuild_cache.append(item)
685
686         if fn in self.status.rundeps and item in self.status.rundeps[fn]:
687             rdepends += self.status.rundeps[fn][item].keys()
688         if fn in self.status.runrecs and item in self.status.runrecs[fn]:
689             rdepends += self.status.runrecs[fn][item].keys()
690
691         bb.debug(2, "Additional runtime dependencies for %s are: %s" % (item, " ".join(rdepends)))
692
693         for rdepend in rdepends:
694             if rdepend in self.status.ignored_dependencies:
695                 continue
696             if not self.buildRProvider(rdepend, buildAllDeps):
697                 return False
698         return True
699
700     def buildDepgraph( self ):
701         all_depends = self.status.all_depends
702         pn_provides = self.status.pn_provides
703
704         def calc_bbfile_priority(filename):
705             for (regex, pri) in self.status.bbfile_config_priorities:
706                 if regex.match(filename):
707                     return pri
708             return 0
709
710         # Handle PREFERRED_PROVIDERS
711         for p in (bb.data.getVar('PREFERRED_PROVIDERS', self.configuration.data, 1) or "").split():
712             (providee, provider) = p.split(':')
713             if providee in self.preferred and self.preferred[providee] != provider:
714                 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
715             self.preferred[providee] = provider
716
717         # Calculate priorities for each file
718         for p in self.status.pkg_fn.keys():
719             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
720
721     def buildWorldTargetList(self):
722         """
723          Build package list for "bitbake world"
724         """
725         all_depends = self.status.all_depends
726         pn_provides = self.status.pn_provides
727         bb.debug(1, "collating packages for \"world\"")
728         for f in self.status.possible_world:
729             terminal = True
730             pn = self.status.pkg_fn[f]
731
732             for p in pn_provides[pn]:
733                 if p.startswith('virtual/'):
734                     bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
735                     terminal = False
736                     break
737                 for pf in self.status.providers[p]:
738                     if self.status.pkg_fn[pf] != pn:
739                         bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
740                         terminal = False
741                         break
742             if terminal:
743                 self.status.world_target.add(pn)
744
745             # drop reference count now
746             self.status.possible_world = None
747             self.status.all_depends    = None
748
749     def myProgressCallback( self, x, y, f, bb_cache, from_cache ):
750         # feed the status with new input
751
752         self.status.handle_bb_data(f, bb_cache, from_cache)
753
754         if bbdebug > 0:
755             return
756         if os.isatty(sys.stdout.fileno()):
757             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
758             sys.stdout.flush()
759         else:
760             if x == 1:
761                 sys.stdout.write("Parsing .bb files, please wait...")
762                 sys.stdout.flush()
763             if x == y:
764                 sys.stdout.write("done.")
765                 sys.stdout.flush()
766
767     def interactiveMode( self ):
768         """Drop off into a shell"""
769         try:
770             from bb import shell
771         except ImportError, details:
772             bb.fatal("Sorry, shell not available (%s)" % details )
773         else:
774             bb.data.update_data( self.configuration.data )
775             shell.start( self )
776             sys.exit( 0 )
777
778     def parseConfigurationFile( self, afile ):
779         try:
780             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
781
782             # Add the handlers we inherited by INHERITS
783             # FIXME: This assumes that we included at least one .inc file
784             for var in bb.data.keys(self.configuration.data):
785                 if bb.data.getVarFlag(var, 'handler', self.configuration.data):
786                     bb.event.register(var,bb.data.getVar(var,self.configuration.data))
787
788         except IOError:
789             bb.fatal( "Unable to open %s" % afile )
790         except bb.parse.ParseError, details:
791             bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
792
793     def handleCollections( self, collections ):
794         """Handle collections"""
795         if collections:
796             collection_list = collections.split()
797             for c in collection_list:
798                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
799                 if regex == None:
800                     bb.error("BBFILE_PATTERN_%s not defined" % c)
801                     continue
802                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
803                 if priority == None:
804                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
805                     continue
806                 try:
807                     cre = re.compile(regex)
808                 except re.error:
809                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
810                     continue
811                 try:
812                     pri = int(priority)
813                     self.status.bbfile_config_priorities.append((cre, pri))
814                 except ValueError:
815                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
816
817
818     def cook( self, configuration, args ):
819         self.configuration = configuration
820
821         if not self.configuration.cmd:
822             self.configuration.cmd = "build"
823
824         if self.configuration.debug:
825             bb.debug_level = self.configuration.debug
826
827         self.configuration.data = bb.data.init()
828
829         for f in self.configuration.file:
830             self.parseConfigurationFile( f )
831
832         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
833
834         if self.configuration.show_environment:
835             self.showEnvironment()
836             sys.exit( 0 )
837
838         # inject custom variables
839         if not bb.data.getVar("BUILDNAME", self.configuration.data):
840             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
841         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
842
843         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
844
845         if self.configuration.interactive:
846             self.interactiveMode()
847
848         if self.configuration.buildfile is not None:
849             bf = os.path.abspath( self.configuration.buildfile )
850             try:
851                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
852             except IOError:
853                 bb.fatal("Unable to open %s" % bf)
854
855             item = bb.data.getVar('PN', bbfile_data, 1)
856             try:
857                 self.tryBuildPackage( bf, item, bbfile_data )
858             except bb.build.EventException:
859                 bb.error( "Build of '%s' failed" % item )
860
861             sys.exit( self.stats.show() )
862
863         # initialise the parsing status now we know we will need deps
864         self.status = BBParsingStatus()
865
866         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
867         self.status.ignored_dependencies = Set( ignore.split() )
868
869         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
870
871         pkgs_to_build = None
872         if args:
873             if not pkgs_to_build:
874                 pkgs_to_build = []
875             pkgs_to_build.extend(args)
876         if not pkgs_to_build:
877                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
878                 if bbpkgs:
879                         pkgs_to_build = bbpkgs.split()
880         if not pkgs_to_build and not self.configuration.show_versions \
881                              and not self.configuration.interactive \
882                              and not self.configuration.show_environment:
883                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
884                 print "for usage information."
885                 sys.exit(0)
886
887         # Import Psyco if available and not disabled
888         if not self.configuration.disable_psyco:
889             try:
890                 import psyco
891             except ImportError:
892                 if bbdebug == 0:
893                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
894             else:
895                 psyco.bind( self.collect_bbfiles )
896         else:
897             bb.note("You have disabled Psyco. This decreases performance.")
898
899         try:
900             bb.debug(1, "collecting .bb files")
901             self.collect_bbfiles( self.myProgressCallback )
902             bb.debug(1, "parsing complete")
903             if bbdebug == 0:
904                 print
905             if self.configuration.parse_only:
906                 print "Requested parsing .bb files only.  Exiting."
907                 return
908
909             bb.data.update_data( self.configuration.data )
910             self.buildDepgraph()
911
912             if self.configuration.show_versions:
913                 self.showVersions()
914                 sys.exit( 0 )
915             if 'world' in pkgs_to_build:
916                 self.buildWorldTargetList()
917                 pkgs_to_build.remove('world')
918                 for t in self.status.world_target:
919                     pkgs_to_build.append(t)
920
921             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.data))
922
923             failures = 0
924             for k in pkgs_to_build:
925                 failed = False
926                 try:
927                     if self.buildProvider( k , False ) == 0:
928                         # already diagnosed
929                         failed = True
930                 except bb.build.EventException:
931                     bb.error("Build of " + k + " failed")
932                     failed = True
933
934                 if failed:
935                     failures += failures
936                     if self.configuration.abort:
937                         sys.exit(1)
938
939             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data, failures))
940
941             sys.exit( self.stats.show() )
942
943         except KeyboardInterrupt:
944             print "\nNOTE: KeyboardInterrupt - Build not completed."
945             sys.exit(1)
946
947     def get_bbfiles( self, path = os.getcwd() ):
948         """Get list of default .bb files by reading out the current directory"""
949         contents = os.listdir(path)
950         bbfiles = []
951         for f in contents:
952             (root, ext) = os.path.splitext(f)
953             if ext == ".bb":
954                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
955         return bbfiles
956
957     def find_bbfiles( self, path ):
958         """Find all the .bb files in a directory (uses find)"""
959         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
960         try:
961             finddata = os.popen(findcmd)
962         except OSError:
963             return []
964         return finddata.readlines()
965
966     def collect_bbfiles( self, progressCallback ):
967         """Collect all available .bb build files"""
968         self.cb = progressCallback
969         parsed, cached, skipped, masked = 0, 0, 0, 0
970         self.bb_cache = bb.cache.init(self)
971
972         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
973         data.setVar("BBFILES", " ".join(files), self.configuration.data)
974
975         if not len(files):
976             files = self.get_bbfiles()
977
978         if not len(files):
979             bb.error("no files to build.")
980
981         newfiles = []
982         for f in files:
983             if os.path.isdir(f):
984                 dirfiles = self.find_bbfiles(f)
985                 if dirfiles:
986                     newfiles += dirfiles
987                     continue
988             newfiles += glob.glob(f) or [ f ]
989
990         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
991         try:
992             bbmask_compiled = re.compile(bbmask)
993         except sre_constants.error:
994             bb.fatal("BBMASK is not a valid regular expression.")
995
996         for i in xrange( len( newfiles ) ):
997             f = newfiles[i]
998             if bbmask and bbmask_compiled.search(f):
999                 bb.debug(1, "bbmake: skipping %s" % f)
1000                 masked += 1
1001                 continue
1002             debug(1, "bbmake: parsing %s" % f)
1003
1004             # read a file's metadata
1005             try:
1006                 fromCache, skip = self.bb_cache.loadData(f, self)
1007                 if skip:
1008                     skipped += 1
1009                     #bb.note("Skipping %s" % f)
1010                     self.bb_cache.skip(f)
1011                     continue
1012                 elif fromCache: cached += 1
1013                 else: parsed += 1
1014                 deps = None
1015
1016                 # allow metadata files to add items to BBFILES
1017                 #data.update_data(self.pkgdata[f])
1018                 addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
1019                 if addbbfiles:
1020                     for aof in addbbfiles.split():
1021                         if not files.count(aof):
1022                             if not os.path.isabs(aof):
1023                                 aof = os.path.join(os.path.dirname(f),aof)
1024                             files.append(aof)
1025
1026                 # now inform the caller
1027                 if self.cb is not None:
1028                     self.cb( i + 1, len( newfiles ), f, self.bb_cache, fromCache )
1029
1030             except IOError, e:
1031                 self.bb_cache.remove(f)
1032                 bb.error("opening %s: %s" % (f, e))
1033                 pass
1034             except KeyboardInterrupt:
1035                 self.bb_cache.sync()
1036                 raise
1037             except Exception, e:
1038                 self.bb_cache.remove(f)
1039                 bb.error("%s while parsing %s" % (e, f))
1040             except:
1041                 self.bb_cache.remove(f)
1042                 raise
1043
1044         if self.cb is not None:
1045             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
1046
1047         self.bb_cache.sync()
1048
1049 #============================================================================#
1050 # main
1051 #============================================================================#
1052
1053 def main():
1054     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
1055     usage = """%prog [options] [package ...]
1056
1057 Executes the specified task (default is 'build') for a given set of BitBake files.
1058 It expects that BBFILES is defined, which is a space seperated list of files to
1059 be executed.  BBFILES does support wildcards.
1060 Default BBFILES are the .bb files in the current directory.""" )
1061
1062     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
1063                action = "store", dest = "buildfile", default = None )
1064
1065     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.",
1066                action = "store_false", dest = "abort", default = True )
1067
1068     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
1069                action = "store_true", dest = "force", default = False )
1070
1071     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
1072                action = "store_true", dest = "interactive", default = False )
1073
1074     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)",
1075                action = "store", dest = "cmd", default = "build" )
1076
1077     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
1078                action = "append", dest = "file", default = [] )
1079
1080     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
1081                action = "store_true", dest = "verbose", default = False )
1082
1083     parser.add_option( "-D", "--debug", help = "Increase the debug level",
1084                action = "count", dest="debug", default = 0)
1085
1086     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
1087                action = "store_true", dest = "dry_run", default = False )
1088
1089     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
1090                action = "store_true", dest = "parse_only", default = False )
1091
1092     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
1093                action = "store_true", dest = "disable_psyco", default = False )
1094
1095     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
1096                action = "store_true", dest = "show_versions", default = False )
1097
1098     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
1099                action = "store_true", dest = "show_environment", default = False )
1100
1101     options, args = parser.parse_args( sys.argv )
1102
1103     cooker = BBCooker()
1104     cooker.cook( BBConfiguration( options ), args[1:] )
1105
1106
1107
1108 if __name__ == "__main__":
1109     main()