summaryrefslogtreecommitdiffstats
path: root/meta/lib
diff options
context:
space:
mode:
authorFredrik Gustafsson <fredrik.gustafsson@axis.com>2020-07-24 16:42:39 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2020-07-27 19:58:10 +0100
commitc5bea36b7275f1692f5847af84d320b674a4c62e (patch)
treead4222a7564b5710472b0673e0503cb03884f6d4 /meta/lib
parent635305fe27b12fca554d694da00ba31d4b552d39 (diff)
downloadpoky-c5bea36b7275f1692f5847af84d320b674a4c62e.tar.gz
rpm: Move package manager to its own dir
This is part of a refactor that will split the package manager code so that it's possible to use other package managers in other layers. RP: Fixes to parse/build (From OE-Core rev: 8b776ed9ed291dd8e112621561762449c7eb5ee2) Signed-off-by: Fredrik Gustafsson <fredrigu@axis.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib')
-rw-r--r--meta/lib/oe/package_manager/__init__.py400
-rw-r--r--meta/lib/oe/package_manager/rpm/__init__.py399
-rw-r--r--meta/lib/oe/package_manager/rpm/rootfs.py2
-rw-r--r--meta/lib/oe/package_manager/rpm/sdk.py2
-rw-r--r--meta/lib/oe/rootfs.py1
-rw-r--r--meta/lib/oe/sdk.py1
-rw-r--r--meta/lib/oeqa/utils/package_manager.py4
7 files changed, 408 insertions, 401 deletions
diff --git a/meta/lib/oe/package_manager/__init__.py b/meta/lib/oe/package_manager/__init__.py
index 35e5cff073..36606d8cb3 100644
--- a/meta/lib/oe/package_manager/__init__.py
+++ b/meta/lib/oe/package_manager/__init__.py
@@ -149,43 +149,6 @@ class Indexer(object, metaclass=ABCMeta):
149 def write_index(self): 149 def write_index(self):
150 pass 150 pass
151 151
152
153class RpmIndexer(Indexer):
154 def write_index(self):
155 self.do_write_index(self.deploy_dir)
156
157 def do_write_index(self, deploy_dir):
158 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
159 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
160 else:
161 signer = None
162
163 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
164 result = create_index("%s --update -q %s" % (createrepo_c, deploy_dir))
165 if result:
166 bb.fatal(result)
167
168 # Sign repomd
169 if signer:
170 sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
171 is_ascii_sig = (sig_type.upper() != "BIN")
172 signer.detach_sign(os.path.join(deploy_dir, 'repodata', 'repomd.xml'),
173 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
174 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
175 armor=is_ascii_sig)
176
177class RpmSubdirIndexer(RpmIndexer):
178 def write_index(self):
179 bb.note("Generating package index for %s" %(self.deploy_dir))
180 self.do_write_index(self.deploy_dir)
181 for entry in os.walk(self.deploy_dir):
182 if os.path.samefile(self.deploy_dir, entry[0]):
183 for dir in entry[1]:
184 if dir != 'repodata':
185 dir_path = oe.path.join(self.deploy_dir, dir)
186 bb.note("Generating package index for %s" %(dir_path))
187 self.do_write_index(dir_path)
188
189class OpkgIndexer(Indexer): 152class OpkgIndexer(Indexer):
190 def write_index(self): 153 def write_index(self):
191 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS", 154 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
@@ -323,10 +286,6 @@ class PkgsList(object, metaclass=ABCMeta):
323 def list_pkgs(self): 286 def list_pkgs(self):
324 pass 287 pass
325 288
326class RpmPkgsList(PkgsList):
327 def list_pkgs(self):
328 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR'), needfeed=False).list_installed()
329
330class OpkgPkgsList(PkgsList): 289class OpkgPkgsList(PkgsList):
331 def __init__(self, d, rootfs_dir, config_file): 290 def __init__(self, d, rootfs_dir, config_file):
332 super(OpkgPkgsList, self).__init__(d, rootfs_dir) 291 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
@@ -737,363 +696,6 @@ def create_packages_dir(d, subrepo_dir, deploydir, taskname, filterbydependencie
737 else: 696 else:
738 raise 697 raise
739 698
740class RpmPM(PackageManager):
741 def __init__(self,
742 d,
743 target_rootfs,
744 target_vendor,
745 task_name='target',
746 arch_var=None,
747 os_var=None,
748 rpm_repo_workdir="oe-rootfs-repo",
749 filterbydependencies=True,
750 needfeed=True):
751 super(RpmPM, self).__init__(d, target_rootfs)
752 self.target_vendor = target_vendor
753 self.task_name = task_name
754 if arch_var == None:
755 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
756 else:
757 self.archs = self.d.getVar(arch_var).replace("-","_")
758 if task_name == "host":
759 self.primary_arch = self.d.getVar('SDK_ARCH')
760 else:
761 self.primary_arch = self.d.getVar('MACHINE_ARCH')
762
763 if needfeed:
764 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
765 create_packages_dir(self.d, oe.path.join(self.rpm_repo_dir, "rpm"), d.getVar("DEPLOY_DIR_RPM"), "package_write_rpm", filterbydependencies)
766
767 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
768 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
769 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
770 self.packaging_data_dirs = ['etc/rpm', 'etc/rpmrc', 'etc/dnf', 'var/lib/rpm', 'var/lib/dnf', 'var/cache/dnf']
771 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
772 self.task_name)
773 if not os.path.exists(self.d.expand('${T}/saved')):
774 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
775
776 def _configure_dnf(self):
777 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
778 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
779 # This prevents accidental matching against libsolv's built-in policies
780 if len(archs) <= 1:
781 archs = archs + ["bogusarch"]
782 # This architecture needs to be upfront so that packages using it are properly prioritized
783 archs = ["sdk_provides_dummy_target"] + archs
784 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
785 bb.utils.mkdirhier(confdir)
786 open(confdir + "arch", 'w').write(":".join(archs))
787 distro_codename = self.d.getVar('DISTRO_CODENAME')
788 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
789
790 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
791
792
793 def _configure_rpm(self):
794 # We need to configure rpm to use our primary package architecture as the installation architecture,
795 # and to make it compatible with other package architectures that we use.
796 # Otherwise it will refuse to proceed with packages installation.
797 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
798 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
799 bb.utils.mkdirhier(platformconfdir)
800 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
801 with open(rpmrcconfdir + "rpmrc", 'w') as f:
802 f.write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
803 f.write("buildarch_compat: %s: noarch\n" % self.primary_arch)
804
805 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
806 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
807 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
808
809 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
810 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
811 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
812 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
813 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
814 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
815 try:
816 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
817 except subprocess.CalledProcessError as e:
818 bb.fatal("Importing GPG key failed. Command '%s' "
819 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
820
821 def create_configs(self):
822 self._configure_dnf()
823 self._configure_rpm()
824
825 def write_index(self):
826 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
827 lf = bb.utils.lockfile(lockfilename, False)
828 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
829 bb.utils.unlockfile(lf)
830
831 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
832 from urllib.parse import urlparse
833
834 if feed_uris == "":
835 return
836
837 gpg_opts = ''
838 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
839 gpg_opts += 'repo_gpgcheck=1\n'
840 gpg_opts += 'gpgkey=file://%s/pki/packagefeed-gpg/PACKAGEFEED-GPG-KEY-%s-%s\n' % (self.d.getVar('sysconfdir'), self.d.getVar('DISTRO'), self.d.getVar('DISTRO_CODENAME'))
841
842 if self.d.getVar('RPM_SIGN_PACKAGES') != '1':
843 gpg_opts += 'gpgcheck=0\n'
844
845 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
846 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
847 for uri in remote_uris:
848 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
849 if feed_archs is not None:
850 for arch in feed_archs.split():
851 repo_uri = uri + "/" + arch
852 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
853 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
854 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
855 "[%s]\nname=%s\nbaseurl=%s\n%s\n" % (repo_id, repo_name, repo_uri, gpg_opts))
856 else:
857 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
858 repo_uri = uri
859 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
860 "[%s]\nname=%s\nbaseurl=%s\n%s" % (repo_base, repo_name, repo_uri, gpg_opts))
861
862 def _prepare_pkg_transaction(self):
863 os.environ['D'] = self.target_rootfs
864 os.environ['OFFLINE_ROOT'] = self.target_rootfs
865 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
866 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
867 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
868 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
869
870
871 def install(self, pkgs, attempt_only = False):
872 if len(pkgs) == 0:
873 return
874 self._prepare_pkg_transaction()
875
876 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
877 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
878 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
879
880 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
881 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
882 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
883 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
884 ["install"] +
885 pkgs)
886
887 failed_scriptlets_pkgnames = collections.OrderedDict()
888 for line in output.splitlines():
889 if line.startswith("Error in POSTIN scriptlet in rpm package"):
890 failed_scriptlets_pkgnames[line.split()[-1]] = True
891
892 if len(failed_scriptlets_pkgnames) > 0:
893 failed_postinsts_abort(list(failed_scriptlets_pkgnames.keys()), self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
894
895 def remove(self, pkgs, with_dependencies = True):
896 if not pkgs:
897 return
898
899 self._prepare_pkg_transaction()
900
901 if with_dependencies:
902 self._invoke_dnf(["remove"] + pkgs)
903 else:
904 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
905 args = ["-e", "-v", "--nodeps", "--root=%s" %self.target_rootfs]
906
907 try:
908 bb.note("Running %s" % ' '.join([cmd] + args + pkgs))
909 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
910 bb.note(output)
911 except subprocess.CalledProcessError as e:
912 bb.fatal("Could not invoke rpm. Command "
913 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
914
915 def upgrade(self):
916 self._prepare_pkg_transaction()
917 self._invoke_dnf(["upgrade"])
918
919 def autoremove(self):
920 self._prepare_pkg_transaction()
921 self._invoke_dnf(["autoremove"])
922
923 def remove_packaging_data(self):
924 self._invoke_dnf(["clean", "all"])
925 for dir in self.packaging_data_dirs:
926 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
927
928 def backup_packaging_data(self):
929 # Save the packaging dirs for increment rpm image generation
930 if os.path.exists(self.saved_packaging_data):
931 bb.utils.remove(self.saved_packaging_data, True)
932 for i in self.packaging_data_dirs:
933 source_dir = oe.path.join(self.target_rootfs, i)
934 target_dir = oe.path.join(self.saved_packaging_data, i)
935 if os.path.isdir(source_dir):
936 shutil.copytree(source_dir, target_dir, symlinks=True)
937 elif os.path.isfile(source_dir):
938 shutil.copy2(source_dir, target_dir)
939
940 def recovery_packaging_data(self):
941 # Move the rpmlib back
942 if os.path.exists(self.saved_packaging_data):
943 for i in self.packaging_data_dirs:
944 target_dir = oe.path.join(self.target_rootfs, i)
945 if os.path.exists(target_dir):
946 bb.utils.remove(target_dir, True)
947 source_dir = oe.path.join(self.saved_packaging_data, i)
948 if os.path.isdir(source_dir):
949 shutil.copytree(source_dir, target_dir, symlinks=True)
950 elif os.path.isfile(source_dir):
951 shutil.copy2(source_dir, target_dir)
952
953 def list_installed(self):
954 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
955 print_output = False)
956 packages = {}
957 current_package = None
958 current_deps = None
959 current_state = "initial"
960 for line in output.splitlines():
961 if line.startswith("Package:"):
962 package_info = line.split(" ")[1:]
963 current_package = package_info[0]
964 package_arch = package_info[1]
965 package_version = package_info[2]
966 package_rpm = package_info[3]
967 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
968 current_deps = []
969 elif line.startswith("Dependencies:"):
970 current_state = "dependencies"
971 elif line.startswith("Recommendations"):
972 current_state = "recommendations"
973 elif line.startswith("DependenciesEndHere:"):
974 current_state = "initial"
975 packages[current_package]["deps"] = current_deps
976 elif len(line) > 0:
977 if current_state == "dependencies":
978 current_deps.append(line)
979 elif current_state == "recommendations":
980 current_deps.append("%s [REC]" % line)
981
982 return packages
983
984 def update(self):
985 self._invoke_dnf(["makecache", "--refresh"])
986
987 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
988 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
989
990 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
991 standard_dnf_args = ["-v", "--rpmverbosity=info", "-y",
992 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
993 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
994 "--installroot=%s" % (self.target_rootfs),
995 "--setopt=logdir=%s" % (self.d.getVar('T'))
996 ]
997 if hasattr(self, "rpm_repo_dir"):
998 standard_dnf_args.append("--repofrompath=oe-repo,%s" % (self.rpm_repo_dir))
999 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
1000 bb.note('Running %s' % ' '.join(cmd))
1001 try:
1002 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
1003 if print_output:
1004 bb.debug(1, output)
1005 return output
1006 except subprocess.CalledProcessError as e:
1007 if print_output:
1008 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
1009 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
1010 else:
1011 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
1012 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
1013 return e.output.decode("utf-8")
1014
1015 def dump_install_solution(self, pkgs):
1016 open(self.solution_manifest, 'w').write(" ".join(pkgs))
1017 return pkgs
1018
1019 def load_old_install_solution(self):
1020 if not os.path.exists(self.solution_manifest):
1021 return []
1022 with open(self.solution_manifest, 'r') as fd:
1023 return fd.read().split()
1024
1025 def _script_num_prefix(self, path):
1026 files = os.listdir(path)
1027 numbers = set()
1028 numbers.add(99)
1029 for f in files:
1030 numbers.add(int(f.split("-")[0]))
1031 return max(numbers) + 1
1032
1033 def save_rpmpostinst(self, pkg):
1034 bb.note("Saving postinstall script of %s" % (pkg))
1035 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
1036 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
1037
1038 try:
1039 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
1040 except subprocess.CalledProcessError as e:
1041 bb.fatal("Could not invoke rpm. Command "
1042 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
1043
1044 # may need to prepend #!/bin/sh to output
1045
1046 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
1047 bb.utils.mkdirhier(target_path)
1048 num = self._script_num_prefix(target_path)
1049 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
1050 open(saved_script_name, 'w').write(output)
1051 os.chmod(saved_script_name, 0o755)
1052
1053 def _handle_intercept_failure(self, registered_pkgs):
1054 rpm_postinsts_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
1055 bb.utils.mkdirhier(rpm_postinsts_dir)
1056
1057 # Save the package postinstalls in /etc/rpm-postinsts
1058 for pkg in registered_pkgs.split():
1059 self.save_rpmpostinst(pkg)
1060
1061 def extract(self, pkg):
1062 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
1063 pkg_name = output.splitlines()[-1]
1064 if not pkg_name.endswith(".rpm"):
1065 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
1066 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
1067
1068 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
1069 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
1070
1071 if not os.path.isfile(pkg_path):
1072 bb.fatal("Unable to extract package for '%s'."
1073 "File %s doesn't exists" % (pkg, pkg_path))
1074
1075 tmp_dir = tempfile.mkdtemp()
1076 current_dir = os.getcwd()
1077 os.chdir(tmp_dir)
1078
1079 try:
1080 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
1081 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1082 except subprocess.CalledProcessError as e:
1083 bb.utils.remove(tmp_dir, recurse=True)
1084 bb.fatal("Unable to extract %s package. Command '%s' "
1085 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
1086 except OSError as e:
1087 bb.utils.remove(tmp_dir, recurse=True)
1088 bb.fatal("Unable to extract %s package. Command '%s' "
1089 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
1090
1091 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1092 os.chdir(current_dir)
1093
1094 return tmp_dir
1095
1096
1097class OpkgDpkgPM(PackageManager): 699class OpkgDpkgPM(PackageManager):
1098 def __init__(self, d, target_rootfs): 700 def __init__(self, d, target_rootfs):
1099 """ 701 """
@@ -1842,6 +1444,8 @@ class DpkgPM(OpkgDpkgPM):
1842 return tmp_dir 1444 return tmp_dir
1843 1445
1844def generate_index_files(d): 1446def generate_index_files(d):
1447 from oe.package_manager.rpm import RpmSubdirIndexer
1448
1845 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split() 1449 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
1846 1450
1847 indexer_map = { 1451 indexer_map = {
diff --git a/meta/lib/oe/package_manager/rpm/__init__.py b/meta/lib/oe/package_manager/rpm/__init__.py
index a2094304c9..6183f81d58 100644
--- a/meta/lib/oe/package_manager/rpm/__init__.py
+++ b/meta/lib/oe/package_manager/rpm/__init__.py
@@ -1,3 +1,402 @@
1# 1#
2# SPDX-License-Identifier: GPL-2.0-only 2# SPDX-License-Identifier: GPL-2.0-only
3# 3#
4
5from oe.package_manager import *
6
7class RpmIndexer(Indexer):
8 def write_index(self):
9 self.do_write_index(self.deploy_dir)
10
11 def do_write_index(self, deploy_dir):
12 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
13 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
14 else:
15 signer = None
16
17 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
18 result = create_index("%s --update -q %s" % (createrepo_c, deploy_dir))
19 if result:
20 bb.fatal(result)
21
22 # Sign repomd
23 if signer:
24 sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
25 is_ascii_sig = (sig_type.upper() != "BIN")
26 signer.detach_sign(os.path.join(deploy_dir, 'repodata', 'repomd.xml'),
27 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
28 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
29 armor=is_ascii_sig)
30
31class RpmSubdirIndexer(RpmIndexer):
32 def write_index(self):
33 bb.note("Generating package index for %s" %(self.deploy_dir))
34 self.do_write_index(self.deploy_dir)
35 for entry in os.walk(self.deploy_dir):
36 if os.path.samefile(self.deploy_dir, entry[0]):
37 for dir in entry[1]:
38 if dir != 'repodata':
39 dir_path = oe.path.join(self.deploy_dir, dir)
40 bb.note("Generating package index for %s" %(dir_path))
41 self.do_write_index(dir_path)
42
43
44class RpmPkgsList(PkgsList):
45 def list_pkgs(self):
46 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR'), needfeed=False).list_installed()
47
48class RpmPM(PackageManager):
49 def __init__(self,
50 d,
51 target_rootfs,
52 target_vendor,
53 task_name='target',
54 arch_var=None,
55 os_var=None,
56 rpm_repo_workdir="oe-rootfs-repo",
57 filterbydependencies=True,
58 needfeed=True):
59 super(RpmPM, self).__init__(d, target_rootfs)
60 self.target_vendor = target_vendor
61 self.task_name = task_name
62 if arch_var == None:
63 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
64 else:
65 self.archs = self.d.getVar(arch_var).replace("-","_")
66 if task_name == "host":
67 self.primary_arch = self.d.getVar('SDK_ARCH')
68 else:
69 self.primary_arch = self.d.getVar('MACHINE_ARCH')
70
71 if needfeed:
72 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
73 create_packages_dir(self.d, oe.path.join(self.rpm_repo_dir, "rpm"), d.getVar("DEPLOY_DIR_RPM"), "package_write_rpm", filterbydependencies)
74
75 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
76 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
77 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
78 self.packaging_data_dirs = ['etc/rpm', 'etc/rpmrc', 'etc/dnf', 'var/lib/rpm', 'var/lib/dnf', 'var/cache/dnf']
79 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
80 self.task_name)
81 if not os.path.exists(self.d.expand('${T}/saved')):
82 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
83
84 def _configure_dnf(self):
85 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
86 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
87 # This prevents accidental matching against libsolv's built-in policies
88 if len(archs) <= 1:
89 archs = archs + ["bogusarch"]
90 # This architecture needs to be upfront so that packages using it are properly prioritized
91 archs = ["sdk_provides_dummy_target"] + archs
92 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
93 bb.utils.mkdirhier(confdir)
94 open(confdir + "arch", 'w').write(":".join(archs))
95 distro_codename = self.d.getVar('DISTRO_CODENAME')
96 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
97
98 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
99
100
101 def _configure_rpm(self):
102 # We need to configure rpm to use our primary package architecture as the installation architecture,
103 # and to make it compatible with other package architectures that we use.
104 # Otherwise it will refuse to proceed with packages installation.
105 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
106 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
107 bb.utils.mkdirhier(platformconfdir)
108 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
109 with open(rpmrcconfdir + "rpmrc", 'w') as f:
110 f.write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
111 f.write("buildarch_compat: %s: noarch\n" % self.primary_arch)
112
113 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
114 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
115 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
116
117 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
118 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
119 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
120 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
121 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
122 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
123 try:
124 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
125 except subprocess.CalledProcessError as e:
126 bb.fatal("Importing GPG key failed. Command '%s' "
127 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
128
129 def create_configs(self):
130 self._configure_dnf()
131 self._configure_rpm()
132
133 def write_index(self):
134 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
135 lf = bb.utils.lockfile(lockfilename, False)
136 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
137 bb.utils.unlockfile(lf)
138
139 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
140 from urllib.parse import urlparse
141
142 if feed_uris == "":
143 return
144
145 gpg_opts = ''
146 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
147 gpg_opts += 'repo_gpgcheck=1\n'
148 gpg_opts += 'gpgkey=file://%s/pki/packagefeed-gpg/PACKAGEFEED-GPG-KEY-%s-%s\n' % (self.d.getVar('sysconfdir'), self.d.getVar('DISTRO'), self.d.getVar('DISTRO_CODENAME'))
149
150 if self.d.getVar('RPM_SIGN_PACKAGES') != '1':
151 gpg_opts += 'gpgcheck=0\n'
152
153 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
154 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
155 for uri in remote_uris:
156 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
157 if feed_archs is not None:
158 for arch in feed_archs.split():
159 repo_uri = uri + "/" + arch
160 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
161 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
162 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
163 "[%s]\nname=%s\nbaseurl=%s\n%s\n" % (repo_id, repo_name, repo_uri, gpg_opts))
164 else:
165 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
166 repo_uri = uri
167 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
168 "[%s]\nname=%s\nbaseurl=%s\n%s" % (repo_base, repo_name, repo_uri, gpg_opts))
169
170 def _prepare_pkg_transaction(self):
171 os.environ['D'] = self.target_rootfs
172 os.environ['OFFLINE_ROOT'] = self.target_rootfs
173 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
174 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
175 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
176 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
177
178
179 def install(self, pkgs, attempt_only = False):
180 if len(pkgs) == 0:
181 return
182 self._prepare_pkg_transaction()
183
184 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
185 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
186 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
187
188 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
189 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
190 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
191 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
192 ["install"] +
193 pkgs)
194
195 failed_scriptlets_pkgnames = collections.OrderedDict()
196 for line in output.splitlines():
197 if line.startswith("Error in POSTIN scriptlet in rpm package"):
198 failed_scriptlets_pkgnames[line.split()[-1]] = True
199
200 if len(failed_scriptlets_pkgnames) > 0:
201 failed_postinsts_abort(list(failed_scriptlets_pkgnames.keys()), self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
202
203 def remove(self, pkgs, with_dependencies = True):
204 if not pkgs:
205 return
206
207 self._prepare_pkg_transaction()
208
209 if with_dependencies:
210 self._invoke_dnf(["remove"] + pkgs)
211 else:
212 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
213 args = ["-e", "-v", "--nodeps", "--root=%s" %self.target_rootfs]
214
215 try:
216 bb.note("Running %s" % ' '.join([cmd] + args + pkgs))
217 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
218 bb.note(output)
219 except subprocess.CalledProcessError as e:
220 bb.fatal("Could not invoke rpm. Command "
221 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
222
223 def upgrade(self):
224 self._prepare_pkg_transaction()
225 self._invoke_dnf(["upgrade"])
226
227 def autoremove(self):
228 self._prepare_pkg_transaction()
229 self._invoke_dnf(["autoremove"])
230
231 def remove_packaging_data(self):
232 self._invoke_dnf(["clean", "all"])
233 for dir in self.packaging_data_dirs:
234 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
235
236 def backup_packaging_data(self):
237 # Save the packaging dirs for increment rpm image generation
238 if os.path.exists(self.saved_packaging_data):
239 bb.utils.remove(self.saved_packaging_data, True)
240 for i in self.packaging_data_dirs:
241 source_dir = oe.path.join(self.target_rootfs, i)
242 target_dir = oe.path.join(self.saved_packaging_data, i)
243 if os.path.isdir(source_dir):
244 shutil.copytree(source_dir, target_dir, symlinks=True)
245 elif os.path.isfile(source_dir):
246 shutil.copy2(source_dir, target_dir)
247
248 def recovery_packaging_data(self):
249 # Move the rpmlib back
250 if os.path.exists(self.saved_packaging_data):
251 for i in self.packaging_data_dirs:
252 target_dir = oe.path.join(self.target_rootfs, i)
253 if os.path.exists(target_dir):
254 bb.utils.remove(target_dir, True)
255 source_dir = oe.path.join(self.saved_packaging_data, i)
256 if os.path.isdir(source_dir):
257 shutil.copytree(source_dir, target_dir, symlinks=True)
258 elif os.path.isfile(source_dir):
259 shutil.copy2(source_dir, target_dir)
260
261 def list_installed(self):
262 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
263 print_output = False)
264 packages = {}
265 current_package = None
266 current_deps = None
267 current_state = "initial"
268 for line in output.splitlines():
269 if line.startswith("Package:"):
270 package_info = line.split(" ")[1:]
271 current_package = package_info[0]
272 package_arch = package_info[1]
273 package_version = package_info[2]
274 package_rpm = package_info[3]
275 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
276 current_deps = []
277 elif line.startswith("Dependencies:"):
278 current_state = "dependencies"
279 elif line.startswith("Recommendations"):
280 current_state = "recommendations"
281 elif line.startswith("DependenciesEndHere:"):
282 current_state = "initial"
283 packages[current_package]["deps"] = current_deps
284 elif len(line) > 0:
285 if current_state == "dependencies":
286 current_deps.append(line)
287 elif current_state == "recommendations":
288 current_deps.append("%s [REC]" % line)
289
290 return packages
291
292 def update(self):
293 self._invoke_dnf(["makecache", "--refresh"])
294
295 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
296 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
297
298 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
299 standard_dnf_args = ["-v", "--rpmverbosity=info", "-y",
300 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
301 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
302 "--installroot=%s" % (self.target_rootfs),
303 "--setopt=logdir=%s" % (self.d.getVar('T'))
304 ]
305 if hasattr(self, "rpm_repo_dir"):
306 standard_dnf_args.append("--repofrompath=oe-repo,%s" % (self.rpm_repo_dir))
307 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
308 bb.note('Running %s' % ' '.join(cmd))
309 try:
310 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
311 if print_output:
312 bb.debug(1, output)
313 return output
314 except subprocess.CalledProcessError as e:
315 if print_output:
316 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
317 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
318 else:
319 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
320 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
321 return e.output.decode("utf-8")
322
323 def dump_install_solution(self, pkgs):
324 open(self.solution_manifest, 'w').write(" ".join(pkgs))
325 return pkgs
326
327 def load_old_install_solution(self):
328 if not os.path.exists(self.solution_manifest):
329 return []
330 with open(self.solution_manifest, 'r') as fd:
331 return fd.read().split()
332
333 def _script_num_prefix(self, path):
334 files = os.listdir(path)
335 numbers = set()
336 numbers.add(99)
337 for f in files:
338 numbers.add(int(f.split("-")[0]))
339 return max(numbers) + 1
340
341 def save_rpmpostinst(self, pkg):
342 bb.note("Saving postinstall script of %s" % (pkg))
343 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
344 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
345
346 try:
347 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
348 except subprocess.CalledProcessError as e:
349 bb.fatal("Could not invoke rpm. Command "
350 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
351
352 # may need to prepend #!/bin/sh to output
353
354 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
355 bb.utils.mkdirhier(target_path)
356 num = self._script_num_prefix(target_path)
357 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
358 open(saved_script_name, 'w').write(output)
359 os.chmod(saved_script_name, 0o755)
360
361 def _handle_intercept_failure(self, registered_pkgs):
362 rpm_postinsts_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
363 bb.utils.mkdirhier(rpm_postinsts_dir)
364
365 # Save the package postinstalls in /etc/rpm-postinsts
366 for pkg in registered_pkgs.split():
367 self.save_rpmpostinst(pkg)
368
369 def extract(self, pkg):
370 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
371 pkg_name = output.splitlines()[-1]
372 if not pkg_name.endswith(".rpm"):
373 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
374 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
375
376 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
377 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
378
379 if not os.path.isfile(pkg_path):
380 bb.fatal("Unable to extract package for '%s'."
381 "File %s doesn't exists" % (pkg, pkg_path))
382
383 tmp_dir = tempfile.mkdtemp()
384 current_dir = os.getcwd()
385 os.chdir(tmp_dir)
386
387 try:
388 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
389 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
390 except subprocess.CalledProcessError as e:
391 bb.utils.remove(tmp_dir, recurse=True)
392 bb.fatal("Unable to extract %s package. Command '%s' "
393 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
394 except OSError as e:
395 bb.utils.remove(tmp_dir, recurse=True)
396 bb.fatal("Unable to extract %s package. Command '%s' "
397 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
398
399 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
400 os.chdir(current_dir)
401
402 return tmp_dir
diff --git a/meta/lib/oe/package_manager/rpm/rootfs.py b/meta/lib/oe/package_manager/rpm/rootfs.py
index 3d9eb95528..2de5752b91 100644
--- a/meta/lib/oe/package_manager/rpm/rootfs.py
+++ b/meta/lib/oe/package_manager/rpm/rootfs.py
@@ -3,10 +3,10 @@
3# 3#
4 4
5from oe.rootfs import Rootfs 5from oe.rootfs import Rootfs
6from oe.package_manager import RpmPM
7from oe.manifest import Manifest 6from oe.manifest import Manifest
8from oe.utils import execute_pre_post_process 7from oe.utils import execute_pre_post_process
9from oe.package_manager.rpm.manifest import RpmManifest 8from oe.package_manager.rpm.manifest import RpmManifest
9from oe.package_manager.rpm import RpmPM
10 10
11class RpmRootfs(Rootfs): 11class RpmRootfs(Rootfs):
12 def __init__(self, d, manifest_dir, progress_reporter=None, logcatcher=None): 12 def __init__(self, d, manifest_dir, progress_reporter=None, logcatcher=None):
diff --git a/meta/lib/oe/package_manager/rpm/sdk.py b/meta/lib/oe/package_manager/rpm/sdk.py
index ab816f43df..4d3f9461ef 100644
--- a/meta/lib/oe/package_manager/rpm/sdk.py
+++ b/meta/lib/oe/package_manager/rpm/sdk.py
@@ -5,7 +5,7 @@
5from oe.utils import execute_pre_post_process 5from oe.utils import execute_pre_post_process
6from oe.sdk import Sdk 6from oe.sdk import Sdk
7from oe.manifest import Manifest 7from oe.manifest import Manifest
8from oe.package_manager import RpmPM 8from oe.package_manager.rpm import RpmPM
9import glob 9import glob
10 10
11class RpmSdk(Sdk): 11class RpmSdk(Sdk):
diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py
index 17ca323588..a37f1e8147 100644
--- a/meta/lib/oe/rootfs.py
+++ b/meta/lib/oe/rootfs.py
@@ -13,6 +13,7 @@ import re
13from oe.package_manager.rpm.manifest import RpmManifest 13from oe.package_manager.rpm.manifest import RpmManifest
14from oe.package_manager.ipk.manifest import OpkgManifest 14from oe.package_manager.ipk.manifest import OpkgManifest
15from oe.package_manager.deb.manifest import DpkgManifest 15from oe.package_manager.deb.manifest import DpkgManifest
16from oe.package_manager.rpm import RpmPkgsList
16 17
17class Rootfs(object, metaclass=ABCMeta): 18class Rootfs(object, metaclass=ABCMeta):
18 """ 19 """
diff --git a/meta/lib/oe/sdk.py b/meta/lib/oe/sdk.py
index 2f8cbd03d7..3a4c9bb7ca 100644
--- a/meta/lib/oe/sdk.py
+++ b/meta/lib/oe/sdk.py
@@ -117,6 +117,7 @@ def sdk_list_installed_packages(d, target, rootfs_dir=None):
117 117
118 rootfs_dir = [sdk_output, os.path.join(sdk_output, target_path)][target is True] 118 rootfs_dir = [sdk_output, os.path.join(sdk_output, target_path)][target is True]
119 119
120 from oe.package_manager.rpm import RpmPkgsList
120 img_type = d.getVar('IMAGE_PKGTYPE') 121 img_type = d.getVar('IMAGE_PKGTYPE')
121 if img_type == "rpm": 122 if img_type == "rpm":
122 arch_var = ["SDK_PACKAGE_ARCHS", None][target is True] 123 arch_var = ["SDK_PACKAGE_ARCHS", None][target is True]
diff --git a/meta/lib/oeqa/utils/package_manager.py b/meta/lib/oeqa/utils/package_manager.py
index 2d358f7172..15ffd59231 100644
--- a/meta/lib/oeqa/utils/package_manager.py
+++ b/meta/lib/oeqa/utils/package_manager.py
@@ -12,7 +12,9 @@ def get_package_manager(d, root_path):
12 """ 12 """
13 Returns an OE package manager that can install packages in root_path. 13 Returns an OE package manager that can install packages in root_path.
14 """ 14 """
15 from oe.package_manager import RpmPM, OpkgPM, DpkgPM 15 from oe.package_manager import OpkgPM, DpkgPM
16 from oe.package_manager.rpm import RpmPM
17
16 18
17 pkg_class = d.getVar("IMAGE_PKGTYPE") 19 pkg_class = d.getVar("IMAGE_PKGTYPE")
18 if pkg_class == "rpm": 20 if pkg_class == "rpm":