1# 2# SPDX-License-Identifier: GPL-2.0-only 3# 4 5import re 6import shutil 7from oe.rootfs import Rootfs 8from oe.manifest import Manifest 9from oe.utils import execute_pre_post_process 10from oe.package_manager.deb.manifest import PkgManifest 11from oe.package_manager.deb import DpkgPM 12 13class DpkgOpkgRootfs(Rootfs): 14 def __init__(self, d, progress_reporter=None, logcatcher=None): 15 super(DpkgOpkgRootfs, self).__init__(d, progress_reporter, logcatcher) 16 17 def _get_pkgs_postinsts(self, status_file): 18 def _get_pkg_depends_list(pkg_depends): 19 pkg_depends_list = [] 20 # filter version requirements like libc (>= 1.1) 21 for dep in pkg_depends.split(', '): 22 m_dep = re.match(r"^(.*) \(.*\)$", dep) 23 if m_dep: 24 dep = m_dep.group(1) 25 pkg_depends_list.append(dep) 26 27 return pkg_depends_list 28 29 pkgs = {} 30 pkg_name = "" 31 pkg_status_match = False 32 pkg_depends = "" 33 34 with open(status_file) as status: 35 data = status.read() 36 status.close() 37 for line in data.split('\n'): 38 m_pkg = re.match(r"^Package: (.*)", line) 39 m_status = re.match(r"^Status:.*unpacked", line) 40 m_depends = re.match(r"^Depends: (.*)", line) 41 42 #Only one of m_pkg, m_status or m_depends is not None at time 43 #If m_pkg is not None, we started a new package 44 if m_pkg is not None: 45 #Get Package name 46 pkg_name = m_pkg.group(1) 47 #Make sure we reset other variables 48 pkg_status_match = False 49 pkg_depends = "" 50 elif m_status is not None: 51 #New status matched 52 pkg_status_match = True 53 elif m_depends is not None: 54 #New depends macthed 55 pkg_depends = m_depends.group(1) 56 else: 57 pass 58 59 #Now check if we can process package depends and postinst 60 if "" != pkg_name and pkg_status_match: 61 pkgs[pkg_name] = _get_pkg_depends_list(pkg_depends) 62 else: 63 #Not enough information 64 pass 65 66 # remove package dependencies not in postinsts 67 pkg_names = list(pkgs.keys()) 68 for pkg_name in pkg_names: 69 deps = pkgs[pkg_name][:] 70 71 for d in deps: 72 if d not in pkg_names: 73 pkgs[pkg_name].remove(d) 74 75 return pkgs 76 77 def _get_delayed_postinsts_common(self, status_file): 78 def _dep_resolve(graph, node, resolved, seen): 79 seen.append(node) 80 81 for edge in graph[node]: 82 if edge not in resolved: 83 if edge in seen: 84 raise RuntimeError("Packages %s and %s have " \ 85 "a circular dependency in postinsts scripts." \ 86 % (node, edge)) 87 _dep_resolve(graph, edge, resolved, seen) 88 89 resolved.append(node) 90 91 pkg_list = [] 92 93 pkgs = None 94 if not self.d.getVar('PACKAGE_INSTALL').strip(): 95 bb.note("Building empty image") 96 else: 97 pkgs = self._get_pkgs_postinsts(status_file) 98 if pkgs: 99 root = "__packagegroup_postinst__" 100 pkgs[root] = list(pkgs.keys()) 101 _dep_resolve(pkgs, root, pkg_list, []) 102 pkg_list.remove(root) 103 104 if len(pkg_list) == 0: 105 return None 106 107 return pkg_list 108 109 def _save_postinsts_common(self, dst_postinst_dir, src_postinst_dir): 110 if bb.utils.contains("IMAGE_FEATURES", "package-management", 111 True, False, self.d): 112 return 113 num = 0 114 for p in self._get_delayed_postinsts(): 115 bb.utils.mkdirhier(dst_postinst_dir) 116 117 if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")): 118 shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"), 119 os.path.join(dst_postinst_dir, "%03d-%s" % (num, p))) 120 121 num += 1 122 123class PkgRootfs(DpkgOpkgRootfs): 124 def __init__(self, d, manifest_dir, progress_reporter=None, logcatcher=None): 125 super(PkgRootfs, self).__init__(d, progress_reporter, logcatcher) 126 self.log_check_regex = '^E:' 127 self.log_check_expected_regexes = \ 128 [ 129 "^E: Unmet dependencies." 130 ] 131 132 bb.utils.remove(self.image_rootfs, True) 133 bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS'), True) 134 self.manifest = PkgManifest(d, manifest_dir) 135 self.pm = DpkgPM(d, d.getVar('IMAGE_ROOTFS'), 136 d.getVar('PACKAGE_ARCHS'), 137 d.getVar('DPKG_ARCH')) 138 139 140 def _create(self): 141 pkgs_to_install = self.manifest.parse_initial_manifest() 142 deb_pre_process_cmds = self.d.getVar('DEB_PREPROCESS_COMMANDS') 143 deb_post_process_cmds = self.d.getVar('DEB_POSTPROCESS_COMMANDS') 144 145 alt_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/alternatives") 146 bb.utils.mkdirhier(alt_dir) 147 148 # update PM index files 149 self.pm.write_index() 150 151 execute_pre_post_process(self.d, deb_pre_process_cmds) 152 153 if self.progress_reporter: 154 self.progress_reporter.next_stage() 155 # Don't support incremental, so skip that 156 self.progress_reporter.next_stage() 157 158 self.pm.update() 159 160 if self.progress_reporter: 161 self.progress_reporter.next_stage() 162 163 for pkg_type in self.install_order: 164 if pkg_type in pkgs_to_install: 165 self.pm.install(pkgs_to_install[pkg_type], 166 [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY]) 167 self.pm.fix_broken_dependencies() 168 169 if self.progress_reporter: 170 # Don't support attemptonly, so skip that 171 self.progress_reporter.next_stage() 172 self.progress_reporter.next_stage() 173 174 self.pm.install_complementary() 175 176 if self.progress_reporter: 177 self.progress_reporter.next_stage() 178 179 self._setup_dbg_rootfs(['/var/lib/dpkg']) 180 181 self.pm.fix_broken_dependencies() 182 183 self.pm.mark_packages("installed") 184 185 self.pm.run_pre_post_installs() 186 187 execute_pre_post_process(self.d, deb_post_process_cmds) 188 189 if self.progress_reporter: 190 self.progress_reporter.next_stage() 191 192 @staticmethod 193 def _depends_list(): 194 return ['DEPLOY_DIR_DEB', 'DEB_SDK_ARCH', 'APTCONF_TARGET', 'APT_ARGS', 'DPKG_ARCH', 'DEB_PREPROCESS_COMMANDS', 'DEB_POSTPROCESS_COMMANDS'] 195 196 def _get_delayed_postinsts(self): 197 status_file = self.image_rootfs + "/var/lib/dpkg/status" 198 return self._get_delayed_postinsts_common(status_file) 199 200 def _save_postinsts(self): 201 dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/deb-postinsts") 202 src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/info") 203 return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir) 204 205 def _log_check(self): 206 self._log_check_warn() 207 self._log_check_error() 208 209 def _cleanup(self): 210 pass 211