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