summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/cooker.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/cooker.py')
-rw-r--r--bitbake/lib/bb/cooker.py318
1 files changed, 153 insertions, 165 deletions
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py
index 8a9c588633..4b2a906133 100644
--- a/bitbake/lib/bb/cooker.py
+++ b/bitbake/lib/bb/cooker.py
@@ -31,29 +31,6 @@ import itertools
31parsespin = itertools.cycle( r'|/-\\' ) 31parsespin = itertools.cycle( r'|/-\\' )
32 32
33#============================================================================# 33#============================================================================#
34# BBStatistics
35#============================================================================#
36class BBStatistics:
37 """
38 Manage build statistics for one run
39 """
40 def __init__(self ):
41 self.attempt = 0
42 self.success = 0
43 self.fail = 0
44 self.deps = 0
45
46 def show( self ):
47 print "Build statistics:"
48 print " Attempted builds: %d" % self.attempt
49 if self.fail:
50 print " Failed builds: %d" % self.fail
51 if self.deps:
52 print " Dependencies not satisfied: %d" % self.deps
53 if self.fail or self.deps: return 1
54 else: return 0
55
56#============================================================================#
57# BBCooker 34# BBCooker
58#============================================================================# 35#============================================================================#
59class BBCooker: 36class BBCooker:
@@ -61,43 +38,61 @@ class BBCooker:
61 Manages one bitbake build run 38 Manages one bitbake build run
62 """ 39 """
63 40
64 Statistics = BBStatistics # make it visible from the shell 41 def __init__(self, configuration):
65
66 def __init__( self ):
67 self.build_cache_fail = []
68 self.build_cache = []
69 self.stats = BBStatistics()
70 self.status = None 42 self.status = None
71 43
72 self.cache = None 44 self.cache = None
73 self.bb_cache = None 45 self.bb_cache = None
74 46
47 self.configuration = configuration
48
49 if self.configuration.verbose:
50 bb.msg.set_verbose(True)
51
52 if self.configuration.debug:
53 bb.msg.set_debug_level(self.configuration.debug)
54 else:
55 bb.msg.set_debug_level(0)
56
57 if self.configuration.debug_domains:
58 bb.msg.set_debug_domains(self.configuration.debug_domains)
59
60 self.configuration.data = bb.data.init()
61
62 for f in self.configuration.file:
63 self.parseConfigurationFile( f )
64
65 self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
66
67 if not self.configuration.cmd:
68 self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data) or "build"
69
70 #
71 # Special updated configuration we use for firing events
72 #
73 self.configuration.event_data = bb.data.createCopy(self.configuration.data)
74 bb.data.update_data(self.configuration.event_data)
75
75 def tryBuildPackage(self, fn, item, task, the_data, build_depends): 76 def tryBuildPackage(self, fn, item, task, the_data, build_depends):
76 """ 77 """
77 Build one task of a package, optionally build following task depends 78 Build one task of a package, optionally build following task depends
78 """ 79 """
79 bb.event.fire(bb.event.PkgStarted(item, the_data)) 80 bb.event.fire(bb.event.PkgStarted(item, the_data))
80 try: 81 try:
81 self.stats.attempt += 1
82 if not build_depends: 82 if not build_depends:
83 bb.data.setVarFlag('do_%s' % task, 'dontrundeps', 1, the_data) 83 bb.data.setVarFlag('do_%s' % task, 'dontrundeps', 1, the_data)
84 if not self.configuration.dry_run: 84 if not self.configuration.dry_run:
85 bb.build.exec_task('do_%s' % task, the_data) 85 bb.build.exec_task('do_%s' % task, the_data)
86 bb.event.fire(bb.event.PkgSucceeded(item, the_data)) 86 bb.event.fire(bb.event.PkgSucceeded(item, the_data))
87 self.build_cache.append(fn)
88 return True 87 return True
89 except bb.build.FuncFailed: 88 except bb.build.FuncFailed:
90 self.stats.fail += 1
91 bb.msg.error(bb.msg.domain.Build, "task stack execution failed") 89 bb.msg.error(bb.msg.domain.Build, "task stack execution failed")
92 bb.event.fire(bb.event.PkgFailed(item, the_data)) 90 bb.event.fire(bb.event.PkgFailed(item, the_data))
93 self.build_cache_fail.append(fn)
94 raise 91 raise
95 except bb.build.EventException, e: 92 except bb.build.EventException, e:
96 self.stats.fail += 1
97 event = e.args[1] 93 event = e.args[1]
98 bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event)) 94 bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event))
99 bb.event.fire(bb.event.PkgFailed(item, the_data)) 95 bb.event.fire(bb.event.PkgFailed(item, the_data))
100 self.build_cache_fail.append(fn)
101 raise 96 raise
102 97
103 def tryBuild( self, fn, build_depends): 98 def tryBuild( self, fn, build_depends):
@@ -112,12 +107,11 @@ class BBCooker:
112 item = self.status.pkg_fn[fn] 107 item = self.status.pkg_fn[fn]
113 108
114 if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data): 109 if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
115 self.build_cache.append(fn)
116 return True 110 return True
117 111
118 return self.tryBuildPackage(fn, item, self.configuration.cmd, the_data, build_depends) 112 return self.tryBuildPackage(fn, item, self.configuration.cmd, the_data, build_depends)
119 113
120 def showVersions( self ): 114 def showVersions(self):
121 pkg_pn = self.status.pkg_pn 115 pkg_pn = self.status.pkg_pn
122 preferred_versions = {} 116 preferred_versions = {}
123 latest_versions = {} 117 latest_versions = {}
@@ -136,11 +130,11 @@ class BBCooker:
136 latest = latest_versions[p] 130 latest = latest_versions[p]
137 131
138 if pref != latest: 132 if pref != latest:
139 prefstr = pref[0][0] + "-" + pref[0][1] 133 prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2]
140 else: 134 else:
141 prefstr = "" 135 prefstr = ""
142 136
143 print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1], 137 print "%-30s %20s %20s" % (p, latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2],
144 prefstr) 138 prefstr)
145 139
146 140
@@ -192,8 +186,8 @@ class BBCooker:
192 taskdata.add_unresolved(localdata, self.status) 186 taskdata.add_unresolved(localdata, self.status)
193 except bb.providers.NoProvider: 187 except bb.providers.NoProvider:
194 sys.exit(1) 188 sys.exit(1)
195 rq = bb.runqueue.RunQueue() 189 rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist)
196 rq.prepare_runqueue(self, self.configuration.data, self.status, taskdata, runlist) 190 rq.prepare_runqueue()
197 191
198 seen_fnids = [] 192 seen_fnids = []
199 depends_file = file('depends.dot', 'w' ) 193 depends_file = file('depends.dot', 'w' )
@@ -371,98 +365,138 @@ class BBCooker:
371 except ValueError: 365 except ValueError:
372 bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority)) 366 bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
373 367
368 def buildSetVars(self):
369 """
370 Setup any variables needed before starting a build
371 """
372 if not bb.data.getVar("BUILDNAME", self.configuration.data):
373 bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
374 bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
374 375
375 def cook(self, configuration): 376 def buildFile(self, buildfile):
376 """ 377 """
377 We are building stuff here. We do the building 378 Build the file matching regexp buildfile
378 from here. By default we try to execute task
379 build.
380 """ 379 """
381 380
382 self.configuration = configuration 381 bf = os.path.abspath(buildfile)
382 try:
383 os.stat(bf)
384 except OSError:
385 (filelist, masked) = self.collect_bbfiles()
386 regexp = re.compile(buildfile)
387 matches = []
388 for f in filelist:
389 if regexp.search(f) and os.path.isfile(f):
390 bf = f
391 matches.append(f)
392 if len(matches) != 1:
393 bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (buildfile, len(matches)))
394 for f in matches:
395 bb.msg.error(bb.msg.domain.Parsing, " %s" % f)
396 sys.exit(1)
397 bf = matches[0]
383 398
384 if self.configuration.verbose: 399 bbfile_data = bb.parse.handle(bf, self.configuration.data)
385 bb.msg.set_verbose(True)
386 400
387 if self.configuration.debug: 401 # Remove stamp for target if force mode active
388 bb.msg.set_debug_level(self.configuration.debug) 402 if self.configuration.force:
403 bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (self.configuration.cmd, bf))
404 bb.build.del_stamp('do_%s' % self.configuration.cmd, bbfile_data)
405
406 item = bb.data.getVar('PN', bbfile_data, 1)
407 try:
408 self.tryBuildPackage(bf, item, self.configuration.cmd, bbfile_data, True)
409 except bb.build.EventException:
410 bb.msg.error(bb.msg.domain.Build, "Build of '%s' failed" % item )
411
412 sys.exit(0)
413
414 def buildTargets(self, targets):
415 """
416 Attempt to build the targets specified
417 """
418
419 buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
420 bb.event.fire(bb.event.BuildStarted(buildname, targets, self.configuration.event_data))
421
422 localdata = data.createCopy(self.configuration.data)
423 bb.data.update_data(localdata)
424 bb.data.expandKeys(localdata)
425
426 taskdata = bb.taskdata.TaskData(self.configuration.abort)
427
428 runlist = []
429 try:
430 for k in targets:
431 taskdata.add_provider(localdata, self.status, k)
432 runlist.append([k, "do_%s" % self.configuration.cmd])
433 taskdata.add_unresolved(localdata, self.status)
434 except bb.providers.NoProvider:
435 sys.exit(1)
436
437 rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist)
438 rq.prepare_runqueue()
439 try:
440 failures = rq.execute_runqueue()
441 except runqueue.TaskFailure, fnids:
442 for fnid in fnids:
443 bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid])
444 sys.exit(1)
445 bb.event.fire(bb.event.BuildCompleted(buildname, targets, self.configuration.event_data, failures))
446
447 sys.exit(0)
448
449 def updateCache(self):
450 # Import Psyco if available and not disabled
451 if not self.configuration.disable_psyco:
452 try:
453 import psyco
454 except ImportError:
455 bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
456 else:
457 psyco.bind( self.parse_bbfiles )
389 else: 458 else:
390 bb.msg.set_debug_level(0) 459 bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
391 460
392 if self.configuration.debug_domains: 461 self.status = bb.cache.CacheData()
393 bb.msg.set_debug_domains(self.configuration.debug_domains)
394 462
395 self.configuration.data = bb.data.init() 463 ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
464 self.status.ignored_dependencies = Set( ignore.split() )
396 465
397 for f in self.configuration.file: 466 self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
398 self.parseConfigurationFile( f )
399 467
400 self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) ) 468 bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files")
469 (filelist, masked) = self.collect_bbfiles()
470 self.parse_bbfiles(filelist, masked, self.myProgressCallback)
471 bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete")
401 472
402 if not self.configuration.cmd: 473 self.buildDepgraph()
403 self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data) or "build"
404 474
405 # 475 def cook(self):
406 # Special updated configuration we use for firing events 476 """
407 # 477 We are building stuff here. We do the building
408 self.configuration.event_data = bb.data.createCopy(self.configuration.data) 478 from here. By default we try to execute task
409 bb.data.update_data(self.configuration.event_data) 479 build.
480 """
410 481
411 if self.configuration.show_environment: 482 if self.configuration.show_environment:
412 self.showEnvironment() 483 self.showEnvironment()
413 sys.exit( 0 ) 484 sys.exit( 0 )
414 485
415 # inject custom variables 486 self.buildSetVars()
416 if not bb.data.getVar("BUILDNAME", self.configuration.data):
417 bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
418 bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data)
419
420 buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
421 487
422 if self.configuration.interactive: 488 if self.configuration.interactive:
423 self.interactiveMode() 489 self.interactiveMode()
424 490
425 if self.configuration.buildfile is not None: 491 if self.configuration.buildfile is not None:
426 bf = os.path.abspath( self.configuration.buildfile ) 492 return self.buildFile(self.configuration.buildfile)
427 try:
428 os.stat(bf)
429 except OSError:
430 (filelist, masked) = self.collect_bbfiles()
431 regexp = re.compile(self.configuration.buildfile)
432 matches = []
433 for f in filelist:
434 if regexp.search(f) and os.path.isfile(f):
435 bf = f
436 matches.append(f)
437 if len(matches) != 1:
438 bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (self.configuration.buildfile, len(matches)))
439 for f in matches:
440 bb.msg.error(bb.msg.domain.Parsing, " %s" % f)
441 sys.exit(1)
442 bf = matches[0]
443
444 bbfile_data = bb.parse.handle(bf, self.configuration.data)
445
446 # Remove stamp for target if force mode active
447 if self.configuration.force:
448 bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (self.configuration.cmd, bf))
449 bb.build.del_stamp('do_%s' % self.configuration.cmd, bbfile_data)
450
451 item = bb.data.getVar('PN', bbfile_data, 1)
452 try:
453 self.tryBuildPackage(bf, item, self.configuration.cmd, bbfile_data, True)
454 except bb.build.EventException:
455 bb.msg.error(bb.msg.domain.Build, "Build of '%s' failed" % item )
456
457 sys.exit( self.stats.show() )
458 493
459 # initialise the parsing status now we know we will need deps 494 # initialise the parsing status now we know we will need deps
460 self.status = bb.cache.CacheData() 495 self.updateCache()
461 496
462 ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or "" 497 if self.configuration.parse_only:
463 self.status.ignored_dependencies = Set( ignore.split() ) 498 bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only. Exiting.")
464 499 return 0
465 self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
466 500
467 pkgs_to_build = self.configuration.pkgs_to_build 501 pkgs_to_build = self.configuration.pkgs_to_build
468 502
@@ -475,30 +509,7 @@ class BBCooker:
475 print "for usage information." 509 print "for usage information."
476 sys.exit(0) 510 sys.exit(0)
477 511
478 # Import Psyco if available and not disabled
479 if not self.configuration.disable_psyco:
480 try:
481 import psyco
482 except ImportError:
483 bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
484 else:
485 psyco.bind( self.parse_bbfiles )
486 else:
487 bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
488
489 try: 512 try:
490 bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files")
491 (filelist, masked) = self.collect_bbfiles()
492 self.parse_bbfiles(filelist, masked, self.myProgressCallback)
493 bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete")
494 print
495 if self.configuration.parse_only:
496 bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only. Exiting.")
497 return
498
499
500 self.buildDepgraph()
501
502 if self.configuration.show_versions: 513 if self.configuration.show_versions:
503 self.showVersions() 514 self.showVersions()
504 sys.exit( 0 ) 515 sys.exit( 0 )
@@ -512,34 +523,7 @@ class BBCooker:
512 self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps ) 523 self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps )
513 sys.exit( 0 ) 524 sys.exit( 0 )
514 525
515 bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data)) 526 return self.buildTargets(pkgs_to_build)
516
517 localdata = data.createCopy(self.configuration.data)
518 bb.data.update_data(localdata)
519 bb.data.expandKeys(localdata)
520
521 taskdata = bb.taskdata.TaskData(self.configuration.abort)
522
523 runlist = []
524 try:
525 for k in pkgs_to_build:
526 taskdata.add_provider(localdata, self.status, k)
527 runlist.append([k, "do_%s" % self.configuration.cmd])
528 taskdata.add_unresolved(localdata, self.status)
529 except bb.providers.NoProvider:
530 sys.exit(1)
531
532 rq = bb.runqueue.RunQueue()
533 rq.prepare_runqueue(self, self.configuration.data, self.status, taskdata, runlist)
534 try:
535 failures = rq.execute_runqueue(self, self.configuration.data, self.status, taskdata, runlist)
536 except runqueue.TaskFailure, fnids:
537 for fnid in fnids:
538 bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid])
539 sys.exit(1)
540 bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures))
541
542 sys.exit( self.stats.show() )
543 527
544 except KeyboardInterrupt: 528 except KeyboardInterrupt:
545 bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.") 529 bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.")
@@ -556,13 +540,17 @@ class BBCooker:
556 return bbfiles 540 return bbfiles
557 541
558 def find_bbfiles( self, path ): 542 def find_bbfiles( self, path ):
559 """Find all the .bb files in a directory (uses find)""" 543 """Find all the .bb files in a directory"""
560 findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/' 544 from os.path import join
561 try: 545
562 finddata = os.popen(findcmd) 546 found = []
563 except OSError: 547 for dir, dirs, files in os.walk(path):
564 return [] 548 for ignored in ('SCCS', 'CVS', '.svn'):
565 return finddata.readlines() 549 if ignored in dirs:
550 dirs.remove(ignored)
551 found += [join(dir,f) for f in files if f.endswith('.bb')]
552
553 return found
566 554
567 def collect_bbfiles( self ): 555 def collect_bbfiles( self ):
568 """Collect all available .bb build files""" 556 """Collect all available .bb build files"""