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