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