summaryrefslogtreecommitdiffstats
path: root/meta/lib/oe
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oe')
-rw-r--r--meta/lib/oe/copy_buildsystem.py102
-rw-r--r--meta/lib/oe/image.py24
-rw-r--r--meta/lib/oe/license.py3
-rw-r--r--meta/lib/oe/lsb.py18
-rw-r--r--meta/lib/oe/package.py28
-rw-r--r--meta/lib/oe/package_manager.py73
-rw-r--r--meta/lib/oe/patch.py161
-rw-r--r--meta/lib/oe/recipeutils.py279
-rw-r--r--meta/lib/oe/rootfs.py148
-rw-r--r--meta/lib/oe/sdk.py2
-rw-r--r--meta/lib/oe/sstatesig.py13
-rw-r--r--meta/lib/oe/terminal.py67
-rw-r--r--meta/lib/oe/utils.py16
13 files changed, 828 insertions, 106 deletions
diff --git a/meta/lib/oe/copy_buildsystem.py b/meta/lib/oe/copy_buildsystem.py
new file mode 100644
index 0000000000..cf7fada7f0
--- /dev/null
+++ b/meta/lib/oe/copy_buildsystem.py
@@ -0,0 +1,102 @@
+# This class should provide easy access to the different aspects of the
+# buildsystem such as layers, bitbake location, etc.
+import stat
+import shutil
+
+def _smart_copy(src, dest):
+ # smart_copy will choose the correct function depending on whether the
+ # source is a file or a directory.
+ mode = os.stat(src).st_mode
+ if stat.S_ISDIR(mode):
+ shutil.copytree(src, dest, symlinks=True)
+ else:
+ shutil.copyfile(src, dest)
+ shutil.copymode(src, dest)
+
+class BuildSystem(object):
+ def __init__(self, d):
+ self.d = d
+ self.layerdirs = d.getVar('BBLAYERS', True).split()
+
+ def copy_bitbake_and_layers(self, destdir):
+ # Copy in all metadata layers + bitbake (as repositories)
+ layers_copied = []
+ bb.utils.mkdirhier(destdir)
+ layers = list(self.layerdirs)
+
+ corebase = self.d.getVar('COREBASE', True)
+ layers.append(corebase)
+
+ corebase_files = self.d.getVar('COREBASE_FILES', True).split()
+
+ # bitbake belongs in corebase so make sure it goes there
+ if "bitbake" not in corebase_files:
+ corebase_files.append("bitbake")
+ corebase_files = [corebase + '/' +x for x in corebase_files]
+
+ for layer in layers:
+ layerconf = os.path.join(layer, 'conf', 'layer.conf')
+ if os.path.exists(layerconf):
+ with open(layerconf, 'r') as f:
+ if f.readline().startswith("# ### workspace layer auto-generated by devtool ###"):
+ bb.warn("Skipping local workspace layer %s" % layer)
+ continue
+
+ # If the layer was already under corebase, leave it there
+ # since layers such as meta have issues when moved.
+ layerdestpath = destdir
+ if corebase == os.path.dirname(layer):
+ layerdestpath += '/' + os.path.basename(corebase)
+ layerdestpath += '/' + os.path.basename(layer)
+
+ layer_relative = os.path.relpath(layerdestpath,
+ destdir)
+ layers_copied.append(layer_relative)
+
+ # Treat corebase as special since it typically will contain
+ # build directories or other custom items.
+ if corebase == layer:
+ bb.utils.mkdirhier(layerdestpath)
+ for f in corebase_files:
+ f_basename = os.path.basename(f)
+ destname = os.path.join(layerdestpath, f_basename)
+ _smart_copy(f, destname)
+ else:
+ if os.path.exists(layerdestpath):
+ bb.note("Skipping layer %s, already handled" % layer)
+ else:
+ _smart_copy(layer, layerdestpath)
+
+ return layers_copied
+
+def generate_locked_sigs(sigfile, d):
+ bb.utils.mkdirhier(os.path.dirname(sigfile))
+ depd = d.getVar('BB_TASKDEPDATA', True)
+ tasks = ['%s.%s' % (v[2], v[1]) for v in depd.itervalues()]
+ bb.parse.siggen.dump_lockedsigs(sigfile, tasks)
+
+def prune_lockedsigs(allowed_tasks, excluded_targets, lockedsigs, pruned_output):
+ with open(lockedsigs, 'r') as infile:
+ bb.utils.mkdirhier(os.path.dirname(pruned_output))
+ with open(pruned_output, 'w') as f:
+ invalue = False
+ for line in infile:
+ if invalue:
+ if line.endswith('\\\n'):
+ splitval = line.strip().split(':')
+ if splitval[1] in allowed_tasks and not splitval[0] in excluded_targets:
+ f.write(line)
+ else:
+ f.write(line)
+ invalue = False
+ elif line.startswith('SIGGEN_LOCKEDSIGS'):
+ invalue = True
+ f.write(line)
+
+def create_locked_sstate_cache(lockedsigs, input_sstate_cache, output_sstate_cache, d, fixedlsbstring=""):
+ bb.note('Generating sstate-cache...')
+
+ bb.process.run("gen-lockedsig-cache %s %s %s" % (lockedsigs, input_sstate_cache, output_sstate_cache))
+ if fixedlsbstring:
+ os.rename(output_sstate_cache + '/' + d.getVar('NATIVELSBSTRING', True),
+ output_sstate_cache + '/' + fixedlsbstring)
diff --git a/meta/lib/oe/image.py b/meta/lib/oe/image.py
index 7e080b00dd..0ce303d570 100644
--- a/meta/lib/oe/image.py
+++ b/meta/lib/oe/image.py
@@ -48,11 +48,13 @@ class ImageDepGraph(object):
graph = dict()
def add_node(node):
+ base_type = self._image_base_type(node)
deps = (self.d.getVar('IMAGE_TYPEDEP_' + node, True) or "")
- if deps != "":
+ base_deps = (self.d.getVar('IMAGE_TYPEDEP_' + base_type, True) or "")
+ if deps != "" or base_deps != "":
graph[node] = deps
- for dep in deps.split():
+ for dep in deps.split() + base_deps.split():
if not dep in graph:
add_node(dep)
else:
@@ -72,6 +74,18 @@ class ImageDepGraph(object):
for item in remove_list:
self.graph.pop(item, None)
+ def _image_base_type(self, type):
+ ctypes = self.d.getVar('COMPRESSIONTYPES', True).split()
+ if type in ["vmdk", "live", "iso", "hddimg"]:
+ type = "ext3"
+ basetype = type
+ for ctype in ctypes:
+ if type.endswith("." + ctype):
+ basetype = type[:-len("." + ctype)]
+ break
+
+ return basetype
+
def _compute_dependencies(self):
"""
returns dict object of nodes with [no_of_depends_on, no_of_depended_by]
@@ -282,7 +296,11 @@ class Image(ImageDepGraph):
bb.data.update_data(localdata)
localdata.setVar('type', type)
- cmds.append("\t" + localdata.getVar("IMAGE_CMD", True))
+ image_cmd = localdata.getVar("IMAGE_CMD", True)
+ if image_cmd:
+ cmds.append("\t" + image_cmd)
+ else:
+ bb.fatal("No IMAGE_CMD defined for IMAGE_FSTYPES entry '%s' - possibly invalid type name or missing support class" % type)
cmds.append(localdata.expand("\tcd ${DEPLOY_DIR_IMAGE}"))
if type in cimages:
diff --git a/meta/lib/oe/license.py b/meta/lib/oe/license.py
index 340da61102..31ca15b574 100644
--- a/meta/lib/oe/license.py
+++ b/meta/lib/oe/license.py
@@ -25,7 +25,8 @@ class InvalidLicense(LicenseError):
def __str__(self):
return "invalid characters in license '%s'" % self.license
-license_operator = re.compile('([&|() ])')
+license_operator_chars = '&|() '
+license_operator = re.compile('([' + license_operator_chars + '])')
license_pattern = re.compile('[a-zA-Z0-9.+_\-]+$')
class LicenseVisitor(ast.NodeVisitor):
diff --git a/meta/lib/oe/lsb.py b/meta/lib/oe/lsb.py
index b53f361035..ddfe71b6b5 100644
--- a/meta/lib/oe/lsb.py
+++ b/meta/lib/oe/lsb.py
@@ -9,6 +9,7 @@ def release_dict():
data = {}
for line in output.splitlines():
+ if line.startswith("-e"): line = line[3:]
try:
key, value = line.split(":\t", 1)
except ValueError:
@@ -36,14 +37,6 @@ def release_dict_file():
if match:
data['DISTRIB_ID'] = match.group(1)
data['DISTRIB_RELEASE'] = match.group(2)
- elif os.path.exists('/etc/SuSE-release'):
- data = {}
- data['DISTRIB_ID'] = 'SUSE LINUX'
- with open('/etc/SuSE-release') as f:
- for line in f:
- if line.startswith('VERSION = '):
- data['DISTRIB_RELEASE'] = line[10:].rstrip()
- break
elif os.path.exists('/etc/os-release'):
data = {}
with open('/etc/os-release') as f:
@@ -52,6 +45,15 @@ def release_dict_file():
data['DISTRIB_ID'] = line[5:].rstrip().strip('"')
if line.startswith('VERSION_ID='):
data['DISTRIB_RELEASE'] = line[11:].rstrip().strip('"')
+ elif os.path.exists('/etc/SuSE-release'):
+ data = {}
+ data['DISTRIB_ID'] = 'SUSE LINUX'
+ with open('/etc/SuSE-release') as f:
+ for line in f:
+ if line.startswith('VERSION = '):
+ data['DISTRIB_RELEASE'] = line[10:].rstrip()
+ break
+
except IOError:
return None
return data
diff --git a/meta/lib/oe/package.py b/meta/lib/oe/package.py
index a26a631837..8bc56c6e88 100644
--- a/meta/lib/oe/package.py
+++ b/meta/lib/oe/package.py
@@ -31,7 +31,7 @@ def runstrip(arg):
extraflags = "--remove-section=.comment --remove-section=.note"
# Use mv to break hardlinks
- stripcmd = "'%s' %s '%s' -o '%s.tmp' && mv '%s.tmp' '%s'" % (strip, extraflags, file, file, file, file)
+ stripcmd = "'%s' %s '%s' -o '%s.tmp' && chown --reference='%s' '%s.tmp' && mv '%s.tmp' '%s'" % (strip, extraflags, file, file, file, file, file, file)
bb.debug(1, "runstrip: %s" % stripcmd)
ret = subprocess.call(stripcmd, shell=True)
@@ -98,3 +98,29 @@ def filedeprunner(arg):
raise e
return (pkg, provides, requires)
+
+
+def read_shlib_providers(d):
+ import re
+
+ shlib_provider = {}
+ shlibs_dirs = d.getVar('SHLIBSDIRS', True).split()
+ list_re = re.compile('^(.*)\.list$')
+ # Go from least to most specific since the last one found wins
+ for dir in reversed(shlibs_dirs):
+ bb.debug(2, "Reading shlib providers in %s" % (dir))
+ if not os.path.exists(dir):
+ continue
+ for file in os.listdir(dir):
+ m = list_re.match(file)
+ if m:
+ dep_pkg = m.group(1)
+ fd = open(os.path.join(dir, file))
+ lines = fd.readlines()
+ fd.close()
+ for l in lines:
+ s = l.strip().split(":")
+ if s[0] not in shlib_provider:
+ shlib_provider[s[0]] = {}
+ shlib_provider[s[0]][s[1]] = (dep_pkg, s[2])
+ return shlib_provider
diff --git a/meta/lib/oe/package_manager.py b/meta/lib/oe/package_manager.py
index 411b9d6309..0460415699 100644
--- a/meta/lib/oe/package_manager.py
+++ b/meta/lib/oe/package_manager.py
@@ -111,11 +111,15 @@ class RpmIndexer(Indexer):
index_cmds = []
rpm_dirs_found = False
for arch in archs:
+ dbpath = os.path.join(self.d.getVar('WORKDIR', True), 'rpmdb', arch)
+ if os.path.exists(dbpath):
+ bb.utils.remove(dbpath, True)
arch_dir = os.path.join(self.deploy_dir, arch)
if not os.path.isdir(arch_dir):
continue
- index_cmds.append("%s --update -q %s" % (rpm_createrepo, arch_dir))
+ index_cmds.append("%s --dbpath %s --update -q %s" % \
+ (rpm_createrepo, dbpath, arch_dir))
rpm_dirs_found = True
@@ -169,7 +173,35 @@ class OpkgIndexer(Indexer):
class DpkgIndexer(Indexer):
+ def _create_configs(self):
+ bb.utils.mkdirhier(self.apt_conf_dir)
+ bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
+ bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
+ bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
+
+ with open(os.path.join(self.apt_conf_dir, "preferences"),
+ "w") as prefs_file:
+ pass
+ with open(os.path.join(self.apt_conf_dir, "sources.list"),
+ "w+") as sources_file:
+ pass
+
+ with open(self.apt_conf_file, "w") as apt_conf:
+ with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
+ "apt", "apt.conf.sample")) as apt_conf_sample:
+ for line in apt_conf_sample.read().split("\n"):
+ line = re.sub("#ROOTFS#", "/dev/null", line)
+ line = re.sub("#APTCONF#", self.apt_conf_dir, line)
+ apt_conf.write(line + "\n")
+
def write_index(self):
+ self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
+ "apt-ftparchive")
+ self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
+ self._create_configs()
+
+ os.environ['APT_CONFIG'] = self.apt_conf_file
+
pkg_archs = self.d.getVar('PACKAGE_ARCHS', True)
if pkg_archs is not None:
arch_list = pkg_archs.split()
@@ -529,7 +561,7 @@ class PackageManager(object):
return
cmd = [bb.utils.which(os.getenv('PATH'), "oe-pkgdata-util"),
- "glob", self.d.getVar('PKGDATA_DIR', True), installed_pkgs_file,
+ "-p", self.d.getVar('PKGDATA_DIR', True), "glob", installed_pkgs_file,
globs]
exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY', True)
if exclude:
@@ -578,7 +610,8 @@ class RpmPM(PackageManager):
self.fullpkglist = list()
self.deploy_dir = self.d.getVar('DEPLOY_DIR_RPM', True)
self.etcrpm_dir = os.path.join(self.target_rootfs, "etc/rpm")
- self.install_dir = os.path.join(self.target_rootfs, "install")
+ self.install_dir_name = "oe_install"
+ self.install_dir_path = os.path.join(self.target_rootfs, self.install_dir_name)
self.rpm_cmd = bb.utils.which(os.getenv('PATH'), "rpm")
self.smart_cmd = bb.utils.which(os.getenv('PATH'), "smart")
self.smart_opt = "--quiet --data-dir=" + os.path.join(target_rootfs,
@@ -679,10 +712,10 @@ class RpmPM(PackageManager):
def _search_pkg_name_in_feeds(self, pkg, feed_archs):
for arch in feed_archs:
arch = arch.replace('-', '_')
+ regex_match = re.compile(r"^%s-[^-]*-[^-]*@%s$" % \
+ (re.escape(pkg), re.escape(arch)))
for p in self.fullpkglist:
- regex_match = r"^%s-[^-]*-[^-]*@%s$" % \
- (re.escape(pkg), re.escape(arch))
- if re.match(regex_match, p) is not None:
+ if regex_match.match(p) is not None:
# First found is best match
# bb.note('%s -> %s' % (pkg, pkg + '@' + arch))
return pkg + '@' + arch
@@ -749,9 +782,9 @@ class RpmPM(PackageManager):
bb.utils.mkdirhier(self.etcrpm_dir)
# Setup temporary directory -- install...
- if os.path.exists(self.install_dir):
- bb.utils.remove(self.install_dir, True)
- bb.utils.mkdirhier(os.path.join(self.install_dir, 'tmp'))
+ if os.path.exists(self.install_dir_path):
+ bb.utils.remove(self.install_dir_path, True)
+ bb.utils.mkdirhier(os.path.join(self.install_dir_path, 'tmp'))
channel_priority = 5
platform_dir = os.path.join(self.etcrpm_dir, "platform")
@@ -838,7 +871,7 @@ class RpmPM(PackageManager):
self._invoke_smart('config --set rpm-dbpath=/var/lib/rpm')
self._invoke_smart('config --set rpm-extra-macros._var=%s' %
self.d.getVar('localstatedir', True))
- cmd = 'config --set rpm-extra-macros._tmppath=/install/tmp'
+ cmd = "config --set rpm-extra-macros._tmppath=/%s/tmp" % (self.install_dir_name)
prefer_color = self.d.getVar('RPM_PREFER_ELF_ARCH', True)
if prefer_color:
@@ -856,6 +889,7 @@ class RpmPM(PackageManager):
% prefer_color)
self._invoke_smart(cmd)
+ self._invoke_smart('config --set rpm-ignoresize=1')
# Write common configuration for host and target usage
self._invoke_smart('config --set rpm-nolinktos=1')
@@ -903,8 +937,10 @@ class RpmPM(PackageManager):
#
if self.rpm_version == 4:
scriptletcmd = "$2 $3 $4\n"
+ scriptpath = "$3"
else:
scriptletcmd = "$2 $1/$3 $4\n"
+ scriptpath = "$1/$3"
SCRIPTLET_FORMAT = "#!/bin/bash\n" \
"\n" \
@@ -922,10 +958,10 @@ class RpmPM(PackageManager):
" mkdir -p $1/etc/rpm-postinsts\n" \
" num=100\n" \
" while [ -e $1/etc/rpm-postinsts/${num}-* ]; do num=$((num + 1)); done\n" \
- " name=`head -1 $1/$3 | cut -d\' \' -f 2`\n" \
+ " name=`head -1 " + scriptpath + " | cut -d\' \' -f 2`\n" \
' echo "#!$2" > $1/etc/rpm-postinsts/${num}-${name}\n' \
' echo "# Arg: $4" >> $1/etc/rpm-postinsts/${num}-${name}\n' \
- " cat $1/$3 >> $1/etc/rpm-postinsts/${num}-${name}\n" \
+ " cat " + scriptpath + " >> $1/etc/rpm-postinsts/${num}-${name}\n" \
" chmod +x $1/etc/rpm-postinsts/${num}-${name}\n" \
" else\n" \
' echo "Error: pre/post remove scriptlet failed"\n' \
@@ -991,7 +1027,7 @@ class RpmPM(PackageManager):
cmd += "--dbpath=/var/lib/rpm "
cmd += "--define='_cross_scriptlet_wrapper %s' " % \
self.scriptlet_wrapper
- cmd += "--define='_tmppath /install/tmp' %s" % ' '.join(pkgs)
+ cmd += "--define='_tmppath /%s/tmp' %s" % (self.install_dir_name, ' '.join(pkgs))
else:
# for pkg in pkgs:
# bb.note('Debug: What required: %s' % pkg)
@@ -1026,7 +1062,7 @@ class RpmPM(PackageManager):
bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/opkg'), True)
# remove temp directory
- bb.utils.remove(self.d.expand('${IMAGE_ROOTFS}/install'), True)
+ bb.utils.remove(self.install_dir_path, True)
def backup_packaging_data(self):
# Save the rpmlib for increment rpm image generation
@@ -1630,10 +1666,10 @@ class DpkgPM(PackageManager):
def remove(self, pkgs, with_dependencies=True):
if with_dependencies:
os.environ['APT_CONFIG'] = self.apt_conf_file
- cmd = "%s remove %s" % (self.apt_get_cmd, ' '.join(pkgs))
+ cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
else:
cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
- " -r --force-depends %s" % \
+ " -P --force-depends %s" % \
(bb.utils.which(os.getenv('PATH'), "dpkg"),
self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
@@ -1725,9 +1761,12 @@ class DpkgPM(PackageManager):
with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
for line in apt_conf_sample.read().split("\n"):
match_arch = re.match(" Architecture \".*\";$", line)
+ architectures = ""
if match_arch:
for base_arch in base_arch_list:
- apt_conf.write(" Architecture \"%s\";\n" % base_arch)
+ architectures += "\"%s\";" % base_arch
+ apt_conf.write(" Architectures {%s};\n" % architectures);
+ apt_conf.write(" Architecture \"%s\";\n" % base_archs)
else:
line = re.sub("#ROOTFS#", self.target_rootfs, line)
line = re.sub("#APTCONF#", self.apt_conf_dir, line)
diff --git a/meta/lib/oe/patch.py b/meta/lib/oe/patch.py
index b085c9d6b5..f68d40f8c8 100644
--- a/meta/lib/oe/patch.py
+++ b/meta/lib/oe/patch.py
@@ -199,10 +199,108 @@ class PatchTree(PatchSet):
self.Pop(all=True)
class GitApplyTree(PatchTree):
+ patch_line_prefix = '%% original patch'
+
def __init__(self, dir, d):
PatchTree.__init__(self, dir, d)
+ @staticmethod
+ def extractPatchHeader(patchfile):
+ """
+ Extract just the header lines from the top of a patch file
+ """
+ lines = []
+ with open(patchfile, 'r') as f:
+ for line in f.readlines():
+ if line.startswith('Index: ') or line.startswith('diff -') or line.startswith('---'):
+ break
+ lines.append(line)
+ return lines
+
+ @staticmethod
+ def prepareCommit(patchfile):
+ """
+ Prepare a git commit command line based on the header from a patch file
+ (typically this is useful for patches that cannot be applied with "git am" due to formatting)
+ """
+ import tempfile
+ import re
+ author_re = re.compile('[\S ]+ <\S+@\S+\.\S+>')
+ # Process patch header and extract useful information
+ lines = GitApplyTree.extractPatchHeader(patchfile)
+ outlines = []
+ author = None
+ date = None
+ for line in lines:
+ if line.startswith('Subject: '):
+ subject = line.split(':', 1)[1]
+ # Remove any [PATCH][oe-core] etc.
+ subject = re.sub(r'\[.+?\]\s*', '', subject)
+ outlines.insert(0, '%s\n\n' % subject.strip())
+ continue
+ if line.startswith('From: ') or line.startswith('Author: '):
+ authorval = line.split(':', 1)[1].strip().replace('"', '')
+ # git is fussy about author formatting i.e. it must be Name <email@domain>
+ if author_re.match(authorval):
+ author = authorval
+ continue
+ if line.startswith('Date: '):
+ if date is None:
+ dateval = line.split(':', 1)[1].strip()
+ # Very crude check for date format, since git will blow up if it's not in the right
+ # format. Without e.g. a python-dateutils dependency we can't do a whole lot more
+ if len(dateval) > 12:
+ date = dateval
+ continue
+ if line.startswith('Signed-off-by: '):
+ authorval = line.split(':', 1)[1].strip().replace('"', '')
+ # git is fussy about author formatting i.e. it must be Name <email@domain>
+ if author_re.match(authorval):
+ author = authorval
+ outlines.append(line)
+ # Write out commit message to a file
+ with tempfile.NamedTemporaryFile('w', delete=False) as tf:
+ tmpfile = tf.name
+ for line in outlines:
+ tf.write(line)
+ # Prepare git command
+ cmd = ["git", "commit", "-F", tmpfile]
+ # git doesn't like plain email addresses as authors
+ if author and '<' in author:
+ cmd.append('--author="%s"' % author)
+ if date:
+ cmd.append('--date="%s"' % date)
+ return (tmpfile, cmd)
+
+ @staticmethod
+ def extractPatches(tree, startcommit, outdir):
+ import tempfile
+ import shutil
+ tempdir = tempfile.mkdtemp(prefix='oepatch')
+ try:
+ shellcmd = ["git", "format-patch", startcommit, "-o", tempdir]
+ out = runcmd(["sh", "-c", " ".join(shellcmd)], tree)
+ if out:
+ for srcfile in out.split():
+ patchlines = []
+ outfile = None
+ with open(srcfile, 'r') as f:
+ for line in f:
+ if line.startswith(GitApplyTree.patch_line_prefix):
+ outfile = line.split()[-1].strip()
+ continue
+ patchlines.append(line)
+ if not outfile:
+ outfile = os.path.basename(srcfile)
+ with open(os.path.join(outdir, outfile), 'w') as of:
+ for line in patchlines:
+ of.write(line)
+ finally:
+ shutil.rmtree(tempdir)
+
def _applypatch(self, patch, force = False, reverse = False, run = True):
+ import shutil
+
def _applypatchhelper(shellcmd, patch, force = False, reverse = False, run = True):
if reverse:
shellcmd.append('-R')
@@ -214,12 +312,65 @@ class GitApplyTree(PatchTree):
return runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ # Add hooks which add a pointer to the original patch file name in the commit message
+ reporoot = (runcmd("git rev-parse --show-toplevel".split(), self.dir) or '').strip()
+ if not reporoot:
+ raise Exception("Cannot get repository root for directory %s" % self.dir)
+ commithook = os.path.join(reporoot, '.git', 'hooks', 'commit-msg')
+ commithook_backup = commithook + '.devtool-orig'
+ applyhook = os.path.join(reporoot, '.git', 'hooks', 'applypatch-msg')
+ applyhook_backup = applyhook + '.devtool-orig'
+ if os.path.exists(commithook):
+ shutil.move(commithook, commithook_backup)
+ if os.path.exists(applyhook):
+ shutil.move(applyhook, applyhook_backup)
+ with open(commithook, 'w') as f:
+ # NOTE: the formatting here is significant; if you change it you'll also need to
+ # change other places which read it back
+ f.write('echo >> $1\n')
+ f.write('echo "%s: $PATCHFILE" >> $1\n' % GitApplyTree.patch_line_prefix)
+ os.chmod(commithook, 0755)
+ shutil.copy2(commithook, applyhook)
try:
- shellcmd = ["git", "--work-tree=.", "am", "-3", "-p%s" % patch['strippath']]
- return _applypatchhelper(shellcmd, patch, force, reverse, run)
- except CmdError:
- shellcmd = ["git", "--git-dir=.", "apply", "-p%s" % patch['strippath']]
- return _applypatchhelper(shellcmd, patch, force, reverse, run)
+ patchfilevar = 'PATCHFILE="%s"' % os.path.basename(patch['file'])
+ try:
+ shellcmd = [patchfilevar, "git", "--work-tree=.", "am", "-3", "--keep-cr", "-p%s" % patch['strippath']]
+ return _applypatchhelper(shellcmd, patch, force, reverse, run)
+ except CmdError:
+ # Need to abort the git am, or we'll still be within it at the end
+ try:
+ shellcmd = ["git", "--work-tree=.", "am", "--abort"]
+ runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ except CmdError:
+ pass
+ # Fall back to git apply
+ shellcmd = ["git", "--git-dir=.", "apply", "-p%s" % patch['strippath']]
+ try:
+ output = _applypatchhelper(shellcmd, patch, force, reverse, run)
+ except CmdError:
+ # Fall back to patch
+ output = PatchTree._applypatch(self, patch, force, reverse, run)
+ # Add all files
+ shellcmd = ["git", "add", "-f", "."]
+ output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ # Exclude the patches directory
+ shellcmd = ["git", "reset", "HEAD", self.patchdir]
+ output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ # Commit the result
+ (tmpfile, shellcmd) = self.prepareCommit(patch['file'])
+ try:
+ shellcmd.insert(0, patchfilevar)
+ output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ finally:
+ os.remove(tmpfile)
+ return output
+ finally:
+ os.remove(commithook)
+ os.remove(applyhook)
+ if os.path.exists(commithook_backup):
+ shutil.move(commithook_backup, commithook)
+ if os.path.exists(applyhook_backup):
+ shutil.move(applyhook_backup, applyhook)
class QuiltTree(PatchSet):
diff --git a/meta/lib/oe/recipeutils.py b/meta/lib/oe/recipeutils.py
new file mode 100644
index 0000000000..159a103719
--- /dev/null
+++ b/meta/lib/oe/recipeutils.py
@@ -0,0 +1,279 @@
+# Utility functions for reading and modifying recipes
+#
+# Some code borrowed from the OE layer index
+#
+# Copyright (C) 2013-2014 Intel Corporation
+#
+
+import sys
+import os
+import os.path
+import tempfile
+import textwrap
+import difflib
+import utils
+import shutil
+import re
+from collections import OrderedDict, defaultdict
+
+
+# Help us to find places to insert values
+recipe_progression = ['SUMMARY', 'DESCRIPTION', 'HOMEPAGE', 'BUGTRACKER', 'SECTION', 'LICENSE', 'LIC_FILES_CHKSUM', 'PROVIDES', 'DEPENDS', 'PR', 'PV', 'SRCREV', 'SRC_URI', 'S', 'do_fetch', 'do_unpack', 'do_patch', 'EXTRA_OECONF', 'do_configure', 'EXTRA_OEMAKE', 'do_compile', 'do_install', 'do_populate_sysroot', 'INITSCRIPT', 'USERADD', 'GROUPADD', 'PACKAGES', 'FILES', 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RPROVIDES', 'RREPLACES', 'RCONFLICTS', 'ALLOW_EMPTY', 'do_package', 'do_deploy']
+# Variables that sometimes are a bit long but shouldn't be wrapped
+nowrap_vars = ['SUMMARY', 'HOMEPAGE', 'BUGTRACKER']
+list_vars = ['SRC_URI', 'LIC_FILES_CHKSUM']
+meta_vars = ['SUMMARY', 'DESCRIPTION', 'HOMEPAGE', 'BUGTRACKER', 'SECTION']
+
+
+def pn_to_recipe(cooker, pn):
+ """Convert a recipe name (PN) to the path to the recipe file"""
+ import bb.providers
+
+ if pn in cooker.recipecache.pkg_pn:
+ filenames = cooker.recipecache.pkg_pn[pn]
+ best = bb.providers.findBestProvider(pn, cooker.data, cooker.recipecache, cooker.recipecache.pkg_pn)
+ return best[3]
+ else:
+ return None
+
+
+def get_unavailable_reasons(cooker, pn):
+ """If a recipe could not be found, find out why if possible"""
+ import bb.taskdata
+ taskdata = bb.taskdata.TaskData(None, skiplist=cooker.skiplist)
+ return taskdata.get_reasons(pn)
+
+
+def parse_recipe(fn, d):
+ """Parse an individual recipe"""
+ import bb.cache
+ envdata = bb.cache.Cache.loadDataFull(fn, [], d)
+ return envdata
+
+
+def get_var_files(fn, varlist, d):
+ """Find the file in which each of a list of variables is set.
+ Note: requires variable history to be enabled when parsing.
+ """
+ envdata = parse_recipe(fn, d)
+ varfiles = {}
+ for v in varlist:
+ history = envdata.varhistory.variable(v)
+ files = []
+ for event in history:
+ if 'file' in event and not 'flag' in event:
+ files.append(event['file'])
+ if files:
+ actualfile = files[-1]
+ else:
+ actualfile = None
+ varfiles[v] = actualfile
+
+ return varfiles
+
+
+def patch_recipe_file(fn, values, patch=False, relpath=''):
+ """Update or insert variable values into a recipe file (assuming you
+ have already identified the exact file you want to update.)
+ Note that some manual inspection/intervention may be required
+ since this cannot handle all situations.
+ """
+ remainingnames = {}
+ for k in values.keys():
+ remainingnames[k] = recipe_progression.index(k) if k in recipe_progression else -1
+ remainingnames = OrderedDict(sorted(remainingnames.iteritems(), key=lambda x: x[1]))
+
+ with tempfile.NamedTemporaryFile('w', delete=False) as tf:
+ def outputvalue(name):
+ rawtext = '%s = "%s"\n' % (name, values[name])
+ if name in nowrap_vars:
+ tf.write(rawtext)
+ elif name in list_vars:
+ splitvalue = values[name].split()
+ if len(splitvalue) > 1:
+ linesplit = ' \\\n' + (' ' * (len(name) + 4))
+ tf.write('%s = "%s%s"\n' % (name, linesplit.join(splitvalue), linesplit))
+ else:
+ tf.write(rawtext)
+ else:
+ wrapped = textwrap.wrap(rawtext)
+ for wrapline in wrapped[:-1]:
+ tf.write('%s \\\n' % wrapline)
+ tf.write('%s\n' % wrapped[-1])
+
+ tfn = tf.name
+ with open(fn, 'r') as f:
+ # First runthrough - find existing names (so we know not to insert based on recipe_progression)
+ # Second runthrough - make the changes
+ existingnames = []
+ for runthrough in [1, 2]:
+ currname = None
+ for line in f:
+ if not currname:
+ insert = False
+ for k in remainingnames.keys():
+ for p in recipe_progression:
+ if re.match('^%s(_prepend|_append)*[ ?:=(]' % p, line):
+ if remainingnames[k] > -1 and recipe_progression.index(p) > remainingnames[k] and runthrough > 1 and not k in existingnames:
+ outputvalue(k)
+ del remainingnames[k]
+ break
+ for k in remainingnames.keys():
+ if re.match('^%s[ ?:=]' % k, line):
+ currname = k
+ if runthrough == 1:
+ existingnames.append(k)
+ else:
+ del remainingnames[k]
+ break
+ if currname and runthrough > 1:
+ outputvalue(currname)
+
+ if currname:
+ sline = line.rstrip()
+ if not sline.endswith('\\'):
+ currname = None
+ continue
+ if runthrough > 1:
+ tf.write(line)
+ f.seek(0)
+ if remainingnames:
+ tf.write('\n')
+ for k in remainingnames.keys():
+ outputvalue(k)
+
+ with open(tfn, 'U') as f:
+ tolines = f.readlines()
+ if patch:
+ with open(fn, 'U') as f:
+ fromlines = f.readlines()
+ relfn = os.path.relpath(fn, relpath)
+ diff = difflib.unified_diff(fromlines, tolines, 'a/%s' % relfn, 'b/%s' % relfn)
+ os.remove(tfn)
+ return diff
+ else:
+ with open(fn, 'w') as f:
+ f.writelines(tolines)
+ os.remove(tfn)
+ return None
+
+def localise_file_vars(fn, varfiles, varlist):
+ """Given a list of variables and variable history (fetched with get_var_files())
+ find where each variable should be set/changed. This handles for example where a
+ recipe includes an inc file where variables might be changed - in most cases
+ we want to update the inc file when changing the variable value rather than adding
+ it to the recipe itself.
+ """
+ fndir = os.path.dirname(fn) + os.sep
+
+ first_meta_file = None
+ for v in meta_vars:
+ f = varfiles.get(v, None)
+ if f:
+ actualdir = os.path.dirname(f) + os.sep
+ if actualdir.startswith(fndir):
+ first_meta_file = f
+ break
+
+ filevars = defaultdict(list)
+ for v in varlist:
+ f = varfiles[v]
+ # Only return files that are in the same directory as the recipe or in some directory below there
+ # (this excludes bbclass files and common inc files that wouldn't be appropriate to set the variable
+ # in if we were going to set a value specific to this recipe)
+ if f:
+ actualfile = f
+ else:
+ # Variable isn't in a file, if it's one of the "meta" vars, use the first file with a meta var in it
+ if first_meta_file:
+ actualfile = first_meta_file
+ else:
+ actualfile = fn
+
+ actualdir = os.path.dirname(actualfile) + os.sep
+ if not actualdir.startswith(fndir):
+ actualfile = fn
+ filevars[actualfile].append(v)
+
+ return filevars
+
+def patch_recipe(d, fn, varvalues, patch=False, relpath=''):
+ """Modify a list of variable values in the specified recipe. Handles inc files if
+ used by the recipe.
+ """
+ varlist = varvalues.keys()
+ varfiles = get_var_files(fn, varlist, d)
+ locs = localise_file_vars(fn, varfiles, varlist)
+ patches = []
+ for f,v in locs.iteritems():
+ vals = {k: varvalues[k] for k in v}
+ patchdata = patch_recipe_file(f, vals, patch, relpath)
+ if patch:
+ patches.append(patchdata)
+
+ if patch:
+ return patches
+ else:
+ return None
+
+
+
+def copy_recipe_files(d, tgt_dir, whole_dir=False, download=True):
+ """Copy (local) recipe files, including both files included via include/require,
+ and files referred to in the SRC_URI variable."""
+ import bb.fetch2
+ import oe.path
+
+ # FIXME need a warning if the unexpanded SRC_URI value contains variable references
+
+ uris = (d.getVar('SRC_URI', True) or "").split()
+ fetch = bb.fetch2.Fetch(uris, d)
+ if download:
+ fetch.download()
+
+ # Copy local files to target directory and gather any remote files
+ bb_dir = os.path.dirname(d.getVar('FILE', True)) + os.sep
+ remotes = []
+ includes = [path for path in d.getVar('BBINCLUDED', True).split() if
+ path.startswith(bb_dir) and os.path.exists(path)]
+ for path in fetch.localpaths() + includes:
+ # Only import files that are under the meta directory
+ if path.startswith(bb_dir):
+ if not whole_dir:
+ relpath = os.path.relpath(path, bb_dir)
+ subdir = os.path.join(tgt_dir, os.path.dirname(relpath))
+ if not os.path.exists(subdir):
+ os.makedirs(subdir)
+ shutil.copy2(path, os.path.join(tgt_dir, relpath))
+ else:
+ remotes.append(path)
+ # Simply copy whole meta dir, if requested
+ if whole_dir:
+ shutil.copytree(bb_dir, tgt_dir)
+
+ return remotes
+
+
+def get_recipe_patches(d):
+ """Get a list of the patches included in SRC_URI within a recipe."""
+ patchfiles = []
+ # Execute src_patches() defined in patch.bbclass - this works since that class
+ # is inherited globally
+ patches = bb.utils.exec_flat_python_func('src_patches', d)
+ for patch in patches:
+ _, _, local, _, _, parm = bb.fetch.decodeurl(patch)
+ patchfiles.append(local)
+ return patchfiles
+
+
+def validate_pn(pn):
+ """Perform validation on a recipe name (PN) for a new recipe."""
+ reserved_names = ['forcevariable', 'append', 'prepend', 'remove']
+ if not re.match('[0-9a-z-]+', pn):
+ return 'Recipe name "%s" is invalid: only characters 0-9, a-z and - are allowed' % pn
+ elif pn in reserved_names:
+ return 'Recipe name "%s" is invalid: is a reserved keyword' % pn
+ elif pn.startswith('pn-'):
+ return 'Recipe name "%s" is invalid: names starting with "pn-" are reserved' % pn
+ return ''
+
diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py
index c4735f2755..6fb749f049 100644
--- a/meta/lib/oe/rootfs.py
+++ b/meta/lib/oe/rootfs.py
@@ -40,6 +40,43 @@ class Rootfs(object):
def _log_check(self):
pass
+ def _log_check_warn(self):
+ r = re.compile('^(warn|Warn|NOTE: warn|NOTE: Warn|WARNING:)')
+ log_path = self.d.expand("${T}/log.do_rootfs")
+ with open(log_path, 'r') as log:
+ for line in log.read().split('\n'):
+ if 'log_check' in line or 'NOTE:' in line:
+ continue
+
+ m = r.search(line)
+ if m:
+ bb.warn('log_check: There is a warn message in the logfile')
+ bb.warn('log_check: Matched keyword: [%s]' % m.group())
+ bb.warn('log_check: %s\n' % line)
+
+ def _log_check_error(self):
+ r = re.compile(self.log_check_regex)
+ log_path = self.d.expand("${T}/log.do_rootfs")
+ with open(log_path, 'r') as log:
+ found_error = 0
+ message = "\n"
+ for line in log.read().split('\n'):
+ if 'log_check' in line:
+ continue
+
+ m = r.search(line)
+ if m:
+ found_error = 1
+ bb.warn('log_check: There were error messages in the logfile')
+ bb.warn('log_check: Matched keyword: [%s]\n\n' % m.group())
+
+ if found_error >= 1 and found_error <= 5:
+ message += line + '\n'
+ found_error += 1
+
+ if found_error == 6:
+ bb.fatal(message)
+
def _insert_feed_uris(self):
if bb.utils.contains("IMAGE_FEATURES", "package-management",
True, False, self.d):
@@ -118,17 +155,19 @@ class Rootfs(object):
if self.d.getVar('USE_DEVFS', True) != "1":
self._create_devfs()
- self._uninstall_uneeded()
+ self._uninstall_unneeded()
self._insert_feed_uris()
self._run_ldconfig()
- self._generate_kernel_module_deps()
+ if self.d.getVar('USE_DEPMOD', True) != "0":
+ self._generate_kernel_module_deps()
self._cleanup()
+ self._log_check()
- def _uninstall_uneeded(self):
+ def _uninstall_unneeded(self):
# Remove unneeded init script symlinks
delayed_postinsts = self._get_delayed_postinsts()
if delayed_postinsts is None:
@@ -137,34 +176,41 @@ class Rootfs(object):
self.d.getVar('IMAGE_ROOTFS', True),
"run-postinsts", "remove"])
- # Remove unneeded package-management related components
- if bb.utils.contains("IMAGE_FEATURES", "package-management",
- True, False, self.d):
- return
+ runtime_pkgmanage = bb.utils.contains("IMAGE_FEATURES", "package-management",
+ True, False, self.d)
+ if not runtime_pkgmanage:
+ # Remove components that we don't need if we're not going to install
+ # additional packages at runtime
+ if delayed_postinsts is None:
+ installed_pkgs_dir = self.d.expand('${WORKDIR}/installed_pkgs.txt')
+ pkgs_to_remove = list()
+ with open(installed_pkgs_dir, "r+") as installed_pkgs:
+ pkgs_installed = installed_pkgs.read().split('\n')
+ for pkg_installed in pkgs_installed[:]:
+ pkg = pkg_installed.split()[0]
+ if pkg in ["update-rc.d",
+ "base-passwd",
+ "shadow",
+ "update-alternatives",
+ self.d.getVar("ROOTFS_BOOTSTRAP_INSTALL", True)
+ ]:
+ pkgs_to_remove.append(pkg)
+ pkgs_installed.remove(pkg_installed)
+
+ if len(pkgs_to_remove) > 0:
+ self.pm.remove(pkgs_to_remove, False)
+ # Update installed_pkgs.txt
+ open(installed_pkgs_dir, "w+").write('\n'.join(pkgs_installed))
- if delayed_postinsts is None:
- installed_pkgs_dir = self.d.expand('${WORKDIR}/installed_pkgs.txt')
- pkgs_to_remove = list()
- with open(installed_pkgs_dir, "r+") as installed_pkgs:
- pkgs_installed = installed_pkgs.read().split('\n')
- for pkg_installed in pkgs_installed[:]:
- pkg = pkg_installed.split()[0]
- if pkg in ["update-rc.d",
- "base-passwd",
- self.d.getVar("ROOTFS_BOOTSTRAP_INSTALL", True)
- ]:
- pkgs_to_remove.append(pkg)
- pkgs_installed.remove(pkg_installed)
-
- if len(pkgs_to_remove) > 0:
- self.pm.remove(pkgs_to_remove, False)
- # Update installed_pkgs.txt
- open(installed_pkgs_dir, "w+").write('\n'.join(pkgs_installed))
+ else:
+ self._save_postinsts()
- else:
- self._save_postinsts()
+ post_uninstall_cmds = self.d.getVar("ROOTFS_POSTUNINSTALL_COMMAND", True)
+ execute_pre_post_process(self.d, post_uninstall_cmds)
- self.pm.remove_packaging_data()
+ if not runtime_pkgmanage:
+ # Remove the package manager data files
+ self.pm.remove_packaging_data()
def _run_intercepts(self):
intercepts_dir = os.path.join(self.d.getVar('WORKDIR', True),
@@ -209,16 +255,17 @@ class Rootfs(object):
'new', '-v'])
def _generate_kernel_module_deps(self):
- kernel_abi_ver_file = os.path.join(self.d.getVar('STAGING_KERNEL_DIR', True),
+ kernel_abi_ver_file = oe.path.join(self.d.getVar('PKGDATA_DIR', True), "kernel-depmod",
'kernel-abiversion')
- if os.path.exists(kernel_abi_ver_file):
- kernel_ver = open(kernel_abi_ver_file).read().strip(' \n')
- modules_dir = os.path.join(self.image_rootfs, 'lib', 'modules', kernel_ver)
+ if not os.path.exists(kernel_abi_ver_file):
+ bb.fatal("No kernel-abiversion file found (%s), cannot run depmod, aborting" % kernel_abi_ver_file)
- bb.utils.mkdirhier(modules_dir)
+ kernel_ver = open(kernel_abi_ver_file).read().strip(' \n')
+ modules_dir = os.path.join(self.image_rootfs, 'lib', 'modules', kernel_ver)
- self._exec_shell_cmd(['depmodwrapper', '-a', '-b', self.image_rootfs,
- kernel_ver])
+ bb.utils.mkdirhier(modules_dir)
+
+ self._exec_shell_cmd(['depmodwrapper', '-a', '-b', self.image_rootfs, kernel_ver])
"""
Create devfs:
@@ -248,7 +295,7 @@ class Rootfs(object):
class RpmRootfs(Rootfs):
def __init__(self, d, manifest_dir):
super(RpmRootfs, self).__init__(d)
-
+ self.log_check_regex = '(unpacking of archive failed|Cannot find package|exit 1|ERR|Fail)'
self.manifest = RpmManifest(d, manifest_dir)
self.pm = RpmPM(d,
@@ -320,8 +367,6 @@ class RpmRootfs(Rootfs):
self.pm.install_complementary()
- self._log_check()
-
if self.inc_rpm_image_gen == "1":
self.pm.backup_packaging_data()
@@ -347,20 +392,6 @@ class RpmRootfs(Rootfs):
# already saved in /etc/rpm-postinsts
pass
- def _log_check_warn(self):
- r = re.compile('(warn|Warn)')
- log_path = self.d.expand("${T}/log.do_rootfs")
- with open(log_path, 'r') as log:
- for line in log.read().split('\n'):
- if 'log_check' in line:
- continue
-
- m = r.search(line)
- if m:
- bb.warn('log_check: There is a warn message in the logfile')
- bb.warn('log_check: Matched keyword: [%s]' % m.group())
- bb.warn('log_check: %s\n' % line)
-
def _log_check_error(self):
r = re.compile('(unpacking of archive failed|Cannot find package|exit 1|ERR|Fail)')
log_path = self.d.expand("${T}/log.do_rootfs")
@@ -402,12 +433,16 @@ class RpmRootfs(Rootfs):
# __db.00* (Berkeley DB files that hold locks, rpm specific environment
# settings, etc.), that should not get into the final rootfs
self.pm.unlock_rpm_db()
- bb.utils.remove(self.image_rootfs + "/install", True)
+ if os.path.isdir(self.pm.install_dir_path + "/tmp") and not os.listdir(self.pm.install_dir_path + "/tmp"):
+ bb.utils.remove(self.pm.install_dir_path + "/tmp", True)
+ if os.path.isdir(self.pm.install_dir_path) and not os.listdir(self.pm.install_dir_path):
+ bb.utils.remove(self.pm.install_dir_path, True)
class DpkgRootfs(Rootfs):
def __init__(self, d, manifest_dir):
super(DpkgRootfs, self).__init__(d)
+ self.log_check_regex = '^E:'
bb.utils.remove(self.image_rootfs, True)
bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True)
@@ -479,7 +514,8 @@ class DpkgRootfs(Rootfs):
self.pm.mark_packages("unpacked", registered_pkgs.split())
def _log_check(self):
- pass
+ self._log_check_warn()
+ self._log_check_error()
def _cleanup(self):
pass
@@ -488,6 +524,7 @@ class DpkgRootfs(Rootfs):
class OpkgRootfs(Rootfs):
def __init__(self, d, manifest_dir):
super(OpkgRootfs, self).__init__(d)
+ self.log_check_regex = '(exit 1|Collected errors)'
self.manifest = OpkgManifest(d, manifest_dir)
self.opkg_conf = self.d.getVar("IPKGCONF_TARGET", True)
@@ -749,7 +786,8 @@ class OpkgRootfs(Rootfs):
self.pm.mark_packages("unpacked", registered_pkgs.split())
def _log_check(self):
- pass
+ self._log_check_warn()
+ self._log_check_error()
def _cleanup(self):
pass
diff --git a/meta/lib/oe/sdk.py b/meta/lib/oe/sdk.py
index c57a441941..a6767412c7 100644
--- a/meta/lib/oe/sdk.py
+++ b/meta/lib/oe/sdk.py
@@ -269,6 +269,8 @@ class DpkgSdk(Sdk):
bb.note("Installing TARGET packages")
self._populate_sysroot(self.target_pm, self.target_manifest)
+ self.target_pm.install_complementary(self.d.getVar('SDKIMAGE_INSTALL_COMPLEMENTARY', True))
+
execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_TARGET_COMMAND", True))
self._copy_apt_dir_to(os.path.join(self.sdk_target_sysroot, "etc", "apt"))
diff --git a/meta/lib/oe/sstatesig.py b/meta/lib/oe/sstatesig.py
index af7617ee61..9d6d7c42fc 100644
--- a/meta/lib/oe/sstatesig.py
+++ b/meta/lib/oe/sstatesig.py
@@ -137,13 +137,16 @@ class SignatureGeneratorOEBasicHash(bb.siggen.SignatureGeneratorBasicHash):
return
super(bb.siggen.SignatureGeneratorBasicHash, self).dump_sigtask(fn, task, stampbase, runtime)
- def dump_lockedsigs(self, sigfile=None):
+ def dump_lockedsigs(self, sigfile=None, taskfilter=None):
if not sigfile:
sigfile = os.getcwd() + "/locked-sigs.inc"
bb.plain("Writing locked sigs to %s" % sigfile)
types = {}
for k in self.runtaskdeps:
+ if taskfilter:
+ if not k in taskfilter:
+ continue
fn = k.rsplit(".",1)[0]
t = self.lockedhashfn[fn].split(" ")[1].split(":")[5]
t = 't-' + t.replace('_', '-')
@@ -203,9 +206,6 @@ def find_siginfo(pn, taskname, taskhashlist, d):
if key.startswith('virtual:native:'):
pn = pn + '-native'
- if taskname in ['do_fetch', 'do_unpack', 'do_patch', 'do_populate_lic']:
- pn.replace("-native", "")
-
filedates = {}
# First search in stamps dir
@@ -246,7 +246,10 @@ def find_siginfo(pn, taskname, taskhashlist, d):
localdata.setVar('PV', '*')
localdata.setVar('PR', '*')
localdata.setVar('BB_TASKHASH', hashval)
- if pn.endswith('-native') or "-cross-" in pn or "-crosssdk-" in pn:
+ swspec = localdata.getVar('SSTATE_SWSPEC', True)
+ if taskname in ['do_fetch', 'do_unpack', 'do_patch', 'do_populate_lic', 'do_preconfigure'] and swspec:
+ localdata.setVar('SSTATE_PKGSPEC', '${SSTATE_SWSPEC}')
+ elif pn.endswith('-native') or "-cross-" in pn or "-crosssdk-" in pn:
localdata.setVar('SSTATE_EXTRAPATH', "${NATIVELSBSTRING}/")
sstatename = taskname[3:]
filespec = '%s_%s.*.siginfo' % (localdata.getVar('SSTATE_PKG', True), sstatename)
diff --git a/meta/lib/oe/terminal.py b/meta/lib/oe/terminal.py
index 0a623c75b1..4f5c611615 100644
--- a/meta/lib/oe/terminal.py
+++ b/meta/lib/oe/terminal.py
@@ -2,6 +2,7 @@ import logging
import oe.classutils
import shlex
from bb.process import Popen, ExecutionError
+from distutils.version import LooseVersion
logger = logging.getLogger('BitBake.OE.Terminal')
@@ -52,9 +53,17 @@ class XTerminal(Terminal):
raise UnsupportedTerminal(self.name)
class Gnome(XTerminal):
- command = 'gnome-terminal -t "{title}" -x {command}'
+ command = 'gnome-terminal -t "{title}" --disable-factory -x {command}'
priority = 2
+ def __init__(self, sh_cmd, title=None, env=None, d=None):
+ # Check version
+ vernum = check_terminal_version("gnome-terminal")
+ if vernum and LooseVersion(vernum) >= '3.10':
+ logger.debug(1, 'Gnome-Terminal 3.10 or later does not support --disable-factory')
+ self.command = 'gnome-terminal -t "{title}" -x {command}'
+ XTerminal.__init__(self, sh_cmd, title, env, d)
+
class Mate(XTerminal):
command = 'mate-terminal -t "{title}" -x {command}'
priority = 2
@@ -63,17 +72,20 @@ class Xfce(XTerminal):
command = 'xfce4-terminal -T "{title}" -e "{command}"'
priority = 2
+class Terminology(XTerminal):
+ command = 'terminology -T="{title}" -e {command}'
+ priority = 2
+
class Konsole(XTerminal):
- command = 'konsole -T "{title}" -e {command}'
+ command = 'konsole --nofork -p tabtitle="{title}" -e {command}'
priority = 2
def __init__(self, sh_cmd, title=None, env=None, d=None):
# Check version
- vernum = check_konsole_version("konsole")
- if vernum:
- if vernum.split('.')[0] == "2":
- logger.debug(1, 'Konsole from KDE 4.x will not work as devshell, skipping')
- raise UnsupportedTerminal(self.name)
+ vernum = check_terminal_version("konsole")
+ if vernum and LooseVersion(vernum) < '2.0.0':
+ # Konsole from KDE 3.x
+ self.command = 'konsole -T "{title}" -e {command}'
XTerminal.__init__(self, sh_cmd, title, env, d)
class XTerm(XTerminal):
@@ -112,6 +124,24 @@ class TmuxRunning(Terminal):
if not os.getenv('TMUX'):
raise UnsupportedTerminal('tmux is not running')
+ if not check_tmux_pane_size('tmux'):
+ raise UnsupportedTerminal('tmux pane too small')
+
+ Terminal.__init__(self, sh_cmd, title, env, d)
+
+class TmuxNewWindow(Terminal):
+ """Open a new window in the current running tmux session"""
+ name = 'tmux-new-window'
+ command = 'tmux new-window -n "{title}" "{command}"'
+ priority = 2.70
+
+ def __init__(self, sh_cmd, title=None, env=None, d=None):
+ if not bb.utils.which(os.getenv('PATH'), 'tmux'):
+ raise UnsupportedTerminal('tmux is not installed')
+
+ if not os.getenv('TMUX'):
+ raise UnsupportedTerminal('tmux is not running')
+
Terminal.__init__(self, sh_cmd, title, env, d)
class Tmux(Terminal):
@@ -180,10 +210,27 @@ def spawn(name, sh_cmd, title=None, env=None, d=None):
if pipe.returncode != 0:
raise ExecutionError(sh_cmd, pipe.returncode, output)
-def check_konsole_version(konsole):
+def check_tmux_pane_size(tmux):
+ import subprocess as sub
+ try:
+ p = sub.Popen('%s list-panes -F "#{?pane_active,#{pane_height},}"' % tmux,
+ shell=True,stdout=sub.PIPE,stderr=sub.PIPE)
+ out, err = p.communicate()
+ size = int(out.strip())
+ except OSError as exc:
+ import errno
+ if exc.errno == errno.ENOENT:
+ return None
+ else:
+ raise
+ if size/2 >= 19:
+ return True
+ return False
+
+def check_terminal_version(terminalName):
import subprocess as sub
try:
- p = sub.Popen(['sh', '-c', '%s --version' % konsole],stdout=sub.PIPE,stderr=sub.PIPE)
+ p = sub.Popen(['sh', '-c', '%s --version' % terminalName],stdout=sub.PIPE,stderr=sub.PIPE)
out, err = p.communicate()
ver_info = out.rstrip().split('\n')
except OSError as exc:
@@ -196,6 +243,8 @@ def check_konsole_version(konsole):
for ver in ver_info:
if ver.startswith('Konsole'):
vernum = ver.split(' ')[-1]
+ if ver.startswith('GNOME Terminal'):
+ vernum = ver.split(' ')[-1]
return vernum
def distro_name():
diff --git a/meta/lib/oe/utils.py b/meta/lib/oe/utils.py
index 35442568e2..b8224de20b 100644
--- a/meta/lib/oe/utils.py
+++ b/meta/lib/oe/utils.py
@@ -42,8 +42,16 @@ def version_less_or_equal(variable, checkvalue, truevalue, falsevalue, d):
return falsevalue
def both_contain(variable1, variable2, checkvalue, d):
- if d.getVar(variable1,1).find(checkvalue) != -1 and d.getVar(variable2,1).find(checkvalue) != -1:
- return checkvalue
+ val1 = d.getVar(variable1, True)
+ val2 = d.getVar(variable2, True)
+ val1 = set(val1.split())
+ val2 = set(val2.split())
+ if isinstance(checkvalue, basestring):
+ checkvalue = set(checkvalue.split())
+ else:
+ checkvalue = set(checkvalue)
+ if checkvalue.issubset(val1) and checkvalue.issubset(val2):
+ return " ".join(checkvalue)
else:
return ""
@@ -180,3 +188,7 @@ def multiprocess_exec(commands, function):
pool.terminate()
pool.join()
raise
+
+def squashspaces(string):
+ import re
+ return re.sub("\s+", " ", string).strip()