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