summaryrefslogtreecommitdiffstats
path: root/meta/lib/oe/package_manager
diff options
context:
space:
mode:
authorFredrik Gustafsson <fredrik.gustafsson@axis.com>2020-07-24 16:42:34 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2020-07-27 19:58:10 +0100
commit873aee855f196cba43bed5201e7b30ba74a2b255 (patch)
tree8fb3ee9cce9f832be590ec08e49b8e3111a81300 /meta/lib/oe/package_manager
parentb8d3d22ccdd3c61d9aab8c39ce13afeccb5dea56 (diff)
downloadpoky-873aee855f196cba43bed5201e7b30ba74a2b255.tar.gz
ipk: Move rootfs to its own dir
This is a 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: 67fa086589bae484a9beca50b627b007766dcb93) Signed-off-by: Fredrik Gustafsson <fredrigu@axis.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oe/package_manager')
-rw-r--r--meta/lib/oe/package_manager/ipk/rootfs.py386
1 files changed, 386 insertions, 0 deletions
diff --git a/meta/lib/oe/package_manager/ipk/rootfs.py b/meta/lib/oe/package_manager/ipk/rootfs.py
new file mode 100644
index 0000000000..373fcbd93a
--- /dev/null
+++ b/meta/lib/oe/package_manager/ipk/rootfs.py
@@ -0,0 +1,386 @@
1#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
5import re
6import filecmp
7from oe.rootfs import Rootfs
8from oe.package_manager import OpkgPM
9from oe.manifest import Manifest
10from oe.utils import execute_pre_post_process
11from oe.package_manager.ipk.manifest import OpkgManifest
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 OpkgRootfs(DpkgOpkgRootfs):
124 def __init__(self, d, manifest_dir, progress_reporter=None, logcatcher=None):
125 super(OpkgRootfs, self).__init__(d, progress_reporter, logcatcher)
126 self.log_check_regex = '(exit 1|Collected errors)'
127
128 self.manifest = OpkgManifest(d, manifest_dir)
129 self.opkg_conf = self.d.getVar("IPKGCONF_TARGET")
130 self.pkg_archs = self.d.getVar("ALL_MULTILIB_PACKAGE_ARCHS")
131
132 self.inc_opkg_image_gen = self.d.getVar('INC_IPK_IMAGE_GEN') or ""
133 if self._remove_old_rootfs():
134 bb.utils.remove(self.image_rootfs, True)
135 self.pm = OpkgPM(d,
136 self.image_rootfs,
137 self.opkg_conf,
138 self.pkg_archs)
139 else:
140 self.pm = OpkgPM(d,
141 self.image_rootfs,
142 self.opkg_conf,
143 self.pkg_archs)
144 self.pm.recover_packaging_data()
145
146 bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS'), True)
147
148 def _prelink_file(self, root_dir, filename):
149 bb.note('prelink %s in %s' % (filename, root_dir))
150 prelink_cfg = oe.path.join(root_dir,
151 self.d.expand('${sysconfdir}/prelink.conf'))
152 if not os.path.exists(prelink_cfg):
153 shutil.copy(self.d.expand('${STAGING_DIR_NATIVE}${sysconfdir_native}/prelink.conf'),
154 prelink_cfg)
155
156 cmd_prelink = self.d.expand('${STAGING_DIR_NATIVE}${sbindir_native}/prelink')
157 self._exec_shell_cmd([cmd_prelink,
158 '--root',
159 root_dir,
160 '-amR',
161 '-N',
162 '-c',
163 self.d.expand('${sysconfdir}/prelink.conf')])
164
165 '''
166 Compare two files with the same key twice to see if they are equal.
167 If they are not equal, it means they are duplicated and come from
168 different packages.
169 1st: Comapre them directly;
170 2nd: While incremental image creation is enabled, one of the
171 files could be probaly prelinked in the previous image
172 creation and the file has been changed, so we need to
173 prelink the other one and compare them.
174 '''
175 def _file_equal(self, key, f1, f2):
176
177 # Both of them are not prelinked
178 if filecmp.cmp(f1, f2):
179 return True
180
181 if bb.data.inherits_class('image-prelink', self.d):
182 if self.image_rootfs not in f1:
183 self._prelink_file(f1.replace(key, ''), f1)
184
185 if self.image_rootfs not in f2:
186 self._prelink_file(f2.replace(key, ''), f2)
187
188 # Both of them are prelinked
189 if filecmp.cmp(f1, f2):
190 return True
191
192 # Not equal
193 return False
194
195 """
196 This function was reused from the old implementation.
197 See commit: "image.bbclass: Added variables for multilib support." by
198 Lianhao Lu.
199 """
200 def _multilib_sanity_test(self, dirs):
201
202 allow_replace = self.d.getVar("MULTILIBRE_ALLOW_REP")
203 if allow_replace is None:
204 allow_replace = ""
205
206 allow_rep = re.compile(re.sub(r"\|$", r"", allow_replace))
207 error_prompt = "Multilib check error:"
208
209 files = {}
210 for dir in dirs:
211 for root, subfolders, subfiles in os.walk(dir):
212 for file in subfiles:
213 item = os.path.join(root, file)
214 key = str(os.path.join("/", os.path.relpath(item, dir)))
215
216 valid = True
217 if key in files:
218 #check whether the file is allow to replace
219 if allow_rep.match(key):
220 valid = True
221 else:
222 if os.path.exists(files[key]) and \
223 os.path.exists(item) and \
224 not self._file_equal(key, files[key], item):
225 valid = False
226 bb.fatal("%s duplicate files %s %s is not the same\n" %
227 (error_prompt, item, files[key]))
228
229 #pass the check, add to list
230 if valid:
231 files[key] = item
232
233 def _multilib_test_install(self, pkgs):
234 ml_temp = self.d.getVar("MULTILIB_TEMP_ROOTFS")
235 bb.utils.mkdirhier(ml_temp)
236
237 dirs = [self.image_rootfs]
238
239 for variant in self.d.getVar("MULTILIB_VARIANTS").split():
240 ml_target_rootfs = os.path.join(ml_temp, variant)
241
242 bb.utils.remove(ml_target_rootfs, True)
243
244 ml_opkg_conf = os.path.join(ml_temp,
245 variant + "-" + os.path.basename(self.opkg_conf))
246
247 ml_pm = OpkgPM(self.d, ml_target_rootfs, ml_opkg_conf, self.pkg_archs, prepare_index=False)
248
249 ml_pm.update()
250 ml_pm.install(pkgs)
251
252 dirs.append(ml_target_rootfs)
253
254 self._multilib_sanity_test(dirs)
255
256 '''
257 While ipk incremental image generation is enabled, it will remove the
258 unneeded pkgs by comparing the old full manifest in previous existing
259 image and the new full manifest in the current image.
260 '''
261 def _remove_extra_packages(self, pkgs_initial_install):
262 if self.inc_opkg_image_gen == "1":
263 # Parse full manifest in previous existing image creation session
264 old_full_manifest = self.manifest.parse_full_manifest()
265
266 # Create full manifest for the current image session, the old one
267 # will be replaced by the new one.
268 self.manifest.create_full(self.pm)
269
270 # Parse full manifest in current image creation session
271 new_full_manifest = self.manifest.parse_full_manifest()
272
273 pkg_to_remove = list()
274 for pkg in old_full_manifest:
275 if pkg not in new_full_manifest:
276 pkg_to_remove.append(pkg)
277
278 if pkg_to_remove != []:
279 bb.note('decremental removed: %s' % ' '.join(pkg_to_remove))
280 self.pm.remove(pkg_to_remove)
281
282 '''
283 Compare with previous existing image creation, if some conditions
284 triggered, the previous old image should be removed.
285 The conditions include any of 'PACKAGE_EXCLUDE, NO_RECOMMENDATIONS
286 and BAD_RECOMMENDATIONS' has been changed.
287 '''
288 def _remove_old_rootfs(self):
289 if self.inc_opkg_image_gen != "1":
290 return True
291
292 vars_list_file = self.d.expand('${T}/vars_list')
293
294 old_vars_list = ""
295 if os.path.exists(vars_list_file):
296 old_vars_list = open(vars_list_file, 'r+').read()
297
298 new_vars_list = '%s:%s:%s\n' % \
299 ((self.d.getVar('BAD_RECOMMENDATIONS') or '').strip(),
300 (self.d.getVar('NO_RECOMMENDATIONS') or '').strip(),
301 (self.d.getVar('PACKAGE_EXCLUDE') or '').strip())
302 open(vars_list_file, 'w+').write(new_vars_list)
303
304 if old_vars_list != new_vars_list:
305 return True
306
307 return False
308
309 def _create(self):
310 pkgs_to_install = self.manifest.parse_initial_manifest()
311 opkg_pre_process_cmds = self.d.getVar('OPKG_PREPROCESS_COMMANDS')
312 opkg_post_process_cmds = self.d.getVar('OPKG_POSTPROCESS_COMMANDS')
313
314 # update PM index files
315 self.pm.write_index()
316
317 execute_pre_post_process(self.d, opkg_pre_process_cmds)
318
319 if self.progress_reporter:
320 self.progress_reporter.next_stage()
321 # Steps are a bit different in order, skip next
322 self.progress_reporter.next_stage()
323
324 self.pm.update()
325
326 if self.progress_reporter:
327 self.progress_reporter.next_stage()
328
329 if self.inc_opkg_image_gen == "1":
330 self._remove_extra_packages(pkgs_to_install)
331
332 if self.progress_reporter:
333 self.progress_reporter.next_stage()
334
335 for pkg_type in self.install_order:
336 if pkg_type in pkgs_to_install:
337 # For multilib, we perform a sanity test before final install
338 # If sanity test fails, it will automatically do a bb.fatal()
339 # and the installation will stop
340 if pkg_type == Manifest.PKG_TYPE_MULTILIB:
341 self._multilib_test_install(pkgs_to_install[pkg_type])
342
343 self.pm.install(pkgs_to_install[pkg_type],
344 [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
345
346 if self.progress_reporter:
347 self.progress_reporter.next_stage()
348
349 self.pm.install_complementary()
350
351 if self.progress_reporter:
352 self.progress_reporter.next_stage()
353
354 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
355 opkg_dir = os.path.join(opkg_lib_dir, 'opkg')
356 self._setup_dbg_rootfs([opkg_dir])
357
358 execute_pre_post_process(self.d, opkg_post_process_cmds)
359
360 if self.inc_opkg_image_gen == "1":
361 self.pm.backup_packaging_data()
362
363 if self.progress_reporter:
364 self.progress_reporter.next_stage()
365
366 @staticmethod
367 def _depends_list():
368 return ['IPKGCONF_SDK', 'IPK_FEED_URIS', 'DEPLOY_DIR_IPK', 'IPKGCONF_TARGET', 'INC_IPK_IMAGE_GEN', 'OPKG_ARGS', 'OPKGLIBDIR', 'OPKG_PREPROCESS_COMMANDS', 'OPKG_POSTPROCESS_COMMANDS', 'OPKGLIBDIR']
369
370 def _get_delayed_postinsts(self):
371 status_file = os.path.join(self.image_rootfs,
372 self.d.getVar('OPKGLIBDIR').strip('/'),
373 "opkg", "status")
374 return self._get_delayed_postinsts_common(status_file)
375
376 def _save_postinsts(self):
377 dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/ipk-postinsts")
378 src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${OPKGLIBDIR}/opkg/info")
379 return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir)
380
381 def _log_check(self):
382 self._log_check_warn()
383 self._log_check_error()
384
385 def _cleanup(self):
386 self.pm.remove_lists()