summaryrefslogtreecommitdiffstats
path: root/bitbake/bin/bitbake
diff options
context:
space:
mode:
authorRichard Purdie <richard@openedhand.com>2007-01-08 23:53:01 +0000
committerRichard Purdie <richard@openedhand.com>2007-01-08 23:53:01 +0000
commitf5665d5bfcfb13d01da9e4c7d5046453e80f7baf (patch)
treeb8908549afaf3006bf3763419711090ac999c2a4 /bitbake/bin/bitbake
parentaec95de5f7dca2afa3a4a0bdb0d4d553c13f680d (diff)
downloadpoky-f5665d5bfcfb13d01da9e4c7d5046453e80f7baf.tar.gz
bitbake: Sync with upstream.
* File licence headers were sanitised causing most of the diff. * cooker.py was created from bin/bitbake. * cvs fetcher port option was added * The -f force option was fixed to work correctly * Multiple entries in rrecrdeps are now handled correctly (allows adding do_deploy to image depends) git-svn-id: https://svn.o-hand.com/repos/poky/trunk@1129 311d38ba-8fff-0310-9ca6-ca027cbcb966
Diffstat (limited to 'bitbake/bin/bitbake')
-rwxr-xr-xbitbake/bin/bitbake679
1 files changed, 19 insertions, 660 deletions
diff --git a/bitbake/bin/bitbake b/bitbake/bin/bitbake
index 33e8933c90..001f229331 100755
--- a/bitbake/bin/bitbake
+++ b/bitbake/bin/bitbake
@@ -9,55 +9,27 @@
9# Copyright (C) 2005 ROAD GmbH 9# Copyright (C) 2005 ROAD GmbH
10# Copyright (C) 2006 Richard Purdie 10# Copyright (C) 2006 Richard Purdie
11# 11#
12# This program is free software; you can redistribute it and/or modify it under 12# This program is free software; you can redistribute it and/or modify
13# the terms of the GNU General Public License as published by the Free Software 13# it under the terms of the GNU General Public License version 2 as
14# Foundation; either version 2 of the License, or (at your option) any later 14# published by the Free Software Foundation.
15# version.
16# 15#
17# This program is distributed in the hope that it will be useful, but WITHOUT 16# This program is distributed in the hope that it will be useful,
18# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20# 20#
21# You should have received a copy of the GNU General Public License along with 21# You should have received a copy of the GNU General Public License along
22# this program; if not, write to the Free Software Foundation, Inc., 59 Temple 22# with this program; if not, write to the Free Software Foundation, Inc.,
23# Place, Suite 330, Boston, MA 02111-1307 USA. 23# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 24
25import sys, os, getopt, glob, copy, os.path, re, time 25import sys, os, getopt, re, time, optparse
26sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib')) 26sys.path.insert(0,os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
27import bb 27import bb
28from bb import utils, data, parse, event, cache, providers, taskdata, runqueue 28from bb import cooker
29from sets import Set
30import itertools, optparse
31
32parsespin = itertools.cycle( r'|/-\\' )
33 29
34__version__ = "1.7.4" 30__version__ = "1.7.4"
35 31
36#============================================================================# 32#============================================================================#
37# BBStatistics
38#============================================================================#
39class BBStatistics:
40 """
41 Manage build statistics for one run
42 """
43 def __init__(self ):
44 self.attempt = 0
45 self.success = 0
46 self.fail = 0
47 self.deps = 0
48
49 def show( self ):
50 print "Build statistics:"
51 print " Attempted builds: %d" % self.attempt
52 if self.fail:
53 print " Failed builds: %d" % self.fail
54 if self.deps:
55 print " Dependencies not satisfied: %d" % self.deps
56 if self.fail or self.deps: return 1
57 else: return 0
58
59
60#============================================================================#
61# BBOptions 33# BBOptions
62#============================================================================# 34#============================================================================#
63class BBConfiguration( object ): 35class BBConfiguration( object ):
@@ -68,622 +40,6 @@ class BBConfiguration( object ):
68 for key, val in options.__dict__.items(): 40 for key, val in options.__dict__.items():
69 setattr( self, key, val ) 41 setattr( self, key, val )
70 42
71#============================================================================#
72# BBCooker
73#============================================================================#
74class BBCooker:
75 """
76 Manages one bitbake build run
77 """
78
79 Statistics = BBStatistics # make it visible from the shell
80
81 def __init__( self ):
82 self.build_cache_fail = []
83 self.build_cache = []
84 self.stats = BBStatistics()
85 self.status = None
86
87 self.cache = None
88 self.bb_cache = None
89
90 def tryBuildPackage(self, fn, item, task, the_data, build_depends):
91 """
92 Build one task of a package, optionally build following task depends
93 """
94 bb.event.fire(bb.event.PkgStarted(item, the_data))
95 try:
96 self.stats.attempt += 1
97 if self.configuration.force:
98 bb.data.setVarFlag('do_%s' % task, 'force', 1, the_data)
99 if not build_depends:
100 bb.data.setVarFlag('do_%s' % task, 'dontrundeps', 1, the_data)
101 if not self.configuration.dry_run:
102 bb.build.exec_task('do_%s' % task, the_data)
103 bb.event.fire(bb.event.PkgSucceeded(item, the_data))
104 self.build_cache.append(fn)
105 return True
106 except bb.build.FuncFailed:
107 self.stats.fail += 1
108 bb.msg.error(bb.msg.domain.Build, "task stack execution failed")
109 bb.event.fire(bb.event.PkgFailed(item, the_data))
110 self.build_cache_fail.append(fn)
111 raise
112 except bb.build.EventException, e:
113 self.stats.fail += 1
114 event = e.args[1]
115 bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event))
116 bb.event.fire(bb.event.PkgFailed(item, the_data))
117 self.build_cache_fail.append(fn)
118 raise
119
120 def tryBuild( self, fn, build_depends):
121 """
122 Build a provider and its dependencies.
123 build_depends is a list of previous build dependencies (not runtime)
124 If build_depends is empty, we're dealing with a runtime depends
125 """
126
127 the_data = self.bb_cache.loadDataFull(fn, self.configuration.data)
128
129 item = self.status.pkg_fn[fn]
130
131 if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data) and not self.configuration.force:
132 self.build_cache.append(fn)
133 return True
134
135 return self.tryBuildPackage(fn, item, self.configuration.cmd, the_data, build_depends)
136
137 def showVersions( self ):
138 pkg_pn = self.status.pkg_pn
139 preferred_versions = {}
140 latest_versions = {}
141
142 # Sort by priority
143 for pn in pkg_pn.keys():
144 (last_ver,last_file,pref_ver,pref_file) = bb.providers.findBestProvider(pn, self.configuration.data, self.status)
145 preferred_versions[pn] = (pref_ver, pref_file)
146 latest_versions[pn] = (last_ver, last_file)
147
148 pkg_list = pkg_pn.keys()
149 pkg_list.sort()
150
151 for p in pkg_list:
152 pref = preferred_versions[p]
153 latest = latest_versions[p]
154
155 if pref != latest:
156 prefstr = pref[0][0] + "-" + pref[0][1]
157 else:
158 prefstr = ""
159
160 print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1],
161 prefstr)
162
163
164 def showEnvironment( self ):
165 """Show the outer or per-package environment"""
166 if self.configuration.buildfile:
167 self.cb = None
168 self.bb_cache = bb.cache.init(self)
169 try:
170 self.configuration.data = self.bb_cache.loadDataFull(self.configuration.buildfile, self.configuration.data)
171 except IOError, e:
172 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to read %s: %s" % ( self.configuration.buildfile, e ))
173 except Exception, e:
174 bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e)
175 # emit variables and shell functions
176 try:
177 data.update_data( self.configuration.data )
178 data.emit_env(sys.__stdout__, self.configuration.data, True)
179 except Exception, e:
180 bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e)
181 # emit the metadata which isnt valid shell
182 data.expandKeys( self.configuration.data )
183 for e in self.configuration.data.keys():
184 if data.getVarFlag( e, 'python', self.configuration.data ):
185 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1)))
186
187 def generateDotGraph( self, pkgs_to_build, ignore_deps ):
188 """
189 Generate a task dependency graph.
190
191 pkgs_to_build A list of packages that needs to be built
192 ignore_deps A list of names where processing of dependencies
193 should be stopped. e.g. dependencies that get
194 """
195
196 for dep in ignore_deps:
197 self.status.ignored_dependencies.add(dep)
198
199 localdata = data.createCopy(self.configuration.data)
200 bb.data.update_data(localdata)
201 bb.data.expandKeys(localdata)
202 taskdata = bb.taskdata.TaskData(self.configuration.abort)
203
204 runlist = []
205 try:
206 for k in pkgs_to_build:
207 taskdata.add_provider(localdata, self.status, k)
208 runlist.append([k, "do_%s" % self.configuration.cmd])
209 taskdata.add_unresolved(localdata, self.status)
210 except bb.providers.NoProvider:
211 sys.exit(1)
212 rq = bb.runqueue.RunQueue()
213 rq.prepare_runqueue(self.configuration.data, self.status, taskdata, runlist)
214
215 seen_fnids = []
216 depends_file = file('depends.dot', 'w' )
217 tdepends_file = file('task-depends.dot', 'w' )
218 print >> depends_file, "digraph depends {"
219 print >> tdepends_file, "digraph depends {"
220 rq.prio_map.reverse()
221 for task1 in range(len(rq.runq_fnid)):
222 task = rq.prio_map[task1]
223 taskname = rq.runq_task[task]
224 fnid = rq.runq_fnid[task]
225 fn = taskdata.fn_index[fnid]
226 pn = self.status.pkg_fn[fn]
227 version = self.bb_cache.getVar('PV', fn, True ) + '-' + self.bb_cache.getVar('PR', fn, True)
228 print >> tdepends_file, '"%s.%s" [label="%s %s\\n%s\\n%s"]' % (pn, taskname, pn, taskname, version, fn)
229 for dep in rq.runq_depends[task]:
230 depfn = taskdata.fn_index[rq.runq_fnid[dep]]
231 deppn = self.status.pkg_fn[depfn]
232 print >> tdepends_file, '"%s.%s" -> "%s.%s"' % (pn, rq.runq_task[task], deppn, rq.runq_task[dep])
233 if fnid not in seen_fnids:
234 seen_fnids.append(fnid)
235 packages = []
236 print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn)
237 for depend in self.status.deps[fn]:
238 print >> depends_file, '"%s" -> "%s"' % (pn, depend)
239 rdepends = self.status.rundeps[fn]
240 for package in rdepends:
241 for rdepend in rdepends[package]:
242 print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend)
243 packages.append(package)
244 rrecs = self.status.runrecs[fn]
245 for package in rrecs:
246 for rdepend in rrecs[package]:
247 print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend)
248 if not package in packages:
249 packages.append(package)
250 for package in packages:
251 if package != pn:
252 print >> depends_file, '"%s" [label="%s(%s) %s\\n%s"]' % (package, package, pn, version, fn)
253 for depend in self.status.deps[fn]:
254 print >> depends_file, '"%s" -> "%s"' % (package, depend)
255 # Prints a flattened form of the above where subpackages of a package are merged into the main pn
256 #print >> depends_file, '"%s" [label="%s %s\\n%s\\n%s"]' % (pn, pn, taskname, version, fn)
257 #for rdep in taskdata.rdepids[fnid]:
258 # print >> depends_file, '"%s" -> "%s" [style=dashed]' % (pn, taskdata.run_names_index[rdep])
259 #for dep in taskdata.depids[fnid]:
260 # print >> depends_file, '"%s" -> "%s"' % (pn, taskdata.build_names_index[dep])
261 print >> depends_file, "}"
262 print >> tdepends_file, "}"
263 bb.msg.note(1, bb.msg.domain.Collection, "Dependencies saved to 'depends.dot'")
264 bb.msg.note(1, bb.msg.domain.Collection, "Task dependencies saved to 'task-depends.dot'")
265
266 def buildDepgraph( self ):
267 all_depends = self.status.all_depends
268 pn_provides = self.status.pn_provides
269
270 localdata = data.createCopy(self.configuration.data)
271 bb.data.update_data(localdata)
272 bb.data.expandKeys(localdata)
273
274 def calc_bbfile_priority(filename):
275 for (regex, pri) in self.status.bbfile_config_priorities:
276 if regex.match(filename):
277 return pri
278 return 0
279
280 # Handle PREFERRED_PROVIDERS
281 for p in (bb.data.getVar('PREFERRED_PROVIDERS', localdata, 1) or "").split():
282 (providee, provider) = p.split(':')
283 if providee in self.status.preferred and self.status.preferred[providee] != provider:
284 bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee]))
285 self.status.preferred[providee] = provider
286
287 # Calculate priorities for each file
288 for p in self.status.pkg_fn.keys():
289 self.status.bbfile_priority[p] = calc_bbfile_priority(p)
290
291 def buildWorldTargetList(self):
292 """
293 Build package list for "bitbake world"
294 """
295 all_depends = self.status.all_depends
296 pn_provides = self.status.pn_provides
297 bb.msg.debug(1, bb.msg.domain.Parsing, "collating packages for \"world\"")
298 for f in self.status.possible_world:
299 terminal = True
300 pn = self.status.pkg_fn[f]
301
302 for p in pn_provides[pn]:
303 if p.startswith('virtual/'):
304 bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to %s provider starting with virtual/" % (f, p))
305 terminal = False
306 break
307 for pf in self.status.providers[p]:
308 if self.status.pkg_fn[pf] != pn:
309 bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to both us and %s providing %s" % (f, pf, p))
310 terminal = False
311 break
312 if terminal:
313 self.status.world_target.add(pn)
314
315 # drop reference count now
316 self.status.possible_world = None
317 self.status.all_depends = None
318
319 def myProgressCallback( self, x, y, f, from_cache ):
320 """Update any tty with the progress change"""
321 if os.isatty(sys.stdout.fileno()):
322 sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
323 sys.stdout.flush()
324 else:
325 if x == 1:
326 sys.stdout.write("Parsing .bb files, please wait...")
327 sys.stdout.flush()
328 if x == y:
329 sys.stdout.write("done.")
330 sys.stdout.flush()
331
332 def interactiveMode( self ):
333 """Drop off into a shell"""
334 try:
335 from bb import shell
336 except ImportError, details:
337 bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details )
338 else:
339 bb.data.update_data( self.configuration.data )
340 bb.data.expandKeys( self.configuration.data )
341 shell.start( self )
342 sys.exit( 0 )
343
344 def parseConfigurationFile( self, afile ):
345 try:
346 self.configuration.data = bb.parse.handle( afile, self.configuration.data )
347
348 # Add the handlers we inherited by INHERIT
349 # we need to do this manually as it is not guranteed
350 # we will pick up these classes... as we only INHERIT
351 # on .inc and .bb files but not on .conf
352 data = bb.data.createCopy( self.configuration.data )
353 inherits = ["base"] + (bb.data.getVar('INHERIT', data, True ) or "").split()
354 for inherit in inherits:
355 data = bb.parse.handle( os.path.join('classes', '%s.bbclass' % inherit ), data, True )
356
357 # FIXME: This assumes that we included at least one .inc file
358 for var in bb.data.keys(data):
359 if bb.data.getVarFlag(var, 'handler', data):
360 bb.event.register(var,bb.data.getVar(var, data))
361
362 except IOError:
363 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to open %s" % afile )
364 except bb.parse.ParseError, details:
365 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (afile, details) )
366
367 def handleCollections( self, collections ):
368 """Handle collections"""
369 if collections:
370 collection_list = collections.split()
371 for c in collection_list:
372 regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
373 if regex == None:
374 bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s not defined" % c)
375 continue
376 priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
377 if priority == None:
378 bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PRIORITY_%s not defined" % c)
379 continue
380 try:
381 cre = re.compile(regex)
382 except re.error:
383 bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
384 continue
385 try:
386 pri = int(priority)
387 self.status.bbfile_config_priorities.append((cre, pri))
388 except ValueError:
389 bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
390
391
392 def cook( self, configuration, args ):
393 """
394 We are building stuff here. We do the building
395 from here. By default we try to execute task
396 build.
397 """
398
399 self.configuration = configuration
400
401 if self.configuration.verbose:
402 bb.msg.set_verbose(True)
403
404 if self.configuration.debug:
405 bb.msg.set_debug_level(self.configuration.debug)
406 else:
407 bb.msg.set_debug_level(0)
408
409 if self.configuration.debug_domains:
410 bb.msg.set_debug_domains(self.configuration.debug_domains)
411
412 self.configuration.data = bb.data.init()
413
414 for f in self.configuration.file:
415 self.parseConfigurationFile( f )
416
417 self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
418
419 if not self.configuration.cmd:
420 self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data)
421
422 # For backwards compatibility - REMOVE ME
423 if not self.configuration.cmd:
424 self.configuration.cmd = "build"
425
426 #
427 # Special updated configuration we use for firing events
428 #
429 self.configuration.event_data = bb.data.createCopy(self.configuration.data)
430 bb.data.update_data(self.configuration.event_data)
431
432 if self.configuration.show_environment:
433 self.showEnvironment()
434 sys.exit( 0 )
435
436 # inject custom variables
437 if not bb.data.getVar("BUILDNAME", self.configuration.data):
438 bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
439 bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
440
441 buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
442
443 if self.configuration.interactive:
444 self.interactiveMode()
445
446 if self.configuration.buildfile is not None:
447 bf = os.path.abspath( self.configuration.buildfile )
448 try:
449 os.stat(bf)
450 except OSError:
451 (filelist, masked) = self.collect_bbfiles()
452 regexp = re.compile(self.configuration.buildfile)
453 matches = []
454 for f in filelist:
455 if regexp.search(f) and os.path.isfile(f):
456 bf = f
457 matches.append(f)
458 if len(matches) != 1:
459 bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (self.configuration.buildfile, len(matches)))
460 for f in matches:
461 bb.msg.error(bb.msg.domain.Parsing, " %s" % f)
462 sys.exit(1)
463 bf = matches[0]
464
465 bbfile_data = bb.parse.handle(bf, self.configuration.data)
466
467 item = bb.data.getVar('PN', bbfile_data, 1)
468 try:
469 self.tryBuildPackage(bf, item, self.configuration.cmd, bbfile_data, True)
470 except bb.build.EventException:
471 bb.msg.error(bb.msg.domain.Build, "Build of '%s' failed" % item )
472
473 sys.exit( self.stats.show() )
474
475 # initialise the parsing status now we know we will need deps
476 self.status = bb.cache.CacheData()
477
478 ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
479 self.status.ignored_dependencies = Set( ignore.split() )
480
481 self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
482
483 pkgs_to_build = None
484 if args:
485 if not pkgs_to_build:
486 pkgs_to_build = []
487 pkgs_to_build.extend(args)
488 if not pkgs_to_build:
489 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
490 if bbpkgs:
491 pkgs_to_build = bbpkgs.split()
492 if not pkgs_to_build and not self.configuration.show_versions \
493 and not self.configuration.interactive \
494 and not self.configuration.show_environment:
495 print "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help'"
496 print "for usage information."
497 sys.exit(0)
498
499 # Import Psyco if available and not disabled
500 if not self.configuration.disable_psyco:
501 try:
502 import psyco
503 except ImportError:
504 bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
505 else:
506 psyco.bind( self.parse_bbfiles )
507 else:
508 bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
509
510 try:
511 bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files")
512 (filelist, masked) = self.collect_bbfiles()
513 self.parse_bbfiles(filelist, masked, self.myProgressCallback)
514 bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete")
515 print
516 if self.configuration.parse_only:
517 bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only. Exiting.")
518 return
519
520
521 self.buildDepgraph()
522
523 if self.configuration.show_versions:
524 self.showVersions()
525 sys.exit( 0 )
526 if 'world' in pkgs_to_build:
527 self.buildWorldTargetList()
528 pkgs_to_build.remove('world')
529 for t in self.status.world_target:
530 pkgs_to_build.append(t)
531
532 if self.configuration.dot_graph:
533 self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps )
534 sys.exit( 0 )
535
536 bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data))
537
538 localdata = data.createCopy(self.configuration.data)
539 bb.data.update_data(localdata)
540 bb.data.expandKeys(localdata)
541
542 taskdata = bb.taskdata.TaskData(self.configuration.abort)
543
544 runlist = []
545 try:
546 for k in pkgs_to_build:
547 taskdata.add_provider(localdata, self.status, k)
548 runlist.append([k, "do_%s" % self.configuration.cmd])
549 taskdata.add_unresolved(localdata, self.status)
550 except bb.providers.NoProvider:
551 sys.exit(1)
552
553 rq = bb.runqueue.RunQueue()
554 rq.prepare_runqueue(self.configuration.data, self.status, taskdata, runlist)
555 try:
556 failures = rq.execute_runqueue(self, self.configuration.data, self.status, taskdata, runlist)
557 except runqueue.TaskFailure, fnids:
558 for fnid in fnids:
559 bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid])
560 sys.exit(1)
561 bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures))
562
563 sys.exit( self.stats.show() )
564
565 except KeyboardInterrupt:
566 bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.")
567 sys.exit(1)
568
569 def get_bbfiles( self, path = os.getcwd() ):
570 """Get list of default .bb files by reading out the current directory"""
571 contents = os.listdir(path)
572 bbfiles = []
573 for f in contents:
574 (root, ext) = os.path.splitext(f)
575 if ext == ".bb":
576 bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
577 return bbfiles
578
579 def find_bbfiles( self, path ):
580 """Find all the .bb files in a directory (uses find)"""
581 findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
582 try:
583 finddata = os.popen(findcmd)
584 except OSError:
585 return []
586 return finddata.readlines()
587
588 def collect_bbfiles( self ):
589 """Collect all available .bb build files"""
590 parsed, cached, skipped, masked = 0, 0, 0, 0
591 self.bb_cache = bb.cache.init(self)
592
593 files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
594 data.setVar("BBFILES", " ".join(files), self.configuration.data)
595
596 if not len(files):
597 files = self.get_bbfiles()
598
599 if not len(files):
600 bb.msg.error(bb.msg.domain.Collection, "no files to build.")
601
602 newfiles = []
603 for f in files:
604 if os.path.isdir(f):
605 dirfiles = self.find_bbfiles(f)
606 if dirfiles:
607 newfiles += dirfiles
608 continue
609 newfiles += glob.glob(f) or [ f ]
610
611 bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1)
612
613 if not bbmask:
614 return (newfiles, 0)
615
616 try:
617 bbmask_compiled = re.compile(bbmask)
618 except sre_constants.error:
619 bb.msg.fatal(bb.msg.domain.Collection, "BBMASK is not a valid regular expression.")
620
621 finalfiles = []
622 for i in xrange( len( newfiles ) ):
623 f = newfiles[i]
624 if bbmask and bbmask_compiled.search(f):
625 bb.msg.debug(1, bb.msg.domain.Collection, "skipping masked file %s" % f)
626 masked += 1
627 continue
628 finalfiles.append(f)
629
630 return (finalfiles, masked)
631
632 def parse_bbfiles(self, filelist, masked, progressCallback = None):
633 parsed, cached, skipped = 0, 0, 0
634 for i in xrange( len( filelist ) ):
635 f = filelist[i]
636
637 bb.msg.debug(1, bb.msg.domain.Collection, "parsing %s" % f)
638
639 # read a file's metadata
640 try:
641 fromCache, skip = self.bb_cache.loadData(f, self.configuration.data)
642 if skip:
643 skipped += 1
644 bb.msg.debug(2, bb.msg.domain.Collection, "skipping %s" % f)
645 self.bb_cache.skip(f)
646 continue
647 elif fromCache: cached += 1
648 else: parsed += 1
649 deps = None
650
651 # Disabled by RP as was no longer functional
652 # allow metadata files to add items to BBFILES
653 #data.update_data(self.pkgdata[f])
654 #addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None
655 #if addbbfiles:
656 # for aof in addbbfiles.split():
657 # if not files.count(aof):
658 # if not os.path.isabs(aof):
659 # aof = os.path.join(os.path.dirname(f),aof)
660 # files.append(aof)
661
662 self.bb_cache.handle_data(f, self.status)
663
664 # now inform the caller
665 if progressCallback is not None:
666 progressCallback( i + 1, len( filelist ), f, fromCache )
667
668 except IOError, e:
669 self.bb_cache.remove(f)
670 bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e))
671 pass
672 except KeyboardInterrupt:
673 self.bb_cache.sync()
674 raise
675 except Exception, e:
676 self.bb_cache.remove(f)
677 bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f))
678 except:
679 self.bb_cache.remove(f)
680 raise
681
682 if progressCallback is not None:
683 print "\r" # need newline after Handling Bitbake files message
684 bb.msg.note(1, bb.msg.domain.Collection, "Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ))
685
686 self.bb_cache.sync()
687 43
688#============================================================================# 44#============================================================================#
689# main 45# main
@@ -747,12 +103,13 @@ Default BBFILES are the .bb files in the current directory.""" )
747 action = "append", dest = "debug_domains", default = [] ) 103 action = "append", dest = "debug_domains", default = [] )
748 104
749 105
750 options, args = parser.parse_args( sys.argv ) 106 options, args = parser.parse_args(sys.argv)
751
752 cooker = BBCooker()
753 cooker.cook( BBConfiguration( options ), args[1:] )
754 107
108 configuration = BBConfiguration(options)
109 configuration.pkgs_to_build = []
110 configuration.pkgs_to_build.extend(args[1:])
755 111
112 bb.cooker.BBCooker().cook(configuration)
756 113
757if __name__ == "__main__": 114if __name__ == "__main__":
758 main() 115 main()
@@ -761,4 +118,6 @@ if __name__ == "__main__":
761 profile.run('main()', "profile.log") 118 profile.run('main()', "profile.log")
762 import pstats 119 import pstats
763 p = pstats.Stats('profile.log') 120 p = pstats.Stats('profile.log')
121 p.sort_stats('time')
764 p.print_stats() 122 p.print_stats()
123 p.print_callers()