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