diff options
Diffstat (limited to 'import-layers/yocto-poky/meta/lib/oe/package_manager.py')
-rw-r--r-- | import-layers/yocto-poky/meta/lib/oe/package_manager.py | 515 |
1 files changed, 373 insertions, 142 deletions
diff --git a/import-layers/yocto-poky/meta/lib/oe/package_manager.py b/import-layers/yocto-poky/meta/lib/oe/package_manager.py index b4b359a8c..3cee9730a 100644 --- a/import-layers/yocto-poky/meta/lib/oe/package_manager.py +++ b/import-layers/yocto-poky/meta/lib/oe/package_manager.py @@ -5,9 +5,11 @@ import subprocess import shutil import multiprocessing import re +import collections import bb import tempfile import oe.utils +import oe.path import string from oe.gpg_sign import get_signer @@ -17,20 +19,79 @@ def create_index(arg): try: bb.note("Executing '%s' ..." % index_cmd) - result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True) + result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8") except subprocess.CalledProcessError as e: return("Index creation command '%s' failed with return code %d:\n%s" % - (e.cmd, e.returncode, e.output)) + (e.cmd, e.returncode, e.output.decode("utf-8"))) if result: bb.note(result) return None - -class Indexer(object): - __metaclass__ = ABCMeta - +""" +This method parse the output from the package managerand return +a dictionary with the information of the packages. This is used +when the packages are in deb or ipk format. +""" +def opkg_query(cmd_output): + verregex = re.compile(' \([=<>]* [^ )]*\)') + output = dict() + pkg = "" + arch = "" + ver = "" + filename = "" + dep = [] + pkgarch = "" + for line in cmd_output.splitlines(): + line = line.rstrip() + if ':' in line: + if line.startswith("Package: "): + pkg = line.split(": ")[1] + elif line.startswith("Architecture: "): + arch = line.split(": ")[1] + elif line.startswith("Version: "): + ver = line.split(": ")[1] + 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(", "): + dep.append(depend) + elif line.startswith("Recommends: "): + recommends = verregex.sub('', line.split(": ")[1]) + for recommend in recommends.split(", "): + dep.append("%s [REC]" % recommend) + 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, "pkgarch":pkgarch } + pkg = "" + arch = "" + ver = "" + filename = "" + dep = [] + pkgarch = "" + + if pkg: + if not filename: + filename = "%s_%s_%s.ipk" % (pkg, ver, arch) + output[pkg] = {"arch":arch, "ver":ver, + "filename":filename, "deps": dep } + + return output + + +class Indexer(object, metaclass=ABCMeta): def __init__(self, d, deploy_dir): self.d = d self.deploy_dir = deploy_dir @@ -42,13 +103,8 @@ class Indexer(object): class RpmIndexer(Indexer): def get_ml_prefix_and_os_list(self, arch_var=None, os_var=None): - package_archs = { - 'default': [], - } - - target_os = { - 'default': "", - } + package_archs = collections.OrderedDict() + target_os = collections.OrderedDict() if arch_var is not None and os_var is not None: package_archs['default'] = self.d.getVar(arch_var, True).split() @@ -79,7 +135,7 @@ class RpmIndexer(Indexer): target_os[eext[1]] = localdata.getVar("TARGET_OS", True).strip() - ml_prefix_list = dict() + ml_prefix_list = collections.OrderedDict() for mlib in package_archs: if mlib == 'default': ml_prefix_list[mlib] = package_archs[mlib] @@ -281,9 +337,7 @@ class DpkgIndexer(Indexer): -class PkgsList(object): - __metaclass__ = ABCMeta - +class PkgsList(object, metaclass=ABCMeta): def __init__(self, d, rootfs_dir): self.d = d self.rootfs_dir = rootfs_dir @@ -293,57 +347,6 @@ class PkgsList(object): pass - """ - This method parse the output from the package manager - and return a dictionary with the information of the - installed packages. This is used whne the packages are - in deb or ipk format - """ - def opkg_query(self, cmd_output): - verregex = re.compile(' \([=<>]* [^ )]*\)') - output = dict() - filename = "" - dep = [] - pkg = "" - for line in cmd_output.splitlines(): - line = line.rstrip() - if ':' in line: - if line.startswith("Package: "): - pkg = line.split(": ")[1] - elif line.startswith("Architecture: "): - arch = line.split(": ")[1] - elif line.startswith("Version: "): - ver = line.split(": ")[1] - elif line.startswith("File: "): - filename = line.split(": ")[1] - elif line.startswith("Depends: "): - depends = verregex.sub('', line.split(": ")[1]) - for depend in depends.split(", "): - dep.append(depend) - elif line.startswith("Recommends: "): - recommends = verregex.sub('', line.split(": ")[1]) - for recommend in recommends.split(", "): - dep.append("%s [REC]" % recommend) - else: - # 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 } - pkg = "" - filename = "" - dep = [] - - if pkg: - if not filename: - filename = "%s_%s_%s.ipk" % (pkg, ver, arch) - output[pkg] = {"arch":arch, "ver":ver, - "filename":filename, "deps": dep } - - return output - - class RpmPkgsList(PkgsList): def __init__(self, d, rootfs_dir, arch_var=None, os_var=None): super(RpmPkgsList, self).__init__(d, rootfs_dir) @@ -357,10 +360,10 @@ class RpmPkgsList(PkgsList): # Determine rpm version cmd = "%s --version" % self.rpm_cmd try: - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8") except subprocess.CalledProcessError as e: bb.fatal("Getting rpm version failed. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) ''' Translate the RPM/Smart format names to the OE multilib format names @@ -401,10 +404,10 @@ class RpmPkgsList(PkgsList): "-t", self.image_rpmlib] try: - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip() + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8") except subprocess.CalledProcessError as e: bb.fatal("Cannot get the package dependencies. Command '%s' " - "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output)) + "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) return output @@ -415,10 +418,10 @@ class RpmPkgsList(PkgsList): try: # bb.note(cmd) - tmp_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip() + tmp_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip().decode("utf-8") except subprocess.CalledProcessError as e: bb.fatal("Cannot get the installed packages list. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) output = dict() deps = dict() @@ -475,11 +478,13 @@ class OpkgPkgsList(PkgsList): # output streams separately and check for empty stderr. p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) cmd_output, cmd_stderr = p.communicate() + cmd_output = cmd_output.decode("utf-8") + cmd_stderr = cmd_stderr.decode("utf-8") if p.returncode or cmd_stderr: bb.fatal("Cannot get the installed packages list. Command '%s' " "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr)) - return self.opkg_query(cmd_output) + return opkg_query(cmd_output) class DpkgPkgsList(PkgsList): @@ -492,19 +497,18 @@ class DpkgPkgsList(PkgsList): cmd.append("-f=Package: ${Package}\nArchitecture: ${PackageArch}\nVersion: ${Version}\nFile: ${Package}_${Version}_${Architecture}.deb\nDepends: ${Depends}\nRecommends: ${Recommends}\n\n") try: - cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip() + cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8") except subprocess.CalledProcessError as e: bb.fatal("Cannot get the installed packages list. Command '%s' " - "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output)) + "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) - return self.opkg_query(cmd_output) + return opkg_query(cmd_output) -class PackageManager(object): +class PackageManager(object, metaclass=ABCMeta): """ This is an abstract class. Do not instantiate this directly. """ - __metaclass__ = ABCMeta def __init__(self, d): self.d = d @@ -594,15 +598,15 @@ class PackageManager(object): globs] exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY', True) if exclude: - cmd.extend(['-x', exclude]) + cmd.extend(['--exclude=' + '|'.join(exclude.split())]) try: bb.note("Installing complementary packages ...") bb.note('Running %s' % cmd) - complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8") except subprocess.CalledProcessError as e: bb.fatal("Could not compute complementary packages list. Command " "'%s' returned %d:\n%s" % - (' '.join(cmd), e.returncode, e.output)) + (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) self.install(complementary_pkgs.split(), attempt_only=True) os.remove(installed_pkgs_file) @@ -631,8 +635,8 @@ class PackageManager(object): def construct_uris(self, uris, base_paths): def _append(arr1, arr2, sep='/'): res = [] - narr1 = map(lambda a: string.rstrip(a, sep), arr1) - narr2 = map(lambda a: string.lstrip(string.rstrip(a, sep), sep), arr2) + narr1 = [a.rstrip(sep) for a in arr1] + narr2 = [a.rstrip(sep).lstrip(sep) for a in arr2] for a1 in narr1: if arr2: for a2 in narr2: @@ -682,7 +686,8 @@ class RpmPM(PackageManager): if not os.path.exists(self.d.expand('${T}/saved')): bb.utils.mkdirhier(self.d.expand('${T}/saved')) - self.indexer = RpmIndexer(self.d, self.deploy_dir) + packageindex_dir = os.path.join(self.d.getVar('WORKDIR', True), 'rpms') + self.indexer = RpmIndexer(self.d, packageindex_dir) self.pkgs_list = RpmPkgsList(self.d, self.target_rootfs, arch_var, os_var) self.ml_prefix_list, self.ml_os_list = self.indexer.get_ml_prefix_and_os_list(arch_var, os_var) @@ -697,18 +702,19 @@ class RpmPM(PackageManager): arch_list = self.feed_archs.split() else: # List must be prefered to least preferred order - default_platform_extra = set() - platform_extra = set() + default_platform_extra = list() + platform_extra = list() bbextendvariant = self.d.getVar('BBEXTENDVARIANT', True) or "" for mlib in self.ml_os_list: for arch in self.ml_prefix_list[mlib]: plt = arch.replace('-', '_') + '-.*-' + self.ml_os_list[mlib] if mlib == bbextendvariant: - default_platform_extra.add(plt) + if plt not in default_platform_extra: + default_platform_extra.append(plt) else: - platform_extra.add(plt) - - platform_extra = platform_extra.union(default_platform_extra) + if plt not in platform_extra: + platform_extra.append(plt) + platform_extra = default_platform_extra + platform_extra for canonical_arch in platform_extra: arch = canonical_arch.split('-')[0] @@ -724,7 +730,7 @@ class RpmPM(PackageManager): for uri in feed_uris: if arch_list: for arch in arch_list: - bb.note('Note: adding Smart channel url%d%s (%s)' % + bb.note('Adding Smart channel url%d%s (%s)' % (uri_iterator, arch, channel_priority)) self._invoke_smart('channel --add url%d-%s type=rpm-md baseurl=%s/%s -y' % (uri_iterator, arch, uri, arch)) @@ -732,7 +738,7 @@ class RpmPM(PackageManager): (uri_iterator, arch, channel_priority)) channel_priority -= 5 else: - bb.note('Note: adding Smart channel url%d (%s)' % + bb.note('Adding Smart channel url%d (%s)' % (uri_iterator, channel_priority)) self._invoke_smart('channel --add url%d type=rpm-md baseurl=%s -y' % (uri_iterator, uri)) @@ -774,12 +780,12 @@ class RpmPM(PackageManager): try: complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT, - shell=True) + shell=True).decode("utf-8") # bb.note(complementary_pkgs) return complementary_pkgs except subprocess.CalledProcessError as e: bb.fatal("Could not invoke smart. Command " - "'%s' returned %d:\n%s" % (cmd, e.returncode, e.output)) + "'%s' returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) def _search_pkg_name_in_feeds(self, pkg, feed_archs): for arch in feed_archs: @@ -798,7 +804,7 @@ class RpmPM(PackageManager): (self.smart_cmd, self.smart_opt, pkg) cmd += " | sed -ne 's/ *Provides://p'" bb.note('cmd: %s' % cmd) - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8") # Found a provider if output: bb.note('Found providers for %s: %s' % (pkg, output)) @@ -833,11 +839,13 @@ class RpmPM(PackageManager): new_pkg = self._search_pkg_name_in_feeds(subst, feed_archs) if not new_pkg: # Failed to translate, package not found! - err_msg = '%s not found in the %s feeds (%s).\n' % \ - (pkg, mlib, " ".join(feed_archs)) + err_msg = '%s not found in the %s feeds (%s) in %s.' % \ + (pkg, mlib, " ".join(feed_archs), self.d.getVar('DEPLOY_DIR_RPM', True)) if not attempt_only: - err_msg += " ".join(self.fullpkglist) - bb.fatal(err_msg) + bb.error(err_msg) + bb.fatal("This is often caused by an empty package declared " \ + "in a recipe's PACKAGES variable. (Empty packages are " \ + "not constructed unless ALLOW_EMPTY_<pkg> = '1' is used.)") bb.warn(err_msg) else: new_pkgs.append(new_pkg) @@ -850,11 +858,13 @@ class RpmPM(PackageManager): default_archs = self.ml_prefix_list['default'] new_pkg = self._search_pkg_name_in_feeds(pkg, default_archs) if not new_pkg: - err_msg = '%s not found in the base feeds (%s).\n' % \ - (pkg, ' '.join(default_archs)) + err_msg = '%s not found in the feeds (%s) in %s.' % \ + (pkg, " ".join(default_archs), self.d.getVar('DEPLOY_DIR_RPM', True)) if not attempt_only: - err_msg += " ".join(self.fullpkglist) - bb.fatal(err_msg) + bb.error(err_msg) + bb.fatal("This is often caused by an empty package declared " \ + "in a recipe's PACKAGES variable. (Empty packages are " \ + "not constructed unless ALLOW_EMPTY_<pkg> = '1' is used.)") bb.warn(err_msg) else: new_pkgs.append(new_pkg) @@ -946,7 +956,7 @@ class RpmPM(PackageManager): subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) except subprocess.CalledProcessError as e: bb.fatal("Create rpm database failed. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) # Import GPG key to RPM database of the target system if self.d.getVar('RPM_SIGN_PACKAGES', True) == '1': pubkey_path = self.d.getVar('RPM_GPG_PUBKEY', True) @@ -1006,9 +1016,19 @@ class RpmPM(PackageManager): ch_already_added = [] for canonical_arch in platform_extra: arch = canonical_arch.split('-')[0] - arch_channel = os.path.join(self.deploy_dir, arch) - if os.path.exists(arch_channel) and not arch in ch_already_added: - bb.note('Note: adding Smart channel %s (%s)' % + arch_channel = os.path.join(self.d.getVar('WORKDIR', True), 'rpms', arch) + oe.path.remove(arch_channel) + deploy_arch_dir = os.path.join(self.deploy_dir, arch) + if not os.path.exists(deploy_arch_dir): + continue + + lockfilename = self.d.getVar('DEPLOY_DIR_RPM', True) + "/rpm.lock" + lf = bb.utils.lockfile(lockfilename, False) + oe.path.copyhardlinktree(deploy_arch_dir, arch_channel) + bb.utils.unlockfile(lf) + + if not arch in ch_already_added: + bb.note('Adding Smart channel %s (%s)' % (arch, channel_priority)) self._invoke_smart('channel --add %s type=rpm-md baseurl=%s -y' % (arch, arch_channel)) @@ -1080,8 +1100,8 @@ class RpmPM(PackageManager): native_root) open(self.scriptlet_wrapper, 'w+').write(scriptlet_content) - bb.note("Note: configuring RPM cross-install scriptlet_wrapper") - os.chmod(self.scriptlet_wrapper, 0755) + bb.note("configuring RPM cross-install scriptlet_wrapper") + os.chmod(self.scriptlet_wrapper, 0o755) cmd = 'config --set rpm-extra-macros._cross_scriptlet_wrapper=%s' % \ self.scriptlet_wrapper self._invoke_smart(cmd) @@ -1099,7 +1119,7 @@ class RpmPM(PackageManager): sub_rdep = sub_data.get("RDEPENDS_" + pkg) if not sub_rdep: continue - done = bb.utils.explode_dep_versions2(sub_rdep).keys() + done = list(bb.utils.explode_dep_versions2(sub_rdep).keys()) next = done # Find all the rdepends on dependency chain while next: @@ -1182,6 +1202,9 @@ class RpmPM(PackageManager): new_depends[new_depend] = deps[depend] pkgs = bb.utils.join_deps(new_depends, commasep=True).split(', ') pkgs = self._pkg_translate_oe_to_smart(pkgs, attempt_only) + if not pkgs: + bb.note("There are no packages to install") + return if not attempt_only: bb.note('to be installed: %s' % ' '.join(pkgs)) cmd = "%s %s install -y %s" % \ @@ -1193,11 +1216,11 @@ class RpmPM(PackageManager): cmd = "%s %s install --attempt -y %s" % \ (self.smart_cmd, self.smart_opt, ' '.join(pkgs)) try: - output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) + output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8") bb.note(output) except subprocess.CalledProcessError as e: bb.fatal("Unable to install packages. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) ''' Remove pkgs with smart, the pkg name is smart/rpm format @@ -1223,11 +1246,11 @@ class RpmPM(PackageManager): try: bb.note(cmd) - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8") bb.note(output) except subprocess.CalledProcessError as e: bb.note("Unable to remove packages. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) def upgrade(self): bb.note('smart upgrade') @@ -1300,7 +1323,7 @@ class RpmPM(PackageManager): install_pkgs.append(pkg) except subprocess.CalledProcessError as e: bb.note("Unable to dump install packages. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) # Recovery rpmsys channel self._invoke_smart('channel --enable rpmsys') return install_pkgs @@ -1342,7 +1365,7 @@ class RpmPM(PackageManager): available_pkgs.append(pkg.strip()) except subprocess.CalledProcessError as e: bb.note("Unable to list all available packages. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) self.fullpkglist = available_pkgs @@ -1369,12 +1392,12 @@ class RpmPM(PackageManager): try: bb.note(cmd) - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip() + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip().decode("utf-8") bb.note(output) - os.chmod(saved_dir, 0755) + os.chmod(saved_dir, 0o755) except subprocess.CalledProcessError as e: bb.fatal("Invoke save_rpmpostinst failed. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) '''Write common configuration for target usage''' def rpm_setup_smart_target_config(self): @@ -1398,8 +1421,157 @@ class RpmPM(PackageManager): for f in rpm_db_locks: bb.utils.remove(f, True) + """ + Returns a dictionary with the package info. + """ + def package_info(self, pkg): + cmd = "%s %s info --urls %s" % (self.smart_cmd, self.smart_opt, pkg) + try: + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8") + except subprocess.CalledProcessError as e: + bb.fatal("Unable to list available packages. Command '%s' " + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) + + # Set default values to avoid UnboundLocalError + arch = "" + ver = "" + filename = "" + + #Parse output + for line in output.splitlines(): + line = line.rstrip() + if line.startswith("Name:"): + pkg = line.split(": ")[1] + elif line.startswith("Version:"): + tmp_str = line.split(": ")[1] + ver, arch = tmp_str.split("@") + break + + # Get filename + index = re.search("^URLs", output, re.MULTILINE) + tmp_str = output[index.end():] + for line in tmp_str.splitlines(): + if "/" in line: + line = line.lstrip() + filename = line.split(" ")[0] + break + + # To have the same data type than other package_info methods + filepath = os.path.join(self.deploy_dir, arch, filename) + pkg_dict = {} + pkg_dict[pkg] = {"arch":arch, "ver":ver, "filename":filename, + "filepath": filepath} + + return pkg_dict + + """ + 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_path = pkg_info[pkg]["filepath"] + + cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio") + rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio") + + 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 %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_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.decode("utf-8"))) + 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)) + os.chdir(current_dir) + + return tmp_dir + + +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).decode("utf-8") + except subprocess.CalledProcessError as e: + bb.fatal("Unable to list available packages. Command '%s' " + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) + 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_info): + + ar_cmd = bb.utils.which(os.getenv("PATH"), "ar") + tar_cmd = bb.utils.which(os.getenv("PATH"), "tar") + pkg_path = pkg_info[pkg]["filepath"] + + 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.decode("utf-8"))) + 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) -class OpkgPM(PackageManager): + return tmp_dir + + +class OpkgPM(OpkgDpkgPM): def __init__(self, d, target_rootfs, config_file, archs, task_name='target'): super(OpkgPM, self).__init__(d) @@ -1411,7 +1583,7 @@ class OpkgPM(PackageManager): self.deploy_dir = self.d.getVar("DEPLOY_DIR_IPK", True) self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock") self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg") - self.opkg_args = "--volatile-cache -f %s -o %s " % (self.config_file, target_rootfs) + self.opkg_args = "--volatile-cache -f %s -t %s -o %s " % (self.config_file, self.d.expand('${T}/ipktemp/') ,target_rootfs) self.opkg_args += self.d.getVar("OPKG_ARGS", True) opkg_lib_dir = self.d.getVar('OPKGLIBDIR', True) @@ -1552,12 +1724,12 @@ class OpkgPM(PackageManager): for arch in archs: if (self.feed_archs is None) and (not os.path.exists(os.path.join(self.deploy_dir, arch))): continue - bb.note('Note: adding opkg feed url-%s-%d (%s)' % + bb.note('Adding opkg feed url-%s-%d (%s)' % (arch, uri_iterator, uri)) config_file.write("src/gz uri-%s-%d %s/%s\n" % (arch, uri_iterator, uri, arch)) else: - bb.note('Note: adding opkg feed url-%d (%s)' % + bb.note('Adding opkg feed url-%d (%s)' % (uri_iterator, uri)) config_file.write("src/gz uri-%d %s\n" % (uri_iterator, uri)) @@ -1574,7 +1746,7 @@ class OpkgPM(PackageManager): except subprocess.CalledProcessError as e: self.deploy_dir_unlock() bb.fatal("Unable to update the package index files. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) self.deploy_dir_unlock() @@ -1595,12 +1767,12 @@ class OpkgPM(PackageManager): try: bb.note("Installing the following packages: %s" % ' '.join(pkgs)) bb.note(cmd) - output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) + output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8") bb.note(output) except subprocess.CalledProcessError as e: (bb.fatal, bb.note)[attempt_only]("Unable to install packages. " "Command '%s' returned %d:\n%s" % - (cmd, e.returncode, e.output)) + (cmd, e.returncode, e.output.decode("utf-8"))) def remove(self, pkgs, with_dependencies=True): if with_dependencies: @@ -1612,11 +1784,11 @@ class OpkgPM(PackageManager): try: bb.note(cmd) - output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) + output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8") bb.note(output) except subprocess.CalledProcessError as e: bb.fatal("Unable to remove packages. Command '%s' " - "returned %d:\n%s" % (e.cmd, e.returncode, e.output)) + "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8"))) def write_index(self): self.deploy_dir_lock() @@ -1659,10 +1831,10 @@ class OpkgPM(PackageManager): pkg_info = cmd + pkg try: - output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip() + output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip().decode("utf-8") except subprocess.CalledProcessError as e: bb.fatal("Cannot get package info. Command '%s' " - "returned %d:\n%s" % (pkg_info, e.returncode, e.output)) + "returned %d:\n%s" % (pkg_info, e.returncode, e.output.decode("utf-8"))) if output == "": bb.note("Ignored bad recommendation: '%s' is " @@ -1699,7 +1871,7 @@ class OpkgPM(PackageManager): subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) except subprocess.CalledProcessError as e: bb.fatal("Unable to update. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) # Dummy installation cmd = "%s %s --noaction install %s " % (self.opkg_cmd, @@ -1709,7 +1881,7 @@ class OpkgPM(PackageManager): output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) except subprocess.CalledProcessError as e: bb.fatal("Unable to dummy install packages. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) bb.utils.remove(temp_rootfs, True) @@ -1734,8 +1906,37 @@ 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) + pkg_info = super(OpkgPM, self).package_info(pkg, cmd) + + pkg_arch = pkg_info[pkg]["arch"] + pkg_filename = pkg_info[pkg]["filename"] + pkg_info[pkg]["filepath"] = \ + os.path.join(self.deploy_dir, pkg_arch, pkg_filename) + + return pkg_info + + """ + 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) + + tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info) + bb.utils.remove(os.path.join(tmp_dir, "data.tar.gz")) -class DpkgPM(PackageManager): + 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 @@ -1746,6 +1947,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) @@ -1823,7 +2025,7 @@ class DpkgPM(PackageManager): subprocess.check_output(p_full, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: bb.note("%s for package %s failed with %d:\n%s" % - (suffix[1], pkg_name, e.returncode, e.output)) + (suffix[1], pkg_name, e.returncode, e.output.decode("utf-8"))) failed_pkgs.append(pkg_name) break @@ -1841,7 +2043,7 @@ class DpkgPM(PackageManager): subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: bb.fatal("Unable to update the package index files. Command '%s' " - "returned %d:\n%s" % (e.cmd, e.returncode, e.output)) + "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8"))) self.deploy_dir_unlock() @@ -1860,7 +2062,7 @@ class DpkgPM(PackageManager): except subprocess.CalledProcessError as e: (bb.fatal, bb.note)[attempt_only]("Unable to install packages. " "Command '%s' returned %d:\n%s" % - (cmd, e.returncode, e.output)) + (cmd, e.returncode, e.output.decode("utf-8"))) # rename *.dpkg-new files/dirs for root, dirs, files in os.walk(self.target_rootfs): @@ -1891,7 +2093,7 @@ class DpkgPM(PackageManager): subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: bb.fatal("Unable to remove packages. Command '%s' " - "returned %d:\n%s" % (e.cmd, e.returncode, e.output)) + "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8"))) def write_index(self): self.deploy_dir_lock() @@ -1925,11 +2127,11 @@ class DpkgPM(PackageManager): for uri in feed_uris: if arch_list: for arch in arch_list: - bb.note('Note: adding dpkg channel at (%s)' % uri) + bb.note('Adding dpkg channel at (%s)' % uri) sources_file.write("deb %s/%s ./\n" % (uri, arch)) else: - bb.note('Note: adding dpkg channel at (%s)' % uri) + bb.note('Adding dpkg channel at (%s)' % uri) sources_file.write("deb %s ./\n" % uri) def _create_configs(self, archs, base_archs): @@ -2024,11 +2226,40 @@ class DpkgPM(PackageManager): subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: bb.fatal("Cannot fix broken dependencies. Command '%s' " - "returned %d:\n%s" % (cmd, e.returncode, e.output)) + "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) 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) + pkg_info = super(DpkgPM, self).package_info(pkg, cmd) + + pkg_arch = pkg_info[pkg]["pkgarch"] + pkg_filename = pkg_info[pkg]["filename"] + pkg_info[pkg]["filepath"] = \ + os.path.join(self.deploy_dir, pkg_arch, pkg_filename) + + return pkg_info + + """ + 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) + + tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info) + 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() |