blob: b0660411eaf55c8b82184546c0a25f58de056f54 [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
Patrick Williamsc124f4f2015-09-15 14:41:29 -05005from abc import ABCMeta, abstractmethod
6import os
7import glob
8import subprocess
9import shutil
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010import re
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011import collections
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012import bb
13import tempfile
14import oe.utils
Patrick Williamsc0f7c042017-02-23 20:41:17 -060015import oe.path
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050016import string
17from oe.gpg_sign import get_signer
Brad Bishop316dfdd2018-06-25 12:45:53 -040018import hashlib
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080019import fnmatch
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020
21# this can be used by all PM backends to create the index files in parallel
22def create_index(arg):
23 index_cmd = arg
24
Brad Bishopd7bf8c12018-02-25 22:55:05 -050025 bb.note("Executing '%s' ..." % index_cmd)
26 result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050027 if result:
28 bb.note(result)
29
Patrick Williamsc0f7c042017-02-23 20:41:17 -060030def opkg_query(cmd_output):
Brad Bishop316dfdd2018-06-25 12:45:53 -040031 """
32 This method parse the output from the package managerand return
33 a dictionary with the information of the packages. This is used
34 when the packages are in deb or ipk format.
35 """
Brad Bishop19323692019-04-05 15:28:33 -040036 verregex = re.compile(r' \([=<>]* [^ )]*\)')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060037 output = dict()
38 pkg = ""
39 arch = ""
40 ver = ""
41 filename = ""
42 dep = []
Andrew Geissler82c905d2020-04-13 13:39:40 -050043 prov = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -060044 pkgarch = ""
Andrew Geissler82c905d2020-04-13 13:39:40 -050045 for line in cmd_output.splitlines()+['']:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046 line = line.rstrip()
47 if ':' in line:
48 if line.startswith("Package: "):
49 pkg = line.split(": ")[1]
50 elif line.startswith("Architecture: "):
51 arch = line.split(": ")[1]
52 elif line.startswith("Version: "):
53 ver = line.split(": ")[1]
54 elif line.startswith("File: ") or line.startswith("Filename:"):
55 filename = line.split(": ")[1]
56 if "/" in filename:
57 filename = os.path.basename(filename)
58 elif line.startswith("Depends: "):
59 depends = verregex.sub('', line.split(": ")[1])
60 for depend in depends.split(", "):
61 dep.append(depend)
62 elif line.startswith("Recommends: "):
63 recommends = verregex.sub('', line.split(": ")[1])
64 for recommend in recommends.split(", "):
65 dep.append("%s [REC]" % recommend)
66 elif line.startswith("PackageArch: "):
67 pkgarch = line.split(": ")[1]
Andrew Geissler82c905d2020-04-13 13:39:40 -050068 elif line.startswith("Provides: "):
69 provides = verregex.sub('', line.split(": ")[1])
70 for provide in provides.split(", "):
71 prov.append(provide)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050072
Patrick Williamsc0f7c042017-02-23 20:41:17 -060073 # When there is a blank line save the package information
74 elif not line:
75 # IPK doesn't include the filename
76 if not filename:
77 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
78 if pkg:
79 output[pkg] = {"arch":arch, "ver":ver,
Andrew Geissler82c905d2020-04-13 13:39:40 -050080 "filename":filename, "deps": dep, "pkgarch":pkgarch, "provs": prov}
Patrick Williamsc0f7c042017-02-23 20:41:17 -060081 pkg = ""
82 arch = ""
83 ver = ""
84 filename = ""
85 dep = []
Andrew Geissler82c905d2020-04-13 13:39:40 -050086 prov = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -060087 pkgarch = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -050088
Patrick Williamsc0f7c042017-02-23 20:41:17 -060089 return output
90
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080091def failed_postinsts_abort(pkgs, log_path):
92 bb.fatal("""Postinstall scriptlets of %s have failed. If the intention is to defer them to first boot,
93then please place them into pkg_postinst_ontarget_${PN} ().
94Deferring to first boot via 'exit 1' is no longer supported.
Brad Bishop316dfdd2018-06-25 12:45:53 -040095Details of the failure are in %s.""" %(pkgs, log_path))
Patrick Williamsc0f7c042017-02-23 20:41:17 -060096
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080097def generate_locale_archive(d, rootfs, target_arch, localedir):
98 # Pretty sure we don't need this for locale archive generation but
99 # keeping it to be safe...
100 locale_arch_options = { \
Brad Bishop19323692019-04-05 15:28:33 -0400101 "arc": ["--uint32-align=4", "--little-endian"],
102 "arceb": ["--uint32-align=4", "--big-endian"],
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800103 "arm": ["--uint32-align=4", "--little-endian"],
104 "armeb": ["--uint32-align=4", "--big-endian"],
105 "aarch64": ["--uint32-align=4", "--little-endian"],
106 "aarch64_be": ["--uint32-align=4", "--big-endian"],
107 "sh4": ["--uint32-align=4", "--big-endian"],
108 "powerpc": ["--uint32-align=4", "--big-endian"],
109 "powerpc64": ["--uint32-align=4", "--big-endian"],
Andrew Geissler82c905d2020-04-13 13:39:40 -0500110 "powerpc64le": ["--uint32-align=4", "--little-endian"],
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800111 "mips": ["--uint32-align=4", "--big-endian"],
112 "mipsisa32r6": ["--uint32-align=4", "--big-endian"],
113 "mips64": ["--uint32-align=4", "--big-endian"],
114 "mipsisa64r6": ["--uint32-align=4", "--big-endian"],
115 "mipsel": ["--uint32-align=4", "--little-endian"],
116 "mipsisa32r6el": ["--uint32-align=4", "--little-endian"],
117 "mips64el": ["--uint32-align=4", "--little-endian"],
118 "mipsisa64r6el": ["--uint32-align=4", "--little-endian"],
119 "riscv64": ["--uint32-align=4", "--little-endian"],
120 "riscv32": ["--uint32-align=4", "--little-endian"],
121 "i586": ["--uint32-align=4", "--little-endian"],
122 "i686": ["--uint32-align=4", "--little-endian"],
123 "x86_64": ["--uint32-align=4", "--little-endian"]
124 }
125 if target_arch in locale_arch_options:
126 arch_options = locale_arch_options[target_arch]
127 else:
128 bb.error("locale_arch_options not found for target_arch=" + target_arch)
129 bb.fatal("unknown arch:" + target_arch + " for locale_arch_options")
130
131 # Need to set this so cross-localedef knows where the archive is
132 env = dict(os.environ)
133 env["LOCALEARCHIVE"] = oe.path.join(localedir, "locale-archive")
134
Andrew Geissler82c905d2020-04-13 13:39:40 -0500135 for name in sorted(os.listdir(localedir)):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800136 path = os.path.join(localedir, name)
137 if os.path.isdir(path):
138 cmd = ["cross-localedef", "--verbose"]
139 cmd += arch_options
140 cmd += ["--add-to-archive", path]
141 subprocess.check_output(cmd, env=env, stderr=subprocess.STDOUT)
142
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600143class Indexer(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500144 def __init__(self, d, deploy_dir):
145 self.d = d
146 self.deploy_dir = deploy_dir
147
148 @abstractmethod
149 def write_index(self):
150 pass
151
152
153class RpmIndexer(Indexer):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500154 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400155 self.do_write_index(self.deploy_dir)
156
157 def do_write_index(self, deploy_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500158 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500159 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
160 else:
161 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500163 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400164 result = create_index("%s --update -q %s" % (createrepo_c, deploy_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500165 if result:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500166 bb.fatal(result)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500167
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500168 # Sign repomd
169 if signer:
170 sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
171 is_ascii_sig = (sig_type.upper() != "BIN")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400172 signer.detach_sign(os.path.join(deploy_dir, 'repodata', 'repomd.xml'),
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500173 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
174 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
175 armor=is_ascii_sig)
176
Brad Bishop316dfdd2018-06-25 12:45:53 -0400177class RpmSubdirIndexer(RpmIndexer):
178 def write_index(self):
179 bb.note("Generating package index for %s" %(self.deploy_dir))
180 self.do_write_index(self.deploy_dir)
181 for entry in os.walk(self.deploy_dir):
182 if os.path.samefile(self.deploy_dir, entry[0]):
183 for dir in entry[1]:
184 if dir != 'repodata':
185 dir_path = oe.path.join(self.deploy_dir, dir)
186 bb.note("Generating package index for %s" %(dir_path))
187 self.do_write_index(dir_path)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500188
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500189class OpkgIndexer(Indexer):
190 def write_index(self):
191 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
192 "SDK_PACKAGE_ARCHS",
Brad Bishop316dfdd2018-06-25 12:45:53 -0400193 ]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500194
195 opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
197 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500198 else:
199 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500200
201 if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
202 open(os.path.join(self.deploy_dir, "Packages"), "w").close()
203
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500204 index_cmds = set()
205 index_sign_files = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500206 for arch_var in arch_vars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500207 archs = self.d.getVar(arch_var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500208 if archs is None:
209 continue
210
211 for arch in archs.split():
212 pkgs_dir = os.path.join(self.deploy_dir, arch)
213 pkgs_file = os.path.join(pkgs_dir, "Packages")
214
215 if not os.path.isdir(pkgs_dir):
216 continue
217
218 if not os.path.exists(pkgs_file):
219 open(pkgs_file, "w").close()
220
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500221 index_cmds.add('%s --checksum md5 --checksum sha256 -r %s -p %s -m %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500222 (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
223
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500224 index_sign_files.add(pkgs_file)
225
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500226 if len(index_cmds) == 0:
227 bb.note("There are no packages in %s!" % self.deploy_dir)
228 return
229
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800230 oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500232 if signer:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500233 feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500234 is_ascii_sig = (feed_sig_type.upper() != "BIN")
235 for f in index_sign_files:
236 signer.detach_sign(f,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500237 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
238 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500239 armor=is_ascii_sig)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500240
241
242class DpkgIndexer(Indexer):
243 def _create_configs(self):
244 bb.utils.mkdirhier(self.apt_conf_dir)
245 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
246 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
247 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
248
249 with open(os.path.join(self.apt_conf_dir, "preferences"),
250 "w") as prefs_file:
251 pass
252 with open(os.path.join(self.apt_conf_dir, "sources.list"),
253 "w+") as sources_file:
254 pass
255
256 with open(self.apt_conf_file, "w") as apt_conf:
257 with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
258 "apt", "apt.conf.sample")) as apt_conf_sample:
259 for line in apt_conf_sample.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -0400260 line = re.sub(r"#ROOTFS#", "/dev/null", line)
261 line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262 apt_conf.write(line + "\n")
263
264 def write_index(self):
265 self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
266 "apt-ftparchive")
267 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
268 self._create_configs()
269
270 os.environ['APT_CONFIG'] = self.apt_conf_file
271
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500272 pkg_archs = self.d.getVar('PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273 if pkg_archs is not None:
274 arch_list = pkg_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500275 sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276 if sdk_pkg_archs is not None:
277 for a in sdk_pkg_archs.split():
278 if a not in pkg_archs:
279 arch_list.append(a)
280
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500281 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
283
284 apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
285 gzip = bb.utils.which(os.getenv('PATH'), "gzip")
286
287 index_cmds = []
288 deb_dirs_found = False
289 for arch in arch_list:
290 arch_dir = os.path.join(self.deploy_dir, arch)
291 if not os.path.isdir(arch_dir):
292 continue
293
294 cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
295
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500296 cmd += "%s -fcn Packages > Packages.gz;" % gzip
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500297
298 with open(os.path.join(arch_dir, "Release"), "w+") as release:
299 release.write("Label: %s\n" % arch)
300
301 cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
Brad Bishop64c979e2019-11-04 13:55:29 -0500302
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303 index_cmds.append(cmd)
304
305 deb_dirs_found = True
306
307 if not deb_dirs_found:
308 bb.note("There are no packages in %s" % self.deploy_dir)
309 return
310
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800311 oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500312 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500313 raise NotImplementedError('Package feed signing not implementd for dpkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314
315
316
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600317class PkgsList(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318 def __init__(self, d, rootfs_dir):
319 self.d = d
320 self.rootfs_dir = rootfs_dir
321
322 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500323 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324 pass
325
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326class RpmPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500327 def list_pkgs(self):
Brad Bishop19323692019-04-05 15:28:33 -0400328 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR'), needfeed=False).list_installed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500329
330class OpkgPkgsList(PkgsList):
331 def __init__(self, d, rootfs_dir, config_file):
332 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
333
334 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
335 self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500336 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500337
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500338 def list_pkgs(self, format=None):
339 cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500341 # opkg returns success even when it printed some
342 # "Collected errors:" report to stderr. Mixing stderr into
343 # stdout then leads to random failures later on when
344 # parsing the output. To avoid this we need to collect both
345 # output streams separately and check for empty stderr.
346 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
347 cmd_output, cmd_stderr = p.communicate()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600348 cmd_output = cmd_output.decode("utf-8")
349 cmd_stderr = cmd_stderr.decode("utf-8")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500350 if p.returncode or cmd_stderr:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500352 "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500353
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600354 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355
356
357class DpkgPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500358
359 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500360 cmd = [bb.utils.which(os.getenv('PATH'), "dpkg-query"),
361 "--admindir=%s/var/lib/dpkg" % self.rootfs_dir,
362 "-W"]
363
Andrew Geissler82c905d2020-04-13 13:39:40 -0500364 cmd.append("-f=Package: ${Package}\nArchitecture: ${PackageArch}\nVersion: ${Version}\nFile: ${Package}_${Version}_${Architecture}.deb\nDepends: ${Depends}\nRecommends: ${Recommends}\nProvides: ${Provides}\n\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500365
366 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500368 except subprocess.CalledProcessError as e:
369 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600370 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600372 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500373
374
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600375class PackageManager(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376 """
377 This is an abstract class. Do not instantiate this directly.
378 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500379
Brad Bishop316dfdd2018-06-25 12:45:53 -0400380 def __init__(self, d, target_rootfs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500381 self.d = d
Brad Bishop316dfdd2018-06-25 12:45:53 -0400382 self.target_rootfs = target_rootfs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500383 self.deploy_dir = None
384 self.deploy_lock = None
Brad Bishop316dfdd2018-06-25 12:45:53 -0400385 self._initialize_intercepts()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500386
Brad Bishop316dfdd2018-06-25 12:45:53 -0400387 def _initialize_intercepts(self):
388 bb.note("Initializing intercept dir for %s" % self.target_rootfs)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400389 # As there might be more than one instance of PackageManager operating at the same time
390 # we need to isolate the intercept_scripts directories from each other,
391 # hence the ugly hash digest in dir name.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800392 self.intercepts_dir = os.path.join(self.d.getVar('WORKDIR'), "intercept_scripts-%s" %
393 (hashlib.sha256(self.target_rootfs.encode()).hexdigest()))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400394
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800395 postinst_intercepts = (self.d.getVar("POSTINST_INTERCEPTS") or "").split()
396 if not postinst_intercepts:
397 postinst_intercepts_path = self.d.getVar("POSTINST_INTERCEPTS_PATH")
398 if not postinst_intercepts_path:
399 postinst_intercepts_path = self.d.getVar("POSTINST_INTERCEPTS_DIR") or self.d.expand("${COREBASE}/scripts/postinst-intercepts")
400 postinst_intercepts = oe.path.which_wild('*', postinst_intercepts_path)
401
402 bb.debug(1, 'Collected intercepts:\n%s' % ''.join(' %s\n' % i for i in postinst_intercepts))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400403 bb.utils.remove(self.intercepts_dir, True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800404 bb.utils.mkdirhier(self.intercepts_dir)
405 for intercept in postinst_intercepts:
406 bb.utils.copyfile(intercept, os.path.join(self.intercepts_dir, os.path.basename(intercept)))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400407
408 @abstractmethod
409 def _handle_intercept_failure(self, failed_script):
410 pass
411
412 def _postpone_to_first_boot(self, postinst_intercept_hook):
413 with open(postinst_intercept_hook) as intercept:
414 registered_pkgs = None
415 for line in intercept.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -0400416 m = re.match(r"^##PKGS:(.*)", line)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400417 if m is not None:
418 registered_pkgs = m.group(1).strip()
419 break
420
421 if registered_pkgs is not None:
422 bb.note("If an image is being built, the postinstalls for the following packages "
423 "will be postponed for first boot: %s" %
424 registered_pkgs)
425
426 # call the backend dependent handler
427 self._handle_intercept_failure(registered_pkgs)
428
429
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800430 def run_intercepts(self, populate_sdk=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400431 intercepts_dir = self.intercepts_dir
432
433 bb.note("Running intercept scripts:")
434 os.environ['D'] = self.target_rootfs
435 os.environ['STAGING_DIR_NATIVE'] = self.d.getVar('STAGING_DIR_NATIVE')
436 for script in os.listdir(intercepts_dir):
437 script_full = os.path.join(intercepts_dir, script)
438
439 if script == "postinst_intercept" or not os.access(script_full, os.X_OK):
440 continue
441
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800442 # we do not want to run any multilib variant of this
443 if script.startswith("delay_to_first_boot"):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400444 self._postpone_to_first_boot(script_full)
445 continue
446
Brad Bishop19323692019-04-05 15:28:33 -0400447 if populate_sdk == 'host' and self.d.getVar('SDK_OS') == 'mingw32':
448 bb.note("The postinstall intercept hook '%s' could not be executed due to missing wine support, details in %s/log.do_%s"
449 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
450 continue
451
Brad Bishop316dfdd2018-06-25 12:45:53 -0400452 bb.note("> Executing %s intercept ..." % script)
453
454 try:
455 output = subprocess.check_output(script_full, stderr=subprocess.STDOUT)
456 if output: bb.note(output.decode("utf-8"))
457 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400458 bb.note("Exit code %d. Output:\n%s" % (e.returncode, e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800459 if populate_sdk == 'host':
Brad Bishop19323692019-04-05 15:28:33 -0400460 bb.fatal("The postinstall intercept hook '%s' failed, details in %s/log.do_%s" % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800461 elif populate_sdk == 'target':
462 if "qemuwrapper: qemu usermode is not supported" in e.output.decode("utf-8"):
Brad Bishop19323692019-04-05 15:28:33 -0400463 bb.note("The postinstall intercept hook '%s' could not be executed due to missing qemu usermode support, details in %s/log.do_%s"
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800464 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
465 else:
466 bb.fatal("The postinstall intercept hook '%s' failed, details in %s/log.do_%s" % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
467 else:
468 if "qemuwrapper: qemu usermode is not supported" in e.output.decode("utf-8"):
469 bb.note("The postinstall intercept hook '%s' could not be executed due to missing qemu usermode support, details in %s/log.do_%s"
470 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
471 self._postpone_to_first_boot(script_full)
472 else:
473 bb.fatal("The postinstall intercept hook '%s' failed, details in %s/log.do_%s" % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400474
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475 @abstractmethod
476 def update(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400477 """
478 Update the package manager package database.
479 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500480 pass
481
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 @abstractmethod
483 def install(self, pkgs, attempt_only=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400484 """
485 Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
486 True, installation failures are ignored.
487 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 pass
489
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500490 @abstractmethod
491 def remove(self, pkgs, with_dependencies=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400492 """
493 Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
494 is False, then any dependencies are left in place.
495 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500496 pass
497
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498 @abstractmethod
499 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400500 """
501 This function creates the index files
502 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500503 pass
504
505 @abstractmethod
506 def remove_packaging_data(self):
507 pass
508
509 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500510 def list_installed(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500511 pass
512
513 @abstractmethod
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500514 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400515 """
516 Returns the path to a tmpdir where resides the contents of a package.
517 Deleting the tmpdir is responsability of the caller.
518 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500519 pass
520
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500521 @abstractmethod
522 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400523 """
524 Add remote package feeds into repository manager configuration. The parameters
525 for the feeds are set by feed_uris, feed_base_paths and feed_archs.
526 See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS
527 for their description.
528 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500529 pass
530
Brad Bishop00111322018-04-01 22:23:53 -0400531 def install_glob(self, globs, sdk=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400532 """
533 Install all packages that match a glob.
534 """
Brad Bishop00111322018-04-01 22:23:53 -0400535 # TODO don't have sdk here but have a property on the superclass
536 # (and respect in install_complementary)
537 if sdk:
538 pkgdatadir = self.d.expand("${TMPDIR}/pkgdata/${SDK_SYS}")
539 else:
540 pkgdatadir = self.d.getVar("PKGDATA_DIR")
541
542 try:
543 bb.note("Installing globbed packages...")
544 cmd = ["oe-pkgdata-util", "-p", pkgdatadir, "list-pkgs", globs]
545 pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
546 self.install(pkgs.split(), attempt_only=True)
547 except subprocess.CalledProcessError as e:
548 # Return code 1 means no packages matched
549 if e.returncode != 1:
550 bb.fatal("Could not compute globbed packages list. Command "
551 "'%s' returned %d:\n%s" %
552 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
553
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554 def install_complementary(self, globs=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400555 """
556 Install complementary packages based upon the list of currently installed
557 packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
558 these packages, if they don't exist then no error will occur. Note: every
559 backend needs to call this function explicitly after the normal package
560 installation
561 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500562 if globs is None:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500563 globs = self.d.getVar('IMAGE_INSTALL_COMPLEMENTARY')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 split_linguas = set()
565
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500566 for translation in self.d.getVar('IMAGE_LINGUAS').split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567 split_linguas.add(translation)
568 split_linguas.add(translation.split('-')[0])
569
570 split_linguas = sorted(split_linguas)
571
572 for lang in split_linguas:
573 globs += " *-locale-%s" % lang
Brad Bishop64c979e2019-11-04 13:55:29 -0500574 for complementary_linguas in (self.d.getVar('IMAGE_LINGUAS_COMPLEMENTARY') or "").split():
575 globs += (" " + complementary_linguas) % lang
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576
577 if globs is None:
578 return
579
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500580 # we need to write the list of installed packages to a file because the
581 # oe-pkgdata-util reads it from a file
582 with tempfile.NamedTemporaryFile(mode="w+", prefix="installed-pkgs") as installed_pkgs:
583 pkgs = self.list_installed()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500584
585 provided_pkgs = set()
586 for pkg in pkgs.values():
587 provided_pkgs |= set(pkg.get('provs', []))
588
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500589 output = oe.utils.format_pkg_list(pkgs, "arch")
590 installed_pkgs.write(output)
591 installed_pkgs.flush()
592
Brad Bishop00111322018-04-01 22:23:53 -0400593 cmd = ["oe-pkgdata-util",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500594 "-p", self.d.getVar('PKGDATA_DIR'), "glob", installed_pkgs.name,
595 globs]
596 exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY')
597 if exclude:
598 cmd.extend(['--exclude=' + '|'.join(exclude.split())])
599 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500600 bb.note('Running %s' % cmd)
601 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
Andrew Geissler82c905d2020-04-13 13:39:40 -0500602 complementary_pkgs = set(complementary_pkgs.split())
603 skip_pkgs = sorted(complementary_pkgs & provided_pkgs)
604 install_pkgs = sorted(complementary_pkgs - provided_pkgs)
605 bb.note("Installing complementary packages ... %s (skipped already provided packages %s)" % (
606 ' '.join(install_pkgs),
607 ' '.join(skip_pkgs)))
608 self.install(install_pkgs, attempt_only=True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500609 except subprocess.CalledProcessError as e:
610 bb.fatal("Could not compute complementary packages list. Command "
611 "'%s' returned %d:\n%s" %
612 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500613
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800614 target_arch = self.d.getVar('TARGET_ARCH')
615 localedir = oe.path.join(self.target_rootfs, self.d.getVar("libdir"), "locale")
616 if os.path.exists(localedir) and os.listdir(localedir):
617 generate_locale_archive(self.d, self.target_rootfs, target_arch, localedir)
618 # And now delete the binary locales
619 self.remove(fnmatch.filter(self.list_installed(), "glibc-binary-localedata-*"), False)
620
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621 def deploy_dir_lock(self):
622 if self.deploy_dir is None:
623 raise RuntimeError("deploy_dir is not set!")
624
625 lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
626
627 self.deploy_lock = bb.utils.lockfile(lock_file_name)
628
629 def deploy_dir_unlock(self):
630 if self.deploy_lock is None:
631 return
632
633 bb.utils.unlockfile(self.deploy_lock)
634
635 self.deploy_lock = None
636
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500637 def construct_uris(self, uris, base_paths):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400638 """
639 Construct URIs based on the following pattern: uri/base_path where 'uri'
640 and 'base_path' correspond to each element of the corresponding array
641 argument leading to len(uris) x len(base_paths) elements on the returned
642 array
643 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500644 def _append(arr1, arr2, sep='/'):
645 res = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600646 narr1 = [a.rstrip(sep) for a in arr1]
647 narr2 = [a.rstrip(sep).lstrip(sep) for a in arr2]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500648 for a1 in narr1:
649 if arr2:
650 for a2 in narr2:
651 res.append("%s%s%s" % (a1, sep, a2))
652 else:
653 res.append(a1)
654 return res
655 return _append(uris, base_paths)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800657def create_packages_dir(d, subrepo_dir, deploydir, taskname, filterbydependencies):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400658 """
659 Go through our do_package_write_X dependencies and hardlink the packages we depend
660 upon into the repo directory. This prevents us seeing other packages that may
661 have been built that we don't depend upon and also packages for architectures we don't
662 support.
663 """
664 import errno
665
666 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
667 mytaskname = d.getVar("BB_RUNTASK")
668 pn = d.getVar("PN")
669 seendirs = set()
670 multilibs = {}
Brad Bishop64c979e2019-11-04 13:55:29 -0500671
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800672 bb.utils.remove(subrepo_dir, recurse=True)
673 bb.utils.mkdirhier(subrepo_dir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400674
675 # Detect bitbake -b usage
676 nodeps = d.getVar("BB_LIMITEDDEPS") or False
677 if nodeps or not filterbydependencies:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800678 oe.path.symlink(deploydir, subrepo_dir, True)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400679 return
680
681 start = None
682 for dep in taskdepdata:
683 data = taskdepdata[dep]
684 if data[1] == mytaskname and data[0] == pn:
685 start = dep
686 break
687 if start is None:
688 bb.fatal("Couldn't find ourself in BB_TASKDEPDATA?")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800689 pkgdeps = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400690 start = [start]
691 seen = set(start)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800692 # Support direct dependencies (do_rootfs -> do_package_write_X)
693 # or indirect dependencies within PN (do_populate_sdk_ext -> do_rootfs -> do_package_write_X)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400694 while start:
695 next = []
696 for dep2 in start:
697 for dep in taskdepdata[dep2][3]:
698 if taskdepdata[dep][0] != pn:
699 if "do_" + taskname in dep:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800700 pkgdeps.add(dep)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400701 elif dep not in seen:
702 next.append(dep)
703 seen.add(dep)
704 start = next
705
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800706 for dep in pkgdeps:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400707 c = taskdepdata[dep][0]
708 manifest, d2 = oe.sstatesig.find_sstate_manifest(c, taskdepdata[dep][2], taskname, d, multilibs)
709 if not manifest:
710 bb.fatal("No manifest generated from: %s in %s" % (c, taskdepdata[dep][2]))
711 if not os.path.exists(manifest):
712 continue
713 with open(manifest, "r") as f:
714 for l in f:
715 l = l.strip()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800716 deploydir = os.path.normpath(deploydir)
717 if bb.data.inherits_class('packagefeed-stability', d):
718 dest = l.replace(deploydir + "-prediff", "")
719 else:
720 dest = l.replace(deploydir, "")
721 dest = subrepo_dir + dest
Brad Bishop316dfdd2018-06-25 12:45:53 -0400722 if l.endswith("/"):
723 if dest not in seendirs:
724 bb.utils.mkdirhier(dest)
725 seendirs.add(dest)
726 continue
727 # Try to hardlink the file, copy if that fails
728 destdir = os.path.dirname(dest)
729 if destdir not in seendirs:
730 bb.utils.mkdirhier(destdir)
731 seendirs.add(destdir)
732 try:
733 os.link(l, dest)
734 except OSError as err:
735 if err.errno == errno.EXDEV:
736 bb.utils.copyfile(l, dest)
737 else:
738 raise
739
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740class RpmPM(PackageManager):
741 def __init__(self,
742 d,
743 target_rootfs,
744 target_vendor,
745 task_name='target',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746 arch_var=None,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500747 os_var=None,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400748 rpm_repo_workdir="oe-rootfs-repo",
Brad Bishop19323692019-04-05 15:28:33 -0400749 filterbydependencies=True,
750 needfeed=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400751 super(RpmPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752 self.target_vendor = target_vendor
753 self.task_name = task_name
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500754 if arch_var == None:
755 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
756 else:
757 self.archs = self.d.getVar(arch_var).replace("-","_")
758 if task_name == "host":
759 self.primary_arch = self.d.getVar('SDK_ARCH')
760 else:
761 self.primary_arch = self.d.getVar('MACHINE_ARCH')
762
Brad Bishop19323692019-04-05 15:28:33 -0400763 if needfeed:
764 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
765 create_packages_dir(self.d, oe.path.join(self.rpm_repo_dir, "rpm"), d.getVar("DEPLOY_DIR_RPM"), "package_write_rpm", filterbydependencies)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500766
767 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
768 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
769 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800770 self.packaging_data_dirs = ['etc/rpm', 'etc/rpmrc', 'etc/dnf', 'var/lib/rpm', 'var/lib/dnf', 'var/cache/dnf']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
772 self.task_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773 if not os.path.exists(self.d.expand('${T}/saved')):
774 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
775
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500776 def _configure_dnf(self):
777 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
778 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
779 # This prevents accidental matching against libsolv's built-in policies
780 if len(archs) <= 1:
781 archs = archs + ["bogusarch"]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500782 # This architecture needs to be upfront so that packages using it are properly prioritized
783 archs = ["sdk_provides_dummy_target"] + archs
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500784 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
785 bb.utils.mkdirhier(confdir)
786 open(confdir + "arch", 'w').write(":".join(archs))
787 distro_codename = self.d.getVar('DISTRO_CODENAME')
788 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500790 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500793 def _configure_rpm(self):
794 # We need to configure rpm to use our primary package architecture as the installation architecture,
795 # and to make it compatible with other package architectures that we use.
796 # Otherwise it will refuse to proceed with packages installation.
797 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
798 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
799 bb.utils.mkdirhier(platformconfdir)
800 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800801 with open(rpmrcconfdir + "rpmrc", 'w') as f:
802 f.write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
803 f.write("buildarch_compat: %s: noarch\n" % self.primary_arch)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500804
805 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
806 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
807 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500808 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500809 open(platformconfdir + "macros", 'a').write("%_prefer_color 7")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500810
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500811 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
812 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
813 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
814 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
815 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
816 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
817 try:
818 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
819 except subprocess.CalledProcessError as e:
820 bb.fatal("Importing GPG key failed. Command '%s' "
821 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500822
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823 def create_configs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500824 self._configure_dnf()
825 self._configure_rpm()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826
827 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500828 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
829 lf = bb.utils.lockfile(lockfilename, False)
830 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
831 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500832
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500833 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
834 from urllib.parse import urlparse
835
836 if feed_uris == "":
837 return
838
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500839 gpg_opts = ''
840 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
841 gpg_opts += 'repo_gpgcheck=1\n'
842 gpg_opts += 'gpgkey=file://%s/pki/packagefeed-gpg/PACKAGEFEED-GPG-KEY-%s-%s\n' % (self.d.getVar('sysconfdir'), self.d.getVar('DISTRO'), self.d.getVar('DISTRO_CODENAME'))
843
Brad Bishop316dfdd2018-06-25 12:45:53 -0400844 if self.d.getVar('RPM_SIGN_PACKAGES') != '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500845 gpg_opts += 'gpgcheck=0\n'
846
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500847 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
848 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
849 for uri in remote_uris:
850 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
851 if feed_archs is not None:
852 for arch in feed_archs.split():
853 repo_uri = uri + "/" + arch
854 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
855 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
856 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500857 "[%s]\nname=%s\nbaseurl=%s\n%s\n" % (repo_id, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500858 else:
859 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
860 repo_uri = uri
861 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500862 "[%s]\nname=%s\nbaseurl=%s\n%s" % (repo_base, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500863
864 def _prepare_pkg_transaction(self):
865 os.environ['D'] = self.target_rootfs
866 os.environ['OFFLINE_ROOT'] = self.target_rootfs
867 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
868 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -0400869 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500870 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
871
872
873 def install(self, pkgs, attempt_only = False):
874 if len(pkgs) == 0:
875 return
876 self._prepare_pkg_transaction()
877
878 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
879 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
880 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
881
882 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
883 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
884 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
885 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
886 ["install"] +
887 pkgs)
888
889 failed_scriptlets_pkgnames = collections.OrderedDict()
890 for line in output.splitlines():
Brad Bishop19323692019-04-05 15:28:33 -0400891 if line.startswith("Error in POSTIN scriptlet in rpm package"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500892 failed_scriptlets_pkgnames[line.split()[-1]] = True
893
Brad Bishop316dfdd2018-06-25 12:45:53 -0400894 if len(failed_scriptlets_pkgnames) > 0:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800895 failed_postinsts_abort(list(failed_scriptlets_pkgnames.keys()), self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500896
897 def remove(self, pkgs, with_dependencies = True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800898 if not pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500899 return
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800900
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500901 self._prepare_pkg_transaction()
902
903 if with_dependencies:
904 self._invoke_dnf(["remove"] + pkgs)
905 else:
906 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500907 args = ["-e", "-v", "--nodeps", "--root=%s" %self.target_rootfs]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500908
909 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500910 bb.note("Running %s" % ' '.join([cmd] + args + pkgs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500911 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500912 bb.note(output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500913 except subprocess.CalledProcessError as e:
914 bb.fatal("Could not invoke rpm. Command "
915 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
916
917 def upgrade(self):
918 self._prepare_pkg_transaction()
919 self._invoke_dnf(["upgrade"])
920
921 def autoremove(self):
922 self._prepare_pkg_transaction()
923 self._invoke_dnf(["autoremove"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500924
925 def remove_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500926 self._invoke_dnf(["clean", "all"])
927 for dir in self.packaging_data_dirs:
928 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500929
930 def backup_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500931 # Save the packaging dirs for increment rpm image generation
932 if os.path.exists(self.saved_packaging_data):
933 bb.utils.remove(self.saved_packaging_data, True)
934 for i in self.packaging_data_dirs:
935 source_dir = oe.path.join(self.target_rootfs, i)
936 target_dir = oe.path.join(self.saved_packaging_data, i)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800937 if os.path.isdir(source_dir):
938 shutil.copytree(source_dir, target_dir, symlinks=True)
939 elif os.path.isfile(source_dir):
940 shutil.copy2(source_dir, target_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500941
942 def recovery_packaging_data(self):
943 # Move the rpmlib back
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500944 if os.path.exists(self.saved_packaging_data):
945 for i in self.packaging_data_dirs:
946 target_dir = oe.path.join(self.target_rootfs, i)
947 if os.path.exists(target_dir):
948 bb.utils.remove(target_dir, True)
949 source_dir = oe.path.join(self.saved_packaging_data, i)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800950 if os.path.isdir(source_dir):
951 shutil.copytree(source_dir, target_dir, symlinks=True)
952 elif os.path.isfile(source_dir):
953 shutil.copy2(source_dir, target_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500955 def list_installed(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500956 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
957 print_output = False)
958 packages = {}
959 current_package = None
960 current_deps = None
961 current_state = "initial"
962 for line in output.splitlines():
963 if line.startswith("Package:"):
964 package_info = line.split(" ")[1:]
965 current_package = package_info[0]
966 package_arch = package_info[1]
967 package_version = package_info[2]
968 package_rpm = package_info[3]
969 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
970 current_deps = []
971 elif line.startswith("Dependencies:"):
972 current_state = "dependencies"
973 elif line.startswith("Recommendations"):
974 current_state = "recommendations"
975 elif line.startswith("DependenciesEndHere:"):
976 current_state = "initial"
977 packages[current_package]["deps"] = current_deps
978 elif len(line) > 0:
979 if current_state == "dependencies":
980 current_deps.append(line)
981 elif current_state == "recommendations":
982 current_deps.append("%s [REC]" % line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500983
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500984 return packages
985
986 def update(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500987 self._invoke_dnf(["makecache", "--refresh"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500988
989 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
990 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
991
992 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
Brad Bishop19323692019-04-05 15:28:33 -0400993 standard_dnf_args = ["-v", "--rpmverbosity=info", "-y",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500994 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
995 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500996 "--installroot=%s" % (self.target_rootfs),
997 "--setopt=logdir=%s" % (self.d.getVar('T'))
998 ]
Brad Bishop19323692019-04-05 15:28:33 -0400999 if hasattr(self, "rpm_repo_dir"):
1000 standard_dnf_args.append("--repofrompath=oe-repo,%s" % (self.rpm_repo_dir))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001001 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
Brad Bishop316dfdd2018-06-25 12:45:53 -04001002 bb.note('Running %s' % ' '.join(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001003 try:
1004 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
1005 if print_output:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001006 bb.debug(1, output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001007 return output
1008 except subprocess.CalledProcessError as e:
1009 if print_output:
1010 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
1011 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
1012 else:
1013 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
1014 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
1015 return e.output.decode("utf-8")
1016
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001017 def dump_install_solution(self, pkgs):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001018 open(self.solution_manifest, 'w').write(" ".join(pkgs))
1019 return pkgs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021 def load_old_install_solution(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001022 if not os.path.exists(self.solution_manifest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001023 return []
Brad Bishop64c979e2019-11-04 13:55:29 -05001024 with open(self.solution_manifest, 'r') as fd:
1025 return fd.read().split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001026
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001027 def _script_num_prefix(self, path):
1028 files = os.listdir(path)
1029 numbers = set()
1030 numbers.add(99)
1031 for f in files:
1032 numbers.add(int(f.split("-")[0]))
1033 return max(numbers) + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001034
1035 def save_rpmpostinst(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001036 bb.note("Saving postinstall script of %s" % (pkg))
1037 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
1038 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001039
1040 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001041 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001042 except subprocess.CalledProcessError as e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001043 bb.fatal("Could not invoke rpm. Command "
1044 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001045
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001046 # may need to prepend #!/bin/sh to output
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001047
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001048 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
1049 bb.utils.mkdirhier(target_path)
1050 num = self._script_num_prefix(target_path)
1051 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
1052 open(saved_script_name, 'w').write(output)
1053 os.chmod(saved_script_name, 0o755)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001054
Brad Bishop316dfdd2018-06-25 12:45:53 -04001055 def _handle_intercept_failure(self, registered_pkgs):
1056 rpm_postinsts_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
1057 bb.utils.mkdirhier(rpm_postinsts_dir)
1058
1059 # Save the package postinstalls in /etc/rpm-postinsts
1060 for pkg in registered_pkgs.split():
1061 self.save_rpmpostinst(pkg)
1062
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001063 def extract(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001064 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
1065 pkg_name = output.splitlines()[-1]
1066 if not pkg_name.endswith(".rpm"):
1067 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
1068 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001069
1070 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
1071 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
1072
1073 if not os.path.isfile(pkg_path):
1074 bb.fatal("Unable to extract package for '%s'."
1075 "File %s doesn't exists" % (pkg, pkg_path))
1076
1077 tmp_dir = tempfile.mkdtemp()
1078 current_dir = os.getcwd()
1079 os.chdir(tmp_dir)
1080
1081 try:
1082 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
1083 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1084 except subprocess.CalledProcessError as e:
1085 bb.utils.remove(tmp_dir, recurse=True)
1086 bb.fatal("Unable to extract %s package. Command '%s' "
1087 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
1088 except OSError as e:
1089 bb.utils.remove(tmp_dir, recurse=True)
1090 bb.fatal("Unable to extract %s package. Command '%s' "
1091 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
1092
1093 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1094 os.chdir(current_dir)
1095
1096 return tmp_dir
1097
1098
1099class OpkgDpkgPM(PackageManager):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001100 def __init__(self, d, target_rootfs):
1101 """
1102 This is an abstract class. Do not instantiate this directly.
1103 """
1104 super(OpkgDpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001105
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001106 def package_info(self, pkg, cmd):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001107 """
1108 Returns a dictionary with the package info.
1109
1110 This method extracts the common parts for Opkg and Dpkg
1111 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001112
1113 try:
1114 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
1115 except subprocess.CalledProcessError as e:
1116 bb.fatal("Unable to list available packages. Command '%s' "
1117 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
1118 return opkg_query(output)
1119
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001120 def extract(self, pkg, pkg_info):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001121 """
1122 Returns the path to a tmpdir where resides the contents of a package.
1123
1124 Deleting the tmpdir is responsability of the caller.
1125
1126 This method extracts the common parts for Opkg and Dpkg
1127 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001128
1129 ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
1130 tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
1131 pkg_path = pkg_info[pkg]["filepath"]
1132
1133 if not os.path.isfile(pkg_path):
1134 bb.fatal("Unable to extract package for '%s'."
1135 "File %s doesn't exists" % (pkg, pkg_path))
1136
1137 tmp_dir = tempfile.mkdtemp()
1138 current_dir = os.getcwd()
1139 os.chdir(tmp_dir)
Brad Bishop19323692019-04-05 15:28:33 -04001140 data_tar = 'data.tar.xz'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001141
1142 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001143 cmd = [ar_cmd, 'x', pkg_path]
1144 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
1145 cmd = [tar_cmd, 'xf', data_tar]
1146 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001147 except subprocess.CalledProcessError as e:
1148 bb.utils.remove(tmp_dir, recurse=True)
1149 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001150 "returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001151 except OSError as e:
1152 bb.utils.remove(tmp_dir, recurse=True)
1153 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001154 "returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001155
1156 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1157 bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
1158 bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
1159 os.chdir(current_dir)
1160
1161 return tmp_dir
1162
Brad Bishop316dfdd2018-06-25 12:45:53 -04001163 def _handle_intercept_failure(self, registered_pkgs):
1164 self.mark_packages("unpacked", registered_pkgs.split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001165
1166class OpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001167 def __init__(self, d, target_rootfs, config_file, archs, task_name='target', ipk_repo_workdir="oe-rootfs-repo", filterbydependencies=True, prepare_index=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001168 super(OpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001169
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001170 self.config_file = config_file
1171 self.pkg_archs = archs
1172 self.task_name = task_name
1173
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001174 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), ipk_repo_workdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001175 self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
1176 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001177 self.opkg_args = "--volatile-cache -f %s -t %s -o %s " % (self.config_file, self.d.expand('${T}/ipktemp/') ,target_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001178 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001179
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001180 if prepare_index:
1181 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_IPK"), "package_write_ipk", filterbydependencies)
1182
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001183 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184 if opkg_lib_dir[0] == "/":
1185 opkg_lib_dir = opkg_lib_dir[1:]
1186
1187 self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
1188
1189 bb.utils.mkdirhier(self.opkg_dir)
1190
1191 self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
1192 if not os.path.exists(self.d.expand('${T}/saved')):
1193 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
1194
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001195 self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001196 if self.from_feeds:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001197 self._create_custom_config()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001198 else:
1199 self._create_config()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001200
1201 self.indexer = OpkgIndexer(self.d, self.deploy_dir)
1202
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001203 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001204 """
1205 This function will change a package's status in /var/lib/opkg/status file.
1206 If 'packages' is None then the new_status will be applied to all
1207 packages
1208 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001209 status_file = os.path.join(self.opkg_dir, "status")
1210
1211 with open(status_file, "r") as sf:
1212 with open(status_file + ".tmp", "w+") as tmp_sf:
1213 if packages is None:
1214 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1215 r"Package: \1\n\2Status: \3%s" % status_tag,
1216 sf.read()))
1217 else:
1218 if type(packages).__name__ != "list":
1219 raise TypeError("'packages' should be a list object")
1220
1221 status = sf.read()
1222 for pkg in packages:
1223 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1224 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1225 status)
1226
1227 tmp_sf.write(status)
1228
1229 os.rename(status_file + ".tmp", status_file)
1230
1231 def _create_custom_config(self):
1232 bb.note("Building from feeds activated!")
1233
1234 with open(self.config_file, "w+") as config_file:
1235 priority = 1
1236 for arch in self.pkg_archs.split():
1237 config_file.write("arch %s %d\n" % (arch, priority))
1238 priority += 5
1239
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001240 for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
Brad Bishop19323692019-04-05 15:28:33 -04001241 feed_match = re.match(r"^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242
1243 if feed_match is not None:
1244 feed_name = feed_match.group(1)
1245 feed_uri = feed_match.group(2)
1246
1247 bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
1248
1249 config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
1250
1251 """
1252 Allow to use package deploy directory contents as quick devel-testing
1253 feed. This creates individual feed configs for each arch subdir of those
1254 specified as compatible for the current machine.
1255 NOTE: Development-helper feature, NOT a full-fledged feed.
1256 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001257 if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001258 for arch in self.pkg_archs.split():
1259 cfg_file_name = os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001260 self.d.getVar("sysconfdir"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001261 "opkg",
1262 "local-%s-feed.conf" % arch)
1263
1264 with open(cfg_file_name, "w+") as cfg_file:
1265 cfg_file.write("src/gz local-%s %s/%s" %
1266 (arch,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001267 self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001268 arch))
1269
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001270 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001271 # There is no command line option for this anymore, we need to add
1272 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1273 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001274 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1275 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1276 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001277 cfg_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1278 cfg_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1279 cfg_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001280
1281
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001282 def _create_config(self):
1283 with open(self.config_file, "w+") as config_file:
1284 priority = 1
1285 for arch in self.pkg_archs.split():
1286 config_file.write("arch %s %d\n" % (arch, priority))
1287 priority += 5
1288
1289 config_file.write("src oe file:%s\n" % self.deploy_dir)
1290
1291 for arch in self.pkg_archs.split():
1292 pkgs_dir = os.path.join(self.deploy_dir, arch)
1293 if os.path.isdir(pkgs_dir):
1294 config_file.write("src oe-%s file:%s\n" %
1295 (arch, pkgs_dir))
1296
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001297 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001298 # There is no command line option for this anymore, we need to add
1299 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1300 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001301 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1302 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1303 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001304 config_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1305 config_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1306 config_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001307
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001308 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1309 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001310 return
1311
1312 rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
1313 % self.target_rootfs)
1314
Brad Bishop96ff1982019-08-19 13:50:42 -04001315 os.makedirs('%s/etc/opkg' % self.target_rootfs, exist_ok=True)
1316
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001317 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
1318 archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001319
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320 with open(rootfs_config, "w+") as config_file:
1321 uri_iterator = 0
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001322 for uri in feed_uris:
1323 if archs:
1324 for arch in archs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001325 if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001326 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001327 bb.note('Adding opkg feed url-%s-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001328 (arch, uri_iterator, uri))
1329 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1330 (arch, uri_iterator, uri, arch))
1331 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001332 bb.note('Adding opkg feed url-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001333 (uri_iterator, uri))
1334 config_file.write("src/gz uri-%d %s\n" %
1335 (uri_iterator, uri))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001336
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001337 uri_iterator += 1
1338
1339 def update(self):
1340 self.deploy_dir_lock()
1341
1342 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1343
1344 try:
1345 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1346 except subprocess.CalledProcessError as e:
1347 self.deploy_dir_unlock()
1348 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001349 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350
1351 self.deploy_dir_unlock()
1352
1353 def install(self, pkgs, attempt_only=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001354 if not pkgs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355 return
1356
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001357 cmd = "%s %s" % (self.opkg_cmd, self.opkg_args)
1358 for exclude in (self.d.getVar("PACKAGE_EXCLUDE") or "").split():
1359 cmd += " --add-exclude %s" % exclude
Brad Bishop19323692019-04-05 15:28:33 -04001360 for bad_recommendation in (self.d.getVar("BAD_RECOMMENDATIONS") or "").split():
1361 cmd += " --add-ignore-recommends %s" % bad_recommendation
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001362 cmd += " install "
1363 cmd += " ".join(pkgs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364
1365 os.environ['D'] = self.target_rootfs
1366 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1367 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1368 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001369 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001370 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001371
1372 try:
1373 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1374 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001375 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001376 bb.note(output)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001377 failed_pkgs = []
1378 for line in output.split('\n'):
1379 if line.endswith("configuration required on target."):
1380 bb.warn(line)
1381 failed_pkgs.append(line.split(".")[0])
1382 if failed_pkgs:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001383 failed_postinsts_abort(failed_pkgs, self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001384 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001385 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001386 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001387 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001388
1389 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001390 if not pkgs:
1391 return
1392
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001393 if with_dependencies:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001394 cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001395 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1396 else:
1397 cmd = "%s %s --force-depends remove %s" % \
1398 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1399
1400 try:
1401 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001402 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001403 bb.note(output)
1404 except subprocess.CalledProcessError as e:
1405 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001406 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001407
1408 def write_index(self):
1409 self.deploy_dir_lock()
1410
1411 result = self.indexer.write_index()
1412
1413 self.deploy_dir_unlock()
1414
1415 if result is not None:
1416 bb.fatal(result)
1417
1418 def remove_packaging_data(self):
1419 bb.utils.remove(self.opkg_dir, True)
1420 # create the directory back, it's needed by PM lock
1421 bb.utils.mkdirhier(self.opkg_dir)
1422
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001423 def remove_lists(self):
1424 if not self.from_feeds:
1425 bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
1426
1427 def list_installed(self):
1428 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001429
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001430 def dummy_install(self, pkgs):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001431 """
1432 The following function dummy installs pkgs and returns the log of output.
1433 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001434 if len(pkgs) == 0:
1435 return
1436
1437 # Create an temp dir as opkg root for dummy installation
1438 temp_rootfs = self.d.expand('${T}/opkg')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001439 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001440 if opkg_lib_dir[0] == "/":
1441 opkg_lib_dir = opkg_lib_dir[1:]
1442 temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001443 bb.utils.mkdirhier(temp_opkg_dir)
1444
1445 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001446 opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001447
1448 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1449 try:
1450 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1451 except subprocess.CalledProcessError as e:
1452 bb.fatal("Unable to update. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001453 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001454
1455 # Dummy installation
1456 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1457 opkg_args,
1458 ' '.join(pkgs))
1459 try:
1460 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1461 except subprocess.CalledProcessError as e:
1462 bb.fatal("Unable to dummy install packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001463 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001464
1465 bb.utils.remove(temp_rootfs, True)
1466
1467 return output
1468
1469 def backup_packaging_data(self):
1470 # Save the opkglib for increment ipk image generation
1471 if os.path.exists(self.saved_opkg_dir):
1472 bb.utils.remove(self.saved_opkg_dir, True)
1473 shutil.copytree(self.opkg_dir,
1474 self.saved_opkg_dir,
1475 symlinks=True)
1476
1477 def recover_packaging_data(self):
1478 # Move the opkglib back
1479 if os.path.exists(self.saved_opkg_dir):
1480 if os.path.exists(self.opkg_dir):
1481 bb.utils.remove(self.opkg_dir, True)
1482
1483 bb.note('Recover packaging data')
1484 shutil.copytree(self.saved_opkg_dir,
1485 self.opkg_dir,
1486 symlinks=True)
1487
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001488 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001489 """
1490 Returns a dictionary with the package info.
1491 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001492 cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
1493 pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001494
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001495 pkg_arch = pkg_info[pkg]["arch"]
1496 pkg_filename = pkg_info[pkg]["filename"]
1497 pkg_info[pkg]["filepath"] = \
1498 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1499
1500 return pkg_info
1501
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001502 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001503 """
1504 Returns the path to a tmpdir where resides the contents of a package.
1505
1506 Deleting the tmpdir is responsability of the caller.
1507 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001508 pkg_info = self.package_info(pkg)
1509 if not pkg_info:
1510 bb.fatal("Unable to get information for package '%s' while "
1511 "trying to extract the package." % pkg)
1512
1513 tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
Brad Bishop19323692019-04-05 15:28:33 -04001514 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001515
1516 return tmp_dir
1517
1518class DpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001519 def __init__(self, d, target_rootfs, archs, base_archs, apt_conf_dir=None, deb_repo_workdir="oe-rootfs-repo", filterbydependencies=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001520 super(DpkgPM, self).__init__(d, target_rootfs)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001521 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), deb_repo_workdir)
1522
1523 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_DEB"), "package_write_deb", filterbydependencies)
1524
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001525 if apt_conf_dir is None:
1526 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1527 else:
1528 self.apt_conf_dir = apt_conf_dir
1529 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1530 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001531 self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001532
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001533 self.apt_args = d.getVar("APT_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534
1535 self.all_arch_list = archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001536 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001537 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1538
1539 self._create_configs(archs, base_archs)
1540
1541 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1542
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001543 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001544 """
1545 This function will change a package's status in /var/lib/dpkg/status file.
1546 If 'packages' is None then the new_status will be applied to all
1547 packages
1548 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001549 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1550
1551 with open(status_file, "r") as sf:
1552 with open(status_file + ".tmp", "w+") as tmp_sf:
1553 if packages is None:
1554 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1555 r"Package: \1\n\2Status: \3%s" % status_tag,
1556 sf.read()))
1557 else:
1558 if type(packages).__name__ != "list":
1559 raise TypeError("'packages' should be a list object")
1560
1561 status = sf.read()
1562 for pkg in packages:
1563 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1564 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1565 status)
1566
1567 tmp_sf.write(status)
1568
1569 os.rename(status_file + ".tmp", status_file)
1570
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001571 def run_pre_post_installs(self, package_name=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001572 """
1573 Run the pre/post installs for package "package_name". If package_name is
1574 None, then run all pre/post install scriptlets.
1575 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001576 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001577 ControlScript = collections.namedtuple("ControlScript", ["suffix", "name", "argument"])
1578 control_scripts = [
1579 ControlScript(".preinst", "Preinstall", "install"),
1580 ControlScript(".postinst", "Postinstall", "configure")]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001581 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1582 installed_pkgs = []
1583
1584 with open(status_file, "r") as status:
1585 for line in status.read().split('\n'):
Brad Bishop19323692019-04-05 15:28:33 -04001586 m = re.match(r"^Package: (.*)", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001587 if m is not None:
1588 installed_pkgs.append(m.group(1))
1589
1590 if package_name is not None and not package_name in installed_pkgs:
1591 return
1592
1593 os.environ['D'] = self.target_rootfs
1594 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1595 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1596 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001597 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001598 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001599
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600 for pkg_name in installed_pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001601 for control_script in control_scripts:
1602 p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001603 if os.path.exists(p_full):
1604 try:
1605 bb.note("Executing %s for package: %s ..." %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001606 (control_script.name.lower(), pkg_name))
1607 output = subprocess.check_output([p_full, control_script.argument],
1608 stderr=subprocess.STDOUT).decode("utf-8")
1609 bb.note(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001610 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001611 bb.warn("%s for package %s failed with %d:\n%s" %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001612 (control_script.name, pkg_name, e.returncode,
1613 e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001614 failed_postinsts_abort([pkg_name], self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001615
1616 def update(self):
1617 os.environ['APT_CONFIG'] = self.apt_conf_file
1618
1619 self.deploy_dir_lock()
1620
1621 cmd = "%s update" % self.apt_get_cmd
1622
1623 try:
1624 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1625 except subprocess.CalledProcessError as e:
1626 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001627 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001628
1629 self.deploy_dir_unlock()
1630
1631 def install(self, pkgs, attempt_only=False):
1632 if attempt_only and len(pkgs) == 0:
1633 return
1634
1635 os.environ['APT_CONFIG'] = self.apt_conf_file
1636
Andrew Geissler82c905d2020-04-13 13:39:40 -05001637 cmd = "%s %s install --force-yes --allow-unauthenticated --no-remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001638 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1639
1640 try:
1641 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1642 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1643 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001644 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001645 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001646 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001647
1648 # rename *.dpkg-new files/dirs
1649 for root, dirs, files in os.walk(self.target_rootfs):
1650 for dir in dirs:
Brad Bishop19323692019-04-05 15:28:33 -04001651 new_dir = re.sub(r"\.dpkg-new", "", dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001652 if dir != new_dir:
1653 os.rename(os.path.join(root, dir),
1654 os.path.join(root, new_dir))
1655
1656 for file in files:
Brad Bishop19323692019-04-05 15:28:33 -04001657 new_file = re.sub(r"\.dpkg-new", "", file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001658 if file != new_file:
1659 os.rename(os.path.join(root, file),
1660 os.path.join(root, new_file))
1661
1662
1663 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001664 if not pkgs:
1665 return
1666
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001667 if with_dependencies:
1668 os.environ['APT_CONFIG'] = self.apt_conf_file
1669 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1670 else:
1671 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1672 " -P --force-depends %s" % \
1673 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1674 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1675
1676 try:
1677 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1678 except subprocess.CalledProcessError as e:
1679 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001680 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001681
1682 def write_index(self):
1683 self.deploy_dir_lock()
1684
1685 result = self.indexer.write_index()
1686
1687 self.deploy_dir_unlock()
1688
1689 if result is not None:
1690 bb.fatal(result)
1691
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001692 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1693 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001694 return
1695
1696 sources_conf = os.path.join("%s/etc/apt/sources.list"
1697 % self.target_rootfs)
1698 arch_list = []
1699
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001700 if feed_archs is None:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001701 for arch in self.all_arch_list:
1702 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1703 continue
1704 arch_list.append(arch)
1705 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001706 arch_list = feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001707
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001708 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001709
1710 with open(sources_conf, "w+") as sources_file:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001711 for uri in feed_uris:
1712 if arch_list:
1713 for arch in arch_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001714 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001715 sources_file.write("deb %s/%s ./\n" %
1716 (uri, arch))
1717 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001718 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001719 sources_file.write("deb %s ./\n" % uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001720
1721 def _create_configs(self, archs, base_archs):
Brad Bishop19323692019-04-05 15:28:33 -04001722 base_archs = re.sub(r"_", r"-", base_archs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001723
1724 if os.path.exists(self.apt_conf_dir):
1725 bb.utils.remove(self.apt_conf_dir, True)
1726
1727 bb.utils.mkdirhier(self.apt_conf_dir)
1728 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1729 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001730 bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001731
1732 arch_list = []
1733 for arch in self.all_arch_list:
1734 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1735 continue
1736 arch_list.append(arch)
1737
1738 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1739 priority = 801
1740 for arch in arch_list:
1741 prefs_file.write(
1742 "Package: *\n"
1743 "Pin: release l=%s\n"
1744 "Pin-Priority: %d\n\n" % (arch, priority))
1745
1746 priority += 5
1747
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001748 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001749 for pkg in pkg_exclude.split():
1750 prefs_file.write(
1751 "Package: %s\n"
1752 "Pin: release *\n"
1753 "Pin-Priority: -1\n\n" % pkg)
1754
1755 arch_list.reverse()
1756
1757 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1758 for arch in arch_list:
1759 sources_file.write("deb file:%s/ ./\n" %
1760 os.path.join(self.deploy_dir, arch))
1761
1762 base_arch_list = base_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001763 multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001764 for variant in multilib_variants.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001765 localdata = bb.data.createCopy(self.d)
1766 variant_tune = localdata.getVar("DEFAULTTUNE_virtclass-multilib-" + variant, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001767 orig_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001768 localdata.setVar("DEFAULTTUNE", variant_tune)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001769 variant_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001770 if variant_arch not in base_arch_list:
1771 base_arch_list.append(variant_arch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001772
1773 with open(self.apt_conf_file, "w+") as apt_conf:
1774 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1775 for line in apt_conf_sample.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -04001776 match_arch = re.match(r" Architecture \".*\";$", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001777 architectures = ""
1778 if match_arch:
1779 for base_arch in base_arch_list:
1780 architectures += "\"%s\";" % base_arch
1781 apt_conf.write(" Architectures {%s};\n" % architectures);
1782 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1783 else:
Brad Bishop19323692019-04-05 15:28:33 -04001784 line = re.sub(r"#ROOTFS#", self.target_rootfs, line)
1785 line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001786 apt_conf.write(line + "\n")
1787
1788 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1789 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1790
1791 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1792
1793 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1794 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1795 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1796 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1797
1798 def remove_packaging_data(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001799 bb.utils.remove(self.target_rootfs + self.d.getVar('opkglibdir'), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001800 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1801
1802 def fix_broken_dependencies(self):
1803 os.environ['APT_CONFIG'] = self.apt_conf_file
1804
1805 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1806
1807 try:
1808 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1809 except subprocess.CalledProcessError as e:
1810 bb.fatal("Cannot fix broken dependencies. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001811 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001812
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001813 def list_installed(self):
1814 return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001815
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001816 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001817 """
1818 Returns a dictionary with the package info.
1819 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001820 cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
1821 pkg_info = super(DpkgPM, self).package_info(pkg, cmd)
1822
1823 pkg_arch = pkg_info[pkg]["pkgarch"]
1824 pkg_filename = pkg_info[pkg]["filename"]
1825 pkg_info[pkg]["filepath"] = \
1826 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1827
1828 return pkg_info
1829
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001830 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001831 """
1832 Returns the path to a tmpdir where resides the contents of a package.
1833
1834 Deleting the tmpdir is responsability of the caller.
1835 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001836 pkg_info = self.package_info(pkg)
1837 if not pkg_info:
1838 bb.fatal("Unable to get information for package '%s' while "
1839 "trying to extract the package." % pkg)
1840
1841 tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info)
1842 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
1843
1844 return tmp_dir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001845
1846def generate_index_files(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001847 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001848
1849 indexer_map = {
Brad Bishop316dfdd2018-06-25 12:45:53 -04001850 "rpm": (RpmSubdirIndexer, d.getVar('DEPLOY_DIR_RPM')),
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001851 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK')),
1852 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001853 }
1854
1855 result = None
1856
1857 for pkg_class in classes:
1858 if not pkg_class in indexer_map:
1859 continue
1860
1861 if os.path.exists(indexer_map[pkg_class][1]):
1862 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1863
1864 if result is not None:
1865 bb.fatal(result)