From b2c9e7347acdfd0efe1c3dabb853d609233b61b6 Mon Sep 17 00:00:00 2001 From: Aníbal Limón Date: Thu, 25 Jun 2015 13:21:15 -0500 Subject: oe/rootfs.py: DpkgRootfs/OpkgRootfs add support for dependency handling in postinsts scripts. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Richard Purdie --- meta/lib/oe/rootfs.py | 156 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 55 deletions(-) (limited to 'meta/lib/oe/rootfs.py') diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py index 48e5754b27..327c8eae86 100644 --- a/meta/lib/oe/rootfs.py +++ b/meta/lib/oe/rootfs.py @@ -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) + + 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 = [] -class DpkgRootfs(Rootfs): + 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()) -- cgit v1.2.3-54-g00ecf