blob: a348b975c2ab0a22470795da30397abb7043f4a9 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001from abc import ABCMeta, abstractmethod
2from oe.utils import execute_pre_post_process
3from oe.package_manager import *
4from oe.manifest import *
5import oe.path
6import filecmp
7import shutil
8import os
9import subprocess
10import re
11
12
Patrick Williamsc0f7c042017-02-23 20:41:17 -060013class Rootfs(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050014 """
15 This is an abstract class. Do not instantiate this directly.
16 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -050017
Patrick Williamsc0f7c042017-02-23 20:41:17 -060018 def __init__(self, d, progress_reporter=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019 self.d = d
20 self.pm = None
21 self.image_rootfs = self.d.getVar('IMAGE_ROOTFS', True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060022 self.deploydir = self.d.getVar('IMGDEPLOYDIR', True)
23 self.progress_reporter = progress_reporter
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024
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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060043 def _log_check_common(self, type, match):
44 # Ignore any lines containing log_check to avoid recursion, and ignore
45 # lines beginning with a + since sh -x may emit code which isn't
46 # actually executed, but may contain error messages
47 excludes = [ 'log_check', r'^\+' ]
48 if hasattr(self, 'log_check_expected_regexes'):
49 excludes.extend(self.log_check_expected_regexes)
50 excludes = [re.compile(x) for x in excludes]
51 r = re.compile(match)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050052 log_path = self.d.expand("${T}/log.do_rootfs")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053 messages = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054 with open(log_path, 'r') as log:
55 for line in log:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060056 for ee in excludes:
57 m = ee.search(line)
58 if m:
59 break
60 if m:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061 continue
62
63 m = r.search(line)
64 if m:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060065 messages.append('[log_check] %s' % line)
66 if messages:
67 if len(messages) == 1:
68 msg = '1 %s message' % type
69 else:
70 msg = '%d %s messages' % (len(messages), type)
71 msg = '[log_check] %s: found %s in the logfile:\n%s' % \
72 (self.d.getVar('PN', True), msg, ''.join(messages))
73 if type == 'error':
74 bb.fatal(msg)
75 else:
76 bb.warn(msg)
77
78 def _log_check_warn(self):
79 self._log_check_common('warning', '^(warn|Warn|WARNING:)')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080
81 def _log_check_error(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 self._log_check_common('error', self.log_check_regex)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050083
84 def _insert_feed_uris(self):
85 if bb.utils.contains("IMAGE_FEATURES", "package-management",
86 True, False, self.d):
87 self.pm.insert_feeds_uris()
88
89 @abstractmethod
90 def _handle_intercept_failure(self, failed_script):
91 pass
92
93 """
94 The _cleanup() method should be used to clean-up stuff that we don't really
95 want to end up on target. For example, in the case of RPM, the DB locks.
96 The method is called, once, at the end of create() method.
97 """
98 @abstractmethod
99 def _cleanup(self):
100 pass
101
102 def _setup_dbg_rootfs(self, dirs):
103 gen_debugfs = self.d.getVar('IMAGE_GEN_DEBUGFS', True) or '0'
104 if gen_debugfs != '1':
105 return
106
107 bb.note(" Renaming the original rootfs...")
108 try:
109 shutil.rmtree(self.image_rootfs + '-orig')
110 except:
111 pass
112 os.rename(self.image_rootfs, self.image_rootfs + '-orig')
113
114 bb.note(" Creating debug rootfs...")
115 bb.utils.mkdirhier(self.image_rootfs)
116
117 bb.note(" Copying back package database...")
118 for dir in dirs:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600119 if not os.path.isdir(self.image_rootfs + '-orig' + dir):
120 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500121 bb.utils.mkdirhier(self.image_rootfs + os.path.dirname(dir))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600122 shutil.copytree(self.image_rootfs + '-orig' + dir, self.image_rootfs + dir, symlinks=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123
124 cpath = oe.cachedpath.CachedPath()
125 # Copy files located in /usr/lib/debug or /usr/src/debug
126 for dir in ["/usr/lib/debug", "/usr/src/debug"]:
127 src = self.image_rootfs + '-orig' + dir
128 if cpath.exists(src):
129 dst = self.image_rootfs + dir
130 bb.utils.mkdirhier(os.path.dirname(dst))
131 shutil.copytree(src, dst)
132
133 # Copy files with suffix '.debug' or located in '.debug' dir.
134 for root, dirs, files in cpath.walk(self.image_rootfs + '-orig'):
135 relative_dir = root[len(self.image_rootfs + '-orig'):]
136 for f in files:
137 if f.endswith('.debug') or '/.debug' in relative_dir:
138 bb.utils.mkdirhier(self.image_rootfs + relative_dir)
139 shutil.copy(os.path.join(root, f),
140 self.image_rootfs + relative_dir)
141
142 bb.note(" Install complementary '*-dbg' packages...")
143 self.pm.install_complementary('*-dbg')
144
145 bb.note(" Rename debug rootfs...")
146 try:
147 shutil.rmtree(self.image_rootfs + '-dbg')
148 except:
149 pass
150 os.rename(self.image_rootfs, self.image_rootfs + '-dbg')
151
152 bb.note(" Restoreing original rootfs...")
153 os.rename(self.image_rootfs + '-orig', self.image_rootfs)
154
155 def _exec_shell_cmd(self, cmd):
156 fakerootcmd = self.d.getVar('FAKEROOT', True)
157 if fakerootcmd is not None:
158 exec_cmd = [fakerootcmd, cmd]
159 else:
160 exec_cmd = cmd
161
162 try:
163 subprocess.check_output(exec_cmd, stderr=subprocess.STDOUT)
164 except subprocess.CalledProcessError as e:
165 return("Command '%s' returned %d:\n%s" % (e.cmd, e.returncode, e.output))
166
167 return None
168
169 def create(self):
170 bb.note("###### Generate rootfs #######")
171 pre_process_cmds = self.d.getVar("ROOTFS_PREPROCESS_COMMAND", True)
172 post_process_cmds = self.d.getVar("ROOTFS_POSTPROCESS_COMMAND", True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500173 rootfs_post_install_cmds = self.d.getVar('ROOTFS_POSTINSTALL_COMMAND', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500174
175 postinst_intercepts_dir = self.d.getVar("POSTINST_INTERCEPTS_DIR", True)
176 if not postinst_intercepts_dir:
177 postinst_intercepts_dir = self.d.expand("${COREBASE}/scripts/postinst-intercepts")
178 intercepts_dir = os.path.join(self.d.getVar('WORKDIR', True),
179 "intercept_scripts")
180
181 bb.utils.remove(intercepts_dir, True)
182
183 bb.utils.mkdirhier(self.image_rootfs)
184
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600185 bb.utils.mkdirhier(self.deploydir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186
187 shutil.copytree(postinst_intercepts_dir, intercepts_dir)
188
189 shutil.copy(self.d.expand("${COREBASE}/meta/files/deploydir_readme.txt"),
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600190 self.deploydir +
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500191 "/README_-_DO_NOT_DELETE_FILES_IN_THIS_DIRECTORY.txt")
192
193 execute_pre_post_process(self.d, pre_process_cmds)
194
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600195 if self.progress_reporter:
196 self.progress_reporter.next_stage()
197
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500198 # call the package manager dependent create method
199 self._create()
200
201 sysconfdir = self.image_rootfs + self.d.getVar('sysconfdir', True)
202 bb.utils.mkdirhier(sysconfdir)
203 with open(sysconfdir + "/version", "w+") as ver:
204 ver.write(self.d.getVar('BUILDNAME', True) + "\n")
205
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500206 execute_pre_post_process(self.d, rootfs_post_install_cmds)
207
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500208 self._run_intercepts()
209
210 execute_pre_post_process(self.d, post_process_cmds)
211
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600212 if self.progress_reporter:
213 self.progress_reporter.next_stage()
214
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500215 if bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs",
216 True, False, self.d):
217 delayed_postinsts = self._get_delayed_postinsts()
218 if delayed_postinsts is not None:
219 bb.fatal("The following packages could not be configured "
220 "offline and rootfs is read-only: %s" %
221 delayed_postinsts)
222
223 if self.d.getVar('USE_DEVFS', True) != "1":
224 self._create_devfs()
225
226 self._uninstall_unneeded()
227
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600228 if self.progress_reporter:
229 self.progress_reporter.next_stage()
230
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231 self._insert_feed_uris()
232
233 self._run_ldconfig()
234
235 if self.d.getVar('USE_DEPMOD', True) != "0":
236 self._generate_kernel_module_deps()
237
238 self._cleanup()
239 self._log_check()
240
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600241 if self.progress_reporter:
242 self.progress_reporter.next_stage()
243
244
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500245 def _uninstall_unneeded(self):
246 # Remove unneeded init script symlinks
247 delayed_postinsts = self._get_delayed_postinsts()
248 if delayed_postinsts is None:
249 if os.path.exists(self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/init.d/run-postinsts")):
250 self._exec_shell_cmd(["update-rc.d", "-f", "-r",
251 self.d.getVar('IMAGE_ROOTFS', True),
252 "run-postinsts", "remove"])
253
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500254 image_rorfs = bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs",
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500255 True, False, self.d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600256 image_rorfs_force = self.d.getVar('FORCE_RO_REMOVE', True)
257
258 if image_rorfs or image_rorfs_force == "1":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500259 # Remove components that we don't need if it's a read-only rootfs
260 unneeded_pkgs = self.d.getVar("ROOTFS_RO_UNNEEDED", True).split()
261 pkgs_installed = image_list_installed_packages(self.d)
262 pkgs_to_remove = [pkg for pkg in pkgs_installed if pkg in unneeded_pkgs]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500263
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500264 if len(pkgs_to_remove) > 0:
265 self.pm.remove(pkgs_to_remove, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500267 if delayed_postinsts:
268 self._save_postinsts()
269 if image_rorfs:
270 bb.warn("There are post install scripts "
271 "in a read-only rootfs")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272
273 post_uninstall_cmds = self.d.getVar("ROOTFS_POSTUNINSTALL_COMMAND", True)
274 execute_pre_post_process(self.d, post_uninstall_cmds)
275
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500276 runtime_pkgmanage = bb.utils.contains("IMAGE_FEATURES", "package-management",
277 True, False, self.d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500278 if not runtime_pkgmanage:
279 # Remove the package manager data files
280 self.pm.remove_packaging_data()
281
282 def _run_intercepts(self):
283 intercepts_dir = os.path.join(self.d.getVar('WORKDIR', True),
284 "intercept_scripts")
285
286 bb.note("Running intercept scripts:")
287 os.environ['D'] = self.image_rootfs
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500288 os.environ['STAGING_DIR_NATIVE'] = self.d.getVar('STAGING_DIR_NATIVE', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500289 for script in os.listdir(intercepts_dir):
290 script_full = os.path.join(intercepts_dir, script)
291
292 if script == "postinst_intercept" or not os.access(script_full, os.X_OK):
293 continue
294
295 bb.note("> Executing %s intercept ..." % script)
296
297 try:
298 subprocess.check_call(script_full)
299 except subprocess.CalledProcessError as e:
300 bb.warn("The postinstall intercept hook '%s' failed (exit code: %d)! See log for details!" %
301 (script, e.returncode))
302
303 with open(script_full) as intercept:
304 registered_pkgs = None
305 for line in intercept.read().split("\n"):
306 m = re.match("^##PKGS:(.*)", line)
307 if m is not None:
308 registered_pkgs = m.group(1).strip()
309 break
310
311 if registered_pkgs is not None:
312 bb.warn("The postinstalls for the following packages "
313 "will be postponed for first boot: %s" %
314 registered_pkgs)
315
316 # call the backend dependent handler
317 self._handle_intercept_failure(registered_pkgs)
318
319 def _run_ldconfig(self):
320 if self.d.getVar('LDCONFIGDEPEND', True):
321 bb.note("Executing: ldconfig -r" + self.image_rootfs + "-c new -v")
322 self._exec_shell_cmd(['ldconfig', '-r', self.image_rootfs, '-c',
323 'new', '-v'])
324
325 def _check_for_kernel_modules(self, modules_dir):
326 for root, dirs, files in os.walk(modules_dir, topdown=True):
327 for name in files:
328 found_ko = name.endswith(".ko")
329 if found_ko:
330 return found_ko
331 return False
332
333 def _generate_kernel_module_deps(self):
334 modules_dir = os.path.join(self.image_rootfs, 'lib', 'modules')
335 # if we don't have any modules don't bother to do the depmod
336 if not self._check_for_kernel_modules(modules_dir):
337 bb.note("No Kernel Modules found, not running depmod")
338 return
339
340 kernel_abi_ver_file = oe.path.join(self.d.getVar('PKGDATA_DIR', True), "kernel-depmod",
341 'kernel-abiversion')
342 if not os.path.exists(kernel_abi_ver_file):
343 bb.fatal("No kernel-abiversion file found (%s), cannot run depmod, aborting" % kernel_abi_ver_file)
344
345 kernel_ver = open(kernel_abi_ver_file).read().strip(' \n')
346 versioned_modules_dir = os.path.join(self.image_rootfs, modules_dir, kernel_ver)
347
348 bb.utils.mkdirhier(versioned_modules_dir)
349
350 self._exec_shell_cmd(['depmodwrapper', '-a', '-b', self.image_rootfs, kernel_ver])
351
352 """
353 Create devfs:
354 * IMAGE_DEVICE_TABLE is the old name to an absolute path to a device table file
355 * IMAGE_DEVICE_TABLES is a new name for a file, or list of files, seached
356 for in the BBPATH
357 If neither are specified then the default name of files/device_table-minimal.txt
358 is searched for in the BBPATH (same as the old version.)
359 """
360 def _create_devfs(self):
361 devtable_list = []
362 devtable = self.d.getVar('IMAGE_DEVICE_TABLE', True)
363 if devtable is not None:
364 devtable_list.append(devtable)
365 else:
366 devtables = self.d.getVar('IMAGE_DEVICE_TABLES', True)
367 if devtables is None:
368 devtables = 'files/device_table-minimal.txt'
369 for devtable in devtables.split():
370 devtable_list.append("%s" % bb.utils.which(self.d.getVar('BBPATH', True), devtable))
371
372 for devtable in devtable_list:
373 self._exec_shell_cmd(["makedevs", "-r",
374 self.image_rootfs, "-D", devtable])
375
376
377class RpmRootfs(Rootfs):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600378 def __init__(self, d, manifest_dir, progress_reporter=None):
379 super(RpmRootfs, self).__init__(d, progress_reporter)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500380 self.log_check_regex = '(unpacking of archive failed|Cannot find package'\
381 '|exit 1|ERROR: |Error: |Error |ERROR '\
382 '|Failed |Failed: |Failed$|Failed\(\d+\):)'
383 self.manifest = RpmManifest(d, manifest_dir)
384
385 self.pm = RpmPM(d,
386 d.getVar('IMAGE_ROOTFS', True),
387 self.d.getVar('TARGET_VENDOR', True)
388 )
389
390 self.inc_rpm_image_gen = self.d.getVar('INC_RPM_IMAGE_GEN', True)
391 if self.inc_rpm_image_gen != "1":
392 bb.utils.remove(self.image_rootfs, True)
393 else:
394 self.pm.recovery_packaging_data()
395 bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True)
396
397 self.pm.create_configs()
398
399 '''
400 While rpm incremental image generation is enabled, it will remove the
401 unneeded pkgs by comparing the new install solution manifest and the
402 old installed manifest.
403 '''
404 def _create_incremental(self, pkgs_initial_install):
405 if self.inc_rpm_image_gen == "1":
406
407 pkgs_to_install = list()
408 for pkg_type in pkgs_initial_install:
409 pkgs_to_install += pkgs_initial_install[pkg_type]
410
411 installed_manifest = self.pm.load_old_install_solution()
412 solution_manifest = self.pm.dump_install_solution(pkgs_to_install)
413
414 pkg_to_remove = list()
415 for pkg in installed_manifest:
416 if pkg not in solution_manifest:
417 pkg_to_remove.append(pkg)
418
419 self.pm.update()
420
421 bb.note('incremental update -- upgrade packages in place ')
422 self.pm.upgrade()
423 if pkg_to_remove != []:
424 bb.note('incremental removed: %s' % ' '.join(pkg_to_remove))
425 self.pm.remove(pkg_to_remove)
426
427 def _create(self):
428 pkgs_to_install = self.manifest.parse_initial_manifest()
429 rpm_pre_process_cmds = self.d.getVar('RPM_PREPROCESS_COMMANDS', True)
430 rpm_post_process_cmds = self.d.getVar('RPM_POSTPROCESS_COMMANDS', True)
431
432 # update PM index files
433 self.pm.write_index()
434
435 execute_pre_post_process(self.d, rpm_pre_process_cmds)
436
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600437 if self.progress_reporter:
438 self.progress_reporter.next_stage()
439
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500440 self.pm.dump_all_available_pkgs()
441
442 if self.inc_rpm_image_gen == "1":
443 self._create_incremental(pkgs_to_install)
444
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600445 if self.progress_reporter:
446 self.progress_reporter.next_stage()
447
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500448 self.pm.update()
449
450 pkgs = []
451 pkgs_attempt = []
452 for pkg_type in pkgs_to_install:
453 if pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY:
454 pkgs_attempt += pkgs_to_install[pkg_type]
455 else:
456 pkgs += pkgs_to_install[pkg_type]
457
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600458 if self.progress_reporter:
459 self.progress_reporter.next_stage()
460
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500461 self.pm.install(pkgs)
462
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600463 if self.progress_reporter:
464 self.progress_reporter.next_stage()
465
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466 self.pm.install(pkgs_attempt, True)
467
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600468 if self.progress_reporter:
469 self.progress_reporter.next_stage()
470
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 self.pm.install_complementary()
472
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600473 if self.progress_reporter:
474 self.progress_reporter.next_stage()
475
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476 self._setup_dbg_rootfs(['/etc/rpm', '/var/lib/rpm', '/var/lib/smart'])
477
478 execute_pre_post_process(self.d, rpm_post_process_cmds)
479
480 self._log_check()
481
482 if self.inc_rpm_image_gen == "1":
483 self.pm.backup_packaging_data()
484
485 self.pm.rpm_setup_smart_target_config()
486
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600487 if self.progress_reporter:
488 self.progress_reporter.next_stage()
489
490
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 @staticmethod
492 def _depends_list():
493 return ['DEPLOY_DIR_RPM', 'INC_RPM_IMAGE_GEN', 'RPM_PREPROCESS_COMMANDS',
494 'RPM_POSTPROCESS_COMMANDS', 'RPM_PREFER_ELF_ARCH']
495
496 def _get_delayed_postinsts(self):
497 postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/rpm-postinsts")
498 if os.path.isdir(postinst_dir):
499 files = os.listdir(postinst_dir)
500 for f in files:
501 bb.note('Delayed package scriptlet: %s' % f)
502 return files
503
504 return None
505
506 def _save_postinsts(self):
507 # this is just a stub. For RPM, the failed postinstalls are
508 # already saved in /etc/rpm-postinsts
509 pass
510
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500511 def _log_check(self):
512 self._log_check_warn()
513 self._log_check_error()
514
515 def _handle_intercept_failure(self, registered_pkgs):
516 rpm_postinsts_dir = self.image_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
517 bb.utils.mkdirhier(rpm_postinsts_dir)
518
519 # Save the package postinstalls in /etc/rpm-postinsts
520 for pkg in registered_pkgs.split():
521 self.pm.save_rpmpostinst(pkg)
522
523 def _cleanup(self):
524 # during the execution of postprocess commands, rpm is called several
525 # times to get the files installed, dependencies, etc. This creates the
526 # __db.00* (Berkeley DB files that hold locks, rpm specific environment
527 # settings, etc.), that should not get into the final rootfs
528 self.pm.unlock_rpm_db()
529 if os.path.isdir(self.pm.install_dir_path + "/tmp") and not os.listdir(self.pm.install_dir_path + "/tmp"):
530 bb.utils.remove(self.pm.install_dir_path + "/tmp", True)
531 if os.path.isdir(self.pm.install_dir_path) and not os.listdir(self.pm.install_dir_path):
532 bb.utils.remove(self.pm.install_dir_path, True)
533
534class DpkgOpkgRootfs(Rootfs):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 def __init__(self, d, progress_reporter=None):
536 super(DpkgOpkgRootfs, self).__init__(d, progress_reporter)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500537
538 def _get_pkgs_postinsts(self, status_file):
539 def _get_pkg_depends_list(pkg_depends):
540 pkg_depends_list = []
541 # filter version requirements like libc (>= 1.1)
542 for dep in pkg_depends.split(', '):
543 m_dep = re.match("^(.*) \(.*\)$", dep)
544 if m_dep:
545 dep = m_dep.group(1)
546 pkg_depends_list.append(dep)
547
548 return pkg_depends_list
549
550 pkgs = {}
551 pkg_name = ""
552 pkg_status_match = False
553 pkg_depends = ""
554
555 with open(status_file) as status:
556 data = status.read()
557 status.close()
558 for line in data.split('\n'):
559 m_pkg = re.match("^Package: (.*)", line)
560 m_status = re.match("^Status:.*unpacked", line)
561 m_depends = re.match("^Depends: (.*)", line)
562
563 if m_pkg is not None:
564 if pkg_name and pkg_status_match:
565 pkgs[pkg_name] = _get_pkg_depends_list(pkg_depends)
566
567 pkg_name = m_pkg.group(1)
568 pkg_status_match = False
569 pkg_depends = ""
570 elif m_status is not None:
571 pkg_status_match = True
572 elif m_depends is not None:
573 pkg_depends = m_depends.group(1)
574
575 # remove package dependencies not in postinsts
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600576 pkg_names = list(pkgs.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500577 for pkg_name in pkg_names:
578 deps = pkgs[pkg_name][:]
579
580 for d in deps:
581 if d not in pkg_names:
582 pkgs[pkg_name].remove(d)
583
584 return pkgs
585
586 def _get_delayed_postinsts_common(self, status_file):
587 def _dep_resolve(graph, node, resolved, seen):
588 seen.append(node)
589
590 for edge in graph[node]:
591 if edge not in resolved:
592 if edge in seen:
593 raise RuntimeError("Packages %s and %s have " \
594 "a circular dependency in postinsts scripts." \
595 % (node, edge))
596 _dep_resolve(graph, edge, resolved, seen)
597
598 resolved.append(node)
599
600 pkg_list = []
601
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500602 pkgs = None
603 if not self.d.getVar('PACKAGE_INSTALL', True).strip():
604 bb.note("Building empty image")
605 else:
606 pkgs = self._get_pkgs_postinsts(status_file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500607 if pkgs:
608 root = "__packagegroup_postinst__"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600609 pkgs[root] = list(pkgs.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610 _dep_resolve(pkgs, root, pkg_list, [])
611 pkg_list.remove(root)
612
613 if len(pkg_list) == 0:
614 return None
615
616 return pkg_list
617
618 def _save_postinsts_common(self, dst_postinst_dir, src_postinst_dir):
619 num = 0
620 for p in self._get_delayed_postinsts():
621 bb.utils.mkdirhier(dst_postinst_dir)
622
623 if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")):
624 shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"),
625 os.path.join(dst_postinst_dir, "%03d-%s" % (num, p)))
626
627 num += 1
628
629class DpkgRootfs(DpkgOpkgRootfs):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600630 def __init__(self, d, manifest_dir, progress_reporter=None):
631 super(DpkgRootfs, self).__init__(d, progress_reporter)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500632 self.log_check_regex = '^E:'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600633 self.log_check_expected_regexes = \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500634 [
635 "^E: Unmet dependencies."
636 ]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500637
638 bb.utils.remove(self.image_rootfs, True)
639 bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True)
640 self.manifest = DpkgManifest(d, manifest_dir)
641 self.pm = DpkgPM(d, d.getVar('IMAGE_ROOTFS', True),
642 d.getVar('PACKAGE_ARCHS', True),
643 d.getVar('DPKG_ARCH', True))
644
645
646 def _create(self):
647 pkgs_to_install = self.manifest.parse_initial_manifest()
648 deb_pre_process_cmds = self.d.getVar('DEB_PREPROCESS_COMMANDS', True)
649 deb_post_process_cmds = self.d.getVar('DEB_POSTPROCESS_COMMANDS', True)
650
651 alt_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/alternatives")
652 bb.utils.mkdirhier(alt_dir)
653
654 # update PM index files
655 self.pm.write_index()
656
657 execute_pre_post_process(self.d, deb_pre_process_cmds)
658
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600659 if self.progress_reporter:
660 self.progress_reporter.next_stage()
661 # Don't support incremental, so skip that
662 self.progress_reporter.next_stage()
663
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500664 self.pm.update()
665
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600666 if self.progress_reporter:
667 self.progress_reporter.next_stage()
668
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500669 for pkg_type in self.install_order:
670 if pkg_type in pkgs_to_install:
671 self.pm.install(pkgs_to_install[pkg_type],
672 [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
673
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600674 if self.progress_reporter:
675 # Don't support attemptonly, so skip that
676 self.progress_reporter.next_stage()
677 self.progress_reporter.next_stage()
678
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500679 self.pm.install_complementary()
680
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600681 if self.progress_reporter:
682 self.progress_reporter.next_stage()
683
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500684 self._setup_dbg_rootfs(['/var/lib/dpkg'])
685
686 self.pm.fix_broken_dependencies()
687
688 self.pm.mark_packages("installed")
689
690 self.pm.run_pre_post_installs()
691
692 execute_pre_post_process(self.d, deb_post_process_cmds)
693
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600694 if self.progress_reporter:
695 self.progress_reporter.next_stage()
696
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500697 @staticmethod
698 def _depends_list():
699 return ['DEPLOY_DIR_DEB', 'DEB_SDK_ARCH', 'APTCONF_TARGET', 'APT_ARGS', 'DPKG_ARCH', 'DEB_PREPROCESS_COMMANDS', 'DEB_POSTPROCESS_COMMANDS']
700
701 def _get_delayed_postinsts(self):
702 status_file = self.image_rootfs + "/var/lib/dpkg/status"
703 return self._get_delayed_postinsts_common(status_file)
704
705 def _save_postinsts(self):
706 dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/deb-postinsts")
707 src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/info")
708 return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir)
709
710 def _handle_intercept_failure(self, registered_pkgs):
711 self.pm.mark_packages("unpacked", registered_pkgs.split())
712
713 def _log_check(self):
714 self._log_check_warn()
715 self._log_check_error()
716
717 def _cleanup(self):
718 pass
719
720
721class OpkgRootfs(DpkgOpkgRootfs):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 def __init__(self, d, manifest_dir, progress_reporter=None):
723 super(OpkgRootfs, self).__init__(d, progress_reporter)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724 self.log_check_regex = '(exit 1|Collected errors)'
725
726 self.manifest = OpkgManifest(d, manifest_dir)
727 self.opkg_conf = self.d.getVar("IPKGCONF_TARGET", True)
728 self.pkg_archs = self.d.getVar("ALL_MULTILIB_PACKAGE_ARCHS", True)
729
730 self.inc_opkg_image_gen = self.d.getVar('INC_IPK_IMAGE_GEN', True) or ""
731 if self._remove_old_rootfs():
732 bb.utils.remove(self.image_rootfs, True)
733 self.pm = OpkgPM(d,
734 self.image_rootfs,
735 self.opkg_conf,
736 self.pkg_archs)
737 else:
738 self.pm = OpkgPM(d,
739 self.image_rootfs,
740 self.opkg_conf,
741 self.pkg_archs)
742 self.pm.recover_packaging_data()
743
744 bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True)
745
746 def _prelink_file(self, root_dir, filename):
747 bb.note('prelink %s in %s' % (filename, root_dir))
748 prelink_cfg = oe.path.join(root_dir,
749 self.d.expand('${sysconfdir}/prelink.conf'))
750 if not os.path.exists(prelink_cfg):
751 shutil.copy(self.d.expand('${STAGING_DIR_NATIVE}${sysconfdir_native}/prelink.conf'),
752 prelink_cfg)
753
754 cmd_prelink = self.d.expand('${STAGING_DIR_NATIVE}${sbindir_native}/prelink')
755 self._exec_shell_cmd([cmd_prelink,
756 '--root',
757 root_dir,
758 '-amR',
759 '-N',
760 '-c',
761 self.d.expand('${sysconfdir}/prelink.conf')])
762
763 '''
764 Compare two files with the same key twice to see if they are equal.
765 If they are not equal, it means they are duplicated and come from
766 different packages.
767 1st: Comapre them directly;
768 2nd: While incremental image creation is enabled, one of the
769 files could be probaly prelinked in the previous image
770 creation and the file has been changed, so we need to
771 prelink the other one and compare them.
772 '''
773 def _file_equal(self, key, f1, f2):
774
775 # Both of them are not prelinked
776 if filecmp.cmp(f1, f2):
777 return True
778
779 if self.image_rootfs not in f1:
780 self._prelink_file(f1.replace(key, ''), f1)
781
782 if self.image_rootfs not in f2:
783 self._prelink_file(f2.replace(key, ''), f2)
784
785 # Both of them are prelinked
786 if filecmp.cmp(f1, f2):
787 return True
788
789 # Not equal
790 return False
791
792 """
793 This function was reused from the old implementation.
794 See commit: "image.bbclass: Added variables for multilib support." by
795 Lianhao Lu.
796 """
797 def _multilib_sanity_test(self, dirs):
798
799 allow_replace = self.d.getVar("MULTILIBRE_ALLOW_REP", True)
800 if allow_replace is None:
801 allow_replace = ""
802
803 allow_rep = re.compile(re.sub("\|$", "", allow_replace))
804 error_prompt = "Multilib check error:"
805
806 files = {}
807 for dir in dirs:
808 for root, subfolders, subfiles in os.walk(dir):
809 for file in subfiles:
810 item = os.path.join(root, file)
811 key = str(os.path.join("/", os.path.relpath(item, dir)))
812
813 valid = True
814 if key in files:
815 #check whether the file is allow to replace
816 if allow_rep.match(key):
817 valid = True
818 else:
819 if os.path.exists(files[key]) and \
820 os.path.exists(item) and \
821 not self._file_equal(key, files[key], item):
822 valid = False
823 bb.fatal("%s duplicate files %s %s is not the same\n" %
824 (error_prompt, item, files[key]))
825
826 #pass the check, add to list
827 if valid:
828 files[key] = item
829
830 def _multilib_test_install(self, pkgs):
831 ml_temp = self.d.getVar("MULTILIB_TEMP_ROOTFS", True)
832 bb.utils.mkdirhier(ml_temp)
833
834 dirs = [self.image_rootfs]
835
836 for variant in self.d.getVar("MULTILIB_VARIANTS", True).split():
837 ml_target_rootfs = os.path.join(ml_temp, variant)
838
839 bb.utils.remove(ml_target_rootfs, True)
840
841 ml_opkg_conf = os.path.join(ml_temp,
842 variant + "-" + os.path.basename(self.opkg_conf))
843
844 ml_pm = OpkgPM(self.d, ml_target_rootfs, ml_opkg_conf, self.pkg_archs)
845
846 ml_pm.update()
847 ml_pm.install(pkgs)
848
849 dirs.append(ml_target_rootfs)
850
851 self._multilib_sanity_test(dirs)
852
853 '''
854 While ipk incremental image generation is enabled, it will remove the
855 unneeded pkgs by comparing the old full manifest in previous existing
856 image and the new full manifest in the current image.
857 '''
858 def _remove_extra_packages(self, pkgs_initial_install):
859 if self.inc_opkg_image_gen == "1":
860 # Parse full manifest in previous existing image creation session
861 old_full_manifest = self.manifest.parse_full_manifest()
862
863 # Create full manifest for the current image session, the old one
864 # will be replaced by the new one.
865 self.manifest.create_full(self.pm)
866
867 # Parse full manifest in current image creation session
868 new_full_manifest = self.manifest.parse_full_manifest()
869
870 pkg_to_remove = list()
871 for pkg in old_full_manifest:
872 if pkg not in new_full_manifest:
873 pkg_to_remove.append(pkg)
874
875 if pkg_to_remove != []:
876 bb.note('decremental removed: %s' % ' '.join(pkg_to_remove))
877 self.pm.remove(pkg_to_remove)
878
879 '''
880 Compare with previous existing image creation, if some conditions
881 triggered, the previous old image should be removed.
882 The conditions include any of 'PACKAGE_EXCLUDE, NO_RECOMMENDATIONS
883 and BAD_RECOMMENDATIONS' has been changed.
884 '''
885 def _remove_old_rootfs(self):
886 if self.inc_opkg_image_gen != "1":
887 return True
888
889 vars_list_file = self.d.expand('${T}/vars_list')
890
891 old_vars_list = ""
892 if os.path.exists(vars_list_file):
893 old_vars_list = open(vars_list_file, 'r+').read()
894
895 new_vars_list = '%s:%s:%s\n' % \
896 ((self.d.getVar('BAD_RECOMMENDATIONS', True) or '').strip(),
897 (self.d.getVar('NO_RECOMMENDATIONS', True) or '').strip(),
898 (self.d.getVar('PACKAGE_EXCLUDE', True) or '').strip())
899 open(vars_list_file, 'w+').write(new_vars_list)
900
901 if old_vars_list != new_vars_list:
902 return True
903
904 return False
905
906 def _create(self):
907 pkgs_to_install = self.manifest.parse_initial_manifest()
908 opkg_pre_process_cmds = self.d.getVar('OPKG_PREPROCESS_COMMANDS', True)
909 opkg_post_process_cmds = self.d.getVar('OPKG_POSTPROCESS_COMMANDS', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910
911 # update PM index files, unless users provide their own feeds
912 if (self.d.getVar('BUILD_IMAGES_FROM_FEEDS', True) or "") != "1":
913 self.pm.write_index()
914
915 execute_pre_post_process(self.d, opkg_pre_process_cmds)
916
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917 if self.progress_reporter:
918 self.progress_reporter.next_stage()
919 # Steps are a bit different in order, skip next
920 self.progress_reporter.next_stage()
921
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922 self.pm.update()
923
924 self.pm.handle_bad_recommendations()
925
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600926 if self.progress_reporter:
927 self.progress_reporter.next_stage()
928
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500929 if self.inc_opkg_image_gen == "1":
930 self._remove_extra_packages(pkgs_to_install)
931
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600932 if self.progress_reporter:
933 self.progress_reporter.next_stage()
934
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935 for pkg_type in self.install_order:
936 if pkg_type in pkgs_to_install:
937 # For multilib, we perform a sanity test before final install
938 # If sanity test fails, it will automatically do a bb.fatal()
939 # and the installation will stop
940 if pkg_type == Manifest.PKG_TYPE_MULTILIB:
941 self._multilib_test_install(pkgs_to_install[pkg_type])
942
943 self.pm.install(pkgs_to_install[pkg_type],
944 [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
945
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600946 if self.progress_reporter:
947 self.progress_reporter.next_stage()
948
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500949 self.pm.install_complementary()
950
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600951 if self.progress_reporter:
952 self.progress_reporter.next_stage()
953
954 self._setup_dbg_rootfs(['/etc', '/var/lib/opkg', '/usr/lib/ssl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955
956 execute_pre_post_process(self.d, opkg_post_process_cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500957
958 if self.inc_opkg_image_gen == "1":
959 self.pm.backup_packaging_data()
960
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600961 if self.progress_reporter:
962 self.progress_reporter.next_stage()
963
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964 @staticmethod
965 def _depends_list():
966 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']
967
968 def _get_delayed_postinsts(self):
969 status_file = os.path.join(self.image_rootfs,
970 self.d.getVar('OPKGLIBDIR', True).strip('/'),
971 "opkg", "status")
972 return self._get_delayed_postinsts_common(status_file)
973
974 def _save_postinsts(self):
975 dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/ipk-postinsts")
976 src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${OPKGLIBDIR}/opkg/info")
977 return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir)
978
979 def _handle_intercept_failure(self, registered_pkgs):
980 self.pm.mark_packages("unpacked", registered_pkgs.split())
981
982 def _log_check(self):
983 self._log_check_warn()
984 self._log_check_error()
985
986 def _cleanup(self):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500987 self.pm.remove_lists()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500988
989def get_class_for_type(imgtype):
990 return {"rpm": RpmRootfs,
991 "ipk": OpkgRootfs,
992 "deb": DpkgRootfs}[imgtype]
993
994def variable_depends(d, manifest_dir=None):
995 img_type = d.getVar('IMAGE_PKGTYPE', True)
996 cls = get_class_for_type(img_type)
997 return cls._depends_list()
998
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600999def create_rootfs(d, manifest_dir=None, progress_reporter=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001000 env_bkp = os.environ.copy()
1001
1002 img_type = d.getVar('IMAGE_PKGTYPE', True)
1003 if img_type == "rpm":
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001004 RpmRootfs(d, manifest_dir, progress_reporter).create()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005 elif img_type == "ipk":
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001006 OpkgRootfs(d, manifest_dir, progress_reporter).create()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007 elif img_type == "deb":
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001008 DpkgRootfs(d, manifest_dir, progress_reporter).create()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009
1010 os.environ.clear()
1011 os.environ.update(env_bkp)
1012
1013
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001014def image_list_installed_packages(d, rootfs_dir=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001015 if not rootfs_dir:
1016 rootfs_dir = d.getVar('IMAGE_ROOTFS', True)
1017
1018 img_type = d.getVar('IMAGE_PKGTYPE', True)
1019 if img_type == "rpm":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001020 return RpmPkgsList(d, rootfs_dir).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021 elif img_type == "ipk":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001022 return OpkgPkgsList(d, rootfs_dir, d.getVar("IPKGCONF_TARGET", True)).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001023 elif img_type == "deb":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001024 return DpkgPkgsList(d, rootfs_dir).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001025
1026if __name__ == "__main__":
1027 """
1028 We should be able to run this as a standalone script, from outside bitbake
1029 environment.
1030 """
1031 """
1032 TBD
1033 """