summaryrefslogtreecommitdiffstats
path: root/import-layers/yocto-poky/bitbake/lib/bb
diff options
context:
space:
mode:
authorBrad Bishop <bradleyb@fuzziesquirrel.com>2018-02-01 10:27:11 -0500
committerBrad Bishop <bradleyb@fuzziesquirrel.com>2018-03-12 22:51:39 -0400
commit6e60e8b2b2bab889379b380a28a167a0edd9d1d3 (patch)
treef12f54d5ba8e74e67e5fad3651a1e125bb8f4191 /import-layers/yocto-poky/bitbake/lib/bb
parent509842add85b53e13164c1569a1fd43d5b8d91c5 (diff)
downloadtalos-openbmc-6e60e8b2b2bab889379b380a28a167a0edd9d1d3.tar.gz
talos-openbmc-6e60e8b2b2bab889379b380a28a167a0edd9d1d3.zip
Yocto 2.3
Move OpenBMC to Yocto 2.3(pyro). Tested: Built and verified Witherspoon and Palmetto images Change-Id: I50744030e771f4850afc2a93a10d3507e76d36bc Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com> Resolves: openbmc/openbmc#2461
Diffstat (limited to 'import-layers/yocto-poky/bitbake/lib/bb')
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/COW.py24
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/__init__.py2
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/build.py96
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/cache.py16
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/codeparser.py37
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/command.py302
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/cooker.py304
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/cookerdata.py17
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/data.py95
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/data_smart.py120
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/event.py44
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/__init__.py151
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/bzr.py9
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/clearcase.py11
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/cvs.py16
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/git.py65
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitannex.py1
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitsm.py3
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/hg.py15
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/local.py18
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/npm.py58
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/osc.py15
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/perforce.py21
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/repo.py7
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/s3.py98
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/sftp.py6
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/ssh.py5
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/svn.py21
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/fetch2/wget.py37
-rwxr-xr-ximport-layers/yocto-poky/bitbake/lib/bb/main.py108
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/monitordisk.py27
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/msg.py15
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/parse/__init__.py2
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/parse/ast.py79
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/BBHandler.py24
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/ConfHandler.py14
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/persist_data.py10
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/process.py4
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/providers.py13
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/remotedata.py116
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/runqueue.py305
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/server/process.py30
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/server/xmlrpc.py2
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/siggen.py189
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/taskdata.py33
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/tests/codeparser.py66
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/tests/data.py253
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/tests/fetch.py2
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/tests/parse.py28
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py490
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py167
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/ui/knotty.py50
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/ui/ncurses.py2
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/ui/taskexp.py (renamed from import-layers/yocto-poky/bitbake/lib/bb/ui/depexp.py)36
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/ui/toasterui.py10
-rw-r--r--import-layers/yocto-poky/bitbake/lib/bb/utils.py90
56 files changed, 2724 insertions, 1055 deletions
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/COW.py b/import-layers/yocto-poky/bitbake/lib/bb/COW.py
index 77a05cfe3..36ebbd9d1 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/COW.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/COW.py
@@ -213,11 +213,11 @@ if __name__ == "__main__":
print()
print("a", a)
- for x in a.items():
+ for x in a.iteritems():
print(x)
print("--")
print("b", b)
- for x in b.items():
+ for x in b.iteritems():
print(x)
print()
@@ -225,11 +225,11 @@ if __name__ == "__main__":
b['a'] = 'c'
print("a", a)
- for x in a.items():
+ for x in a.iteritems():
print(x)
print("--")
print("b", b)
- for x in b.items():
+ for x in b.iteritems():
print(x)
print()
@@ -244,22 +244,22 @@ if __name__ == "__main__":
a['set'].add("o2")
print("a", a)
- for x in a['set'].values():
+ for x in a['set'].itervalues():
print(x)
print("--")
print("b", b)
- for x in b['set'].values():
+ for x in b['set'].itervalues():
print(x)
print()
b['set'].add('o3')
print("a", a)
- for x in a['set'].values():
+ for x in a['set'].itervalues():
print(x)
print("--")
print("b", b)
- for x in b['set'].values():
+ for x in b['set'].itervalues():
print(x)
print()
@@ -269,7 +269,7 @@ if __name__ == "__main__":
a['set2'].add("o2")
print("a", a)
- for x in a.items():
+ for x in a.iteritems():
print(x)
print("--")
print("b", b)
@@ -289,7 +289,7 @@ if __name__ == "__main__":
print("Yay - has_key with delete works!")
print("a", a)
- for x in a.items():
+ for x in a.iteritems():
print(x)
print("--")
print("b", b)
@@ -300,7 +300,7 @@ if __name__ == "__main__":
b.__revertitem__('b')
print("a", a)
- for x in a.items():
+ for x in a.iteritems():
print(x)
print("--")
print("b", b)
@@ -310,7 +310,7 @@ if __name__ == "__main__":
b.__revertitem__('dict')
print("a", a)
- for x in a.items():
+ for x in a.iteritems():
print(x)
print("--")
print("b", b)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/__init__.py b/import-layers/yocto-poky/bitbake/lib/bb/__init__.py
index f019d4831..bfe0ca5d8 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/__init__.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/__init__.py
@@ -21,7 +21,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-__version__ = "1.32.0"
+__version__ = "1.34.0"
import sys
if sys.version_info < (3, 4, 0):
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/build.py b/import-layers/yocto-poky/bitbake/lib/bb/build.py
index b59a49bc1..0d0100a06 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/build.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/build.py
@@ -91,14 +91,14 @@ class TaskBase(event.Event):
def __init__(self, t, logfile, d):
self._task = t
- self._package = d.getVar("PF", True)
- self._mc = d.getVar("BB_CURRENT_MC", True)
- self.taskfile = d.getVar("FILE", True)
+ self._package = d.getVar("PF")
+ self._mc = d.getVar("BB_CURRENT_MC")
+ self.taskfile = d.getVar("FILE")
self.taskname = self._task
self.logfile = logfile
self.time = time.time()
event.Event.__init__(self)
- self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName())
+ self._message = "recipe %s: task %s: %s" % (d.getVar("PF"), t, self.getDisplayName())
def getTask(self):
return self._task
@@ -195,13 +195,13 @@ def exec_func(func, d, dirs = None, pythonexception=False):
oldcwd = None
flags = d.getVarFlags(func)
- cleandirs = flags.get('cleandirs')
+ cleandirs = flags.get('cleandirs') if flags else None
if cleandirs:
for cdir in d.expand(cleandirs).split():
bb.utils.remove(cdir, True)
bb.utils.mkdirhier(cdir)
- if dirs is None:
+ if flags and dirs is None:
dirs = flags.get('dirs')
if dirs:
dirs = d.expand(dirs).split()
@@ -227,17 +227,17 @@ def exec_func(func, d, dirs = None, pythonexception=False):
else:
lockfiles = None
- tempdir = d.getVar('T', True)
+ tempdir = d.getVar('T')
# or func allows items to be executed outside of the normal
# task set, such as buildhistory
- task = d.getVar('BB_RUNTASK', True) or func
+ task = d.getVar('BB_RUNTASK') or func
if task == func:
taskfunc = task
else:
taskfunc = "%s.%s" % (task, func)
- runfmt = d.getVar('BB_RUNFMT', True) or "run.{func}.{pid}"
+ runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
runfile = os.path.join(tempdir, runfn)
bb.utils.mkdirhier(os.path.dirname(runfile))
@@ -369,7 +369,7 @@ exit $ret
cmd = runfile
if d.getVarFlag(func, 'fakeroot', False):
- fakerootcmd = d.getVar('FAKEROOT', True)
+ fakerootcmd = d.getVar('FAKEROOT')
if fakerootcmd:
cmd = [fakerootcmd, runfile]
@@ -378,7 +378,7 @@ exit $ret
else:
logfile = sys.stdout
- progress = d.getVarFlag(func, 'progress', True)
+ progress = d.getVarFlag(func, 'progress')
if progress:
if progress == 'percent':
# Use default regex
@@ -430,7 +430,7 @@ exit $ret
else:
break
- tempdir = d.getVar('T', True)
+ tempdir = d.getVar('T')
fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
if os.path.exists(fifopath):
os.unlink(fifopath)
@@ -443,7 +443,7 @@ exit $ret
with open(os.devnull, 'r+') as stdin:
bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
except bb.process.CmdError:
- logfn = d.getVar('BB_LOGFILE', True)
+ logfn = d.getVar('BB_LOGFILE')
raise FuncFailed(func, logfn)
finally:
os.unlink(fifopath)
@@ -474,18 +474,18 @@ def _exec_task(fn, task, d, quieterr):
logger.debug(1, "Executing task %s", task)
localdata = _task_data(fn, task, d)
- tempdir = localdata.getVar('T', True)
+ tempdir = localdata.getVar('T')
if not tempdir:
bb.fatal("T variable not set, unable to build")
# Change nice level if we're asked to
- nice = localdata.getVar("BB_TASK_NICE_LEVEL", True)
+ nice = localdata.getVar("BB_TASK_NICE_LEVEL")
if nice:
curnice = os.nice(0)
nice = int(nice) - curnice
newnice = os.nice(nice)
logger.debug(1, "Renice to %s " % newnice)
- ionice = localdata.getVar("BB_TASK_IONICE_LEVEL", True)
+ ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
if ionice:
try:
cls, prio = ionice.split(".", 1)
@@ -496,7 +496,7 @@ def _exec_task(fn, task, d, quieterr):
bb.utils.mkdirhier(tempdir)
# Determine the logfile to generate
- logfmt = localdata.getVar('BB_LOGFMT', True) or 'log.{task}.{pid}'
+ logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
logbase = logfmt.format(task=task, pid=os.getpid())
# Document the order of the tasks...
@@ -563,6 +563,7 @@ def _exec_task(fn, task, d, quieterr):
localdata.setVar('BB_LOGFILE', logfn)
localdata.setVar('BB_RUNTASK', task)
+ localdata.setVar('BB_TASK_LOGGER', bblogger)
flags = localdata.getVarFlags(task)
@@ -628,7 +629,7 @@ def exec_task(fn, task, d, profile = False):
quieterr = True
if profile:
- profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task)
+ profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
try:
import cProfile as profile
except:
@@ -668,9 +669,9 @@ def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
stamp = d.stamp[file_name]
extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
else:
- stamp = d.getVar('STAMP', True)
- file_name = d.getVar('BB_FILENAME', True)
- extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
+ stamp = d.getVar('STAMP')
+ file_name = d.getVar('BB_FILENAME')
+ extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
if baseonly:
return stamp
@@ -704,9 +705,9 @@ def stamp_cleanmask_internal(taskname, d, file_name):
stamp = d.stampclean[file_name]
extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
else:
- stamp = d.getVar('STAMPCLEAN', True)
- file_name = d.getVar('BB_FILENAME', True)
- extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or ""
+ stamp = d.getVar('STAMPCLEAN')
+ file_name = d.getVar('BB_FILENAME')
+ extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
if not stamp:
return []
@@ -742,7 +743,7 @@ def make_stamp(task, d, file_name = None):
# as it completes
if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
stampbase = stamp_internal(task, d, None, True)
- file_name = d.getVar('BB_FILENAME', True)
+ file_name = d.getVar('BB_FILENAME')
bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
def del_stamp(task, d, file_name = None):
@@ -764,7 +765,7 @@ def write_taint(task, d, file_name = None):
if file_name:
taintfn = d.stamp[file_name] + '.' + task + '.taint'
else:
- taintfn = d.getVar('STAMP', True) + '.' + task + '.taint'
+ taintfn = d.getVar('STAMP') + '.' + task + '.taint'
bb.utils.mkdirhier(os.path.dirname(taintfn))
# The specific content of the taint file is not really important,
# we just need it to be random, so a random UUID is used
@@ -861,3 +862,46 @@ def deltask(task, d):
if task in deps:
deps.remove(task)
d.setVarFlag(bbtask, 'deps', deps)
+
+def preceedtask(task, with_recrdeptasks, d):
+ """
+ Returns a set of tasks in the current recipe which were specified as
+ precondition by the task itself ("after") or which listed themselves
+ as precondition ("before"). Preceeding tasks specified via the
+ "recrdeptask" are included in the result only if requested. Beware
+ that this may lead to the task itself being listed.
+ """
+ preceed = set()
+ preceed.update(d.getVarFlag(task, 'deps') or [])
+ if with_recrdeptasks:
+ recrdeptask = d.getVarFlag(task, 'recrdeptask')
+ if recrdeptask:
+ preceed.update(recrdeptask.split())
+ return preceed
+
+def tasksbetween(task_start, task_end, d):
+ """
+ Return the list of tasks between two tasks in the current recipe,
+ where task_start is to start at and task_end is the task to end at
+ (and task_end has a dependency chain back to task_start).
+ """
+ outtasks = []
+ tasks = list(filter(lambda k: d.getVarFlag(k, "task"), d.keys()))
+ def follow_chain(task, endtask, chain=None):
+ if not chain:
+ chain = []
+ chain.append(task)
+ for othertask in tasks:
+ if othertask == task:
+ continue
+ if task == endtask:
+ for ctask in chain:
+ if ctask not in outtasks:
+ outtasks.append(ctask)
+ else:
+ deps = d.getVarFlag(othertask, 'deps', False)
+ if task in deps:
+ follow_chain(othertask, endtask, chain)
+ chain.pop()
+ follow_chain(task_start, task_end)
+ return outtasks
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/cache.py b/import-layers/yocto-poky/bitbake/lib/bb/cache.py
index dd9cfdfac..e7eeb4f50 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/cache.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/cache.py
@@ -37,7 +37,7 @@ import bb.utils
logger = logging.getLogger("BitBake.Cache")
-__cache_version__ = "150"
+__cache_version__ = "151"
def getCacheFile(path, filename, data_hash):
return os.path.join(path, filename + "." + data_hash)
@@ -71,7 +71,7 @@ class RecipeInfoCommon(object):
@classmethod
def flaglist(cls, flag, varlist, metadata, squash=False):
- out_dict = dict((var, metadata.getVarFlag(var, flag, True))
+ out_dict = dict((var, metadata.getVarFlag(var, flag))
for var in varlist)
if squash:
return dict((k,v) for (k,v) in out_dict.items() if v)
@@ -296,7 +296,7 @@ def parse_recipe(bb_data, bbfile, appends, mc=''):
bb_data.setVar("__BBMULTICONFIG", mc)
# expand tmpdir to include this topdir
- bb_data.setVar('TMPDIR', bb_data.getVar('TMPDIR', True) or "")
+ bb_data.setVar('TMPDIR', bb_data.getVar('TMPDIR') or "")
bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
oldpath = os.path.abspath(os.getcwd())
bb.parse.cached_mtime_noerror(bbfile_loc)
@@ -378,7 +378,7 @@ class Cache(NoCache):
# It will be used later for deciding whether we
# need extra cache file dump/load support
self.caches_array = caches_array
- self.cachedir = data.getVar("CACHE", True)
+ self.cachedir = data.getVar("CACHE")
self.clean = set()
self.checked = set()
self.depends_cache = {}
@@ -462,6 +462,10 @@ class Cache(NoCache):
self.depends_cache[key] = [value]
# only fire events on even percentage boundaries
current_progress = cachefile.tell() + previous_progress
+ if current_progress > cachesize:
+ # we might have calculated incorrect total size because a file
+ # might've been written out just after we checked its size
+ cachesize = current_progress
current_percent = 100 * current_progress / cachesize
if current_percent > previous_percent:
previous_percent = current_percent
@@ -792,8 +796,8 @@ class MultiProcessCache(object):
self.cachedata_extras = self.create_cachedata()
def init_cache(self, d, cache_file_name=None):
- cachedir = (d.getVar("PERSISTENT_DIR", True) or
- d.getVar("CACHE", True))
+ cachedir = (d.getVar("PERSISTENT_DIR") or
+ d.getVar("CACHE"))
if cachedir in [None, '']:
return
bb.utils.mkdirhier(cachedir)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/codeparser.py b/import-layers/yocto-poky/bitbake/lib/bb/codeparser.py
index 5d2d44065..530f44e57 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/codeparser.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/codeparser.py
@@ -1,3 +1,22 @@
+"""
+BitBake code parser
+
+Parses actual code (i.e. python and shell) for functions and in-line
+expressions. Used mainly to determine dependencies on other functions
+and variables within the BitBake metadata. Also provides a cache for
+this information in order to speed up processing.
+
+(Not to be confused with the code that parses the metadata itself,
+see lib/bb/parse/ for that).
+
+NOTE: if you change how the parsers gather information you will almost
+certainly need to increment CodeParserCache.CACHE_VERSION below so that
+any existing codeparser cache gets invalidated. Additionally you'll need
+to increment __cache_version__ in cache.py in order to ensure that old
+recipe caches don't trigger "Taskhash mismatch" errors.
+
+"""
+
import ast
import sys
import codegen
@@ -117,7 +136,11 @@ class shellCacheLine(object):
class CodeParserCache(MultiProcessCache):
cache_file_name = "bb_codeparser.dat"
- CACHE_VERSION = 8
+ # NOTE: you must increment this if you change how the parsers gather information,
+ # so that an existing cache gets invalidated. Additionally you'll need
+ # to increment __cache_version__ in cache.py in order to ensure that old
+ # recipe caches don't trigger "Taskhash mismatch" errors.
+ CACHE_VERSION = 9
def __init__(self):
MultiProcessCache.__init__(self)
@@ -186,13 +209,15 @@ class BufferedLogger(Logger):
def flush(self):
for record in self.buffer:
- self.target.handle(record)
+ if self.target.isEnabledFor(record.levelno):
+ self.target.handle(record)
self.buffer = []
class PythonParser():
getvars = (".getVar", ".appendVar", ".prependVar")
getvarflags = (".getVarFlag", ".appendVarFlag", ".prependVarFlag")
- containsfuncs = ("bb.utils.contains", "base_contains", "bb.utils.contains_any")
+ containsfuncs = ("bb.utils.contains", "base_contains")
+ containsanyfuncs = ("bb.utils.contains_any", "bb.utils.filter")
execfuncs = ("bb.build.exec_func", "bb.build.exec_task")
def warn(self, func, arg):
@@ -211,13 +236,17 @@ class PythonParser():
def visit_Call(self, node):
name = self.called_node_name(node.func)
- if name and (name.endswith(self.getvars) or name.endswith(self.getvarflags) or name in self.containsfuncs):
+ if name and (name.endswith(self.getvars) or name.endswith(self.getvarflags) or name in self.containsfuncs or name in self.containsanyfuncs):
if isinstance(node.args[0], ast.Str):
varname = node.args[0].s
if name in self.containsfuncs and isinstance(node.args[1], ast.Str):
if varname not in self.contains:
self.contains[varname] = set()
self.contains[varname].add(node.args[1].s)
+ elif name in self.containsanyfuncs and isinstance(node.args[1], ast.Str):
+ if varname not in self.contains:
+ self.contains[varname] = set()
+ self.contains[varname].update(node.args[1].s.split())
elif name.endswith(self.getvarflags):
if isinstance(node.args[1], ast.Str):
self.references.add('%s[%s]' % (varname, node.args[1].s))
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/command.py b/import-layers/yocto-poky/bitbake/lib/bb/command.py
index caa3e4d45..a919f58d2 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/command.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/command.py
@@ -28,8 +28,15 @@ and must not trigger events, directly or indirectly.
Commands are queued in a CommandQueue
"""
+from collections import OrderedDict, defaultdict
+
import bb.event
import bb.cooker
+import bb.remotedata
+
+class DataStoreConnectionHandle(object):
+ def __init__(self, dsindex=0):
+ self.dsindex = dsindex
class CommandCompleted(bb.event.Event):
pass
@@ -55,6 +62,7 @@ class Command:
self.cooker = cooker
self.cmds_sync = CommandsSync()
self.cmds_async = CommandsAsync()
+ self.remotedatastores = bb.remotedata.RemoteDatastores(cooker)
# FIXME Add lock for this
self.currentAsyncCommand = None
@@ -125,14 +133,20 @@ class Command:
def finishAsyncCommand(self, msg=None, code=None):
if msg or msg == "":
- bb.event.fire(CommandFailed(msg), self.cooker.expanded_data)
+ bb.event.fire(CommandFailed(msg), self.cooker.data)
elif code:
- bb.event.fire(CommandExit(code), self.cooker.expanded_data)
+ bb.event.fire(CommandExit(code), self.cooker.data)
else:
- bb.event.fire(CommandCompleted(), self.cooker.expanded_data)
+ bb.event.fire(CommandCompleted(), self.cooker.data)
self.currentAsyncCommand = None
self.cooker.finishcommand()
+def split_mc_pn(pn):
+ if pn.startswith("multiconfig:"):
+ _, mc, pn = pn.split(":", 2)
+ return (mc, pn)
+ return ('', pn)
+
class CommandsSync:
"""
A class of synchronous commands
@@ -179,6 +193,7 @@ class CommandsSync:
"""
varname = params[0]
value = str(params[1])
+ command.cooker.extraconfigdata[varname] = value
command.cooker.data.setVar(varname, value)
def getSetVariable(self, command, params):
@@ -295,9 +310,274 @@ class CommandsSync:
def updateConfig(self, command, params):
options = params[0]
environment = params[1]
- command.cooker.updateConfigOpts(options, environment)
+ cmdline = params[2]
+ command.cooker.updateConfigOpts(options, environment, cmdline)
updateConfig.needconfig = False
+ def parseConfiguration(self, command, params):
+ """Instruct bitbake to parse its configuration
+ NOTE: it is only necessary to call this if you aren't calling any normal action
+ (otherwise parsing is taken care of automatically)
+ """
+ command.cooker.parseConfiguration()
+ parseConfiguration.needconfig = False
+
+ def getLayerPriorities(self, command, params):
+ ret = []
+ # regex objects cannot be marshalled by xmlrpc
+ for collection, pattern, regex, pri in command.cooker.bbfile_config_priorities:
+ ret.append((collection, pattern, regex.pattern, pri))
+ return ret
+ getLayerPriorities.readonly = True
+
+ def getRecipes(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return list(command.cooker.recipecaches[mc].pkg_pn.items())
+ getRecipes.readonly = True
+
+ def getRecipeDepends(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return list(command.cooker.recipecaches[mc].deps.items())
+ getRecipeDepends.readonly = True
+
+ def getRecipeVersions(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].pkg_pepvpr
+ getRecipeVersions.readonly = True
+
+ def getRuntimeDepends(self, command, params):
+ ret = []
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ rundeps = command.cooker.recipecaches[mc].rundeps
+ for key, value in rundeps.items():
+ if isinstance(value, defaultdict):
+ value = dict(value)
+ ret.append((key, value))
+ return ret
+ getRuntimeDepends.readonly = True
+
+ def getRuntimeRecommends(self, command, params):
+ ret = []
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ runrecs = command.cooker.recipecaches[mc].runrecs
+ for key, value in runrecs.items():
+ if isinstance(value, defaultdict):
+ value = dict(value)
+ ret.append((key, value))
+ return ret
+ getRuntimeRecommends.readonly = True
+
+ def getRecipeInherits(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].inherits
+ getRecipeInherits.readonly = True
+
+ def getBbFilePriority(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].bbfile_priority
+ getBbFilePriority.readonly = True
+
+ def getDefaultPreference(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return command.cooker.recipecaches[mc].pkg_dp
+ getDefaultPreference.readonly = True
+
+ def getSkippedRecipes(self, command, params):
+ # Return list sorted by reverse priority order
+ import bb.cache
+ skipdict = OrderedDict(sorted(command.cooker.skiplist.items(),
+ key=lambda x: (-command.cooker.collection.calc_bbfile_priority(bb.cache.virtualfn2realfn(x[0])[0]), x[0])))
+ return list(skipdict.items())
+ getSkippedRecipes.readonly = True
+
+ def getOverlayedRecipes(self, command, params):
+ return list(command.cooker.collection.overlayed.items())
+ getOverlayedRecipes.readonly = True
+
+ def getFileAppends(self, command, params):
+ fn = params[0]
+ return command.cooker.collection.get_file_appends(fn)
+ getFileAppends.readonly = True
+
+ def getAllAppends(self, command, params):
+ return command.cooker.collection.bbappends
+ getAllAppends.readonly = True
+
+ def findProviders(self, command, params):
+ return command.cooker.findProviders()
+ findProviders.readonly = True
+
+ def findBestProvider(self, command, params):
+ (mc, pn) = split_mc_pn(params[0])
+ return command.cooker.findBestProvider(pn, mc)
+ findBestProvider.readonly = True
+
+ def allProviders(self, command, params):
+ try:
+ mc = params[0]
+ except IndexError:
+ mc = ''
+ return list(bb.providers.allProviders(command.cooker.recipecaches[mc]).items())
+ allProviders.readonly = True
+
+ def getRuntimeProviders(self, command, params):
+ rprovide = params[0]
+ try:
+ mc = params[1]
+ except IndexError:
+ mc = ''
+ all_p = bb.providers.getRuntimeProviders(command.cooker.recipecaches[mc], rprovide)
+ if all_p:
+ best = bb.providers.filterProvidersRunTime(all_p, rprovide,
+ command.cooker.data,
+ command.cooker.recipecaches[mc])[0][0]
+ else:
+ best = None
+ return all_p, best
+ getRuntimeProviders.readonly = True
+
+ def dataStoreConnectorFindVar(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ datastore = command.remotedatastores[dsindex]
+ value, overridedata = datastore._findVar(name)
+
+ if value:
+ content = value.get('_content', None)
+ if isinstance(content, bb.data_smart.DataSmart):
+ # Value is a datastore (e.g. BB_ORIGENV) - need to handle this carefully
+ idx = command.remotedatastores.check_store(content, True)
+ return {'_content': DataStoreConnectionHandle(idx),
+ '_connector_origtype': 'DataStoreConnectionHandle',
+ '_connector_overrides': overridedata}
+ elif isinstance(content, set):
+ return {'_content': list(content),
+ '_connector_origtype': 'set',
+ '_connector_overrides': overridedata}
+ else:
+ value['_connector_overrides'] = overridedata
+ else:
+ value = {}
+ value['_connector_overrides'] = overridedata
+ return value
+ dataStoreConnectorFindVar.readonly = True
+
+ def dataStoreConnectorGetKeys(self, command, params):
+ dsindex = params[0]
+ datastore = command.remotedatastores[dsindex]
+ return list(datastore.keys())
+ dataStoreConnectorGetKeys.readonly = True
+
+ def dataStoreConnectorGetVarHistory(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ datastore = command.remotedatastores[dsindex]
+ return datastore.varhistory.variable(name)
+ dataStoreConnectorGetVarHistory.readonly = True
+
+ def dataStoreConnectorExpandPythonRef(self, command, params):
+ config_data_dict = params[0]
+ varname = params[1]
+ expr = params[2]
+
+ config_data = command.remotedatastores.receive_datastore(config_data_dict)
+
+ varparse = bb.data_smart.VariableParse(varname, config_data)
+ return varparse.python_sub(expr)
+
+ def dataStoreConnectorRelease(self, command, params):
+ dsindex = params[0]
+ if dsindex <= 0:
+ raise CommandError('dataStoreConnectorRelease: invalid index %d' % dsindex)
+ command.remotedatastores.release(dsindex)
+
+ def dataStoreConnectorSetVarFlag(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ flag = params[2]
+ value = params[3]
+ datastore = command.remotedatastores[dsindex]
+ datastore.setVarFlag(name, flag, value)
+
+ def dataStoreConnectorDelVar(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ datastore = command.remotedatastores[dsindex]
+ if len(params) > 2:
+ flag = params[2]
+ datastore.delVarFlag(name, flag)
+ else:
+ datastore.delVar(name)
+
+ def dataStoreConnectorRenameVar(self, command, params):
+ dsindex = params[0]
+ name = params[1]
+ newname = params[2]
+ datastore = command.remotedatastores[dsindex]
+ datastore.renameVar(name, newname)
+
+ def parseRecipeFile(self, command, params):
+ """
+ Parse the specified recipe file (with or without bbappends)
+ and return a datastore object representing the environment
+ for the recipe.
+ """
+ fn = params[0]
+ appends = params[1]
+ appendlist = params[2]
+ if len(params) > 3:
+ config_data_dict = params[3]
+ config_data = command.remotedatastores.receive_datastore(config_data_dict)
+ else:
+ config_data = None
+
+ if appends:
+ if appendlist is not None:
+ appendfiles = appendlist
+ else:
+ appendfiles = command.cooker.collection.get_file_appends(fn)
+ else:
+ appendfiles = []
+ # We are calling bb.cache locally here rather than on the server,
+ # but that's OK because it doesn't actually need anything from
+ # the server barring the global datastore (which we have a remote
+ # version of)
+ if config_data:
+ # We have to use a different function here if we're passing in a datastore
+ # NOTE: we took a copy above, so we don't do it here again
+ envdata = bb.cache.parse_recipe(config_data, fn, appendfiles)['']
+ else:
+ # Use the standard path
+ parser = bb.cache.NoCache(command.cooker.databuilder)
+ envdata = parser.loadDataFull(fn, appendfiles)
+ idx = command.remotedatastores.store(envdata)
+ return DataStoreConnectionHandle(idx)
+ parseRecipeFile.readonly = True
+
class CommandsAsync:
"""
A class of asynchronous commands
@@ -311,8 +591,12 @@ class CommandsAsync:
"""
bfile = params[0]
task = params[1]
+ if len(params) > 2:
+ hidewarning = params[2]
+ else:
+ hidewarning = False
- command.cooker.buildFile(bfile, task)
+ command.cooker.buildFile(bfile, task, hidewarning)
buildFile.needcache = False
def buildTargets(self, command, params):
@@ -472,3 +756,11 @@ class CommandsAsync:
command.finishAsyncCommand()
resetCooker.needcache = False
+ def clientComplete(self, command, params):
+ """
+ Do the right thing when the controlling client exits
+ """
+ command.cooker.clientComplete()
+ command.finishAsyncCommand()
+ clientComplete.needcache = False
+
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/cooker.py b/import-layers/yocto-poky/bitbake/lib/bb/cooker.py
index 07897be27..3c9e88cd2 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/cooker.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/cooker.py
@@ -192,6 +192,8 @@ class BBCooker:
bb.parse.__mtime_cache = {}
bb.parse.BBHandler.cached_statements = {}
+ self.ui_cmdline = None
+
self.initConfigurationData()
# we log all events to a file if so directed
@@ -271,12 +273,15 @@ class BBCooker:
self.inotify_modified_files.append(event.pathname)
self.parsecache_valid = False
- def add_filewatch(self, deps, watcher=None):
+ def add_filewatch(self, deps, watcher=None, dirs=False):
if not watcher:
watcher = self.watcher
for i in deps:
watcher.bbwatchedfiles.append(i[0])
- f = os.path.dirname(i[0])
+ if dirs:
+ f = i[0]
+ else:
+ f = os.path.dirname(i[0])
if f in watcher.bbseen:
continue
watcher.bbseen.append(f)
@@ -331,7 +336,7 @@ class BBCooker:
# Need to preserve BB_CONSOLELOG over resets
consolelog = None
if hasattr(self, "data"):
- consolelog = self.data.getVar("BB_CONSOLELOG", True)
+ consolelog = self.data.getVar("BB_CONSOLELOG")
if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
self.enableDataTracking()
@@ -358,17 +363,18 @@ class BBCooker:
self.databuilder.parseBaseConfiguration()
self.data = self.databuilder.data
self.data_hash = self.databuilder.data_hash
+ self.extraconfigdata = {}
if consolelog:
self.data.setVar("BB_CONSOLELOG", consolelog)
+ self.data.setVar('BB_CMDLINE', self.ui_cmdline)
+
#
# Copy of the data store which has been expanded.
# Used for firing events and accessing variables where expansion needs to be accounted for
#
- self.expanded_data = bb.data.createCopy(self.data)
- bb.data.update_data(self.expanded_data)
- bb.parse.init_parser(self.expanded_data)
+ bb.parse.init_parser(self.data)
if CookerFeatures.BASEDATASTORE_TRACKING in self.featureset:
self.disableDataTracking()
@@ -526,7 +532,7 @@ class BBCooker:
bb.msg.loggerVerboseLogs = True
# Change nice level if we're asked to
- nice = self.data.getVar("BB_NICE_LEVEL", True)
+ nice = self.data.getVar("BB_NICE_LEVEL")
if nice:
curnice = os.nice(0)
nice = int(nice) - curnice
@@ -539,9 +545,10 @@ class BBCooker:
for mc in self.multiconfigs:
self.recipecaches[mc] = bb.cache.CacheData(self.caches_array)
- self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS", True))
+ self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
- def updateConfigOpts(self, options, environment):
+ def updateConfigOpts(self, options, environment, cmdline):
+ self.ui_cmdline = cmdline
clean = True
for o in options:
if o in ['prefile', 'postfile']:
@@ -583,13 +590,12 @@ class BBCooker:
def showVersions(self):
- pkg_pn = self.recipecaches[''].pkg_pn
- (latest_versions, preferred_versions) = bb.providers.findProviders(self.data, self.recipecaches[''], pkg_pn)
+ (latest_versions, preferred_versions) = self.findProviders()
logger.plain("%-35s %25s %25s", "Recipe Name", "Latest Version", "Preferred Version")
logger.plain("%-35s %25s %25s\n", "===========", "==============", "=================")
- for p in sorted(pkg_pn):
+ for p in sorted(self.recipecaches[''].pkg_pn):
pref = preferred_versions[p]
latest = latest_versions[p]
@@ -619,7 +625,7 @@ class BBCooker:
fn = self.matchFile(fn)
fn = bb.cache.realfn2virtual(fn, cls, mc)
elif len(pkgs_to_build) == 1:
- ignore = self.expanded_data.getVar("ASSUME_PROVIDED", True) or ""
+ ignore = self.data.getVar("ASSUME_PROVIDED") or ""
if pkgs_to_build[0] in set(ignore.split()):
bb.fatal("%s is in ASSUME_PROVIDED" % pkgs_to_build[0])
@@ -644,14 +650,13 @@ class BBCooker:
logger.plain(env.getvalue())
# emit variables and shell functions
- 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():
+ for e in sorted(envdata.keys()):
if envdata.getVarFlag(e, 'func', False) and envdata.getVarFlag(e, 'python', False):
logger.plain("\npython %s () {\n%s}\n", e, envdata.getVar(e, False))
@@ -705,7 +710,6 @@ class BBCooker:
for mc in self.multiconfigs:
taskdata[mc] = bb.taskdata.TaskData(abort, skiplist=self.skiplist, allowincomplete=allowincomplete)
localdata[mc] = data.createCopy(self.databuilder.mcdata[mc])
- bb.data.update_data(localdata[mc])
bb.data.expandKeys(localdata[mc])
current = 0
@@ -766,7 +770,7 @@ class BBCooker:
@staticmethod
def add_mc_prefix(mc, pn):
if mc:
- return "multiconfig:%s.%s" % (mc, pn)
+ return "multiconfig:%s:%s" % (mc, pn)
return pn
def buildDependTree(self, rq, taskdata):
@@ -951,62 +955,54 @@ class BBCooker:
depgraph = self.generateTaskDepTreeData(pkgs_to_build, task)
- # Prints a flattened form of package-depends below where subpackages of a package are merged into the main pn
- depends_file = open('pn-depends.dot', 'w' )
- buildlist_file = open('pn-buildlist', 'w' )
- print("digraph depends {", file=depends_file)
- for pn in depgraph["pn"]:
- fn = depgraph["pn"][pn]["filename"]
- version = depgraph["pn"][pn]["version"]
- print('"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn), file=depends_file)
- print("%s" % pn, file=buildlist_file)
- buildlist_file.close()
+ with open('pn-buildlist', 'w') as f:
+ for pn in depgraph["pn"]:
+ f.write(pn + "\n")
logger.info("PN build list saved to 'pn-buildlist'")
- for pn in depgraph["depends"]:
- for depend in depgraph["depends"][pn]:
- print('"%s" -> "%s" [style=solid]' % (pn, depend), file=depends_file)
- for pn in depgraph["rdepends-pn"]:
- for rdepend in depgraph["rdepends-pn"][pn]:
- print('"%s" -> "%s" [style=dashed]' % (pn, rdepend), file=depends_file)
- print("}", file=depends_file)
- depends_file.close()
- logger.info("PN dependencies saved to 'pn-depends.dot'")
-
- depends_file = open('package-depends.dot', 'w' )
- print("digraph depends {", file=depends_file)
- for package in depgraph["packages"]:
- pn = depgraph["packages"][package]["pn"]
- fn = depgraph["packages"][package]["filename"]
- version = depgraph["packages"][package]["version"]
- if package == pn:
- print('"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn), file=depends_file)
- else:
- print('"%s" [label="%s(%s) %s\\n%s"]' % (package, package, pn, version, fn), file=depends_file)
- for depend in depgraph["depends"][pn]:
- print('"%s" -> "%s" [style=solid]' % (package, depend), file=depends_file)
- for package in depgraph["rdepends-pkg"]:
- for rdepend in depgraph["rdepends-pkg"][package]:
- print('"%s" -> "%s" [style=dashed]' % (package, rdepend), file=depends_file)
- for package in depgraph["rrecs-pkg"]:
- for rdepend in depgraph["rrecs-pkg"][package]:
- print('"%s" -> "%s" [style=dotted]' % (package, rdepend), file=depends_file)
- print("}", file=depends_file)
- depends_file.close()
- logger.info("Package dependencies saved to 'package-depends.dot'")
-
- tdepends_file = open('task-depends.dot', 'w' )
- print("digraph depends {", file=tdepends_file)
- for task in depgraph["tdepends"]:
- (pn, taskname) = task.rsplit(".", 1)
- fn = depgraph["pn"][pn]["filename"]
- version = depgraph["pn"][pn]["version"]
- print('"%s.%s" [label="%s %s\\n%s\\n%s"]' % (pn, taskname, pn, taskname, version, fn), file=tdepends_file)
- for dep in depgraph["tdepends"][task]:
- print('"%s" -> "%s"' % (task, dep), file=tdepends_file)
- print("}", file=tdepends_file)
- tdepends_file.close()
+
+ # Remove old format output files to ensure no confusion with stale data
+ try:
+ os.unlink('pn-depends.dot')
+ except FileNotFoundError:
+ pass
+ try:
+ os.unlink('package-depends.dot')
+ except FileNotFoundError:
+ pass
+
+ with open('task-depends.dot', 'w') as f:
+ f.write("digraph depends {\n")
+ for task in depgraph["tdepends"]:
+ (pn, taskname) = task.rsplit(".", 1)
+ fn = depgraph["pn"][pn]["filename"]
+ version = depgraph["pn"][pn]["version"]
+ f.write('"%s.%s" [label="%s %s\\n%s\\n%s"]\n' % (pn, taskname, pn, taskname, version, fn))
+ for dep in depgraph["tdepends"][task]:
+ f.write('"%s" -> "%s"\n' % (task, dep))
+ f.write("}\n")
logger.info("Task dependencies saved to 'task-depends.dot'")
+ with open('recipe-depends.dot', 'w') as f:
+ f.write("digraph depends {\n")
+ pndeps = {}
+ for task in depgraph["tdepends"]:
+ (pn, taskname) = task.rsplit(".", 1)
+ if pn not in pndeps:
+ pndeps[pn] = set()
+ for dep in depgraph["tdepends"][task]:
+ (deppn, deptaskname) = dep.rsplit(".", 1)
+ pndeps[pn].add(deppn)
+ for pn in pndeps:
+ fn = depgraph["pn"][pn]["filename"]
+ version = depgraph["pn"][pn]["version"]
+ f.write('"%s" [label="%s\\n%s\\n%s"]\n' % (pn, pn, version, fn))
+ for dep in pndeps[pn]:
+ if dep == pn:
+ continue
+ f.write('"%s" -> "%s"\n' % (pn, dep))
+ f.write("}\n")
+ logger.info("Flatened recipe dependencies saved to 'recipe-depends.dot'")
+
def show_appends_with_no_recipes(self):
# Determine which bbappends haven't been applied
@@ -1037,11 +1033,10 @@ class BBCooker:
for mc in self.multiconfigs:
localdata = data.createCopy(self.databuilder.mcdata[mc])
- bb.data.update_data(localdata)
bb.data.expandKeys(localdata)
# Handle PREFERRED_PROVIDERS
- for p in (localdata.getVar('PREFERRED_PROVIDERS', True) or "").split():
+ for p in (localdata.getVar('PREFERRED_PROVIDERS') or "").split():
try:
(providee, provider) = p.split(':')
except:
@@ -1052,7 +1047,7 @@ class BBCooker:
self.recipecaches[mc].preferred[providee] = provider
def findCoreBaseFiles(self, subdir, configfile):
- corebase = self.data.getVar('COREBASE', True) or ""
+ corebase = self.data.getVar('COREBASE') or ""
paths = []
for root, dirs, files in os.walk(corebase + '/' + subdir):
for d in dirs:
@@ -1102,7 +1097,7 @@ class BBCooker:
"""
matches = []
- bbpaths = self.data.getVar('BBPATH', True).split(':')
+ bbpaths = self.data.getVar('BBPATH').split(':')
for path in bbpaths:
dirpath = os.path.join(path, directory)
if os.path.exists(dirpath):
@@ -1114,6 +1109,20 @@ class BBCooker:
if matches:
bb.event.fire(bb.event.FilesMatchingFound(filepattern, matches), self.data)
+ def findProviders(self, mc=''):
+ return bb.providers.findProviders(self.data, self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
+
+ def findBestProvider(self, pn, mc=''):
+ if pn in self.recipecaches[mc].providers:
+ filenames = self.recipecaches[mc].providers[pn]
+ eligible, foundUnique = bb.providers.filterProviders(filenames, pn, self.data, self.recipecaches[mc])
+ filename = eligible[0]
+ return None, None, None, filename
+ elif pn in self.recipecaches[mc].pkg_pn:
+ return bb.providers.findBestProvider(pn, self.data, self.recipecaches[mc], self.recipecaches[mc].pkg_pn)
+ else:
+ return None, None, None, None
+
def findConfigFiles(self, varname):
"""
Find config files which are appropriate values for varname.
@@ -1124,7 +1133,7 @@ class BBCooker:
data = self.data
# iterate configs
- bbpaths = data.getVar('BBPATH', True).split(':')
+ bbpaths = data.getVar('BBPATH').split(':')
for path in bbpaths:
confpath = os.path.join(path, "conf", var)
if os.path.exists(confpath):
@@ -1193,7 +1202,7 @@ class BBCooker:
bb.debug(1,'Processing %s in collection list' % (c))
# Get collection priority if defined explicitly
- priority = self.data.getVar("BBFILE_PRIORITY_%s" % c, True)
+ priority = self.data.getVar("BBFILE_PRIORITY_%s" % c)
if priority:
try:
prio = int(priority)
@@ -1207,7 +1216,7 @@ class BBCooker:
collection_priorities[c] = None
# Check dependencies and store information for priority calculation
- deps = self.data.getVar("LAYERDEPENDS_%s" % c, True)
+ deps = self.data.getVar("LAYERDEPENDS_%s" % c)
if deps:
try:
depDict = bb.utils.explode_dep_versions2(deps)
@@ -1216,7 +1225,7 @@ class BBCooker:
for dep, oplist in list(depDict.items()):
if dep in collection_list:
for opstr in oplist:
- layerver = self.data.getVar("LAYERVERSION_%s" % dep, True)
+ layerver = self.data.getVar("LAYERVERSION_%s" % dep)
(op, depver) = opstr.split()
if layerver:
try:
@@ -1237,7 +1246,7 @@ class BBCooker:
collection_depends[c] = []
# Check recommends and store information for priority calculation
- recs = self.data.getVar("LAYERRECOMMENDS_%s" % c, True)
+ recs = self.data.getVar("LAYERRECOMMENDS_%s" % c)
if recs:
try:
recDict = bb.utils.explode_dep_versions2(recs)
@@ -1247,7 +1256,7 @@ class BBCooker:
if rec in collection_list:
if oplist:
opstr = oplist[0]
- layerver = self.data.getVar("LAYERVERSION_%s" % rec, True)
+ layerver = self.data.getVar("LAYERVERSION_%s" % rec)
if layerver:
(op, recver) = opstr.split()
try:
@@ -1281,17 +1290,21 @@ class BBCooker:
# Calculate all layer priorities using calc_layer_priority and store in bbfile_config_priorities
for c in collection_list:
calc_layer_priority(c)
- regex = self.data.getVar("BBFILE_PATTERN_%s" % c, True)
+ regex = self.data.getVar("BBFILE_PATTERN_%s" % c)
if regex == None:
parselog.error("BBFILE_PATTERN_%s not defined" % c)
errors = True
continue
- try:
- cre = re.compile(regex)
- except re.error:
- parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
- errors = True
- continue
+ elif regex == "":
+ parselog.debug(1, "BBFILE_PATTERN_%s is empty" % c)
+ errors = False
+ else:
+ try:
+ cre = re.compile(regex)
+ except re.error:
+ parselog.error("BBFILE_PATTERN_%s \"%s\" is not a valid regular expression", c, regex)
+ errors = True
+ continue
self.bbfile_config_priorities.append((c, regex, cre, collection_priorities[c]))
if errors:
# We've already printed the actual error(s)
@@ -1316,7 +1329,7 @@ class BBCooker:
bf = os.path.abspath(bf)
self.collection = CookerCollectFiles(self.bbfile_config_priorities)
- filelist, masked = self.collection.collect_bbfiles(self.data, self.expanded_data)
+ filelist, masked, searchdirs = self.collection.collect_bbfiles(self.data, self.data)
try:
os.stat(bf)
bf = os.path.abspath(bf)
@@ -1347,15 +1360,16 @@ class BBCooker:
raise NoSpecificMatch
return matches[0]
- def buildFile(self, buildfile, task):
+ def buildFile(self, buildfile, task, hidewarning=False):
"""
Build the file matching regexp buildfile
"""
- bb.event.fire(bb.event.BuildInit(), self.expanded_data)
+ bb.event.fire(bb.event.BuildInit(), self.data)
- # Too many people use -b because they think it's how you normally
- # specify a target to be built, so show a warning
- bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
+ if not hidewarning:
+ # Too many people use -b because they think it's how you normally
+ # specify a target to be built, so show a warning
+ bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")
# Parse the configuration here. We need to do it explicitly here since
# buildFile() doesn't use the cache
@@ -1392,6 +1406,7 @@ class BBCooker:
item = info_array[0].pn
self.recipecaches[mc].ignored_dependencies = set()
self.recipecaches[mc].bbfile_priority[fn] = 1
+ self.configuration.limited_deps = True
# Remove external dependencies
self.recipecaches[mc].task_deps[fn]['depends'] = {}
@@ -1409,8 +1424,8 @@ class BBCooker:
taskdata[mc] = bb.taskdata.TaskData(self.configuration.abort)
taskdata[mc].add_provider(self.data, self.recipecaches[mc], item)
- buildname = self.data.getVar("BUILDNAME", True)
- bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.expanded_data)
+ buildname = self.data.getVar("BUILDNAME")
+ bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.data)
# Execute the runqueue
runlist = [[mc, item, task, fn]]
@@ -1440,7 +1455,7 @@ class BBCooker:
return False
if not retval:
- bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, item, failures, interrupted), self.expanded_data)
+ bb.event.fire(bb.event.BuildCompleted(len(rq.rqdata.runtaskentries), buildname, item, failures, interrupted), self.data)
self.command.finishAsyncCommand(msg)
return False
if retval is True:
@@ -1495,7 +1510,7 @@ class BBCooker:
packages = [target if ':' in target else '%s:%s' % (target, task) for target in targets]
- bb.event.fire(bb.event.BuildInit(packages), self.expanded_data)
+ bb.event.fire(bb.event.BuildInit(packages), self.data)
taskdata, runlist = self.buildTaskData(targets, task, self.configuration.abort)
@@ -1528,7 +1543,7 @@ class BBCooker:
v = self.data.getVar(k, expand)
if not k.startswith("__") and not isinstance(v, bb.data_smart.DataSmart):
dump[k] = {
- 'v' : v ,
+ 'v' : str(v) ,
'history' : self.data.varhistory.variable(k),
}
for d in flaglist:
@@ -1627,14 +1642,18 @@ class BBCooker:
bb.event.fire(bb.event.SanityCheck(False), self.databuilder.mcdata[mc])
for mc in self.multiconfigs:
- ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED", True) or ""
+ ignore = self.databuilder.mcdata[mc].getVar("ASSUME_PROVIDED") or ""
self.recipecaches[mc].ignored_dependencies = set(ignore.split())
for dep in self.configuration.extra_assume_provided:
self.recipecaches[mc].ignored_dependencies.add(dep)
self.collection = CookerCollectFiles(self.bbfile_config_priorities)
- (filelist, masked) = self.collection.collect_bbfiles(self.data, self.expanded_data)
+ (filelist, masked, searchdirs) = self.collection.collect_bbfiles(self.data, self.data)
+
+ # Add inotify watches for directories searched for bb/bbappend files
+ for dirent in searchdirs:
+ self.add_filewatch([[dirent]], dirs=True)
self.parser = CookerParser(self, filelist, masked)
self.parsecache_valid = True
@@ -1668,7 +1687,7 @@ class BBCooker:
if len(pkgs_to_build) == 0:
raise NothingToBuild
- ignore = (self.expanded_data.getVar("ASSUME_PROVIDED", True) or "").split()
+ ignore = (self.data.getVar("ASSUME_PROVIDED") or "").split()
for pkg in pkgs_to_build:
if pkg in ignore:
parselog.warning("Explicit target \"%s\" is in ASSUME_PROVIDED, ignoring" % pkg)
@@ -1688,6 +1707,15 @@ class BBCooker:
pkgs_to_build.remove('universe')
for mc in self.multiconfigs:
for t in self.recipecaches[mc].universe_target:
+ if task:
+ foundtask = False
+ for provider_fn in self.recipecaches[mc].providers[t]:
+ if task in self.recipecaches[mc].task_deps[provider_fn]['tasks']:
+ foundtask = True
+ break
+ if not foundtask:
+ bb.debug(1, "Skipping %s for universe tasks as task %s doesn't exist" % (t, task))
+ continue
if mc:
t = "multiconfig:" + mc + ":" + t
pkgs_to_build.append(t)
@@ -1701,13 +1729,13 @@ class BBCooker:
try:
self.prhost = prserv.serv.auto_start(self.data)
except prserv.serv.PRServiceConfigError:
- bb.event.fire(CookerExit(), self.expanded_data)
+ bb.event.fire(CookerExit(), self.data)
self.state = state.error
return
def post_serve(self):
prserv.serv.auto_shutdown(self.data)
- bb.event.fire(CookerExit(), self.expanded_data)
+ bb.event.fire(CookerExit(), self.data)
lockfile = self.lock.name
self.lock.close()
self.lock = None
@@ -1745,6 +1773,8 @@ class BBCooker:
if self.parser:
self.parser.shutdown(clean=not force, force=force)
+ self.notifier.stop()
+ self.confignotifier.stop()
def finishcommand(self):
self.state = state.initial
@@ -1752,6 +1782,13 @@ class BBCooker:
def reset(self):
self.initConfigurationData()
+ def clientComplete(self):
+ """Called when the client is done using the server"""
+ if self.configuration.server_only:
+ self.finishcommand()
+ else:
+ self.shutdown(True)
+
def lockBitbake(self):
if not hasattr(self, 'lock'):
self.lock = None
@@ -1838,7 +1875,7 @@ class CookerCollectFiles(object):
collectlog.debug(1, "collecting .bb files")
- files = (config.getVar( "BBFILES", True) or "").split()
+ files = (config.getVar( "BBFILES") or "").split()
config.setVar("BBFILES", " ".join(files))
# Sort files by priority
@@ -1851,30 +1888,49 @@ class CookerCollectFiles(object):
collectlog.error("no recipe files to build, check your BBPATH and BBFILES?")
bb.event.fire(CookerExit(), eventdata)
- # Can't use set here as order is important
- newfiles = []
- for f in files:
- if os.path.isdir(f):
- dirfiles = self.find_bbfiles(f)
- for g in dirfiles:
- if g not in newfiles:
- newfiles.append(g)
- else:
- globbed = glob.glob(f)
- if not globbed and os.path.exists(f):
- globbed = [f]
- # glob gives files in order on disk. Sort to be deterministic.
- for g in sorted(globbed):
- if g not in newfiles:
- newfiles.append(g)
+ # We need to track where we look so that we can add inotify watches. There
+ # is no nice way to do this, this is horrid. We intercept the os.listdir()
+ # calls while we run glob().
+ origlistdir = os.listdir
+ searchdirs = []
+
+ def ourlistdir(d):
+ searchdirs.append(d)
+ return origlistdir(d)
+
+ os.listdir = ourlistdir
+ try:
+ # Can't use set here as order is important
+ newfiles = []
+ for f in files:
+ if os.path.isdir(f):
+ dirfiles = self.find_bbfiles(f)
+ for g in dirfiles:
+ if g not in newfiles:
+ newfiles.append(g)
+ else:
+ globbed = glob.glob(f)
+ if not globbed and os.path.exists(f):
+ globbed = [f]
+ # glob gives files in order on disk. Sort to be deterministic.
+ for g in sorted(globbed):
+ if g not in newfiles:
+ newfiles.append(g)
+ finally:
+ os.listdir = origlistdir
- bbmask = config.getVar('BBMASK', True)
+ bbmask = config.getVar('BBMASK')
if bbmask:
# First validate the individual regular expressions and ignore any
# that do not compile
bbmasks = []
for mask in bbmask.split():
+ # When constructing an older style single regex, it's possible for BBMASK
+ # to end up beginning with '|', which matches and masks _everything_.
+ if mask.startswith("|"):
+ collectlog.warn("BBMASK contains regular expression beginning with '|', fixing: %s" % mask)
+ mask = mask[1:]
try:
re.compile(mask)
bbmasks.append(mask)
@@ -1921,7 +1977,7 @@ class CookerCollectFiles(object):
topfile = bbfile_seen[base]
self.overlayed[topfile].append(f)
- return (bbfiles, masked)
+ return (bbfiles, masked, searchdirs)
def get_file_appends(self, fn):
"""
@@ -1964,7 +2020,7 @@ class CookerCollectFiles(object):
for collection, pattern, regex, _ in self.bbfile_config_priorities:
if regex in unmatched:
- if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection, True) != '1':
+ if d.getVar('BBFILE_PATTERN_IGNORE_EMPTY_%s' % collection) != '1':
collectlog.warning("No bb files matched BBFILE_PATTERN_%s '%s'" % (collection, pattern))
return priorities
@@ -2121,7 +2177,7 @@ class CookerParser(object):
self.toparse = self.total - len(self.fromcache)
self.progress_chunk = int(max(self.toparse / 100, 1))
- self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS", True) or
+ self.num_processes = min(int(self.cfgdata.getVar("BB_NUMBER_PARSE_THREADS") or
multiprocessing.cpu_count()), len(self.willparse))
self.start()
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/cookerdata.py b/import-layers/yocto-poky/bitbake/lib/bb/cookerdata.py
index 98f56ac7b..e408a35e1 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/cookerdata.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/cookerdata.py
@@ -79,7 +79,7 @@ class ConfigParameters(object):
"prefile", "postfile"]:
options[o] = getattr(self.options, o)
- ret, error = server.runCommand(["updateConfig", options, environment])
+ ret, error = server.runCommand(["updateConfig", options, environment, sys.argv])
if error:
raise Exception("Unable to update the server configuration with local parameters: %s" % error)
@@ -146,6 +146,9 @@ class CookerConfiguration(object):
self.tracking = False
self.interface = []
self.writeeventlog = False
+ self.server_only = False
+ self.limited_deps = False
+ self.runall = None
self.env = {}
@@ -212,7 +215,7 @@ def _inherit(bbclass, data):
def findConfigFile(configfile, data):
search = []
- bbpath = data.getVar("BBPATH", True)
+ bbpath = data.getVar("BBPATH")
if bbpath:
for i in bbpath.split(":"):
search.append(os.path.join(i, "conf", configfile))
@@ -286,7 +289,7 @@ class CookerDataBuilder(object):
self.data_hash = self.data.get_hash()
self.mcdata[''] = self.data
- multiconfig = (self.data.getVar("BBMULTICONFIG", True) or "").split()
+ multiconfig = (self.data.getVar("BBMULTICONFIG") or "").split()
for config in multiconfig:
mcdata = self.parseConfigurationFiles(self.prefiles, self.postfiles, config)
bb.event.fire(bb.event.ConfigParsed(), mcdata)
@@ -320,7 +323,7 @@ class CookerDataBuilder(object):
data.setVar("TOPDIR", os.path.dirname(os.path.dirname(layerconf)))
data = parse_config_file(layerconf, data)
- layers = (data.getVar('BBLAYERS', True) or "").split()
+ layers = (data.getVar('BBLAYERS') or "").split()
data = bb.data.createCopy(data)
approved = bb.utils.approved_variables()
@@ -343,7 +346,7 @@ class CookerDataBuilder(object):
data.delVar('LAYERDIR_RE')
data.delVar('LAYERDIR')
- if not data.getVar("BBPATH", True):
+ if not data.getVar("BBPATH"):
msg = "The BBPATH variable is not set"
if not layerconf:
msg += (" and bitbake did not find a conf/bblayers.conf file in"
@@ -358,7 +361,7 @@ class CookerDataBuilder(object):
data = parse_config_file(p, data)
# Handle any INHERITs and inherit the base class
- bbclasses = ["base"] + (data.getVar('INHERIT', True) or "").split()
+ bbclasses = ["base"] + (data.getVar('INHERIT') or "").split()
for bbclass in bbclasses:
data = _inherit(bbclass, data)
@@ -370,7 +373,7 @@ class CookerDataBuilder(object):
parselog.critical("Undefined event handler function '%s'" % var)
sys.exit(1)
handlerln = int(data.getVarFlag(var, "lineno", False))
- bb.event.register(var, data.getVar(var, False), (data.getVarFlag(var, "eventmask", True) or "").split(), handlerfn, handlerln)
+ bb.event.register(var, data.getVar(var, False), (data.getVarFlag(var, "eventmask") or "").split(), handlerfn, handlerln)
data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/data.py b/import-layers/yocto-poky/bitbake/lib/bb/data.py
index c56965c60..134afaacc 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/data.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/data.py
@@ -78,59 +78,6 @@ def initVar(var, d):
"""Non-destructive var init for data structure"""
d.initVar(var)
-
-def setVar(var, value, d):
- """Set a variable to a given value"""
- d.setVar(var, value)
-
-
-def getVar(var, d, exp = False):
- """Gets the value of a variable"""
- return d.getVar(var, exp)
-
-
-def renameVar(key, newkey, d):
- """Renames a variable from key to newkey"""
- d.renameVar(key, newkey)
-
-def delVar(var, d):
- """Removes a variable from the data set"""
- d.delVar(var)
-
-def appendVar(var, value, d):
- """Append additional value to a variable"""
- d.appendVar(var, value)
-
-def setVarFlag(var, flag, flagvalue, d):
- """Set a flag for a given variable to a given value"""
- d.setVarFlag(var, flag, flagvalue)
-
-def getVarFlag(var, flag, d):
- """Gets given flag from given var"""
- return d.getVarFlag(var, flag, False)
-
-def delVarFlag(var, flag, d):
- """Removes a given flag from the variable's flags"""
- d.delVarFlag(var, flag)
-
-def setVarFlags(var, flags, d):
- """Set the flags for a given variable
-
- Note:
- setVarFlags will not clear previous
- flags. Think of this method as
- addVarFlags
- """
- d.setVarFlags(var, flags)
-
-def getVarFlags(var, d):
- """Gets a variable's flags"""
- return d.getVarFlags(var)
-
-def delVarFlags(var, d):
- """Removes a variable's flags"""
- d.delVarFlags(var)
-
def keys(d):
"""Return a list of keys in d"""
return d.keys()
@@ -174,7 +121,7 @@ def inheritFromOS(d, savedenv, permitted):
for s in savedenv.keys():
if s in permitted:
try:
- d.setVar(s, savedenv.getVar(s, True), op = 'from env')
+ d.setVar(s, savedenv.getVar(s), op = 'from env')
if s in exportlist:
d.setVarFlag(s, "export", True, op = 'auto env export')
except TypeError:
@@ -194,7 +141,7 @@ def emit_var(var, o=sys.__stdout__, d = init(), all=False):
try:
if all:
oval = d.getVar(var, False)
- val = d.getVar(var, True)
+ val = d.getVar(var)
except (KeyboardInterrupt, bb.build.FuncFailed):
raise
except Exception as exc:
@@ -249,7 +196,7 @@ def emit_env(o=sys.__stdout__, d = init(), all=False):
keys = sorted((key for key in d.keys() if not key.startswith("__")), key=isfunc)
grouped = groupby(keys, isfunc)
for isfunc, keys in grouped:
- for key in keys:
+ for key in sorted(keys):
emit_var(key, o, d, all and not isfunc) and o.write('\n')
def exported_keys(d):
@@ -261,9 +208,9 @@ def exported_vars(d):
k = list(exported_keys(d))
for key in k:
try:
- value = d.getVar(key, True)
+ value = d.getVar(key)
except Exception as err:
- bb.warn("%s: Unable to export ${%s}: %s" % (d.getVar("FILE", True), key, err))
+ bb.warn("%s: Unable to export ${%s}: %s" % (d.getVar("FILE"), key, err))
continue
if value is not None:
@@ -273,13 +220,13 @@ def emit_func(func, o=sys.__stdout__, d = init()):
"""Emits all items in the data store in a format such that it can be sourced by a shell."""
keys = (key for key in d.keys() if not key.startswith("__") and not d.getVarFlag(key, "func", False))
- for key in keys:
+ for key in sorted(keys):
emit_var(key, o, d, False)
o.write('\n')
emit_var(func, o, d, False) and o.write('\n')
- newdeps = bb.codeparser.ShellParser(func, logger).parse_shell(d.getVar(func, True))
- newdeps |= set((d.getVarFlag(func, "vardeps", True) or "").split())
+ newdeps = bb.codeparser.ShellParser(func, logger).parse_shell(d.getVar(func))
+ newdeps |= set((d.getVarFlag(func, "vardeps") or "").split())
seen = set()
while newdeps:
deps = newdeps
@@ -288,8 +235,8 @@ def emit_func(func, o=sys.__stdout__, d = init()):
for dep in deps:
if d.getVarFlag(dep, "func", False) and not d.getVarFlag(dep, "python", False):
emit_var(dep, o, d, False) and o.write('\n')
- newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep, True))
- newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
+ newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep))
+ newdeps |= set((d.getVarFlag(dep, "vardeps") or "").split())
newdeps -= seen
_functionfmt = """
@@ -312,7 +259,7 @@ def emit_func_python(func, o=sys.__stdout__, d = init()):
pp = bb.codeparser.PythonParser(func, logger)
pp.parse_python(d.getVar(func, False))
newdeps = pp.execs
- newdeps |= set((d.getVarFlag(func, "vardeps", True) or "").split())
+ newdeps |= set((d.getVarFlag(func, "vardeps") or "").split())
seen = set()
while newdeps:
deps = newdeps
@@ -324,7 +271,7 @@ def emit_func_python(func, o=sys.__stdout__, d = init()):
pp = bb.codeparser.PythonParser(dep, logger)
pp.parse_python(d.getVar(dep, False))
newdeps |= pp.execs
- newdeps |= set((d.getVarFlag(dep, "vardeps", True) or "").split())
+ newdeps |= set((d.getVarFlag(dep, "vardeps") or "").split())
newdeps -= seen
def update_data(d):
@@ -348,12 +295,14 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
def handle_contains(value, contains, d):
newvalue = ""
for k in sorted(contains):
- l = (d.getVar(k, True) or "").split()
- for word in sorted(contains[k]):
- if word in l:
- newvalue += "\n%s{%s} = Set" % (k, word)
+ l = (d.getVar(k) or "").split()
+ for item in sorted(contains[k]):
+ for word in item.split():
+ if not word in l:
+ newvalue += "\n%s{%s} = Unset" % (k, item)
+ break
else:
- newvalue += "\n%s{%s} = Unset" % (k, word)
+ newvalue += "\n%s{%s} = Set" % (k, item)
if not newvalue:
return value
if not value:
@@ -366,7 +315,7 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
if varflags.get("python"):
parser = bb.codeparser.PythonParser(key, logger)
if value and "\t" in value:
- logger.warning("Variable %s contains tabs, please remove these (%s)" % (key, d.getVar("FILE", True)))
+ logger.warning("Variable %s contains tabs, please remove these (%s)" % (key, d.getVar("FILE")))
parser.parse_python(value, filename=varflags.get("filename"), lineno=varflags.get("lineno"))
deps = deps | parser.references
deps = deps | (keys & parser.execs)
@@ -410,6 +359,8 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
deps |= set((vardeps or "").split())
deps -= set(varflags.get("vardepsexclude", "").split())
+ except bb.parse.SkipRecipe:
+ raise
except Exception as e:
bb.warn("Exception during build_dependencies for %s" % key)
raise
@@ -421,7 +372,7 @@ def generate_dependencies(d):
keys = set(key for key in d if not key.startswith("__"))
shelldeps = set(key for key in d.getVar("__exportlist", False) if d.getVarFlag(key, "export", False) and not d.getVarFlag(key, "unexport", False))
- varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS', True)
+ varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS')
deps = {}
values = {}
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/data_smart.py b/import-layers/yocto-poky/bitbake/lib/bb/data_smart.py
index 805a9a71f..7dc1c6870 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/data_smart.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/data_smart.py
@@ -108,7 +108,7 @@ class VariableParse:
varparse = self.d.expand_cache[key]
var = varparse.value
else:
- var = self.d.getVarFlag(key, "_content", True)
+ var = self.d.getVarFlag(key, "_content")
self.references.add(key)
if var is not None:
return var
@@ -116,13 +116,21 @@ class VariableParse:
return match.group()
def python_sub(self, match):
- code = match.group()[3:-1]
+ if isinstance(match, str):
+ code = match
+ else:
+ code = match.group()[3:-1]
+
+ if "_remote_data" in self.d:
+ connector = self.d["_remote_data"]
+ return connector.expandPythonRef(self.varname, code, self.d)
+
codeobj = compile(code.strip(), self.varname or "<expansion>", "eval")
parser = bb.codeparser.PythonParser(self.varname, logger)
parser.parse_python(code)
if self.varname:
- vardeps = self.d.getVarFlag(self.varname, "vardeps", True)
+ vardeps = self.d.getVarFlag(self.varname, "vardeps")
if vardeps is None:
parser.log.flush()
else:
@@ -146,7 +154,7 @@ class DataContext(dict):
self['d'] = metadata
def __missing__(self, key):
- value = self.metadata.getVar(key, True)
+ value = self.metadata.getVar(key)
if value is None or self.metadata.getVarFlag(key, 'func', False):
raise KeyError(key)
else:
@@ -222,6 +230,19 @@ class VariableHistory(object):
new.variables = self.variables.copy()
return new
+ def __getstate__(self):
+ vardict = {}
+ for k, v in self.variables.iteritems():
+ vardict[k] = v
+ return {'dataroot': self.dataroot,
+ 'variables': vardict}
+
+ def __setstate__(self, state):
+ self.dataroot = state['dataroot']
+ self.variables = COWDictBase.copy()
+ for k, v in state['variables'].items():
+ self.variables[k] = v
+
def record(self, *kwonly, **loginfo):
if not self.dataroot._tracking:
return
@@ -247,10 +268,15 @@ class VariableHistory(object):
self.variables[var].append(loginfo.copy())
def variable(self, var):
- if var in self.variables:
- return self.variables[var]
+ remote_connector = self.dataroot.getVar('_remote_data', False)
+ if remote_connector:
+ varhistory = remote_connector.getVarHistory(var)
else:
- return []
+ varhistory = []
+
+ if var in self.variables:
+ varhistory.extend(self.variables[var])
+ return varhistory
def emit(self, var, oval, val, o, d):
history = self.variable(var)
@@ -318,7 +344,7 @@ class VariableHistory(object):
the files in which they were added.
"""
history = self.variable(var)
- finalitems = (d.getVar(var, True) or '').split()
+ finalitems = (d.getVar(var) or '').split()
filemap = {}
isset = False
for event in history:
@@ -426,11 +452,11 @@ class DataSmart(MutableMapping):
# Can end up here recursively so setup dummy values
self.overrides = []
self.overridesset = set()
- self.overrides = (self.getVar("OVERRIDES", True) or "").split(":") or []
+ self.overrides = (self.getVar("OVERRIDES") or "").split(":") or []
self.overridesset = set(self.overrides)
self.inoverride = False
self.expand_cache = {}
- newoverrides = (self.getVar("OVERRIDES", True) or "").split(":") or []
+ newoverrides = (self.getVar("OVERRIDES") or "").split(":") or []
if newoverrides == self.overrides:
break
self.overrides = newoverrides
@@ -447,17 +473,22 @@ class DataSmart(MutableMapping):
dest = self.dict
while dest:
if var in dest:
- return dest[var]
+ return dest[var], self.overridedata.get(var, None)
+
+ if "_remote_data" in dest:
+ connector = dest["_remote_data"]["_content"]
+ return connector.getVar(var)
if "_data" not in dest:
break
dest = dest["_data"]
+ return None, self.overridedata.get(var, None)
def _makeShadowCopy(self, var):
if var in self.dict:
return
- local_var = self._findVar(var)
+ local_var, _ = self._findVar(var)
if local_var:
self.dict[var] = copy.copy(local_var)
@@ -471,6 +502,12 @@ class DataSmart(MutableMapping):
if 'parsing' in loginfo:
parsing=True
+ if '_remote_data' in self.dict:
+ connector = self.dict["_remote_data"]["_content"]
+ res = connector.setVar(var, value)
+ if not res:
+ return
+
if 'op' not in loginfo:
loginfo['op'] = "set"
self.expand_cache = {}
@@ -509,6 +546,8 @@ class DataSmart(MutableMapping):
del self.dict[var]["_append"]
if "_prepend" in self.dict[var]:
del self.dict[var]["_prepend"]
+ if "_remove" in self.dict[var]:
+ del self.dict[var]["_remove"]
if var in self.overridedata:
active = []
self.need_overrides()
@@ -541,7 +580,7 @@ class DataSmart(MutableMapping):
nextnew = set()
self.overridevars.update(new)
for i in new:
- vardata = self.expandWithRefs(self.getVar(i, True), i)
+ vardata = self.expandWithRefs(self.getVar(i), i)
nextnew.update(vardata.references)
nextnew.update(vardata.contains.keys())
new = nextnew
@@ -565,13 +604,19 @@ class DataSmart(MutableMapping):
if len(shortvar) == 0:
override = None
- def getVar(self, var, expand, noweakdefault=False, parsing=False):
+ def getVar(self, var, expand=True, noweakdefault=False, parsing=False):
return self.getVarFlag(var, "_content", expand, noweakdefault, parsing)
def renameVar(self, key, newkey, **loginfo):
"""
Rename the variable key to newkey
"""
+ if '_remote_data' in self.dict:
+ connector = self.dict["_remote_data"]["_content"]
+ res = connector.renameVar(key, newkey)
+ if not res:
+ return
+
val = self.getVar(key, 0, parsing=True)
if val is not None:
loginfo['variable'] = newkey
@@ -615,6 +660,12 @@ class DataSmart(MutableMapping):
self.setVar(var + "_prepend", value, ignore=True, parsing=True)
def delVar(self, var, **loginfo):
+ if '_remote_data' in self.dict:
+ connector = self.dict["_remote_data"]["_content"]
+ res = connector.delVar(var)
+ if not res:
+ return
+
loginfo['detail'] = ""
loginfo['op'] = 'del'
self.varhistory.record(**loginfo)
@@ -641,6 +692,12 @@ class DataSmart(MutableMapping):
override = None
def setVarFlag(self, var, flag, value, **loginfo):
+ if '_remote_data' in self.dict:
+ connector = self.dict["_remote_data"]["_content"]
+ res = connector.setVarFlag(var, flag, value)
+ if not res:
+ return
+
self.expand_cache = {}
if 'op' not in loginfo:
loginfo['op'] = "set"
@@ -662,14 +719,14 @@ class DataSmart(MutableMapping):
self.dict["__exportlist"]["_content"] = set()
self.dict["__exportlist"]["_content"].add(var)
- def getVarFlag(self, var, flag, expand, noweakdefault=False, parsing=False):
- local_var = self._findVar(var)
+ def getVarFlag(self, var, flag, expand=True, noweakdefault=False, parsing=False):
+ local_var, overridedata = self._findVar(var)
value = None
- if flag == "_content" and var in self.overridedata and not parsing:
+ if flag == "_content" and overridedata is not None and not parsing:
match = False
active = {}
self.need_overrides()
- for (r, o) in self.overridedata[var]:
+ for (r, o) in overridedata:
# What about double overrides both with "_" in the name?
if o in self.overridesset:
active[o] = r
@@ -759,8 +816,14 @@ class DataSmart(MutableMapping):
return value
def delVarFlag(self, var, flag, **loginfo):
+ if '_remote_data' in self.dict:
+ connector = self.dict["_remote_data"]["_content"]
+ res = connector.delVarFlag(var, flag)
+ if not res:
+ return
+
self.expand_cache = {}
- local_var = self._findVar(var)
+ local_var, _ = self._findVar(var)
if not local_var:
return
if not var in self.dict:
@@ -803,7 +866,7 @@ class DataSmart(MutableMapping):
self.dict[var][i] = flags[i]
def getVarFlags(self, var, expand = False, internalflags=False):
- local_var = self._findVar(var)
+ local_var, _ = self._findVar(var)
flags = {}
if local_var:
@@ -845,7 +908,7 @@ class DataSmart(MutableMapping):
data = DataSmart()
data.dict["_data"] = self.dict
data.varhistory = self.varhistory.copy()
- data.varhistory.datasmart = data
+ data.varhistory.dataroot = data
data.inchistory = self.inchistory.copy()
data._tracking = self._tracking
@@ -876,7 +939,7 @@ class DataSmart(MutableMapping):
def localkeys(self):
for key in self.dict:
- if key != '_data':
+ if key not in ['_data', '_remote_data']:
yield key
def __iter__(self):
@@ -885,7 +948,7 @@ class DataSmart(MutableMapping):
def keylist(d):
klist = set()
for key in d:
- if key == "_data":
+ if key in ["_data", "_remote_data"]:
continue
if key in deleted:
continue
@@ -899,6 +962,13 @@ class DataSmart(MutableMapping):
if "_data" in d:
klist |= keylist(d["_data"])
+ if "_remote_data" in d:
+ connector = d["_remote_data"]["_content"]
+ for key in connector.getKeys():
+ if key in deleted:
+ continue
+ klist.add(key)
+
return klist
self.need_overrides()
@@ -936,9 +1006,8 @@ class DataSmart(MutableMapping):
data = {}
d = self.createCopy()
bb.data.expandKeys(d)
- bb.data.update_data(d)
- config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST", True) or "").split())
+ config_whitelist = set((d.getVar("BB_HASHCONFIG_WHITELIST") or "").split())
keys = set(key for key in iter(d) if not key.startswith("__"))
for key in keys:
if key in config_whitelist:
@@ -957,7 +1026,6 @@ class DataSmart(MutableMapping):
for key in ["__BBTASKS", "__BBANONFUNCS", "__BBHANDLERS"]:
bb_list = d.getVar(key, False) or []
- bb_list.sort()
data.update({key:str(bb_list)})
if key == "__BBANONFUNCS":
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/event.py b/import-layers/yocto-poky/bitbake/lib/bb/event.py
index 6f1cb101f..6d8493b17 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/event.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/event.py
@@ -48,6 +48,16 @@ class Event(object):
def __init__(self):
self.pid = worker_pid
+
+class HeartbeatEvent(Event):
+ """Triggered at regular time intervals of 10 seconds. Other events can fire much more often
+ (runQueueTaskStarted when there are many short tasks) or not at all for long periods
+ of time (again runQueueTaskStarted, when there is just one long-running task), so this
+ event is more suitable for doing some task-independent work occassionally."""
+ def __init__(self, time):
+ Event.__init__(self)
+ self.time = time
+
Registered = 10
AlreadyRegistered = 14
@@ -351,6 +361,17 @@ class RecipeEvent(Event):
class RecipePreFinalise(RecipeEvent):
""" Recipe Parsing Complete but not yet finialised"""
+class RecipeTaskPreProcess(RecipeEvent):
+ """
+ Recipe Tasks about to be finalised
+ The list of tasks should be final at this point and handlers
+ are only able to change interdependencies
+ """
+ def __init__(self, fn, tasklist):
+ self.fn = fn
+ self.tasklist = tasklist
+ Event.__init__(self)
+
class RecipeParsed(RecipeEvent):
""" Recipe Parsing Complete """
@@ -372,7 +393,7 @@ class StampUpdate(Event):
targets = property(getTargets)
class BuildBase(Event):
- """Base class for bbmake run events"""
+ """Base class for bitbake build events"""
def __init__(self, n, p, failures = 0):
self._name = n
@@ -417,13 +438,13 @@ class BuildInit(BuildBase):
BuildBase.__init__(self, name, p)
class BuildStarted(BuildBase, OperationStarted):
- """bbmake build run started"""
+ """Event when builds start"""
def __init__(self, n, p, failures = 0):
OperationStarted.__init__(self, "Building Started")
BuildBase.__init__(self, n, p, failures)
class BuildCompleted(BuildBase, OperationCompleted):
- """bbmake build run completed"""
+ """Event when builds have completed"""
def __init__(self, total, n, p, failures=0, interrupted=0):
if not failures:
OperationCompleted.__init__(self, total, "Building Succeeded")
@@ -441,6 +462,23 @@ class DiskFull(Event):
self._free = freespace
self._mountpoint = mountpoint
+class DiskUsageSample:
+ def __init__(self, available_bytes, free_bytes, total_bytes):
+ # Number of bytes available to non-root processes.
+ self.available_bytes = available_bytes
+ # Number of bytes available to root processes.
+ self.free_bytes = free_bytes
+ # Total capacity of the volume.
+ self.total_bytes = total_bytes
+
+class MonitorDiskEvent(Event):
+ """If BB_DISKMON_DIRS is set, then this event gets triggered each time disk space is checked.
+ Provides information about devices that are getting monitored."""
+ def __init__(self, disk_usage):
+ Event.__init__(self)
+ # hash of device root path -> DiskUsageSample
+ self.disk_usage = disk_usage
+
class NoProvider(Event):
"""No Provider for an Event"""
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/__init__.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/__init__.py
index cd7362c44..b853da30b 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/__init__.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/__init__.py
@@ -35,9 +35,9 @@ import operator
import collections
import subprocess
import pickle
+import errno
import bb.persist_data, bb.utils
import bb.checksum
-from bb import data
import bb.process
__version__ = "2"
@@ -355,7 +355,7 @@ def decodeurl(url):
user, password, parameters).
"""
- m = re.compile('(?P<type>[^:]*)://((?P<user>[^/]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
+ m = re.compile('(?P<type>[^:]*)://((?P<user>[^/;]+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
if not m:
raise MalformedUrl(url)
@@ -491,7 +491,7 @@ def fetcher_init(d):
Calls before this must not hit the cache.
"""
# When to drop SCM head revisions controlled by user policy
- srcrev_policy = d.getVar('BB_SRCREV_POLICY', True) or "clear"
+ srcrev_policy = d.getVar('BB_SRCREV_POLICY') or "clear"
if srcrev_policy == "cache":
logger.debug(1, "Keeping SRCREV cache due to cache policy of: %s", srcrev_policy)
elif srcrev_policy == "clear":
@@ -537,7 +537,11 @@ def fetcher_compare_revisions():
return False
def mirror_from_string(data):
- return [ i.split() for i in (data or "").replace('\\n','\n').split('\n') if i ]
+ mirrors = (data or "").replace('\\n',' ').split()
+ # Split into pairs
+ if len(mirrors) % 2 != 0:
+ bb.warn('Invalid mirror data %s, should have paired members.' % data)
+ return list(zip(*[iter(mirrors)]*2))
def verify_checksum(ud, d, precomputed={}):
"""
@@ -572,7 +576,7 @@ def verify_checksum(ud, d, precomputed={}):
if ud.method.recommends_checksum(ud) and not ud.md5_expected and not ud.sha256_expected:
# If strict checking enabled and neither sum defined, raise error
- strict = d.getVar("BB_STRICT_CHECKSUM", True) or "0"
+ strict = d.getVar("BB_STRICT_CHECKSUM") or "0"
if strict == "1":
logger.error('No checksum specified for %s, please add at least one to the recipe:\n'
'SRC_URI[%s] = "%s"\nSRC_URI[%s] = "%s"' %
@@ -621,7 +625,7 @@ def verify_donestamp(ud, d, origud=None):
Returns True, if the donestamp exists and is valid, False otherwise. When
returning False, any existing done stamps are removed.
"""
- if not ud.needdonestamp:
+ if not ud.needdonestamp or (origud and not origud.needdonestamp):
return True
if not os.path.exists(ud.donestamp):
@@ -718,13 +722,13 @@ def subprocess_setup():
def get_autorev(d):
# only not cache src rev in autorev case
- if d.getVar('BB_SRCREV_POLICY', True) != "cache":
+ if d.getVar('BB_SRCREV_POLICY') != "cache":
d.setVar('BB_DONT_CACHE', '1')
return "AUTOINC"
def get_srcrev(d, method_name='sortable_revision'):
"""
- Return the revsion string, usually for use in the version string (PV) of the current package
+ Return the revision string, usually for use in the version string (PV) of the current package
Most packages usually only have one SCM so we just pass on the call.
In the multi SCM case, we build a value based on SRCREV_FORMAT which must
have been set.
@@ -737,7 +741,7 @@ def get_srcrev(d, method_name='sortable_revision'):
"""
scms = []
- fetcher = Fetch(d.getVar('SRC_URI', True).split(), d)
+ fetcher = Fetch(d.getVar('SRC_URI').split(), d)
urldata = fetcher.ud
for u in urldata:
if urldata[u].method.supports_srcrev():
@@ -757,7 +761,7 @@ def get_srcrev(d, method_name='sortable_revision'):
#
# Mutiple SCMs are in SRC_URI so we resort to SRCREV_FORMAT
#
- format = d.getVar('SRCREV_FORMAT', True)
+ format = d.getVar('SRCREV_FORMAT')
if not format:
raise FetchError("The SRCREV_FORMAT variable must be set when multiple SCMs are used.")
@@ -819,9 +823,18 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
if not cleanup:
cleanup = []
+ # If PATH contains WORKDIR which contains PV which contains SRCPV we
+ # can end up in circular recursion here so give the option of breaking it
+ # in a data store copy.
+ try:
+ d.getVar("PV")
+ except bb.data_smart.ExpansionError:
+ d = bb.data.createCopy(d)
+ d.setVar("PV", "fetcheravoidrecurse")
+
origenv = d.getVar("BB_ORIGENV", False)
for var in exportvars:
- val = d.getVar(var, True) or (origenv and origenv.getVar(var, True))
+ val = d.getVar(var) or (origenv and origenv.getVar(var))
if val:
cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
@@ -856,12 +869,15 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None, log=None, workdir=None):
return output
-def check_network_access(d, info = "", url = None):
+def check_network_access(d, info, url):
"""
- log remote network access, and error if BB_NO_NETWORK is set
+ log remote network access, and error if BB_NO_NETWORK is set or the given
+ URI is untrusted
"""
- if d.getVar("BB_NO_NETWORK", True) == "1":
+ if d.getVar("BB_NO_NETWORK") == "1":
raise NetworkAccess(url, info)
+ elif not trusted_network(d, url):
+ raise UntrustedUrl(url, info)
else:
logger.debug(1, "Fetcher accessed the network with the command %s" % info)
@@ -958,7 +974,7 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
# We may be obtaining a mirror tarball which needs further processing by the real fetcher
# If that tarball is a local file:// we need to provide a symlink to it
- dldir = ld.getVar("DL_DIR", True)
+ dldir = ld.getVar("DL_DIR")
if origud.mirrortarball and os.path.basename(ud.localpath) == os.path.basename(origud.mirrortarball) \
and os.path.basename(ud.localpath) != os.path.basename(origud.localpath):
# Create donestamp in old format to avoid triggering a re-download
@@ -967,7 +983,14 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
open(ud.donestamp, 'w').close()
dest = os.path.join(dldir, os.path.basename(ud.localpath))
if not os.path.exists(dest):
- os.symlink(ud.localpath, dest)
+ # In case this is executing without any file locks held (as is
+ # the case for file:// URLs), two tasks may end up here at the
+ # same time, in which case we do not want the second task to
+ # fail when the link has already been created by the first task.
+ try:
+ os.symlink(ud.localpath, dest)
+ except FileExistsError:
+ pass
if not verify_donestamp(origud, ld) or origud.method.need_update(origud, ld):
origud.method.download(origud, ld)
if hasattr(origud.method,"build_mirror_data"):
@@ -979,13 +1002,23 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
# Broken symbolic link
os.unlink(origud.localpath)
- os.symlink(ud.localpath, origud.localpath)
+ # As per above, in case two tasks end up here simultaneously.
+ try:
+ os.symlink(ud.localpath, origud.localpath)
+ except FileExistsError:
+ pass
update_stamp(origud, ld)
return ud.localpath
except bb.fetch2.NetworkAccess:
raise
+ except IOError as e:
+ if e.errno in [os.errno.ESTALE]:
+ logger.warn("Stale Error Observed %s." % ud.url)
+ return False
+ raise
+
except bb.fetch2.BBFetchException as e:
if isinstance(e, ChecksumError):
logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
@@ -1032,14 +1065,14 @@ def trusted_network(d, url):
BB_ALLOWED_NETWORKS is set globally or for a specific recipe.
Note: modifies SRC_URI & mirrors.
"""
- if d.getVar('BB_NO_NETWORK', True) == "1":
+ if d.getVar('BB_NO_NETWORK') == "1":
return True
pkgname = d.expand(d.getVar('PN', False))
trusted_hosts = d.getVarFlag('BB_ALLOWED_NETWORKS', pkgname, False)
if not trusted_hosts:
- trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS', True)
+ trusted_hosts = d.getVar('BB_ALLOWED_NETWORKS')
# Not enabled.
if not trusted_hosts:
@@ -1071,7 +1104,7 @@ def srcrev_internal_helper(ud, d, name):
"""
srcrev = None
- pn = d.getVar("PN", True)
+ pn = d.getVar("PN")
attempts = []
if name != '' and pn:
attempts.append("SRCREV_%s_pn-%s" % (name, pn))
@@ -1082,7 +1115,7 @@ def srcrev_internal_helper(ud, d, name):
attempts.append("SRCREV")
for a in attempts:
- srcrev = d.getVar(a, True)
+ srcrev = d.getVar(a)
if srcrev and srcrev != "INVALID":
break
@@ -1115,7 +1148,7 @@ def get_checksum_file_list(d):
"""
fetch = Fetch([], d, cache = False, localonly = True)
- dl_dir = d.getVar('DL_DIR', True)
+ dl_dir = d.getVar('DL_DIR')
filelist = []
for u in fetch.urls:
ud = fetch.ud[u]
@@ -1129,9 +1162,9 @@ def get_checksum_file_list(d):
if f.startswith(dl_dir):
# The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else
if os.path.exists(f):
- bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN', True), os.path.basename(f)))
+ bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN'), os.path.basename(f)))
else:
- bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN', True), os.path.basename(f)))
+ bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN'), os.path.basename(f)))
filelist.append(f + ":" + str(os.path.exists(f)))
return " ".join(filelist)
@@ -1160,7 +1193,7 @@ class FetchData(object):
self.mirrortarball = None
self.basename = None
self.basepath = None
- (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(data.expand(url, d))
+ (self.type, self.host, self.path, self.user, self.pswd, self.parm) = decodeurl(d.expand(url))
self.date = self.getSRCDate(d)
self.url = url
if not self.user and "user" in self.parm:
@@ -1177,16 +1210,16 @@ class FetchData(object):
self.sha256_name = "sha256sum"
if self.md5_name in self.parm:
self.md5_expected = self.parm[self.md5_name]
- elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
+ elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
self.md5_expected = None
else:
- self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name, True)
+ self.md5_expected = d.getVarFlag("SRC_URI", self.md5_name)
if self.sha256_name in self.parm:
self.sha256_expected = self.parm[self.sha256_name]
- elif self.type not in ["http", "https", "ftp", "ftps", "sftp"]:
+ elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3"]:
self.sha256_expected = None
else:
- self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name, True)
+ self.sha256_expected = d.getVarFlag("SRC_URI", self.sha256_name)
self.ignore_checksums = False
self.names = self.parm.get("name",'default').split(',')
@@ -1204,7 +1237,7 @@ class FetchData(object):
raise NonLocalMethod()
if self.parm.get("proto", None) and "protocol" not in self.parm:
- logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN', True))
+ logger.warning('Consider updating %s recipe to use "protocol" not "proto" in SRC_URI.', d.getVar('PN'))
self.parm["protocol"] = self.parm.get("proto", None)
if hasattr(self.method, "urldata_init"):
@@ -1217,7 +1250,7 @@ class FetchData(object):
elif self.localfile:
self.localpath = self.method.localpath(self, d)
- dldir = d.getVar("DL_DIR", True)
+ dldir = d.getVar("DL_DIR")
if not self.needdonestamp:
return
@@ -1235,7 +1268,7 @@ class FetchData(object):
self.donestamp = basepath + '.done'
self.lockfile = basepath + '.lock'
- def setup_revisons(self, d):
+ def setup_revisions(self, d):
self.revisions = {}
for name in self.names:
self.revisions[name] = srcrev_internal_helper(self, d, name)
@@ -1257,12 +1290,12 @@ class FetchData(object):
if "srcdate" in self.parm:
return self.parm['srcdate']
- pn = d.getVar("PN", True)
+ pn = d.getVar("PN")
if pn:
- return d.getVar("SRCDATE_%s" % pn, True) or d.getVar("SRCDATE", True) or d.getVar("DATE", True)
+ return d.getVar("SRCDATE_%s" % pn) or d.getVar("SRCDATE") or d.getVar("DATE")
- return d.getVar("SRCDATE", True) or d.getVar("DATE", True)
+ return d.getVar("SRCDATE") or d.getVar("DATE")
class FetchMethod(object):
"""Base class for 'fetch'ing data"""
@@ -1282,7 +1315,7 @@ class FetchMethod(object):
Can also setup variables in urldata for use in go (saving code duplication
and duplicate code execution)
"""
- return os.path.join(data.getVar("DL_DIR", d, True), urldata.localfile)
+ return os.path.join(d.getVar("DL_DIR"), urldata.localfile)
def supports_checksum(self, urldata):
"""
@@ -1382,6 +1415,10 @@ class FetchMethod(object):
cmd = 'lzip -dc %s | tar x --no-same-owner -f -' % file
elif file.endswith('.lz'):
cmd = 'lzip -dc %s > %s' % (file, efile)
+ elif file.endswith('.tar.7z'):
+ cmd = '7z x -so %s | tar x --no-same-owner -f -' % file
+ elif file.endswith('.7z'):
+ cmd = '7za x -y %s 1>/dev/null' % file
elif file.endswith('.zip') or file.endswith('.jar'):
try:
dos = bb.utils.to_boolean(urldata.parm.get('dos'), False)
@@ -1413,10 +1450,6 @@ class FetchMethod(object):
else:
raise UnpackError("Unable to unpack deb/ipk package - could not list contents", urldata.url)
cmd = 'ar x %s %s && tar --no-same-owner -xpf %s && rm %s' % (file, datafile, datafile, datafile)
- elif file.endswith('.tar.7z'):
- cmd = '7z x -so %s | tar xf - ' % file
- elif file.endswith('.7z'):
- cmd = '7za x -y %s 1>/dev/null' % file
# If 'subdir' param exists, create a dir and use it as destination for unpack cmd
if 'subdir' in urldata.parm:
@@ -1450,7 +1483,7 @@ class FetchMethod(object):
if not cmd:
return
- path = data.getVar('PATH', True)
+ path = data.getVar('PATH')
if path:
cmd = "PATH=\"%s\" %s" % (path, cmd)
bb.note("Unpacking %s to %s/" % (file, unpackdir))
@@ -1507,7 +1540,7 @@ class FetchMethod(object):
def generate_revision_key(self, ud, d, name):
key = self._revision_key(ud, d, name)
- return "%s-%s" % (key, d.getVar("PN", True) or "")
+ return "%s-%s" % (key, d.getVar("PN") or "")
class Fetch(object):
def __init__(self, urls, d, cache = True, localonly = False, connection_cache = None):
@@ -1515,14 +1548,14 @@ class Fetch(object):
raise Exception("bb.fetch2.Fetch.__init__: cannot set cache and localonly at same time")
if len(urls) == 0:
- urls = d.getVar("SRC_URI", True).split()
+ urls = d.getVar("SRC_URI").split()
self.urls = urls
self.d = d
self.ud = {}
self.connection_cache = connection_cache
- fn = d.getVar('FILE', True)
- mc = d.getVar('__BBMULTICONFIG', True) or ""
+ fn = d.getVar('FILE')
+ mc = d.getVar('__BBMULTICONFIG') or ""
if cache and fn and mc + fn in urldata_cache:
self.ud = urldata_cache[mc + fn]
@@ -1565,8 +1598,8 @@ class Fetch(object):
if not urls:
urls = self.urls
- network = self.d.getVar("BB_NO_NETWORK", True)
- premirroronly = (self.d.getVar("BB_FETCH_PREMIRRORONLY", True) == "1")
+ network = self.d.getVar("BB_NO_NETWORK")
+ premirroronly = (self.d.getVar("BB_FETCH_PREMIRRORONLY") == "1")
for u in urls:
ud = self.ud[u]
@@ -1584,8 +1617,17 @@ class Fetch(object):
localpath = ud.localpath
elif m.try_premirror(ud, self.d):
logger.debug(1, "Trying PREMIRRORS")
- mirrors = mirror_from_string(self.d.getVar('PREMIRRORS', True))
+ mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
localpath = try_mirrors(self, self.d, ud, mirrors, False)
+ if localpath:
+ try:
+ # early checksum verification so that if the checksum of the premirror
+ # contents mismatch the fetcher can still try upstream and mirrors
+ update_stamp(ud, self.d)
+ except ChecksumError as e:
+ logger.warning("Checksum failure encountered with premirror download of %s - will attempt other sources." % u)
+ logger.debug(1, str(e))
+ localpath = ""
if premirroronly:
self.d.setVar("BB_NO_NETWORK", "1")
@@ -1624,7 +1666,7 @@ class Fetch(object):
if not verified_stamp:
m.clean(ud, self.d)
logger.debug(1, "Trying MIRRORS")
- mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
+ mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
localpath = try_mirrors(self, self.d, ud, mirrors)
if not localpath or ((not os.path.exists(localpath)) and localpath.find("*") == -1):
@@ -1634,6 +1676,11 @@ class Fetch(object):
update_stamp(ud, self.d)
+ except IOError as e:
+ if e.errno in [os.errno.ESTALE]:
+ logger.error("Stale Error Observed %s." % u)
+ raise ChecksumError("Stale Error Detected")
+
except BBFetchException as e:
if isinstance(e, ChecksumError):
logger.error("Checksum failure fetching %s" % u)
@@ -1657,7 +1704,7 @@ class Fetch(object):
m = ud.method
logger.debug(1, "Testing URL %s", u)
# First try checking uri, u, from PREMIRRORS
- mirrors = mirror_from_string(self.d.getVar('PREMIRRORS', True))
+ mirrors = mirror_from_string(self.d.getVar('PREMIRRORS'))
ret = try_mirrors(self, self.d, ud, mirrors, True)
if not ret:
# Next try checking from the original uri, u
@@ -1665,7 +1712,7 @@ class Fetch(object):
ret = m.checkstatus(self, ud, self.d)
except:
# Finally, try checking uri, u, from MIRRORS
- mirrors = mirror_from_string(self.d.getVar('MIRRORS', True))
+ mirrors = mirror_from_string(self.d.getVar('MIRRORS'))
ret = try_mirrors(self, self.d, ud, mirrors, True)
if not ret:
@@ -1763,6 +1810,7 @@ from . import svn
from . import wget
from . import ssh
from . import sftp
+from . import s3
from . import perforce
from . import bzr
from . import hg
@@ -1780,6 +1828,7 @@ methods.append(gitannex.GitANNEX())
methods.append(cvs.Cvs())
methods.append(ssh.SSH())
methods.append(sftp.SFTP())
+methods.append(s3.S3())
methods.append(perforce.Perforce())
methods.append(bzr.Bzr())
methods.append(hg.Hg())
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/bzr.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/bzr.py
index 72264afb5..16123f8af 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/bzr.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/bzr.py
@@ -27,7 +27,6 @@ import os
import sys
import logging
import bb
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import runfetchcmd
@@ -43,14 +42,14 @@ class Bzr(FetchMethod):
"""
# Create paths to bzr checkouts
relpath = self._strip_leading_slashes(ud.path)
- ud.pkgdir = os.path.join(data.expand('${BZRDIR}', d), ud.host, relpath)
+ ud.pkgdir = os.path.join(d.expand('${BZRDIR}'), ud.host, relpath)
- ud.setup_revisons(d)
+ ud.setup_revisions(d)
if not ud.revision:
ud.revision = self.latest_revision(ud, d)
- ud.localfile = data.expand('bzr_%s_%s_%s.tar.gz' % (ud.host, ud.path.replace('/', '.'), ud.revision), d)
+ ud.localfile = d.expand('bzr_%s_%s_%s.tar.gz' % (ud.host, ud.path.replace('/', '.'), ud.revision))
def _buildbzrcommand(self, ud, d, command):
"""
@@ -58,7 +57,7 @@ class Bzr(FetchMethod):
command is "fetch", "update", "revno"
"""
- basecmd = data.expand('${FETCHCMD_bzr}', d)
+ basecmd = d.expand('${FETCHCMD_bzr}')
proto = ud.parm.get('protocol', 'http')
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/clearcase.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/clearcase.py
index 70e280a8d..36beab6a5 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/clearcase.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/clearcase.py
@@ -65,7 +65,6 @@ import os
import sys
import shutil
import bb
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import runfetchcmd
@@ -108,13 +107,13 @@ class ClearCase(FetchMethod):
else:
ud.module = ""
- ud.basecmd = d.getVar("FETCHCMD_ccrc", True) or spawn.find_executable("cleartool") or spawn.find_executable("rcleartool")
+ ud.basecmd = d.getVar("FETCHCMD_ccrc") or spawn.find_executable("cleartool") or spawn.find_executable("rcleartool")
- if data.getVar("SRCREV", d, True) == "INVALID":
+ if d.getVar("SRCREV") == "INVALID":
raise FetchError("Set a valid SRCREV for the clearcase fetcher in your recipe, e.g. SRCREV = \"/main/LATEST\" or any other label of your choice.")
ud.label = d.getVar("SRCREV", False)
- ud.customspec = d.getVar("CCASE_CUSTOM_CONFIG_SPEC", True)
+ ud.customspec = d.getVar("CCASE_CUSTOM_CONFIG_SPEC")
ud.server = "%s://%s%s" % (ud.proto, ud.host, ud.path)
@@ -124,7 +123,7 @@ class ClearCase(FetchMethod):
ud.viewname = "%s-view%s" % (ud.identifier, d.getVar("DATETIME", d, True))
ud.csname = "%s-config-spec" % (ud.identifier)
- ud.ccasedir = os.path.join(data.getVar("DL_DIR", d, True), ud.type)
+ ud.ccasedir = os.path.join(d.getVar("DL_DIR"), ud.type)
ud.viewdir = os.path.join(ud.ccasedir, ud.viewname)
ud.configspecfile = os.path.join(ud.ccasedir, ud.csname)
ud.localfile = "%s.tar.gz" % (ud.identifier)
@@ -144,7 +143,7 @@ class ClearCase(FetchMethod):
self.debug("configspecfile = %s" % ud.configspecfile)
self.debug("localfile = %s" % ud.localfile)
- ud.localfile = os.path.join(data.getVar("DL_DIR", d, True), ud.localfile)
+ ud.localfile = os.path.join(d.getVar("DL_DIR"), ud.localfile)
def _build_ccase_command(self, ud, command):
"""
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/cvs.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/cvs.py
index 5ff70ba92..490c95471 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/cvs.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/cvs.py
@@ -63,7 +63,7 @@ class Cvs(FetchMethod):
if 'fullpath' in ud.parm:
fullpath = '_fullpath'
- ud.localfile = bb.data.expand('%s_%s_%s_%s%s%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.tag, ud.date, norecurse, fullpath), d)
+ ud.localfile = d.expand('%s_%s_%s_%s%s%s.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.tag, ud.date, norecurse, fullpath))
def need_update(self, ud, d):
if (ud.date == "now"):
@@ -87,10 +87,10 @@ class Cvs(FetchMethod):
cvsroot = ud.path
else:
cvsroot = ":" + method
- cvsproxyhost = d.getVar('CVS_PROXY_HOST', True)
+ cvsproxyhost = d.getVar('CVS_PROXY_HOST')
if cvsproxyhost:
cvsroot += ";proxy=" + cvsproxyhost
- cvsproxyport = d.getVar('CVS_PROXY_PORT', True)
+ cvsproxyport = d.getVar('CVS_PROXY_PORT')
if cvsproxyport:
cvsroot += ";proxyport=" + cvsproxyport
cvsroot += ":" + ud.user
@@ -110,7 +110,7 @@ class Cvs(FetchMethod):
if ud.tag:
options.append("-r %s" % ud.tag)
- cvsbasecmd = d.getVar("FETCHCMD_cvs", True)
+ cvsbasecmd = d.getVar("FETCHCMD_cvs")
cvscmd = cvsbasecmd + " '-d" + cvsroot + "' co " + " ".join(options) + " " + ud.module
cvsupdatecmd = cvsbasecmd + " '-d" + cvsroot + "' update -d -P " + " ".join(options)
@@ -120,8 +120,8 @@ class Cvs(FetchMethod):
# create module directory
logger.debug(2, "Fetch: checking for module directory")
- pkg = d.getVar('PN', True)
- pkgdir = os.path.join(d.getVar('CVSDIR', True), pkg)
+ pkg = d.getVar('PN')
+ pkgdir = os.path.join(d.getVar('CVSDIR'), pkg)
moddir = os.path.join(pkgdir, localdir)
workdir = None
if os.access(os.path.join(moddir, 'CVS'), os.R_OK):
@@ -164,8 +164,8 @@ class Cvs(FetchMethod):
def clean(self, ud, d):
""" Clean CVS Files and tarballs """
- pkg = d.getVar('PN', True)
- pkgdir = os.path.join(d.getVar("CVSDIR", True), pkg)
+ pkg = d.getVar('PN')
+ pkgdir = os.path.join(d.getVar("CVSDIR"), pkg)
bb.utils.remove(pkgdir, True)
bb.utils.remove(ud.localpath)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/git.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/git.py
index 792c18376..7442f8441 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/git.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/git.py
@@ -50,7 +50,7 @@ Supported SRC_URI options are:
The default is "0", set nobranch=1 if needed.
- usehead
- For local git:// urls to use the current branch HEAD as the revsion for use with
+ For local git:// urls to use the current branch HEAD as the revision for use with
AUTOREV. Implies nobranch.
"""
@@ -76,7 +76,6 @@ import re
import bb
import errno
import bb.progress
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import runfetchcmd
from bb.fetch2 import logger
@@ -174,19 +173,19 @@ class Git(FetchMethod):
if len(branches) != len(ud.names):
raise bb.fetch2.ParameterError("The number of name and branch parameters is not balanced", ud.url)
ud.branches = {}
- for name in ud.names:
- branch = branches[ud.names.index(name)]
+ for pos, name in enumerate(ud.names):
+ branch = branches[pos]
ud.branches[name] = branch
ud.unresolvedrev[name] = branch
if ud.usehead:
ud.unresolvedrev['default'] = 'HEAD'
- ud.basecmd = data.getVar("FETCHCMD_git", d, True) or "git -c core.fsyncobjectfiles=0"
+ ud.basecmd = d.getVar("FETCHCMD_git") or "git -c core.fsyncobjectfiles=0"
- ud.write_tarballs = ((data.getVar("BB_GENERATE_MIRROR_TARBALLS", d, True) or "0") != "0") or ud.rebaseable
+ ud.write_tarballs = ((d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0") != "0") or ud.rebaseable
- ud.setup_revisons(d)
+ ud.setup_revisions(d)
for name in ud.names:
# Ensure anything that doesn't look like a sha256 checksum/revision is translated into one
@@ -206,9 +205,9 @@ class Git(FetchMethod):
if ud.rebaseable:
for name in ud.names:
gitsrcname = gitsrcname + '_' + ud.revisions[name]
- ud.mirrortarball = 'git2_%s.tar.gz' % (gitsrcname)
- ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
- gitdir = d.getVar("GITDIR", True) or (d.getVar("DL_DIR", True) + "/git2/")
+ ud.mirrortarball = 'git2_%s.tar.gz' % gitsrcname
+ ud.fullmirror = os.path.join(d.getVar("DL_DIR"), ud.mirrortarball)
+ gitdir = d.getVar("GITDIR") or (d.getVar("DL_DIR") + "/git2/")
ud.clonedir = os.path.join(gitdir, gitsrcname)
ud.localfile = ud.clonedir
@@ -229,7 +228,7 @@ class Git(FetchMethod):
def try_premirror(self, ud, d):
# If we don't do this, updating an existing checkout with only premirrors
# is not possible
- if d.getVar("BB_FETCH_PREMIRRORONLY", True) is not None:
+ if d.getVar("BB_FETCH_PREMIRRORONLY") is not None:
return True
if os.path.exists(ud.clonedir):
return False
@@ -241,7 +240,7 @@ class Git(FetchMethod):
# If the checkout doesn't exist and the mirror tarball does, extract it
if not os.path.exists(ud.clonedir) and os.path.exists(ud.fullmirror):
bb.utils.mkdirhier(ud.clonedir)
- runfetchcmd("tar -xzf %s" % (ud.fullmirror), d, workdir=ud.clonedir)
+ runfetchcmd("tar -xzf %s" % ud.fullmirror, d, workdir=ud.clonedir)
repourl = self._get_repo_url(ud)
@@ -252,7 +251,7 @@ class Git(FetchMethod):
repourl = repourl[7:]
clone_cmd = "LANG=C %s clone --bare --mirror %s %s --progress" % (ud.basecmd, repourl, ud.clonedir)
if ud.proto.lower() != 'file':
- bb.fetch2.check_network_access(d, clone_cmd)
+ bb.fetch2.check_network_access(d, clone_cmd, ud.url)
progresshandler = GitProgressHandler(d)
runfetchcmd(clone_cmd, d, log=progresshandler)
@@ -292,15 +291,15 @@ class Git(FetchMethod):
os.unlink(ud.fullmirror)
logger.info("Creating tarball of git repository")
- runfetchcmd("tar -czf %s %s" % (ud.fullmirror, os.path.join(".") ), d, workdir=ud.clonedir)
- runfetchcmd("touch %s.done" % (ud.fullmirror), d, workdir=ud.clonedir)
+ runfetchcmd("tar -czf %s ." % ud.fullmirror, d, workdir=ud.clonedir)
+ runfetchcmd("touch %s.done" % ud.fullmirror, d)
def unpack(self, ud, destdir, d):
""" unpack the downloaded src to destdir"""
subdir = ud.parm.get("subpath", "")
if subdir != "":
- readpathspec = ":%s" % (subdir)
+ readpathspec = ":%s" % subdir
def_destsuffix = "%s/" % os.path.basename(subdir.rstrip('/'))
else:
readpathspec = ""
@@ -380,14 +379,26 @@ class Git(FetchMethod):
"""
Run git ls-remote with the specified search string
"""
- repourl = self._get_repo_url(ud)
- cmd = "%s ls-remote %s %s" % \
- (ud.basecmd, repourl, search)
- if ud.proto.lower() != 'file':
- bb.fetch2.check_network_access(d, cmd)
- output = runfetchcmd(cmd, d, True)
- if not output:
- raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, ud.url)
+ # Prevent recursion e.g. in OE if SRCPV is in PV, PV is in WORKDIR,
+ # and WORKDIR is in PATH (as a result of RSS), our call to
+ # runfetchcmd() exports PATH so this function will get called again (!)
+ # In this scenario the return call of the function isn't actually
+ # important - WORKDIR isn't needed in PATH to call git ls-remote
+ # anyway.
+ if d.getVar('_BB_GIT_IN_LSREMOTE', False):
+ return ''
+ d.setVar('_BB_GIT_IN_LSREMOTE', '1')
+ try:
+ repourl = self._get_repo_url(ud)
+ cmd = "%s ls-remote %s %s" % \
+ (ud.basecmd, repourl, search)
+ if ud.proto.lower() != 'file':
+ bb.fetch2.check_network_access(d, cmd, repourl)
+ output = runfetchcmd(cmd, d, True)
+ if not output:
+ raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, ud.url)
+ finally:
+ d.delVar('_BB_GIT_IN_LSREMOTE')
return output
def _latest_revision(self, ud, d, name):
@@ -418,7 +429,7 @@ class Git(FetchMethod):
"""
pupver = ('', '')
- tagregex = re.compile(d.getVar('UPSTREAM_CHECK_GITTAGREGEX', True) or "(?P<pver>([0-9][\.|_]?)+)")
+ tagregex = re.compile(d.getVar('UPSTREAM_CHECK_GITTAGREGEX') or "(?P<pver>([0-9][\.|_]?)+)")
try:
output = self._lsremote(ud, d, "refs/tags/*")
except bb.fetch2.FetchError or bb.fetch2.NetworkAccess:
@@ -470,7 +481,7 @@ class Git(FetchMethod):
if not os.path.exists(rev_file) or not os.path.getsize(rev_file):
from pipes import quote
commits = bb.fetch2.runfetchcmd(
- "git rev-list %s -- | wc -l" % (quote(rev)),
+ "git rev-list %s -- | wc -l" % quote(rev),
d, quiet=True).strip().lstrip('0')
if commits:
open(rev_file, "w").write("%d\n" % int(commits))
@@ -485,5 +496,5 @@ class Git(FetchMethod):
try:
self._lsremote(ud, d, "")
return True
- except FetchError:
+ except bb.fetch2.FetchError:
return False
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitannex.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitannex.py
index 4937a1089..c66c21142 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitannex.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitannex.py
@@ -22,7 +22,6 @@ BitBake 'Fetch' git annex implementation
import os
import bb
-from bb import data
from bb.fetch2.git import Git
from bb.fetch2 import runfetchcmd
from bb.fetch2 import logger
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitsm.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitsm.py
index 661376204..a95584c82 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitsm.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/gitsm.py
@@ -31,7 +31,6 @@ NOTE: Switching a SRC_URI from "git://" to "gitsm://" requires a clean of your r
import os
import bb
-from bb import data
from bb.fetch2.git import Git
from bb.fetch2 import runfetchcmd
from bb.fetch2 import logger
@@ -108,7 +107,7 @@ class GitSM(Git):
os.rename(ud.clonedir, gitdir)
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*true/bare = false/'", d)
runfetchcmd(ud.basecmd + " reset --hard", d, workdir=tmpclonedir)
- runfetchcmd(ud.basecmd + " checkout " + ud.revisions[ud.names[0]], d, workdir=tmpclonedir)
+ runfetchcmd(ud.basecmd + " checkout -f " + ud.revisions[ud.names[0]], d, workdir=tmpclonedir)
runfetchcmd(ud.basecmd + " submodule update --init --recursive", d, workdir=tmpclonedir)
self._set_relative_paths(tmpclonedir)
runfetchcmd("sed " + gitdir + "/config -i -e 's/bare.*=.*false/bare = true/'", d, workdir=tmpclonedir)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/hg.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/hg.py
index 20df8016d..b5f268601 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/hg.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/hg.py
@@ -29,7 +29,6 @@ import sys
import logging
import bb
import errno
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import MissingParameterError
@@ -67,7 +66,7 @@ class Hg(FetchMethod):
else:
ud.proto = "hg"
- ud.setup_revisons(d)
+ ud.setup_revisions(d)
if 'rev' in ud.parm:
ud.revision = ud.parm['rev']
@@ -78,15 +77,15 @@ class Hg(FetchMethod):
hgsrcname = '%s_%s_%s' % (ud.module.replace('/', '.'), \
ud.host, ud.path.replace('/', '.'))
ud.mirrortarball = 'hg_%s.tar.gz' % hgsrcname
- ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
+ ud.fullmirror = os.path.join(d.getVar("DL_DIR"), ud.mirrortarball)
- hgdir = d.getVar("HGDIR", True) or (d.getVar("DL_DIR", True) + "/hg/")
+ hgdir = d.getVar("HGDIR") or (d.getVar("DL_DIR") + "/hg/")
ud.pkgdir = os.path.join(hgdir, hgsrcname)
ud.moddir = os.path.join(ud.pkgdir, ud.module)
ud.localfile = ud.moddir
- ud.basecmd = data.getVar("FETCHCMD_hg", d, True) or "/usr/bin/env hg"
+ ud.basecmd = d.getVar("FETCHCMD_hg") or "/usr/bin/env hg"
- ud.write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS", True)
+ ud.write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS")
def need_update(self, ud, d):
revTag = ud.parm.get('rev', 'tip')
@@ -99,7 +98,7 @@ class Hg(FetchMethod):
def try_premirror(self, ud, d):
# If we don't do this, updating an existing checkout with only premirrors
# is not possible
- if d.getVar("BB_FETCH_PREMIRRORONLY", True) is not None:
+ if d.getVar("BB_FETCH_PREMIRRORONLY") is not None:
return True
if os.path.exists(ud.moddir):
return False
@@ -221,7 +220,7 @@ class Hg(FetchMethod):
"""
Compute tip revision for the url
"""
- bb.fetch2.check_network_access(d, self._buildhgcommand(ud, d, "info"))
+ bb.fetch2.check_network_access(d, self._buildhgcommand(ud, d, "info"), ud.url)
output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d)
return output.strip()
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/local.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/local.py
index 51ca78d12..a114ac12e 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/local.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/local.py
@@ -29,7 +29,6 @@ import os
import urllib.request, urllib.parse, urllib.error
import bb
import bb.utils
-from bb import data
from bb.fetch2 import FetchMethod, FetchError
from bb.fetch2 import logger
@@ -63,17 +62,11 @@ class Local(FetchMethod):
newpath = path
if path[0] == "/":
return [path]
- filespath = data.getVar('FILESPATH', d, True)
+ filespath = d.getVar('FILESPATH')
if filespath:
logger.debug(2, "Searching for %s in paths:\n %s" % (path, "\n ".join(filespath.split(":"))))
newpath, hist = bb.utils.which(filespath, path, history=True)
searched.extend(hist)
- if not newpath:
- filesdir = data.getVar('FILESDIR', d, True)
- if filesdir:
- logger.debug(2, "Searching for %s in path: %s" % (path, filesdir))
- newpath = os.path.join(filesdir, path)
- searched.append(newpath)
if (not newpath or not os.path.exists(newpath)) and path.find("*") != -1:
# For expressions using '*', best we can do is take the first directory in FILESPATH that exists
newpath, hist = bb.utils.which(filespath, ".", history=True)
@@ -81,7 +74,7 @@ class Local(FetchMethod):
logger.debug(2, "Searching for %s in path: %s" % (path, newpath))
return searched
if not os.path.exists(newpath):
- dldirfile = os.path.join(d.getVar("DL_DIR", True), path)
+ dldirfile = os.path.join(d.getVar("DL_DIR"), path)
logger.debug(2, "Defaulting to %s for %s" % (dldirfile, path))
bb.utils.mkdirhier(os.path.dirname(dldirfile))
searched.append(dldirfile)
@@ -100,13 +93,10 @@ class Local(FetchMethod):
# no need to fetch local files, we'll deal with them in place.
if self.supports_checksum(urldata) and not os.path.exists(urldata.localpath):
locations = []
- filespath = data.getVar('FILESPATH', d, True)
+ filespath = d.getVar('FILESPATH')
if filespath:
locations = filespath.split(":")
- filesdir = data.getVar('FILESDIR', d, True)
- if filesdir:
- locations.append(filesdir)
- locations.append(d.getVar("DL_DIR", True))
+ locations.append(d.getVar("DL_DIR"))
msg = "Unable to find file " + urldata.url + " anywhere. The paths that were searched were:\n " + "\n ".join(locations)
raise FetchError(msg)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/npm.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/npm.py
index 699ae72e0..73a75fe98 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/npm.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/npm.py
@@ -25,7 +25,6 @@ import json
import subprocess
import signal
import bb
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import ChecksumError
@@ -80,6 +79,7 @@ class Npm(FetchMethod):
if not ud.version:
raise ParameterError("NPM fetcher requires a version parameter", ud.url)
ud.bbnpmmanifest = "%s-%s.deps.json" % (ud.pkgname, ud.version)
+ ud.bbnpmmanifest = ud.bbnpmmanifest.replace('/', '-')
ud.registry = "http://%s" % (ud.url.replace('npm://', '', 1).split(';'))[0]
prefixdir = "npm/%s" % ud.pkgname
ud.pkgdatadir = d.expand("${DL_DIR}/%s" % prefixdir)
@@ -87,12 +87,13 @@ class Npm(FetchMethod):
bb.utils.mkdirhier(ud.pkgdatadir)
ud.localpath = d.expand("${DL_DIR}/npm/%s" % ud.bbnpmmanifest)
- self.basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -O -t 2 -T 30 -nv --passive-ftp --no-check-certificate "
+ self.basecmd = d.getVar("FETCHCMD_wget") or "/usr/bin/env wget -O -t 2 -T 30 -nv --passive-ftp --no-check-certificate "
ud.prefixdir = prefixdir
- ud.write_tarballs = ((data.getVar("BB_GENERATE_MIRROR_TARBALLS", d, True) or "0") != "0")
+ ud.write_tarballs = ((d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0") != "0")
ud.mirrortarball = 'npm_%s-%s.tar.xz' % (ud.pkgname, ud.version)
- ud.fullmirror = os.path.join(d.getVar("DL_DIR", True), ud.mirrortarball)
+ ud.mirrortarball = ud.mirrortarball.replace('/', '-')
+ ud.fullmirror = os.path.join(d.getVar("DL_DIR"), ud.mirrortarball)
def need_update(self, ud, d):
if os.path.exists(ud.localpath):
@@ -101,8 +102,8 @@ class Npm(FetchMethod):
def _runwget(self, ud, d, command, quiet):
logger.debug(2, "Fetching %s using command '%s'" % (ud.url, command))
- bb.fetch2.check_network_access(d, command)
- dldir = d.getVar("DL_DIR", True)
+ bb.fetch2.check_network_access(d, command, ud.url)
+ dldir = d.getVar("DL_DIR")
runfetchcmd(command, d, quiet, workdir=dldir)
def _unpackdep(self, ud, pkg, data, destdir, dldir, d):
@@ -116,7 +117,7 @@ class Npm(FetchMethod):
# Change to subdir before executing command
if not os.path.exists(destdir):
os.makedirs(destdir)
- path = d.getVar('PATH', True)
+ path = d.getVar('PATH')
if path:
cmd = "PATH=\"%s\" %s" % (path, cmd)
bb.note("Unpacking %s to %s/" % (file, destdir))
@@ -132,9 +133,8 @@ class Npm(FetchMethod):
def unpack(self, ud, destdir, d):
- dldir = d.getVar("DL_DIR", True)
- depdumpfile = "%s-%s.deps.json" % (ud.pkgname, ud.version)
- with open("%s/npm/%s" % (dldir, depdumpfile)) as datafile:
+ dldir = d.getVar("DL_DIR")
+ with open("%s/npm/%s" % (dldir, ud.bbnpmmanifest)) as datafile:
workobj = json.load(datafile)
dldir = "%s/%s" % (os.path.dirname(ud.localpath), ud.pkgname)
@@ -182,7 +182,12 @@ class Npm(FetchMethod):
if pkg_os:
if not isinstance(pkg_os, list):
pkg_os = [pkg_os]
- if 'linux' not in pkg_os or '!linux' in pkg_os:
+ blacklist = False
+ for item in pkg_os:
+ if item.startswith('!'):
+ blacklist = True
+ break
+ if (not blacklist and 'linux' not in pkg_os) or '!linux' in pkg_os:
logger.debug(2, "Skipping %s since it's incompatible with Linux" % pkg)
return
#logger.debug(2, "Output URL is %s - %s - %s" % (ud.basepath, ud.basename, ud.localfile))
@@ -195,6 +200,7 @@ class Npm(FetchMethod):
dependencies = pdata.get('dependencies', {})
optionalDependencies = pdata.get('optionalDependencies', {})
+ dependencies.update(optionalDependencies)
depsfound = {}
optdepsfound = {}
data[pkg]['deps'] = {}
@@ -251,24 +257,30 @@ class Npm(FetchMethod):
lockdown = {}
if not os.listdir(ud.pkgdatadir) and os.path.exists(ud.fullmirror):
- dest = d.getVar("DL_DIR", True)
+ dest = d.getVar("DL_DIR")
bb.utils.mkdirhier(dest)
runfetchcmd("tar -xJf %s" % (ud.fullmirror), d, workdir=dest)
return
- shwrf = d.getVar('NPM_SHRINKWRAP', True)
+ shwrf = d.getVar('NPM_SHRINKWRAP')
logger.debug(2, "NPM shrinkwrap file is %s" % shwrf)
- try:
- with open(shwrf) as datafile:
- shrinkobj = json.load(datafile)
- except:
+ if shwrf:
+ try:
+ with open(shwrf) as datafile:
+ shrinkobj = json.load(datafile)
+ except Exception as e:
+ raise FetchError('Error loading NPM_SHRINKWRAP file "%s" for %s: %s' % (shwrf, ud.pkgname, str(e)))
+ elif not ud.ignore_checksums:
logger.warning('Missing shrinkwrap file in NPM_SHRINKWRAP for %s, this will lead to unreliable builds!' % ud.pkgname)
- lckdf = d.getVar('NPM_LOCKDOWN', True)
+ lckdf = d.getVar('NPM_LOCKDOWN')
logger.debug(2, "NPM lockdown file is %s" % lckdf)
- try:
- with open(lckdf) as datafile:
- lockdown = json.load(datafile)
- except:
+ if lckdf:
+ try:
+ with open(lckdf) as datafile:
+ lockdown = json.load(datafile)
+ except Exception as e:
+ raise FetchError('Error loading NPM_LOCKDOWN file "%s" for %s: %s' % (lckdf, ud.pkgname, str(e)))
+ elif not ud.ignore_checksums:
logger.warning('Missing lockdown file in NPM_LOCKDOWN for %s, this will lead to unreproducible builds!' % ud.pkgname)
if ('name' not in shrinkobj):
@@ -286,7 +298,7 @@ class Npm(FetchMethod):
if os.path.islink(ud.fullmirror):
os.unlink(ud.fullmirror)
- dldir = d.getVar("DL_DIR", True)
+ dldir = d.getVar("DL_DIR")
logger.info("Creating tarball of npm data")
runfetchcmd("tar -cJf %s npm/%s npm/%s" % (ud.fullmirror, ud.bbnpmmanifest, ud.pkgname), d,
workdir=dldir)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/osc.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/osc.py
index 295abf953..2b4f7d9c1 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/osc.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/osc.py
@@ -10,7 +10,6 @@ import os
import sys
import logging
import bb
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import MissingParameterError
@@ -34,7 +33,7 @@ class Osc(FetchMethod):
# Create paths to osc checkouts
relpath = self._strip_leading_slashes(ud.path)
- ud.pkgdir = os.path.join(d.getVar('OSCDIR', True), ud.host)
+ ud.pkgdir = os.path.join(d.getVar('OSCDIR'), ud.host)
ud.moddir = os.path.join(ud.pkgdir, relpath, ud.module)
if 'rev' in ud.parm:
@@ -47,7 +46,7 @@ class Osc(FetchMethod):
else:
ud.revision = ""
- ud.localfile = data.expand('%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.path.replace('/', '.'), ud.revision), d)
+ ud.localfile = d.expand('%s_%s_%s.tar.gz' % (ud.module.replace('/', '.'), ud.path.replace('/', '.'), ud.revision))
def _buildosccommand(self, ud, d, command):
"""
@@ -55,7 +54,7 @@ class Osc(FetchMethod):
command is "fetch", "update", "info"
"""
- basecmd = data.expand('${FETCHCMD_osc}', d)
+ basecmd = d.expand('${FETCHCMD_osc}')
proto = ud.parm.get('protocol', 'ocs')
@@ -84,7 +83,7 @@ class Osc(FetchMethod):
logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'")
- if os.access(os.path.join(d.getVar('OSCDIR', True), ud.path, ud.module), os.R_OK):
+ if os.access(os.path.join(d.getVar('OSCDIR'), ud.path, ud.module), os.R_OK):
oscupdatecmd = self._buildosccommand(ud, d, "update")
logger.info("Update "+ ud.url)
# update sources there
@@ -112,7 +111,7 @@ class Osc(FetchMethod):
Generate a .oscrc to be used for this run.
"""
- config_path = os.path.join(d.getVar('OSCDIR', True), "oscrc")
+ config_path = os.path.join(d.getVar('OSCDIR'), "oscrc")
if (os.path.exists(config_path)):
os.remove(config_path)
@@ -121,8 +120,8 @@ class Osc(FetchMethod):
f.write("apisrv = %s\n" % ud.host)
f.write("scheme = http\n")
f.write("su-wrapper = su -c\n")
- f.write("build-root = %s\n" % d.getVar('WORKDIR', True))
- f.write("urllist = %s\n" % d.getVar("OSCURLLIST", True))
+ f.write("build-root = %s\n" % d.getVar('WORKDIR'))
+ f.write("urllist = %s\n" % d.getVar("OSCURLLIST"))
f.write("extra-pkgs = gzip\n")
f.write("\n")
f.write("[%s]\n" % ud.host)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/perforce.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/perforce.py
index 50cb47909..3debad59f 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/perforce.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/perforce.py
@@ -26,7 +26,6 @@ BitBake 'Fetch' implementation for perforce
import os
import logging
import bb
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import logger
@@ -44,13 +43,13 @@ class Perforce(FetchMethod):
provided by the env, use it. If P4PORT is specified by the recipe, use
its values, which may override the settings in P4CONFIG.
"""
- ud.basecmd = d.getVar('FETCHCMD_p4', True)
+ ud.basecmd = d.getVar('FETCHCMD_p4')
if not ud.basecmd:
ud.basecmd = "/usr/bin/env p4"
- ud.dldir = d.getVar('P4DIR', True)
+ ud.dldir = d.getVar('P4DIR')
if not ud.dldir:
- ud.dldir = '%s/%s' % (d.getVar('DL_DIR', True), 'p4')
+ ud.dldir = '%s/%s' % (d.getVar('DL_DIR'), 'p4')
path = ud.url.split('://')[1]
path = path.split(';')[0]
@@ -62,7 +61,7 @@ class Perforce(FetchMethod):
ud.path = path
ud.usingp4config = False
- p4port = d.getVar('P4PORT', True)
+ p4port = d.getVar('P4PORT')
if p4port:
logger.debug(1, 'Using recipe provided P4PORT: %s' % p4port)
@@ -71,7 +70,7 @@ class Perforce(FetchMethod):
logger.debug(1, 'Trying to use P4CONFIG to automatically set P4PORT...')
ud.usingp4config = True
p4cmd = '%s info | grep "Server address"' % ud.basecmd
- bb.fetch2.check_network_access(d, p4cmd)
+ bb.fetch2.check_network_access(d, p4cmd, ud.url)
ud.host = runfetchcmd(p4cmd, d, True)
ud.host = ud.host.split(': ')[1].strip()
logger.debug(1, 'Determined P4PORT to be: %s' % ud.host)
@@ -87,9 +86,9 @@ class Perforce(FetchMethod):
cleanedhost = ud.host.replace(':', '.')
ud.pkgdir = os.path.join(ud.dldir, cleanedhost, cleanedpath)
- ud.setup_revisons(d)
+ ud.setup_revisions(d)
- ud.localfile = data.expand('%s_%s_%s.tar.gz' % (cleanedhost, cleanedpath, ud.revision), d)
+ ud.localfile = d.expand('%s_%s_%s.tar.gz' % (cleanedhost, cleanedpath, ud.revision))
def _buildp4command(self, ud, d, command, depot_filename=None):
"""
@@ -140,7 +139,7 @@ class Perforce(FetchMethod):
'p4 files' command, including trailing '#rev' file revision indicator
"""
p4cmd = self._buildp4command(ud, d, 'files')
- bb.fetch2.check_network_access(d, p4cmd)
+ bb.fetch2.check_network_access(d, p4cmd, ud.url)
p4fileslist = runfetchcmd(p4cmd, d, True)
p4fileslist = [f.rstrip() for f in p4fileslist.splitlines()]
@@ -171,7 +170,7 @@ class Perforce(FetchMethod):
for afile in filelist:
p4fetchcmd = self._buildp4command(ud, d, 'print', afile)
- bb.fetch2.check_network_access(d, p4fetchcmd)
+ bb.fetch2.check_network_access(d, p4fetchcmd, ud.url)
runfetchcmd(p4fetchcmd, d, workdir=ud.pkgdir)
runfetchcmd('tar -czf %s p4' % (ud.localpath), d, cleanup=[ud.localpath], workdir=ud.pkgdir)
@@ -191,7 +190,7 @@ class Perforce(FetchMethod):
def _latest_revision(self, ud, d, name):
""" Return the latest upstream scm revision number """
p4cmd = self._buildp4command(ud, d, "changes")
- bb.fetch2.check_network_access(d, p4cmd)
+ bb.fetch2.check_network_access(d, p4cmd, ud.url)
tip = runfetchcmd(p4cmd, d, True)
if not tip:
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/repo.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/repo.py
index ecc6e68e9..1be91cc69 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/repo.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/repo.py
@@ -25,7 +25,6 @@ BitBake "Fetch" repo (git) implementation
import os
import bb
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import runfetchcmd
@@ -51,17 +50,17 @@ class Repo(FetchMethod):
if not ud.manifest.endswith('.xml'):
ud.manifest += '.xml'
- ud.localfile = data.expand("repo_%s%s_%s_%s.tar.gz" % (ud.host, ud.path.replace("/", "."), ud.manifest, ud.branch), d)
+ ud.localfile = d.expand("repo_%s%s_%s_%s.tar.gz" % (ud.host, ud.path.replace("/", "."), ud.manifest, ud.branch))
def download(self, ud, d):
"""Fetch url"""
- if os.access(os.path.join(data.getVar("DL_DIR", d, True), ud.localfile), os.R_OK):
+ if os.access(os.path.join(d.getVar("DL_DIR"), ud.localfile), os.R_OK):
logger.debug(1, "%s already exists (or was stashed). Skipping repo init / sync.", ud.localpath)
return
gitsrcname = "%s%s" % (ud.host, ud.path.replace("/", "."))
- repodir = data.getVar("REPODIR", d, True) or os.path.join(data.getVar("DL_DIR", d, True), "repo")
+ repodir = d.getVar("REPODIR") or os.path.join(d.getVar("DL_DIR"), "repo")
codir = os.path.join(repodir, gitsrcname, ud.manifest)
if ud.user:
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/s3.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/s3.py
new file mode 100644
index 000000000..162928862
--- /dev/null
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/s3.py
@@ -0,0 +1,98 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+"""
+BitBake 'Fetch' implementation for Amazon AWS S3.
+
+Class for fetching files from Amazon S3 using the AWS Command Line Interface.
+The aws tool must be correctly installed and configured prior to use.
+
+"""
+
+# Copyright (C) 2017, Andre McCurdy <armccurdy@gmail.com>
+#
+# Based in part on bb.fetch2.wget:
+# Copyright (C) 2003, 2004 Chris Larson
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Based on functions from the base bb module, Copyright 2003 Holger Schurig
+
+import os
+import bb
+import urllib.request, urllib.parse, urllib.error
+from bb.fetch2 import FetchMethod
+from bb.fetch2 import FetchError
+from bb.fetch2 import runfetchcmd
+
+class S3(FetchMethod):
+ """Class to fetch urls via 'aws s3'"""
+
+ def supports(self, ud, d):
+ """
+ Check to see if a given url can be fetched with s3.
+ """
+ return ud.type in ['s3']
+
+ def recommends_checksum(self, urldata):
+ return True
+
+ def urldata_init(self, ud, d):
+ if 'downloadfilename' in ud.parm:
+ ud.basename = ud.parm['downloadfilename']
+ else:
+ ud.basename = os.path.basename(ud.path)
+
+ ud.localfile = d.expand(urllib.parse.unquote(ud.basename))
+
+ ud.basecmd = d.getVar("FETCHCMD_s3") or "/usr/bin/env aws s3"
+
+ def download(self, ud, d):
+ """
+ Fetch urls
+ Assumes localpath was called first
+ """
+
+ cmd = '%s cp s3://%s%s %s' % (ud.basecmd, ud.host, ud.path, ud.localpath)
+ bb.fetch2.check_network_access(d, cmd, ud.url)
+ runfetchcmd(cmd, d)
+
+ # Additional sanity checks copied from the wget class (although there
+ # are no known issues which mean these are required, treat the aws cli
+ # tool with a little healthy suspicion).
+
+ if not os.path.exists(ud.localpath):
+ raise FetchError("The aws cp command returned success for s3://%s%s but %s doesn't exist?!" % (ud.host, ud.path, ud.localpath))
+
+ if os.path.getsize(ud.localpath) == 0:
+ os.remove(ud.localpath)
+ raise FetchError("The aws cp command for s3://%s%s resulted in a zero size file?! Deleting and failing since this isn't right." % (ud.host, ud.path))
+
+ return True
+
+ def checkstatus(self, fetch, ud, d):
+ """
+ Check the status of a URL
+ """
+
+ cmd = '%s ls s3://%s%s' % (ud.basecmd, ud.host, ud.path)
+ bb.fetch2.check_network_access(d, cmd, ud.url)
+ output = runfetchcmd(cmd, d)
+
+ # "aws s3 ls s3://mybucket/foo" will exit with success even if the file
+ # is not found, so check output of the command to confirm success.
+
+ if not output:
+ raise FetchError("The aws ls command for s3://%s%s gave empty output" % (ud.host, ud.path))
+
+ return True
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/sftp.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/sftp.py
index 7989fccc7..81884a6aa 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/sftp.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/sftp.py
@@ -62,12 +62,10 @@ SRC_URI = "sftp://user@host.example.com/dir/path.file.txt"
import os
import bb
import urllib.request, urllib.parse, urllib.error
-from bb import data
from bb.fetch2 import URI
from bb.fetch2 import FetchMethod
from bb.fetch2 import runfetchcmd
-
class SFTP(FetchMethod):
"""Class to fetch urls via 'sftp'"""
@@ -92,7 +90,7 @@ class SFTP(FetchMethod):
else:
ud.basename = os.path.basename(ud.path)
- ud.localfile = data.expand(urllib.parse.unquote(ud.basename), d)
+ ud.localfile = d.expand(urllib.parse.unquote(ud.basename))
def download(self, ud, d):
"""Fetch urls"""
@@ -104,7 +102,7 @@ class SFTP(FetchMethod):
port = '-P %d' % urlo.port
urlo.port = None
- dldir = data.getVar('DL_DIR', d, True)
+ dldir = d.getVar('DL_DIR')
lpath = os.path.join(dldir, ud.localfile)
user = ''
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/ssh.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/ssh.py
index 56f9b7eb3..6047ee417 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/ssh.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/ssh.py
@@ -43,7 +43,6 @@ IETF secsh internet draft:
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import re, os
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import logger
@@ -87,11 +86,11 @@ class SSH(FetchMethod):
m = __pattern__.match(urldata.url)
path = m.group('path')
host = m.group('host')
- urldata.localpath = os.path.join(d.getVar('DL_DIR', True),
+ urldata.localpath = os.path.join(d.getVar('DL_DIR'),
os.path.basename(os.path.normpath(path)))
def download(self, urldata, d):
- dldir = d.getVar('DL_DIR', True)
+ dldir = d.getVar('DL_DIR')
m = __pattern__.match(urldata.url)
path = m.group('path')
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/svn.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/svn.py
index 6ca79d35d..3f172eec9 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/svn.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/svn.py
@@ -28,7 +28,6 @@ import sys
import logging
import bb
import re
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import MissingParameterError
@@ -50,7 +49,7 @@ class Svn(FetchMethod):
if not "module" in ud.parm:
raise MissingParameterError('module', ud.url)
- ud.basecmd = d.getVar('FETCHCMD_svn', True)
+ ud.basecmd = d.getVar('FETCHCMD_svn')
ud.module = ud.parm["module"]
@@ -61,15 +60,15 @@ class Svn(FetchMethod):
# Create paths to svn checkouts
relpath = self._strip_leading_slashes(ud.path)
- ud.pkgdir = os.path.join(data.expand('${SVNDIR}', d), ud.host, relpath)
+ ud.pkgdir = os.path.join(d.expand('${SVNDIR}'), ud.host, relpath)
ud.moddir = os.path.join(ud.pkgdir, ud.module)
- ud.setup_revisons(d)
+ ud.setup_revisions(d)
if 'rev' in ud.parm:
ud.revision = ud.parm['rev']
- ud.localfile = data.expand('%s_%s_%s_%s_.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision), d)
+ ud.localfile = d.expand('%s_%s_%s_%s_.tar.gz' % (ud.module.replace('/', '.'), ud.host, ud.path.replace('/', '.'), ud.revision))
def _buildsvncommand(self, ud, d, command):
"""
@@ -79,9 +78,9 @@ class Svn(FetchMethod):
proto = ud.parm.get('protocol', 'svn')
- svn_rsh = None
- if proto == "svn+ssh" and "rsh" in ud.parm:
- svn_rsh = ud.parm["rsh"]
+ svn_ssh = None
+ if proto == "svn+ssh" and "ssh" in ud.parm:
+ svn_ssh = ud.parm["ssh"]
svnroot = ud.host + ud.path
@@ -113,8 +112,8 @@ class Svn(FetchMethod):
else:
raise FetchError("Invalid svn command %s" % command, ud.url)
- if svn_rsh:
- svncmd = "svn_RSH=\"%s\" %s" % (svn_rsh, svncmd)
+ if svn_ssh:
+ svncmd = "SVN_SSH=\"%s\" %s" % (svn_ssh, svncmd)
return svncmd
@@ -173,7 +172,7 @@ class Svn(FetchMethod):
"""
Return the latest upstream revision number
"""
- bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "log1"))
+ bb.fetch2.check_network_access(d, self._buildsvncommand(ud, d, "log1"), ud.url)
output = runfetchcmd("LANG=C LC_ALL=C " + self._buildsvncommand(ud, d, "log1"), d, True)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/wget.py b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/wget.py
index 23d48acb0..ae0ffa8c9 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/fetch2/wget.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/fetch2/wget.py
@@ -33,7 +33,6 @@ import logging
import bb
import bb.progress
import urllib.request, urllib.parse, urllib.error
-from bb import data
from bb.fetch2 import FetchMethod
from bb.fetch2 import FetchError
from bb.fetch2 import logger
@@ -84,18 +83,18 @@ class Wget(FetchMethod):
else:
ud.basename = os.path.basename(ud.path)
- ud.localfile = data.expand(urllib.parse.unquote(ud.basename), d)
+ ud.localfile = d.expand(urllib.parse.unquote(ud.basename))
if not ud.localfile:
- ud.localfile = data.expand(urllib.parse.unquote(ud.host + ud.path).replace("/", "."), d)
+ ud.localfile = d.expand(urllib.parse.unquote(ud.host + ud.path).replace("/", "."))
- self.basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -t 2 -T 30 --passive-ftp --no-check-certificate"
+ self.basecmd = d.getVar("FETCHCMD_wget") or "/usr/bin/env wget -t 2 -T 30 --passive-ftp --no-check-certificate"
def _runwget(self, ud, d, command, quiet):
progresshandler = WgetProgressHandler(d)
logger.debug(2, "Fetching %s using command '%s'" % (ud.url, command))
- bb.fetch2.check_network_access(d, command)
+ bb.fetch2.check_network_access(d, command, ud.url)
runfetchcmd(command + ' --progress=dot -v', d, quiet, log=progresshandler)
def download(self, ud, d):
@@ -104,7 +103,7 @@ class Wget(FetchMethod):
fetchcmd = self.basecmd
if 'downloadfilename' in ud.parm:
- dldir = d.getVar("DL_DIR", True)
+ dldir = d.getVar("DL_DIR")
bb.utils.mkdirhier(os.path.dirname(dldir + os.sep + ud.localfile))
fetchcmd += " -O " + dldir + os.sep + ud.localfile
@@ -304,12 +303,24 @@ class Wget(FetchMethod):
r = urllib.request.Request(uri)
r.get_method = lambda: "HEAD"
- if ud.user:
+ def add_basic_auth(login_str, request):
+ '''Adds Basic auth to http request, pass in login:password as string'''
import base64
- encodeuser = base64.b64encode(ud.user.encode('utf-8')).decode("utf-8")
+ encodeuser = base64.b64encode(login_str.encode('utf-8')).decode("utf-8")
authheader = "Basic %s" % encodeuser
r.add_header("Authorization", authheader)
+ if ud.user:
+ add_basic_auth(ud.user, r)
+
+ try:
+ import netrc, urllib.parse
+ n = netrc.netrc()
+ login, unused, password = n.authenticators(urllib.parse.urlparse(uri).hostname)
+ add_basic_auth("%s:%s" % (login, password), r)
+ except (TypeError, ImportError, IOError, netrc.NetrcParseError):
+ pass
+
opener.open(r)
except urllib.error.URLError as e:
if try_again:
@@ -534,7 +545,7 @@ class Wget(FetchMethod):
# src.rpm extension was added only for rpm package. Can be removed if the rpm
# packaged will always be considered as having to be manually upgraded
- psuffix_regex = "(tar\.gz|tgz|tar\.bz2|zip|xz|rpm|bz2|orig\.tar\.gz|tar\.xz|src\.tar\.gz|src\.tgz|svnr\d+\.tar\.bz2|stable\.tar\.gz|src\.rpm)"
+ psuffix_regex = "(tar\.gz|tgz|tar\.bz2|zip|xz|tar\.lz|rpm|bz2|orig\.tar\.gz|tar\.xz|src\.tar\.gz|src\.tgz|svnr\d+\.tar\.bz2|stable\.tar\.gz|src\.rpm)"
# match name, version and archive type of a package
package_regex_comp = re.compile("(?P<name>%s?\.?v?)(?P<pver>%s)(?P<arch>%s)?[\.-](?P<type>%s$)"
@@ -542,7 +553,7 @@ class Wget(FetchMethod):
self.suffix_regex_comp = re.compile(psuffix_regex)
# compile regex, can be specific by package or generic regex
- pn_regex = d.getVar('UPSTREAM_CHECK_REGEX', True)
+ pn_regex = d.getVar('UPSTREAM_CHECK_REGEX')
if pn_regex:
package_custom_regex_comp = re.compile(pn_regex)
else:
@@ -563,7 +574,7 @@ class Wget(FetchMethod):
sanity check to ensure same name and type.
"""
package = ud.path.split("/")[-1]
- current_version = ['', d.getVar('PV', True), '']
+ current_version = ['', d.getVar('PV'), '']
"""possible to have no version in pkg name, such as spectrum-fw"""
if not re.search("\d+", package):
@@ -578,7 +589,7 @@ class Wget(FetchMethod):
bb.debug(3, "latest_versionstring, regex: %s" % (package_regex.pattern))
uri = ""
- regex_uri = d.getVar("UPSTREAM_CHECK_URI", True)
+ regex_uri = d.getVar("UPSTREAM_CHECK_URI")
if not regex_uri:
path = ud.path.split(package)[0]
@@ -587,7 +598,7 @@ class Wget(FetchMethod):
dirver_regex = re.compile("(?P<dirver>[^/]*(\d+\.)*\d+([-_]r\d+)*)/")
m = dirver_regex.search(path)
if m:
- pn = d.getVar('PN', True)
+ pn = d.getVar('PN')
dirver = m.group('dirver')
dirver_pn_regex = re.compile("%s\d?" % (re.escape(pn)))
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/main.py b/import-layers/yocto-poky/bitbake/lib/bb/main.py
index f2f59f670..8c948c2c1 100755
--- a/import-layers/yocto-poky/bitbake/lib/bb/main.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/main.py
@@ -174,13 +174,24 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
help="Read the specified file after bitbake.conf.")
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
- help="Output more log message data to the terminal.")
+ help="Enable tracing of shell tasks (with 'set -x'). "
+ "Also print bb.note(...) messages to stdout (in "
+ "addition to writing them to ${T}/log.do_<task>).")
parser.add_option("-D", "--debug", action="count", dest="debug", default=0,
- help="Increase the debug level. You can specify this more than once.")
-
- parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False,
- help="Output less log message data to the terminal.")
+ help="Increase the debug level. You can specify this "
+ "more than once. -D sets the debug level to 1, "
+ "where only bb.debug(1, ...) messages are printed "
+ "to stdout; -DD sets the debug level to 2, where "
+ "both bb.debug(1, ...) and bb.debug(2, ...) "
+ "messages are printed; etc. Without -D, no debug "
+ "messages are printed. Note that -D only affects "
+ "output to stdout. All debug messages are written "
+ "to ${T}/log.do_taskname, regardless of the debug "
+ "level.")
+
+ parser.add_option("-q", "--quiet", action="count", dest="quiet", default=0,
+ help="Output less log message data to the terminal. You can specify this more than once.")
parser.add_option("-n", "--dry-run", action="store_true", dest="dry_run", default=False,
help="Don't execute, just go through the motions.")
@@ -287,6 +298,9 @@ class BitBakeConfigParameters(cookerdata.ConfigParameters):
help="Writes the event log of the build to a bitbake event json file. "
"Use '' (empty string) to assign the name automatically.")
+ parser.add_option("", "--runall", action="store", dest="runall",
+ help="Run the specified task for all build targets and their dependencies.")
+
options, targets = parser.parse_args(argv)
if options.quiet and options.verbose:
@@ -367,6 +381,7 @@ def start_server(servermodule, configParams, configuration, features):
raise
if not configParams.foreground:
server.detach()
+ cooker.shutdown()
cooker.lock.close()
return server
@@ -389,12 +404,8 @@ def bitbake_main(configParams, configuration):
except:
pass
-
configuration.setConfigParameters(configParams)
- ui_module = import_extension_module(bb.ui, configParams.ui, 'main')
- servermodule = import_extension_module(bb.server, configParams.servertype, 'BitBakeServer')
-
if configParams.server_only:
if configParams.servertype != "xmlrpc":
raise BBMainException("FATAL: If '--server-only' is defined, we must set the "
@@ -442,6 +453,31 @@ def bitbake_main(configParams, configuration):
bb.msg.init_msgconfig(configParams.verbose, configuration.debug,
configuration.debug_domains)
+ server, server_connection, ui_module = setup_bitbake(configParams, configuration)
+ if server_connection is None and configParams.kill_server:
+ return 0
+
+ if not configParams.server_only:
+ if configParams.status_only:
+ server_connection.terminate()
+ return 0
+
+ try:
+ return ui_module.main(server_connection.connection, server_connection.events,
+ configParams)
+ finally:
+ bb.event.ui_queue = []
+ server_connection.terminate()
+ else:
+ print("Bitbake server address: %s, server port: %s" % (server.serverImpl.host,
+ server.serverImpl.port))
+ if configParams.foreground:
+ server.serverImpl.serve_forever()
+ return 0
+
+ return 1
+
+def setup_bitbake(configParams, configuration, extrafeatures=None):
# Ensure logging messages get sent to the UI as events
handler = bb.event.LogHandler()
if not configParams.status_only:
@@ -451,8 +487,11 @@ def bitbake_main(configParams, configuration):
# Clear away any spurious environment variables while we stoke up the cooker
cleanedvars = bb.utils.clean_environment()
- featureset = []
- if not configParams.server_only:
+ if configParams.server_only:
+ featureset = []
+ ui_module = None
+ else:
+ ui_module = import_extension_module(bb.ui, configParams.ui, 'main')
# Collect the feature set for the UI
featureset = getattr(ui_module, "featureSet", [])
@@ -463,11 +502,15 @@ def bitbake_main(configParams, configuration):
setattr(configuration, "%s_server" % param, value)
param = "%s_server" % param
- if not configParams.remote_server:
- # we start a server with a given configuration
- server = start_server(servermodule, configParams, configuration, featureset)
- bb.event.ui_queue = []
- else:
+ if extrafeatures:
+ for feature in extrafeatures:
+ if not feature in featureset:
+ featureset.append(feature)
+
+ servermodule = import_extension_module(bb.server,
+ configParams.servertype,
+ 'BitBakeServer')
+ if configParams.remote_server:
if os.getenv('BBSERVER') == 'autostart':
if configParams.remote_server == 'autostart' or \
not servermodule.check_connection(configParams.remote_server, timeout=2):
@@ -475,14 +518,19 @@ def bitbake_main(configParams, configuration):
srv = start_server(servermodule, configParams, configuration, featureset)
configParams.remote_server = '%s:%d' % tuple(configuration.interface)
bb.event.ui_queue = []
-
# we start a stub server that is actually a XMLRPClient that connects to a real server
+ from bb.server.xmlrpc import BitBakeXMLRPCClient
server = servermodule.BitBakeXMLRPCClient(configParams.observe_only,
configParams.xmlrpctoken)
server.saveConnectionDetails(configParams.remote_server)
+ else:
+ # we start a server with a given configuration
+ server = start_server(servermodule, configParams, configuration, featureset)
+ bb.event.ui_queue = []
-
- if not configParams.server_only:
+ if configParams.server_only:
+ server_connection = None
+ else:
try:
server_connection = server.establishConnection(featureset)
except Exception as e:
@@ -491,7 +539,7 @@ def bitbake_main(configParams, configuration):
if configParams.kill_server:
server_connection.connection.terminateServer()
bb.event.ui_queue = []
- return 0
+ return None, None, None
server_connection.setupEventQueue()
@@ -501,22 +549,4 @@ def bitbake_main(configParams, configuration):
logger.removeHandler(handler)
-
- if configParams.status_only:
- server_connection.terminate()
- return 0
-
- try:
- return ui_module.main(server_connection.connection, server_connection.events,
- configParams)
- finally:
- bb.event.ui_queue = []
- server_connection.terminate()
- else:
- print("Bitbake server address: %s, server port: %s" % (server.serverImpl.host,
- server.serverImpl.port))
- if configParams.foreground:
- server.serverImpl.serve_forever()
- return 0
-
- return 1
+ return server, server_connection, ui_module
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/monitordisk.py b/import-layers/yocto-poky/bitbake/lib/bb/monitordisk.py
index 203c40504..833cd3d34 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/monitordisk.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/monitordisk.py
@@ -129,7 +129,7 @@ def getDiskData(BBDirs, configuration):
bb.utils.mkdirhier(path)
dev = getMountedDev(path)
# Use path/action as the key
- devDict[os.path.join(path, action)] = [dev, minSpace, minInode]
+ devDict[(path, action)] = [dev, minSpace, minInode]
return devDict
@@ -141,7 +141,7 @@ def getInterval(configuration):
spaceDefault = 50 * 1024 * 1024
inodeDefault = 5 * 1024
- interval = configuration.getVar("BB_DISKMON_WARNINTERVAL", True)
+ interval = configuration.getVar("BB_DISKMON_WARNINTERVAL")
if not interval:
return spaceDefault, inodeDefault
else:
@@ -179,7 +179,7 @@ class diskMonitor:
self.enableMonitor = False
self.configuration = configuration
- BBDirs = configuration.getVar("BB_DISKMON_DIRS", True) or None
+ BBDirs = configuration.getVar("BB_DISKMON_DIRS") or None
if BBDirs:
self.devDict = getDiskData(BBDirs, configuration)
if self.devDict:
@@ -205,18 +205,21 @@ class diskMonitor:
""" Take action for the monitor """
if self.enableMonitor:
- for k in self.devDict:
- path = os.path.dirname(k)
- action = os.path.basename(k)
- dev = self.devDict[k][0]
- minSpace = self.devDict[k][1]
- minInode = self.devDict[k][2]
+ diskUsage = {}
+ for k, attributes in self.devDict.items():
+ path, action = k
+ dev, minSpace, minInode = attributes
st = os.statvfs(path)
- # The free space, float point number
+ # The available free space, integer number
freeSpace = st.f_bavail * st.f_frsize
+ # Send all relevant information in the event.
+ freeSpaceRoot = st.f_bfree * st.f_frsize
+ totalSpace = st.f_blocks * st.f_frsize
+ diskUsage[dev] = bb.event.DiskUsageSample(freeSpace, freeSpaceRoot, totalSpace)
+
if minSpace and freeSpace < minSpace:
# Always show warning, the self.checked would always be False if the action is WARN
if self.preFreeS[k] == 0 or self.preFreeS[k] - freeSpace > self.spaceInterval and not self.checked[k]:
@@ -235,7 +238,7 @@ class diskMonitor:
rq.finish_runqueue(True)
bb.event.fire(bb.event.DiskFull(dev, 'disk', freeSpace, path), self.configuration)
- # The free inodes, float point number
+ # The free inodes, integer number
freeInode = st.f_favail
if minInode and freeInode < minInode:
@@ -260,4 +263,6 @@ class diskMonitor:
self.checked[k] = True
rq.finish_runqueue(True)
bb.event.fire(bb.event.DiskFull(dev, 'inode', freeInode, path), self.configuration)
+
+ bb.event.fire(bb.event.MonitorDiskEvent(diskUsage), self.configuration)
return
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/msg.py b/import-layers/yocto-poky/bitbake/lib/bb/msg.py
index b7c39fa13..90b158238 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/msg.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/msg.py
@@ -201,3 +201,18 @@ def fatal(msgdomain, msg):
logger = logging.getLogger("BitBake")
logger.critical(msg)
sys.exit(1)
+
+def logger_create(name, output=sys.stderr, level=logging.INFO, preserve_handlers=False, color='auto'):
+ """Standalone logger creation function"""
+ logger = logging.getLogger(name)
+ console = logging.StreamHandler(output)
+ format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
+ if color == 'always' or (color == 'auto' and output.isatty()):
+ format.enable_color()
+ console.setFormatter(format)
+ if preserve_handlers:
+ logger.addHandler(console)
+ else:
+ logger.handlers = [console]
+ logger.setLevel(level)
+ return logger
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/parse/__init__.py b/import-layers/yocto-poky/bitbake/lib/bb/parse/__init__.py
index 26ae7ead8..a2952ecc0 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/parse/__init__.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/parse/__init__.py
@@ -123,7 +123,7 @@ def init_parser(d):
def resolve_file(fn, d):
if not os.path.isabs(fn):
- bbpath = d.getVar("BBPATH", True)
+ bbpath = d.getVar("BBPATH")
newfn, attempts = bb.utils.which(bbpath, fn, history=True)
for af in attempts:
mark_dependency(d, af)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/parse/ast.py b/import-layers/yocto-poky/bitbake/lib/bb/parse/ast.py
index fa83b1898..dba4540f5 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/parse/ast.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/parse/ast.py
@@ -30,8 +30,6 @@ import itertools
from bb import methodpool
from bb.parse import logger
-_bbversions_re = re.compile(r"\[(?P<from>[0-9]+)-(?P<to>[0-9]+)\]")
-
class StatementGroup(list):
def eval(self, data):
for statement in self:
@@ -132,7 +130,6 @@ class DataNode(AstNode):
val = groupd["value"]
elif "colon" in groupd and groupd["colon"] != None:
e = data.createCopy()
- bb.data.update_data(e)
op = "immediate"
val = e.expand(groupd["value"], key + "[:=]")
elif "append" in groupd and groupd["append"] != None:
@@ -347,19 +344,18 @@ def finalize(fn, d, variant = None):
if not handlerfn:
bb.fatal("Undefined event handler function '%s'" % var)
handlerln = int(d.getVarFlag(var, "lineno", False))
- bb.event.register(var, d.getVar(var, False), (d.getVarFlag(var, "eventmask", True) or "").split(), handlerfn, handlerln)
+ bb.event.register(var, d.getVar(var, False), (d.getVarFlag(var, "eventmask") or "").split(), handlerfn, handlerln)
bb.event.fire(bb.event.RecipePreFinalise(fn), d)
bb.data.expandKeys(d)
- bb.data.update_data(d)
code = []
for funcname in d.getVar("__BBANONFUNCS", False) or []:
code.append("%s(d)" % funcname)
bb.utils.better_exec("\n".join(code), {"d": d})
- bb.data.update_data(d)
tasklist = d.getVar('__BBTASKS', False) or []
+ bb.event.fire(bb.event.RecipeTaskPreProcess(fn, list(tasklist)), d)
bb.build.add_tasks(tasklist, d)
bb.parse.siggen.finalise(fn, d, variant)
@@ -385,29 +381,8 @@ def _create_variants(datastores, names, function, onlyfinalise):
else:
create_variant("%s-%s" % (variant, name), datastores[variant], name)
-def _expand_versions(versions):
- def expand_one(version, start, end):
- for i in range(start, end + 1):
- ver = _bbversions_re.sub(str(i), version, 1)
- yield ver
-
- versions = iter(versions)
- while True:
- try:
- version = next(versions)
- except StopIteration:
- break
-
- range_ver = _bbversions_re.search(version)
- if not range_ver:
- yield version
- else:
- newversions = expand_one(version, int(range_ver.group("from")),
- int(range_ver.group("to")))
- versions = itertools.chain(newversions, versions)
-
def multi_finalize(fn, d):
- appends = (d.getVar("__BBAPPEND", True) or "").split()
+ appends = (d.getVar("__BBAPPEND") or "").split()
for append in appends:
logger.debug(1, "Appending .bbappend file %s to %s", append, fn)
bb.parse.BBHandler.handle(append, d, True)
@@ -422,51 +397,7 @@ def multi_finalize(fn, d):
d.setVar("__SKIPPED", e.args[0])
datastores = {"": safe_d}
- versions = (d.getVar("BBVERSIONS", True) or "").split()
- if versions:
- pv = orig_pv = d.getVar("PV", True)
- baseversions = {}
-
- def verfunc(ver, d, pv_d = None):
- if pv_d is None:
- pv_d = d
-
- overrides = d.getVar("OVERRIDES", True).split(":")
- pv_d.setVar("PV", ver)
- overrides.append(ver)
- bpv = baseversions.get(ver) or orig_pv
- pv_d.setVar("BPV", bpv)
- overrides.append(bpv)
- d.setVar("OVERRIDES", ":".join(overrides))
-
- versions = list(_expand_versions(versions))
- for pos, version in enumerate(list(versions)):
- try:
- pv, bpv = version.split(":", 2)
- except ValueError:
- pass
- else:
- versions[pos] = pv
- baseversions[pv] = bpv
-
- if pv in versions and not baseversions.get(pv):
- versions.remove(pv)
- else:
- pv = versions.pop()
-
- # This is necessary because our existing main datastore
- # has already been finalized with the old PV, we need one
- # that's been finalized with the new PV.
- d = bb.data.createCopy(safe_d)
- verfunc(pv, d, safe_d)
- try:
- finalize(fn, d)
- except bb.parse.SkipRecipe as e:
- d.setVar("__SKIPPED", e.args[0])
-
- _create_variants(datastores, versions, verfunc, onlyfinalise)
-
- extended = d.getVar("BBCLASSEXTEND", True) or ""
+ extended = d.getVar("BBCLASSEXTEND") or ""
if extended:
# the following is to support bbextends with arguments, for e.g. multilib
# an example is as follows:
@@ -484,7 +415,7 @@ def multi_finalize(fn, d):
else:
extendedmap[ext] = ext
- pn = d.getVar("PN", True)
+ pn = d.getVar("PN")
def extendfunc(name, d):
if name != extendedmap[name]:
d.setVar("BBEXTENDCURR", extendedmap[name])
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/BBHandler.py b/import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/BBHandler.py
index c54a07979..fe918a41f 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/BBHandler.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/BBHandler.py
@@ -66,7 +66,7 @@ def inherit(files, fn, lineno, d):
file = os.path.join('classes', '%s.bbclass' % file)
if not os.path.isabs(file):
- bbpath = d.getVar("BBPATH", True)
+ bbpath = d.getVar("BBPATH")
abs_fn, attempts = bb.utils.which(bbpath, file, history=True)
for af in attempts:
if af != abs_fn:
@@ -87,17 +87,17 @@ def get_statements(filename, absolute_filename, base_name):
try:
return cached_statements[absolute_filename]
except KeyError:
- file = open(absolute_filename, 'r')
- statements = ast.StatementGroup()
-
- lineno = 0
- while True:
- lineno = lineno + 1
- s = file.readline()
- if not s: break
- s = s.rstrip()
- feeder(lineno, s, filename, base_name, statements)
- file.close()
+ with open(absolute_filename, 'r') as f:
+ statements = ast.StatementGroup()
+
+ lineno = 0
+ while True:
+ lineno = lineno + 1
+ s = f.readline()
+ if not s: break
+ s = s.rstrip()
+ feeder(lineno, s, filename, base_name, statements)
+
if __inpython__:
# add a blank line to close out any python definition
feeder(lineno, "", filename, base_name, statements, eof=True)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/ConfHandler.py b/import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/ConfHandler.py
index 875250de4..f7d0cf74a 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/ConfHandler.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/parse/parse_py/ConfHandler.py
@@ -33,7 +33,7 @@ from bb.parse import ParseError, resolve_file, ast, logger, handle
__config_regexp__ = re.compile( r"""
^
(?P<exp>export\s*)?
- (?P<var>[a-zA-Z0-9\-~_+.${}/]+?)
+ (?P<var>[a-zA-Z0-9\-_+.${}/~]+?)
(\[(?P<flag>[a-zA-Z0-9\-_+.]+)\])?
\s* (
@@ -56,9 +56,9 @@ __config_regexp__ = re.compile( r"""
""", re.X)
__include_regexp__ = re.compile( r"include\s+(.+)" )
__require_regexp__ = re.compile( r"require\s+(.+)" )
-__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/]+)$" )
-__unset_regexp__ = re.compile( r"unset\s+([a-zA-Z0-9\-_+.${}/]+)$" )
-__unset_flag_regexp__ = re.compile( r"unset\s+([a-zA-Z0-9\-_+.${}/]+)\[([a-zA-Z0-9\-_+.${}/]+)\]$" )
+__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/~]+)$" )
+__unset_regexp__ = re.compile( r"unset\s+([a-zA-Z0-9\-_+.${}/~]+)$" )
+__unset_flag_regexp__ = re.compile( r"unset\s+([a-zA-Z0-9\-_+.${}/~]+)\[([a-zA-Z0-9\-_+.]+)\]$" )
def init(data):
topdir = data.getVar('TOPDIR', False)
@@ -83,16 +83,16 @@ def include(parentfn, fn, lineno, data, error_out):
if not os.path.isabs(fn):
dname = os.path.dirname(parentfn)
- bbpath = "%s:%s" % (dname, data.getVar("BBPATH", True))
+ bbpath = "%s:%s" % (dname, data.getVar("BBPATH"))
abs_fn, attempts = bb.utils.which(bbpath, fn, history=True)
if abs_fn and bb.parse.check_dependency(data, abs_fn):
- logger.warning("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE', True)))
+ logger.warning("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE')))
for af in attempts:
bb.parse.mark_dependency(data, af)
if abs_fn:
fn = abs_fn
elif bb.parse.check_dependency(data, fn):
- logger.warning("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE', True)))
+ logger.warning("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE')))
try:
bb.parse.handle(fn, data, True)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/persist_data.py b/import-layers/yocto-poky/bitbake/lib/bb/persist_data.py
index bb6deca52..bef701861 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/persist_data.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/persist_data.py
@@ -28,11 +28,7 @@ import sys
import warnings
from bb.compat import total_ordering
from collections import Mapping
-
-try:
- import sqlite3
-except ImportError:
- from pysqlite2 import dbapi2 as sqlite3
+import sqlite3
sqlversion = sqlite3.sqlite_version_info
if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
@@ -207,8 +203,8 @@ def connect(database):
def persist(domain, d):
"""Convenience factory for SQLTable objects based upon metadata"""
import bb.utils
- cachedir = (d.getVar("PERSISTENT_DIR", True) or
- d.getVar("CACHE", True))
+ cachedir = (d.getVar("PERSISTENT_DIR") or
+ d.getVar("CACHE"))
if not cachedir:
logger.critical("Please set the 'PERSISTENT_DIR' or 'CACHE' variable")
sys.exit(1)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/process.py b/import-layers/yocto-poky/bitbake/lib/bb/process.py
index c62d7bca4..a4a559982 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/process.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/process.py
@@ -162,9 +162,9 @@ def run(cmd, input=None, log=None, extrafiles=None, **options):
stdout, stderr = _logged_communicate(pipe, log, input, extrafiles)
else:
stdout, stderr = pipe.communicate(input)
- if stdout:
+ if not stdout is None:
stdout = stdout.decode("utf-8")
- if stderr:
+ if not stderr is None:
stderr = stderr.decode("utf-8")
if pipe.returncode != 0:
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/providers.py b/import-layers/yocto-poky/bitbake/lib/bb/providers.py
index db02a0b0d..443187e17 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/providers.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/providers.py
@@ -48,7 +48,6 @@ def findProviders(cfgData, dataCache, pkg_pn = None):
# Need to ensure data store is expanded
localdata = data.createCopy(cfgData)
- bb.data.update_data(localdata)
bb.data.expandKeys(localdata)
preferred_versions = {}
@@ -123,11 +122,11 @@ def findPreferredProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
# pn can contain '_', e.g. gcc-cross-x86_64 and an override cannot
# hence we do this manually rather than use OVERRIDES
- preferred_v = cfgData.getVar("PREFERRED_VERSION_pn-%s" % pn, True)
+ preferred_v = cfgData.getVar("PREFERRED_VERSION_pn-%s" % pn)
if not preferred_v:
- preferred_v = cfgData.getVar("PREFERRED_VERSION_%s" % pn, True)
+ preferred_v = cfgData.getVar("PREFERRED_VERSION_%s" % pn)
if not preferred_v:
- preferred_v = cfgData.getVar("PREFERRED_VERSION", True)
+ preferred_v = cfgData.getVar("PREFERRED_VERSION")
if preferred_v:
m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
@@ -289,7 +288,7 @@ def filterProviders(providers, item, cfgData, dataCache):
eligible = _filterProviders(providers, item, cfgData, dataCache)
- prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % item, True)
+ prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % item)
if prefervar:
dataCache.preferred[item] = prefervar
@@ -318,7 +317,7 @@ def filterProvidersRunTime(providers, item, cfgData, dataCache):
eligible = _filterProviders(providers, item, cfgData, dataCache)
# First try and match any PREFERRED_RPROVIDER entry
- prefervar = cfgData.getVar('PREFERRED_RPROVIDER_%s' % item, True)
+ prefervar = cfgData.getVar('PREFERRED_RPROVIDER_%s' % item)
foundUnique = False
if prefervar:
for p in eligible:
@@ -345,7 +344,7 @@ def filterProvidersRunTime(providers, item, cfgData, dataCache):
pn = dataCache.pkg_fn[p]
provides = dataCache.pn_provides[pn]
for provide in provides:
- prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide, True)
+ prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide)
#logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
if prefervar in pns and pns[prefervar] not in preferred:
var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/remotedata.py b/import-layers/yocto-poky/bitbake/lib/bb/remotedata.py
new file mode 100644
index 000000000..68ecffc19
--- /dev/null
+++ b/import-layers/yocto-poky/bitbake/lib/bb/remotedata.py
@@ -0,0 +1,116 @@
+"""
+BitBake 'remotedata' module
+
+Provides support for using a datastore from the bitbake client
+"""
+
+# Copyright (C) 2016 Intel Corporation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import bb.data
+
+class RemoteDatastores:
+ """Used on the server side to manage references to server-side datastores"""
+ def __init__(self, cooker):
+ self.cooker = cooker
+ self.datastores = {}
+ self.locked = []
+ self.nextindex = 1
+
+ def __len__(self):
+ return len(self.datastores)
+
+ def __getitem__(self, key):
+ if key is None:
+ return self.cooker.data
+ else:
+ return self.datastores[key]
+
+ def items(self):
+ return self.datastores.items()
+
+ def store(self, d, locked=False):
+ """
+ Put a datastore into the collection. If locked=True then the datastore
+ is understood to be managed externally and cannot be released by calling
+ release().
+ """
+ idx = self.nextindex
+ self.datastores[idx] = d
+ if locked:
+ self.locked.append(idx)
+ self.nextindex += 1
+ return idx
+
+ def check_store(self, d, locked=False):
+ """
+ Put a datastore into the collection if it's not already in there;
+ in either case return the index
+ """
+ for key, val in self.datastores.items():
+ if val is d:
+ idx = key
+ break
+ else:
+ idx = self.store(d, locked)
+ return idx
+
+ def release(self, idx):
+ """Discard a datastore in the collection"""
+ if idx in self.locked:
+ raise Exception('Tried to release locked datastore %d' % idx)
+ del self.datastores[idx]
+
+ def receive_datastore(self, remote_data):
+ """Receive a datastore object sent from the client (as prepared by transmit_datastore())"""
+ dct = dict(remote_data)
+ d = bb.data_smart.DataSmart()
+ d.dict = dct
+ while True:
+ if '_remote_data' in dct:
+ dsindex = dct['_remote_data']['_content']
+ del dct['_remote_data']
+ if dsindex is None:
+ dct['_data'] = self.cooker.data.dict
+ else:
+ dct['_data'] = self.datastores[dsindex].dict
+ break
+ elif '_data' in dct:
+ idct = dict(dct['_data'])
+ dct['_data'] = idct
+ dct = idct
+ else:
+ break
+ return d
+
+ @staticmethod
+ def transmit_datastore(d):
+ """Prepare a datastore object for sending over IPC from the client end"""
+ # FIXME content might be a dict, need to turn that into a list as well
+ def copy_dicts(dct):
+ if '_remote_data' in dct:
+ dsindex = dct['_remote_data']['_content'].dsindex
+ newdct = dct.copy()
+ newdct['_remote_data'] = {'_content': dsindex}
+ return list(newdct.items())
+ elif '_data' in dct:
+ newdct = dct.copy()
+ newdata = copy_dicts(dct['_data'])
+ if newdata:
+ newdct['_data'] = newdata
+ return list(newdct.items())
+ return None
+ main_dict = copy_dicts(d.dict)
+ return main_dict
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/runqueue.py b/import-layers/yocto-poky/bitbake/lib/bb/runqueue.py
index 9384c72ba..7d2ff818e 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/runqueue.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/runqueue.py
@@ -36,6 +36,7 @@ from bb import msg, data, event
from bb import monitordisk
import subprocess
import pickle
+from multiprocessing import Process
bblogger = logging.getLogger("BitBake")
logger = logging.getLogger("BitBake.RunQueue")
@@ -183,6 +184,18 @@ class RunQueueScheduler(object):
def newbuilable(self, task):
self.buildable.append(task)
+ def describe_task(self, taskid):
+ result = 'ID %s' % taskid
+ if self.rev_prio_map:
+ result = result + (' pri %d' % self.rev_prio_map[taskid])
+ return result
+
+ def dump_prio(self, comment):
+ bb.debug(3, '%s (most important first):\n%s' %
+ (comment,
+ '\n'.join(['%d. %s' % (index + 1, self.describe_task(taskid)) for
+ index, taskid in enumerate(self.prio_map)])))
+
class RunQueueSchedulerSpeed(RunQueueScheduler):
"""
A scheduler optimised for speed. The priority map is sorted by task weight,
@@ -212,35 +225,100 @@ class RunQueueSchedulerSpeed(RunQueueScheduler):
class RunQueueSchedulerCompletion(RunQueueSchedulerSpeed):
"""
- A scheduler optimised to complete .bb files are quickly as possible. The
+ A scheduler optimised to complete .bb files as quickly as possible. The
priority map is sorted by task weight, but then reordered so once a given
- .bb file starts to build, it's completed as quickly as possible. This works
- well where disk space is at a premium and classes like OE's rm_work are in
- force.
+ .bb file starts to build, it's completed as quickly as possible by
+ running all tasks related to the same .bb file one after the after.
+ This works well where disk space is at a premium and classes like OE's
+ rm_work are in force.
"""
name = "completion"
def __init__(self, runqueue, rqdata):
- RunQueueSchedulerSpeed.__init__(self, runqueue, rqdata)
-
- #FIXME - whilst this groups all fns together it does not reorder the
- #fn groups optimally.
-
- basemap = copy.deepcopy(self.prio_map)
- self.prio_map = []
- while (len(basemap) > 0):
- entry = basemap.pop(0)
- self.prio_map.append(entry)
- fn = fn_from_tid(entry)
- todel = []
- for entry in basemap:
- entry_fn = fn_from_tid(entry)
- if entry_fn == fn:
- todel.append(basemap.index(entry))
- self.prio_map.append(entry)
- todel.reverse()
- for idx in todel:
- del basemap[idx]
+ super(RunQueueSchedulerCompletion, self).__init__(runqueue, rqdata)
+
+ # Extract list of tasks for each recipe, with tasks sorted
+ # ascending from "must run first" (typically do_fetch) to
+ # "runs last" (do_build). The speed scheduler prioritizes
+ # tasks that must run first before the ones that run later;
+ # this is what we depend on here.
+ task_lists = {}
+ for taskid in self.prio_map:
+ fn, taskname = taskid.rsplit(':', 1)
+ task_lists.setdefault(fn, []).append(taskname)
+
+ # Now unify the different task lists. The strategy is that
+ # common tasks get skipped and new ones get inserted after the
+ # preceeding common one(s) as they are found. Because task
+ # lists should differ only by their number of tasks, but not
+ # the ordering of the common tasks, this should result in a
+ # deterministic result that is a superset of the individual
+ # task ordering.
+ all_tasks = []
+ for recipe, new_tasks in task_lists.items():
+ index = 0
+ old_task = all_tasks[index] if index < len(all_tasks) else None
+ for new_task in new_tasks:
+ if old_task == new_task:
+ # Common task, skip it. This is the fast-path which
+ # avoids a full search.
+ index += 1
+ old_task = all_tasks[index] if index < len(all_tasks) else None
+ else:
+ try:
+ index = all_tasks.index(new_task)
+ # Already present, just not at the current
+ # place. We re-synchronized by changing the
+ # index so that it matches again. Now
+ # move on to the next existing task.
+ index += 1
+ old_task = all_tasks[index] if index < len(all_tasks) else None
+ except ValueError:
+ # Not present. Insert before old_task, which
+ # remains the same (but gets shifted back).
+ all_tasks.insert(index, new_task)
+ index += 1
+ bb.debug(3, 'merged task list: %s' % all_tasks)
+
+ # Now reverse the order so that tasks that finish the work on one
+ # recipe are considered more imporant (= come first). The ordering
+ # is now so that do_build is most important.
+ all_tasks.reverse()
+
+ # Group tasks of the same kind before tasks of less important
+ # kinds at the head of the queue (because earlier = lower
+ # priority number = runs earlier), while preserving the
+ # ordering by recipe. If recipe foo is more important than
+ # bar, then the goal is to work on foo's do_populate_sysroot
+ # before bar's do_populate_sysroot and on the more important
+ # tasks of foo before any of the less important tasks in any
+ # other recipe (if those other recipes are more important than
+ # foo).
+ #
+ # All of this only applies when tasks are runable. Explicit
+ # dependencies still override this ordering by priority.
+ #
+ # Here's an example why this priority re-ordering helps with
+ # minimizing disk usage. Consider a recipe foo with a higher
+ # priority than bar where foo DEPENDS on bar. Then the
+ # implicit rule (from base.bbclass) is that foo's do_configure
+ # depends on bar's do_populate_sysroot. This ensures that
+ # bar's do_populate_sysroot gets done first. Normally the
+ # tasks from foo would continue to run once that is done, and
+ # bar only gets completed and cleaned up later. By ordering
+ # bar's task that depend on bar's do_populate_sysroot before foo's
+ # do_configure, that problem gets avoided.
+ task_index = 0
+ self.dump_prio('original priorities')
+ for task in all_tasks:
+ for index in range(task_index, self.numTasks):
+ taskid = self.prio_map[index]
+ taskname = taskid.rsplit(':', 1)[1]
+ if taskname == task:
+ del self.prio_map[index]
+ self.prio_map.insert(task_index, taskid)
+ task_index += 1
+ self.dump_prio('completion priorities')
class RunTaskEntry(object):
def __init__(self):
@@ -262,10 +340,11 @@ class RunQueueData:
self.rq = rq
self.warn_multi_bb = False
- self.stampwhitelist = cfgData.getVar("BB_STAMP_WHITELIST", True) or ""
- self.multi_provider_whitelist = (cfgData.getVar("MULTI_PROVIDER_WHITELIST", True) or "").split()
+ self.stampwhitelist = cfgData.getVar("BB_STAMP_WHITELIST") or ""
+ self.multi_provider_whitelist = (cfgData.getVar("MULTI_PROVIDER_WHITELIST") or "").split()
self.setscenewhitelist = get_setscene_enforce_whitelist(cfgData)
self.setscenewhitelist_checked = False
+ self.setscene_enforce = (cfgData.getVar('BB_SETSCENE_ENFORCE') == "1")
self.init_progress_reporter = bb.progress.DummyMultiStageProcessProgressReporter()
self.reset()
@@ -565,6 +644,8 @@ class RunQueueData:
for (depname, idependtask) in irdepends:
if depname in taskData[mc].run_targets:
# Won't be in run_targets if ASSUME_PROVIDED
+ if not taskData[mc].run_targets[depname]:
+ continue
depdata = taskData[mc].run_targets[depname][0]
if depdata is not None:
t = depdata + ":" + idependtask
@@ -616,6 +697,9 @@ class RunQueueData:
seendeps.add(t)
newdeps.add(t)
for i in newdeps:
+ if i not in self.runtaskentries:
+ # Not all recipes might have the recrdeptask task as a task
+ continue
task = self.runtaskentries[i].task
for n in self.runtaskentries[i].depends:
if n not in seendeps:
@@ -722,6 +806,23 @@ class RunQueueData:
self.init_progress_reporter.next_stage()
+ if self.cooker.configuration.runall is not None:
+ runall = "do_%s" % self.cooker.configuration.runall
+ runall_tids = { k: v for k, v in self.runtaskentries.items() if taskname_from_tid(k) == runall }
+
+ # re-run the mark_active and then drop unused tasks from new list
+ runq_build = {}
+ for tid in list(runall_tids):
+ mark_active(tid,1)
+
+ for tid in list(self.runtaskentries.keys()):
+ if tid not in runq_build:
+ del self.runtaskentries[tid]
+ delcount += 1
+
+ if len(self.runtaskentries) == 0:
+ bb.msg.fatal("RunQueue", "No remaining tasks to run for build target %s with runall %s" % (target, runall))
+
#
# Step D - Sanity checks and computation
#
@@ -976,16 +1077,22 @@ class RunQueue:
self.cfgData = cfgData
self.rqdata = RunQueueData(self, cooker, cfgData, dataCaches, taskData, targets)
- self.stamppolicy = cfgData.getVar("BB_STAMP_POLICY", True) or "perfile"
- self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION", True) or None
- self.setsceneverify = cfgData.getVar("BB_SETSCENE_VERIFY_FUNCTION2", True) or None
- self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID", True) or None
+ self.stamppolicy = cfgData.getVar("BB_STAMP_POLICY") or "perfile"
+ self.hashvalidate = cfgData.getVar("BB_HASHCHECK_FUNCTION") or None
+ self.setsceneverify = cfgData.getVar("BB_SETSCENE_VERIFY_FUNCTION2") or None
+ self.depvalidate = cfgData.getVar("BB_SETSCENE_DEPVALID") or None
self.state = runQueuePrepare
# For disk space monitor
+ # Invoked at regular time intervals via the bitbake heartbeat event
+ # while the build is running. We generate a unique name for the handler
+ # here, just in case that there ever is more than one RunQueue instance,
+ # start the handler when reaching runQueueSceneRun, and stop it when
+ # done with the build.
self.dm = monitordisk.diskMonitor(cfgData)
-
+ self.dm_event_handler_name = '_bb_diskmonitor_' + str(id(self))
+ self.dm_event_handler_registered = False
self.rqexe = None
self.worker = {}
self.fakeworker = {}
@@ -998,8 +1105,8 @@ class RunQueue:
if fakeroot:
magic = magic + "beef"
mcdata = self.cooker.databuilder.mcdata[mc]
- fakerootcmd = mcdata.getVar("FAKEROOTCMD", True)
- fakerootenv = (mcdata.getVar("FAKEROOTBASEENV", True) or "").split()
+ fakerootcmd = mcdata.getVar("FAKEROOTCMD")
+ fakerootenv = (mcdata.getVar("FAKEROOTBASEENV") or "").split()
env = os.environ.copy()
for key, value in (var.split('=') for var in fakerootenv):
env[key] = value
@@ -1025,12 +1132,13 @@ class RunQueue:
"logdefaultverboselogs" : bb.msg.loggerVerboseLogs,
"logdefaultdomain" : bb.msg.loggerDefaultDomains,
"prhost" : self.cooker.prhost,
- "buildname" : self.cfgData.getVar("BUILDNAME", True),
- "date" : self.cfgData.getVar("DATE", True),
- "time" : self.cfgData.getVar("TIME", True),
+ "buildname" : self.cfgData.getVar("BUILDNAME"),
+ "date" : self.cfgData.getVar("DATE"),
+ "time" : self.cfgData.getVar("TIME"),
}
worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
+ worker.stdin.write(b"<extraconfigdata>" + pickle.dumps(self.cooker.extraconfigdata) + b"</extraconfigdata>")
worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
worker.stdin.flush()
@@ -1208,10 +1316,12 @@ class RunQueue:
self.rqdata.init_progress_reporter.next_stage()
self.rqexe = RunQueueExecuteScenequeue(self)
- if self.state in [runQueueSceneRun, runQueueRunning, runQueueCleanUp]:
- self.dm.check(self)
-
if self.state is runQueueSceneRun:
+ if not self.dm_event_handler_registered:
+ res = bb.event.register(self.dm_event_handler_name,
+ lambda x: self.dm.check(self) if self.state in [runQueueSceneRun, runQueueRunning, runQueueCleanUp] else False,
+ ('bb.event.HeartbeatEvent',))
+ self.dm_event_handler_registered = True
retval = self.rqexe.execute()
if self.state is runQueueRunInit:
@@ -1230,7 +1340,13 @@ class RunQueue:
if self.state is runQueueCleanUp:
retval = self.rqexe.finish()
- if (self.state is runQueueComplete or self.state is runQueueFailed) and self.rqexe:
+ build_done = self.state is runQueueComplete or self.state is runQueueFailed
+
+ if build_done and self.dm_event_handler_registered:
+ bb.event.remove(self.dm_event_handler_name, None)
+ self.dm_event_handler_registered = False
+
+ if build_done and self.rqexe:
self.teardown_workers()
if self.rqexe.stats.failed:
logger.info("Tasks Summary: Attempted %d tasks of which %d didn't need to be rerun and %d failed.", self.rqexe.stats.completed + self.rqexe.stats.failed, self.rqexe.stats.skipped, self.rqexe.stats.failed)
@@ -1287,15 +1403,36 @@ class RunQueue:
else:
self.rqexe.finish()
+ def rq_dump_sigfn(self, fn, options):
+ bb_cache = bb.cache.NoCache(self.cooker.databuilder)
+ the_data = bb_cache.loadDataFull(fn, self.cooker.collection.get_file_appends(fn))
+ siggen = bb.parse.siggen
+ dataCaches = self.rqdata.dataCaches
+ siggen.dump_sigfn(fn, dataCaches, options)
+
def dump_signatures(self, options):
- done = set()
+ fns = set()
bb.note("Reparsing files to collect dependency data")
- bb_cache = bb.cache.NoCache(self.cooker.databuilder)
+
for tid in self.rqdata.runtaskentries:
fn = fn_from_tid(tid)
- if fn not in done:
- the_data = bb_cache.loadDataFull(fn, self.cooker.collection.get_file_appends(fn))
- done.add(fn)
+ fns.add(fn)
+
+ max_process = int(self.cfgData.getVar("BB_NUMBER_PARSE_THREADS") or os.cpu_count() or 1)
+ # We cannot use the real multiprocessing.Pool easily due to some local data
+ # that can't be pickled. This is a cheap multi-process solution.
+ launched = []
+ while fns:
+ if len(launched) < max_process:
+ p = Process(target=self.rq_dump_sigfn, args=(fns.pop(), options))
+ p.start()
+ launched.append(p)
+ for q in launched:
+ # The finished processes are joined when calling is_alive()
+ if not q.is_alive():
+ launched.remove(q)
+ for p in launched:
+ p.join()
bb.parse.siggen.dump_sigs(self.rqdata.dataCaches, options)
@@ -1326,7 +1463,7 @@ class RunQueue:
sq_hash.append(self.rqdata.runtaskentries[tid].hash)
sq_taskname.append(taskname)
sq_task.append(tid)
- locs = { "sq_fn" : sq_fn, "sq_task" : sq_taskname, "sq_hash" : sq_hash, "sq_hashfn" : sq_hashfn, "d" : self.cooker.expanded_data }
+ locs = { "sq_fn" : sq_fn, "sq_task" : sq_taskname, "sq_hash" : sq_hash, "sq_hashfn" : sq_hashfn, "d" : self.cooker.data }
try:
call = self.hashvalidate + "(sq_fn, sq_task, sq_hash, sq_hashfn, d, siginfo=True)"
valid = bb.utils.better_eval(call, locs)
@@ -1427,8 +1564,8 @@ class RunQueueExecute:
self.cfgData = rq.cfgData
self.rqdata = rq.rqdata
- self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS", True) or 1)
- self.scheduler = self.cfgData.getVar("BB_SCHEDULER", True) or "speed"
+ self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or 1)
+ self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
self.runq_buildable = set()
self.runq_running = set()
@@ -1510,7 +1647,7 @@ class RunQueueExecute:
pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
taskdata[dep] = [pn, taskname, fn]
call = self.rq.depvalidate + "(task, taskdata, notneeded, d)"
- locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.expanded_data }
+ locs = { "task" : task, "taskdata" : taskdata, "notneeded" : self.scenequeue_notneeded, "d" : self.cooker.data }
valid = bb.utils.better_eval(call, locs)
return valid
@@ -1578,7 +1715,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
invalidtasks.append(tid)
call = self.rq.setsceneverify + "(covered, tasknames, fns, d, invalidtasks=invalidtasks)"
- locs = { "covered" : self.rq.scenequeue_covered, "tasknames" : tasknames, "fns" : fns, "d" : self.cooker.expanded_data, "invalidtasks" : invalidtasks }
+ locs = { "covered" : self.rq.scenequeue_covered, "tasknames" : tasknames, "fns" : fns, "d" : self.cooker.data, "invalidtasks" : invalidtasks }
covered_remove = bb.utils.better_eval(call, locs)
def removecoveredtask(tid):
@@ -1630,7 +1767,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
if type(obj) is type and
issubclass(obj, RunQueueScheduler))
- user_schedulers = self.cfgData.getVar("BB_SCHEDULERS", True)
+ user_schedulers = self.cfgData.getVar("BB_SCHEDULERS")
if user_schedulers:
for sched in user_schedulers.split():
if not "." in sched:
@@ -1775,7 +1912,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
bb.event.fire(startevent, self.cfgData)
self.runq_running.add(task)
self.stats.taskActive()
- if not self.cooker.configuration.dry_run:
+ if not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
bb.build.make_stamp(taskname, self.rqdata.dataCaches[mc], taskfn)
self.task_complete(task)
return True
@@ -1786,7 +1923,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
taskdepdata = self.build_taskdepdata(task)
taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
- if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
+ if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not (self.cooker.configuration.dry_run or self.rqdata.setscene_enforce):
if not mc in self.rq.fakeworker:
try:
self.rq.start_fakeworker(self, mc)
@@ -1795,10 +1932,10 @@ class RunQueueExecuteTasks(RunQueueExecute):
self.rq.state = runQueueFailed
self.stats.taskFailed()
return True
- self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, False, self.cooker.collection.get_file_appends(fn), taskdepdata)) + b"</runtask>")
+ self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, False, self.cooker.collection.get_file_appends(fn), taskdepdata, self.rqdata.setscene_enforce)) + b"</runtask>")
self.rq.fakeworker[mc].process.stdin.flush()
else:
- self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, False, self.cooker.collection.get_file_appends(taskfn), taskdepdata)) + b"</runtask>")
+ self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, False, self.cooker.collection.get_file_appends(taskfn), taskdepdata, self.rqdata.setscene_enforce)) + b"</runtask>")
self.rq.worker[mc].process.stdin.flush()
self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn, noextra=True)
@@ -1839,7 +1976,8 @@ class RunQueueExecuteTasks(RunQueueExecute):
pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
deps = self.rqdata.runtaskentries[revdep].depends
provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
- taskdepdata[revdep] = [pn, taskname, fn, deps, provides]
+ taskhash = self.rqdata.runtaskentries[revdep].hash
+ taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash]
for revdep2 in deps:
if revdep2 not in taskdepdata:
additional.append(revdep2)
@@ -1892,6 +2030,8 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
for tid in self.rqdata.runq_setscene_tids:
#bb.warn("Added endpoint 2 %s" % (tid))
for dep in self.rqdata.runtaskentries[tid].depends:
+ if tid in sq_revdeps[dep]:
+ sq_revdeps[dep].remove(tid)
if dep not in endpoints:
endpoints[dep] = set()
#bb.warn(" Added endpoint 3 %s" % (dep))
@@ -1911,12 +2051,13 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
if point in self.rqdata.runq_setscene_tids:
sq_revdeps_new[point] = tasks
tasks = set()
+ continue
for dep in self.rqdata.runtaskentries[point].depends:
if point in sq_revdeps[dep]:
sq_revdeps[dep].remove(point)
if tasks:
sq_revdeps_new[dep] |= tasks
- if (len(sq_revdeps[dep]) == 0 or len(sq_revdeps_new[dep]) != 0) and dep not in self.rqdata.runq_setscene_tids:
+ if len(sq_revdeps[dep]) == 0 and dep not in self.rqdata.runq_setscene_tids:
newendpoints[dep] = task
if len(newendpoints) != 0:
process_endpoints(newendpoints)
@@ -2072,7 +2213,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
sq_taskname.append(taskname)
sq_task.append(tid)
call = self.rq.hashvalidate + "(sq_fn, sq_task, sq_hash, sq_hashfn, d)"
- locs = { "sq_fn" : sq_fn, "sq_task" : sq_taskname, "sq_hash" : sq_hash, "sq_hashfn" : sq_hashfn, "d" : self.cooker.expanded_data }
+ locs = { "sq_fn" : sq_fn, "sq_task" : sq_taskname, "sq_hash" : sq_hash, "sq_hashfn" : sq_hashfn, "d" : self.cooker.data }
valid = bb.utils.better_eval(call, locs)
valid_new = stamppresent
@@ -2199,14 +2340,16 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
startevent = sceneQueueTaskStarted(task, self.stats, self.rq)
bb.event.fire(startevent, self.cfgData)
+ taskdepdata = self.build_taskdepdata(task)
+
taskdep = self.rqdata.dataCaches[mc].task_deps[taskfn]
if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
if not mc in self.rq.fakeworker:
self.rq.start_fakeworker(self, mc)
- self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, True, self.cooker.collection.get_file_appends(taskfn), None)) + b"</runtask>")
+ self.rq.fakeworker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, True, self.cooker.collection.get_file_appends(taskfn), taskdepdata, False)) + b"</runtask>")
self.rq.fakeworker[mc].process.stdin.flush()
else:
- self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, True, self.cooker.collection.get_file_appends(taskfn), None)) + b"</runtask>")
+ self.rq.worker[mc].process.stdin.write(b"<runtask>" + pickle.dumps((taskfn, task, taskname, True, self.cooker.collection.get_file_appends(taskfn), taskdepdata, False)) + b"</runtask>")
self.rq.worker[mc].process.stdin.flush()
self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCaches[mc], taskfn, noextra=True)
@@ -2241,6 +2384,44 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
def runqueue_process_waitpid(self, task, status):
RunQueueExecute.runqueue_process_waitpid(self, task, status)
+
+ def build_taskdepdata(self, task):
+ def getsetscenedeps(tid):
+ deps = set()
+ (mc, fn, taskname, _) = split_tid_mcfn(tid)
+ realtid = tid + "_setscene"
+ idepends = self.rqdata.taskData[mc].taskentries[realtid].idepends
+ for (depname, idependtask) in idepends:
+ if depname not in self.rqdata.taskData[mc].build_targets:
+ continue
+
+ depfn = self.rqdata.taskData[mc].build_targets[depname][0]
+ if depfn is None:
+ continue
+ deptid = depfn + ":" + idependtask.replace("_setscene", "")
+ deps.add(deptid)
+ return deps
+
+ taskdepdata = {}
+ next = getsetscenedeps(task)
+ next.add(task)
+ while next:
+ additional = []
+ for revdep in next:
+ (mc, fn, taskname, taskfn) = split_tid_mcfn(revdep)
+ pn = self.rqdata.dataCaches[mc].pkg_fn[taskfn]
+ deps = getsetscenedeps(revdep)
+ provides = self.rqdata.dataCaches[mc].fn_provides[taskfn]
+ taskhash = self.rqdata.runtaskentries[revdep].hash
+ taskdepdata[revdep] = [pn, taskname, fn, deps, provides, taskhash]
+ for revdep2 in deps:
+ if revdep2 not in taskdepdata:
+ additional.append(revdep2)
+ next = additional
+
+ #bb.note("Task %s: " % task + str(taskdepdata).replace("], ", "],\n"))
+ return taskdepdata
+
class TaskFailure(Exception):
"""
Exception raised when a task in a runqueue fails
@@ -2406,9 +2587,9 @@ class runQueuePipe():
self.input.close()
def get_setscene_enforce_whitelist(d):
- if d.getVar('BB_SETSCENE_ENFORCE', True) != '1':
+ if d.getVar('BB_SETSCENE_ENFORCE') != '1':
return None
- whitelist = (d.getVar("BB_SETSCENE_ENFORCE_WHITELIST", True) or "").split()
+ whitelist = (d.getVar("BB_SETSCENE_ENFORCE_WHITELIST") or "").split()
outlist = []
for item in whitelist[:]:
if item.startswith('%:'):
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/server/process.py b/import-layers/yocto-poky/bitbake/lib/bb/server/process.py
index 982fcf71c..c3c1450a5 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/server/process.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/server/process.py
@@ -92,6 +92,8 @@ class ProcessServer(Process, BaseImplServer):
self.event = EventAdapter(event_queue)
self.featurelist = featurelist
self.quit = False
+ self.heartbeat_seconds = 1 # default, BB_HEARTBEAT_EVENT will be checked once we have a datastore.
+ self.next_heartbeat = time.time()
self.quitin, self.quitout = Pipe()
self.event_handle = multiprocessing.Value("i")
@@ -101,6 +103,14 @@ class ProcessServer(Process, BaseImplServer):
self.event_queue.put(event)
self.event_handle.value = bb.event.register_UIHhandler(self, True)
+ heartbeat_event = self.cooker.data.getVar('BB_HEARTBEAT_EVENT')
+ if heartbeat_event:
+ try:
+ self.heartbeat_seconds = float(heartbeat_event)
+ except:
+ # Throwing an exception here causes bitbake to hang.
+ # Just warn about the invalid setting and continue
+ bb.warn('Ignoring invalid BB_HEARTBEAT_EVENT=%s, must be a float specifying seconds.' % heartbeat_event)
bb.cooker.server_main(self.cooker, self.main)
def main(self):
@@ -160,6 +170,21 @@ class ProcessServer(Process, BaseImplServer):
del self._idlefuns[function]
self.quit = True
+ # Create new heartbeat event?
+ now = time.time()
+ if now >= self.next_heartbeat:
+ # We might have missed heartbeats. Just trigger once in
+ # that case and continue after the usual delay.
+ self.next_heartbeat += self.heartbeat_seconds
+ if self.next_heartbeat <= now:
+ self.next_heartbeat = now + self.heartbeat_seconds
+ heartbeat = bb.event.HeartbeatEvent(now)
+ bb.event.fire(heartbeat, self.cooker.data)
+ if nextsleep and now + nextsleep > self.next_heartbeat:
+ # Shorten timeout so that we we wake up in time for
+ # the heartbeat.
+ nextsleep = self.next_heartbeat - now
+
if nextsleep is not None:
select.select(fds,[],[],nextsleep)
@@ -199,7 +224,6 @@ class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
if isinstance(event, logging.LogRecord):
logger.handle(event)
- signal.signal(signal.SIGINT, signal.SIG_IGN)
self.procserver.stop()
while self.procserver.is_alive():
@@ -209,6 +233,9 @@ class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
self.ui_channel.close()
self.event_queue.close()
self.event_queue.setexit()
+ # XXX: Call explicity close in _writer to avoid
+ # fd leakage because isn't called on Queue.close()
+ self.event_queue._writer.close()
# Wrap Queue to provide API which isn't server implementation specific
class ProcessEventQueue(multiprocessing.queues.Queue):
@@ -240,7 +267,6 @@ class ProcessEventQueue(multiprocessing.queues.Queue):
sys.exit(1)
return None
-
class BitBakeServer(BitBakeBaseServer):
def initServer(self, single_use=True):
# establish communication channels. We use bidirectional pipes for
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/server/xmlrpc.py b/import-layers/yocto-poky/bitbake/lib/bb/server/xmlrpc.py
index 452f14bb3..a06007f5a 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/server/xmlrpc.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/server/xmlrpc.py
@@ -190,7 +190,7 @@ class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
self.send_header("Content-type", "text/plain")
self.send_header("Content-length", str(len(response)))
self.end_headers()
- self.wfile.write(response)
+ self.wfile.write(bytes(response, 'utf-8'))
class XMLRPCProxyServer(BaseImplServer):
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/siggen.py b/import-layers/yocto-poky/bitbake/lib/bb/siggen.py
index 542bbb9d1..f71190ad4 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/siggen.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/siggen.py
@@ -5,6 +5,8 @@ import re
import tempfile
import pickle
import bb.data
+import difflib
+import simplediff
from bb.checksum import FileChecksumCache
logger = logging.getLogger('BitBake.SigGen')
@@ -13,7 +15,7 @@ def init(d):
siggens = [obj for obj in globals().values()
if type(obj) is type and issubclass(obj, SignatureGenerator)]
- desired = d.getVar("BB_SIGNATURE_HANDLER", True) or "noop"
+ desired = d.getVar("BB_SIGNATURE_HANDLER") or "noop"
for sg in siggens:
if desired == sg.name:
return sg(d)
@@ -82,10 +84,10 @@ class SignatureGeneratorBasic(SignatureGenerator):
self.gendeps = {}
self.lookupcache = {}
self.pkgnameextract = re.compile("(?P<fn>.*)\..*")
- self.basewhitelist = set((data.getVar("BB_HASHBASE_WHITELIST", True) or "").split())
+ self.basewhitelist = set((data.getVar("BB_HASHBASE_WHITELIST") or "").split())
self.taskwhitelist = None
self.init_rundepcheck(data)
- checksum_cache_file = data.getVar("BB_HASH_CHECKSUM_CACHE_FILE", True)
+ checksum_cache_file = data.getVar("BB_HASH_CHECKSUM_CACHE_FILE")
if checksum_cache_file:
self.checksum_cache = FileChecksumCache()
self.checksum_cache.init_cache(data, checksum_cache_file)
@@ -93,7 +95,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
self.checksum_cache = None
def init_rundepcheck(self, data):
- self.taskwhitelist = data.getVar("BB_HASHTASK_WHITELIST", True) or None
+ self.taskwhitelist = data.getVar("BB_HASHTASK_WHITELIST") or None
if self.taskwhitelist:
self.twl = re.compile(self.taskwhitelist)
else:
@@ -101,6 +103,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
def _build_data(self, fn, d):
+ ignore_mismatch = ((d.getVar("BB_HASH_IGNORE_MISMATCH") or '') == '1')
tasklist, gendeps, lookupcache = bb.data.generate_dependencies(d)
taskdeps = {}
@@ -135,7 +138,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
data = data + str(var)
datahash = hashlib.md5(data.encode("utf-8")).hexdigest()
k = fn + "." + task
- if k in self.basehash and self.basehash[k] != datahash:
+ if not ignore_mismatch and k in self.basehash and self.basehash[k] != datahash:
bb.error("When reparsing %s, the basehash value changed from %s to %s. The metadata is not deterministic and this needs to be fixed." % (k, self.basehash[k], datahash))
self.basehash[k] = datahash
taskdeps[task] = alldeps
@@ -154,13 +157,15 @@ class SignatureGeneratorBasic(SignatureGenerator):
try:
taskdeps = self._build_data(fn, d)
+ except bb.parse.SkipRecipe:
+ raise
except:
bb.warn("Error during finalise of %s" % fn)
raise
#Slow but can be useful for debugging mismatched basehashes
#for task in self.taskdeps[fn]:
- # self.dump_sigtask(fn, task, d.getVar("STAMP", True), False)
+ # self.dump_sigtask(fn, task, d.getVar("STAMP"), False)
for task in taskdeps:
d.setVar("BB_BASEHASH_task-%s" % task, self.basehash[fn + "." + task])
@@ -306,8 +311,8 @@ class SignatureGeneratorBasic(SignatureGenerator):
pass
raise err
- def dump_sigs(self, dataCaches, options):
- for fn in self.taskdeps:
+ def dump_sigfn(self, fn, dataCaches, options):
+ if fn in self.taskdeps:
for task in self.taskdeps[fn]:
tid = fn + ":" + task
(mc, _, _) = bb.runqueue.split_tid(tid)
@@ -345,16 +350,67 @@ class SignatureGeneratorBasicHash(SignatureGeneratorBasic):
def dump_this_task(outfile, d):
import bb.parse
- fn = d.getVar("BB_FILENAME", True)
- task = "do_" + d.getVar("BB_CURRENTTASK", True)
+ fn = d.getVar("BB_FILENAME")
+ task = "do_" + d.getVar("BB_CURRENTTASK")
referencestamp = bb.build.stamp_internal(task, d, None, True)
bb.parse.siggen.dump_sigtask(fn, task, outfile, "customfile:" + referencestamp)
+def init_colors(enable_color):
+ """Initialise colour dict for passing to compare_sigfiles()"""
+ # First set up the colours
+ colors = {'color_title': '\033[1;37;40m',
+ 'color_default': '\033[0;37;40m',
+ 'color_add': '\033[1;32;40m',
+ 'color_remove': '\033[1;31;40m',
+ }
+ # Leave all keys present but clear the values
+ if not enable_color:
+ for k in colors.keys():
+ colors[k] = ''
+ return colors
+
+def worddiff_str(oldstr, newstr, colors=None):
+ if not colors:
+ colors = init_colors(False)
+ diff = simplediff.diff(oldstr.split(' '), newstr.split(' '))
+ ret = []
+ for change, value in diff:
+ value = ' '.join(value)
+ if change == '=':
+ ret.append(value)
+ elif change == '+':
+ item = '{color_add}{{+{value}+}}{color_default}'.format(value=value, **colors)
+ ret.append(item)
+ elif change == '-':
+ item = '{color_remove}[-{value}-]{color_default}'.format(value=value, **colors)
+ ret.append(item)
+ whitespace_note = ''
+ if oldstr != newstr and ' '.join(oldstr.split()) == ' '.join(newstr.split()):
+ whitespace_note = ' (whitespace changed)'
+ return '"%s"%s' % (' '.join(ret), whitespace_note)
+
+def list_inline_diff(oldlist, newlist, colors=None):
+ if not colors:
+ colors = init_colors(False)
+ diff = simplediff.diff(oldlist, newlist)
+ ret = []
+ for change, value in diff:
+ value = ' '.join(value)
+ if change == '=':
+ ret.append("'%s'" % value)
+ elif change == '+':
+ item = '{color_add}+{value}{color_default}'.format(value=value, **colors)
+ ret.append(item)
+ elif change == '-':
+ item = '{color_remove}-{value}{color_default}'.format(value=value, **colors)
+ ret.append(item)
+ return '[%s]' % (', '.join(ret))
+
def clean_basepath(a):
mc = None
if a.startswith("multiconfig:"):
_, mc, a = a.split(":", 2)
- b = a.rsplit("/", 2)[1] + a.rsplit("/", 2)[2]
+ b = a.rsplit("/", 2)[1] + '/' + a.rsplit("/", 2)[2]
if a.startswith("virtual:"):
b = b + ":" + a.rsplit(":", 1)[0]
if mc:
@@ -373,9 +429,26 @@ def clean_basepaths_list(a):
b.append(clean_basepath(x))
return b
-def compare_sigfiles(a, b, recursecb = None):
+def compare_sigfiles(a, b, recursecb=None, color=False, collapsed=False):
output = []
+ colors = init_colors(color)
+ def color_format(formatstr, **values):
+ """
+ Return colour formatted string.
+ NOTE: call with the format string, not an already formatted string
+ containing values (otherwise you could have trouble with { and }
+ characters)
+ """
+ if not formatstr.endswith('{color_default}'):
+ formatstr += '{color_default}'
+ # In newer python 3 versions you can pass both of these directly,
+ # but we only require 3.4 at the moment
+ formatparams = {}
+ formatparams.update(colors)
+ formatparams.update(values)
+ return formatstr.format(**formatparams)
+
with open(a, 'rb') as f:
p1 = pickle.Unpickler(f)
a_data = p1.load()
@@ -429,39 +502,59 @@ def compare_sigfiles(a, b, recursecb = None):
return changed, added, removed
if 'basewhitelist' in a_data and a_data['basewhitelist'] != b_data['basewhitelist']:
- output.append("basewhitelist changed from '%s' to '%s'" % (a_data['basewhitelist'], b_data['basewhitelist']))
+ output.append(color_format("{color_title}basewhitelist changed{color_default} from '%s' to '%s'") % (a_data['basewhitelist'], b_data['basewhitelist']))
if a_data['basewhitelist'] and b_data['basewhitelist']:
output.append("changed items: %s" % a_data['basewhitelist'].symmetric_difference(b_data['basewhitelist']))
if 'taskwhitelist' in a_data and a_data['taskwhitelist'] != b_data['taskwhitelist']:
- output.append("taskwhitelist changed from '%s' to '%s'" % (a_data['taskwhitelist'], b_data['taskwhitelist']))
+ output.append(color_format("{color_title}taskwhitelist changed{color_default} from '%s' to '%s'") % (a_data['taskwhitelist'], b_data['taskwhitelist']))
if a_data['taskwhitelist'] and b_data['taskwhitelist']:
output.append("changed items: %s" % a_data['taskwhitelist'].symmetric_difference(b_data['taskwhitelist']))
if a_data['taskdeps'] != b_data['taskdeps']:
- output.append("Task dependencies changed from:\n%s\nto:\n%s" % (sorted(a_data['taskdeps']), sorted(b_data['taskdeps'])))
+ output.append(color_format("{color_title}Task dependencies changed{color_default} from:\n%s\nto:\n%s") % (sorted(a_data['taskdeps']), sorted(b_data['taskdeps'])))
- if a_data['basehash'] != b_data['basehash']:
- output.append("basehash changed from %s to %s" % (a_data['basehash'], b_data['basehash']))
+ if a_data['basehash'] != b_data['basehash'] and not collapsed:
+ output.append(color_format("{color_title}basehash changed{color_default} from %s to %s") % (a_data['basehash'], b_data['basehash']))
changed, added, removed = dict_diff(a_data['gendeps'], b_data['gendeps'], a_data['basewhitelist'] & b_data['basewhitelist'])
if changed:
for dep in changed:
- output.append("List of dependencies for variable %s changed from '%s' to '%s'" % (dep, a_data['gendeps'][dep], b_data['gendeps'][dep]))
+ output.append(color_format("{color_title}List of dependencies for variable %s changed from '{color_default}%s{color_title}' to '{color_default}%s{color_title}'") % (dep, a_data['gendeps'][dep], b_data['gendeps'][dep]))
if a_data['gendeps'][dep] and b_data['gendeps'][dep]:
output.append("changed items: %s" % a_data['gendeps'][dep].symmetric_difference(b_data['gendeps'][dep]))
if added:
for dep in added:
- output.append("Dependency on variable %s was added" % (dep))
+ output.append(color_format("{color_title}Dependency on variable %s was added") % (dep))
if removed:
for dep in removed:
- output.append("Dependency on Variable %s was removed" % (dep))
+ output.append(color_format("{color_title}Dependency on Variable %s was removed") % (dep))
changed, added, removed = dict_diff(a_data['varvals'], b_data['varvals'])
if changed:
for dep in changed:
- output.append("Variable %s value changed from '%s' to '%s'" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]))
+ oldval = a_data['varvals'][dep]
+ newval = b_data['varvals'][dep]
+ if newval and oldval and ('\n' in oldval or '\n' in newval):
+ diff = difflib.unified_diff(oldval.splitlines(), newval.splitlines(), lineterm='')
+ # Cut off the first two lines, since we aren't interested in
+ # the old/new filename (they are blank anyway in this case)
+ difflines = list(diff)[2:]
+ if color:
+ # Add colour to diff output
+ for i, line in enumerate(difflines):
+ if line.startswith('+'):
+ line = color_format('{color_add}{line}', line=line)
+ difflines[i] = line
+ elif line.startswith('-'):
+ line = color_format('{color_remove}{line}', line=line)
+ difflines[i] = line
+ output.append(color_format("{color_title}Variable {var} value changed:{color_default}\n{diff}", var=dep, diff='\n'.join(difflines)))
+ elif newval and oldval and (' ' in oldval or ' ' in newval):
+ output.append(color_format("{color_title}Variable {var} value changed:{color_default}\n{diff}", var=dep, diff=worddiff_str(oldval, newval, colors)))
+ else:
+ output.append(color_format("{color_title}Variable {var} value changed from '{color_default}{oldval}{color_title}' to '{color_default}{newval}{color_title}'{color_default}", var=dep, oldval=oldval, newval=newval))
if not 'file_checksum_values' in a_data:
a_data['file_checksum_values'] = {}
@@ -471,32 +564,38 @@ def compare_sigfiles(a, b, recursecb = None):
changed, added, removed = file_checksums_diff(a_data['file_checksum_values'], b_data['file_checksum_values'])
if changed:
for f, old, new in changed:
- output.append("Checksum for file %s changed from %s to %s" % (f, old, new))
+ output.append(color_format("{color_title}Checksum for file %s changed{color_default} from %s to %s") % (f, old, new))
if added:
for f in added:
- output.append("Dependency on checksum of file %s was added" % (f))
+ output.append(color_format("{color_title}Dependency on checksum of file %s was added") % (f))
if removed:
for f in removed:
- output.append("Dependency on checksum of file %s was removed" % (f))
+ output.append(color_format("{color_title}Dependency on checksum of file %s was removed") % (f))
if not 'runtaskdeps' in a_data:
a_data['runtaskdeps'] = {}
if not 'runtaskdeps' in b_data:
b_data['runtaskdeps'] = {}
- if len(a_data['runtaskdeps']) != len(b_data['runtaskdeps']):
- changed = ["Number of task dependencies changed"]
- else:
- changed = []
- for idx, task in enumerate(a_data['runtaskdeps']):
- a = a_data['runtaskdeps'][idx]
- b = b_data['runtaskdeps'][idx]
- if a_data['runtaskhashes'][a] != b_data['runtaskhashes'][b]:
- changed.append("%s with hash %s\n changed to\n%s with hash %s" % (a, a_data['runtaskhashes'][a], b, b_data['runtaskhashes'][b]))
+ if not collapsed:
+ if len(a_data['runtaskdeps']) != len(b_data['runtaskdeps']):
+ changed = ["Number of task dependencies changed"]
+ else:
+ changed = []
+ for idx, task in enumerate(a_data['runtaskdeps']):
+ a = a_data['runtaskdeps'][idx]
+ b = b_data['runtaskdeps'][idx]
+ if a_data['runtaskhashes'][a] != b_data['runtaskhashes'][b] and not collapsed:
+ changed.append("%s with hash %s\n changed to\n%s with hash %s" % (clean_basepath(a), a_data['runtaskhashes'][a], clean_basepath(b), b_data['runtaskhashes'][b]))
- if changed:
- output.append("runtaskdeps changed from %s to %s" % (clean_basepaths_list(a_data['runtaskdeps']), clean_basepaths_list(b_data['runtaskdeps'])))
- output.append("\n".join(changed))
+ if changed:
+ clean_a = clean_basepaths_list(a_data['runtaskdeps'])
+ clean_b = clean_basepaths_list(b_data['runtaskdeps'])
+ if clean_a != clean_b:
+ output.append(color_format("{color_title}runtaskdeps changed:{color_default}\n%s") % list_inline_diff(clean_a, clean_b, colors))
+ else:
+ output.append(color_format("{color_title}runtaskdeps changed:"))
+ output.append("\n".join(changed))
if 'runtaskhashes' in a_data and 'runtaskhashes' in b_data:
@@ -512,7 +611,7 @@ def compare_sigfiles(a, b, recursecb = None):
#output.append("Dependency on task %s was replaced by %s with same hash" % (dep, bdep))
bdep_found = True
if not bdep_found:
- output.append("Dependency on task %s was added with hash %s" % (clean_basepath(dep), b[dep]))
+ output.append(color_format("{color_title}Dependency on task %s was added{color_default} with hash %s") % (clean_basepath(dep), b[dep]))
if removed:
for dep in removed:
adep_found = False
@@ -522,21 +621,25 @@ def compare_sigfiles(a, b, recursecb = None):
#output.append("Dependency on task %s was replaced by %s with same hash" % (adep, dep))
adep_found = True
if not adep_found:
- output.append("Dependency on task %s was removed with hash %s" % (clean_basepath(dep), a[dep]))
+ output.append(color_format("{color_title}Dependency on task %s was removed{color_default} with hash %s") % (clean_basepath(dep), a[dep]))
if changed:
for dep in changed:
- output.append("Hash for dependent task %s changed from %s to %s" % (clean_basepath(dep), a[dep], b[dep]))
+ if not collapsed:
+ output.append(color_format("{color_title}Hash for dependent task %s changed{color_default} from %s to %s") % (clean_basepath(dep), a[dep], b[dep]))
if callable(recursecb):
- # If a dependent hash changed, might as well print the line above and then defer to the changes in
- # that hash since in all likelyhood, they're the same changes this task also saw.
recout = recursecb(dep, a[dep], b[dep])
if recout:
- output = [output[-1]] + recout
+ if collapsed:
+ output.extend(recout)
+ else:
+ # If a dependent hash changed, might as well print the line above and then defer to the changes in
+ # that hash since in all likelyhood, they're the same changes this task also saw.
+ output = [output[-1]] + recout
a_taint = a_data.get('taint', None)
b_taint = b_data.get('taint', None)
if a_taint != b_taint:
- output.append("Taint (by forced/invalidated task) changed from %s to %s" % (a_taint, b_taint))
+ output.append(color_format("{color_title}Taint (by forced/invalidated task) changed{color_default} from %s to %s") % (a_taint, b_taint))
return output
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/taskdata.py b/import-layers/yocto-poky/bitbake/lib/bb/taskdata.py
index d8bdbcabf..8c96a5628 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/taskdata.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/taskdata.py
@@ -89,6 +89,19 @@ class TaskData:
self.add_extra_deps(fn, dataCache)
+ # Common code for dep_name/depends = 'depends'/idepends and 'rdepends'/irdepends
+ def handle_deps(task, dep_name, depends, seen):
+ if dep_name in task_deps and task in task_deps[dep_name]:
+ ids = []
+ for dep in task_deps[dep_name][task].split():
+ if dep:
+ parts = dep.split(":")
+ if len(parts) != 2:
+ bb.msg.fatal("TaskData", "Error for %s:%s[%s], dependency %s in '%s' does not contain exactly one ':' character.\n Task '%s' should be specified in the form 'packagename:task'" % (fn, task, dep_name, dep, task_deps[dep_name][task], dep_name))
+ ids.append((parts[0], parts[1]))
+ seen(parts[0])
+ depends.extend(ids)
+
for task in task_deps['tasks']:
tid = "%s:%s" % (fn, task)
@@ -105,24 +118,8 @@ class TaskData:
self.taskentries[tid].tdepends.extend(parentids)
# Touch all intertask dependencies
- if 'depends' in task_deps and task in task_deps['depends']:
- ids = []
- for dep in task_deps['depends'][task].split():
- if dep:
- if ":" not in dep:
- bb.msg.fatal("TaskData", "Error for %s, dependency %s does not contain ':' character\n. Task 'depends' should be specified in the form 'packagename:task'" % (fn, dep))
- ids.append(((dep.split(":")[0]), dep.split(":")[1]))
- self.seen_build_target(dep.split(":")[0])
- self.taskentries[tid].idepends.extend(ids)
- if 'rdepends' in task_deps and task in task_deps['rdepends']:
- ids = []
- for dep in task_deps['rdepends'][task].split():
- if dep:
- if ":" not in dep:
- bb.msg.fatal("TaskData", "Error for %s, dependency %s does not contain ':' character\n. Task 'rdepends' should be specified in the form 'packagename:task'" % (fn, dep))
- ids.append(((dep.split(":")[0]), dep.split(":")[1]))
- self.seen_run_target(dep.split(":")[0])
- self.taskentries[tid].irdepends.extend(ids)
+ handle_deps(task, 'depends', self.taskentries[tid].idepends, self.seen_build_target)
+ handle_deps(task, 'rdepends', self.taskentries[tid].irdepends, self.seen_run_target)
# Work out build dependencies
if not fn in self.depids:
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/tests/codeparser.py b/import-layers/yocto-poky/bitbake/lib/bb/tests/codeparser.py
index 14f0e2572..e30e78c15 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/tests/codeparser.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/tests/codeparser.py
@@ -49,6 +49,9 @@ class ReferenceTest(unittest.TestCase):
def assertExecs(self, execs):
self.assertEqual(self.execs, execs)
+ def assertContains(self, contains):
+ self.assertEqual(self.contains, contains)
+
class VariableReferenceTest(ReferenceTest):
def parseExpression(self, exp):
@@ -68,7 +71,7 @@ class VariableReferenceTest(ReferenceTest):
def test_python_reference(self):
self.setEmptyVars(["BAR"])
- self.parseExpression("${@bb.data.getVar('BAR', d, True) + 'foo'}")
+ self.parseExpression("${@d.getVar('BAR') + 'foo'}")
self.assertReferences(set(["BAR"]))
class ShellReferenceTest(ReferenceTest):
@@ -201,6 +204,7 @@ class PythonReferenceTest(ReferenceTest):
self.references = parsedvar.references | parser.references
self.execs = parser.execs
+ self.contains = parser.contains
@staticmethod
def indent(value):
@@ -209,17 +213,17 @@ be. These unit tests are testing snippets."""
return " " + value
def test_getvar_reference(self):
- self.parseExpression("bb.data.getVar('foo', d, True)")
+ self.parseExpression("d.getVar('foo')")
self.assertReferences(set(["foo"]))
self.assertExecs(set())
def test_getvar_computed_reference(self):
- self.parseExpression("bb.data.getVar('f' + 'o' + 'o', d, True)")
+ self.parseExpression("d.getVar('f' + 'o' + 'o')")
self.assertReferences(set())
self.assertExecs(set())
def test_getvar_exec_reference(self):
- self.parseExpression("eval('bb.data.getVar(\"foo\", d, True)')")
+ self.parseExpression("eval('d.getVar(\"foo\")')")
self.assertReferences(set())
self.assertExecs(set(["eval"]))
@@ -265,15 +269,35 @@ be. These unit tests are testing snippets."""
self.assertExecs(set(["testget"]))
del self.context["testget"]
+ def test_contains(self):
+ self.parseExpression('bb.utils.contains("TESTVAR", "one", "true", "false", d)')
+ self.assertContains({'TESTVAR': {'one'}})
+
+ def test_contains_multi(self):
+ self.parseExpression('bb.utils.contains("TESTVAR", "one two", "true", "false", d)')
+ self.assertContains({'TESTVAR': {'one two'}})
+
+ def test_contains_any(self):
+ self.parseExpression('bb.utils.contains_any("TESTVAR", "hello", "true", "false", d)')
+ self.assertContains({'TESTVAR': {'hello'}})
+
+ def test_contains_any_multi(self):
+ self.parseExpression('bb.utils.contains_any("TESTVAR", "one two three", "true", "false", d)')
+ self.assertContains({'TESTVAR': {'one', 'two', 'three'}})
+
+ def test_contains_filter(self):
+ self.parseExpression('bb.utils.filter("TESTVAR", "hello there world", d)')
+ self.assertContains({'TESTVAR': {'hello', 'there', 'world'}})
+
class DependencyReferenceTest(ReferenceTest):
pydata = """
-bb.data.getVar('somevar', d, True)
+d.getVar('somevar')
def test(d):
foo = 'bar %s' % 'foo'
def test2(d):
- d.getVar(foo, True)
+ d.getVar(foo)
d.getVar('bar', False)
test2(d)
@@ -285,9 +309,9 @@ def a():
test(d)
-bb.data.expand(bb.data.getVar("something", False, d), d)
-bb.data.expand("${inexpand} somethingelse", d)
-bb.data.getVar(a(), d, False)
+d.expand(d.getVar("something", False))
+d.expand("${inexpand} somethingelse")
+d.getVar(a(), False)
"""
def test_python(self):
@@ -370,6 +394,30 @@ esac
self.assertEqual(deps, set(["oe_libinstall"]))
+ def test_contains_vardeps(self):
+ expr = '${@bb.utils.filter("TESTVAR", "somevalue anothervalue", d)} \
+ ${@bb.utils.contains("TESTVAR", "testval testval2", "yetanothervalue", "", d)} \
+ ${@bb.utils.contains("TESTVAR", "testval2 testval3", "blah", "", d)} \
+ ${@bb.utils.contains_any("TESTVAR", "testval2 testval3", "lastone", "", d)}'
+ parsedvar = self.d.expandWithRefs(expr, None)
+ # Check contains
+ self.assertEqual(parsedvar.contains, {'TESTVAR': {'testval2 testval3', 'anothervalue', 'somevalue', 'testval testval2', 'testval2', 'testval3'}})
+ # Check dependencies
+ self.d.setVar('ANOTHERVAR', expr)
+ self.d.setVar('TESTVAR', 'anothervalue testval testval2')
+ deps, values = bb.data.build_dependencies("ANOTHERVAR", set(self.d.keys()), set(), set(), self.d)
+ self.assertEqual(sorted(values.splitlines()),
+ sorted([expr,
+ 'TESTVAR{anothervalue} = Set',
+ 'TESTVAR{somevalue} = Unset',
+ 'TESTVAR{testval testval2} = Set',
+ 'TESTVAR{testval2 testval3} = Unset',
+ 'TESTVAR{testval2} = Set',
+ 'TESTVAR{testval3} = Unset'
+ ]))
+ # Check final value
+ self.assertEqual(self.d.getVar('ANOTHERVAR').split(), ['anothervalue', 'yetanothervalue', 'lastone'])
+
#Currently no wildcard support
#def test_vardeps_wildcards(self):
# self.d.setVar("oe_libinstall", "echo test")
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/tests/data.py b/import-layers/yocto-poky/bitbake/lib/bb/tests/data.py
index b54eb0679..a4a9dd30f 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/tests/data.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/tests/data.py
@@ -77,13 +77,13 @@ class DataExpansions(unittest.TestCase):
self.assertEqual(str(val), "boo value_of_foo")
def test_python_snippet_getvar(self):
- val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
+ val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
self.assertEqual(str(val), "value_of_foo value_of_bar")
def test_python_unexpanded(self):
self.d.setVar("bar", "${unsetvar}")
- val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
- self.assertEqual(str(val), "${@d.getVar('foo', True) + ' ${unsetvar}'}")
+ val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
+ self.assertEqual(str(val), "${@d.getVar('foo') + ' ${unsetvar}'}")
def test_python_snippet_syntax_error(self):
self.d.setVar("FOO", "${@foo = 5}")
@@ -99,7 +99,7 @@ class DataExpansions(unittest.TestCase):
self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
def test_value_containing_value(self):
- val = self.d.expand("${@d.getVar('foo', True) + ' ${bar}'}")
+ val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
self.assertEqual(str(val), "value_of_foo value_of_bar")
def test_reference_undefined_var(self):
@@ -109,7 +109,7 @@ class DataExpansions(unittest.TestCase):
def test_double_reference(self):
self.d.setVar("BAR", "bar value")
self.d.setVar("FOO", "${BAR} foo ${BAR}")
- val = self.d.getVar("FOO", True)
+ val = self.d.getVar("FOO")
self.assertEqual(str(val), "bar value foo bar value")
def test_direct_recursion(self):
@@ -129,12 +129,12 @@ class DataExpansions(unittest.TestCase):
def test_incomplete_varexp_single_quotes(self):
self.d.setVar("FOO", "sed -i -e 's:IP{:I${:g' $pc")
- val = self.d.getVar("FOO", True)
+ val = self.d.getVar("FOO")
self.assertEqual(str(val), "sed -i -e 's:IP{:I${:g' $pc")
def test_nonstring(self):
self.d.setVar("TEST", 5)
- val = self.d.getVar("TEST", True)
+ val = self.d.getVar("TEST")
self.assertEqual(str(val), "5")
def test_rename(self):
@@ -234,19 +234,19 @@ class TestConcat(unittest.TestCase):
def test_prepend(self):
self.d.setVar("TEST", "${VAL}")
self.d.prependVar("TEST", "${FOO}:")
- self.assertEqual(self.d.getVar("TEST", True), "foo:val")
+ self.assertEqual(self.d.getVar("TEST"), "foo:val")
def test_append(self):
self.d.setVar("TEST", "${VAL}")
self.d.appendVar("TEST", ":${BAR}")
- self.assertEqual(self.d.getVar("TEST", True), "val:bar")
+ self.assertEqual(self.d.getVar("TEST"), "val:bar")
def test_multiple_append(self):
self.d.setVar("TEST", "${VAL}")
self.d.prependVar("TEST", "${FOO}:")
self.d.appendVar("TEST", ":val2")
self.d.appendVar("TEST", ":${BAR}")
- self.assertEqual(self.d.getVar("TEST", True), "foo:val:val2:bar")
+ self.assertEqual(self.d.getVar("TEST"), "foo:val:val2:bar")
class TestConcatOverride(unittest.TestCase):
def setUp(self):
@@ -258,62 +258,66 @@ class TestConcatOverride(unittest.TestCase):
def test_prepend(self):
self.d.setVar("TEST", "${VAL}")
self.d.setVar("TEST_prepend", "${FOO}:")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "foo:val")
+ self.assertEqual(self.d.getVar("TEST"), "foo:val")
def test_append(self):
self.d.setVar("TEST", "${VAL}")
self.d.setVar("TEST_append", ":${BAR}")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "val:bar")
+ self.assertEqual(self.d.getVar("TEST"), "val:bar")
def test_multiple_append(self):
self.d.setVar("TEST", "${VAL}")
self.d.setVar("TEST_prepend", "${FOO}:")
self.d.setVar("TEST_append", ":val2")
self.d.setVar("TEST_append", ":${BAR}")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "foo:val:val2:bar")
+ self.assertEqual(self.d.getVar("TEST"), "foo:val:val2:bar")
def test_append_unset(self):
self.d.setVar("TEST_prepend", "${FOO}:")
self.d.setVar("TEST_append", ":val2")
self.d.setVar("TEST_append", ":${BAR}")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "foo::val2:bar")
+ self.assertEqual(self.d.getVar("TEST"), "foo::val2:bar")
def test_remove(self):
self.d.setVar("TEST", "${VAL} ${BAR}")
self.d.setVar("TEST_remove", "val")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "bar")
+ self.assertEqual(self.d.getVar("TEST"), "bar")
+
+ def test_remove_cleared(self):
+ self.d.setVar("TEST", "${VAL} ${BAR}")
+ self.d.setVar("TEST_remove", "val")
+ self.d.setVar("TEST", "${VAL} ${BAR}")
+ self.assertEqual(self.d.getVar("TEST"), "val bar")
+
+ # Ensure the value is unchanged if we have an inactive remove override
+ # (including that whitespace is preserved)
+ def test_remove_inactive_override(self):
+ self.d.setVar("TEST", "${VAL} ${BAR} 123")
+ self.d.setVar("TEST_remove_inactiveoverride", "val")
+ self.assertEqual(self.d.getVar("TEST"), "val bar 123")
def test_doubleref_remove(self):
self.d.setVar("TEST", "${VAL} ${BAR}")
self.d.setVar("TEST_remove", "val")
self.d.setVar("TEST_TEST", "${TEST} ${TEST}")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST_TEST", True), "bar bar")
+ self.assertEqual(self.d.getVar("TEST_TEST"), "bar bar")
def test_empty_remove(self):
self.d.setVar("TEST", "")
self.d.setVar("TEST_remove", "val")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "")
+ self.assertEqual(self.d.getVar("TEST"), "")
def test_remove_expansion(self):
self.d.setVar("BAR", "Z")
self.d.setVar("TEST", "${BAR}/X Y")
self.d.setVar("TEST_remove", "${BAR}/X")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "Y")
+ self.assertEqual(self.d.getVar("TEST"), "Y")
def test_remove_expansion_items(self):
self.d.setVar("TEST", "A B C D")
self.d.setVar("BAR", "B D")
self.d.setVar("TEST_remove", "${BAR}")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "A C")
+ self.assertEqual(self.d.getVar("TEST"), "A C")
class TestOverrides(unittest.TestCase):
def setUp(self):
@@ -322,60 +326,53 @@ class TestOverrides(unittest.TestCase):
self.d.setVar("TEST", "testvalue")
def test_no_override(self):
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "testvalue")
+ self.assertEqual(self.d.getVar("TEST"), "testvalue")
def test_one_override(self):
self.d.setVar("TEST_bar", "testvalue2")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "testvalue2")
+ self.assertEqual(self.d.getVar("TEST"), "testvalue2")
def test_one_override_unset(self):
self.d.setVar("TEST2_bar", "testvalue2")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST2", True), "testvalue2")
+
+ self.assertEqual(self.d.getVar("TEST2"), "testvalue2")
self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2_bar'])
def test_multiple_override(self):
self.d.setVar("TEST_bar", "testvalue2")
self.d.setVar("TEST_local", "testvalue3")
self.d.setVar("TEST_foo", "testvalue4")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
+ self.assertEqual(self.d.getVar("TEST"), "testvalue3")
self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST_foo', 'OVERRIDES', 'TEST_bar', 'TEST_local'])
def test_multiple_combined_overrides(self):
self.d.setVar("TEST_local_foo_bar", "testvalue3")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
+ self.assertEqual(self.d.getVar("TEST"), "testvalue3")
def test_multiple_overrides_unset(self):
self.d.setVar("TEST2_local_foo_bar", "testvalue3")
- bb.data.update_data(self.d)
- self.assertEqual(self.d.getVar("TEST2", True), "testvalue3")
+ self.assertEqual(self.d.getVar("TEST2"), "testvalue3")
def test_keyexpansion_override(self):
self.d.setVar("LOCAL", "local")
self.d.setVar("TEST_bar", "testvalue2")
self.d.setVar("TEST_${LOCAL}", "testvalue3")
self.d.setVar("TEST_foo", "testvalue4")
- bb.data.update_data(self.d)
bb.data.expandKeys(self.d)
- self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
+ self.assertEqual(self.d.getVar("TEST"), "testvalue3")
def test_rename_override(self):
self.d.setVar("ALTERNATIVE_ncurses-tools_class-target", "a")
self.d.setVar("OVERRIDES", "class-target")
- bb.data.update_data(self.d)
self.d.renameVar("ALTERNATIVE_ncurses-tools", "ALTERNATIVE_lib32-ncurses-tools")
- self.assertEqual(self.d.getVar("ALTERNATIVE_lib32-ncurses-tools", True), "a")
+ self.assertEqual(self.d.getVar("ALTERNATIVE_lib32-ncurses-tools"), "a")
def test_underscore_override(self):
self.d.setVar("TEST_bar", "testvalue2")
self.d.setVar("TEST_some_val", "testvalue3")
self.d.setVar("TEST_foo", "testvalue4")
self.d.setVar("OVERRIDES", "foo:bar:some_val")
- self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
+ self.assertEqual(self.d.getVar("TEST"), "testvalue3")
class TestKeyExpansion(unittest.TestCase):
def setUp(self):
@@ -389,7 +386,7 @@ class TestKeyExpansion(unittest.TestCase):
with LogRecord() as logs:
bb.data.expandKeys(self.d)
self.assertTrue(logContains("Variable key VAL_${FOO} (A) replaces original key VAL_foo (B)", logs))
- self.assertEqual(self.d.getVar("VAL_foo", True), "A")
+ self.assertEqual(self.d.getVar("VAL_foo"), "A")
class TestFlags(unittest.TestCase):
def setUp(self):
@@ -444,3 +441,167 @@ class Contains(unittest.TestCase):
self.assertFalse(bb.utils.contains_any("SOMEFLAG", "x", True, False, self.d))
self.assertFalse(bb.utils.contains_any("SOMEFLAG", "x y z", True, False, self.d))
+
+
+class Serialize(unittest.TestCase):
+
+ def test_serialize(self):
+ import tempfile
+ import pickle
+ d = bb.data.init()
+ d.enableTracking()
+ d.setVar('HELLO', 'world')
+ d.setVarFlag('HELLO', 'other', 'planet')
+ with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
+ tmpfilename = tmpfile.name
+ pickle.dump(d, tmpfile)
+
+ with open(tmpfilename, 'rb') as f:
+ newd = pickle.load(f)
+
+ os.remove(tmpfilename)
+
+ self.assertEqual(d, newd)
+ self.assertEqual(newd.getVar('HELLO'), 'world')
+ self.assertEqual(newd.getVarFlag('HELLO', 'other'), 'planet')
+
+
+# Remote datastore tests
+# These really only test the interface, since in actual usage we have a
+# tinfoil connector that does everything over RPC, and this doesn't test
+# that.
+
+class TestConnector:
+ d = None
+ def __init__(self, d):
+ self.d = d
+ def getVar(self, name):
+ return self.d._findVar(name)
+ def getKeys(self):
+ return set(self.d.keys())
+ def getVarHistory(self, name):
+ return self.d.varhistory.variable(name)
+ def expandPythonRef(self, varname, expr, d):
+ localdata = self.d.createCopy()
+ for key in d.localkeys():
+ localdata.setVar(d.getVar(key))
+ varparse = bb.data_smart.VariableParse(varname, localdata)
+ return varparse.python_sub(expr)
+ def setVar(self, name, value):
+ self.d.setVar(name, value)
+ def setVarFlag(self, name, flag, value):
+ self.d.setVarFlag(name, flag, value)
+ def delVar(self, name):
+ self.d.delVar(name)
+ return False
+ def delVarFlag(self, name, flag):
+ self.d.delVarFlag(name, flag)
+ return False
+ def renameVar(self, name, newname):
+ self.d.renameVar(name, newname)
+ return False
+
+class Remote(unittest.TestCase):
+ def test_remote(self):
+
+ d1 = bb.data.init()
+ d1.enableTracking()
+ d2 = bb.data.init()
+ d2.enableTracking()
+ connector = TestConnector(d1)
+
+ d2.setVar('_remote_data', connector)
+
+ d1.setVar('HELLO', 'world')
+ d1.setVarFlag('OTHER', 'flagname', 'flagvalue')
+ self.assertEqual(d2.getVar('HELLO'), 'world')
+ self.assertEqual(d2.expand('${HELLO}'), 'world')
+ self.assertEqual(d2.expand('${@d.getVar("HELLO")}'), 'world')
+ self.assertIn('flagname', d2.getVarFlags('OTHER'))
+ self.assertEqual(d2.getVarFlag('OTHER', 'flagname'), 'flagvalue')
+ self.assertEqual(d1.varhistory.variable('HELLO'), d2.varhistory.variable('HELLO'))
+ # Test setVar on client side affects server
+ d2.setVar('HELLO', 'other-world')
+ self.assertEqual(d1.getVar('HELLO'), 'other-world')
+ # Test setVarFlag on client side affects server
+ d2.setVarFlag('HELLO', 'flagname', 'flagvalue')
+ self.assertEqual(d1.getVarFlag('HELLO', 'flagname'), 'flagvalue')
+ # Test client side data is incorporated in python expansion (which is done on server)
+ d2.setVar('FOO', 'bar')
+ self.assertEqual(d2.expand('${@d.getVar("FOO")}'), 'bar')
+ # Test overrides work
+ d1.setVar('FOO_test', 'baz')
+ d1.appendVar('OVERRIDES', ':test')
+ self.assertEqual(d2.getVar('FOO'), 'baz')
+
+
+# Remote equivalents of local test classes
+# Note that these aren't perfect since we only test in one direction
+
+class RemoteDataExpansions(DataExpansions):
+ def setUp(self):
+ self.d1 = bb.data.init()
+ self.d = bb.data.init()
+ self.d1["foo"] = "value_of_foo"
+ self.d1["bar"] = "value_of_bar"
+ self.d1["value_of_foo"] = "value_of_'value_of_foo'"
+ connector = TestConnector(self.d1)
+ self.d.setVar('_remote_data', connector)
+
+class TestRemoteNestedExpansions(TestNestedExpansions):
+ def setUp(self):
+ self.d1 = bb.data.init()
+ self.d = bb.data.init()
+ self.d1["foo"] = "foo"
+ self.d1["bar"] = "bar"
+ self.d1["value_of_foobar"] = "187"
+ connector = TestConnector(self.d1)
+ self.d.setVar('_remote_data', connector)
+
+class TestRemoteConcat(TestConcat):
+ def setUp(self):
+ self.d1 = bb.data.init()
+ self.d = bb.data.init()
+ self.d1.setVar("FOO", "foo")
+ self.d1.setVar("VAL", "val")
+ self.d1.setVar("BAR", "bar")
+ connector = TestConnector(self.d1)
+ self.d.setVar('_remote_data', connector)
+
+class TestRemoteConcatOverride(TestConcatOverride):
+ def setUp(self):
+ self.d1 = bb.data.init()
+ self.d = bb.data.init()
+ self.d1.setVar("FOO", "foo")
+ self.d1.setVar("VAL", "val")
+ self.d1.setVar("BAR", "bar")
+ connector = TestConnector(self.d1)
+ self.d.setVar('_remote_data', connector)
+
+class TestRemoteOverrides(TestOverrides):
+ def setUp(self):
+ self.d1 = bb.data.init()
+ self.d = bb.data.init()
+ self.d1.setVar("OVERRIDES", "foo:bar:local")
+ self.d1.setVar("TEST", "testvalue")
+ connector = TestConnector(self.d1)
+ self.d.setVar('_remote_data', connector)
+
+class TestRemoteKeyExpansion(TestKeyExpansion):
+ def setUp(self):
+ self.d1 = bb.data.init()
+ self.d = bb.data.init()
+ self.d1.setVar("FOO", "foo")
+ self.d1.setVar("BAR", "foo")
+ connector = TestConnector(self.d1)
+ self.d.setVar('_remote_data', connector)
+
+class TestRemoteFlags(TestFlags):
+ def setUp(self):
+ self.d1 = bb.data.init()
+ self.d = bb.data.init()
+ self.d1.setVar("foo", "value of foo")
+ self.d1.setVarFlag("foo", "flag1", "value of flag1")
+ self.d1.setVarFlag("foo", "flag2", "value of flag2")
+ connector = TestConnector(self.d1)
+ self.d.setVar('_remote_data', connector)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/tests/fetch.py b/import-layers/yocto-poky/bitbake/lib/bb/tests/fetch.py
index 0fd2c0216..5a8d89285 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/tests/fetch.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/tests/fetch.py
@@ -793,6 +793,7 @@ class FetchLatestVersionTest(FetcherTest):
ud = bb.fetch2.FetchData(k[1], self.d)
pupver= ud.method.latest_versionstring(ud, self.d)
verstring = pupver[0]
+ self.assertTrue(verstring, msg="Could not find upstream version")
r = bb.utils.vercmp_string(v, verstring)
self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], v, verstring))
@@ -804,6 +805,7 @@ class FetchLatestVersionTest(FetcherTest):
ud = bb.fetch2.FetchData(k[1], self.d)
pupver = ud.method.latest_versionstring(ud, self.d)
verstring = pupver[0]
+ self.assertTrue(verstring, msg="Could not find upstream version")
r = bb.utils.vercmp_string(v, verstring)
self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], v, verstring))
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/tests/parse.py b/import-layers/yocto-poky/bitbake/lib/bb/tests/parse.py
index 0b2706af0..ab6ca9031 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/tests/parse.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/tests/parse.py
@@ -58,9 +58,9 @@ C = "3"
def test_parse_simple(self):
f = self.parsehelper(self.testfile)
d = bb.parse.handle(f.name, self.d)['']
- self.assertEqual(d.getVar("A", True), "1")
- self.assertEqual(d.getVar("B", True), "2")
- self.assertEqual(d.getVar("C", True), "3")
+ self.assertEqual(d.getVar("A"), "1")
+ self.assertEqual(d.getVar("B"), "2")
+ self.assertEqual(d.getVar("C"), "3")
def test_parse_incomplete_function(self):
testfileB = self.testfile.replace("}", "")
@@ -80,9 +80,9 @@ unset B[flag]
def test_parse_unset(self):
f = self.parsehelper(self.unsettest)
d = bb.parse.handle(f.name, self.d)['']
- self.assertEqual(d.getVar("A", True), None)
- self.assertEqual(d.getVarFlag("A","flag", True), None)
- self.assertEqual(d.getVar("B", True), "2")
+ self.assertEqual(d.getVar("A"), None)
+ self.assertEqual(d.getVarFlag("A","flag"), None)
+ self.assertEqual(d.getVar("B"), "2")
overridetest = """
@@ -95,11 +95,11 @@ PN = "gtk+"
def test_parse_overrides(self):
f = self.parsehelper(self.overridetest)
d = bb.parse.handle(f.name, self.d)['']
- self.assertEqual(d.getVar("RRECOMMENDS", True), "b")
+ self.assertEqual(d.getVar("RRECOMMENDS"), "b")
bb.data.expandKeys(d)
- self.assertEqual(d.getVar("RRECOMMENDS", True), "b")
+ self.assertEqual(d.getVar("RRECOMMENDS"), "b")
d.setVar("RRECOMMENDS_gtk+", "c")
- self.assertEqual(d.getVar("RRECOMMENDS", True), "c")
+ self.assertEqual(d.getVar("RRECOMMENDS"), "c")
overridetest2 = """
EXTRA_OECONF = ""
@@ -112,7 +112,7 @@ EXTRA_OECONF_append = " c"
d = bb.parse.handle(f.name, self.d)['']
d.appendVar("EXTRA_OECONF", " d")
d.setVar("OVERRIDES", "class-target")
- self.assertEqual(d.getVar("EXTRA_OECONF", True), "b c d")
+ self.assertEqual(d.getVar("EXTRA_OECONF"), "b c d")
overridetest3 = """
DESCRIPTION = "A"
@@ -124,11 +124,11 @@ PN = "bc"
f = self.parsehelper(self.overridetest3)
d = bb.parse.handle(f.name, self.d)['']
bb.data.expandKeys(d)
- self.assertEqual(d.getVar("DESCRIPTION_bc-dev", True), "A B")
+ self.assertEqual(d.getVar("DESCRIPTION_bc-dev"), "A B")
d.setVar("DESCRIPTION", "E")
d.setVar("DESCRIPTION_bc-dev", "C D")
d.setVar("OVERRIDES", "bc-dev")
- self.assertEqual(d.getVar("DESCRIPTION", True), "C D")
+ self.assertEqual(d.getVar("DESCRIPTION"), "C D")
classextend = """
@@ -159,6 +159,6 @@ python () {
alldata = bb.parse.handle(f.name, self.d)
d1 = alldata['']
d2 = alldata[cls.name]
- self.assertEqual(d1.getVar("VAR_var", True), "B")
- self.assertEqual(d2.getVar("VAR_var", True), None)
+ self.assertEqual(d1.getVar("VAR_var"), "B")
+ self.assertEqual(d2.getVar("VAR_var"), None)
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py b/import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py
index 9fa5b5b3d..928333a50 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/tinfoil.py
@@ -1,6 +1,6 @@
# tinfoil: a simple wrapper around cooker for bitbake-based command-line utilities
#
-# Copyright (C) 2012 Intel Corporation
+# Copyright (C) 2012-2017 Intel Corporation
# Copyright (C) 2011 Mentor Graphics Corporation
#
# This program is free software; you can redistribute it and/or modify
@@ -17,50 +17,210 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import logging
-import warnings
import os
import sys
+import atexit
+import re
+from collections import OrderedDict, defaultdict
import bb.cache
import bb.cooker
import bb.providers
+import bb.taskdata
import bb.utils
-from bb.cooker import state, BBCooker, CookerFeatures
+import bb.command
+import bb.remotedata
from bb.cookerdata import CookerConfiguration, ConfigParameters
+from bb.main import setup_bitbake, BitBakeConfigParameters, BBMainException
import bb.fetch2
+
+# We need this in order to shut down the connection to the bitbake server,
+# otherwise the process will never properly exit
+_server_connections = []
+def _terminate_connections():
+ for connection in _server_connections:
+ connection.terminate()
+atexit.register(_terminate_connections)
+
+class TinfoilUIException(Exception):
+ """Exception raised when the UI returns non-zero from its main function"""
+ def __init__(self, returncode):
+ self.returncode = returncode
+ def __repr__(self):
+ return 'UI module main returned %d' % self.returncode
+
+class TinfoilCommandFailed(Exception):
+ """Exception raised when run_command fails"""
+
+class TinfoilDataStoreConnector:
+
+ def __init__(self, tinfoil, dsindex):
+ self.tinfoil = tinfoil
+ self.dsindex = dsindex
+ def getVar(self, name):
+ value = self.tinfoil.run_command('dataStoreConnectorFindVar', self.dsindex, name)
+ overrides = None
+ if isinstance(value, dict):
+ if '_connector_origtype' in value:
+ value['_content'] = self.tinfoil._reconvert_type(value['_content'], value['_connector_origtype'])
+ del value['_connector_origtype']
+ if '_connector_overrides' in value:
+ overrides = value['_connector_overrides']
+ del value['_connector_overrides']
+ return value, overrides
+ def getKeys(self):
+ return set(self.tinfoil.run_command('dataStoreConnectorGetKeys', self.dsindex))
+ def getVarHistory(self, name):
+ return self.tinfoil.run_command('dataStoreConnectorGetVarHistory', self.dsindex, name)
+ def expandPythonRef(self, varname, expr, d):
+ ds = bb.remotedata.RemoteDatastores.transmit_datastore(d)
+ ret = self.tinfoil.run_command('dataStoreConnectorExpandPythonRef', ds, varname, expr)
+ return ret
+ def setVar(self, varname, value):
+ if self.dsindex is None:
+ self.tinfoil.run_command('setVariable', varname, value)
+ else:
+ # Not currently implemented - indicate that setting should
+ # be redirected to local side
+ return True
+ def setVarFlag(self, varname, flagname, value):
+ if self.dsindex is None:
+ self.tinfoil.run_command('dataStoreConnectorSetVarFlag', self.dsindex, varname, flagname, value)
+ else:
+ # Not currently implemented - indicate that setting should
+ # be redirected to local side
+ return True
+ def delVar(self, varname):
+ if self.dsindex is None:
+ self.tinfoil.run_command('dataStoreConnectorDelVar', self.dsindex, varname)
+ else:
+ # Not currently implemented - indicate that setting should
+ # be redirected to local side
+ return True
+ def delVarFlag(self, varname, flagname):
+ if self.dsindex is None:
+ self.tinfoil.run_command('dataStoreConnectorDelVar', self.dsindex, varname, flagname)
+ else:
+ # Not currently implemented - indicate that setting should
+ # be redirected to local side
+ return True
+ def renameVar(self, name, newname):
+ if self.dsindex is None:
+ self.tinfoil.run_command('dataStoreConnectorRenameVar', self.dsindex, name, newname)
+ else:
+ # Not currently implemented - indicate that setting should
+ # be redirected to local side
+ return True
+
+class TinfoilCookerAdapter:
+ """
+ Provide an adapter for existing code that expects to access a cooker object via Tinfoil,
+ since now Tinfoil is on the client side it no longer has direct access.
+ """
+
+ class TinfoilCookerCollectionAdapter:
+ """ cooker.collection adapter """
+ def __init__(self, tinfoil):
+ self.tinfoil = tinfoil
+ def get_file_appends(self, fn):
+ return self.tinfoil.get_file_appends(fn)
+ def __getattr__(self, name):
+ if name == 'overlayed':
+ return self.tinfoil.get_overlayed_recipes()
+ elif name == 'bbappends':
+ return self.tinfoil.run_command('getAllAppends')
+ else:
+ raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
+
+ class TinfoilRecipeCacheAdapter:
+ """ cooker.recipecache adapter """
+ def __init__(self, tinfoil):
+ self.tinfoil = tinfoil
+ self._cache = {}
+
+ def get_pkg_pn_fn(self):
+ pkg_pn = defaultdict(list, self.tinfoil.run_command('getRecipes') or [])
+ pkg_fn = {}
+ for pn, fnlist in pkg_pn.items():
+ for fn in fnlist:
+ pkg_fn[fn] = pn
+ self._cache['pkg_pn'] = pkg_pn
+ self._cache['pkg_fn'] = pkg_fn
+
+ def __getattr__(self, name):
+ # Grab these only when they are requested since they aren't always used
+ if name in self._cache:
+ return self._cache[name]
+ elif name == 'pkg_pn':
+ self.get_pkg_pn_fn()
+ return self._cache[name]
+ elif name == 'pkg_fn':
+ self.get_pkg_pn_fn()
+ return self._cache[name]
+ elif name == 'deps':
+ attrvalue = defaultdict(list, self.tinfoil.run_command('getRecipeDepends') or [])
+ elif name == 'rundeps':
+ attrvalue = defaultdict(lambda: defaultdict(list), self.tinfoil.run_command('getRuntimeDepends') or [])
+ elif name == 'runrecs':
+ attrvalue = defaultdict(lambda: defaultdict(list), self.tinfoil.run_command('getRuntimeRecommends') or [])
+ elif name == 'pkg_pepvpr':
+ attrvalue = self.tinfoil.run_command('getRecipeVersions') or {}
+ elif name == 'inherits':
+ attrvalue = self.tinfoil.run_command('getRecipeInherits') or {}
+ elif name == 'bbfile_priority':
+ attrvalue = self.tinfoil.run_command('getBbFilePriority') or {}
+ elif name == 'pkg_dp':
+ attrvalue = self.tinfoil.run_command('getDefaultPreference') or {}
+ else:
+ raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
+
+ self._cache[name] = attrvalue
+ return attrvalue
+
+ def __init__(self, tinfoil):
+ self.tinfoil = tinfoil
+ self.collection = self.TinfoilCookerCollectionAdapter(tinfoil)
+ self.recipecaches = {}
+ # FIXME all machines
+ self.recipecaches[''] = self.TinfoilRecipeCacheAdapter(tinfoil)
+ self._cache = {}
+ def __getattr__(self, name):
+ # Grab these only when they are requested since they aren't always used
+ if name in self._cache:
+ return self._cache[name]
+ elif name == 'skiplist':
+ attrvalue = self.tinfoil.get_skipped_recipes()
+ elif name == 'bbfile_config_priorities':
+ ret = self.tinfoil.run_command('getLayerPriorities')
+ bbfile_config_priorities = []
+ for collection, pattern, regex, pri in ret:
+ bbfile_config_priorities.append((collection, pattern, re.compile(regex), pri))
+
+ attrvalue = bbfile_config_priorities
+ else:
+ raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
+
+ self._cache[name] = attrvalue
+ return attrvalue
+
+ def findBestProvider(self, pn):
+ return self.tinfoil.find_best_provider(pn)
+
+
class Tinfoil:
- def __init__(self, output=sys.stdout, tracking=False):
- # Needed to avoid deprecation warnings with python 2.6
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- # Set up logging
+ def __init__(self, output=sys.stdout, tracking=False, setup_logging=True):
self.logger = logging.getLogger('BitBake')
- self._log_hdlr = logging.StreamHandler(output)
- bb.msg.addDefaultlogFilter(self._log_hdlr)
- format = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
- if output.isatty():
- format.enable_color()
- self._log_hdlr.setFormatter(format)
- self.logger.addHandler(self._log_hdlr)
-
- self.config = CookerConfiguration()
- configparams = TinfoilConfigParameters(parse_only=True)
- self.config.setConfigParameters(configparams)
- self.config.setServerRegIdleCallback(self.register_idle_function)
- features = []
- if tracking:
- features.append(CookerFeatures.BASEDATASTORE_TRACKING)
- cleanedvars = bb.utils.clean_environment()
- self.cooker = BBCooker(self.config, features)
- self.config_data = self.cooker.data
- bb.providers.logger.setLevel(logging.ERROR)
- self.cooker_data = None
- for k in cleanedvars:
- os.environ[k] = cleanedvars[k]
-
- def register_idle_function(self, function, data):
- pass
+ self.config_data = None
+ self.cooker = None
+ self.tracking = tracking
+ self.ui_module = None
+ self.server_connection = None
+ if setup_logging:
+ # This is the *client-side* logger, nothing to do with
+ # logging messages from the server
+ bb.msg.logger_create('BitBake', output)
def __enter__(self):
return self
@@ -68,30 +228,161 @@ class Tinfoil:
def __exit__(self, type, value, traceback):
self.shutdown()
- def parseRecipes(self):
- sys.stderr.write("Parsing recipes..")
- self.logger.setLevel(logging.WARNING)
+ def prepare(self, config_only=False, config_params=None, quiet=0):
+ if self.tracking:
+ extrafeatures = [bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING]
+ else:
+ extrafeatures = []
+
+ if not config_params:
+ config_params = TinfoilConfigParameters(config_only=config_only, quiet=quiet)
- try:
- while self.cooker.state in (state.initial, state.parsing):
- self.cooker.updateCache()
- except KeyboardInterrupt:
- self.cooker.shutdown()
- self.cooker.updateCache()
- sys.exit(2)
+ cookerconfig = CookerConfiguration()
+ cookerconfig.setConfigParameters(config_params)
- self.logger.setLevel(logging.INFO)
- sys.stderr.write("done.\n")
+ server, self.server_connection, ui_module = setup_bitbake(config_params,
+ cookerconfig,
+ extrafeatures)
- self.cooker_data = self.cooker.recipecaches['']
+ self.ui_module = ui_module
+
+ # Ensure the path to bitbake's bin directory is in PATH so that things like
+ # bitbake-worker can be run (usually this is the case, but it doesn't have to be)
+ path = os.getenv('PATH').split(':')
+ bitbakebinpath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'bin'))
+ for entry in path:
+ if entry.endswith(os.sep):
+ entry = entry[:-1]
+ if os.path.abspath(entry) == bitbakebinpath:
+ break
+ else:
+ path.insert(0, bitbakebinpath)
+ os.environ['PATH'] = ':'.join(path)
- def prepare(self, config_only = False):
- if not self.cooker_data:
+ if self.server_connection:
+ _server_connections.append(self.server_connection)
if config_only:
- self.cooker.parseConfiguration()
- self.cooker_data = self.cooker.recipecaches['']
+ config_params.updateToServer(self.server_connection.connection, os.environ.copy())
+ self.run_command('parseConfiguration')
else:
- self.parseRecipes()
+ self.run_actions(config_params)
+
+ self.config_data = bb.data.init()
+ connector = TinfoilDataStoreConnector(self, None)
+ self.config_data.setVar('_remote_data', connector)
+ self.cooker = TinfoilCookerAdapter(self)
+ self.cooker_data = self.cooker.recipecaches['']
+ else:
+ raise Exception('Failed to start bitbake server')
+
+ def run_actions(self, config_params):
+ """
+ Run the actions specified in config_params through the UI.
+ """
+ ret = self.ui_module.main(self.server_connection.connection, self.server_connection.events, config_params)
+ if ret:
+ raise TinfoilUIException(ret)
+
+ def parseRecipes(self):
+ """
+ Force a parse of all recipes. Normally you should specify
+ config_only=False when calling prepare() instead of using this
+ function; this function is designed for situations where you need
+ to initialise Tinfoil and use it with config_only=True first and
+ then conditionally call this function to parse recipes later.
+ """
+ config_params = TinfoilConfigParameters(config_only=False)
+ self.run_actions(config_params)
+
+ def run_command(self, command, *params):
+ """
+ Run a command on the server (as implemented in bb.command).
+ Note that there are two types of command - synchronous and
+ asynchronous; in order to receive the results of asynchronous
+ commands you will need to set an appropriate event mask
+ using set_event_mask() and listen for the result using
+ wait_event() - with the correct event mask you'll at least get
+ bb.command.CommandCompleted and possibly other events before
+ that depending on the command.
+ """
+ if not self.server_connection:
+ raise Exception('Not connected to server (did you call .prepare()?)')
+
+ commandline = [command]
+ if params:
+ commandline.extend(params)
+ result = self.server_connection.connection.runCommand(commandline)
+ if result[1]:
+ raise TinfoilCommandFailed(result[1])
+ return result[0]
+
+ def set_event_mask(self, eventlist):
+ """Set the event mask which will be applied within wait_event()"""
+ if not self.server_connection:
+ raise Exception('Not connected to server (did you call .prepare()?)')
+ llevel, debug_domains = bb.msg.constructLogOptions()
+ ret = self.run_command('setEventMask', self.server_connection.connection.getEventHandle(), llevel, debug_domains, eventlist)
+ if not ret:
+ raise Exception('setEventMask failed')
+
+ def wait_event(self, timeout=0):
+ """
+ Wait for an event from the server for the specified time.
+ A timeout of 0 means don't wait if there are no events in the queue.
+ Returns the next event in the queue or None if the timeout was
+ reached. Note that in order to recieve any events you will
+ first need to set the internal event mask using set_event_mask()
+ (otherwise whatever event mask the UI set up will be in effect).
+ """
+ if not self.server_connection:
+ raise Exception('Not connected to server (did you call .prepare()?)')
+ return self.server_connection.events.waitEvent(timeout)
+
+ def get_overlayed_recipes(self):
+ return defaultdict(list, self.run_command('getOverlayedRecipes'))
+
+ def get_skipped_recipes(self):
+ return OrderedDict(self.run_command('getSkippedRecipes'))
+
+ def get_all_providers(self):
+ return defaultdict(list, self.run_command('allProviders'))
+
+ def find_providers(self):
+ return self.run_command('findProviders')
+
+ def find_best_provider(self, pn):
+ return self.run_command('findBestProvider', pn)
+
+ def get_runtime_providers(self, rdep):
+ return self.run_command('getRuntimeProviders', rdep)
+
+ def get_recipe_file(self, pn):
+ """
+ Get the file name for the specified recipe/target. Raises
+ bb.providers.NoProvider if there is no match or the recipe was
+ skipped.
+ """
+ best = self.find_best_provider(pn)
+ if not best or (len(best) > 3 and not best[3]):
+ skiplist = self.get_skipped_recipes()
+ taskdata = bb.taskdata.TaskData(None, skiplist=skiplist)
+ skipreasons = taskdata.get_reasons(pn)
+ if skipreasons:
+ raise bb.providers.NoProvider('%s is unavailable:\n %s' % (pn, ' \n'.join(skipreasons)))
+ else:
+ raise bb.providers.NoProvider('Unable to find any recipe file matching "%s"' % pn)
+ return best[3]
+
+ def get_file_appends(self, fn):
+ return self.run_command('getFileAppends', fn)
+
+ def parse_recipe(self, pn):
+ """
+ Parse the specified recipe and return a datastore object
+ representing the environment for the recipe.
+ """
+ fn = self.get_recipe_file(pn)
+ return self.parse_recipe_file(fn)
def parse_recipe_file(self, fn, appends=True, appendlist=None, config_data=None):
"""
@@ -110,41 +401,82 @@ class Tinfoil:
"""
if appends and appendlist == []:
appends = False
- if appends:
- if appendlist:
- appendfiles = appendlist
- else:
- if not hasattr(self.cooker, 'collection'):
- raise Exception('You must call tinfoil.prepare() with config_only=False in order to get bbappends')
- appendfiles = self.cooker.collection.get_file_appends(fn)
- else:
- appendfiles = None
if config_data:
- # We have to use a different function here if we're passing in a datastore
- localdata = bb.data.createCopy(config_data)
- envdata = bb.cache.parse_recipe(localdata, fn, appendfiles)['']
+ dctr = bb.remotedata.RemoteDatastores.transmit_datastore(config_data)
+ dscon = self.run_command('parseRecipeFile', fn, appends, appendlist, dctr)
else:
- # Use the standard path
- parser = bb.cache.NoCache(self.cooker.databuilder)
- envdata = parser.loadDataFull(fn, appendfiles)
- return envdata
+ dscon = self.run_command('parseRecipeFile', fn, appends, appendlist)
+ if dscon:
+ return self._reconvert_type(dscon, 'DataStoreConnectionHandle')
+ else:
+ return None
+
+ def build_file(self, buildfile, task):
+ """
+ Runs the specified task for just a single recipe (i.e. no dependencies).
+ This is equivalent to bitbake -b, except no warning will be printed.
+ """
+ return self.run_command('buildFile', buildfile, task, True)
def shutdown(self):
- self.cooker.shutdown(force=True)
- self.cooker.post_serve()
- self.cooker.unlockBitbake()
- self.logger.removeHandler(self._log_hdlr)
+ if self.server_connection:
+ self.run_command('clientComplete')
+ _server_connections.remove(self.server_connection)
+ bb.event.ui_queue = []
+ self.server_connection.terminate()
+ self.server_connection = None
-class TinfoilConfigParameters(ConfigParameters):
+ def _reconvert_type(self, obj, origtypename):
+ """
+ Convert an object back to the right type, in the case
+ that marshalling has changed it (especially with xmlrpc)
+ """
+ supported_types = {
+ 'set': set,
+ 'DataStoreConnectionHandle': bb.command.DataStoreConnectionHandle,
+ }
+
+ origtype = supported_types.get(origtypename, None)
+ if origtype is None:
+ raise Exception('Unsupported type "%s"' % origtypename)
+ if type(obj) == origtype:
+ newobj = obj
+ elif isinstance(obj, dict):
+ # New style class
+ newobj = origtype()
+ for k,v in obj.items():
+ setattr(newobj, k, v)
+ else:
+ # Assume we can coerce the type
+ newobj = origtype(obj)
+
+ if isinstance(newobj, bb.command.DataStoreConnectionHandle):
+ connector = TinfoilDataStoreConnector(self, newobj.dsindex)
+ newobj = bb.data.init()
+ newobj.setVar('_remote_data', connector)
+
+ return newobj
- def __init__(self, **options):
+
+class TinfoilConfigParameters(BitBakeConfigParameters):
+
+ def __init__(self, config_only, **options):
self.initial_options = options
- super(TinfoilConfigParameters, self).__init__()
+ # Apply some sane defaults
+ if not 'parse_only' in options:
+ self.initial_options['parse_only'] = not config_only
+ #if not 'status_only' in options:
+ # self.initial_options['status_only'] = config_only
+ if not 'ui' in options:
+ self.initial_options['ui'] = 'knotty'
+ if not 'argv' in options:
+ self.initial_options['argv'] = []
- def parseCommandLine(self, argv=sys.argv):
- class DummyOptions:
- def __init__(self, initial_options):
- for key, val in initial_options.items():
- setattr(self, key, val)
+ super(TinfoilConfigParameters, self).__init__()
- return DummyOptions(self.initial_options), None
+ def parseCommandLine(self, argv=None):
+ # We don't want any parameters parsed from the command line
+ opts = super(TinfoilConfigParameters, self).parseCommandLine([])
+ for key, val in self.initial_options.items():
+ setattr(opts[0], key, val)
+ return opts
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py b/import-layers/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py
index 3ddcb2ac6..e451c630d 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/ui/buildinfohelper.py
@@ -42,10 +42,12 @@ from orm.models import Variable, VariableHistory
from orm.models import Package, Package_File, Target_Installed_Package, Target_File
from orm.models import Task_Dependency, Package_Dependency
from orm.models import Recipe_Dependency, Provides
-from orm.models import Project, CustomImagePackage, CustomImageRecipe
+from orm.models import Project, CustomImagePackage
from orm.models import signal_runbuilds
from bldcontrol.models import BuildEnvironment, BuildRequest
+from bldcontrol.models import BRLayer
+from bldcontrol import bbcontroller
from bb.msg import BBLogFormatter as formatter
from django.db import models
@@ -361,11 +363,6 @@ class ORMWrapper(object):
def get_update_layer_version_object(self, build_obj, layer_obj, layer_version_information):
if isinstance(layer_obj, Layer_Version):
- # Special case the toaster-custom-images layer which is created
- # on the fly so don't update the values which may cause the layer
- # to be duplicated on a future get_or_create
- if layer_obj.layer.name == CustomImageRecipe.LAYER_NAME:
- return layer_obj
# We already found our layer version for this build so just
# update it with the new build information
logger.debug("We found our layer from toaster")
@@ -384,8 +381,8 @@ class ORMWrapper(object):
local_path=layer_version_information['local_path'],
)
- logger.info("created new historical layer version %d",
- layer_copy.pk)
+ logger.debug("Created new layer version %s for build history",
+ layer_copy.layer.name)
self.layer_version_built.append(layer_copy)
@@ -441,48 +438,33 @@ class ORMWrapper(object):
else:
br_id, be_id = brbe.split(":")
- # find layer by checkout path;
- from bldcontrol import bbcontroller
- bc = bbcontroller.getBuildEnvironmentController(pk = be_id)
-
- # we might have a race condition here, as the project layers may change between the build trigger and the actual build execution
- # but we can only match on the layer name, so the worst thing can happen is a mis-identification of the layer, not a total failure
-
- # note that this is different
- buildrequest = BuildRequest.objects.get(pk = br_id)
- for brl in buildrequest.brlayer_set.all():
- if brl.local_source_dir:
- localdirname = os.path.join(brl.local_source_dir,
- brl.dirpath)
- else:
- localdirname = os.path.join(bc.getGitCloneDirectory(brl.giturl, brl.commit), brl.dirpath)
- # we get a relative path, unless running in HEAD mode where the path is absolute
- if not localdirname.startswith("/"):
- localdirname = os.path.join(bc.be.sourcedir, localdirname)
- #logger.debug(1, "Localdirname %s lcal_path %s" % (localdirname, layer_information['local_path']))
- if localdirname.startswith(layer_information['local_path']):
- # If the build request came from toaster this field
- # should contain the information from the layer_version
- # That created this build request.
- if brl.layer_version:
- return brl.layer_version
-
- # This might be a local layer (i.e. no git info) so try
- # matching local_source_dir
- if brl.local_source_dir and brl.local_source_dir == layer_information["local_path"]:
- return brl.layer_version
-
- # we matched the BRLayer, but we need the layer_version that generated this BR; reverse of the Project.schedule_build()
- #logger.debug(1, "Matched %s to BRlayer %s" % (pformat(layer_information["local_path"]), localdirname))
-
- for pl in buildrequest.project.projectlayer_set.filter(layercommit__layer__name = brl.name):
- if pl.layercommit.layer.vcs_url == brl.giturl :
- layer = pl.layercommit.layer
- layer.save()
- return layer
-
- raise NotExisting("Unidentified layer %s" % pformat(layer_information))
+ # Find the layer version by matching the layer event information
+ # against the metadata we have in Toaster
+ try:
+ br_layer = BRLayer.objects.get(req=br_id,
+ name=layer_information['name'])
+ return br_layer.layer_version
+ except (BRLayer.MultipleObjectsReturned, BRLayer.DoesNotExist):
+ # There are multiple of the same layer name or the name
+ # hasn't been determined by the toaster.bbclass layer
+ # so let's filter by the local_path
+ bc = bbcontroller.getBuildEnvironmentController(pk=be_id)
+ for br_layer in BRLayer.objects.filter(req=br_id):
+ if br_layer.giturl and \
+ layer_information['local_path'].endswith(
+ bc.getGitCloneDirectory(br_layer.giturl,
+ br_layer.commit)):
+ return br_layer.layer_version
+
+ if br_layer.local_source_dir == \
+ layer_information['local_path']:
+ return br_layer.layer_version
+
+ # We've reached the end of our search and couldn't find the layer
+ # we can continue but some data may be missing
+ raise NotExisting("Unidentified layer %s" %
+ pformat(layer_information))
def save_target_file_information(self, build_obj, target_obj, filedata):
assert isinstance(build_obj, Build)
@@ -876,6 +858,12 @@ class MockEvent(object):
self.pathname = None
self.lineno = None
+ def getMessage(self):
+ """
+ Simulate LogRecord message return
+ """
+ return self.msg
+
class BuildInfoHelper(object):
""" This class gathers the build information from the server and sends it
@@ -983,9 +971,10 @@ class BuildInfoHelper(object):
return task_information
def _get_layer_version_for_dependency(self, pathRE):
- """ Returns the layer in the toaster db that has a full regex match to the pathRE.
- pathRE - the layer path passed as a regex in the event. It is created in
- cooker.py as a collection for the layer priorities.
+ """ Returns the layer in the toaster db that has a full regex
+ match to the pathRE. pathRE - the layer path passed as a regex in the
+ event. It is created in cooker.py as a collection for the layer
+ priorities.
"""
self._ensure_build()
@@ -993,19 +982,31 @@ class BuildInfoHelper(object):
assert isinstance(layer_version, Layer_Version)
return len(layer_version.local_path)
- # we don't care if we match the trailing slashes
- p = re.compile(re.sub("/[^/]*?$","",pathRE))
- # Heuristics: we always match recipe to the deepest layer path in the discovered layers
- for lvo in sorted(self.orm_wrapper.layer_version_objects, reverse=True, key=_sort_longest_path):
- if p.fullmatch(lvo.local_path):
+ # Our paths don't append a trailing slash
+ if pathRE.endswith("/"):
+ pathRE = pathRE[:-1]
+
+ p = re.compile(pathRE)
+ path=re.sub(r'[$^]',r'',pathRE)
+ # Heuristics: we always match recipe to the deepest layer path in
+ # the discovered layers
+ for lvo in sorted(self.orm_wrapper.layer_version_objects,
+ reverse=True, key=_sort_longest_path):
+ if p.fullmatch(os.path.abspath(lvo.local_path)):
return lvo
if lvo.layer.local_source_dir:
- if p.fullmatch(lvo.layer.local_source_dir):
+ if p.fullmatch(os.path.abspath(lvo.layer.local_source_dir)):
return lvo
- #if we get here, we didn't read layers correctly; dump whatever information we have on the error log
- logger.warning("Could not match layer dependency for path %s : %s", path, self.orm_wrapper.layer_version_objects)
-
+ if 0 == path.find(lvo.local_path):
+ # sub-layer path inside existing layer
+ return lvo
+ # if we get here, we didn't read layers correctly;
+ # dump whatever information we have on the error log
+ logger.warning("Could not match layer dependency for path %s : %s",
+ pathRE,
+ self.orm_wrapper.layer_version_objects)
+ return None
def _get_layer_version_for_path(self, path):
self._ensure_build()
@@ -1268,6 +1269,14 @@ class BuildInfoHelper(object):
candidates = [x for x in self.internal_state['taskdata'].keys() if x.endswith(identifier)]
if len(candidates) == 1:
identifier = candidates[0]
+ elif len(candidates) > 1 and hasattr(event,'_package'):
+ if 'native-' in event._package:
+ identifier = 'native:' + identifier
+ if 'nativesdk-' in event._package:
+ identifier = 'nativesdk:' + identifier
+ candidates = [x for x in self.internal_state['taskdata'].keys() if x.endswith(identifier)]
+ if len(candidates) == 1:
+ identifier = candidates[0]
assert identifier in self.internal_state['taskdata']
identifierlist = identifier.split(":")
@@ -1398,9 +1407,9 @@ class BuildInfoHelper(object):
for lv in event._depgraph['layer-priorities']:
(_, path, _, priority) = lv
layer_version_obj = self._get_layer_version_for_dependency(path)
- assert layer_version_obj is not None
- layer_version_obj.priority = priority
- layer_version_obj.save()
+ if layer_version_obj:
+ layer_version_obj.priority = priority
+ layer_version_obj.save()
# save recipe information
self.internal_state['recipes'] = {}
@@ -1665,6 +1674,36 @@ class BuildInfoHelper(object):
break
return endswith
+ def scan_task_artifacts(self, event):
+ """
+ The 'TaskArtifacts' event passes the manifest file content for the
+ tasks 'do_deploy', 'do_image_complete', 'do_populate_sdk', and
+ 'do_populate_sdk_ext'. The first two will be implemented later.
+ """
+ task_vars = BuildInfoHelper._get_data_from_event(event)
+ task_name = task_vars['task'][task_vars['task'].find(':')+1:]
+ task_artifacts = task_vars['artifacts']
+
+ if task_name in ['do_populate_sdk', 'do_populate_sdk_ext']:
+ targets = [target for target in self.internal_state['targets'] \
+ if target.task == task_name[3:]]
+ if not targets:
+ logger.warning("scan_task_artifacts: SDK targets not found: %s\n", task_name)
+ return
+ for artifact_path in task_artifacts:
+ if not os.path.isfile(artifact_path):
+ logger.warning("scan_task_artifacts: artifact file not found: %s\n", artifact_path)
+ continue
+ for target in targets:
+ # don't record the file if it's already been added
+ # to this target
+ matching_files = TargetSDKFile.objects.filter(
+ target=target, file_name=artifact_path)
+ if matching_files.count() == 0:
+ artifact_size = os.stat(artifact_path).st_size
+ self.orm_wrapper.save_target_sdk_file(
+ target, artifact_path, artifact_size)
+
def _get_image_files(self, deploy_dir_image, image_name, image_file_extensions):
"""
Find files in deploy_dir_image whose basename starts with the
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/ui/knotty.py b/import-layers/yocto-poky/bitbake/lib/bb/ui/knotty.py
index 948f52769..82aa7c464 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/ui/knotty.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/ui/knotty.py
@@ -75,10 +75,8 @@ class BBProgress(progressbar.ProgressBar):
extrastr = str(extra)
if extrastr[0] != ' ':
extrastr = ' ' + extrastr
- if extrastr[-1] != ' ':
- extrastr += ' '
else:
- extrastr = ' '
+ extrastr = ''
self.widgets[self.extrapos] = extrastr
def _need_update(self):
@@ -284,7 +282,7 @@ class TerminalFilter(object):
content = self.main_progress.update(progress)
print('')
lines = 1 + int(len(content) / (self.columns + 1))
- if not self.quiet:
+ if self.quiet == 0:
for tasknum, task in enumerate(tasks[:(self.rows - 2)]):
if isinstance(task, tuple):
pbar, progress, rate, start_time = task
@@ -312,7 +310,7 @@ class TerminalFilter(object):
fd = sys.stdin.fileno()
self.termios.tcsetattr(fd, self.termios.TCSADRAIN, self.stdinbackup)
-def _log_settings_from_server(server):
+def _log_settings_from_server(server, observe_only):
# Get values of variables which control our output
includelogs, error = server.runCommand(["getVariable", "BBINCLUDELOGS"])
if error:
@@ -322,7 +320,11 @@ def _log_settings_from_server(server):
if error:
logger.error("Unable to get the value of BBINCLUDELOGS_LINES variable: %s" % error)
raise BaseException(error)
- consolelogfile, error = server.runCommand(["getSetVariable", "BB_CONSOLELOG"])
+ if observe_only:
+ cmd = 'getVariable'
+ else:
+ cmd = 'getSetVariable'
+ consolelogfile, error = server.runCommand([cmd, "BB_CONSOLELOG"])
if error:
logger.error("Unable to get the value of BB_CONSOLELOG variable: %s" % error)
raise BaseException(error)
@@ -340,7 +342,7 @@ _evt_list = [ "bb.runqueue.runQueueExitWait", "bb.event.LogExecTTY", "logging.Lo
def main(server, eventHandler, params, tf = TerminalFilter):
- includelogs, loglines, consolelogfile = _log_settings_from_server(server)
+ includelogs, loglines, consolelogfile = _log_settings_from_server(server, params.observe_only)
if sys.stdin.isatty() and sys.stdout.isatty():
log_exec_tty = True
@@ -353,10 +355,13 @@ def main(server, eventHandler, params, tf = TerminalFilter):
errconsole = logging.StreamHandler(sys.stderr)
format_str = "%(levelname)s: %(message)s"
format = bb.msg.BBLogFormatter(format_str)
- if params.options.quiet:
- bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut, bb.msg.BBLogFormatter.WARNING)
+ if params.options.quiet == 0:
+ forcelevel = None
+ elif params.options.quiet > 2:
+ forcelevel = bb.msg.BBLogFormatter.ERROR
else:
- bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut)
+ forcelevel = bb.msg.BBLogFormatter.WARNING
+ bb.msg.addDefaultlogFilter(console, bb.msg.BBLogFilterStdOut, forcelevel)
bb.msg.addDefaultlogFilter(errconsole, bb.msg.BBLogFilterStdErr)
console.setFormatter(format)
errconsole.setFormatter(format)
@@ -506,35 +511,47 @@ def main(server, eventHandler, params, tf = TerminalFilter):
logger.info(event._message)
continue
if isinstance(event, bb.event.ParseStarted):
+ if params.options.quiet > 1:
+ continue
if event.total == 0:
continue
parseprogress = new_progress("Parsing recipes", event.total).start()
continue
if isinstance(event, bb.event.ParseProgress):
+ if params.options.quiet > 1:
+ continue
if parseprogress:
parseprogress.update(event.current)
else:
bb.warn("Got ParseProgress event for parsing that never started?")
continue
if isinstance(event, bb.event.ParseCompleted):
+ if params.options.quiet > 1:
+ continue
if not parseprogress:
continue
parseprogress.finish()
pasreprogress = None
- if not params.options.quiet:
+ if params.options.quiet == 0:
print(("Parsing of %d .bb files complete (%d cached, %d parsed). %d targets, %d skipped, %d masked, %d errors."
% ( event.total, event.cached, event.parsed, event.virtuals, event.skipped, event.masked, event.errors)))
continue
if isinstance(event, bb.event.CacheLoadStarted):
+ if params.options.quiet > 1:
+ continue
cacheprogress = new_progress("Loading cache", event.total).start()
continue
if isinstance(event, bb.event.CacheLoadProgress):
+ if params.options.quiet > 1:
+ continue
cacheprogress.update(event.current)
continue
if isinstance(event, bb.event.CacheLoadCompleted):
+ if params.options.quiet > 1:
+ continue
cacheprogress.finish()
- if not params.options.quiet:
+ if params.options.quiet == 0:
print("Loaded %d entries from dependency cache." % event.num_entries)
continue
@@ -620,16 +637,22 @@ def main(server, eventHandler, params, tf = TerminalFilter):
continue
if isinstance(event, bb.event.ProcessStarted):
+ if params.options.quiet > 1:
+ continue
parseprogress = new_progress(event.processname, event.total)
parseprogress.start(False)
continue
if isinstance(event, bb.event.ProcessProgress):
+ if params.options.quiet > 1:
+ continue
if parseprogress:
parseprogress.update(event.progress)
else:
bb.warn("Got ProcessProgress event for someting that never started?")
continue
if isinstance(event, bb.event.ProcessFinished):
+ if params.options.quiet > 1:
+ continue
if parseprogress:
parseprogress.finish()
parseprogress = None
@@ -647,6 +670,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
bb.event.OperationCompleted,
bb.event.OperationProgress,
bb.event.DiskFull,
+ bb.event.HeartbeatEvent,
bb.build.TaskProgress)):
continue
@@ -700,7 +724,7 @@ def main(server, eventHandler, params, tf = TerminalFilter):
if return_value and errors:
summary += pluralise("\nSummary: There was %s ERROR message shown, returning a non-zero exit code.",
"\nSummary: There were %s ERROR messages shown, returning a non-zero exit code.", errors)
- if summary and not params.options.quiet:
+ if summary and params.options.quiet == 0:
print(summary)
if interrupted:
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/ui/ncurses.py b/import-layers/yocto-poky/bitbake/lib/bb/ui/ncurses.py
index d81e4138b..ca845a32a 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/ui/ncurses.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/ui/ncurses.py
@@ -297,7 +297,7 @@ class NCursesUI:
# bb.error("log data follows (%s)" % logfile)
# number_of_lines = data.getVar("BBINCLUDELOGS_LINES", d)
# if number_of_lines:
-# subprocess.call('tail -n%s %s' % (number_of_lines, logfile), shell=True)
+# subprocess.check_call('tail -n%s %s' % (number_of_lines, logfile), shell=True)
# else:
# f = open(logfile, "r")
# while True:
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/ui/depexp.py b/import-layers/yocto-poky/bitbake/lib/bb/ui/taskexp.py
index d879e04c0..9d14ecefa 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/ui/depexp.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/ui/taskexp.py
@@ -78,7 +78,7 @@ class PackageReverseDepView(Gtk.TreeView):
class DepExplorer(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
- self.set_title("Dependency Explorer")
+ self.set_title("Task Dependency Explorer")
self.set_default_size(500, 500)
self.connect("delete-event", Gtk.main_quit)
@@ -106,30 +106,21 @@ class DepExplorer(Gtk.Window):
box = Gtk.VBox(homogeneous=True, spacing=4)
- # Runtime Depends
+ # Task Depends
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled.set_shadow_type(Gtk.ShadowType.IN)
- self.rdep_treeview = PackageDepView(self.depends_model, TYPE_RDEP, "Runtime Depends")
- self.rdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
- scrolled.add(self.rdep_treeview)
- box.add(scrolled)
-
- # Build Depends
- scrolled = Gtk.ScrolledWindow()
- scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
- scrolled.set_shadow_type(Gtk.ShadowType.IN)
- self.dep_treeview = PackageDepView(self.depends_model, TYPE_DEP, "Build Depends")
+ self.dep_treeview = PackageDepView(self.depends_model, TYPE_DEP, "Dependencies")
self.dep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PACKAGE)
scrolled.add(self.dep_treeview)
box.add(scrolled)
pane.add2(box)
- # Reverse Depends
+ # Reverse Task Depends
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled.set_shadow_type(Gtk.ShadowType.IN)
- self.revdep_treeview = PackageReverseDepView(self.depends_model, "Reverse Depends")
+ self.revdep_treeview = PackageReverseDepView(self.depends_model, "Dependent Tasks")
self.revdep_treeview.connect("row-activated", self.on_package_activated, COL_DEP_PARENT)
scrolled.add(self.revdep_treeview)
box.add(scrolled)
@@ -160,22 +151,15 @@ class DepExplorer(Gtk.Window):
current_package = None
else:
current_package = model.get_value(it, COL_PKG_NAME)
- self.rdep_treeview.set_current_package(current_package)
self.dep_treeview.set_current_package(current_package)
self.revdep_treeview.set_current_package(current_package)
def parse(self, depgraph):
- for package in depgraph["pn"]:
- self.pkg_model.insert(0, (package,))
-
- for package in depgraph["depends"]:
- for depend in depgraph["depends"][package]:
- self.depends_model.insert (0, (TYPE_DEP, package, depend))
-
- for package in depgraph["rdepends-pn"]:
- for rdepend in depgraph["rdepends-pn"][package]:
- self.depends_model.insert (0, (TYPE_RDEP, package, rdepend))
+ for task in depgraph["tdepends"]:
+ self.pkg_model.insert(0, (task,))
+ for depend in depgraph["tdepends"][task]:
+ self.depends_model.insert (0, (TYPE_DEP, task, depend))
class gtkthread(threading.Thread):
@@ -313,7 +297,7 @@ def main(server, eventHandler, params):
extra = ". Close matches:\n %s" % '\n '.join(event._close_matches)
if event._dependees:
- print("Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s" % r, event._item, ", ".join(event._dependees), r, extra)
+ print("Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s" % (r, event._item, ", ".join(event._dependees), r, extra))
else:
print("Nothing %sPROVIDES '%s'%s" % (r, event._item, extra))
if event._reasons:
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/ui/toasterui.py b/import-layers/yocto-poky/bitbake/lib/bb/ui/toasterui.py
index 9808f6bc8..71f04fa5c 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/ui/toasterui.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/ui/toasterui.py
@@ -168,6 +168,9 @@ def main(server, eventHandler, params):
logger.warning("buildhistory is not enabled. Please enable INHERIT += \"buildhistory\" to see image details.")
build_history_enabled = False
+ if not "buildstats" in inheritlist.split(" "):
+ logger.warning("buildstats is not enabled. Please enable INHERIT += \"buildstats\" to generate build statistics.")
+
if not params.observe_only:
params.updateFromServer(server)
params.updateToServer(server, os.environ.copy())
@@ -233,6 +236,9 @@ def main(server, eventHandler, params):
# pylint: disable=protected-access
# the code will look into the protected variables of the event; no easy way around this
+ if isinstance(event, bb.event.HeartbeatEvent):
+ continue
+
if isinstance(event, bb.event.ParseStarted):
if not (build_log and build_log_file_path):
build_log, build_log_file_path = _open_build_log(log_dir)
@@ -432,9 +438,7 @@ def main(server, eventHandler, params):
elif event.type == "SetBRBE":
buildinfohelper.brbe = buildinfohelper._get_data_from_event(event)
elif event.type == "TaskArtifacts":
- # not implemented yet
- # see https://bugzilla.yoctoproject.org/show_bug.cgi?id=10283 for details
- pass
+ buildinfohelper.scan_task_artifacts(event)
elif event.type == "OSErrorException":
logger.error(event)
else:
diff --git a/import-layers/yocto-poky/bitbake/lib/bb/utils.py b/import-layers/yocto-poky/bitbake/lib/bb/utils.py
index 16fc9db25..6a44db57d 100644
--- a/import-layers/yocto-poky/bitbake/lib/bb/utils.py
+++ b/import-layers/yocto-poky/bitbake/lib/bb/utils.py
@@ -523,12 +523,8 @@ def md5_file(filename):
"""
Return the hex string representation of the MD5 checksum of filename.
"""
- try:
- import hashlib
- m = hashlib.md5()
- except ImportError:
- import md5
- m = md5.new()
+ import hashlib
+ m = hashlib.md5()
with open(filename, "rb") as f:
for line in f:
@@ -538,14 +534,9 @@ def md5_file(filename):
def sha256_file(filename):
"""
Return the hex string representation of the 256-bit SHA checksum of
- filename. On Python 2.4 this will return None, so callers will need to
- handle that by either skipping SHA checks, or running a standalone sha256sum
- binary.
+ filename.
"""
- try:
- import hashlib
- except ImportError:
- return None
+ import hashlib
s = hashlib.sha256()
with open(filename, "rb") as f:
@@ -557,10 +548,7 @@ def sha1_file(filename):
"""
Return the hex string representation of the SHA1 checksum of the filename
"""
- try:
- import hashlib
- except ImportError:
- return None
+ import hashlib
s = hashlib.sha1()
with open(filename, "rb") as f:
@@ -665,7 +653,7 @@ def build_environment(d):
for var in bb.data.keys(d):
export = d.getVarFlag(var, "export", False)
if export:
- os.environ[var] = d.getVar(var, True) or ""
+ os.environ[var] = d.getVar(var) or ""
def _check_unsafe_delete_path(path):
"""
@@ -692,7 +680,7 @@ def remove(path, recurse=False):
if _check_unsafe_delete_path(path):
raise Exception('bb.utils.remove: called with dangerous path "%s" and recurse=True, refusing to delete!' % path)
# shutil.rmtree(name) would be ideal but its too slow
- subprocess.call(['rm', '-rf'] + glob.glob(path))
+ subprocess.check_call(['rm', '-rf'] + glob.glob(path))
return
for name in glob.glob(path):
try:
@@ -911,11 +899,20 @@ def copyfile(src, dest, newmtime = None, sstat = None):
newmtime = sstat[stat.ST_MTIME]
return newmtime
-def which(path, item, direction = 0, history = False):
+def which(path, item, direction = 0, history = False, executable=False):
"""
- Locate a file in a PATH
+ Locate `item` in the list of paths `path` (colon separated string like $PATH).
+ If `direction` is non-zero then the list is reversed.
+ If `history` is True then the list of candidates also returned as result,history.
+ If `executable` is True then the candidate has to be an executable file,
+ otherwise the candidate simply has to exist.
"""
+ if executable:
+ is_candidate = lambda p: os.path.isfile(p) and os.access(p, os.X_OK)
+ else:
+ is_candidate = lambda p: os.path.exists(p)
+
hist = []
paths = (path or "").split(':')
if direction != 0:
@@ -924,7 +921,7 @@ def which(path, item, direction = 0, history = False):
for p in paths:
next = os.path.join(p, item)
hist.append(next)
- if os.path.exists(next):
+ if is_candidate(next):
if not os.path.isabs(next):
next = os.path.abspath(next)
if history:
@@ -953,7 +950,7 @@ def contains(variable, checkvalues, truevalue, falsevalue, d):
Arguments:
variable -- the variable name. This will be fetched and expanded (using
- d.getVar(variable, True)) and then split into a set().
+ d.getVar(variable)) and then split into a set().
checkvalues -- if this is a string it is split on whitespace into a set(),
otherwise coerced directly into a set().
@@ -966,7 +963,7 @@ def contains(variable, checkvalues, truevalue, falsevalue, d):
d -- the data store.
"""
- val = d.getVar(variable, True)
+ val = d.getVar(variable)
if not val:
return falsevalue
val = set(val.split())
@@ -979,7 +976,7 @@ def contains(variable, checkvalues, truevalue, falsevalue, d):
return falsevalue
def contains_any(variable, checkvalues, truevalue, falsevalue, d):
- val = d.getVar(variable, True)
+ val = d.getVar(variable)
if not val:
return falsevalue
val = set(val.split())
@@ -991,6 +988,30 @@ def contains_any(variable, checkvalues, truevalue, falsevalue, d):
return truevalue
return falsevalue
+def filter(variable, checkvalues, d):
+ """Return all words in the variable that are present in the checkvalues.
+
+ Arguments:
+
+ variable -- the variable name. This will be fetched and expanded (using
+ d.getVar(variable)) and then split into a set().
+
+ checkvalues -- if this is a string it is split on whitespace into a set(),
+ otherwise coerced directly into a set().
+
+ d -- the data store.
+ """
+
+ val = d.getVar(variable)
+ if not val:
+ return ''
+ val = set(val.split())
+ if isinstance(checkvalues, str):
+ checkvalues = set(checkvalues.split())
+ else:
+ checkvalues = set(checkvalues)
+ return ' '.join(sorted(checkvalues & val))
+
def cpu_count():
return multiprocessing.cpu_count()
@@ -1378,10 +1399,10 @@ def edit_bblayers_conf(bblayers_conf, add, remove):
def get_file_layer(filename, d):
"""Determine the collection (as defined by a layer's layer.conf file) containing the specified file"""
- collections = (d.getVar('BBFILE_COLLECTIONS', True) or '').split()
+ collections = (d.getVar('BBFILE_COLLECTIONS') or '').split()
collection_res = {}
for collection in collections:
- collection_res[collection] = d.getVar('BBFILE_PATTERN_%s' % collection, True) or ''
+ collection_res[collection] = d.getVar('BBFILE_PATTERN_%s' % collection) or ''
def path_to_layer(path):
# Use longest path so we handle nested layers
@@ -1394,7 +1415,7 @@ def get_file_layer(filename, d):
return match
result = None
- bbfiles = (d.getVar('BBFILES', True) or '').split()
+ bbfiles = (d.getVar('BBFILES') or '').split()
bbfilesmatch = False
for bbfilesentry in bbfiles:
if fnmatch.fnmatch(filename, bbfilesentry):
@@ -1471,7 +1492,7 @@ def export_proxies(d):
if v in os.environ.keys():
exported = True
else:
- v_proxy = d.getVar(v, True)
+ v_proxy = d.getVar(v)
if v_proxy is not None:
os.environ[v] = v_proxy
exported = True
@@ -1503,3 +1524,14 @@ def load_plugins(logger, plugins, pluginpath):
plugins.append(obj or plugin)
else:
plugins.append(plugin)
+
+
+class LogCatcher(logging.Handler):
+ """Logging handler for collecting logged messages so you can check them later"""
+ def __init__(self):
+ self.messages = []
+ logging.Handler.__init__(self, logging.WARNING)
+ def emit(self, record):
+ self.messages.append(bb.build.logformatter.format(record))
+ def contains(self, message):
+ return (message in self.messages)
OpenPOWER on IntegriCloud