bitbake/bin/bitbake:
[vuplus_bitbake] / bin / bitbake
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 #
5 # Copyright (C) 2003, 2004  Chris Larson
6 # Copyright (C) 2003, 2004  Phil Blundell
7 # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
8 # Copyright (C) 2005        Holger Hans Peter Freyther
9 # Copyright (C) 2005        ROAD GmbH
10 #
11 # This program is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 #
16 # This program is distributed in the hope that it will be useful, but WITHOUT
17 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along with
21 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22 # Place, Suite 330, Boston, MA 02111-1307 USA.
23
24 import sys, os, getopt, glob, copy, os.path, re, time
25 sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
26 import bb
27 from bb import utils, data, parse, debug, event, fatal, cache
28 from sets import Set
29 import itertools, optparse
30
31 parsespin = itertools.cycle( r'|/-\\' )
32 bbdebug = 0
33
34 __version__ = "1.3.9"
35
36 #============================================================================#
37 # BBParsingStatus
38 #============================================================================#
39 class BBParsingStatus:
40     """
41     The initial idea for this status class is to use the data when it is
42     already loaded instead of loading it from various place over and over
43     again.
44     """
45
46     def __init__(self):
47         self.providers   = {}
48         self.rproviders = {}
49         self.packages = {}
50         self.packages_dynamic = {}
51         self.bbfile_priority = {}
52         self.bbfile_config_priorities = []
53         self.ignored_dependencies = None
54         self.possible_world = []
55         self.world_target = Set()
56         self.pkg_pn = {}
57         self.pkg_fn = {}
58         self.pkg_pvpr = {}
59         self.pkg_dp = {}
60         self.pn_provides = {}
61         self.all_depends = Set()
62         self.build_all = {}
63         self.rundeps = {}
64         self.runrecs = {}
65         self.stamp = {}
66
67     def handle_bb_data(self, file_name, bb_cache, cached):
68         """
69         We will fill the dictionaries with the stuff we
70         need for building the tree more fast
71         """
72
73         pn       = bb_cache.getVar('PN', file_name, True)
74         pv       = bb_cache.getVar('PV', file_name, True)
75         pr       = bb_cache.getVar('PR', file_name, True)
76         dp       = int(bb_cache.getVar('DEFAULT_PREFERENCE', file_name, True) or "0")
77         provides  = Set([pn] + (bb_cache.getVar("PROVIDES", file_name, True) or "").split())
78         depends   = (bb_cache.getVar("DEPENDS", file_name, True) or "").split()
79         packages  = (bb_cache.getVar('PACKAGES', file_name, True) or "").split()
80         packages_dynamic = (bb_cache.getVar('PACKAGES_DYNAMIC', file_name, True) or "").split()
81         rprovides = (bb_cache.getVar("RPROVIDES", file_name, True) or "").split()
82
83         # build PackageName to FileName lookup table
84         if pn not in self.pkg_pn:
85             self.pkg_pn[pn] = []
86         self.pkg_pn[pn].append(file_name)
87
88         self.build_all[file_name] = int(bb_cache.getVar('BUILD_ALL_DEPS', file_name, True) or "0")
89         self.stamp[file_name] = bb_cache.getVar('STAMP', file_name, True)
90
91         # build FileName to PackageName lookup table
92         self.pkg_fn[file_name] = pn
93         self.pkg_pvpr[file_name] = (pv,pr)
94         self.pkg_dp[file_name] = dp
95
96         # Build forward and reverse provider hashes
97         # Forward: virtual -> [filenames]
98         # Reverse: PN -> [virtuals]
99         if pn not in self.pn_provides:
100             self.pn_provides[pn] = Set()
101         self.pn_provides[pn] |= provides
102
103         for provide in provides:
104             if provide not in self.providers:
105                 self.providers[provide] = []
106             self.providers[provide].append(file_name)
107
108         for dep in depends:
109             self.all_depends.add(dep)
110
111         # Build reverse hash for PACKAGES, so runtime dependencies 
112         # can be be resolved (RDEPENDS, RRECOMMENDS etc.)
113         for package in packages:
114             if not package in self.packages:
115                 self.packages[package] = []
116             self.packages[package].append(file_name)
117             rprovides += (bb_cache.getVar("RPROVIDES_%s" % package, file_name, 1) or "").split() 
118
119         for package in packages_dynamic:
120             if not package in self.packages_dynamic:
121                 self.packages_dynamic[package] = []
122             self.packages_dynamic[package].append(file_name)
123
124         for rprovide in rprovides:
125             if not rprovide in self.rproviders:
126                 self.rproviders[rprovide] = []
127             self.rproviders[rprovide].append(file_name)
128
129         # Build hash of runtime depeneds and rececommends
130
131         def add_dep(deplist, deps):
132             for dep in deps:
133                 if not dep in deplist:
134                     deplist[dep] = ""
135
136         if not file_name in self.rundeps:
137             self.rundeps[file_name] = {}
138         if not file_name in self.runrecs:
139             self.runrecs[file_name] = {}
140
141         for package in packages + [pn]:
142             if not package in self.rundeps[file_name]:
143                 self.rundeps[file_name][package] = {}
144             if not package in self.runrecs[file_name]:
145                 self.runrecs[file_name][package] = {}
146
147             add_dep(self.rundeps[file_name][package], bb.utils.explode_deps(bb_cache.getVar('RDEPENDS', file_name, True) or ""))
148             add_dep(self.runrecs[file_name][package], bb.utils.explode_deps(bb_cache.getVar('RRECOMMENDS', file_name, True) or ""))
149             add_dep(self.rundeps[file_name][package], bb.utils.explode_deps(bb_cache.getVar("RDEPENDS_%s" % package, file_name, True) or ""))
150             add_dep(self.runrecs[file_name][package], bb.utils.explode_deps(bb_cache.getVar("RRECOMMENDS_%s" % package, file_name, True) or ""))
151
152         # Collect files we may need for possible world-dep
153         # calculations
154         if not bb_cache.getVar('BROKEN', file_name, True) and not bb_cache.getVar('EXCLUDE_FROM_WORLD', file_name, True):
155             self.possible_world.append(file_name)
156
157
158 #============================================================================#
159 # BBStatistics
160 #============================================================================#
161 class BBStatistics:
162     """
163     Manage build statistics for one run
164     """
165     def __init__(self ):
166         self.attempt = 0
167         self.success = 0
168         self.fail = 0
169         self.deps = 0
170
171     def show( self ):
172         print "Build statistics:"
173         print "  Attempted builds: %d" % self.attempt
174         if self.fail:
175             print "  Failed builds: %d" % self.fail
176         if self.deps:
177             print "  Dependencies not satisfied: %d" % self.deps
178         if self.fail or self.deps: return 1
179         else: return 0
180
181
182 #============================================================================#
183 # BBOptions
184 #============================================================================#
185 class BBConfiguration( object ):
186     """
187     Manages build options and configurations for one run
188     """
189     def __init__( self, options ):
190         for key, val in options.__dict__.items():
191             setattr( self, key, val )
192         self.data = data.init()
193
194 #============================================================================#
195 # BBCooker
196 #============================================================================#
197 class BBCooker:
198     """
199     Manages one bitbake build run
200     """
201
202     ParsingStatus = BBParsingStatus     # make it visible from the shell
203     Statistics = BBStatistics           # make it visible from the shell
204
205     def __init__( self ):
206         self.build_cache_fail = []
207         self.build_cache = []
208         self.rbuild_cache = []
209         self.building_list = []
210         self.build_path = []
211         self.consider_msgs_cache = []
212         self.preferred = {}
213         self.stats = BBStatistics()
214         self.status = None
215
216         self.cache = None
217         self.bb_cache = None
218
219     def tryBuildPackage( self, fn, item, the_data ):
220         """Build one package"""
221         bb.event.fire(bb.event.PkgStarted(item, the_data))
222         try:
223             self.stats.attempt += 1
224             if self.configuration.force:
225                 bb.data.setVarFlag('do_%s' % self.configuration.cmd, 'force', 1, the_data)
226             if not self.configuration.dry_run:
227                 bb.build.exec_task('do_%s' % self.configuration.cmd, the_data)
228             bb.event.fire(bb.event.PkgSucceeded(item, the_data))
229             self.build_cache.append(fn)
230             return True
231         except bb.build.FuncFailed:
232             self.stats.fail += 1
233             bb.error("task stack execution failed")
234             bb.event.fire(bb.event.PkgFailed(item, the_data))
235             self.build_cache_fail.append(fn)
236             raise
237         except bb.build.EventException, e:
238             self.stats.fail += 1
239             event = e.args[1]
240             bb.error("%s event exception, aborting" % bb.event.getName(event))
241             bb.event.fire(bb.event.PkgFailed(item, the_data))
242             self.build_cache_fail.append(fn)
243             raise
244
245     def tryBuild( self, fn, virtual , buildAllDeps , build_depends = []):
246         """
247         Build a provider and its dependencies. 
248         build_depends is a list of previous build dependencies (not runtime)
249         If build_depends is empty, we're dealing with a runtime depends
250         """
251
252         the_data = self.bb_cache.loadDataFull(fn, self)
253
254         # 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         except IOError:
782             bb.fatal( "Unable to open %s" % afile )
783         except bb.parse.ParseError, details:
784             bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
785
786     def handleCollections( self, collections ):
787         """Handle collections"""
788         if collections:
789             collection_list = collections.split()
790             for c in collection_list:
791                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
792                 if regex == None:
793                     bb.error("BBFILE_PATTERN_%s not defined" % c)
794                     continue
795                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
796                 if priority == None:
797                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
798                     continue
799                 try:
800                     cre = re.compile(regex)
801                 except re.error:
802                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
803                     continue
804                 try:
805                     pri = int(priority)
806                     self.status.bbfile_config_priorities.append((cre, pri))
807                 except ValueError:
808                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
809
810
811     def cook( self, configuration, args ):
812         self.configuration = configuration
813
814         if not self.configuration.cmd:
815             self.configuration.cmd = "build"
816
817         if self.configuration.debug:
818             bb.debug_level = self.configuration.debug
819
820         self.configuration.data = bb.data.init()
821
822         for f in self.configuration.file:
823             self.parseConfigurationFile( f )
824
825         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
826
827         if self.configuration.show_environment:
828             self.showEnvironment()
829             sys.exit( 0 )
830
831         # inject custom variables
832         if not bb.data.getVar("BUILDNAME", self.configuration.data):
833             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
834         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
835
836         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
837
838         if self.configuration.interactive:
839             self.interactiveMode()
840
841         if self.configuration.buildfile is not None:
842             bf = os.path.abspath( self.configuration.buildfile )
843             try:
844                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
845             except IOError:
846                 bb.fatal("Unable to open %s" % bf)
847
848             item = bb.data.getVar('PN', bbfile_data, 1)
849             try:
850                 self.tryBuildPackage( bf, item, bbfile_data )
851             except bb.build.EventException:
852                 bb.error( "Build of '%s' failed" % item )
853
854             sys.exit( self.stats.show() )
855
856         # initialise the parsing status now we know we will need deps
857         self.status = BBParsingStatus()
858
859         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
860         self.status.ignored_dependencies = Set( ignore.split() )
861
862         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
863
864         pkgs_to_build = None
865         if args:
866             if not pkgs_to_build:
867                 pkgs_to_build = []
868             pkgs_to_build.extend(args)
869         if not pkgs_to_build:
870                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
871                 if bbpkgs:
872                         pkgs_to_build = bbpkgs.split()
873         if not pkgs_to_build and not self.configuration.show_versions \
874                              and not self.configuration.interactive \
875                              and not self.configuration.show_environment:
876                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
877                 print "for usage information."
878                 sys.exit(0)
879
880         # Import Psyco if available and not disabled
881         if not self.configuration.disable_psyco:
882             try:
883                 import psyco
884             except ImportError:
885                 if bbdebug == 0:
886                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
887             else:
888                 psyco.bind( self.collect_bbfiles )
889         else:
890             bb.note("You have disabled Psyco. This decreases performance.")
891
892         try:
893             bb.debug(1, "collecting .bb files")
894             self.collect_bbfiles( self.myProgressCallback )
895             bb.debug(1, "parsing complete")
896             if bbdebug == 0:
897                 print
898             if self.configuration.parse_only:
899                 print "Requested parsing .bb files only.  Exiting."
900                 return
901
902             bb.data.update_data( self.configuration.data )
903             self.buildDepgraph()
904
905             if self.configuration.show_versions:
906                 self.showVersions()
907                 sys.exit( 0 )
908             if 'world' in pkgs_to_build:
909                 self.buildWorldTargetList()
910                 pkgs_to_build.remove('world')
911                 for t in self.status.world_target:
912                     pkgs_to_build.append(t)
913
914             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.data))
915
916             failures = 0
917             for k in pkgs_to_build:
918                 failed = False
919                 try:
920                     if self.buildProvider( k , False ) == 0:
921                         # already diagnosed
922                         failed = True
923                 except bb.build.EventException:
924                     bb.error("Build of " + k + " failed")
925                     failed = True
926
927                 if failed:
928                     failures += failures
929                     if self.configuration.abort:
930                         sys.exit(1)
931
932             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.data, failures))
933
934             sys.exit( self.stats.show() )
935
936         except KeyboardInterrupt:
937             print "\nNOTE: KeyboardInterrupt - Build not completed."
938             sys.exit(1)
939
940     def get_bbfiles( self, path = os.getcwd() ):
941         """Get list of default .bb files by reading out the current directory"""
942         contents = os.listdir(path)
943         bbfiles = []
944         for f in contents:
945             (root, ext) = os.path.splitext(f)
946             if ext == ".bb":
947                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
948         return bbfiles
949
950     def find_bbfiles( self, path ):
951         """Find all the .bb files in a directory (uses find)"""
952         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
953         try:
954             finddata = os.popen(findcmd)
955         except OSError:
956             return []
957         return finddata.readlines()
958
959     def collect_bbfiles( self, progressCallback ):
960         """Collect all available .bb build files"""
961         self.cb = progressCallback
962         parsed, cached, skipped, masked = 0, 0, 0, 0
963         self.bb_cache = bb.cache.init(self)
964
965         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
966         data.setVar("BBFILES", " ".join(files), self.configuration.data)
967
968         if not len(files):
969             files = self.get_bbfiles()
970
971         if not len(files):
972             bb.error("no files to build.")
973
974         newfiles = []
975         for f in files:
976             if os.path.isdir(f):
977                 dirfiles = self.find_bbfiles(f)
978                 if dirfiles:
979                     newfiles += dirfiles
980                     continue
981             newfiles += glob.glob(f) or [ f ]
982
983         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
984         try:
985             bbmask_compiled = re.compile(bbmask)
986         except sre_constants.error:
987             bb.fatal("BBMASK is not a valid regular expression.")
988
989         for i in xrange( len( newfiles ) ):
990             f = newfiles[i]
991             if bbmask and bbmask_compiled.search(f):
992                 bb.debug(1, "bbmake: skipping %s" % f)
993                 masked += 1
994                 continue
995             debug(1, "bbmake: parsing %s" % f)
996
997             # read a file's metadata
998             try:
999                 fromCache, skip = self.bb_cache.loadData(f, self)
1000                 if skip:
1001                     skipped += 1
1002                     #bb.note("Skipping %s" % f)
1003                     self.bb_cache.skip(f)
1004                     continue
1005                 elif fromCache: cached += 1
1006                 else: parsed += 1
1007                 deps = None
1008
1009                 # allow metadata files to add items to BBFILES
1010                 #data.update_data(self.pkgdata[f])
1011                 addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
1012                 if addbbfiles:
1013                     for aof in addbbfiles.split():
1014                         if not files.count(aof):
1015                             if not os.path.isabs(aof):
1016                                 aof = os.path.join(os.path.dirname(f),aof)
1017                             files.append(aof)
1018
1019                 # now inform the caller
1020                 if self.cb is not None:
1021                     self.cb( i + 1, len( newfiles ), f, self.bb_cache, fromCache )
1022
1023             except IOError, e:
1024                 self.bb_cache.remove(f)
1025                 bb.error("opening %s: %s" % (f, e))
1026                 pass
1027             except KeyboardInterrupt:
1028                 self.bb_cache.sync()
1029                 raise
1030             except Exception, e:
1031                 self.bb_cache.remove(f)
1032                 bb.error("%s while parsing %s" % (e, f))
1033             except:
1034                 self.bb_cache.remove(f)
1035                 raise
1036
1037         if self.cb is not None:
1038             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
1039
1040         self.bb_cache.sync()
1041
1042 #============================================================================#
1043 # main
1044 #============================================================================#
1045
1046 def main():
1047     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
1048     usage = """%prog [options] [package ...]
1049
1050 Executes the specified task (default is 'build') for a given set of BitBake files.
1051 It expects that BBFILES is defined, which is a space seperated list of files to
1052 be executed.  BBFILES does support wildcards.
1053 Default BBFILES are the .bb files in the current directory.""" )
1054
1055     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
1056                action = "store", dest = "buildfile", default = None )
1057
1058     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.",
1059                action = "store_false", dest = "abort", default = True )
1060
1061     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
1062                action = "store_true", dest = "force", default = False )
1063
1064     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
1065                action = "store_true", dest = "interactive", default = False )
1066
1067     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)",
1068                action = "store", dest = "cmd", default = "build" )
1069
1070     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
1071                action = "append", dest = "file", default = [] )
1072
1073     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
1074                action = "store_true", dest = "verbose", default = False )
1075
1076     parser.add_option( "-D", "--debug", help = "Increase the debug level",
1077                action = "count", dest="debug", default = 0)
1078
1079     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
1080                action = "store_true", dest = "dry_run", default = False )
1081
1082     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
1083                action = "store_true", dest = "parse_only", default = False )
1084
1085     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
1086                action = "store_true", dest = "disable_psyco", default = False )
1087
1088     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
1089                action = "store_true", dest = "show_versions", default = False )
1090
1091     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
1092                action = "store_true", dest = "show_environment", default = False )
1093
1094     options, args = parser.parse_args( sys.argv )
1095
1096     cooker = BBCooker()
1097     cooker.cook( BBConfiguration( options ), args[1:] )
1098
1099
1100
1101 if __name__ == "__main__":
1102     main()