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