summaryrefslogtreecommitdiff
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.py436
1 files changed, 232 insertions, 204 deletions
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py
index 95f38f6236..e524db7498 100644
--- a/bitbake/lib/bb/cooker.py
+++ b/bitbake/lib/bb/cooker.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
@@ -23,37 +24,36 @@
from __future__ import print_function
import sys, os, glob, os.path, re, time
+import atexit
+import itertools
+import logging
+import multiprocessing
+import signal
import sre_constants
+import threading
from cStringIO import StringIO
from contextlib import closing
import bb
from bb import utils, data, parse, event, cache, providers, taskdata, command, runqueue
+logger = logging.getLogger("BitBake")
+collectlog = logging.getLogger("BitBake.Collection")
+buildlog = logging.getLogger("BitBake.Build")
+parselog = logging.getLogger("BitBake.Parsing")
+providerlog = logging.getLogger("BitBake.Provider")
+
class MultipleMatches(Exception):
"""
Exception raised when multiple file matches are found
"""
-class ParsingErrorsFound(Exception):
- """
- Exception raised when parsing errors are found
- """
-
class NothingToBuild(Exception):
"""
Exception raised when there is nothing to build
"""
-
-# Different states cooker can be in
-cookerClean = 1
-cookerParsing = 2
-cookerParsed = 3
-
-# Different action states the cooker can be in
-cookerRun = 1 # Cooker is running normally
-cookerShutdown = 2 # Active tasks should be brought to a controlled stop
-cookerStop = 3 # Stop, now!
+class state:
+ initial, parsing, running, shutdown, stop = range(5)
#============================================================================#
# BBCooker
@@ -65,9 +65,7 @@ class BBCooker:
def __init__(self, configuration, server):
self.status = None
-
- self.cache = None
- self.bb_cache = None
+ self.appendlist = {}
if server:
self.server = server.BitBakeServer(self)
@@ -102,13 +100,12 @@ class BBCooker:
import termios
tcattr = termios.tcgetattr(fd)
if tcattr[3] & termios.TOSTOP:
- bb.msg.note(1, bb.msg.domain.Build, "The terminal had the TOSTOP bit set, clearing...")
+ buildlog.info("The terminal had the TOSTOP bit set, clearing...")
tcattr[3] = tcattr[3] & ~termios.TOSTOP
termios.tcsetattr(fd, termios.TCSANOW, tcattr)
self.command = bb.command.Command(self)
- self.cookerState = cookerClean
- self.cookerAction = cookerRun
+ self.state = state.initial
def parseConfiguration(self):
@@ -118,7 +115,7 @@ class BBCooker:
if nice:
curnice = os.nice(0)
nice = int(nice) - curnice
- bb.msg.note(2, bb.msg.domain.Build, "Renice to %s " % os.nice(nice))
+ buildlog.verbose("Renice to %s " % os.nice(nice))
def parseCommandLine(self):
# Parse any commandline into actions
@@ -126,11 +123,11 @@ class BBCooker:
self.commandlineAction = None
if 'world' in self.configuration.pkgs_to_build:
- bb.msg.error(bb.msg.domain.Build, "'world' is not a valid target for --environment.")
+ buildlog.error("'world' is not a valid target for --environment.")
elif len(self.configuration.pkgs_to_build) > 1:
- bb.msg.error(bb.msg.domain.Build, "Only one target can be used with the --environment option.")
+ buildlog.error("Only one target can be used with the --environment option.")
elif self.configuration.buildfile and len(self.configuration.pkgs_to_build) > 0:
- bb.msg.error(bb.msg.domain.Build, "No target should be used with the --environment and --buildfile options.")
+ buildlog.error("No target should be used with the --environment and --buildfile options.")
elif len(self.configuration.pkgs_to_build) > 0:
self.commandlineAction = ["showEnvironmentTarget", self.configuration.pkgs_to_build]
else:
@@ -148,13 +145,13 @@ class BBCooker:
self.commandlineAction = ["generateDotGraph", self.configuration.pkgs_to_build, self.configuration.cmd]
else:
self.commandlineAction = None
- bb.msg.error(bb.msg.domain.Build, "Please specify a package name for dependency graph generation.")
+ buildlog.error("Please specify a package name for dependency graph generation.")
else:
if self.configuration.pkgs_to_build:
self.commandlineAction = ["buildTargets", self.configuration.pkgs_to_build, self.configuration.cmd]
else:
self.commandlineAction = None
- bb.msg.error(bb.msg.domain.Build, "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
+ buildlog.error("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
def runCommands(self, server, data, abort):
"""
@@ -180,8 +177,8 @@ class BBCooker:
preferred_versions[pn] = (pref_ver, pref_file)
latest_versions[pn] = (last_ver, last_file)
- bb.msg.plain("%-35s %25s %25s" % ("Package Name", "Latest Version", "Preferred Version"))
- bb.msg.plain("%-35s %25s %25s\n" % ("============", "==============", "================="))
+ logger.plain("%-35s %25s %25s", "Package Name", "Latest Version", "Preferred Version")
+ logger.plain("%-35s %25s %25s\n", "============", "==============", "=================")
for p in sorted(pkg_pn):
pref = preferred_versions[p]
@@ -193,11 +190,7 @@ class BBCooker:
if pref == latest:
prefstr = ""
- bb.msg.plain("%-35s %25s %25s" % (p, lateststr, prefstr))
-
- def compareRevisions(self):
- ret = bb.fetch.fetcher_compare_revisons(self.configuration.data)
- bb.event.fire(bb.command.CookerCommandSetExitCode(ret), self.configuration.event_data)
+ logger.plain("%-35s %25s %25s", p, lateststr, prefstr)
def showEnvironment(self, buildfile = None, pkgs_to_build = []):
"""
@@ -207,8 +200,6 @@ class BBCooker:
envdata = None
if buildfile:
- self.cb = None
- self.bb_cache = bb.cache.init(self)
fn = self.matchFile(buildfile)
elif len(pkgs_to_build) == 1:
self.updateCache()
@@ -229,28 +220,22 @@ class BBCooker:
if fn:
try:
- envdata = self.bb_cache.loadDataFull(fn, self.get_file_appends(fn), self.configuration.data)
- except IOError as e:
- bb.msg.error(bb.msg.domain.Parsing, "Unable to read %s: %s" % (fn, e))
- raise
- except Exception as e:
- bb.msg.error(bb.msg.domain.Parsing, "%s" % e)
+ envdata = bb.cache.Cache.loadDataFull(fn, self.get_file_appends(fn), self.configuration.data)
+ except Exception, e:
+ parselog.exception("Unable to read %s", fn)
raise
# emit variables and shell functions
- try:
- data.update_data(envdata)
- with closing(StringIO()) as env:
- data.emit_env(env, envdata, True)
- bb.msg.plain(env.getvalue())
- except Exception as e:
- bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e)
+ data.update_data(envdata)
+ with closing(StringIO()) as env:
+ data.emit_env(env, envdata, True)
+ logger.plain(env.getvalue())
# emit the metadata which isnt valid shell
data.expandKeys(envdata)
for e in envdata.keys():
if data.getVarFlag( e, 'python', envdata ):
- bb.msg.plain("\npython %s () {\n%s}\n" % (e, data.getVar(e, envdata, 1)))
+ logger.plain("\npython %s () {\n%s}\n", e, data.getVar(e, envdata, 1))
def generateDepTreeData(self, pkgs_to_build, task):
"""
@@ -290,7 +275,7 @@ class BBCooker:
depend_tree["rdepends-pkg"] = {}
depend_tree["rrecs-pkg"] = {}
- for task in range(len(rq.rqdata.runq_fnid)):
+ for task in xrange(len(rq.rqdata.runq_fnid)):
taskname = rq.rqdata.runq_task[task]
fnid = rq.rqdata.runq_fnid[task]
fn = taskdata.fn_index[fnid]
@@ -374,7 +359,7 @@ class BBCooker:
for rdepend in depgraph["rdepends-pn"][pn]:
print('"%s" -> "%s" [style=dashed]' % (pn, rdepend), file=depends_file)
print("}", file=depends_file)
- bb.msg.plain("PN dependencies saved to 'pn-depends.dot'")
+ logger.info("PN dependencies saved to 'pn-depends.dot'")
depends_file = file('package-depends.dot', 'w' )
print("digraph depends {", file=depends_file)
@@ -395,7 +380,7 @@ class BBCooker:
for rdepend in depgraph["rrecs-pkg"][package]:
print('"%s" -> "%s" [style=dashed]' % (package, rdepend), file=depends_file)
print("}", file=depends_file)
- bb.msg.plain("Package dependencies saved to 'package-depends.dot'")
+ logger.info("Package dependencies saved to 'package-depends.dot'")
tdepends_file = file('task-depends.dot', 'w' )
print("digraph depends {", file=tdepends_file)
@@ -407,7 +392,7 @@ class BBCooker:
for dep in depgraph["tdepends"][task]:
print('"%s" -> "%s"' % (task, dep), file=tdepends_file)
print("}", file=tdepends_file)
- bb.msg.plain("Task dependencies saved to 'task-depends.dot'")
+ logger.info("Task dependencies saved to 'task-depends.dot'")
def buildDepgraph( self ):
all_depends = self.status.all_depends
@@ -431,10 +416,10 @@ class BBCooker:
try:
(providee, provider) = p.split(':')
except:
- bb.msg.fatal(bb.msg.domain.Provider, "Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
+ providerlog.critical("Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
continue
if providee in self.status.preferred and self.status.preferred[providee] != provider:
- bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee]))
+ providerlog.error("conflicting preferences for %s: both %s and %s specified", providee, provider, self.status.preferred[providee])
self.status.preferred[providee] = provider
# Calculate priorities for each file
@@ -443,8 +428,7 @@ class BBCooker:
for collection, pattern, regex, _ in self.status.bbfile_config_priorities:
if not regex in matched:
- bb.msg.warn(bb.msg.domain.Provider, "No bb files matched BBFILE_PATTERN_%s '%s'" %
- (collection, pattern))
+ collectlog.warn("No bb files matched BBFILE_PATTERN_%s '%s'" % (collection, pattern))
def buildWorldTargetList(self):
"""
@@ -452,19 +436,19 @@ class BBCooker:
"""
all_depends = self.status.all_depends
pn_provides = self.status.pn_provides
- bb.msg.debug(1, bb.msg.domain.Parsing, "collating packages for \"world\"")
+ parselog.debug(1, "collating packages for \"world\"")
for f in self.status.possible_world:
terminal = True
pn = self.status.pkg_fn[f]
for p in pn_provides[pn]:
if p.startswith('virtual/'):
- bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to %s provider starting with virtual/" % (f, p))
+ parselog.debug(2, "World build skipping %s due to %s provider starting with virtual/", f, p)
terminal = False
break
for pf in self.status.providers[p]:
if self.status.pkg_fn[pf] != pn:
- bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to both us and %s providing %s" % (f, pf, p))
+ parselog.debug(2, "World build skipping %s due to both us and %s providing %s", f, pf, p)
terminal = False
break
if terminal:
@@ -478,8 +462,9 @@ class BBCooker:
"""Drop off into a shell"""
try:
from bb import shell
- except ImportError as details:
- bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details )
+ except ImportError:
+ parselog.exception("Interactive mode not available")
+ sys.exit(1)
else:
shell.start( self )
@@ -493,70 +478,56 @@ class BBCooker:
path, _ = os.path.split(path)
def parseConfigurationFiles(self, files):
- try:
- data = self.configuration.data
-
- bb.parse.init_parser(data, self.configuration.dump_signatures)
- for f in files:
- data = bb.parse.handle(f, data)
-
- layerconf = self._findLayerConf()
- if layerconf:
- bb.msg.debug(2, bb.msg.domain.Parsing, "Found bblayers.conf (%s)" % layerconf)
- data = bb.parse.handle(layerconf, data)
-
- layers = (bb.data.getVar('BBLAYERS', data, True) or "").split()
-
- data = bb.data.createCopy(data)
- for layer in layers:
- bb.msg.debug(2, bb.msg.domain.Parsing, "Adding layer %s" % layer)
- bb.data.setVar('LAYERDIR', layer, data)
- data = bb.parse.handle(os.path.join(layer, "conf", "layer.conf"), data)
+ def _parse(f, data, include=False):
+ try:
+ return bb.parse.handle(f, data, include)
+ except (IOError, bb.parse.ParseError) as exc:
+ parselog.critical("Unable to parse %s: %s" % (f, exc))
+ sys.exit(1)
- # XXX: Hack, relies on the local keys of the datasmart
- # instance being stored in the 'dict' attribute and makes
- # assumptions about how variable expansion works, but
- # there's no better way to force an expansion of a single
- # variable across the datastore today, and this at least
- # lets us reference LAYERDIR without having to immediately
- # eval all our variables that use it.
- for key in data.dict:
- if key != "_data":
- value = data.getVar(key, False)
- if value and "${LAYERDIR}" in value:
- data.setVar(key, value.replace("${LAYERDIR}", layer))
+ data = self.configuration.data
+ bb.parse.init_parser(data)
+ for f in files:
+ data = _parse(f, data)
- bb.data.delVar('LAYERDIR', data)
+ layerconf = self._findLayerConf()
+ if layerconf:
+ parselog.debug(2, "Found bblayers.conf (%s)", layerconf)
+ data = _parse(layerconf, data)
- if not data.getVar("BBPATH", True):
- bb.fatal("The BBPATH variable is not set")
+ layers = (bb.data.getVar('BBLAYERS', data, True) or "").split()
- data = bb.parse.handle(os.path.join("conf", "bitbake.conf"), data)
+ data = bb.data.createCopy(data)
+ for layer in layers:
+ parselog.debug(2, "Adding layer %s", layer)
+ bb.data.setVar('LAYERDIR', layer, data)
+ data = _parse(os.path.join(layer, "conf", "layer.conf"), data)
+ data.expandVarref('LAYERDIR')
- self.configuration.data = data
+ bb.data.delVar('LAYERDIR', data)
- # Handle any INHERITs and inherit the base class
- inherits = ["base"] + (bb.data.getVar('INHERIT', self.configuration.data, True ) or "").split()
- for inherit in inherits:
- self.configuration.data = bb.parse.handle(os.path.join('classes', '%s.bbclass' % inherit), self.configuration.data, True )
+ if not data.getVar("BBPATH", True):
+ raise SystemExit("The BBPATH variable is not set")
- # Nomally we only register event handlers at the end of parsing .bb files
- # We register any handlers we've found so far here...
- for var in bb.data.getVar('__BBHANDLERS', self.configuration.data) or []:
- bb.event.register(var, bb.data.getVar(var, self.configuration.data))
+ data = _parse(os.path.join("conf", "bitbake.conf"), data)
- if bb.data.getVar("BB_WORKERCONTEXT", self.configuration.data) is None:
- bb.fetch.fetcher_init(self.configuration.data)
- bb.codeparser.parser_cache_init(self.configuration.data)
+ self.configuration.data = data
- bb.parse.init_parser(data, self.configuration.dump_signatures)
+ # Handle any INHERITs and inherit the base class
+ inherits = ["base"] + (bb.data.getVar('INHERIT', self.configuration.data, True ) or "").split()
+ for inherit in inherits:
+ self.configuration.data = _parse(os.path.join('classes', '%s.bbclass' % inherit), self.configuration.data, True )
- bb.event.fire(bb.event.ConfigParsed(), self.configuration.data)
+ # Nomally we only register event handlers at the end of parsing .bb files
+ # We register any handlers we've found so far here...
+ for var in bb.data.getVar('__BBHANDLERS', self.configuration.data) or []:
+ bb.event.register(var, bb.data.getVar(var, self.configuration.data))
- except IOError as e:
- bb.msg.fatal(bb.msg.domain.Parsing, "Error when parsing %s: %s" % (files, str(e)))
- except bb.parse.ParseError as details:
- bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (files, details) )
+ if bb.data.getVar("BB_WORKERCONTEXT", self.configuration.data) is None:
+ bb.fetch.fetcher_init(self.configuration.data)
+ bb.codeparser.parser_cache_init(self.configuration.data)
+ bb.parse.init_parser(data)
+ bb.event.fire(bb.event.ConfigParsed(), self.configuration.data)
def handleCollections( self, collections ):
"""Handle collections"""
@@ -565,22 +536,22 @@ class BBCooker:
for c in collection_list:
regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1)
if regex == None:
- bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s not defined" % c)
+ parselog.error("BBFILE_PATTERN_%s not defined" % c)
continue
priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1)
if priority == None:
- bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PRIORITY_%s not defined" % c)
+ parselog.error("BBFILE_PRIORITY_%s not defined" % c)
continue
try:
cre = re.compile(regex)
except re.error:
- bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
+ parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
continue
try:
pri = int(priority)
self.status.bbfile_config_priorities.append((c, regex, cre, pri))
except ValueError:
- bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority))
+ parselog.error("invalid value for BBFILE_PRIORITY_%s: \"%s\"", c, priority)
def buildSetVars(self):
"""
@@ -596,7 +567,7 @@ class BBCooker:
"""
bf = os.path.abspath(buildfile)
- (filelist, masked) = self.collect_bbfiles()
+ filelist, masked = self.collect_bbfiles()
try:
os.stat(bf)
return [bf]
@@ -616,9 +587,9 @@ class BBCooker:
"""
matches = self.matchFiles(buildfile)
if len(matches) != 1:
- bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (buildfile, len(matches)))
+ parselog.error("Unable to match %s (%s matches found):" % (buildfile, len(matches)))
for f in matches:
- bb.msg.error(bb.msg.domain.Parsing, " %s" % f)
+ parselog.error(" %s" % f)
raise MultipleMatches
return matches[0]
@@ -635,22 +606,23 @@ class BBCooker:
if (task == None):
task = self.configuration.cmd
- self.bb_cache = bb.cache.init(self)
- self.status = bb.cache.CacheData()
-
- (fn, cls) = self.bb_cache.virtualfn2realfn(buildfile)
+ (fn, cls) = bb.cache.Cache.virtualfn2realfn(buildfile)
buildfile = self.matchFile(fn)
- fn = self.bb_cache.realfn2virtual(buildfile, cls)
+ fn = bb.cache.Cache.realfn2virtual(buildfile, cls)
self.buildSetVars()
- # Load data into the cache for fn and parse the loaded cache data
- the_data = self.bb_cache.loadDataFull(fn, self.get_file_appends(fn), self.configuration.data)
- self.bb_cache.setData(fn, buildfile, the_data)
- self.bb_cache.handle_data(fn, self.status)
+ self.status = bb.cache.CacheData()
+ infos = bb.cache.Cache.parse(fn, self.get_file_appends(fn), \
+ self.configuration.data)
+ maininfo = None
+ for vfn, info in infos:
+ self.status.add_from_recipeinfo(vfn, info)
+ if vfn == fn:
+ maininfo = info
# Tweak some variables
- item = self.bb_cache.getVar('PN', fn, True)
+ item = maininfo.pn
self.status.ignored_dependencies = set()
self.status.bbfile_priority[fn] = 1
@@ -662,7 +634,7 @@ class BBCooker:
# Remove stamp for target if force mode active
if self.configuration.force:
- bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (task, fn))
+ logger.verbose("Remove stamp %s, %s", task, fn)
bb.build.del_stamp('do_%s' % task, self.status, fn)
# Setup taskdata structure
@@ -682,17 +654,17 @@ class BBCooker:
def buildFileIdle(server, rq, abort):
- if abort or self.cookerAction == cookerStop:
+ if abort or self.state == state.stop:
rq.finish_runqueue(True)
- elif self.cookerAction == cookerShutdown:
+ elif self.state == state.shutdown:
rq.finish_runqueue(False)
failures = 0
try:
retval = rq.execute_runqueue()
except runqueue.TaskFailure as exc:
for fnid in exc.args:
- bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid])
- failures = failures + 1
+ buildlog.error("'%s' failed" % taskdata.fn_index[fnid])
+ failures += len(exc.args)
retval = False
if not retval:
bb.event.fire(bb.event.BuildCompleted(buildname, item, failures), self.configuration.event_data)
@@ -719,17 +691,17 @@ class BBCooker:
targets = self.checkPackages(targets)
def buildTargetsIdle(server, rq, abort):
- if abort or self.cookerAction == cookerStop:
+ if abort or self.state == state.stop:
rq.finish_runqueue(True)
- elif self.cookerAction == cookerShutdown:
+ elif self.state == state.shutdown:
rq.finish_runqueue(False)
failures = 0
try:
retval = rq.execute_runqueue()
except runqueue.TaskFailure as exc:
for fnid in exc.args:
- bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid])
- failures = failures + 1
+ buildlog.error("'%s' failed" % taskdata.fn_index[fnid])
+ failures += len(exc.args)
retval = False
if not retval:
bb.event.fire(bb.event.BuildCompleted(buildname, targets, failures), self.configuration.event_data)
@@ -764,12 +736,10 @@ class BBCooker:
self.server.register_idle_function(buildTargetsIdle, rq)
def updateCache(self):
-
- if self.cookerState == cookerParsed:
+ if self.state == state.running:
return
- if self.cookerState != cookerParsing:
-
+ if self.state != state.parsing:
self.parseConfiguration ()
# Import Psyco if available and not disabled
@@ -779,11 +749,11 @@ class BBCooker:
try:
import psyco
except ImportError:
- bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
+ collectlog.info("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
else:
psyco.bind( CookerParser.parse_next )
else:
- bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
+ collectlog.info("You have disabled Psyco. This decreases performance.")
self.status = bb.cache.CacheData()
@@ -799,12 +769,12 @@ class BBCooker:
bb.data.renameVar("__depends", "__base_depends", self.configuration.data)
self.parser = CookerParser(self, filelist, masked)
- self.cookerState = cookerParsing
+ self.state = state.parsing
if not self.parser.parse_next():
- bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete")
+ collectlog.debug(1, "parsing complete")
self.buildDepgraph()
- self.cookerState = cookerParsed
+ self.state = state.running
return None
return True
@@ -848,9 +818,8 @@ class BBCooker:
def collect_bbfiles( self ):
"""Collect all available .bb build files"""
parsed, cached, skipped, masked = 0, 0, 0, 0
- self.bb_cache = bb.cache.init(self)
- bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files")
+ collectlog.debug(1, "collecting .bb files")
files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split()
data.setVar("BBFILES", " ".join(files), self.configuration.data)
@@ -859,7 +828,7 @@ class BBCooker:
files = self.get_bbfiles()
if not len(files):
- bb.msg.error(bb.msg.domain.Collection, "no recipe files to build, check your BBPATH and BBFILES?")
+ collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
bb.event.fire(CookerExit(), self.configuration.event_data)
newfiles = set()
@@ -879,13 +848,14 @@ class BBCooker:
try:
bbmask_compiled = re.compile(bbmask)
except sre_constants.error:
- bb.msg.fatal(bb.msg.domain.Collection, "BBMASK is not a valid regular expression.")
+ collectlog.critical("BBMASK is not a valid regular expression, ignoring.")
+ return list(newfiles), 0
bbfiles = []
bbappend = []
for f in newfiles:
if bbmask and bbmask_compiled.search(f):
- bb.msg.debug(1, bb.msg.domain.Collection, "skipping masked file %s" % f)
+ collectlog.debug(1, "skipping masked file %s", f)
masked += 1
continue
if f.endswith('.bb'):
@@ -893,26 +863,25 @@ class BBCooker:
elif f.endswith('.bbappend'):
bbappend.append(f)
else:
- bb.msg.note(1, bb.msg.domain.Collection, "File %s of unknown filetype in BBFILES? Ignorning..." % f)
+ collectlog.debug(1, "skipping %s: unknown file extension", f)
# Build a list of .bbappend files for each .bb file
- self.appendlist = {}
for f in bbappend:
base = os.path.basename(f).replace('.bbappend', '.bb')
if not base in self.appendlist:
self.appendlist[base] = []
self.appendlist[base].append(f)
-
+
return (bbfiles, masked)
def get_file_appends(self, fn):
"""
Returns a list of .bbappend files to apply to fn
- NB: collect_files() must have been called prior to this
+ NB: collect_bbfiles() must have been called prior to this
"""
f = os.path.basename(fn)
if f in self.appendlist:
- return self.appendlist[f]
+ return self.appendlist[f]
return []
def pre_serve(self):
@@ -924,6 +893,11 @@ class BBCooker:
def post_serve(self):
bb.event.fire(CookerExit(), self.configuration.event_data)
+ def shutdown(self):
+ self.state = state.shutdown
+
+ def stop(self):
+ self.state = state.stop
def server_main(cooker, func, *args):
cooker.pre_serve()
@@ -974,65 +948,119 @@ class CookerExit(bb.event.Event):
def __init__(self):
bb.event.Event.__init__(self)
-class CookerParser:
+def parse_file(task):
+ filename, appends = task
+ try:
+ return True, bb.cache.Cache.parse(filename, appends, parse_file.cfg)
+ except Exception, exc:
+ exc.recipe = filename
+ raise exc
+
+class CookerParser(object):
def __init__(self, cooker, filelist, masked):
- # Internal data
self.filelist = filelist
self.cooker = cooker
+ self.cfgdata = cooker.configuration.data
# Accounting statistics
self.parsed = 0
self.cached = 0
self.error = 0
self.masked = masked
- self.total = len(filelist)
self.skipped = 0
self.virtuals = 0
+ self.total = len(filelist)
- # Pointer to the next file to parse
- self.pointer = 0
+ self.current = 0
+ self.num_processes = int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS", True) or
+ multiprocessing.cpu_count())
+
+ self.bb_cache = bb.cache.Cache(self.cfgdata)
+ self.fromcache = []
+ self.willparse = []
+ for filename in self.filelist:
+ appends = self.cooker.get_file_appends(filename)
+ if not self.bb_cache.cacheValid(filename):
+ self.willparse.append((filename, appends))
+ else:
+ self.fromcache.append((filename, appends))
+ self.toparse = self.total - len(self.fromcache)
+ self.progress_chunk = max(self.toparse / 100, 1)
- def parse_next(self):
- cooker = self.cooker
- if self.pointer < len(self.filelist):
- f = self.filelist[self.pointer]
+ self.start()
- try:
- fromCache, skipped, virtuals = cooker.bb_cache.loadData(f, cooker.get_file_appends(f), cooker.configuration.data, cooker.status)
- if fromCache:
- self.cached += 1
- else:
- self.parsed += 1
-
- self.skipped += skipped
- self.virtuals += virtuals
-
- except IOError as e:
- self.error += 1
- cooker.bb_cache.remove(f)
- bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e))
- pass
- except KeyboardInterrupt:
- cooker.bb_cache.remove(f)
- cooker.bb_cache.sync()
- raise
- except Exception as e:
- self.error += 1
- cooker.bb_cache.remove(f)
- bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f))
- except:
- cooker.bb_cache.remove(f)
- raise
- finally:
- bb.event.fire(bb.event.ParseProgress(self.cached, self.parsed, self.skipped, self.masked, self.virtuals, self.error, self.total), cooker.configuration.event_data)
+ def start(self):
+ def init(cfg):
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ parse_file.cfg = cfg
+
+ bb.event.fire(bb.event.ParseStarted(self.toparse), self.cfgdata)
- self.pointer += 1
+ self.pool = multiprocessing.Pool(self.num_processes, init, [self.cfgdata])
+ parsed = self.pool.imap(parse_file, self.willparse)
+ self.pool.close()
- if self.pointer >= self.total:
- cooker.bb_cache.sync()
- bb.codeparser.parser_cache_save(cooker.configuration.data)
- if self.error > 0:
- raise ParsingErrorsFound
+ self.results = itertools.chain(self.load_cached(), parsed)
+
+ def shutdown(self, clean=True):
+ if clean:
+ event = bb.event.ParseCompleted(self.cached, self.parsed,
+ self.skipped, self.masked,
+ self.virtuals, self.error,
+ self.total)
+ bb.event.fire(event, self.cfgdata)
+ else:
+ self.pool.terminate()
+ self.pool.join()
+
+ sync = threading.Thread(target=self.bb_cache.sync)
+ sync.start()
+ atexit.register(lambda: sync.join())
+
+ codesync = threading.Thread(target=bb.codeparser.parser_cache_save(self.cooker.configuration.data))
+ codesync.start()
+ atexit.register(lambda: codesync.join())
+
+ def load_cached(self):
+ for filename, appends in self.fromcache:
+ cached, infos = self.bb_cache.load(filename, appends, self.cfgdata)
+ yield not cached, infos
+
+ def parse_next(self):
+ try:
+ parsed, result = self.results.next()
+ except StopIteration:
+ self.shutdown()
return False
+ except KeyboardInterrupt:
+ self.shutdown(clean=False)
+ raise
+ except Exception as exc:
+ self.shutdown(clean=False)
+ bb.fatal('Error parsing %s: %s' % (exc.recipe, exc))
+
+ self.current += 1
+ self.virtuals += len(result)
+ if parsed:
+ self.parsed += 1
+ if self.parsed % self.progress_chunk == 0:
+ bb.event.fire(bb.event.ParseProgress(self.parsed),
+ self.cfgdata)
+ else:
+ self.cached += 1
+
+ for virtualfn, info in result:
+ if info.skipped:
+ self.skipped += 1
+ else:
+ self.bb_cache.add_info(virtualfn, info, self.cooker.status,
+ parsed=parsed)
return True
+
+ def reparse(self, filename):
+ infos = self.bb_cache.parse(filename,
+ self.cooker.get_file_appends(filename),
+ self.cfgdata)
+ for vfn, info in infos:
+ self.cooker.status.add_from_recipeinfo(vfn, info)