blob: 06feb4def7272a5f1738db801d3c0eb12a28b211 [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 = []
43 pkgarch = ""
44 for line in cmd_output.splitlines():
45 line = line.rstrip()
46 if ':' in line:
47 if line.startswith("Package: "):
48 pkg = line.split(": ")[1]
49 elif line.startswith("Architecture: "):
50 arch = line.split(": ")[1]
51 elif line.startswith("Version: "):
52 ver = line.split(": ")[1]
53 elif line.startswith("File: ") or line.startswith("Filename:"):
54 filename = line.split(": ")[1]
55 if "/" in filename:
56 filename = os.path.basename(filename)
57 elif line.startswith("Depends: "):
58 depends = verregex.sub('', line.split(": ")[1])
59 for depend in depends.split(", "):
60 dep.append(depend)
61 elif line.startswith("Recommends: "):
62 recommends = verregex.sub('', line.split(": ")[1])
63 for recommend in recommends.split(", "):
64 dep.append("%s [REC]" % recommend)
65 elif line.startswith("PackageArch: "):
66 pkgarch = line.split(": ")[1]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050067
Patrick Williamsc0f7c042017-02-23 20:41:17 -060068 # When there is a blank line save the package information
69 elif not line:
70 # IPK doesn't include the filename
71 if not filename:
72 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
73 if pkg:
74 output[pkg] = {"arch":arch, "ver":ver,
75 "filename":filename, "deps": dep, "pkgarch":pkgarch }
76 pkg = ""
77 arch = ""
78 ver = ""
79 filename = ""
80 dep = []
81 pkgarch = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -050082
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083 if pkg:
84 if not filename:
85 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
86 output[pkg] = {"arch":arch, "ver":ver,
87 "filename":filename, "deps": dep }
88
89 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"],
110 "mips": ["--uint32-align=4", "--big-endian"],
111 "mipsisa32r6": ["--uint32-align=4", "--big-endian"],
112 "mips64": ["--uint32-align=4", "--big-endian"],
113 "mipsisa64r6": ["--uint32-align=4", "--big-endian"],
114 "mipsel": ["--uint32-align=4", "--little-endian"],
115 "mipsisa32r6el": ["--uint32-align=4", "--little-endian"],
116 "mips64el": ["--uint32-align=4", "--little-endian"],
117 "mipsisa64r6el": ["--uint32-align=4", "--little-endian"],
118 "riscv64": ["--uint32-align=4", "--little-endian"],
119 "riscv32": ["--uint32-align=4", "--little-endian"],
120 "i586": ["--uint32-align=4", "--little-endian"],
121 "i686": ["--uint32-align=4", "--little-endian"],
122 "x86_64": ["--uint32-align=4", "--little-endian"]
123 }
124 if target_arch in locale_arch_options:
125 arch_options = locale_arch_options[target_arch]
126 else:
127 bb.error("locale_arch_options not found for target_arch=" + target_arch)
128 bb.fatal("unknown arch:" + target_arch + " for locale_arch_options")
129
130 # Need to set this so cross-localedef knows where the archive is
131 env = dict(os.environ)
132 env["LOCALEARCHIVE"] = oe.path.join(localedir, "locale-archive")
133
134 for name in os.listdir(localedir):
135 path = os.path.join(localedir, name)
136 if os.path.isdir(path):
137 cmd = ["cross-localedef", "--verbose"]
138 cmd += arch_options
139 cmd += ["--add-to-archive", path]
140 subprocess.check_output(cmd, env=env, stderr=subprocess.STDOUT)
141
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600142class Indexer(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143 def __init__(self, d, deploy_dir):
144 self.d = d
145 self.deploy_dir = deploy_dir
146
147 @abstractmethod
148 def write_index(self):
149 pass
150
151
152class RpmIndexer(Indexer):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500153 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400154 self.do_write_index(self.deploy_dir)
155
156 def do_write_index(self, deploy_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500157 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500158 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
159 else:
160 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500162 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400163 result = create_index("%s --update -q %s" % (createrepo_c, deploy_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500164 if result:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500165 bb.fatal(result)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500167 # Sign repomd
168 if signer:
169 sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
170 is_ascii_sig = (sig_type.upper() != "BIN")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400171 signer.detach_sign(os.path.join(deploy_dir, 'repodata', 'repomd.xml'),
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500172 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
173 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
174 armor=is_ascii_sig)
175
Brad Bishop316dfdd2018-06-25 12:45:53 -0400176class RpmSubdirIndexer(RpmIndexer):
177 def write_index(self):
178 bb.note("Generating package index for %s" %(self.deploy_dir))
179 self.do_write_index(self.deploy_dir)
180 for entry in os.walk(self.deploy_dir):
181 if os.path.samefile(self.deploy_dir, entry[0]):
182 for dir in entry[1]:
183 if dir != 'repodata':
184 dir_path = oe.path.join(self.deploy_dir, dir)
185 bb.note("Generating package index for %s" %(dir_path))
186 self.do_write_index(dir_path)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500187
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500188class OpkgIndexer(Indexer):
189 def write_index(self):
190 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
191 "SDK_PACKAGE_ARCHS",
Brad Bishop316dfdd2018-06-25 12:45:53 -0400192 ]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500193
194 opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500195 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
196 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500197 else:
198 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199
200 if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
201 open(os.path.join(self.deploy_dir, "Packages"), "w").close()
202
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500203 index_cmds = set()
204 index_sign_files = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500205 for arch_var in arch_vars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500206 archs = self.d.getVar(arch_var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500207 if archs is None:
208 continue
209
210 for arch in archs.split():
211 pkgs_dir = os.path.join(self.deploy_dir, arch)
212 pkgs_file = os.path.join(pkgs_dir, "Packages")
213
214 if not os.path.isdir(pkgs_dir):
215 continue
216
217 if not os.path.exists(pkgs_file):
218 open(pkgs_file, "w").close()
219
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500220 index_cmds.add('%s -r %s -p %s -m %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221 (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
222
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500223 index_sign_files.add(pkgs_file)
224
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500225 if len(index_cmds) == 0:
226 bb.note("There are no packages in %s!" % self.deploy_dir)
227 return
228
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800229 oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500230
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500231 if signer:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500232 feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500233 is_ascii_sig = (feed_sig_type.upper() != "BIN")
234 for f in index_sign_files:
235 signer.detach_sign(f,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500236 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
237 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500238 armor=is_ascii_sig)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500239
240
241class DpkgIndexer(Indexer):
242 def _create_configs(self):
243 bb.utils.mkdirhier(self.apt_conf_dir)
244 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
245 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
246 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
247
248 with open(os.path.join(self.apt_conf_dir, "preferences"),
249 "w") as prefs_file:
250 pass
251 with open(os.path.join(self.apt_conf_dir, "sources.list"),
252 "w+") as sources_file:
253 pass
254
255 with open(self.apt_conf_file, "w") as apt_conf:
256 with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
257 "apt", "apt.conf.sample")) as apt_conf_sample:
258 for line in apt_conf_sample.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -0400259 line = re.sub(r"#ROOTFS#", "/dev/null", line)
260 line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500261 apt_conf.write(line + "\n")
262
263 def write_index(self):
264 self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
265 "apt-ftparchive")
266 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
267 self._create_configs()
268
269 os.environ['APT_CONFIG'] = self.apt_conf_file
270
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500271 pkg_archs = self.d.getVar('PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272 if pkg_archs is not None:
273 arch_list = pkg_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500274 sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500275 if sdk_pkg_archs is not None:
276 for a in sdk_pkg_archs.split():
277 if a not in pkg_archs:
278 arch_list.append(a)
279
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500280 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500281 arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
282
283 apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
284 gzip = bb.utils.which(os.getenv('PATH'), "gzip")
285
286 index_cmds = []
287 deb_dirs_found = False
288 for arch in arch_list:
289 arch_dir = os.path.join(self.deploy_dir, arch)
290 if not os.path.isdir(arch_dir):
291 continue
292
293 cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
294
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500295 cmd += "%s -fcn Packages > Packages.gz;" % gzip
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500296
297 with open(os.path.join(arch_dir, "Release"), "w+") as release:
298 release.write("Label: %s\n" % arch)
299
300 cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
301
302 index_cmds.append(cmd)
303
304 deb_dirs_found = True
305
306 if not deb_dirs_found:
307 bb.note("There are no packages in %s" % self.deploy_dir)
308 return
309
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800310 oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500311 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500312 raise NotImplementedError('Package feed signing not implementd for dpkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313
314
315
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316class PkgsList(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 def __init__(self, d, rootfs_dir):
318 self.d = d
319 self.rootfs_dir = rootfs_dir
320
321 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500322 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323 pass
324
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500325class RpmPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500326 def list_pkgs(self):
Brad Bishop19323692019-04-05 15:28:33 -0400327 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR'), needfeed=False).list_installed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500328
329class OpkgPkgsList(PkgsList):
330 def __init__(self, d, rootfs_dir, config_file):
331 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
332
333 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
334 self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500335 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500336
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500337 def list_pkgs(self, format=None):
338 cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500340 # opkg returns success even when it printed some
341 # "Collected errors:" report to stderr. Mixing stderr into
342 # stdout then leads to random failures later on when
343 # parsing the output. To avoid this we need to collect both
344 # output streams separately and check for empty stderr.
345 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
346 cmd_output, cmd_stderr = p.communicate()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600347 cmd_output = cmd_output.decode("utf-8")
348 cmd_stderr = cmd_stderr.decode("utf-8")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500349 if p.returncode or cmd_stderr:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500350 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500351 "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500352
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600353 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500354
355
356class DpkgPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500357
358 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500359 cmd = [bb.utils.which(os.getenv('PATH'), "dpkg-query"),
360 "--admindir=%s/var/lib/dpkg" % self.rootfs_dir,
361 "-W"]
362
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500363 cmd.append("-f=Package: ${Package}\nArchitecture: ${PackageArch}\nVersion: ${Version}\nFile: ${Package}_${Version}_${Architecture}.deb\nDepends: ${Depends}\nRecommends: ${Recommends}\n\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500364
365 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600366 cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367 except subprocess.CalledProcessError as e:
368 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600369 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500370
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600371 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500372
373
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600374class PackageManager(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500375 """
376 This is an abstract class. Do not instantiate this directly.
377 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500378
Brad Bishop316dfdd2018-06-25 12:45:53 -0400379 def __init__(self, d, target_rootfs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500380 self.d = d
Brad Bishop316dfdd2018-06-25 12:45:53 -0400381 self.target_rootfs = target_rootfs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500382 self.deploy_dir = None
383 self.deploy_lock = None
Brad Bishop316dfdd2018-06-25 12:45:53 -0400384 self._initialize_intercepts()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500385
Brad Bishop316dfdd2018-06-25 12:45:53 -0400386 def _initialize_intercepts(self):
387 bb.note("Initializing intercept dir for %s" % self.target_rootfs)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400388 # As there might be more than one instance of PackageManager operating at the same time
389 # we need to isolate the intercept_scripts directories from each other,
390 # hence the ugly hash digest in dir name.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800391 self.intercepts_dir = os.path.join(self.d.getVar('WORKDIR'), "intercept_scripts-%s" %
392 (hashlib.sha256(self.target_rootfs.encode()).hexdigest()))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400393
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800394 postinst_intercepts = (self.d.getVar("POSTINST_INTERCEPTS") or "").split()
395 if not postinst_intercepts:
396 postinst_intercepts_path = self.d.getVar("POSTINST_INTERCEPTS_PATH")
397 if not postinst_intercepts_path:
398 postinst_intercepts_path = self.d.getVar("POSTINST_INTERCEPTS_DIR") or self.d.expand("${COREBASE}/scripts/postinst-intercepts")
399 postinst_intercepts = oe.path.which_wild('*', postinst_intercepts_path)
400
401 bb.debug(1, 'Collected intercepts:\n%s' % ''.join(' %s\n' % i for i in postinst_intercepts))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400402 bb.utils.remove(self.intercepts_dir, True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800403 bb.utils.mkdirhier(self.intercepts_dir)
404 for intercept in postinst_intercepts:
405 bb.utils.copyfile(intercept, os.path.join(self.intercepts_dir, os.path.basename(intercept)))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400406
407 @abstractmethod
408 def _handle_intercept_failure(self, failed_script):
409 pass
410
411 def _postpone_to_first_boot(self, postinst_intercept_hook):
412 with open(postinst_intercept_hook) as intercept:
413 registered_pkgs = None
414 for line in intercept.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -0400415 m = re.match(r"^##PKGS:(.*)", line)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400416 if m is not None:
417 registered_pkgs = m.group(1).strip()
418 break
419
420 if registered_pkgs is not None:
421 bb.note("If an image is being built, the postinstalls for the following packages "
422 "will be postponed for first boot: %s" %
423 registered_pkgs)
424
425 # call the backend dependent handler
426 self._handle_intercept_failure(registered_pkgs)
427
428
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800429 def run_intercepts(self, populate_sdk=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400430 intercepts_dir = self.intercepts_dir
431
432 bb.note("Running intercept scripts:")
433 os.environ['D'] = self.target_rootfs
434 os.environ['STAGING_DIR_NATIVE'] = self.d.getVar('STAGING_DIR_NATIVE')
435 for script in os.listdir(intercepts_dir):
436 script_full = os.path.join(intercepts_dir, script)
437
438 if script == "postinst_intercept" or not os.access(script_full, os.X_OK):
439 continue
440
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800441 # we do not want to run any multilib variant of this
442 if script.startswith("delay_to_first_boot"):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400443 self._postpone_to_first_boot(script_full)
444 continue
445
Brad Bishop19323692019-04-05 15:28:33 -0400446 if populate_sdk == 'host' and self.d.getVar('SDK_OS') == 'mingw32':
447 bb.note("The postinstall intercept hook '%s' could not be executed due to missing wine support, details in %s/log.do_%s"
448 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
449 continue
450
Brad Bishop316dfdd2018-06-25 12:45:53 -0400451 bb.note("> Executing %s intercept ..." % script)
452
453 try:
454 output = subprocess.check_output(script_full, stderr=subprocess.STDOUT)
455 if output: bb.note(output.decode("utf-8"))
456 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400457 bb.note("Exit code %d. Output:\n%s" % (e.returncode, e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800458 if populate_sdk == 'host':
Brad Bishop19323692019-04-05 15:28:33 -0400459 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 -0800460 elif populate_sdk == 'target':
461 if "qemuwrapper: qemu usermode is not supported" in e.output.decode("utf-8"):
Brad Bishop19323692019-04-05 15:28:33 -0400462 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 -0800463 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
464 else:
465 bb.fatal("The postinstall intercept hook '%s' failed, details in %s/log.do_%s" % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
466 else:
467 if "qemuwrapper: qemu usermode is not supported" in e.output.decode("utf-8"):
468 bb.note("The postinstall intercept hook '%s' could not be executed due to missing qemu usermode support, details in %s/log.do_%s"
469 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
470 self._postpone_to_first_boot(script_full)
471 else:
472 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 -0400473
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500474 @abstractmethod
475 def update(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400476 """
477 Update the package manager package database.
478 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500479 pass
480
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500481 @abstractmethod
482 def install(self, pkgs, attempt_only=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400483 """
484 Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
485 True, installation failures are ignored.
486 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500487 pass
488
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500489 @abstractmethod
490 def remove(self, pkgs, with_dependencies=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400491 """
492 Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
493 is False, then any dependencies are left in place.
494 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500495 pass
496
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500497 @abstractmethod
498 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400499 """
500 This function creates the index files
501 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500502 pass
503
504 @abstractmethod
505 def remove_packaging_data(self):
506 pass
507
508 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500509 def list_installed(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500510 pass
511
512 @abstractmethod
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500513 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400514 """
515 Returns the path to a tmpdir where resides the contents of a package.
516 Deleting the tmpdir is responsability of the caller.
517 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500518 pass
519
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500520 @abstractmethod
521 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400522 """
523 Add remote package feeds into repository manager configuration. The parameters
524 for the feeds are set by feed_uris, feed_base_paths and feed_archs.
525 See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS
526 for their description.
527 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500528 pass
529
Brad Bishop00111322018-04-01 22:23:53 -0400530 def install_glob(self, globs, sdk=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400531 """
532 Install all packages that match a glob.
533 """
Brad Bishop00111322018-04-01 22:23:53 -0400534 # TODO don't have sdk here but have a property on the superclass
535 # (and respect in install_complementary)
536 if sdk:
537 pkgdatadir = self.d.expand("${TMPDIR}/pkgdata/${SDK_SYS}")
538 else:
539 pkgdatadir = self.d.getVar("PKGDATA_DIR")
540
541 try:
542 bb.note("Installing globbed packages...")
543 cmd = ["oe-pkgdata-util", "-p", pkgdatadir, "list-pkgs", globs]
544 pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
545 self.install(pkgs.split(), attempt_only=True)
546 except subprocess.CalledProcessError as e:
547 # Return code 1 means no packages matched
548 if e.returncode != 1:
549 bb.fatal("Could not compute globbed packages list. Command "
550 "'%s' returned %d:\n%s" %
551 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
552
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553 def install_complementary(self, globs=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400554 """
555 Install complementary packages based upon the list of currently installed
556 packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
557 these packages, if they don't exist then no error will occur. Note: every
558 backend needs to call this function explicitly after the normal package
559 installation
560 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500561 if globs is None:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500562 globs = self.d.getVar('IMAGE_INSTALL_COMPLEMENTARY')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500563 split_linguas = set()
564
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500565 for translation in self.d.getVar('IMAGE_LINGUAS').split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566 split_linguas.add(translation)
567 split_linguas.add(translation.split('-')[0])
568
569 split_linguas = sorted(split_linguas)
570
571 for lang in split_linguas:
572 globs += " *-locale-%s" % lang
573
574 if globs is None:
575 return
576
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500577 # we need to write the list of installed packages to a file because the
578 # oe-pkgdata-util reads it from a file
579 with tempfile.NamedTemporaryFile(mode="w+", prefix="installed-pkgs") as installed_pkgs:
580 pkgs = self.list_installed()
581 output = oe.utils.format_pkg_list(pkgs, "arch")
582 installed_pkgs.write(output)
583 installed_pkgs.flush()
584
Brad Bishop00111322018-04-01 22:23:53 -0400585 cmd = ["oe-pkgdata-util",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500586 "-p", self.d.getVar('PKGDATA_DIR'), "glob", installed_pkgs.name,
587 globs]
588 exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY')
589 if exclude:
590 cmd.extend(['--exclude=' + '|'.join(exclude.split())])
591 try:
592 bb.note("Installing complementary packages ...")
593 bb.note('Running %s' % cmd)
594 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishop00111322018-04-01 22:23:53 -0400595 self.install(complementary_pkgs.split(), attempt_only=True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500596 except subprocess.CalledProcessError as e:
597 bb.fatal("Could not compute complementary packages list. Command "
598 "'%s' returned %d:\n%s" %
599 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800601 target_arch = self.d.getVar('TARGET_ARCH')
602 localedir = oe.path.join(self.target_rootfs, self.d.getVar("libdir"), "locale")
603 if os.path.exists(localedir) and os.listdir(localedir):
604 generate_locale_archive(self.d, self.target_rootfs, target_arch, localedir)
605 # And now delete the binary locales
606 self.remove(fnmatch.filter(self.list_installed(), "glibc-binary-localedata-*"), False)
607
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608 def deploy_dir_lock(self):
609 if self.deploy_dir is None:
610 raise RuntimeError("deploy_dir is not set!")
611
612 lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
613
614 self.deploy_lock = bb.utils.lockfile(lock_file_name)
615
616 def deploy_dir_unlock(self):
617 if self.deploy_lock is None:
618 return
619
620 bb.utils.unlockfile(self.deploy_lock)
621
622 self.deploy_lock = None
623
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500624 def construct_uris(self, uris, base_paths):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400625 """
626 Construct URIs based on the following pattern: uri/base_path where 'uri'
627 and 'base_path' correspond to each element of the corresponding array
628 argument leading to len(uris) x len(base_paths) elements on the returned
629 array
630 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500631 def _append(arr1, arr2, sep='/'):
632 res = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600633 narr1 = [a.rstrip(sep) for a in arr1]
634 narr2 = [a.rstrip(sep).lstrip(sep) for a in arr2]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500635 for a1 in narr1:
636 if arr2:
637 for a2 in narr2:
638 res.append("%s%s%s" % (a1, sep, a2))
639 else:
640 res.append(a1)
641 return res
642 return _append(uris, base_paths)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800644def create_packages_dir(d, subrepo_dir, deploydir, taskname, filterbydependencies):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400645 """
646 Go through our do_package_write_X dependencies and hardlink the packages we depend
647 upon into the repo directory. This prevents us seeing other packages that may
648 have been built that we don't depend upon and also packages for architectures we don't
649 support.
650 """
651 import errno
652
653 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
654 mytaskname = d.getVar("BB_RUNTASK")
655 pn = d.getVar("PN")
656 seendirs = set()
657 multilibs = {}
658
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800659 bb.utils.remove(subrepo_dir, recurse=True)
660 bb.utils.mkdirhier(subrepo_dir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400661
662 # Detect bitbake -b usage
663 nodeps = d.getVar("BB_LIMITEDDEPS") or False
664 if nodeps or not filterbydependencies:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800665 oe.path.symlink(deploydir, subrepo_dir, True)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400666 return
667
668 start = None
669 for dep in taskdepdata:
670 data = taskdepdata[dep]
671 if data[1] == mytaskname and data[0] == pn:
672 start = dep
673 break
674 if start is None:
675 bb.fatal("Couldn't find ourself in BB_TASKDEPDATA?")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800676 pkgdeps = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400677 start = [start]
678 seen = set(start)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800679 # Support direct dependencies (do_rootfs -> do_package_write_X)
680 # or indirect dependencies within PN (do_populate_sdk_ext -> do_rootfs -> do_package_write_X)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400681 while start:
682 next = []
683 for dep2 in start:
684 for dep in taskdepdata[dep2][3]:
685 if taskdepdata[dep][0] != pn:
686 if "do_" + taskname in dep:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800687 pkgdeps.add(dep)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400688 elif dep not in seen:
689 next.append(dep)
690 seen.add(dep)
691 start = next
692
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800693 for dep in pkgdeps:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400694 c = taskdepdata[dep][0]
695 manifest, d2 = oe.sstatesig.find_sstate_manifest(c, taskdepdata[dep][2], taskname, d, multilibs)
696 if not manifest:
697 bb.fatal("No manifest generated from: %s in %s" % (c, taskdepdata[dep][2]))
698 if not os.path.exists(manifest):
699 continue
700 with open(manifest, "r") as f:
701 for l in f:
702 l = l.strip()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800703 deploydir = os.path.normpath(deploydir)
704 if bb.data.inherits_class('packagefeed-stability', d):
705 dest = l.replace(deploydir + "-prediff", "")
706 else:
707 dest = l.replace(deploydir, "")
708 dest = subrepo_dir + dest
Brad Bishop316dfdd2018-06-25 12:45:53 -0400709 if l.endswith("/"):
710 if dest not in seendirs:
711 bb.utils.mkdirhier(dest)
712 seendirs.add(dest)
713 continue
714 # Try to hardlink the file, copy if that fails
715 destdir = os.path.dirname(dest)
716 if destdir not in seendirs:
717 bb.utils.mkdirhier(destdir)
718 seendirs.add(destdir)
719 try:
720 os.link(l, dest)
721 except OSError as err:
722 if err.errno == errno.EXDEV:
723 bb.utils.copyfile(l, dest)
724 else:
725 raise
726
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727class RpmPM(PackageManager):
728 def __init__(self,
729 d,
730 target_rootfs,
731 target_vendor,
732 task_name='target',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733 arch_var=None,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500734 os_var=None,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400735 rpm_repo_workdir="oe-rootfs-repo",
Brad Bishop19323692019-04-05 15:28:33 -0400736 filterbydependencies=True,
737 needfeed=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400738 super(RpmPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739 self.target_vendor = target_vendor
740 self.task_name = task_name
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500741 if arch_var == None:
742 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
743 else:
744 self.archs = self.d.getVar(arch_var).replace("-","_")
745 if task_name == "host":
746 self.primary_arch = self.d.getVar('SDK_ARCH')
747 else:
748 self.primary_arch = self.d.getVar('MACHINE_ARCH')
749
Brad Bishop19323692019-04-05 15:28:33 -0400750 if needfeed:
751 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
752 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 -0500753
754 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
755 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
756 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800757 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 -0500758 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
759 self.task_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500760 if not os.path.exists(self.d.expand('${T}/saved')):
761 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
762
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500763 def _configure_dnf(self):
764 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
765 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
766 # This prevents accidental matching against libsolv's built-in policies
767 if len(archs) <= 1:
768 archs = archs + ["bogusarch"]
769 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
770 bb.utils.mkdirhier(confdir)
771 open(confdir + "arch", 'w').write(":".join(archs))
772 distro_codename = self.d.getVar('DISTRO_CODENAME')
773 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500775 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500776
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500777
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500778 def _configure_rpm(self):
779 # We need to configure rpm to use our primary package architecture as the installation architecture,
780 # and to make it compatible with other package architectures that we use.
781 # Otherwise it will refuse to proceed with packages installation.
782 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
783 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
784 bb.utils.mkdirhier(platformconfdir)
785 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800786 with open(rpmrcconfdir + "rpmrc", 'w') as f:
787 f.write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
788 f.write("buildarch_compat: %s: noarch\n" % self.primary_arch)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500789
790 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
791 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
792 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500793 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500794 open(platformconfdir + "macros", 'a').write("%_prefer_color 7")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500795
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500796 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
797 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
798 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
799 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
800 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
801 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
802 try:
803 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
804 except subprocess.CalledProcessError as e:
805 bb.fatal("Importing GPG key failed. Command '%s' "
806 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500807
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808 def create_configs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500809 self._configure_dnf()
810 self._configure_rpm()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811
812 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500813 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
814 lf = bb.utils.lockfile(lockfilename, False)
815 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
816 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500818 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
819 from urllib.parse import urlparse
820
821 if feed_uris == "":
822 return
823
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500824 gpg_opts = ''
825 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
826 gpg_opts += 'repo_gpgcheck=1\n'
827 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'))
828
Brad Bishop316dfdd2018-06-25 12:45:53 -0400829 if self.d.getVar('RPM_SIGN_PACKAGES') != '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500830 gpg_opts += 'gpgcheck=0\n'
831
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500832 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
833 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
834 for uri in remote_uris:
835 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
836 if feed_archs is not None:
837 for arch in feed_archs.split():
838 repo_uri = uri + "/" + arch
839 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
840 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
841 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500842 "[%s]\nname=%s\nbaseurl=%s\n%s\n" % (repo_id, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500843 else:
844 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
845 repo_uri = uri
846 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500847 "[%s]\nname=%s\nbaseurl=%s\n%s" % (repo_base, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500848
849 def _prepare_pkg_transaction(self):
850 os.environ['D'] = self.target_rootfs
851 os.environ['OFFLINE_ROOT'] = self.target_rootfs
852 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
853 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -0400854 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500855 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
856
857
858 def install(self, pkgs, attempt_only = False):
859 if len(pkgs) == 0:
860 return
861 self._prepare_pkg_transaction()
862
863 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
864 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
865 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
866
867 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
868 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
869 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
870 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
871 ["install"] +
872 pkgs)
873
874 failed_scriptlets_pkgnames = collections.OrderedDict()
875 for line in output.splitlines():
Brad Bishop19323692019-04-05 15:28:33 -0400876 if line.startswith("Error in POSTIN scriptlet in rpm package"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500877 failed_scriptlets_pkgnames[line.split()[-1]] = True
878
Brad Bishop316dfdd2018-06-25 12:45:53 -0400879 if len(failed_scriptlets_pkgnames) > 0:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800880 failed_postinsts_abort(list(failed_scriptlets_pkgnames.keys()), self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500881
882 def remove(self, pkgs, with_dependencies = True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800883 if not pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500884 return
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800885
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500886 self._prepare_pkg_transaction()
887
888 if with_dependencies:
889 self._invoke_dnf(["remove"] + pkgs)
890 else:
891 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500892 args = ["-e", "-v", "--nodeps", "--root=%s" %self.target_rootfs]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500893
894 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500895 bb.note("Running %s" % ' '.join([cmd] + args + pkgs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500896 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500897 bb.note(output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500898 except subprocess.CalledProcessError as e:
899 bb.fatal("Could not invoke rpm. Command "
900 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
901
902 def upgrade(self):
903 self._prepare_pkg_transaction()
904 self._invoke_dnf(["upgrade"])
905
906 def autoremove(self):
907 self._prepare_pkg_transaction()
908 self._invoke_dnf(["autoremove"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500909
910 def remove_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500911 self._invoke_dnf(["clean", "all"])
912 for dir in self.packaging_data_dirs:
913 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500914
915 def backup_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500916 # Save the packaging dirs for increment rpm image generation
917 if os.path.exists(self.saved_packaging_data):
918 bb.utils.remove(self.saved_packaging_data, True)
919 for i in self.packaging_data_dirs:
920 source_dir = oe.path.join(self.target_rootfs, i)
921 target_dir = oe.path.join(self.saved_packaging_data, i)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800922 if os.path.isdir(source_dir):
923 shutil.copytree(source_dir, target_dir, symlinks=True)
924 elif os.path.isfile(source_dir):
925 shutil.copy2(source_dir, target_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500926
927 def recovery_packaging_data(self):
928 # Move the rpmlib back
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500929 if os.path.exists(self.saved_packaging_data):
930 for i in self.packaging_data_dirs:
931 target_dir = oe.path.join(self.target_rootfs, i)
932 if os.path.exists(target_dir):
933 bb.utils.remove(target_dir, True)
934 source_dir = oe.path.join(self.saved_packaging_data, i)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800935 if os.path.isdir(source_dir):
936 shutil.copytree(source_dir, target_dir, symlinks=True)
937 elif os.path.isfile(source_dir):
938 shutil.copy2(source_dir, target_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500940 def list_installed(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500941 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
942 print_output = False)
943 packages = {}
944 current_package = None
945 current_deps = None
946 current_state = "initial"
947 for line in output.splitlines():
948 if line.startswith("Package:"):
949 package_info = line.split(" ")[1:]
950 current_package = package_info[0]
951 package_arch = package_info[1]
952 package_version = package_info[2]
953 package_rpm = package_info[3]
954 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
955 current_deps = []
956 elif line.startswith("Dependencies:"):
957 current_state = "dependencies"
958 elif line.startswith("Recommendations"):
959 current_state = "recommendations"
960 elif line.startswith("DependenciesEndHere:"):
961 current_state = "initial"
962 packages[current_package]["deps"] = current_deps
963 elif len(line) > 0:
964 if current_state == "dependencies":
965 current_deps.append(line)
966 elif current_state == "recommendations":
967 current_deps.append("%s [REC]" % line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500968
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500969 return packages
970
971 def update(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500972 self._invoke_dnf(["makecache", "--refresh"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500973
974 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
975 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
976
977 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
Brad Bishop19323692019-04-05 15:28:33 -0400978 standard_dnf_args = ["-v", "--rpmverbosity=info", "-y",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500979 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
980 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500981 "--installroot=%s" % (self.target_rootfs),
982 "--setopt=logdir=%s" % (self.d.getVar('T'))
983 ]
Brad Bishop19323692019-04-05 15:28:33 -0400984 if hasattr(self, "rpm_repo_dir"):
985 standard_dnf_args.append("--repofrompath=oe-repo,%s" % (self.rpm_repo_dir))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500986 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
Brad Bishop316dfdd2018-06-25 12:45:53 -0400987 bb.note('Running %s' % ' '.join(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500988 try:
989 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
990 if print_output:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800991 bb.debug(1, output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500992 return output
993 except subprocess.CalledProcessError as e:
994 if print_output:
995 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
996 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
997 else:
998 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
999 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
1000 return e.output.decode("utf-8")
1001
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001002 def dump_install_solution(self, pkgs):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001003 open(self.solution_manifest, 'w').write(" ".join(pkgs))
1004 return pkgs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001006 def load_old_install_solution(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007 if not os.path.exists(self.solution_manifest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 return []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001009
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001010 return open(self.solution_manifest, 'r').read().split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001011
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001012 def _script_num_prefix(self, path):
1013 files = os.listdir(path)
1014 numbers = set()
1015 numbers.add(99)
1016 for f in files:
1017 numbers.add(int(f.split("-")[0]))
1018 return max(numbers) + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001019
1020 def save_rpmpostinst(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001021 bb.note("Saving postinstall script of %s" % (pkg))
1022 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
1023 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001024
1025 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001026 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001027 except subprocess.CalledProcessError as e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001028 bb.fatal("Could not invoke rpm. Command "
1029 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001030
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001031 # may need to prepend #!/bin/sh to output
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001033 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
1034 bb.utils.mkdirhier(target_path)
1035 num = self._script_num_prefix(target_path)
1036 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
1037 open(saved_script_name, 'w').write(output)
1038 os.chmod(saved_script_name, 0o755)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001039
Brad Bishop316dfdd2018-06-25 12:45:53 -04001040 def _handle_intercept_failure(self, registered_pkgs):
1041 rpm_postinsts_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
1042 bb.utils.mkdirhier(rpm_postinsts_dir)
1043
1044 # Save the package postinstalls in /etc/rpm-postinsts
1045 for pkg in registered_pkgs.split():
1046 self.save_rpmpostinst(pkg)
1047
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001048 def extract(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001049 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
1050 pkg_name = output.splitlines()[-1]
1051 if not pkg_name.endswith(".rpm"):
1052 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
1053 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001054
1055 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
1056 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
1057
1058 if not os.path.isfile(pkg_path):
1059 bb.fatal("Unable to extract package for '%s'."
1060 "File %s doesn't exists" % (pkg, pkg_path))
1061
1062 tmp_dir = tempfile.mkdtemp()
1063 current_dir = os.getcwd()
1064 os.chdir(tmp_dir)
1065
1066 try:
1067 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
1068 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1069 except subprocess.CalledProcessError as e:
1070 bb.utils.remove(tmp_dir, recurse=True)
1071 bb.fatal("Unable to extract %s package. Command '%s' "
1072 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
1073 except OSError as e:
1074 bb.utils.remove(tmp_dir, recurse=True)
1075 bb.fatal("Unable to extract %s package. Command '%s' "
1076 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
1077
1078 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1079 os.chdir(current_dir)
1080
1081 return tmp_dir
1082
1083
1084class OpkgDpkgPM(PackageManager):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001085 def __init__(self, d, target_rootfs):
1086 """
1087 This is an abstract class. Do not instantiate this directly.
1088 """
1089 super(OpkgDpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001090
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001091 def package_info(self, pkg, cmd):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001092 """
1093 Returns a dictionary with the package info.
1094
1095 This method extracts the common parts for Opkg and Dpkg
1096 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001097
1098 try:
1099 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
1100 except subprocess.CalledProcessError as e:
1101 bb.fatal("Unable to list available packages. Command '%s' "
1102 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
1103 return opkg_query(output)
1104
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001105 def extract(self, pkg, pkg_info):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001106 """
1107 Returns the path to a tmpdir where resides the contents of a package.
1108
1109 Deleting the tmpdir is responsability of the caller.
1110
1111 This method extracts the common parts for Opkg and Dpkg
1112 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001113
1114 ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
1115 tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
1116 pkg_path = pkg_info[pkg]["filepath"]
1117
1118 if not os.path.isfile(pkg_path):
1119 bb.fatal("Unable to extract package for '%s'."
1120 "File %s doesn't exists" % (pkg, pkg_path))
1121
1122 tmp_dir = tempfile.mkdtemp()
1123 current_dir = os.getcwd()
1124 os.chdir(tmp_dir)
Brad Bishop19323692019-04-05 15:28:33 -04001125 data_tar = 'data.tar.xz'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001126
1127 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001128 cmd = [ar_cmd, 'x', pkg_path]
1129 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
1130 cmd = [tar_cmd, 'xf', data_tar]
1131 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001132 except subprocess.CalledProcessError as e:
1133 bb.utils.remove(tmp_dir, recurse=True)
1134 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001135 "returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001136 except OSError as e:
1137 bb.utils.remove(tmp_dir, recurse=True)
1138 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001139 "returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001140
1141 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1142 bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
1143 bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
1144 os.chdir(current_dir)
1145
1146 return tmp_dir
1147
Brad Bishop316dfdd2018-06-25 12:45:53 -04001148 def _handle_intercept_failure(self, registered_pkgs):
1149 self.mark_packages("unpacked", registered_pkgs.split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150
1151class OpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001152 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 -04001153 super(OpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001154
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001155 self.config_file = config_file
1156 self.pkg_archs = archs
1157 self.task_name = task_name
1158
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001159 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), ipk_repo_workdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001160 self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
1161 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162 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 -05001163 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001164
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001165 if prepare_index:
1166 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_IPK"), "package_write_ipk", filterbydependencies)
1167
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001168 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001169 if opkg_lib_dir[0] == "/":
1170 opkg_lib_dir = opkg_lib_dir[1:]
1171
1172 self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
1173
1174 bb.utils.mkdirhier(self.opkg_dir)
1175
1176 self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
1177 if not os.path.exists(self.d.expand('${T}/saved')):
1178 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
1179
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001180 self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001181 if self.from_feeds:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001182 self._create_custom_config()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001183 else:
1184 self._create_config()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001185
1186 self.indexer = OpkgIndexer(self.d, self.deploy_dir)
1187
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001188 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001189 """
1190 This function will change a package's status in /var/lib/opkg/status file.
1191 If 'packages' is None then the new_status will be applied to all
1192 packages
1193 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001194 status_file = os.path.join(self.opkg_dir, "status")
1195
1196 with open(status_file, "r") as sf:
1197 with open(status_file + ".tmp", "w+") as tmp_sf:
1198 if packages is None:
1199 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1200 r"Package: \1\n\2Status: \3%s" % status_tag,
1201 sf.read()))
1202 else:
1203 if type(packages).__name__ != "list":
1204 raise TypeError("'packages' should be a list object")
1205
1206 status = sf.read()
1207 for pkg in packages:
1208 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1209 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1210 status)
1211
1212 tmp_sf.write(status)
1213
1214 os.rename(status_file + ".tmp", status_file)
1215
1216 def _create_custom_config(self):
1217 bb.note("Building from feeds activated!")
1218
1219 with open(self.config_file, "w+") as config_file:
1220 priority = 1
1221 for arch in self.pkg_archs.split():
1222 config_file.write("arch %s %d\n" % (arch, priority))
1223 priority += 5
1224
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001225 for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
Brad Bishop19323692019-04-05 15:28:33 -04001226 feed_match = re.match(r"^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001227
1228 if feed_match is not None:
1229 feed_name = feed_match.group(1)
1230 feed_uri = feed_match.group(2)
1231
1232 bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
1233
1234 config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
1235
1236 """
1237 Allow to use package deploy directory contents as quick devel-testing
1238 feed. This creates individual feed configs for each arch subdir of those
1239 specified as compatible for the current machine.
1240 NOTE: Development-helper feature, NOT a full-fledged feed.
1241 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242 if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243 for arch in self.pkg_archs.split():
1244 cfg_file_name = os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001245 self.d.getVar("sysconfdir"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001246 "opkg",
1247 "local-%s-feed.conf" % arch)
1248
1249 with open(cfg_file_name, "w+") as cfg_file:
1250 cfg_file.write("src/gz local-%s %s/%s" %
1251 (arch,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001253 arch))
1254
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001255 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001256 # There is no command line option for this anymore, we need to add
1257 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1258 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001259 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1260 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1261 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001262 cfg_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1263 cfg_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1264 cfg_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001265
1266
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001267 def _create_config(self):
1268 with open(self.config_file, "w+") as config_file:
1269 priority = 1
1270 for arch in self.pkg_archs.split():
1271 config_file.write("arch %s %d\n" % (arch, priority))
1272 priority += 5
1273
1274 config_file.write("src oe file:%s\n" % self.deploy_dir)
1275
1276 for arch in self.pkg_archs.split():
1277 pkgs_dir = os.path.join(self.deploy_dir, arch)
1278 if os.path.isdir(pkgs_dir):
1279 config_file.write("src oe-%s file:%s\n" %
1280 (arch, pkgs_dir))
1281
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001282 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001283 # There is no command line option for this anymore, we need to add
1284 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1285 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001286 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1287 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1288 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001289 config_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1290 config_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1291 config_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001292
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001293 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1294 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001295 return
1296
1297 rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
1298 % self.target_rootfs)
1299
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001300 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
1301 archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001302
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001303 with open(rootfs_config, "w+") as config_file:
1304 uri_iterator = 0
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001305 for uri in feed_uris:
1306 if archs:
1307 for arch in archs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001308 if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001309 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001310 bb.note('Adding opkg feed url-%s-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001311 (arch, uri_iterator, uri))
1312 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1313 (arch, uri_iterator, uri, arch))
1314 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001315 bb.note('Adding opkg feed url-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001316 (uri_iterator, uri))
1317 config_file.write("src/gz uri-%d %s\n" %
1318 (uri_iterator, uri))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001319
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001320 uri_iterator += 1
1321
1322 def update(self):
1323 self.deploy_dir_lock()
1324
1325 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1326
1327 try:
1328 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1329 except subprocess.CalledProcessError as e:
1330 self.deploy_dir_unlock()
1331 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001332 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001333
1334 self.deploy_dir_unlock()
1335
1336 def install(self, pkgs, attempt_only=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001337 if not pkgs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001338 return
1339
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001340 cmd = "%s %s" % (self.opkg_cmd, self.opkg_args)
1341 for exclude in (self.d.getVar("PACKAGE_EXCLUDE") or "").split():
1342 cmd += " --add-exclude %s" % exclude
Brad Bishop19323692019-04-05 15:28:33 -04001343 for bad_recommendation in (self.d.getVar("BAD_RECOMMENDATIONS") or "").split():
1344 cmd += " --add-ignore-recommends %s" % bad_recommendation
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001345 cmd += " install "
1346 cmd += " ".join(pkgs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001347
1348 os.environ['D'] = self.target_rootfs
1349 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1350 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1351 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001352 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001353 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354
1355 try:
1356 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1357 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001358 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001359 bb.note(output)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001360 failed_pkgs = []
1361 for line in output.split('\n'):
1362 if line.endswith("configuration required on target."):
1363 bb.warn(line)
1364 failed_pkgs.append(line.split(".")[0])
1365 if failed_pkgs:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001366 failed_postinsts_abort(failed_pkgs, self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001368 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001369 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001370 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001371
1372 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001373 if not pkgs:
1374 return
1375
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001376 if with_dependencies:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001377 cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001378 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1379 else:
1380 cmd = "%s %s --force-depends remove %s" % \
1381 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1382
1383 try:
1384 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001385 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001386 bb.note(output)
1387 except subprocess.CalledProcessError as e:
1388 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001389 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001390
1391 def write_index(self):
1392 self.deploy_dir_lock()
1393
1394 result = self.indexer.write_index()
1395
1396 self.deploy_dir_unlock()
1397
1398 if result is not None:
1399 bb.fatal(result)
1400
1401 def remove_packaging_data(self):
1402 bb.utils.remove(self.opkg_dir, True)
1403 # create the directory back, it's needed by PM lock
1404 bb.utils.mkdirhier(self.opkg_dir)
1405
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001406 def remove_lists(self):
1407 if not self.from_feeds:
1408 bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
1409
1410 def list_installed(self):
1411 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001412
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001413 def dummy_install(self, pkgs):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001414 """
1415 The following function dummy installs pkgs and returns the log of output.
1416 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001417 if len(pkgs) == 0:
1418 return
1419
1420 # Create an temp dir as opkg root for dummy installation
1421 temp_rootfs = self.d.expand('${T}/opkg')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001422 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001423 if opkg_lib_dir[0] == "/":
1424 opkg_lib_dir = opkg_lib_dir[1:]
1425 temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426 bb.utils.mkdirhier(temp_opkg_dir)
1427
1428 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001429 opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001430
1431 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1432 try:
1433 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1434 except subprocess.CalledProcessError as e:
1435 bb.fatal("Unable to update. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001437
1438 # Dummy installation
1439 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1440 opkg_args,
1441 ' '.join(pkgs))
1442 try:
1443 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1444 except subprocess.CalledProcessError as e:
1445 bb.fatal("Unable to dummy install packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001446 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001447
1448 bb.utils.remove(temp_rootfs, True)
1449
1450 return output
1451
1452 def backup_packaging_data(self):
1453 # Save the opkglib for increment ipk image generation
1454 if os.path.exists(self.saved_opkg_dir):
1455 bb.utils.remove(self.saved_opkg_dir, True)
1456 shutil.copytree(self.opkg_dir,
1457 self.saved_opkg_dir,
1458 symlinks=True)
1459
1460 def recover_packaging_data(self):
1461 # Move the opkglib back
1462 if os.path.exists(self.saved_opkg_dir):
1463 if os.path.exists(self.opkg_dir):
1464 bb.utils.remove(self.opkg_dir, True)
1465
1466 bb.note('Recover packaging data')
1467 shutil.copytree(self.saved_opkg_dir,
1468 self.opkg_dir,
1469 symlinks=True)
1470
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001471 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001472 """
1473 Returns a dictionary with the package info.
1474 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001475 cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
1476 pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001477
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001478 pkg_arch = pkg_info[pkg]["arch"]
1479 pkg_filename = pkg_info[pkg]["filename"]
1480 pkg_info[pkg]["filepath"] = \
1481 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1482
1483 return pkg_info
1484
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001485 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001486 """
1487 Returns the path to a tmpdir where resides the contents of a package.
1488
1489 Deleting the tmpdir is responsability of the caller.
1490 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001491 pkg_info = self.package_info(pkg)
1492 if not pkg_info:
1493 bb.fatal("Unable to get information for package '%s' while "
1494 "trying to extract the package." % pkg)
1495
1496 tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
Brad Bishop19323692019-04-05 15:28:33 -04001497 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001498
1499 return tmp_dir
1500
1501class DpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001502 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 -04001503 super(DpkgPM, self).__init__(d, target_rootfs)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001504 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), deb_repo_workdir)
1505
1506 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_DEB"), "package_write_deb", filterbydependencies)
1507
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001508 if apt_conf_dir is None:
1509 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1510 else:
1511 self.apt_conf_dir = apt_conf_dir
1512 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1513 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001514 self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001515
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001516 self.apt_args = d.getVar("APT_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001517
1518 self.all_arch_list = archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001519 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001520 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1521
1522 self._create_configs(archs, base_archs)
1523
1524 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1525
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001526 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001527 """
1528 This function will change a package's status in /var/lib/dpkg/status file.
1529 If 'packages' is None then the new_status will be applied to all
1530 packages
1531 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001532 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1533
1534 with open(status_file, "r") as sf:
1535 with open(status_file + ".tmp", "w+") as tmp_sf:
1536 if packages is None:
1537 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1538 r"Package: \1\n\2Status: \3%s" % status_tag,
1539 sf.read()))
1540 else:
1541 if type(packages).__name__ != "list":
1542 raise TypeError("'packages' should be a list object")
1543
1544 status = sf.read()
1545 for pkg in packages:
1546 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1547 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1548 status)
1549
1550 tmp_sf.write(status)
1551
1552 os.rename(status_file + ".tmp", status_file)
1553
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001554 def run_pre_post_installs(self, package_name=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001555 """
1556 Run the pre/post installs for package "package_name". If package_name is
1557 None, then run all pre/post install scriptlets.
1558 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001559 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001560 ControlScript = collections.namedtuple("ControlScript", ["suffix", "name", "argument"])
1561 control_scripts = [
1562 ControlScript(".preinst", "Preinstall", "install"),
1563 ControlScript(".postinst", "Postinstall", "configure")]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001564 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1565 installed_pkgs = []
1566
1567 with open(status_file, "r") as status:
1568 for line in status.read().split('\n'):
Brad Bishop19323692019-04-05 15:28:33 -04001569 m = re.match(r"^Package: (.*)", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001570 if m is not None:
1571 installed_pkgs.append(m.group(1))
1572
1573 if package_name is not None and not package_name in installed_pkgs:
1574 return
1575
1576 os.environ['D'] = self.target_rootfs
1577 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1578 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1579 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001580 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001581 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001582
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001583 for pkg_name in installed_pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001584 for control_script in control_scripts:
1585 p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001586 if os.path.exists(p_full):
1587 try:
1588 bb.note("Executing %s for package: %s ..." %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001589 (control_script.name.lower(), pkg_name))
1590 output = subprocess.check_output([p_full, control_script.argument],
1591 stderr=subprocess.STDOUT).decode("utf-8")
1592 bb.note(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001593 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001594 bb.warn("%s for package %s failed with %d:\n%s" %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001595 (control_script.name, pkg_name, e.returncode,
1596 e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001597 failed_postinsts_abort([pkg_name], self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001598
1599 def update(self):
1600 os.environ['APT_CONFIG'] = self.apt_conf_file
1601
1602 self.deploy_dir_lock()
1603
1604 cmd = "%s update" % self.apt_get_cmd
1605
1606 try:
1607 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1608 except subprocess.CalledProcessError as e:
1609 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001610 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001611
1612 self.deploy_dir_unlock()
1613
1614 def install(self, pkgs, attempt_only=False):
1615 if attempt_only and len(pkgs) == 0:
1616 return
1617
1618 os.environ['APT_CONFIG'] = self.apt_conf_file
1619
1620 cmd = "%s %s install --force-yes --allow-unauthenticated %s" % \
1621 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1622
1623 try:
1624 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1625 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1626 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001627 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001628 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001629 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001630
1631 # rename *.dpkg-new files/dirs
1632 for root, dirs, files in os.walk(self.target_rootfs):
1633 for dir in dirs:
Brad Bishop19323692019-04-05 15:28:33 -04001634 new_dir = re.sub(r"\.dpkg-new", "", dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001635 if dir != new_dir:
1636 os.rename(os.path.join(root, dir),
1637 os.path.join(root, new_dir))
1638
1639 for file in files:
Brad Bishop19323692019-04-05 15:28:33 -04001640 new_file = re.sub(r"\.dpkg-new", "", file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001641 if file != new_file:
1642 os.rename(os.path.join(root, file),
1643 os.path.join(root, new_file))
1644
1645
1646 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001647 if not pkgs:
1648 return
1649
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650 if with_dependencies:
1651 os.environ['APT_CONFIG'] = self.apt_conf_file
1652 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1653 else:
1654 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1655 " -P --force-depends %s" % \
1656 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1657 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1658
1659 try:
1660 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1661 except subprocess.CalledProcessError as e:
1662 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001663 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001664
1665 def write_index(self):
1666 self.deploy_dir_lock()
1667
1668 result = self.indexer.write_index()
1669
1670 self.deploy_dir_unlock()
1671
1672 if result is not None:
1673 bb.fatal(result)
1674
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001675 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1676 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001677 return
1678
1679 sources_conf = os.path.join("%s/etc/apt/sources.list"
1680 % self.target_rootfs)
1681 arch_list = []
1682
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001683 if feed_archs is None:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001684 for arch in self.all_arch_list:
1685 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1686 continue
1687 arch_list.append(arch)
1688 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001689 arch_list = feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001690
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001691 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001692
1693 with open(sources_conf, "w+") as sources_file:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001694 for uri in feed_uris:
1695 if arch_list:
1696 for arch in arch_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001697 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001698 sources_file.write("deb %s/%s ./\n" %
1699 (uri, arch))
1700 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001701 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001702 sources_file.write("deb %s ./\n" % uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001703
1704 def _create_configs(self, archs, base_archs):
Brad Bishop19323692019-04-05 15:28:33 -04001705 base_archs = re.sub(r"_", r"-", base_archs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001706
1707 if os.path.exists(self.apt_conf_dir):
1708 bb.utils.remove(self.apt_conf_dir, True)
1709
1710 bb.utils.mkdirhier(self.apt_conf_dir)
1711 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1712 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001713 bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001714
1715 arch_list = []
1716 for arch in self.all_arch_list:
1717 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1718 continue
1719 arch_list.append(arch)
1720
1721 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1722 priority = 801
1723 for arch in arch_list:
1724 prefs_file.write(
1725 "Package: *\n"
1726 "Pin: release l=%s\n"
1727 "Pin-Priority: %d\n\n" % (arch, priority))
1728
1729 priority += 5
1730
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001731 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001732 for pkg in pkg_exclude.split():
1733 prefs_file.write(
1734 "Package: %s\n"
1735 "Pin: release *\n"
1736 "Pin-Priority: -1\n\n" % pkg)
1737
1738 arch_list.reverse()
1739
1740 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1741 for arch in arch_list:
1742 sources_file.write("deb file:%s/ ./\n" %
1743 os.path.join(self.deploy_dir, arch))
1744
1745 base_arch_list = base_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001746 multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001747 for variant in multilib_variants.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001748 localdata = bb.data.createCopy(self.d)
1749 variant_tune = localdata.getVar("DEFAULTTUNE_virtclass-multilib-" + variant, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001750 orig_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001751 localdata.setVar("DEFAULTTUNE", variant_tune)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001752 variant_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001753 if variant_arch not in base_arch_list:
1754 base_arch_list.append(variant_arch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001755
1756 with open(self.apt_conf_file, "w+") as apt_conf:
1757 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1758 for line in apt_conf_sample.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -04001759 match_arch = re.match(r" Architecture \".*\";$", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001760 architectures = ""
1761 if match_arch:
1762 for base_arch in base_arch_list:
1763 architectures += "\"%s\";" % base_arch
1764 apt_conf.write(" Architectures {%s};\n" % architectures);
1765 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1766 else:
Brad Bishop19323692019-04-05 15:28:33 -04001767 line = re.sub(r"#ROOTFS#", self.target_rootfs, line)
1768 line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001769 apt_conf.write(line + "\n")
1770
1771 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1772 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1773
1774 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1775
1776 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1777 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1778 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1779 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1780
1781 def remove_packaging_data(self):
1782 bb.utils.remove(os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001783 self.d.getVar('opkglibdir')), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001784 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1785
1786 def fix_broken_dependencies(self):
1787 os.environ['APT_CONFIG'] = self.apt_conf_file
1788
1789 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1790
1791 try:
1792 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1793 except subprocess.CalledProcessError as e:
1794 bb.fatal("Cannot fix broken dependencies. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001795 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001796
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001797 def list_installed(self):
1798 return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001799
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001800 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001801 """
1802 Returns a dictionary with the package info.
1803 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001804 cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
1805 pkg_info = super(DpkgPM, self).package_info(pkg, cmd)
1806
1807 pkg_arch = pkg_info[pkg]["pkgarch"]
1808 pkg_filename = pkg_info[pkg]["filename"]
1809 pkg_info[pkg]["filepath"] = \
1810 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1811
1812 return pkg_info
1813
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001814 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001815 """
1816 Returns the path to a tmpdir where resides the contents of a package.
1817
1818 Deleting the tmpdir is responsability of the caller.
1819 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001820 pkg_info = self.package_info(pkg)
1821 if not pkg_info:
1822 bb.fatal("Unable to get information for package '%s' while "
1823 "trying to extract the package." % pkg)
1824
1825 tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info)
1826 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
1827
1828 return tmp_dir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001829
1830def generate_index_files(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001831 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001832
1833 indexer_map = {
Brad Bishop316dfdd2018-06-25 12:45:53 -04001834 "rpm": (RpmSubdirIndexer, d.getVar('DEPLOY_DIR_RPM')),
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001835 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK')),
1836 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001837 }
1838
1839 result = None
1840
1841 for pkg_class in classes:
1842 if not pkg_class in indexer_map:
1843 continue
1844
1845 if os.path.exists(indexer_map[pkg_class][1]):
1846 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1847
1848 if result is not None:
1849 bb.fatal(result)