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