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