[OE-core] [PATCHv2 2/3] package_manager.py: Add extract() method for opkg and dpkg

mariano.lopez at linux.intel.com mariano.lopez at linux.intel.com
Thu May 12 11:28:13 UTC 2016


From: Mariano Lopez <mariano.lopez at linux.intel.com>

Sometimes it is needed to have the content of a package outside
the recipe context.  This new method extract the content of an
IPK/DEB file to a tmpdir, without actually installing the package.

A new OpkgDpkgPM class was added to share the code for opkg and dpkg.

There were need some changes to opkg_query() in order to use it
with apt-cache output. Also set default values to avoid UnboundLocalError

[YOCTO #9569]

Signed-off-by: Mariano Lopez <mariano.lopez at linux.intel.com>
---
 meta/lib/oe/package_manager.py | 140 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 134 insertions(+), 6 deletions(-)

diff --git a/meta/lib/oe/package_manager.py b/meta/lib/oe/package_manager.py
index 427518d..f5517a4 100644
--- a/meta/lib/oe/package_manager.py
+++ b/meta/lib/oe/package_manager.py
@@ -35,9 +35,12 @@ when the packages are in deb or ipk format.
 def opkg_query(cmd_output):
     verregex = re.compile(' \([=<>]* [^ )]*\)')
     output = dict()
+    pkg = ""
+    arch = ""
+    ver = ""
     filename = ""
     dep = []
-    pkg = ""
+    pkgarch = ""
     for line in cmd_output.splitlines():
         line = line.rstrip()
         if ':' in line:
@@ -47,8 +50,10 @@ def opkg_query(cmd_output):
                 arch = line.split(": ")[1]
             elif line.startswith("Version: "):
                 ver = line.split(": ")[1]
-            elif line.startswith("File: "):
+            elif line.startswith("File: ") or line.startswith("Filename:"):
                 filename = line.split(": ")[1]
+                if "/" in filename:
+                    filename = os.path.basename(filename)
             elif line.startswith("Depends: "):
                 depends = verregex.sub('', line.split(": ")[1])
                 for depend in depends.split(", "):
@@ -57,16 +62,23 @@ def opkg_query(cmd_output):
                 recommends = verregex.sub('', line.split(": ")[1])
                 for recommend in recommends.split(", "):
                     dep.append("%s [REC]" % recommend)
-        else:
+            elif line.startswith("PackageArch: "):
+                pkgarch = line.split(": ")[1]
+
+        # When there is a blank line save the package information
+        elif not line:
             # IPK doesn't include the filename
             if not filename:
                 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
             if pkg:
                 output[pkg] = {"arch":arch, "ver":ver,
-                        "filename":filename, "deps": dep }
+                        "filename":filename, "deps": dep, "pkgarch":pkgarch }
             pkg = ""
+            arch = ""
+            ver = ""
             filename = ""
             dep = []
+            pkgarch = ""
 
     if pkg:
         if not filename:
@@ -1397,7 +1409,70 @@ class RpmPM(PackageManager):
             bb.utils.remove(f, True)
 
 
-class OpkgPM(PackageManager):
+class OpkgDpkgPM(PackageManager):
+    """
+    This is an abstract class. Do not instantiate this directly.
+    """
+    def __init__(self, d):
+        super(OpkgDpkgPM, self).__init__(d)
+
+    """
+    Returns a dictionary with the package info.
+
+    This method extracts the common parts for Opkg and Dpkg
+    """
+    def package_info(self, pkg, cmd):
+
+        try:
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
+        except subprocess.CalledProcessError as e:
+            bb.fatal("Unable to list available packages. Command '%s' "
+                     "returned %d:\n%s" % (cmd, e.returncode, e.output))
+        return opkg_query(output)
+
+    """
+    Returns the path to a tmpdir where resides the contents of a package.
+
+    Deleting the tmpdir is responsability of the caller.
+
+    This method extracts the common parts for Opkg and Dpkg
+    """
+    def extract(self, pkg, pkg_path):
+
+        ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
+        tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
+
+        if not os.path.isfile(pkg_path):
+            bb.fatal("Unable to extract package for '%s'."
+                     "File %s doesn't exists" % (pkg, pkg_path))
+
+        tmp_dir = tempfile.mkdtemp()
+        current_dir = os.getcwd()
+        os.chdir(tmp_dir)
+
+        try:
+            cmd = "%s x %s" % (ar_cmd, pkg_path)
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
+            cmd = "%s xf data.tar.*" % tar_cmd
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
+        except subprocess.CalledProcessError as e:
+            bb.utils.remove(tmp_dir, recurse=True)
+            bb.fatal("Unable to extract %s package. Command '%s' "
+                     "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output))
+        except OSError as e:
+            bb.utils.remove(tmp_dir, recurse=True)
+            bb.fatal("Unable to extract %s package. Command '%s' "
+                     "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
+
+        bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
+        bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
+        bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
+        os.chdir(current_dir)
+
+        return tmp_dir
+
+
+class OpkgPM(OpkgDpkgPM):
     def __init__(self, d, target_rootfs, config_file, archs, task_name='target'):
         super(OpkgPM, self).__init__(d)
 
@@ -1732,8 +1807,34 @@ class OpkgPM(PackageManager):
                             self.opkg_dir,
                             symlinks=True)
 
+    """
+    Returns a dictionary with the package info.
+    """
+    def package_info(self, pkg):
+        cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
+        return super(OpkgPM, self).package_info(pkg, cmd)
+
+    """
+    Returns the path to a tmpdir where resides the contents of a package.
+
+    Deleting the tmpdir is responsability of the caller.
+    """
+    def extract(self, pkg):
+        pkg_info = self.package_info(pkg)
+        if not pkg_info:
+            bb.fatal("Unable to get information for package '%s' while "
+                     "trying to extract the package."  % pkg)
+
+        pkg_arch = pkg_info[pkg]["arch"]
+        pkg_filename = pkg_info[pkg]["filename"]
+        pkg_path = os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
+
+        tmp_dir = super(OpkgPM, self).extract(pkg, pkg_path)
+        bb.utils.remove(os.path.join(tmp_dir, "data.tar.gz"))
+
+        return tmp_dir
 
-class DpkgPM(PackageManager):
+class DpkgPM(OpkgDpkgPM):
     def __init__(self, d, target_rootfs, archs, base_archs, apt_conf_dir=None):
         super(DpkgPM, self).__init__(d)
         self.target_rootfs = target_rootfs
@@ -1744,6 +1845,7 @@ class DpkgPM(PackageManager):
             self.apt_conf_dir = apt_conf_dir
         self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
         self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
+        self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
 
         self.apt_args = d.getVar("APT_ARGS", True)
 
@@ -2027,6 +2129,32 @@ class DpkgPM(PackageManager):
     def list_installed(self):
         return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
 
+    """
+    Returns a dictionary with the package info.
+    """
+    def package_info(self, pkg):
+        cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
+        return super(DpkgPM, self).package_info(pkg, cmd)
+
+    """
+    Returns the path to a tmpdir where resides the contents of a package.
+
+    Deleting the tmpdir is responsability of the caller.
+    """
+    def extract(self, pkg):
+        pkg_info = self.package_info(pkg)
+        if not pkg_info:
+            bb.fatal("Unable to get information for package '%s' while "
+                     "trying to extract the package."  % pkg)
+
+        pkg_arch = pkg_info[pkg]["pkgarch"]
+        pkg_filename = pkg_info[pkg]["filename"]
+        pkg_path = os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
+
+        tmp_dir = super(DpkgPM, self).extract(pkg, pkg_path)
+        bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
+
+        return tmp_dir
 
 def generate_index_files(d):
     classes = d.getVar('PACKAGE_CLASSES', True).replace("package_", "").split()
-- 
2.6.6




More information about the Openembedded-core mailing list