blob: f96788399f7cd3acfd403e9b6b5a415123d8b8c4 [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
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500189 execute_pre_post_process(self.d, pre_process_cmds)
190
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600191 if self.progress_reporter:
192 self.progress_reporter.next_stage()
193
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500194 # call the package manager dependent create method
195 self._create()
196
197 sysconfdir = self.image_rootfs + self.d.getVar('sysconfdir', True)
198 bb.utils.mkdirhier(sysconfdir)
199 with open(sysconfdir + "/version", "w+") as ver:
200 ver.write(self.d.getVar('BUILDNAME', True) + "\n")
201
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500202 execute_pre_post_process(self.d, rootfs_post_install_cmds)
203
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500204 self._run_intercepts()
205
206 execute_pre_post_process(self.d, post_process_cmds)
207
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600208 if self.progress_reporter:
209 self.progress_reporter.next_stage()
210
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211 if bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs",
212 True, False, self.d):
213 delayed_postinsts = self._get_delayed_postinsts()
214 if delayed_postinsts is not None:
215 bb.fatal("The following packages could not be configured "
216 "offline and rootfs is read-only: %s" %
217 delayed_postinsts)
218
219 if self.d.getVar('USE_DEVFS', True) != "1":
220 self._create_devfs()
221
222 self._uninstall_unneeded()
223
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600224 if self.progress_reporter:
225 self.progress_reporter.next_stage()
226
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500227 self._insert_feed_uris()
228
229 self._run_ldconfig()
230
231 if self.d.getVar('USE_DEPMOD', True) != "0":
232 self._generate_kernel_module_deps()
233
234 self._cleanup()
235 self._log_check()
236
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600237 if self.progress_reporter:
238 self.progress_reporter.next_stage()
239
240
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500241 def _uninstall_unneeded(self):
242 # Remove unneeded init script symlinks
243 delayed_postinsts = self._get_delayed_postinsts()
244 if delayed_postinsts is None:
245 if os.path.exists(self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/init.d/run-postinsts")):
246 self._exec_shell_cmd(["update-rc.d", "-f", "-r",
247 self.d.getVar('IMAGE_ROOTFS', True),
248 "run-postinsts", "remove"])
249
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500250 image_rorfs = bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs",
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500251 True, False, self.d)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600252 image_rorfs_force = self.d.getVar('FORCE_RO_REMOVE', True)
253
254 if image_rorfs or image_rorfs_force == "1":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500255 # Remove components that we don't need if it's a read-only rootfs
256 unneeded_pkgs = self.d.getVar("ROOTFS_RO_UNNEEDED", True).split()
257 pkgs_installed = image_list_installed_packages(self.d)
258 pkgs_to_remove = [pkg for pkg in pkgs_installed if pkg in unneeded_pkgs]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500259
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500260 if len(pkgs_to_remove) > 0:
261 self.pm.remove(pkgs_to_remove, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500263 if delayed_postinsts:
264 self._save_postinsts()
265 if image_rorfs:
266 bb.warn("There are post install scripts "
267 "in a read-only rootfs")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268
269 post_uninstall_cmds = self.d.getVar("ROOTFS_POSTUNINSTALL_COMMAND", True)
270 execute_pre_post_process(self.d, post_uninstall_cmds)
271
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500272 runtime_pkgmanage = bb.utils.contains("IMAGE_FEATURES", "package-management",
273 True, False, self.d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 if not runtime_pkgmanage:
275 # Remove the package manager data files
276 self.pm.remove_packaging_data()
277
278 def _run_intercepts(self):
279 intercepts_dir = os.path.join(self.d.getVar('WORKDIR', True),
280 "intercept_scripts")
281
282 bb.note("Running intercept scripts:")
283 os.environ['D'] = self.image_rootfs
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500284 os.environ['STAGING_DIR_NATIVE'] = self.d.getVar('STAGING_DIR_NATIVE', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285 for script in os.listdir(intercepts_dir):
286 script_full = os.path.join(intercepts_dir, script)
287
288 if script == "postinst_intercept" or not os.access(script_full, os.X_OK):
289 continue
290
291 bb.note("> Executing %s intercept ..." % script)
292
293 try:
294 subprocess.check_call(script_full)
295 except subprocess.CalledProcessError as e:
296 bb.warn("The postinstall intercept hook '%s' failed (exit code: %d)! See log for details!" %
297 (script, e.returncode))
298
299 with open(script_full) as intercept:
300 registered_pkgs = None
301 for line in intercept.read().split("\n"):
302 m = re.match("^##PKGS:(.*)", line)
303 if m is not None:
304 registered_pkgs = m.group(1).strip()
305 break
306
307 if registered_pkgs is not None:
308 bb.warn("The postinstalls for the following packages "
309 "will be postponed for first boot: %s" %
310 registered_pkgs)
311
312 # call the backend dependent handler
313 self._handle_intercept_failure(registered_pkgs)
314
315 def _run_ldconfig(self):
316 if self.d.getVar('LDCONFIGDEPEND', True):
317 bb.note("Executing: ldconfig -r" + self.image_rootfs + "-c new -v")
318 self._exec_shell_cmd(['ldconfig', '-r', self.image_rootfs, '-c',
319 'new', '-v'])
320
321 def _check_for_kernel_modules(self, modules_dir):
322 for root, dirs, files in os.walk(modules_dir, topdown=True):
323 for name in files:
324 found_ko = name.endswith(".ko")
325 if found_ko:
326 return found_ko
327 return False
328
329 def _generate_kernel_module_deps(self):
330 modules_dir = os.path.join(self.image_rootfs, 'lib', 'modules')
331 # if we don't have any modules don't bother to do the depmod
332 if not self._check_for_kernel_modules(modules_dir):
333 bb.note("No Kernel Modules found, not running depmod")
334 return
335
336 kernel_abi_ver_file = oe.path.join(self.d.getVar('PKGDATA_DIR', True), "kernel-depmod",
337 'kernel-abiversion')
338 if not os.path.exists(kernel_abi_ver_file):
339 bb.fatal("No kernel-abiversion file found (%s), cannot run depmod, aborting" % kernel_abi_ver_file)
340
341 kernel_ver = open(kernel_abi_ver_file).read().strip(' \n')
342 versioned_modules_dir = os.path.join(self.image_rootfs, modules_dir, kernel_ver)
343
344 bb.utils.mkdirhier(versioned_modules_dir)
345
346 self._exec_shell_cmd(['depmodwrapper', '-a', '-b', self.image_rootfs, kernel_ver])
347
348 """
349 Create devfs:
350 * IMAGE_DEVICE_TABLE is the old name to an absolute path to a device table file
351 * IMAGE_DEVICE_TABLES is a new name for a file, or list of files, seached
352 for in the BBPATH
353 If neither are specified then the default name of files/device_table-minimal.txt
354 is searched for in the BBPATH (same as the old version.)
355 """
356 def _create_devfs(self):
357 devtable_list = []
358 devtable = self.d.getVar('IMAGE_DEVICE_TABLE', True)
359 if devtable is not None:
360 devtable_list.append(devtable)
361 else:
362 devtables = self.d.getVar('IMAGE_DEVICE_TABLES', True)
363 if devtables is None:
364 devtables = 'files/device_table-minimal.txt'
365 for devtable in devtables.split():
366 devtable_list.append("%s" % bb.utils.which(self.d.getVar('BBPATH', True), devtable))
367
368 for devtable in devtable_list:
369 self._exec_shell_cmd(["makedevs", "-r",
370 self.image_rootfs, "-D", devtable])
371
372
373class RpmRootfs(Rootfs):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600374 def __init__(self, d, manifest_dir, progress_reporter=None):
375 super(RpmRootfs, self).__init__(d, progress_reporter)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376 self.log_check_regex = '(unpacking of archive failed|Cannot find package'\
377 '|exit 1|ERROR: |Error: |Error |ERROR '\
378 '|Failed |Failed: |Failed$|Failed\(\d+\):)'
379 self.manifest = RpmManifest(d, manifest_dir)
380
381 self.pm = RpmPM(d,
382 d.getVar('IMAGE_ROOTFS', True),
383 self.d.getVar('TARGET_VENDOR', True)
384 )
385
386 self.inc_rpm_image_gen = self.d.getVar('INC_RPM_IMAGE_GEN', True)
387 if self.inc_rpm_image_gen != "1":
388 bb.utils.remove(self.image_rootfs, True)
389 else:
390 self.pm.recovery_packaging_data()
391 bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True)
392
393 self.pm.create_configs()
394
395 '''
396 While rpm incremental image generation is enabled, it will remove the
397 unneeded pkgs by comparing the new install solution manifest and the
398 old installed manifest.
399 '''
400 def _create_incremental(self, pkgs_initial_install):
401 if self.inc_rpm_image_gen == "1":
402
403 pkgs_to_install = list()
404 for pkg_type in pkgs_initial_install:
405 pkgs_to_install += pkgs_initial_install[pkg_type]
406
407 installed_manifest = self.pm.load_old_install_solution()
408 solution_manifest = self.pm.dump_install_solution(pkgs_to_install)
409
410 pkg_to_remove = list()
411 for pkg in installed_manifest:
412 if pkg not in solution_manifest:
413 pkg_to_remove.append(pkg)
414
415 self.pm.update()
416
417 bb.note('incremental update -- upgrade packages in place ')
418 self.pm.upgrade()
419 if pkg_to_remove != []:
420 bb.note('incremental removed: %s' % ' '.join(pkg_to_remove))
421 self.pm.remove(pkg_to_remove)
422
423 def _create(self):
424 pkgs_to_install = self.manifest.parse_initial_manifest()
425 rpm_pre_process_cmds = self.d.getVar('RPM_PREPROCESS_COMMANDS', True)
426 rpm_post_process_cmds = self.d.getVar('RPM_POSTPROCESS_COMMANDS', True)
427
428 # update PM index files
429 self.pm.write_index()
430
431 execute_pre_post_process(self.d, rpm_pre_process_cmds)
432
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600433 if self.progress_reporter:
434 self.progress_reporter.next_stage()
435
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436 self.pm.dump_all_available_pkgs()
437
438 if self.inc_rpm_image_gen == "1":
439 self._create_incremental(pkgs_to_install)
440
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 if self.progress_reporter:
442 self.progress_reporter.next_stage()
443
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444 self.pm.update()
445
446 pkgs = []
447 pkgs_attempt = []
448 for pkg_type in pkgs_to_install:
449 if pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY:
450 pkgs_attempt += pkgs_to_install[pkg_type]
451 else:
452 pkgs += pkgs_to_install[pkg_type]
453
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600454 if self.progress_reporter:
455 self.progress_reporter.next_stage()
456
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500457 self.pm.install(pkgs)
458
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600459 if self.progress_reporter:
460 self.progress_reporter.next_stage()
461
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500462 self.pm.install(pkgs_attempt, True)
463
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600464 if self.progress_reporter:
465 self.progress_reporter.next_stage()
466
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500467 self.pm.install_complementary()
468
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600469 if self.progress_reporter:
470 self.progress_reporter.next_stage()
471
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472 self._setup_dbg_rootfs(['/etc/rpm', '/var/lib/rpm', '/var/lib/smart'])
473
474 execute_pre_post_process(self.d, rpm_post_process_cmds)
475
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476 if self.inc_rpm_image_gen == "1":
477 self.pm.backup_packaging_data()
478
479 self.pm.rpm_setup_smart_target_config()
480
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600481 if self.progress_reporter:
482 self.progress_reporter.next_stage()
483
484
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500485 @staticmethod
486 def _depends_list():
487 return ['DEPLOY_DIR_RPM', 'INC_RPM_IMAGE_GEN', 'RPM_PREPROCESS_COMMANDS',
488 'RPM_POSTPROCESS_COMMANDS', 'RPM_PREFER_ELF_ARCH']
489
490 def _get_delayed_postinsts(self):
491 postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/rpm-postinsts")
492 if os.path.isdir(postinst_dir):
493 files = os.listdir(postinst_dir)
494 for f in files:
495 bb.note('Delayed package scriptlet: %s' % f)
496 return files
497
498 return None
499
500 def _save_postinsts(self):
501 # this is just a stub. For RPM, the failed postinstalls are
502 # already saved in /etc/rpm-postinsts
503 pass
504
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500505 def _log_check(self):
506 self._log_check_warn()
507 self._log_check_error()
508
509 def _handle_intercept_failure(self, registered_pkgs):
510 rpm_postinsts_dir = self.image_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
511 bb.utils.mkdirhier(rpm_postinsts_dir)
512
513 # Save the package postinstalls in /etc/rpm-postinsts
514 for pkg in registered_pkgs.split():
515 self.pm.save_rpmpostinst(pkg)
516
517 def _cleanup(self):
518 # during the execution of postprocess commands, rpm is called several
519 # times to get the files installed, dependencies, etc. This creates the
520 # __db.00* (Berkeley DB files that hold locks, rpm specific environment
521 # settings, etc.), that should not get into the final rootfs
522 self.pm.unlock_rpm_db()
523 if os.path.isdir(self.pm.install_dir_path + "/tmp") and not os.listdir(self.pm.install_dir_path + "/tmp"):
524 bb.utils.remove(self.pm.install_dir_path + "/tmp", True)
525 if os.path.isdir(self.pm.install_dir_path) and not os.listdir(self.pm.install_dir_path):
526 bb.utils.remove(self.pm.install_dir_path, True)
527
528class DpkgOpkgRootfs(Rootfs):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 def __init__(self, d, progress_reporter=None):
530 super(DpkgOpkgRootfs, self).__init__(d, progress_reporter)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531
532 def _get_pkgs_postinsts(self, status_file):
533 def _get_pkg_depends_list(pkg_depends):
534 pkg_depends_list = []
535 # filter version requirements like libc (>= 1.1)
536 for dep in pkg_depends.split(', '):
537 m_dep = re.match("^(.*) \(.*\)$", dep)
538 if m_dep:
539 dep = m_dep.group(1)
540 pkg_depends_list.append(dep)
541
542 return pkg_depends_list
543
544 pkgs = {}
545 pkg_name = ""
546 pkg_status_match = False
547 pkg_depends = ""
548
549 with open(status_file) as status:
550 data = status.read()
551 status.close()
552 for line in data.split('\n'):
553 m_pkg = re.match("^Package: (.*)", line)
554 m_status = re.match("^Status:.*unpacked", line)
555 m_depends = re.match("^Depends: (.*)", line)
556
557 if m_pkg is not None:
558 if pkg_name and pkg_status_match:
559 pkgs[pkg_name] = _get_pkg_depends_list(pkg_depends)
560
561 pkg_name = m_pkg.group(1)
562 pkg_status_match = False
563 pkg_depends = ""
564 elif m_status is not None:
565 pkg_status_match = True
566 elif m_depends is not None:
567 pkg_depends = m_depends.group(1)
568
569 # remove package dependencies not in postinsts
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600570 pkg_names = list(pkgs.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571 for pkg_name in pkg_names:
572 deps = pkgs[pkg_name][:]
573
574 for d in deps:
575 if d not in pkg_names:
576 pkgs[pkg_name].remove(d)
577
578 return pkgs
579
580 def _get_delayed_postinsts_common(self, status_file):
581 def _dep_resolve(graph, node, resolved, seen):
582 seen.append(node)
583
584 for edge in graph[node]:
585 if edge not in resolved:
586 if edge in seen:
587 raise RuntimeError("Packages %s and %s have " \
588 "a circular dependency in postinsts scripts." \
589 % (node, edge))
590 _dep_resolve(graph, edge, resolved, seen)
591
592 resolved.append(node)
593
594 pkg_list = []
595
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500596 pkgs = None
597 if not self.d.getVar('PACKAGE_INSTALL', True).strip():
598 bb.note("Building empty image")
599 else:
600 pkgs = self._get_pkgs_postinsts(status_file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601 if pkgs:
602 root = "__packagegroup_postinst__"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 pkgs[root] = list(pkgs.keys())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500604 _dep_resolve(pkgs, root, pkg_list, [])
605 pkg_list.remove(root)
606
607 if len(pkg_list) == 0:
608 return None
609
610 return pkg_list
611
612 def _save_postinsts_common(self, dst_postinst_dir, src_postinst_dir):
613 num = 0
614 for p in self._get_delayed_postinsts():
615 bb.utils.mkdirhier(dst_postinst_dir)
616
617 if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")):
618 shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"),
619 os.path.join(dst_postinst_dir, "%03d-%s" % (num, p)))
620
621 num += 1
622
623class DpkgRootfs(DpkgOpkgRootfs):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600624 def __init__(self, d, manifest_dir, progress_reporter=None):
625 super(DpkgRootfs, self).__init__(d, progress_reporter)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500626 self.log_check_regex = '^E:'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600627 self.log_check_expected_regexes = \
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500628 [
629 "^E: Unmet dependencies."
630 ]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631
632 bb.utils.remove(self.image_rootfs, True)
633 bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True)
634 self.manifest = DpkgManifest(d, manifest_dir)
635 self.pm = DpkgPM(d, d.getVar('IMAGE_ROOTFS', True),
636 d.getVar('PACKAGE_ARCHS', True),
637 d.getVar('DPKG_ARCH', True))
638
639
640 def _create(self):
641 pkgs_to_install = self.manifest.parse_initial_manifest()
642 deb_pre_process_cmds = self.d.getVar('DEB_PREPROCESS_COMMANDS', True)
643 deb_post_process_cmds = self.d.getVar('DEB_POSTPROCESS_COMMANDS', True)
644
645 alt_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/alternatives")
646 bb.utils.mkdirhier(alt_dir)
647
648 # update PM index files
649 self.pm.write_index()
650
651 execute_pre_post_process(self.d, deb_pre_process_cmds)
652
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600653 if self.progress_reporter:
654 self.progress_reporter.next_stage()
655 # Don't support incremental, so skip that
656 self.progress_reporter.next_stage()
657
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500658 self.pm.update()
659
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 if self.progress_reporter:
661 self.progress_reporter.next_stage()
662
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 for pkg_type in self.install_order:
664 if pkg_type in pkgs_to_install:
665 self.pm.install(pkgs_to_install[pkg_type],
666 [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
667
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600668 if self.progress_reporter:
669 # Don't support attemptonly, so skip that
670 self.progress_reporter.next_stage()
671 self.progress_reporter.next_stage()
672
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673 self.pm.install_complementary()
674
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600675 if self.progress_reporter:
676 self.progress_reporter.next_stage()
677
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678 self._setup_dbg_rootfs(['/var/lib/dpkg'])
679
680 self.pm.fix_broken_dependencies()
681
682 self.pm.mark_packages("installed")
683
684 self.pm.run_pre_post_installs()
685
686 execute_pre_post_process(self.d, deb_post_process_cmds)
687
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 if self.progress_reporter:
689 self.progress_reporter.next_stage()
690
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500691 @staticmethod
692 def _depends_list():
693 return ['DEPLOY_DIR_DEB', 'DEB_SDK_ARCH', 'APTCONF_TARGET', 'APT_ARGS', 'DPKG_ARCH', 'DEB_PREPROCESS_COMMANDS', 'DEB_POSTPROCESS_COMMANDS']
694
695 def _get_delayed_postinsts(self):
696 status_file = self.image_rootfs + "/var/lib/dpkg/status"
697 return self._get_delayed_postinsts_common(status_file)
698
699 def _save_postinsts(self):
700 dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/deb-postinsts")
701 src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}/var/lib/dpkg/info")
702 return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir)
703
704 def _handle_intercept_failure(self, registered_pkgs):
705 self.pm.mark_packages("unpacked", registered_pkgs.split())
706
707 def _log_check(self):
708 self._log_check_warn()
709 self._log_check_error()
710
711 def _cleanup(self):
712 pass
713
714
715class OpkgRootfs(DpkgOpkgRootfs):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600716 def __init__(self, d, manifest_dir, progress_reporter=None):
717 super(OpkgRootfs, self).__init__(d, progress_reporter)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500718 self.log_check_regex = '(exit 1|Collected errors)'
719
720 self.manifest = OpkgManifest(d, manifest_dir)
721 self.opkg_conf = self.d.getVar("IPKGCONF_TARGET", True)
722 self.pkg_archs = self.d.getVar("ALL_MULTILIB_PACKAGE_ARCHS", True)
723
724 self.inc_opkg_image_gen = self.d.getVar('INC_IPK_IMAGE_GEN', True) or ""
725 if self._remove_old_rootfs():
726 bb.utils.remove(self.image_rootfs, True)
727 self.pm = OpkgPM(d,
728 self.image_rootfs,
729 self.opkg_conf,
730 self.pkg_archs)
731 else:
732 self.pm = OpkgPM(d,
733 self.image_rootfs,
734 self.opkg_conf,
735 self.pkg_archs)
736 self.pm.recover_packaging_data()
737
738 bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS', True), True)
739
740 def _prelink_file(self, root_dir, filename):
741 bb.note('prelink %s in %s' % (filename, root_dir))
742 prelink_cfg = oe.path.join(root_dir,
743 self.d.expand('${sysconfdir}/prelink.conf'))
744 if not os.path.exists(prelink_cfg):
745 shutil.copy(self.d.expand('${STAGING_DIR_NATIVE}${sysconfdir_native}/prelink.conf'),
746 prelink_cfg)
747
748 cmd_prelink = self.d.expand('${STAGING_DIR_NATIVE}${sbindir_native}/prelink')
749 self._exec_shell_cmd([cmd_prelink,
750 '--root',
751 root_dir,
752 '-amR',
753 '-N',
754 '-c',
755 self.d.expand('${sysconfdir}/prelink.conf')])
756
757 '''
758 Compare two files with the same key twice to see if they are equal.
759 If they are not equal, it means they are duplicated and come from
760 different packages.
761 1st: Comapre them directly;
762 2nd: While incremental image creation is enabled, one of the
763 files could be probaly prelinked in the previous image
764 creation and the file has been changed, so we need to
765 prelink the other one and compare them.
766 '''
767 def _file_equal(self, key, f1, f2):
768
769 # Both of them are not prelinked
770 if filecmp.cmp(f1, f2):
771 return True
772
773 if self.image_rootfs not in f1:
774 self._prelink_file(f1.replace(key, ''), f1)
775
776 if self.image_rootfs not in f2:
777 self._prelink_file(f2.replace(key, ''), f2)
778
779 # Both of them are prelinked
780 if filecmp.cmp(f1, f2):
781 return True
782
783 # Not equal
784 return False
785
786 """
787 This function was reused from the old implementation.
788 See commit: "image.bbclass: Added variables for multilib support." by
789 Lianhao Lu.
790 """
791 def _multilib_sanity_test(self, dirs):
792
793 allow_replace = self.d.getVar("MULTILIBRE_ALLOW_REP", True)
794 if allow_replace is None:
795 allow_replace = ""
796
797 allow_rep = re.compile(re.sub("\|$", "", allow_replace))
798 error_prompt = "Multilib check error:"
799
800 files = {}
801 for dir in dirs:
802 for root, subfolders, subfiles in os.walk(dir):
803 for file in subfiles:
804 item = os.path.join(root, file)
805 key = str(os.path.join("/", os.path.relpath(item, dir)))
806
807 valid = True
808 if key in files:
809 #check whether the file is allow to replace
810 if allow_rep.match(key):
811 valid = True
812 else:
813 if os.path.exists(files[key]) and \
814 os.path.exists(item) and \
815 not self._file_equal(key, files[key], item):
816 valid = False
817 bb.fatal("%s duplicate files %s %s is not the same\n" %
818 (error_prompt, item, files[key]))
819
820 #pass the check, add to list
821 if valid:
822 files[key] = item
823
824 def _multilib_test_install(self, pkgs):
825 ml_temp = self.d.getVar("MULTILIB_TEMP_ROOTFS", True)
826 bb.utils.mkdirhier(ml_temp)
827
828 dirs = [self.image_rootfs]
829
830 for variant in self.d.getVar("MULTILIB_VARIANTS", True).split():
831 ml_target_rootfs = os.path.join(ml_temp, variant)
832
833 bb.utils.remove(ml_target_rootfs, True)
834
835 ml_opkg_conf = os.path.join(ml_temp,
836 variant + "-" + os.path.basename(self.opkg_conf))
837
838 ml_pm = OpkgPM(self.d, ml_target_rootfs, ml_opkg_conf, self.pkg_archs)
839
840 ml_pm.update()
841 ml_pm.install(pkgs)
842
843 dirs.append(ml_target_rootfs)
844
845 self._multilib_sanity_test(dirs)
846
847 '''
848 While ipk incremental image generation is enabled, it will remove the
849 unneeded pkgs by comparing the old full manifest in previous existing
850 image and the new full manifest in the current image.
851 '''
852 def _remove_extra_packages(self, pkgs_initial_install):
853 if self.inc_opkg_image_gen == "1":
854 # Parse full manifest in previous existing image creation session
855 old_full_manifest = self.manifest.parse_full_manifest()
856
857 # Create full manifest for the current image session, the old one
858 # will be replaced by the new one.
859 self.manifest.create_full(self.pm)
860
861 # Parse full manifest in current image creation session
862 new_full_manifest = self.manifest.parse_full_manifest()
863
864 pkg_to_remove = list()
865 for pkg in old_full_manifest:
866 if pkg not in new_full_manifest:
867 pkg_to_remove.append(pkg)
868
869 if pkg_to_remove != []:
870 bb.note('decremental removed: %s' % ' '.join(pkg_to_remove))
871 self.pm.remove(pkg_to_remove)
872
873 '''
874 Compare with previous existing image creation, if some conditions
875 triggered, the previous old image should be removed.
876 The conditions include any of 'PACKAGE_EXCLUDE, NO_RECOMMENDATIONS
877 and BAD_RECOMMENDATIONS' has been changed.
878 '''
879 def _remove_old_rootfs(self):
880 if self.inc_opkg_image_gen != "1":
881 return True
882
883 vars_list_file = self.d.expand('${T}/vars_list')
884
885 old_vars_list = ""
886 if os.path.exists(vars_list_file):
887 old_vars_list = open(vars_list_file, 'r+').read()
888
889 new_vars_list = '%s:%s:%s\n' % \
890 ((self.d.getVar('BAD_RECOMMENDATIONS', True) or '').strip(),
891 (self.d.getVar('NO_RECOMMENDATIONS', True) or '').strip(),
892 (self.d.getVar('PACKAGE_EXCLUDE', True) or '').strip())
893 open(vars_list_file, 'w+').write(new_vars_list)
894
895 if old_vars_list != new_vars_list:
896 return True
897
898 return False
899
900 def _create(self):
901 pkgs_to_install = self.manifest.parse_initial_manifest()
902 opkg_pre_process_cmds = self.d.getVar('OPKG_PREPROCESS_COMMANDS', True)
903 opkg_post_process_cmds = self.d.getVar('OPKG_POSTPROCESS_COMMANDS', True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500904
905 # update PM index files, unless users provide their own feeds
906 if (self.d.getVar('BUILD_IMAGES_FROM_FEEDS', True) or "") != "1":
907 self.pm.write_index()
908
909 execute_pre_post_process(self.d, opkg_pre_process_cmds)
910
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600911 if self.progress_reporter:
912 self.progress_reporter.next_stage()
913 # Steps are a bit different in order, skip next
914 self.progress_reporter.next_stage()
915
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500916 self.pm.update()
917
918 self.pm.handle_bad_recommendations()
919
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600920 if self.progress_reporter:
921 self.progress_reporter.next_stage()
922
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500923 if self.inc_opkg_image_gen == "1":
924 self._remove_extra_packages(pkgs_to_install)
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 for pkg_type in self.install_order:
930 if pkg_type in pkgs_to_install:
931 # For multilib, we perform a sanity test before final install
932 # If sanity test fails, it will automatically do a bb.fatal()
933 # and the installation will stop
934 if pkg_type == Manifest.PKG_TYPE_MULTILIB:
935 self._multilib_test_install(pkgs_to_install[pkg_type])
936
937 self.pm.install(pkgs_to_install[pkg_type],
938 [False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
939
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600940 if self.progress_reporter:
941 self.progress_reporter.next_stage()
942
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500943 self.pm.install_complementary()
944
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600945 if self.progress_reporter:
946 self.progress_reporter.next_stage()
947
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500948 opkg_lib_dir = self.d.getVar('OPKGLIBDIR', True)
949 opkg_dir = os.path.join(opkg_lib_dir, 'opkg')
950 self._setup_dbg_rootfs(['/etc', opkg_dir, '/usr/lib/ssl'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500951
952 execute_pre_post_process(self.d, opkg_post_process_cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500953
954 if self.inc_opkg_image_gen == "1":
955 self.pm.backup_packaging_data()
956
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600957 if self.progress_reporter:
958 self.progress_reporter.next_stage()
959
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500960 @staticmethod
961 def _depends_list():
962 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']
963
964 def _get_delayed_postinsts(self):
965 status_file = os.path.join(self.image_rootfs,
966 self.d.getVar('OPKGLIBDIR', True).strip('/'),
967 "opkg", "status")
968 return self._get_delayed_postinsts_common(status_file)
969
970 def _save_postinsts(self):
971 dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/ipk-postinsts")
972 src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${OPKGLIBDIR}/opkg/info")
973 return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir)
974
975 def _handle_intercept_failure(self, registered_pkgs):
976 self.pm.mark_packages("unpacked", registered_pkgs.split())
977
978 def _log_check(self):
979 self._log_check_warn()
980 self._log_check_error()
981
982 def _cleanup(self):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500983 self.pm.remove_lists()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984
985def get_class_for_type(imgtype):
986 return {"rpm": RpmRootfs,
987 "ipk": OpkgRootfs,
988 "deb": DpkgRootfs}[imgtype]
989
990def variable_depends(d, manifest_dir=None):
991 img_type = d.getVar('IMAGE_PKGTYPE', True)
992 cls = get_class_for_type(img_type)
993 return cls._depends_list()
994
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600995def create_rootfs(d, manifest_dir=None, progress_reporter=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500996 env_bkp = os.environ.copy()
997
998 img_type = d.getVar('IMAGE_PKGTYPE', True)
999 if img_type == "rpm":
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001000 RpmRootfs(d, manifest_dir, progress_reporter).create()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001 elif img_type == "ipk":
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001002 OpkgRootfs(d, manifest_dir, progress_reporter).create()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003 elif img_type == "deb":
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001004 DpkgRootfs(d, manifest_dir, progress_reporter).create()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005
1006 os.environ.clear()
1007 os.environ.update(env_bkp)
1008
1009
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001010def image_list_installed_packages(d, rootfs_dir=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001011 if not rootfs_dir:
1012 rootfs_dir = d.getVar('IMAGE_ROOTFS', True)
1013
1014 img_type = d.getVar('IMAGE_PKGTYPE', True)
1015 if img_type == "rpm":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001016 return RpmPkgsList(d, rootfs_dir).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001017 elif img_type == "ipk":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001018 return OpkgPkgsList(d, rootfs_dir, d.getVar("IPKGCONF_TARGET", True)).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001019 elif img_type == "deb":
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001020 return DpkgPkgsList(d, rootfs_dir).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021
1022if __name__ == "__main__":
1023 """
1024 We should be able to run this as a standalone script, from outside bitbake
1025 environment.
1026 """
1027 """
1028 TBD
1029 """