oe/rootfs.py: DpkgRootfs/OpkgRootfs add support for dependency handling in postinsts scripts.

The old code don't take into account package dependencies causing
undefined execution order in postinsts scripts, in order to fix:

Add DpkgOpkgRootfs class for store common operations in DpkgRootfs
and OpkgRootfs.

Add _get_delayed_postinsts_common method that process Depends from
status file in dpkg/opkg and resolve dependency order causing an
execption if found circular dependencies.

[YOCTO #5318]

(From OE-Core rev: ed52d1040ee8be0bfa080d5679c583b1012bb575)

Signed-off-by: Aníbal Limón <anibal.limon@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Aníbal Limón
2015-06-25 13:21:15 -05:00
committed by Richard Purdie
parent f8661aaeba
commit b2c9e7347a

View File

@@ -495,8 +495,98 @@ class RpmRootfs(Rootfs):
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 DpkgOpkgRootfs(Rootfs):
def __init__(self, d):
super(DpkgOpkgRootfs, self).__init__(d)
class DpkgRootfs(Rootfs):
def _get_pkgs_postinsts(self, status_file):
def _get_pkg_depends_list(pkg_depends):
pkg_depends_list = []
# filter version requirements like libc (>= 1.1)
for dep in pkg_depends.split(', '):
m_dep = re.match("^(.*) \(.*\)$", dep)
if m_dep:
dep = m_dep.group(1)
pkg_depends_list.append(dep)
return pkg_depends_list
pkgs = {}
pkg_name = ""
pkg_status_match = False
pkg_depends = ""
with open(status_file) as status:
data = status.read()
status.close()
for line in data.split('\n'):
m_pkg = re.match("^Package: (.*)", line)
m_status = re.match("^Status:.*unpacked", line)
m_depends = re.match("^Depends: (.*)", line)
if m_pkg is not None:
if pkg_name and pkg_status_match:
pkgs[pkg_name] = _get_pkg_depends_list(pkg_depends)
pkg_name = m_pkg.group(1)
pkg_status_match = False
pkg_depends = ""
elif m_status is not None:
pkg_status_match = True
elif m_depends is not None:
pkg_depends = m_depends.group(1)
# remove package dependencies not in postinsts
pkg_names = pkgs.keys()
for pkg_name in pkg_names:
deps = pkgs[pkg_name][:]
for d in deps:
if d not in pkg_names:
pkgs[pkg_name].remove(d)
return pkgs
def _get_delayed_postinsts_common(self, status_file):
def _dep_resolve(graph, node, resolved, seen):
seen.append(node)
for edge in graph[node]:
if edge not in resolved:
if edge in seen:
raise RuntimeError("Packages %s and %s have " \
"a circular dependency in postinsts scripts." \
% (node, edge))
_dep_resolve(graph, edge, resolved, seen)
resolved.append(node)
pkg_list = []
pkgs = self._get_pkgs_postinsts(status_file)
if pkgs:
root = "__packagegroup_postinst__"
pkgs[root] = pkgs.keys()
_dep_resolve(pkgs, root, pkg_list, [])
pkg_list.remove(root)
if len(pkg_list) == 0:
return None
return pkg_list
def _save_postinsts_common(self, dst_postinst_dir, src_postinst_dir):
num = 0
for p in self._get_delayed_postinsts():
bb.utils.mkdirhier(dst_postinst_dir)
if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")):
shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"),
os.path.join(dst_postinst_dir, "%03d-%s" % (num, p)))
num += 1
class DpkgRootfs(DpkgOpkgRootfs):
def __init__(self, d, manifest_dir):
super(DpkgRootfs, self).__init__(d)
self.log_check_regex = '^E:'
@@ -540,34 +630,13 @@ class DpkgRootfs(Rootfs):
return ['DEPLOY_DIR_DEB', 'DEB_SDK_ARCH', 'APTCONF_TARGET', 'APT_ARGS', 'DPKG_ARCH', 'DEB_PREPROCESS_COMMANDS', 'DEB_POSTPROCESS_COMMAND']
def _get_delayed_postinsts(self):
pkg_list = []
with open(self.image_rootfs + "/var/lib/dpkg/status") as status:
for line in status:
m_pkg = re.match("^Package: (.*)", line)
m_status = re.match("^Status:.*unpacked", line)
if m_pkg is not None:
pkg_name = m_pkg.group(1)
elif m_status is not None:
pkg_list.append(pkg_name)
if len(pkg_list) == 0:
return None
return pkg_list
status_file = self.image_rootfs + "/var/lib/dpkg/status"
return self._get_delayed_postinsts_common(status_file)
def _save_postinsts(self):
num = 0
for p in self._get_delayed_postinsts():
dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/deb-postinsts")
src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/info")
bb.utils.mkdirhier(dst_postinst_dir)
if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")):
shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"),
os.path.join(dst_postinst_dir, "%03d-%s" % (num, p)))
num += 1
dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/deb-postinsts")
src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/info")
return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir)
def _handle_intercept_failure(self, registered_pkgs):
self.pm.mark_packages("unpacked", registered_pkgs.split())
@@ -580,7 +649,7 @@ class DpkgRootfs(Rootfs):
pass
class OpkgRootfs(Rootfs):
class OpkgRootfs(DpkgOpkgRootfs):
def __init__(self, d, manifest_dir):
super(OpkgRootfs, self).__init__(d)
self.log_check_regex = '(exit 1|Collected errors)'
@@ -810,38 +879,15 @@ class OpkgRootfs(Rootfs):
return ['IPKGCONF_SDK', 'IPK_FEED_URIS', 'DEPLOY_DIR_IPK', 'IPKGCONF_TARGET', 'INC_IPK_IMAGE_GEN', 'OPKG_ARGS', 'OPKGLIBDIR', 'OPKG_PREPROCESS_COMMANDS', 'OPKG_POSTPROCESS_COMMANDS', 'OPKGLIBDIR']
def _get_delayed_postinsts(self):
pkg_list = []
status_file = os.path.join(self.image_rootfs,
self.d.getVar('OPKGLIBDIR', True).strip('/'),
"opkg", "status")
with open(status_file) as status:
for line in status:
m_pkg = re.match("^Package: (.*)", line)
m_status = re.match("^Status:.*unpacked", line)
if m_pkg is not None:
pkg_name = m_pkg.group(1)
elif m_status is not None:
pkg_list.append(pkg_name)
if len(pkg_list) == 0:
return None
return pkg_list
return self._get_delayed_postinsts_common(status_file)
def _save_postinsts(self):
num = 0
for p in self._get_delayed_postinsts():
dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/ipk-postinsts")
src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${OPKGLIBDIR}/opkg/info")
bb.utils.mkdirhier(dst_postinst_dir)
if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")):
shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"),
os.path.join(dst_postinst_dir, "%03d-%s" % (num, p)))
num += 1
dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/ipk-postinsts")
src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${OPKGLIBDIR}/opkg/info")
return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir)
def _handle_intercept_failure(self, registered_pkgs):
self.pm.mark_packages("unpacked", registered_pkgs.split())