diff options
Diffstat (limited to 'meta/lib/oe/rootfs.py')
-rw-r--r-- | meta/lib/oe/rootfs.py | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py new file mode 100644 index 0000000000..dddbef4d64 --- /dev/null +++ b/meta/lib/oe/rootfs.py | |||
@@ -0,0 +1,757 @@ | |||
1 | from abc import ABCMeta, abstractmethod | ||
2 | from oe.utils import execute_pre_post_process | ||
3 | from oe.utils import contains as base_contains | ||
4 | from oe.package_manager import * | ||
5 | from oe.manifest import * | ||
6 | import oe.path | ||
7 | import filecmp | ||
8 | import shutil | ||
9 | import os | ||
10 | import subprocess | ||
11 | import re | ||
12 | |||
13 | |||
14 | class Rootfs(object): | ||
15 | """ | ||
16 | This is an abstract class. Do not instantiate this directly. | ||
17 | """ | ||
18 | __metaclass__ = ABCMeta | ||
19 | |||
20 | def __init__(self, d): | ||
21 | self.d = d | ||
22 | self.pm = None | ||
23 | self.image_rootfs = self.d.getVar('IMAGE_ROOTFS', True) | ||
24 | self.deploy_dir_image = self.d.getVar('DEPLOY_DIR_IMAGE', True) | ||
25 | |||
26 | self.install_order = Manifest.INSTALL_ORDER | ||
27 | |||
28 | @abstractmethod | ||
29 | def _create(self): | ||
30 | pass | ||
31 | |||
32 | @abstractmethod | ||
33 | def _get_delayed_postinsts(self): | ||
34 | pass | ||
35 | |||
36 | @abstractmethod | ||
37 | def _save_postinsts(self): | ||
38 | pass | ||
39 | |||
40 | @abstractmethod | ||
41 | def _log_check(self): | ||
42 | pass | ||
43 | |||
44 | def _insert_feed_uris(self): | ||
45 | if base_contains("IMAGE_FEATURES", "package-management", | ||
46 | True, False, self.d): | ||
47 | self.pm.insert_feeds_uris() | ||
48 | |||
49 | @abstractmethod | ||
50 | def _handle_intercept_failure(self, failed_script): | ||
51 | pass | ||
52 | |||
53 | """ | ||
54 | The _cleanup() method should be used to clean-up stuff that we don't really | ||
55 | want to end up on target. For example, in the case of RPM, the DB locks. | ||
56 | The method is called, once, at the end of create() method. | ||
57 | """ | ||
58 | @abstractmethod | ||
59 | def _cleanup(self): | ||
60 | pass | ||
61 | |||
62 | def _exec_shell_cmd(self, cmd): | ||
63 | fakerootcmd = self.d.getVar('FAKEROOT', True) | ||
64 | if fakerootcmd is not None: | ||
65 | exec_cmd = [fakerootcmd, cmd] | ||
66 | else: | ||
67 | exec_cmd = cmd | ||
68 | |||
69 | try: | ||
70 | subprocess.check_output(exec_cmd, stderr=subprocess.STDOUT) | ||
71 | except subprocess.CalledProcessError as e: | ||
72 | return("Command '%s' returned %d:\n%s" % (e.cmd, e.returncode, e.output)) | ||
73 | |||
74 | return None | ||
75 | |||
76 | def create(self): | ||
77 | bb.note("###### Generate rootfs #######") | ||
78 | pre_process_cmds = self.d.getVar("ROOTFS_PREPROCESS_COMMAND", True) | ||
79 | post_process_cmds = self.d.getVar("ROOTFS_POSTPROCESS_COMMAND", True) | ||
80 | |||
81 | intercepts_dir = os.path.join(self.d.getVar('WORKDIR', True), | ||
82 | "intercept_scripts") | ||
83 | |||
84 | bb.utils.remove(intercepts_dir, True) | ||
85 | |||
86 | bb.utils.mkdirhier(self.image_rootfs) | ||
87 | |||
88 | bb.utils.mkdirhier(self.deploy_dir_image) | ||
89 | |||
90 | shutil.copytree(self.d.expand("${COREBASE}/scripts/postinst-intercepts"), | ||
91 | intercepts_dir) | ||
92 | |||
93 | shutil.copy(self.d.expand("${COREBASE}/meta/files/deploydir_readme.txt"), | ||
94 | self.deploy_dir_image + | ||
95 | "/README_-_DO_NOT_DELETE_FILES_IN_THIS_DIRECTORY.txt") | ||
96 | |||
97 | execute_pre_post_process(self.d, pre_process_cmds) | ||
98 | |||
99 | # call the package manager dependent create method | ||
100 | self._create() | ||
101 | |||
102 | sysconfdir = self.image_rootfs + self.d.getVar('sysconfdir', True) | ||
103 | bb.utils.mkdirhier(sysconfdir) | ||
104 | with open(sysconfdir + "/version", "w+") as ver: | ||
105 | ver.write(self.d.getVar('BUILDNAME', True) + "\n") | ||
106 | |||
107 | self._run_intercepts() | ||
108 | |||
109 | execute_pre_post_process(self.d, post_process_cmds) | ||
110 | |||
111 | if base_contains("IMAGE_FEATURES", "read-only-rootfs", | ||
112 | True, False, self.d): | ||
113 | delayed_postinsts = self._get_delayed_postinsts() | ||
114 | if delayed_postinsts is not None: | ||
115 | bb.fatal("The following packages could not be configured" | ||
116 | "offline and rootfs is read-only: %s" % | ||
117 | delayed_postinsts) | ||
118 | |||
119 | if self.d.getVar('USE_DEVFS', True) != "1": | ||
120 | self._create_devfs() | ||
121 | |||
122 | self._uninstall_uneeded() | ||
123 | |||
124 | self._insert_feed_uris() | ||
125 | |||
126 | self._run_ldconfig() | ||
127 | |||
128 | self._generate_kernel_module_deps() | ||
129 | |||
130 | self._cleanup() | ||
131 | |||
132 | def _uninstall_uneeded(self): | ||
133 | if base_contains("IMAGE_FEATURES", "package-management", | ||
134 | True, False, self.d): | ||
135 | return | ||
136 | |||
137 | delayed_postinsts = self._get_delayed_postinsts() | ||
138 | if delayed_postinsts is None: | ||
139 | installed_pkgs_dir = self.d.expand('${WORKDIR}/installed_pkgs.txt') | ||
140 | pkgs_to_remove = list() | ||
141 | with open(installed_pkgs_dir, "r+") as installed_pkgs: | ||
142 | pkgs_installed = installed_pkgs.read().split('\n') | ||
143 | for pkg_installed in pkgs_installed[:]: | ||
144 | pkg = pkg_installed.split()[0] | ||
145 | if pkg in ["update-rc.d", | ||
146 | "base-passwd", | ||
147 | self.d.getVar("ROOTFS_BOOTSTRAP_INSTALL", True) | ||
148 | ]: | ||
149 | pkgs_to_remove.append(pkg) | ||
150 | pkgs_installed.remove(pkg_installed) | ||
151 | |||
152 | if len(pkgs_to_remove) > 0: | ||
153 | self.pm.remove(pkgs_to_remove, False) | ||
154 | # Update installed_pkgs.txt | ||
155 | open(installed_pkgs_dir, "w+").write('\n'.join(pkgs_installed)) | ||
156 | |||
157 | if os.path.exists(self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/init.d/run-postinsts")): | ||
158 | self._exec_shell_cmd(["update-rc.d", "-f", "-r", | ||
159 | self.d.getVar('IMAGE_ROOTFS', True), | ||
160 | "run-postinsts", "remove"]) | ||
161 | else: | ||
162 | self._save_postinsts() | ||
163 | |||
164 | self.pm.remove_packaging_data() | ||
165 | |||
166 | def _run_intercepts(self): | ||
167 | intercepts_dir = os.path.join(self.d.getVar('WORKDIR', True), | ||
168 | "intercept_scripts") | ||
169 | |||
170 | bb.note("Running intercept scripts:") | ||
171 | os.environ['D'] = self.image_rootfs | ||
172 | for script in os.listdir(intercepts_dir): | ||
173 | script_full = os.path.join(intercepts_dir, script) | ||
174 | |||
175 | if script == "postinst_intercept" or not os.access(script_full, os.X_OK): | ||
176 | continue | ||
177 | |||
178 | bb.note("> Executing %s intercept ..." % script) | ||
179 | |||
180 | try: | ||
181 | subprocess.check_output(script_full) | ||
182 | except subprocess.CalledProcessError as e: | ||
183 | bb.warn("The postinstall intercept hook '%s' failed (exit code: %d)! See log for details!" % | ||
184 | (script, e.returncode)) | ||
185 | |||
186 | with open(script_full) as intercept: | ||
187 | registered_pkgs = None | ||
188 | for line in intercept.read().split("\n"): | ||
189 | m = re.match("^##PKGS:(.*)", line) | ||
190 | if m is not None: | ||
191 | registered_pkgs = m.group(1).strip() | ||
192 | break | ||
193 | |||
194 | if registered_pkgs is not None: | ||
195 | bb.warn("The postinstalls for the following packages " | ||
196 | "will be postponed for first boot: %s" % | ||
197 | registered_pkgs) | ||
198 | |||
199 | # call the backend dependent handler | ||
200 | self._handle_intercept_failure(registered_pkgs) | ||
201 | |||
202 | def _run_ldconfig(self): | ||
203 | if self.d.getVar('LDCONFIGDEPEND', True): | ||
204 | bb.note("Executing: ldconfig -r" + self.image_rootfs + "-c new -v") | ||
205 | self._exec_shell_cmd(['ldconfig', '-r', self.image_rootfs, '-c', | ||
206 | 'new', '-v']) | ||
207 | |||
208 | def _generate_kernel_module_deps(self): | ||
209 | kernel_abi_ver_file = os.path.join(self.d.getVar('STAGING_KERNEL_DIR', True), | ||
210 | 'kernel-abiversion') | ||
211 | if os.path.exists(kernel_abi_ver_file): | ||
212 | kernel_ver = open(kernel_abi_ver_file).read().strip(' \n') | ||
213 | modules_dir = os.path.join(self.image_rootfs, 'lib', 'modules', kernel_ver) | ||
214 | |||
215 | bb.utils.mkdirhier(modules_dir) | ||
216 | |||
217 | self._exec_shell_cmd(['depmodwrapper', '-a', '-b', self.image_rootfs, | ||
218 | kernel_ver]) | ||
219 | |||
220 | """ | ||
221 | Create devfs: | ||
222 | * IMAGE_DEVICE_TABLE is the old name to an absolute path to a device table file | ||
223 | * IMAGE_DEVICE_TABLES is a new name for a file, or list of files, seached | ||
224 | for in the BBPATH | ||
225 | If neither are specified then the default name of files/device_table-minimal.txt | ||
226 | is searched for in the BBPATH (same as the old version.) | ||
227 | """ | ||
228 | def _create_devfs(self): | ||
229 | devtable_list = [] | ||
230 | devtable = self.d.getVar('IMAGE_DEVICE_TABLE', True) | ||
231 | if devtable is not None: | ||
232 | devtable_list.append(devtable) | ||
233 | else: | ||
234 | devtables = self.d.getVar('IMAGE_DEVICE_TABLES', True) | ||
235 | if devtables is None: | ||
236 | devtables = 'files/device_table-minimal.txt' | ||
237 | for devtable in devtables.split(): | ||
238 | devtable_list.append("%s" % bb.utils.which(self.d.getVar('BBPATH', True), devtable)) | ||
239 | |||
240 | for devtable in devtable_list: | ||
241 | self._exec_shell_cmd(["makedevs", "-r", | ||
242 | self.image_rootfs, "-D", devtable]) | ||
243 | |||
244 | |||
245 | class RpmRootfs(Rootfs): | ||
246 | def __init__(self, d, manifest_dir): | ||
247 | super(RpmRootfs, self).__init__(d) | ||
248 | |||
249 | self.manifest = RpmManifest(d, manifest_dir) | ||
250 | |||
251 | self.pm = RpmPM(d, | ||
252 | d.getVar('IMAGE_ROOTFS', True), | ||
253 | self.d.getVar('TARGET_VENDOR', True) | ||
254 | ) | ||
255 | |||
256 | self.inc_rpm_image_gen = self.d.getVar('INC_RPM_IMAGE_GEN', True) | ||
257 | if self.inc_rpm_image_gen != "1": | ||
258 | bb.utils.remove(self.image_rootfs, True) | ||
259 | else: | ||
260 | self.pm.recovery_packaging_data() | ||
261 | bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True) | ||
262 | |||
263 | self.pm.create_configs() | ||
264 | |||
265 | ''' | ||
266 | While rpm incremental image generation is enabled, it will remove the | ||
267 | unneeded pkgs by comparing the new install solution manifest and the | ||
268 | old installed manifest. | ||
269 | ''' | ||
270 | def _create_incremental(self, pkgs_initial_install): | ||
271 | if self.inc_rpm_image_gen == "1": | ||
272 | |||
273 | pkgs_to_install = list() | ||
274 | for pkg_type in pkgs_initial_install: | ||
275 | pkgs_to_install += pkgs_initial_install[pkg_type] | ||
276 | |||
277 | installed_manifest = self.pm.load_old_install_solution() | ||
278 | solution_manifest = self.pm.dump_install_solution(pkgs_to_install) | ||
279 | |||
280 | pkg_to_remove = list() | ||
281 | for pkg in installed_manifest: | ||
282 | if pkg not in solution_manifest: | ||
283 | pkg_to_remove.append(pkg) | ||
284 | |||
285 | self.pm.update() | ||
286 | |||
287 | bb.note('incremental update -- upgrade packages in place ') | ||
288 | self.pm.upgrade() | ||
289 | if pkg_to_remove != []: | ||
290 | bb.note('incremental removed: %s' % ' '.join(pkg_to_remove)) | ||
291 | self.pm.remove(pkg_to_remove) | ||
292 | |||
293 | def _create(self): | ||
294 | pkgs_to_install = self.manifest.parse_initial_manifest() | ||
295 | |||
296 | # update PM index files | ||
297 | self.pm.write_index() | ||
298 | |||
299 | self.pm.dump_all_available_pkgs() | ||
300 | |||
301 | if self.inc_rpm_image_gen == "1": | ||
302 | self._create_incremental(pkgs_to_install) | ||
303 | |||
304 | self.pm.update() | ||
305 | |||
306 | pkgs = [] | ||
307 | pkgs_attempt = [] | ||
308 | for pkg_type in pkgs_to_install: | ||
309 | if pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY: | ||
310 | pkgs_attempt += pkgs_to_install[pkg_type] | ||
311 | else: | ||
312 | pkgs += pkgs_to_install[pkg_type] | ||
313 | |||
314 | self.pm.install(pkgs) | ||
315 | |||
316 | self.pm.install(pkgs_attempt, True) | ||
317 | |||
318 | self.pm.install_complementary() | ||
319 | |||
320 | self._log_check() | ||
321 | |||
322 | if self.inc_rpm_image_gen == "1": | ||
323 | self.pm.backup_packaging_data() | ||
324 | |||
325 | self.pm.rpm_setup_smart_target_config() | ||
326 | |||
327 | def _get_delayed_postinsts(self): | ||
328 | postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/rpm-postinsts") | ||
329 | if os.path.isdir(postinst_dir): | ||
330 | files = os.listdir(postinst_dir) | ||
331 | for f in files: | ||
332 | bb.note('Delayed package scriptlet: %s' % f) | ||
333 | return files | ||
334 | |||
335 | return None | ||
336 | |||
337 | def _save_postinsts(self): | ||
338 | # this is just a stub. For RPM, the failed postinstalls are | ||
339 | # already saved in /etc/rpm-postinsts | ||
340 | pass | ||
341 | |||
342 | def _log_check(self): | ||
343 | r = re.compile('(unpacking of archive failed|Cannot find package|exit 1|ERR|Fail)') | ||
344 | log_path = self.d.expand("${T}/log.do_rootfs") | ||
345 | with open(log_path, 'r') as log: | ||
346 | found_error = 0 | ||
347 | message = "\n" | ||
348 | for line in log.read().split('\n'): | ||
349 | if 'log_check' in line: | ||
350 | continue | ||
351 | |||
352 | m = r.search(line) | ||
353 | if m: | ||
354 | found_error = 1 | ||
355 | bb.warn('log_check: There were error messages in the logfile') | ||
356 | bb.warn('log_check: Matched keyword: [%s]\n\n' % m.group()) | ||
357 | |||
358 | if found_error >= 1 and found_error <= 5: | ||
359 | message += line + '\n' | ||
360 | found_error += 1 | ||
361 | |||
362 | if found_error == 6: | ||
363 | bb.fatal(message) | ||
364 | |||
365 | def _handle_intercept_failure(self, registered_pkgs): | ||
366 | rpm_postinsts_dir = self.image_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/') | ||
367 | bb.utils.mkdirhier(rpm_postinsts_dir) | ||
368 | |||
369 | # Save the package postinstalls in /etc/rpm-postinsts | ||
370 | for pkg in registered_pkgs.split(): | ||
371 | self.pm.save_rpmpostinst(pkg) | ||
372 | |||
373 | def _cleanup(self): | ||
374 | # during the execution of postprocess commands, rpm is called several | ||
375 | # times to get the files installed, dependencies, etc. This creates the | ||
376 | # __db.00* (Berkeley DB files that hold locks, rpm specific environment | ||
377 | # settings, etc.), that should not get into the final rootfs | ||
378 | self.pm.unlock_rpm_db() | ||
379 | |||
380 | |||
381 | class DpkgRootfs(Rootfs): | ||
382 | def __init__(self, d, manifest_dir): | ||
383 | super(DpkgRootfs, self).__init__(d) | ||
384 | |||
385 | bb.utils.remove(self.image_rootfs, True) | ||
386 | bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True) | ||
387 | self.manifest = DpkgManifest(d, manifest_dir) | ||
388 | self.pm = DpkgPM(d, d.getVar('IMAGE_ROOTFS', True), | ||
389 | d.getVar('PACKAGE_ARCHS', True), | ||
390 | d.getVar('DPKG_ARCH', True)) | ||
391 | |||
392 | |||
393 | def _create(self): | ||
394 | pkgs_to_install = self.manifest.parse_initial_manifest() | ||
395 | |||
396 | alt_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/alternatives") | ||
397 | bb.utils.mkdirhier(alt_dir) | ||
398 | |||
399 | # update PM index files | ||
400 | self.pm.write_index() | ||
401 | |||
402 | self.pm.update() | ||
403 | |||
404 | for pkg_type in self.install_order: | ||
405 | if pkg_type in pkgs_to_install: | ||
406 | self.pm.install(pkgs_to_install[pkg_type], | ||
407 | [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY]) | ||
408 | |||
409 | self.pm.install_complementary() | ||
410 | |||
411 | self.pm.fix_broken_dependencies() | ||
412 | |||
413 | self.pm.mark_packages("installed") | ||
414 | |||
415 | self.pm.run_pre_post_installs() | ||
416 | |||
417 | def _get_delayed_postinsts(self): | ||
418 | pkg_list = [] | ||
419 | with open(self.image_rootfs + "/var/lib/dpkg/status") as status: | ||
420 | for line in status: | ||
421 | m_pkg = re.match("^Package: (.*)", line) | ||
422 | m_status = re.match("^Status:.*unpacked", line) | ||
423 | if m_pkg is not None: | ||
424 | pkg_name = m_pkg.group(1) | ||
425 | elif m_status is not None: | ||
426 | pkg_list.append(pkg_name) | ||
427 | |||
428 | if len(pkg_list) == 0: | ||
429 | return None | ||
430 | |||
431 | return pkg_list | ||
432 | |||
433 | def _save_postinsts(self): | ||
434 | num = 0 | ||
435 | for p in self._get_delayed_postinsts(): | ||
436 | dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/deb-postinsts") | ||
437 | src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/info") | ||
438 | |||
439 | bb.utils.mkdirhier(dst_postinst_dir) | ||
440 | |||
441 | if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")): | ||
442 | shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"), | ||
443 | os.path.join(dst_postinst_dir, "%03d-%s" % (num, p))) | ||
444 | |||
445 | num += 1 | ||
446 | |||
447 | def _handle_intercept_failure(self, registered_pkgs): | ||
448 | self.pm.mark_packages("unpacked", registered_pkgs.split()) | ||
449 | |||
450 | def _log_check(self): | ||
451 | pass | ||
452 | |||
453 | def _cleanup(self): | ||
454 | pass | ||
455 | |||
456 | |||
457 | class OpkgRootfs(Rootfs): | ||
458 | def __init__(self, d, manifest_dir): | ||
459 | super(OpkgRootfs, self).__init__(d) | ||
460 | |||
461 | self.manifest = OpkgManifest(d, manifest_dir) | ||
462 | self.opkg_conf = self.d.getVar("IPKGCONF_TARGET", True) | ||
463 | self.pkg_archs = self.d.getVar("ALL_MULTILIB_PACKAGE_ARCHS", True) | ||
464 | |||
465 | self.inc_opkg_image_gen = self.d.getVar('INC_IPK_IMAGE_GEN', True) or "" | ||
466 | if self._remove_old_rootfs(): | ||
467 | bb.utils.remove(self.image_rootfs, True) | ||
468 | self.pm = OpkgPM(d, | ||
469 | self.image_rootfs, | ||
470 | self.opkg_conf, | ||
471 | self.pkg_archs) | ||
472 | else: | ||
473 | self.pm = OpkgPM(d, | ||
474 | self.image_rootfs, | ||
475 | self.opkg_conf, | ||
476 | self.pkg_archs) | ||
477 | self.pm.recover_packaging_data() | ||
478 | |||
479 | bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True) | ||
480 | |||
481 | def _prelink_file(self, root_dir, filename): | ||
482 | bb.note('prelink %s in %s' % (filename, root_dir)) | ||
483 | prelink_cfg = oe.path.join(root_dir, | ||
484 | self.d.expand('${sysconfdir}/prelink.conf')) | ||
485 | if not os.path.exists(prelink_cfg): | ||
486 | shutil.copy(self.d.expand('${STAGING_DIR_NATIVE}${sysconfdir_native}/prelink.conf'), | ||
487 | prelink_cfg) | ||
488 | |||
489 | cmd_prelink = self.d.expand('${STAGING_DIR_NATIVE}${sbindir_native}/prelink') | ||
490 | self._exec_shell_cmd([cmd_prelink, | ||
491 | '--root', | ||
492 | root_dir, | ||
493 | '-amR', | ||
494 | '-N', | ||
495 | '-c', | ||
496 | self.d.expand('${sysconfdir}/prelink.conf')]) | ||
497 | |||
498 | ''' | ||
499 | Compare two files with the same key twice to see if they are equal. | ||
500 | If they are not equal, it means they are duplicated and come from | ||
501 | different packages. | ||
502 | 1st: Comapre them directly; | ||
503 | 2nd: While incremental image creation is enabled, one of the | ||
504 | files could be probaly prelinked in the previous image | ||
505 | creation and the file has been changed, so we need to | ||
506 | prelink the other one and compare them. | ||
507 | ''' | ||
508 | def _file_equal(self, key, f1, f2): | ||
509 | |||
510 | # Both of them are not prelinked | ||
511 | if filecmp.cmp(f1, f2): | ||
512 | return True | ||
513 | |||
514 | if self.image_rootfs not in f1: | ||
515 | self._prelink_file(f1.replace(key, ''), f1) | ||
516 | |||
517 | if self.image_rootfs not in f2: | ||
518 | self._prelink_file(f2.replace(key, ''), f2) | ||
519 | |||
520 | # Both of them are prelinked | ||
521 | if filecmp.cmp(f1, f2): | ||
522 | return True | ||
523 | |||
524 | # Not equal | ||
525 | return False | ||
526 | |||
527 | """ | ||
528 | This function was reused from the old implementation. | ||
529 | See commit: "image.bbclass: Added variables for multilib support." by | ||
530 | Lianhao Lu. | ||
531 | """ | ||
532 | def _multilib_sanity_test(self, dirs): | ||
533 | |||
534 | allow_replace = self.d.getVar("MULTILIBRE_ALLOW_REP", True) | ||
535 | if allow_replace is None: | ||
536 | allow_replace = "" | ||
537 | |||
538 | allow_rep = re.compile(re.sub("\|$", "", allow_replace)) | ||
539 | error_prompt = "Multilib check error:" | ||
540 | |||
541 | files = {} | ||
542 | for dir in dirs: | ||
543 | for root, subfolders, subfiles in os.walk(dir): | ||
544 | for file in subfiles: | ||
545 | item = os.path.join(root, file) | ||
546 | key = str(os.path.join("/", os.path.relpath(item, dir))) | ||
547 | |||
548 | valid = True | ||
549 | if key in files: | ||
550 | #check whether the file is allow to replace | ||
551 | if allow_rep.match(key): | ||
552 | valid = True | ||
553 | else: | ||
554 | if os.path.exists(files[key]) and \ | ||
555 | os.path.exists(item) and \ | ||
556 | not self._file_equal(key, files[key], item): | ||
557 | valid = False | ||
558 | bb.fatal("%s duplicate files %s %s is not the same\n" % | ||
559 | (error_prompt, item, files[key])) | ||
560 | |||
561 | #pass the check, add to list | ||
562 | if valid: | ||
563 | files[key] = item | ||
564 | |||
565 | def _multilib_test_install(self, pkgs): | ||
566 | ml_temp = self.d.getVar("MULTILIB_TEMP_ROOTFS", True) | ||
567 | bb.utils.mkdirhier(ml_temp) | ||
568 | |||
569 | dirs = [self.image_rootfs] | ||
570 | |||
571 | for variant in self.d.getVar("MULTILIB_VARIANTS", True).split(): | ||
572 | ml_target_rootfs = os.path.join(ml_temp, variant) | ||
573 | |||
574 | bb.utils.remove(ml_target_rootfs, True) | ||
575 | |||
576 | ml_opkg_conf = os.path.join(ml_temp, | ||
577 | variant + "-" + os.path.basename(self.opkg_conf)) | ||
578 | |||
579 | ml_pm = OpkgPM(self.d, ml_target_rootfs, ml_opkg_conf, self.pkg_archs) | ||
580 | |||
581 | ml_pm.update() | ||
582 | ml_pm.install(pkgs) | ||
583 | |||
584 | dirs.append(ml_target_rootfs) | ||
585 | |||
586 | self._multilib_sanity_test(dirs) | ||
587 | |||
588 | ''' | ||
589 | While ipk incremental image generation is enabled, it will remove the | ||
590 | unneeded pkgs by comparing the old full manifest in previous existing | ||
591 | image and the new full manifest in the current image. | ||
592 | ''' | ||
593 | def _remove_extra_packages(self, pkgs_initial_install): | ||
594 | if self.inc_opkg_image_gen == "1": | ||
595 | # Parse full manifest in previous existing image creation session | ||
596 | old_full_manifest = self.manifest.parse_full_manifest() | ||
597 | |||
598 | # Create full manifest for the current image session, the old one | ||
599 | # will be replaced by the new one. | ||
600 | self.manifest.create_full(self.pm) | ||
601 | |||
602 | # Parse full manifest in current image creation session | ||
603 | new_full_manifest = self.manifest.parse_full_manifest() | ||
604 | |||
605 | pkg_to_remove = list() | ||
606 | for pkg in old_full_manifest: | ||
607 | if pkg not in new_full_manifest: | ||
608 | pkg_to_remove.append(pkg) | ||
609 | |||
610 | if pkg_to_remove != []: | ||
611 | bb.note('decremental removed: %s' % ' '.join(pkg_to_remove)) | ||
612 | self.pm.remove(pkg_to_remove) | ||
613 | |||
614 | ''' | ||
615 | Compare with previous existing image creation, if some conditions | ||
616 | triggered, the previous old image should be removed. | ||
617 | The conditions include any of 'PACKAGE_EXCLUDE, NO_RECOMMENDATIONS | ||
618 | and BAD_RECOMMENDATIONS' has been changed. | ||
619 | ''' | ||
620 | def _remove_old_rootfs(self): | ||
621 | if self.inc_opkg_image_gen != "1": | ||
622 | return True | ||
623 | |||
624 | vars_list_file = self.d.expand('${T}/vars_list') | ||
625 | |||
626 | old_vars_list = "" | ||
627 | if os.path.exists(vars_list_file): | ||
628 | old_vars_list = open(vars_list_file, 'r+').read() | ||
629 | |||
630 | new_vars_list = '%s:%s:%s\n' % \ | ||
631 | ((self.d.getVar('BAD_RECOMMENDATIONS', True) or '').strip(), | ||
632 | (self.d.getVar('NO_RECOMMENDATIONS', True) or '').strip(), | ||
633 | (self.d.getVar('PACKAGE_EXCLUDE', True) or '').strip()) | ||
634 | open(vars_list_file, 'w+').write(new_vars_list) | ||
635 | |||
636 | if old_vars_list != new_vars_list: | ||
637 | return True | ||
638 | |||
639 | return False | ||
640 | |||
641 | def _create(self): | ||
642 | pkgs_to_install = self.manifest.parse_initial_manifest() | ||
643 | opkg_pre_process_cmds = self.d.getVar('OPKG_PREPROCESS_COMMANDS', True) | ||
644 | opkg_post_process_cmds = self.d.getVar('OPKG_POSTPROCESS_COMMANDS', True) | ||
645 | rootfs_post_install_cmds = self.d.getVar('ROOTFS_POSTINSTALL_COMMAND', True) | ||
646 | |||
647 | # update PM index files, unless users provide their own feeds | ||
648 | if (self.d.getVar('BUILD_IMAGES_FROM_FEEDS', True) or "") != "1": | ||
649 | self.pm.write_index() | ||
650 | |||
651 | execute_pre_post_process(self.d, opkg_pre_process_cmds) | ||
652 | |||
653 | self.pm.update() | ||
654 | |||
655 | self.pm.handle_bad_recommendations() | ||
656 | |||
657 | if self.inc_opkg_image_gen == "1": | ||
658 | self._remove_extra_packages(pkgs_to_install) | ||
659 | |||
660 | for pkg_type in self.install_order: | ||
661 | if pkg_type in pkgs_to_install: | ||
662 | # For multilib, we perform a sanity test before final install | ||
663 | # If sanity test fails, it will automatically do a bb.fatal() | ||
664 | # and the installation will stop | ||
665 | if pkg_type == Manifest.PKG_TYPE_MULTILIB: | ||
666 | self._multilib_test_install(pkgs_to_install[pkg_type]) | ||
667 | |||
668 | self.pm.install(pkgs_to_install[pkg_type], | ||
669 | [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY]) | ||
670 | |||
671 | self.pm.install_complementary() | ||
672 | |||
673 | execute_pre_post_process(self.d, opkg_post_process_cmds) | ||
674 | execute_pre_post_process(self.d, rootfs_post_install_cmds) | ||
675 | |||
676 | if self.inc_opkg_image_gen == "1": | ||
677 | self.pm.backup_packaging_data() | ||
678 | |||
679 | def _get_delayed_postinsts(self): | ||
680 | pkg_list = [] | ||
681 | status_file = os.path.join(self.image_rootfs, | ||
682 | self.d.getVar('OPKGLIBDIR', True).strip('/'), | ||
683 | "opkg", "status") | ||
684 | |||
685 | with open(status_file) as status: | ||
686 | for line in status: | ||
687 | m_pkg = re.match("^Package: (.*)", line) | ||
688 | m_status = re.match("^Status:.*unpacked", line) | ||
689 | if m_pkg is not None: | ||
690 | pkg_name = m_pkg.group(1) | ||
691 | elif m_status is not None: | ||
692 | pkg_list.append(pkg_name) | ||
693 | |||
694 | if len(pkg_list) == 0: | ||
695 | return None | ||
696 | |||
697 | return pkg_list | ||
698 | |||
699 | def _save_postinsts(self): | ||
700 | num = 0 | ||
701 | for p in self._get_delayed_postinsts(): | ||
702 | dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/ipk-postinsts") | ||
703 | src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${OPKGLIBDIR}/opkg/info") | ||
704 | |||
705 | bb.utils.mkdirhier(dst_postinst_dir) | ||
706 | |||
707 | if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")): | ||
708 | shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"), | ||
709 | os.path.join(dst_postinst_dir, "%03d-%s" % (num, p))) | ||
710 | |||
711 | num += 1 | ||
712 | |||
713 | def _handle_intercept_failure(self, registered_pkgs): | ||
714 | self.pm.mark_packages("unpacked", registered_pkgs.split()) | ||
715 | |||
716 | def _log_check(self): | ||
717 | pass | ||
718 | |||
719 | def _cleanup(self): | ||
720 | pass | ||
721 | |||
722 | |||
723 | def create_rootfs(d, manifest_dir=None): | ||
724 | env_bkp = os.environ.copy() | ||
725 | |||
726 | img_type = d.getVar('IMAGE_PKGTYPE', True) | ||
727 | if img_type == "rpm": | ||
728 | RpmRootfs(d, manifest_dir).create() | ||
729 | elif img_type == "ipk": | ||
730 | OpkgRootfs(d, manifest_dir).create() | ||
731 | elif img_type == "deb": | ||
732 | DpkgRootfs(d, manifest_dir).create() | ||
733 | |||
734 | os.environ.clear() | ||
735 | os.environ.update(env_bkp) | ||
736 | |||
737 | |||
738 | def image_list_installed_packages(d, format=None, rootfs_dir=None): | ||
739 | if not rootfs_dir: | ||
740 | rootfs_dir = d.getVar('IMAGE_ROOTFS', True) | ||
741 | |||
742 | img_type = d.getVar('IMAGE_PKGTYPE', True) | ||
743 | if img_type == "rpm": | ||
744 | return RpmPkgsList(d, rootfs_dir).list(format) | ||
745 | elif img_type == "ipk": | ||
746 | return OpkgPkgsList(d, rootfs_dir, d.getVar("IPKGCONF_TARGET", True)).list(format) | ||
747 | elif img_type == "deb": | ||
748 | return DpkgPkgsList(d, rootfs_dir).list(format) | ||
749 | |||
750 | if __name__ == "__main__": | ||
751 | """ | ||
752 | We should be able to run this as a standalone script, from outside bitbake | ||
753 | environment. | ||
754 | """ | ||
755 | """ | ||
756 | TBD | ||
757 | """ | ||