package_manager.py: Add extract() method for opkg and dpkg

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]

(From OE-Core rev: 7d214b34e11dc57316ed5c1c7747c4601286f6d2)

Signed-off-by: Mariano Lopez <mariano.lopez@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Mariano Lopez 2016-05-12 11:28:13 +00:00 committed by Richard Purdie
parent c5aa5246e0
commit b372a82818
1 changed files with 134 additions and 6 deletions

View File

@ -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)
class DpkgPM(PackageManager):
"""
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(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()