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