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