blob: 7c373715adf9ae3c69ec23a93d5e3e54dd958abb [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 Bishop96ff1982019-08-19 13:50:42 -04001300 os.makedirs('%s/etc/opkg' % self.target_rootfs, exist_ok=True)
1301
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001302 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
1303 archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001304
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305 with open(rootfs_config, "w+") as config_file:
1306 uri_iterator = 0
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001307 for uri in feed_uris:
1308 if archs:
1309 for arch in archs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310 if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001311 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001312 bb.note('Adding opkg feed url-%s-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001313 (arch, uri_iterator, uri))
1314 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1315 (arch, uri_iterator, uri, arch))
1316 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001317 bb.note('Adding opkg feed url-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001318 (uri_iterator, uri))
1319 config_file.write("src/gz uri-%d %s\n" %
1320 (uri_iterator, uri))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001321
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322 uri_iterator += 1
1323
1324 def update(self):
1325 self.deploy_dir_lock()
1326
1327 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1328
1329 try:
1330 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1331 except subprocess.CalledProcessError as e:
1332 self.deploy_dir_unlock()
1333 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001334 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001335
1336 self.deploy_dir_unlock()
1337
1338 def install(self, pkgs, attempt_only=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001339 if not pkgs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001340 return
1341
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001342 cmd = "%s %s" % (self.opkg_cmd, self.opkg_args)
1343 for exclude in (self.d.getVar("PACKAGE_EXCLUDE") or "").split():
1344 cmd += " --add-exclude %s" % exclude
Brad Bishop19323692019-04-05 15:28:33 -04001345 for bad_recommendation in (self.d.getVar("BAD_RECOMMENDATIONS") or "").split():
1346 cmd += " --add-ignore-recommends %s" % bad_recommendation
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001347 cmd += " install "
1348 cmd += " ".join(pkgs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001349
1350 os.environ['D'] = self.target_rootfs
1351 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1352 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1353 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001354 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001355 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356
1357 try:
1358 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1359 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001360 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001361 bb.note(output)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001362 failed_pkgs = []
1363 for line in output.split('\n'):
1364 if line.endswith("configuration required on target."):
1365 bb.warn(line)
1366 failed_pkgs.append(line.split(".")[0])
1367 if failed_pkgs:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001368 failed_postinsts_abort(failed_pkgs, self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001369 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001370 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001371 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001372 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001373
1374 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001375 if not pkgs:
1376 return
1377
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001378 if with_dependencies:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001379 cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001380 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1381 else:
1382 cmd = "%s %s --force-depends remove %s" % \
1383 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1384
1385 try:
1386 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001387 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001388 bb.note(output)
1389 except subprocess.CalledProcessError as e:
1390 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001391 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001392
1393 def write_index(self):
1394 self.deploy_dir_lock()
1395
1396 result = self.indexer.write_index()
1397
1398 self.deploy_dir_unlock()
1399
1400 if result is not None:
1401 bb.fatal(result)
1402
1403 def remove_packaging_data(self):
1404 bb.utils.remove(self.opkg_dir, True)
1405 # create the directory back, it's needed by PM lock
1406 bb.utils.mkdirhier(self.opkg_dir)
1407
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001408 def remove_lists(self):
1409 if not self.from_feeds:
1410 bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
1411
1412 def list_installed(self):
1413 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001414
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001415 def dummy_install(self, pkgs):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001416 """
1417 The following function dummy installs pkgs and returns the log of output.
1418 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001419 if len(pkgs) == 0:
1420 return
1421
1422 # Create an temp dir as opkg root for dummy installation
1423 temp_rootfs = self.d.expand('${T}/opkg')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001424 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001425 if opkg_lib_dir[0] == "/":
1426 opkg_lib_dir = opkg_lib_dir[1:]
1427 temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001428 bb.utils.mkdirhier(temp_opkg_dir)
1429
1430 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001431 opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001432
1433 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1434 try:
1435 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1436 except subprocess.CalledProcessError as e:
1437 bb.fatal("Unable to update. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001438 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001439
1440 # Dummy installation
1441 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1442 opkg_args,
1443 ' '.join(pkgs))
1444 try:
1445 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1446 except subprocess.CalledProcessError as e:
1447 bb.fatal("Unable to dummy install packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001448 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001449
1450 bb.utils.remove(temp_rootfs, True)
1451
1452 return output
1453
1454 def backup_packaging_data(self):
1455 # Save the opkglib for increment ipk image generation
1456 if os.path.exists(self.saved_opkg_dir):
1457 bb.utils.remove(self.saved_opkg_dir, True)
1458 shutil.copytree(self.opkg_dir,
1459 self.saved_opkg_dir,
1460 symlinks=True)
1461
1462 def recover_packaging_data(self):
1463 # Move the opkglib back
1464 if os.path.exists(self.saved_opkg_dir):
1465 if os.path.exists(self.opkg_dir):
1466 bb.utils.remove(self.opkg_dir, True)
1467
1468 bb.note('Recover packaging data')
1469 shutil.copytree(self.saved_opkg_dir,
1470 self.opkg_dir,
1471 symlinks=True)
1472
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001473 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001474 """
1475 Returns a dictionary with the package info.
1476 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001477 cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
1478 pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001479
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480 pkg_arch = pkg_info[pkg]["arch"]
1481 pkg_filename = pkg_info[pkg]["filename"]
1482 pkg_info[pkg]["filepath"] = \
1483 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1484
1485 return pkg_info
1486
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001487 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001488 """
1489 Returns the path to a tmpdir where resides the contents of a package.
1490
1491 Deleting the tmpdir is responsability of the caller.
1492 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001493 pkg_info = self.package_info(pkg)
1494 if not pkg_info:
1495 bb.fatal("Unable to get information for package '%s' while "
1496 "trying to extract the package." % pkg)
1497
1498 tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
Brad Bishop19323692019-04-05 15:28:33 -04001499 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001500
1501 return tmp_dir
1502
1503class DpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001504 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 -04001505 super(DpkgPM, self).__init__(d, target_rootfs)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001506 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), deb_repo_workdir)
1507
1508 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_DEB"), "package_write_deb", filterbydependencies)
1509
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001510 if apt_conf_dir is None:
1511 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1512 else:
1513 self.apt_conf_dir = apt_conf_dir
1514 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1515 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001516 self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001517
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001518 self.apt_args = d.getVar("APT_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001519
1520 self.all_arch_list = archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001521 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1523
1524 self._create_configs(archs, base_archs)
1525
1526 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1527
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001528 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001529 """
1530 This function will change a package's status in /var/lib/dpkg/status file.
1531 If 'packages' is None then the new_status will be applied to all
1532 packages
1533 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1535
1536 with open(status_file, "r") as sf:
1537 with open(status_file + ".tmp", "w+") as tmp_sf:
1538 if packages is None:
1539 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1540 r"Package: \1\n\2Status: \3%s" % status_tag,
1541 sf.read()))
1542 else:
1543 if type(packages).__name__ != "list":
1544 raise TypeError("'packages' should be a list object")
1545
1546 status = sf.read()
1547 for pkg in packages:
1548 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1549 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1550 status)
1551
1552 tmp_sf.write(status)
1553
1554 os.rename(status_file + ".tmp", status_file)
1555
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001556 def run_pre_post_installs(self, package_name=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001557 """
1558 Run the pre/post installs for package "package_name". If package_name is
1559 None, then run all pre/post install scriptlets.
1560 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001561 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001562 ControlScript = collections.namedtuple("ControlScript", ["suffix", "name", "argument"])
1563 control_scripts = [
1564 ControlScript(".preinst", "Preinstall", "install"),
1565 ControlScript(".postinst", "Postinstall", "configure")]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001566 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1567 installed_pkgs = []
1568
1569 with open(status_file, "r") as status:
1570 for line in status.read().split('\n'):
Brad Bishop19323692019-04-05 15:28:33 -04001571 m = re.match(r"^Package: (.*)", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001572 if m is not None:
1573 installed_pkgs.append(m.group(1))
1574
1575 if package_name is not None and not package_name in installed_pkgs:
1576 return
1577
1578 os.environ['D'] = self.target_rootfs
1579 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1580 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1581 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001582 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001583 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001584
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001585 for pkg_name in installed_pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001586 for control_script in control_scripts:
1587 p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001588 if os.path.exists(p_full):
1589 try:
1590 bb.note("Executing %s for package: %s ..." %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001591 (control_script.name.lower(), pkg_name))
1592 output = subprocess.check_output([p_full, control_script.argument],
1593 stderr=subprocess.STDOUT).decode("utf-8")
1594 bb.note(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001595 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001596 bb.warn("%s for package %s failed with %d:\n%s" %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001597 (control_script.name, pkg_name, e.returncode,
1598 e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001599 failed_postinsts_abort([pkg_name], self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600
1601 def update(self):
1602 os.environ['APT_CONFIG'] = self.apt_conf_file
1603
1604 self.deploy_dir_lock()
1605
1606 cmd = "%s update" % self.apt_get_cmd
1607
1608 try:
1609 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1610 except subprocess.CalledProcessError as e:
1611 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001612 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001613
1614 self.deploy_dir_unlock()
1615
1616 def install(self, pkgs, attempt_only=False):
1617 if attempt_only and len(pkgs) == 0:
1618 return
1619
1620 os.environ['APT_CONFIG'] = self.apt_conf_file
1621
1622 cmd = "%s %s install --force-yes --allow-unauthenticated %s" % \
1623 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1624
1625 try:
1626 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1627 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1628 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001629 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001630 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001631 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001632
1633 # rename *.dpkg-new files/dirs
1634 for root, dirs, files in os.walk(self.target_rootfs):
1635 for dir in dirs:
Brad Bishop19323692019-04-05 15:28:33 -04001636 new_dir = re.sub(r"\.dpkg-new", "", dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001637 if dir != new_dir:
1638 os.rename(os.path.join(root, dir),
1639 os.path.join(root, new_dir))
1640
1641 for file in files:
Brad Bishop19323692019-04-05 15:28:33 -04001642 new_file = re.sub(r"\.dpkg-new", "", file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001643 if file != new_file:
1644 os.rename(os.path.join(root, file),
1645 os.path.join(root, new_file))
1646
1647
1648 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001649 if not pkgs:
1650 return
1651
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001652 if with_dependencies:
1653 os.environ['APT_CONFIG'] = self.apt_conf_file
1654 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1655 else:
1656 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1657 " -P --force-depends %s" % \
1658 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1659 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1660
1661 try:
1662 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1663 except subprocess.CalledProcessError as e:
1664 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001665 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001666
1667 def write_index(self):
1668 self.deploy_dir_lock()
1669
1670 result = self.indexer.write_index()
1671
1672 self.deploy_dir_unlock()
1673
1674 if result is not None:
1675 bb.fatal(result)
1676
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001677 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1678 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001679 return
1680
1681 sources_conf = os.path.join("%s/etc/apt/sources.list"
1682 % self.target_rootfs)
1683 arch_list = []
1684
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001685 if feed_archs is None:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001686 for arch in self.all_arch_list:
1687 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1688 continue
1689 arch_list.append(arch)
1690 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001691 arch_list = feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001692
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001693 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001694
1695 with open(sources_conf, "w+") as sources_file:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001696 for uri in feed_uris:
1697 if arch_list:
1698 for arch in arch_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001699 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001700 sources_file.write("deb %s/%s ./\n" %
1701 (uri, arch))
1702 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001703 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001704 sources_file.write("deb %s ./\n" % uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001705
1706 def _create_configs(self, archs, base_archs):
Brad Bishop19323692019-04-05 15:28:33 -04001707 base_archs = re.sub(r"_", r"-", base_archs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001708
1709 if os.path.exists(self.apt_conf_dir):
1710 bb.utils.remove(self.apt_conf_dir, True)
1711
1712 bb.utils.mkdirhier(self.apt_conf_dir)
1713 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1714 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001715 bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001716
1717 arch_list = []
1718 for arch in self.all_arch_list:
1719 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1720 continue
1721 arch_list.append(arch)
1722
1723 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1724 priority = 801
1725 for arch in arch_list:
1726 prefs_file.write(
1727 "Package: *\n"
1728 "Pin: release l=%s\n"
1729 "Pin-Priority: %d\n\n" % (arch, priority))
1730
1731 priority += 5
1732
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001733 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001734 for pkg in pkg_exclude.split():
1735 prefs_file.write(
1736 "Package: %s\n"
1737 "Pin: release *\n"
1738 "Pin-Priority: -1\n\n" % pkg)
1739
1740 arch_list.reverse()
1741
1742 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1743 for arch in arch_list:
1744 sources_file.write("deb file:%s/ ./\n" %
1745 os.path.join(self.deploy_dir, arch))
1746
1747 base_arch_list = base_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001748 multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001749 for variant in multilib_variants.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001750 localdata = bb.data.createCopy(self.d)
1751 variant_tune = localdata.getVar("DEFAULTTUNE_virtclass-multilib-" + variant, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001752 orig_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001753 localdata.setVar("DEFAULTTUNE", variant_tune)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001754 variant_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001755 if variant_arch not in base_arch_list:
1756 base_arch_list.append(variant_arch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001757
1758 with open(self.apt_conf_file, "w+") as apt_conf:
1759 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1760 for line in apt_conf_sample.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -04001761 match_arch = re.match(r" Architecture \".*\";$", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001762 architectures = ""
1763 if match_arch:
1764 for base_arch in base_arch_list:
1765 architectures += "\"%s\";" % base_arch
1766 apt_conf.write(" Architectures {%s};\n" % architectures);
1767 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1768 else:
Brad Bishop19323692019-04-05 15:28:33 -04001769 line = re.sub(r"#ROOTFS#", self.target_rootfs, line)
1770 line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001771 apt_conf.write(line + "\n")
1772
1773 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1774 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1775
1776 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1777
1778 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1779 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1780 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1781 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1782
1783 def remove_packaging_data(self):
1784 bb.utils.remove(os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001785 self.d.getVar('opkglibdir')), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001786 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1787
1788 def fix_broken_dependencies(self):
1789 os.environ['APT_CONFIG'] = self.apt_conf_file
1790
1791 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1792
1793 try:
1794 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1795 except subprocess.CalledProcessError as e:
1796 bb.fatal("Cannot fix broken dependencies. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001797 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001798
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001799 def list_installed(self):
1800 return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001801
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001802 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001803 """
1804 Returns a dictionary with the package info.
1805 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001806 cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
1807 pkg_info = super(DpkgPM, self).package_info(pkg, cmd)
1808
1809 pkg_arch = pkg_info[pkg]["pkgarch"]
1810 pkg_filename = pkg_info[pkg]["filename"]
1811 pkg_info[pkg]["filepath"] = \
1812 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1813
1814 return pkg_info
1815
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001816 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001817 """
1818 Returns the path to a tmpdir where resides the contents of a package.
1819
1820 Deleting the tmpdir is responsability of the caller.
1821 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001822 pkg_info = self.package_info(pkg)
1823 if not pkg_info:
1824 bb.fatal("Unable to get information for package '%s' while "
1825 "trying to extract the package." % pkg)
1826
1827 tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info)
1828 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
1829
1830 return tmp_dir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001831
1832def generate_index_files(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001833 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001834
1835 indexer_map = {
Brad Bishop316dfdd2018-06-25 12:45:53 -04001836 "rpm": (RpmSubdirIndexer, d.getVar('DEPLOY_DIR_RPM')),
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001837 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK')),
1838 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001839 }
1840
1841 result = None
1842
1843 for pkg_class in classes:
1844 if not pkg_class in indexer_map:
1845 continue
1846
1847 if os.path.exists(indexer_map[pkg_class][1]):
1848 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1849
1850 if result is not None:
1851 bb.fatal(result)