[OE-core] [PATCH 1/2] oe/rootfs.py: DpkgRootfs/OpkgRootfs add support for dependency handling in postinsts scripts.

Aníbal Limón anibal.limon at linux.intel.com
Thu Jun 25 18:21:15 UTC 2015


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]

Signed-off-by: Aníbal Limón <anibal.limon at linux.intel.com>
---
 meta/lib/oe/rootfs.py | 156 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 101 insertions(+), 55 deletions(-)

diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py
index 48e5754..327c8ea 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())
-- 
1.9.1




More information about the Openembedded-core mailing list