1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
#!/usr/bin/env python3
#
# Helpers for preparing pseudo-managed rootfs trees (NFS booting, SDK/rootfs
# extraction, target deploy).
#
# SPDX-License-Identifier: GPL-2.0-only
"""Extract rootfs tarballs and locate their pseudo state for NFS booting."""
import os
import subprocess
from pathlib import Path
class PseudoRootfsError(Exception):
"""Raised when a pseudo-managed rootfs cannot be prepared or exported."""
def pseudo_native_environment():
"""Return the pseudo native environment (PSEUDO and OECORE_NATIVE_SYSROOT), from the
qemu-helper-native recipe which provides a pseudo binary usable outside a recipe sysroot."""
native_sysroot = os.environ.get('OECORE_NATIVE_SYSROOT')
if not native_sysroot:
try:
import bb.tinfoil
except ImportError as exc:
raise PseudoRootfsError(
'Unable to import bitbake.\n'
'Did you forget to source your build system environment setup script?') from exc
try:
with bb.tinfoil.Tinfoil() as tinfoil:
tinfoil.prepare(quiet=2)
native_sysroot = tinfoil.parse_recipe('qemu-helper-native').getVar('STAGING_DIR_NATIVE')
except Exception as exc:
raise PseudoRootfsError('Unable to set up the qemu-helper-native sysroot') from exc
if not native_sysroot or not os.path.exists(native_sysroot):
raise PseudoRootfsError("%s doesn't exist" % native_sysroot)
environment = {'OECORE_NATIVE_SYSROOT': native_sysroot}
environment['PSEUDO'] = os.path.join(native_sysroot, 'usr', 'bin', 'pseudo')
return environment
def _tar_options(rootfs_tarball):
tar_extract_options = {
'.tar.xz': '-xJf',
'.tar.bz2': '-xjf',
'.tar.gz': '-xzf',
'.tar.zst': '--zstd -xf',
'.tar': '-xf',
}
for extension, option in tar_extract_options.items():
if rootfs_tarball.endswith(extension):
return ['--numeric-owner', *option.split()]
raise PseudoRootfsError(
'Unable to determine sdk tarball format\n'
'Accepted types: .tar / .tar.gz / .tar.bz2 / .tar.xz / .tar.zst')
def pseudo_state_dir(rootfs_dir):
"""Return the pseudo database location associated with an extracted rootfs."""
return os.path.realpath(rootfs_dir) + '.pseudo_state'
def extract_sdk_rootfs(rootfs_tarball, rootfs_dir, pseudo_cmd, environment):
"""Extract a rootfs tarball under pseudo and return its absolute directory.
pseudo_cmd is the pseudo invocation prefix, e.g. a recipe's own
[FAKEROOTCMD] or [pseudo, '-P', native_sysroot_usr] from
pseudo_native_environment(). environment supplies the matching pseudo
database/env vars (e.g. a parsed FAKEROOTENV, or pseudo_native_environment()
itself); PSEUDO_LOCALSTATEDIR/PSEUDO_INCLUDE_PATHS are always overridden
here to point at rootfs_dir's own pseudo state, regardless of what's
already set in environment.
"""
if not os.path.exists(rootfs_tarball):
raise PseudoRootfsError("sdk tarball '%s' does not exist" % rootfs_tarball)
rootfs_tarball = os.path.realpath(rootfs_tarball)
rootfs_dir = os.path.realpath(rootfs_dir)
tar_options = _tar_options(rootfs_tarball)
state_dir = pseudo_state_dir(rootfs_dir)
debug_image = '-dbg' in os.path.basename(rootfs_tarball)
if os.path.exists(state_dir) and not debug_image:
raise PseudoRootfsError(
'%s already exists!\n'
'Please delete the rootfs tree and pseudo directory manually\n'
'if this is really what you want.' % state_dir)
os.makedirs(rootfs_dir, exist_ok=True)
os.makedirs(state_dir, exist_ok=True)
Path(state_dir, 'pseudo.pid').touch()
environment = dict(environment)
environment['PSEUDO_LOCALSTATEDIR'] = state_dir
environment['PSEUDO_INCLUDE_PATHS'] = rootfs_dir
command = list(pseudo_cmd) + ['tar', '-C', rootfs_dir] + tar_options + [rootfs_tarball]
print('Extracting rootfs tarball using pseudo...')
print(' '.join(command))
try:
subprocess.run(command, env=environment, check=True)
except subprocess.CalledProcessError as exc:
raise PseudoRootfsError('Failed to extract rootfs tarball') from exc
if len(os.listdir(rootfs_dir)) < 4:
print("Warning: I don't see many files in %s" % rootfs_dir)
print('Please double-check the extraction worked as intended')
else:
print('SDK image successfully extracted to %s' % rootfs_dir)
return rootfs_dir
|