bitbake/bin/bitbake: Generate DOT depedency start
[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 ):
453         """
454         Generate two graphs one for the DEPENDS and RDEPENDS. The current
455         implementation creates crappy graphs ;)
456         """
457
458         rdepends_file = file('rdepends.dot', 'w' )
459         depends_file  = file('depends.dot', 'w' )
460
461
462         # setup the graphs
463         print >> depends_file, "digraph depends {"
464         print >> rdepends_file, "digraph rdepends {"
465
466
467         # try to avoid adding the same rdepends over an over again
468         seen_depends   = {}
469         seen_rdepends =  {}
470
471         added_depends_error = False
472
473         def add_depends(package_list):
474             """
475             Add all depends of all packages from this list
476             """
477             for package in package_list:
478                 if package in seen_depends:
479                     continue
480                 else:
481                     seen_depends[package] = 1
482
483                 if not package in self.status.providers:
484                     """
485                     We have not seen this name -> error in
486                     dependency handling
487                     """
488                     print >> depends_file, '"%(package)s" -> ERROR' % vars()
489                     continue
490
491                 # get all providers for this package
492                 providers = self.status.providers[package]
493
494                 # now let us find the bestProvider for it
495                 elligible = self.filterProviders(providers, package)
496                 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % package, self.configuration.data, 1)
497
498                 # try the preferred provider first
499                 if prefervar:
500                     for p in elligible:
501                         if prefervar == self.status.pkg_fn[p]:
502                             bb.note("Selecting PREFERRED_PROVIDER %s" % prefervar)
503                             elligible.remove(p)
504                             elligible = [p] + elligible
505
506                 fn = elligible[0] 
507                 depends  = bb.utils.explode_deps(self.bb_cache.getVar('DEPENDS', fn, True) or "")
508                 rdepends = bb.utils.explode_deps(self.bb_cache.getVar('RDEPENDS', fn, True) or "")
509                 add_depends( depends )
510                 add_rdepends( rdepends )
511
512                 for depend in depends:
513                     print >> depends_file, '"%(package)s" -> "%(depend)s"' % vars()
514
515         def add_rdepends(package_list):
516             """
517             for each package of the package_list
518             we will see if we have handled it already
519             """
520             pass
521         
522         # start with the initial list
523         add_depends( pkgs_to_build )
524         add_rdepends( pkgs_to_build )
525
526         # finish it up
527         print >> depends_file,  "}"
528         print >> rdepends_file, "}"
529
530     def filterProviders(self, providers, item):
531         """
532         Take a list of providers and filter/reorder according to the 
533         environment variables and previous build results
534         """
535         eligible = []
536         preferred_versions = {}
537
538         # Collate providers by PN
539         pkg_pn = {}
540         for p in providers:
541             pn = self.status.pkg_fn[p]
542             if pn not in pkg_pn:
543                 pkg_pn[pn] = []
544             pkg_pn[pn].append(p)
545
546         bb.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
547
548         for pn in pkg_pn.keys():
549             preferred_versions[pn] = self.findBestProvider(pn, pkg_pn)[2:4]
550             eligible.append(preferred_versions[pn][1])
551
552         for p in eligible:
553             if p in self.build_cache_fail:
554                 bb.debug(1, "rejecting already-failed %s" % p)
555                 eligible.remove(p)
556
557         if len(eligible) == 0:
558             bb.error("no eligible providers for %s" % item)
559             return 0
560
561         # look to see if one of them is already staged, or marked as preferred.
562         # if so, bump it to the head of the queue
563         for p in providers:
564             pn = self.status.pkg_fn[p]
565             pv, pr = self.status.pkg_pvpr[p]
566
567             stamp = '%s.do_populate_staging' % self.status.stamp[p]
568             if os.path.exists(stamp):
569                 (newvers, fn) = preferred_versions[pn]
570                 if not fn in eligible:
571                     # package was made ineligible by already-failed check
572                     continue
573                 oldver = "%s-%s" % (pv, pr)
574                 newver = '-'.join(newvers)
575                 if (newver != oldver):
576                     extra_chat = "%s (%s) already staged but upgrading to %s to satisfy %s" % (pn, oldver, newver, item)
577                 else:
578                     extra_chat = "Selecting already-staged %s (%s) to satisfy %s" % (pn, oldver, item)
579                 if self.configuration.verbose:
580                     bb.note("%s" % extra_chat)
581                 eligible.remove(fn)
582                 eligible = [fn] + eligible
583                 discriminated = True
584                 break
585
586         return eligible
587
588     def buildProvider( self, item , buildAllDeps , build_depends = [] ):
589         """
590         Build something to provide a named build requirement
591         (takes item names from DEPENDS namespace)
592         """
593
594         fn = None
595         discriminated = False
596
597         if not item in self.status.providers:
598             bb.error("Nothing provides dependency %s" % item)
599             bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
600             return 0
601
602         all_p = self.status.providers[item]
603
604         for p in all_p:
605             if p in self.build_cache:
606                 bb.debug(1, "already built %s in this run\n" % p)
607                 return 1
608
609         eligible = self.filterProviders(all_p, item)
610
611         if not eligible:
612             return 0
613
614         prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, self.configuration.data, 1)
615         if prefervar:
616             self.preferred[item] = prefervar
617
618         if item in self.preferred:
619             for p in eligible:
620                 pn = self.status.pkg_fn[p]
621                 if self.preferred[item] == pn:
622                     if self.configuration.verbose:
623                         bb.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
624                     eligible.remove(p)
625                     eligible = [p] + eligible
626                     discriminated = True
627                     break
628
629         if len(eligible) > 1 and discriminated == False:
630             if item not in self.consider_msgs_cache:
631                 providers_list = []
632                 for fn in eligible:
633                     providers_list.append(self.status.pkg_fn[fn])
634                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
635                 bb.note("consider defining PREFERRED_PROVIDER_%s" % item)
636                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data))
637             self.consider_msgs_cache.append(item)
638
639
640         # run through the list until we find one that we can build
641         for fn in eligible:
642             bb.debug(2, "selecting %s to satisfy %s" % (fn, item))
643             if self.tryBuild(fn, item, buildAllDeps, build_depends + [fn]):
644                 return 1
645
646         bb.note("no buildable providers for %s" % item)
647         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
648         return 0
649
650     def buildRProvider( self, item , buildAllDeps ):
651         """
652         Build something to provide a named runtime requirement
653         (takes item names from RDEPENDS/PACKAGES namespace)
654         """
655
656         fn = None
657         all_p = []
658         discriminated = False
659
660         if not buildAllDeps:
661             return True
662
663         all_p = self.getProvidersRun(item)
664
665         if not all_p:
666             bb.error("Nothing provides runtime dependency %s" % (item))
667             bb.event.fire(bb.event.NoProvider(item,self.configuration.data,runtime=True))
668             return False
669
670         for p in all_p:
671             if p in self.rbuild_cache:
672                 bb.debug(2, "Already built %s providing runtime %s\n" % (p,item))
673                 return True
674             if p in self.build_cache:
675                 bb.debug(2, "Already built %s but adding any further RDEPENDS for %s\n" % (p, item))
676                 return self.addRunDeps(p, item , buildAllDeps)
677
678         eligible = self.filterProviders(all_p, item)
679         if not eligible:
680             return 0
681
682         preferred = []
683         for p in eligible:
684             pn = self.status.pkg_fn[p]
685             provides = self.status.pn_provides[pn]
686             for provide in provides:
687                 prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, self.configuration.data, 1)
688                 if prefervar == pn:
689                     if self.configuration.verbose:
690                         bb.note("selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
691                     eligible.remove(p)
692                     eligible = [p] + eligible
693                     preferred.append(p)
694
695         if len(eligible) > 1 and len(preferred) == 0:
696             if item not in self.consider_msgs_cache:
697                 providers_list = []
698                 for fn in eligible:
699                     providers_list.append(self.status.pkg_fn[fn])
700                 bb.note("multiple providers are available (%s);" % ", ".join(providers_list))
701                 bb.note("consider defining a PREFERRED_PROVIDER to match runtime %s" % item)
702                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
703             self.consider_msgs_cache.append(item)
704
705         if len(preferred) > 1:
706             if item not in self.consider_msgs_cache:
707                 providers_list = []
708                 for fn in preferred:
709                     providers_list.append(self.status.pkg_fn[fn])
710                 bb.note("multiple preferred providers are available (%s);" % ", ".join(providers_list))
711                 bb.note("consider defining only one PREFERRED_PROVIDER to match runtime %s" % item)
712                 bb.event.fire(bb.event.MultipleProviders(item,providers_list,self.configuration.data,runtime=True))
713             self.consider_msgs_cache.append(item)
714
715         # run through the list until we find one that we can build
716         for fn in eligible:
717             bb.debug(2, "selecting %s to satisfy runtime %s" % (fn, item))
718             if self.tryBuild(fn, item, buildAllDeps):
719                 return True
720
721         bb.error("No buildable providers for runtime %s" % item)
722         bb.event.fire(bb.event.NoProvider(item,self.configuration.data))
723         return False
724
725     def getProvidersRun(self, rdepend):
726         """
727         Return any potential providers of runtime rdepend
728         """
729         rproviders = []
730
731         if rdepend in self.status.rproviders:
732             rproviders += self.status.rproviders[rdepend]
733
734         if rdepend in self.status.packages:
735             rproviders += self.status.packages[rdepend]
736
737         if rproviders:
738             return rproviders
739
740         # Only search dynamic packages if we can't find anything in other variables
741         for pattern in self.status.packages_dynamic:
742             regexp = re.compile(pattern)
743             if regexp.match(rdepend):
744                 rproviders += self.status.packages_dynamic[pattern]
745
746         return rproviders
747
748     def addRunDeps(self , fn, item , buildAllDeps):
749         """
750         Add any runtime dependencies of runtime item provided by fn 
751         as long as item has't previously been processed by this function.
752         """
753
754         if item in self.rbuild_cache:
755             return True
756
757         if not buildAllDeps:
758             return True
759
760         rdepends = []
761         self.rbuild_cache.append(item)
762
763         if fn in self.status.rundeps and item in self.status.rundeps[fn]:
764             rdepends += self.status.rundeps[fn][item].keys()
765         if fn in self.status.runrecs and item in self.status.runrecs[fn]:
766             rdepends += self.status.runrecs[fn][item].keys()
767
768         bb.debug(2, "Additional runtime dependencies for %s are: %s" % (item, " ".join(rdepends)))
769
770         for rdepend in rdepends:
771             if rdepend in self.status.ignored_dependencies:
772                 continue
773             if not self.buildRProvider(rdepend, buildAllDeps):
774                 return False
775         return True
776
777     def buildDepgraph( self ):
778         all_depends = self.status.all_depends
779         pn_provides = self.status.pn_provides
780
781         localdata = data.createCopy(self.configuration.data)
782         bb.data.update_data(localdata)
783
784         def calc_bbfile_priority(filename):
785             for (regex, pri) in self.status.bbfile_config_priorities:
786                 if regex.match(filename):
787                     return pri
788             return 0
789
790         # Handle PREFERRED_PROVIDERS
791         for p in (bb.data.getVar('PREFERRED_PROVIDERS', localdata, 1) or "").split():
792             (providee, provider) = p.split(':')
793             if providee in self.preferred and self.preferred[providee] != provider:
794                 bb.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.preferred[providee]))
795             self.preferred[providee] = provider
796
797         # Calculate priorities for each file
798         for p in self.status.pkg_fn.keys():
799             self.status.bbfile_priority[p] = calc_bbfile_priority(p)
800
801     def buildWorldTargetList(self):
802         """
803          Build package list for "bitbake world"
804         """
805         all_depends = self.status.all_depends
806         pn_provides = self.status.pn_provides
807         bb.debug(1, "collating packages for \"world\"")
808         for f in self.status.possible_world:
809             terminal = True
810             pn = self.status.pkg_fn[f]
811
812             for p in pn_provides[pn]:
813                 if p.startswith('virtual/'):
814                     bb.debug(2, "skipping %s due to %s provider starting with virtual/" % (f, p))
815                     terminal = False
816                     break
817                 for pf in self.status.providers[p]:
818                     if self.status.pkg_fn[pf] != pn:
819                         bb.debug(2, "skipping %s due to both us and %s providing %s" % (f, pf, p))
820                         terminal = False
821                         break
822             if terminal:
823                 self.status.world_target.add(pn)
824
825             # drop reference count now
826             self.status.possible_world = None
827             self.status.all_depends    = None
828
829     def myProgressCallback( self, x, y, f, bb_cache, from_cache ):
830         # feed the status with new input
831
832         self.status.handle_bb_data(f, bb_cache, from_cache)
833
834         if bbdebug > 0:
835             return
836         if os.isatty(sys.stdout.fileno()):
837             sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
838             sys.stdout.flush()
839         else:
840             if x == 1:
841                 sys.stdout.write("Parsing .bb files, please wait...")
842                 sys.stdout.flush()
843             if x == y:
844                 sys.stdout.write("done.")
845                 sys.stdout.flush()
846
847     def interactiveMode( self ):
848         """Drop off into a shell"""
849         try:
850             from bb import shell
851         except ImportError, details:
852             bb.fatal("Sorry, shell not available (%s)" % details )
853         else:
854             bb.data.update_data( self.configuration.data )
855             shell.start( self )
856             sys.exit( 0 )
857
858     def parseConfigurationFile( self, afile ):
859         try:
860             self.configuration.data = bb.parse.handle( afile, self.configuration.data )
861
862             # Add the handlers we inherited by INHERITS
863             # FIXME: This assumes that we included at least one .inc file
864             for var in bb.data.keys(self.configuration.data):
865                 if bb.data.getVarFlag(var, 'handler', self.configuration.data):
866                     bb.event.register(var,bb.data.getVar(var,self.configuration.data))
867
868         except IOError:
869             bb.fatal( "Unable to open %s" % afile )
870         except bb.parse.ParseError, details:
871             bb.fatal( "Unable to parse %s (%s)" % (afile, details) )
872
873     def handleCollections( self, collections ):
874         """Handle collections"""
875         if collections:
876             collection_list = collections.split()
877             for c in collection_list:
878                 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
879                 if regex == None:
880                     bb.error("BBFILE_PATTERN_%s not defined" % c)
881                     continue
882                 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
883                 if priority == None:
884                     bb.error("BBFILE_PRIORITY_%s not defined" % c)
885                     continue
886                 try:
887                     cre = re.compile(regex)
888                 except re.error:
889                     bb.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
890                     continue
891                 try:
892                     pri = int(priority)
893                     self.status.bbfile_config_priorities.append((cre, pri))
894                 except ValueError:
895                     bb.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
896
897
898     def cook( self, configuration, args ):
899         """
900         We are building stuff here. We do the building
901         from here. By default we try to execute task
902         build.
903         """
904
905         self.configuration = configuration
906
907         if not self.configuration.cmd:
908             self.configuration.cmd = "build"
909
910         if self.configuration.debug:
911             bb.debug_level = self.configuration.debug
912
913         self.configuration.data = bb.data.init()
914
915         for f in self.configuration.file:
916             self.parseConfigurationFile( f )
917
918         self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
919
920
921         #
922         # Special updated configuration we use for firing events
923         #
924         self.configuration.event_data = bb.data.createCopy(self.configuration.data)
925         bb.data.update_data(self.configuration.event_data)
926
927         if self.configuration.show_environment:
928             self.showEnvironment()
929             sys.exit( 0 )
930
931         # inject custom variables
932         if not bb.data.getVar("BUILDNAME", self.configuration.data):
933             bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
934         bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
935
936         buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
937
938         if self.configuration.interactive:
939             self.interactiveMode()
940
941         if self.configuration.buildfile is not None:
942             bf = os.path.abspath( self.configuration.buildfile )
943             try:
944                 bbfile_data = bb.parse.handle(bf, self.configuration.data)
945             except IOError:
946                 bb.fatal("Unable to open %s" % bf)
947
948             item = bb.data.getVar('PN', bbfile_data, 1)
949             try:
950                 self.tryBuildPackage( bf, item, bbfile_data )
951             except bb.build.EventException:
952                 bb.error( "Build of '%s' failed" % item )
953
954             sys.exit( self.stats.show() )
955
956         # initialise the parsing status now we know we will need deps
957         self.status = BBParsingStatus()
958
959         ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
960         self.status.ignored_dependencies = Set( ignore.split() )
961
962         self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
963
964         pkgs_to_build = None
965         if args:
966             if not pkgs_to_build:
967                 pkgs_to_build = []
968             pkgs_to_build.extend(args)
969         if not pkgs_to_build:
970                 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
971                 if bbpkgs:
972                         pkgs_to_build = bbpkgs.split()
973         if not pkgs_to_build and not self.configuration.show_versions \
974                              and not self.configuration.interactive \
975                              and not self.configuration.show_environment:
976                 print "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help'"
977                 print "for usage information."
978                 sys.exit(0)
979
980         # Import Psyco if available and not disabled
981         if not self.configuration.disable_psyco:
982             try:
983                 import psyco
984             except ImportError:
985                 if bbdebug == 0:
986                     bb.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
987             else:
988                 psyco.bind( self.collect_bbfiles )
989         else:
990             bb.note("You have disabled Psyco. This decreases performance.")
991
992         try:
993             bb.debug(1, "collecting .bb files")
994             self.collect_bbfiles( self.myProgressCallback )
995             bb.debug(1, "parsing complete")
996             if bbdebug == 0:
997                 print
998             if self.configuration.parse_only:
999                 print "Requested parsing .bb files only.  Exiting."
1000                 return
1001
1002
1003             self.buildDepgraph()
1004
1005             if self.configuration.show_versions:
1006                 self.showVersions()
1007                 sys.exit( 0 )
1008             if 'world' in pkgs_to_build:
1009                 self.buildWorldTargetList()
1010                 pkgs_to_build.remove('world')
1011                 for t in self.status.world_target:
1012                     pkgs_to_build.append(t)
1013     
1014             if self.configuration.dot_graph:
1015                 self.generateDotGraph( pkgs_to_build )
1016                 sys.exit( 0 )
1017
1018
1019             bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data))
1020
1021             failures = 0
1022             for k in pkgs_to_build:
1023                 failed = False
1024                 try:
1025                     if self.buildProvider( k , False ) == 0:
1026                         # already diagnosed
1027                         failed = True
1028                 except bb.build.EventException:
1029                     bb.error("Build of " + k + " failed")
1030                     failed = True
1031
1032                 if failed:
1033                     failures += failures
1034                     if self.configuration.abort:
1035                         sys.exit(1)
1036
1037             bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures))
1038
1039             sys.exit( self.stats.show() )
1040
1041         except KeyboardInterrupt:
1042             print "\nNOTE: KeyboardInterrupt - Build not completed."
1043             sys.exit(1)
1044
1045     def get_bbfiles( self, path = os.getcwd() ):
1046         """Get list of default .bb files by reading out the current directory"""
1047         contents = os.listdir(path)
1048         bbfiles = []
1049         for f in contents:
1050             (root, ext) = os.path.splitext(f)
1051             if ext == ".bb":
1052                 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
1053         return bbfiles
1054
1055     def find_bbfiles( self, path ):
1056         """Find all the .bb files in a directory (uses find)"""
1057         findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
1058         try:
1059             finddata = os.popen(findcmd)
1060         except OSError:
1061             return []
1062         return finddata.readlines()
1063
1064     def collect_bbfiles( self, progressCallback ):
1065         """Collect all available .bb build files"""
1066         self.cb = progressCallback
1067         parsed, cached, skipped, masked = 0, 0, 0, 0
1068         self.bb_cache = bb.cache.init(self)
1069
1070         files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
1071         data.setVar("BBFILES", " ".join(files), self.configuration.data)
1072
1073         if not len(files):
1074             files = self.get_bbfiles()
1075
1076         if not len(files):
1077             bb.error("no files to build.")
1078
1079         newfiles = []
1080         for f in files:
1081             if os.path.isdir(f):
1082                 dirfiles = self.find_bbfiles(f)
1083                 if dirfiles:
1084                     newfiles += dirfiles
1085                     continue
1086             newfiles += glob.glob(f) or [ f ]
1087
1088         bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) or ""
1089         try:
1090             bbmask_compiled = re.compile(bbmask)
1091         except sre_constants.error:
1092             bb.fatal("BBMASK is not a valid regular expression.")
1093
1094         for i in xrange( len( newfiles ) ):
1095             f = newfiles[i]
1096             if bbmask and bbmask_compiled.search(f):
1097                 bb.debug(1, "bbmake: skipping %s" % f)
1098                 masked += 1
1099                 continue
1100             debug(1, "bbmake: parsing %s" % f)
1101
1102             # read a file's metadata
1103             try:
1104                 fromCache, skip = self.bb_cache.loadData(f, self)
1105                 if skip:
1106                     skipped += 1
1107                     #bb.note("Skipping %s" % f)
1108                     self.bb_cache.skip(f)
1109                     continue
1110                 elif fromCache: cached += 1
1111                 else: parsed += 1
1112                 deps = None
1113
1114                 # allow metadata files to add items to BBFILES
1115                 #data.update_data(self.pkgdata[f])
1116                 addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
1117                 if addbbfiles:
1118                     for aof in addbbfiles.split():
1119                         if not files.count(aof):
1120                             if not os.path.isabs(aof):
1121                                 aof = os.path.join(os.path.dirname(f),aof)
1122                             files.append(aof)
1123
1124                 # now inform the caller
1125                 if self.cb is not None:
1126                     self.cb( i + 1, len( newfiles ), f, self.bb_cache, fromCache )
1127
1128             except IOError, e:
1129                 self.bb_cache.remove(f)
1130                 bb.error("opening %s: %s" % (f, e))
1131                 pass
1132             except KeyboardInterrupt:
1133                 self.bb_cache.sync()
1134                 raise
1135             except Exception, e:
1136                 self.bb_cache.remove(f)
1137                 bb.error("%s while parsing %s" % (e, f))
1138             except:
1139                 self.bb_cache.remove(f)
1140                 raise
1141
1142         if self.cb is not None:
1143             print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
1144
1145         self.bb_cache.sync()
1146
1147 #============================================================================#
1148 # main
1149 #============================================================================#
1150
1151 def main():
1152     parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % ( bb.__version__, __version__ ),
1153     usage = """%prog [options] [package ...]
1154
1155 Executes the specified task (default is 'build') for a given set of BitBake files.
1156 It expects that BBFILES is defined, which is a space seperated list of files to
1157 be executed.  BBFILES does support wildcards.
1158 Default BBFILES are the .bb files in the current directory.""" )
1159
1160     parser.add_option( "-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES.",
1161                action = "store", dest = "buildfile", default = None )
1162
1163     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.",
1164                action = "store_false", dest = "abort", default = True )
1165
1166     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
1167                action = "store_true", dest = "force", default = False )
1168
1169     parser.add_option( "-i", "--interactive", help = "drop into the interactive mode.",
1170                action = "store_true", dest = "interactive", default = False )
1171
1172     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)",
1173                action = "store", dest = "cmd", default = "build" )
1174
1175     parser.add_option( "-r", "--read", help = "read the specified file before bitbake.conf",
1176                action = "append", dest = "file", default = [] )
1177
1178     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
1179                action = "store_true", dest = "verbose", default = False )
1180
1181     parser.add_option( "-D", "--debug", help = "Increase the debug level",
1182                action = "count", dest="debug", default = 0)
1183
1184     parser.add_option( "-n", "--dry-run", help = "don't execute, just go through the motions",
1185                action = "store_true", dest = "dry_run", default = False )
1186
1187     parser.add_option( "-p", "--parse-only", help = "quit after parsing the BB files (developers only)",
1188                action = "store_true", dest = "parse_only", default = False )
1189
1190     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
1191                action = "store_true", dest = "disable_psyco", default = False )
1192
1193     parser.add_option( "-s", "--show-versions", help = "show current and preferred versions of all packages",
1194                action = "store_true", dest = "show_versions", default = False )
1195
1196     parser.add_option( "-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)",
1197                action = "store_true", dest = "show_environment", default = False )
1198
1199     parser.add_option( "-g", "--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax",
1200                 action = "store_true", dest = "dot_graph", default = False )
1201
1202     options, args = parser.parse_args( sys.argv )
1203
1204     cooker = BBCooker()
1205     cooker.cook( BBConfiguration( options ), args[1:] )
1206
1207
1208
1209 if __name__ == "__main__":
1210     print """WARNING, WARNING, WARNING
1211 This is a Bitbake from the Unstable/Development Branch.
1212 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."""
1213     import time
1214     time.sleep(5)
1215     main()