trunk/bitbake/bin/bitbake:
[bitbake.git] / bin / bitbake
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 #
5 # Copyright (C) 2003, 2004  Chris Larson
6 # Copyright (C) 2003, 2004  Phil Blundell
7 # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
8 # Copyright (C) 2005        Holger Hans Peter Freyther
9 # Copyright (C) 2005        ROAD GmbH
10 #
11 # This program is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 #
16 # This program is distributed in the hope that it will be useful, but WITHOUT
17 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along with
21 # this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22 # Place, Suite 330, Boston, MA 02111-1307 USA.
23
24 import sys, os, getopt, glob, copy, os.path, re, time
25 sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
26 import bb
27 from bb import utils, data, parse, 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) = self.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] = self.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(self.getProvidersRun(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 = self.getProvidersRun(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 = self.getProvidersRun(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 getProvidersRun(self, rdepend):
689         """
690         Return any potential providers of runtime rdepend
691         """
692         rproviders = []
693
694         if rdepend in self.status.rproviders:
695             rproviders += self.status.rproviders[rdepend]
696
697         if rdepend in self.status.packages:
698             rproviders += self.status.packages[rdepend]
699
700         if rproviders:
701             return rproviders
702
703         # Only search dynamic packages if we can't find anything in other variables
704         for pattern in self.status.packages_dynamic:
705             regexp = re.compile(pattern)
706             if regexp.match(rdepend):
707                 rproviders += self.status.packages_dynamic[pattern]
708
709         return rproviders
710
711     def addRunDeps(self , fn, item , buildAllDeps):
712         """
713         Add any runtime dependencies of runtime item provided by fn 
714         as long as item has't previously been processed by this function.
715         """
716
717         if item in self.rbuild_cache:
718             return True
719
720         if not buildAllDeps:
721             return True
722
723         rdepends = []
724         self.rbuild_cache.append(item)
725
726         if fn in self.status.rundeps and item in self.status.rundeps[fn]:
727             rdepends += self.status.rundeps[fn][item].keys()
728         if fn in self.status.runrecs and item in self.status.runrecs[fn]:
729             rdepends += self.status.runrecs[fn][item].keys()
730
731         bb.msg.debug(2, bb.msg.domain.Provider, "Additional runtime dependencies for %s are: %s" % (item, " ".join(rdepends)))
732
733         for rdepend in rdepends:
734             if rdepend in self.status.ignored_dependencies:
735                 continue
736             if not self.buildRProvider(rdepend, buildAllDeps):
737                 return False
738         return True
739
740     def buildDepgraph( self ):
741         all_depends = self.status.all_depends
742         pn_provides = self.status.pn_provides
743
744         localdata = data.createCopy(self.configuration.data)
745         bb.data.update_data(localdata)
746
747         def calc_bbfile_priority(filename):
748             for (regex, pri) in self.status.bbfile_config_priorities:
749                 if regex.match(filename):
750                     return pri
751             return 0
752
753         # Handle PREFERRED_PROVIDERS
754         for p in (bb.data.getVar('PREFERRED_PROVIDERS', localdata, 1) or "").split():
755             (providee, provider) = p.split(':')
756             if providee in self.status.preferred and self.status.preferred[providee] != provider:
757                 bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee]))
758             self.status.preferred[providee] = provider
759
760         # Calculate priorities for each file
761         for p in self.status.pkg_fn.keys():
762             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
763
764     def buildWorldTargetList(self):
765         """
766          Build package list for "bitbake world"
767         """
768         all_depends = self.status.all_depends
769         pn_provides = self.status.pn_provides
770         bb.msg.debug(1, bb.msg.domain.Parsing, "collating packages for \"world\"")
771         for f in self.status.possible_world:
772             terminal = True
773             pn = self.status.pkg_fn[f]
774
775             for p in pn_provides[pn]:
776                 if p.startswith('virtual/'):
777                     bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to %s provider starting with virtual/" % (f, p))
778                     terminal = False
779                     break
780                 for pf in self.status.providers[p]:
781                     if self.status.pkg_fn[pf] != pn:
782                         bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to both us and %s providing %s" % (f, pf, p))
783                         terminal = False
784                         break
785             if terminal:
786                 self.status.world_target.add(pn)
787
788             # drop reference count now
789             self.status.possible_world = None
790             self.status.all_depends    = None
791
792     def myProgressCallback( self, x, y, f, bb_cache, from_cache ):
793         # feed the status with new input
794
795         self.status.handle_bb_data(f, bb_cache, from_cache)
796
797         if os.isatty(sys.stdout.fileno()):
798             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
799             sys.stdout.flush()
800         else:
801             if x == 1:
802                 sys.stdout.write("Parsing .bb files, please wait...")
803                 sys.stdout.flush()
804             if x == y:
805                 sys.stdout.write("done.")
806                 sys.stdout.flush()
807
808     def interactiveMode( self ):
809         """Drop off into a shell"""
810         try:
811             from bb import shell
812         except ImportError, details:
813             bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details )
814         else:
815             bb.data.update_data( self.configuration.data )
816             shell.start( self )
817             sys.exit( 0 )
818
819     def parseConfigurationFile( self, afile ):
820         try:
821             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
822
823             # Add the handlers we inherited by INHERIT
824             # we need to do this manually as it is not guranteed
825             # we will pick up these classes... as we only INHERIT
826             # on .inc and .bb files but not on .conf
827             data = bb.data.createCopy( self.configuration.data )
828             inherits  = ["base"] + (bb.data.getVar('INHERIT', data, True ) or "").split()
829             for inherit in inherits:
830                 data = bb.parse.handle( os.path.join('classes', '%s.bbclass' % inherit ), data, True )
831
832             # FIXME: This assumes that we included at least one .inc file
833             for var in bb.data.keys(data):
834                 if bb.data.getVarFlag(var, 'handler', data):
835                     bb.event.register(var,bb.data.getVar(var, data))
836
837         except IOError:
838             bb.msg.fatal(bb.msg.domain.Parsing, "Unable to open %s" % afile )
839         except bb.parse.ParseError, details:
840             bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (afile, details) )
841
842     def handleCollections( self, collections ):
843         """Handle collections"""
844         if collections:
845             collection_list = collections.split()
846             for c in collection_list:
847                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
848                 if regex == None:
849                     bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s not defined" % c)
850                     continue
851                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
852                 if priority == None:
853                     bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PRIORITY_%s not defined" % c)
854                     continue
855                 try:
856                     cre = re.compile(regex)
857                 except re.error:
858                     bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
859                     continue
860                 try:
861                     pri = int(priority)
862                     self.status.bbfile_config_priorities.append((cre, pri))
863                 except ValueError:
864                     bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
865
866
867     def cook( self, configuration, args ):
868         """
869         We are building stuff here. We do the building
870         from here. By default we try to execute task
871         build.
872         """
873
874         self.configuration = configuration
875
876         if self.configuration.verbose:
877             bb.msg.set_verbose(True)
878
879         if not self.configuration.cmd:
880             self.configuration.cmd = "build"
881
882         if self.configuration.debug:
883             bb.msg.set_debug_level(self.configuration.debug)
884
885         self.configuration.data = bb.data.init()
886
887         for f in self.configuration.file:
888             self.parseConfigurationFile( f )
889
890         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
891
892
893         #
894         # Special updated configuration we use for firing events
895         #
896         self.configuration.event_data = bb.data.createCopy(self.configuration.data)
897         bb.data.update_data(self.configuration.event_data)
898
899         if self.configuration.show_environment:
900             self.showEnvironment()
901             sys.exit( 0 )
902
903         # inject custom variables
904         if not bb.data.getVar("BUILDNAME", self.configuration.data):
905             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
906         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
907
908         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
909
910         if self.configuration.interactive:
911             self.interactiveMode()
912
913         if self.configuration.buildfile is not None:
914             bf = os.path.abspath( self.configuration.buildfile )
915             try:
916                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
917             except IOError:
918                 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to open %s" % bf)
919
920             item = bb.data.getVar('PN', bbfile_data, 1)
921             try:
922                 self.tryBuildPackage( bf, item, bbfile_data )
923             except bb.build.EventException:
924                 bb.msg.error(bb.msg.domain.Build,  "Build of '%s' failed" % item )
925
926             sys.exit( self.stats.show() )
927
928         # initialise the parsing status now we know we will need deps
929         self.status = BBParsingStatus()
930
931         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
932         self.status.ignored_dependencies = Set( ignore.split() )
933
934         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
935
936         pkgs_to_build = None
937         if args:
938             if not pkgs_to_build:
939                 pkgs_to_build = []
940             pkgs_to_build.extend(args)
941         if not pkgs_to_build:
942                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
943                 if bbpkgs:
944                         pkgs_to_build = bbpkgs.split()
945         if not pkgs_to_build and not self.configuration.show_versions \
946                              and not self.configuration.interactive \
947                              and not self.configuration.show_environment:
948                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
949                 print "for usage information."
950                 sys.exit(0)
951
952         # Import Psyco if available and not disabled
953         if not self.configuration.disable_psyco:
954             try:
955                 import psyco
956             except ImportError:
957                 bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
958             else:
959                 psyco.bind( self.collect_bbfiles )
960         else:
961             bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
962
963         try:
964             bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files")
965             self.collect_bbfiles( self.myProgressCallback )
966             bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete")
967             print
968             if self.configuration.parse_only:
969                 bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only.  Exiting.")
970                 return
971
972
973             self.buildDepgraph()
974
975             if self.configuration.show_versions:
976                 self.showVersions()
977                 sys.exit( 0 )
978             if 'world' in pkgs_to_build:
979                 self.buildWorldTargetList()
980                 pkgs_to_build.remove('world')
981                 for t in self.status.world_target:
982                     pkgs_to_build.append(t)
983
984             if self.configuration.dot_graph:
985                 self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps )
986                 sys.exit( 0 )
987
988
989             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data))
990
991             failures = 0
992             for k in pkgs_to_build:
993                 failed = False
994                 try:
995                     if self.buildProvider( k , False ) == 0:
996                         # already diagnosed
997                         failed = True
998                 except bb.build.EventException:
999                     bb.msg.error(bb.msg.domain.Build, "Build of " + k + " failed")
1000                     failed = True
1001
1002                 if failed:
1003                     failures += failures
1004                     if self.configuration.abort:
1005                         sys.exit(1)
1006
1007             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures))
1008
1009             sys.exit( self.stats.show() )
1010
1011         except KeyboardInterrupt:
1012             bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.")
1013             sys.exit(1)
1014
1015     def get_bbfiles( self, path = os.getcwd() ):
1016         """Get list of default .bb files by reading out the current directory"""
1017         contents = os.listdir(path)
1018         bbfiles = []
1019         for f in contents:
1020             (root, ext) = os.path.splitext(f)
1021             if ext == ".bb":
1022                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
1023         return bbfiles
1024
1025     def find_bbfiles( self, path ):
1026         """Find all the .bb files in a directory (uses find)"""
1027         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
1028         try:
1029             finddata = os.popen(findcmd)
1030         except OSError:
1031             return []
1032         return finddata.readlines()
1033
1034     def collect_bbfiles( self, progressCallback ):
1035         """Collect all available .bb build files"""
1036         self.cb = progressCallback
1037         parsed, cached, skipped, masked = 0, 0, 0, 0
1038         self.bb_cache = bb.cache.init(self)
1039
1040         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
1041         data.setVar("BBFILES", " ".join(files), self.configuration.data)
1042
1043         if not len(files):
1044             files = self.get_bbfiles()
1045
1046         if not len(files):
1047             bb.msg.error(bb.msg.domain.Collection, "no files to build.")
1048
1049         newfiles = []
1050         for f in files:
1051             if os.path.isdir(f):
1052                 dirfiles = self.find_bbfiles(f)
1053                 if dirfiles:
1054                     newfiles += dirfiles
1055                     continue
1056             newfiles += glob.glob(f) or [ f ]
1057
1058         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
1059         try:
1060             bbmask_compiled = re.compile(bbmask)
1061         except sre_constants.error:
1062             bb.msg.fatal(bb.msg.domain.Collection, "BBMASK is not a valid regular expression.")
1063
1064         for i in xrange( len( newfiles ) ):
1065             f = newfiles[i]
1066             if bbmask and bbmask_compiled.search(f):
1067                 bb.msg.debug(1, bb.msg.domain.Collection, "bbmake: skipping %s" % f, f)
1068                 masked += 1
1069                 continue
1070             bb.msg.debug(1, bb.msg.domain.Collection, "bbmake: parsing %s" % f, f)
1071
1072             # read a file's metadata
1073             try:
1074                 fromCache, skip = self.bb_cache.loadData(f, self)
1075                 if skip:
1076                     skipped += 1
1077                     bb.msg.debug(2, bb.msg.domain.Collection, "Skipping %s" % f, f)
1078                     self.bb_cache.skip(f)
1079                     continue
1080                 elif fromCache: cached += 1
1081                 else: parsed += 1
1082                 deps = None
1083
1084                 # allow metadata files to add items to BBFILES
1085                 #data.update_data(self.pkgdata[f])
1086                 addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
1087                 if addbbfiles:
1088                     for aof in addbbfiles.split():
1089                         if not files.count(aof):
1090                             if not os.path.isabs(aof):
1091                                 aof = os.path.join(os.path.dirname(f),aof)
1092                             files.append(aof)
1093
1094                 # now inform the caller
1095                 if self.cb is not None:
1096                     self.cb( i + 1, len( newfiles ), f, self.bb_cache, fromCache )
1097
1098             except IOError, e:
1099                 self.bb_cache.remove(f)
1100                 bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e), f)
1101                 pass
1102             except KeyboardInterrupt:
1103                 self.bb_cache.sync()
1104                 raise
1105             except Exception, e:
1106                 self.bb_cache.remove(f)
1107                 bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f), f)
1108             except:
1109                 self.bb_cache.remove(f)
1110                 raise
1111
1112         if self.cb is not None:
1113             print "\r" # need newline after Handling Bitbake files message
1114             bb.msg.note(1, bb.msg.domain.Collection, "Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ))
1115
1116         self.bb_cache.sync()
1117
1118 #============================================================================#
1119 # main
1120 #============================================================================#
1121
1122 def main():
1123     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
1124     usage = """%prog [options] [package ...]
1125
1126 Executes the specified task (default is 'build') for a given set of BitBake files.
1127 It expects that BBFILES is defined, which is a space seperated list of files to
1128 be executed.  BBFILES does support wildcards.
1129 Default BBFILES are the .bb files in the current directory.""" )
1130
1131     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
1132                action = "store", dest = "buildfile", default = None )
1133
1134     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.",
1135                action = "store_false", dest = "abort", default = True )
1136
1137     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
1138                action = "store_true", dest = "force", default = False )
1139
1140     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode also called the BitBake shell.",
1141                action = "store_true", dest = "interactive", default = False )
1142
1143     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",
1144                action = "store", dest = "cmd", default = "build" )
1145
1146     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
1147                action = "append", dest = "file", default = [] )
1148
1149     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
1150                action = "store_true", dest = "verbose", default = False )
1151
1152     parser.add_option( "-D", "--debug", help = "Increase the debug level. You can specify this more than once.",
1153                action = "count", dest="debug", default = 0)
1154
1155     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
1156                action = "store_true", dest = "dry_run", default = False )
1157
1158     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
1159                action = "store_true", dest = "parse_only", default = False )
1160
1161     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
1162                action = "store_true", dest = "disable_psyco", default = False )
1163
1164     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
1165                action = "store_true", dest = "show_versions", default = False )
1166
1167     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
1168                action = "store_true", dest = "show_environment", default = False )
1169
1170     parser.add_option( "-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax",
1171                 action = "store_true", dest = "dot_graph", default = False )
1172     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""",
1173                 action = "append", dest = "ignored_dot_deps", default = [] )
1174
1175
1176     options, args = parser.parse_args( sys.argv )
1177
1178     cooker = BBCooker()
1179     cooker.cook( BBConfiguration( options ), args[1:] )
1180
1181
1182
1183 if __name__ == "__main__":
1184     print """WARNING, WARNING, WARNING
1185 This is a Bitbake from the Unstable/Development Branch.
1186 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."""
1187     import time
1188     time.sleep(5)
1189     main()