blob: 882e7c429fd060938f34e5152eb4946605472c74 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001from abc import ABCMeta, abstractmethod
2import os
3import glob
4import subprocess
5import shutil
Patrick Williamsc124f4f2015-09-15 14:41:29 -05006import re
Patrick Williamsc0f7c042017-02-23 20:41:17 -06007import collections
Patrick Williamsc124f4f2015-09-15 14:41:29 -05008import bb
9import tempfile
10import oe.utils
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011import oe.path
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050012import string
13from oe.gpg_sign import get_signer
Brad Bishop316dfdd2018-06-25 12:45:53 -040014import hashlib
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080015import fnmatch
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016
17# this can be used by all PM backends to create the index files in parallel
18def create_index(arg):
19 index_cmd = arg
20
Brad Bishopd7bf8c12018-02-25 22:55:05 -050021 bb.note("Executing '%s' ..." % index_cmd)
22 result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023 if result:
24 bb.note(result)
25
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026def opkg_query(cmd_output):
Brad Bishop316dfdd2018-06-25 12:45:53 -040027 """
28 This method parse the output from the package managerand return
29 a dictionary with the information of the packages. This is used
30 when the packages are in deb or ipk format.
31 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -060032 verregex = re.compile(' \([=<>]* [^ )]*\)')
33 output = dict()
34 pkg = ""
35 arch = ""
36 ver = ""
37 filename = ""
38 dep = []
39 pkgarch = ""
40 for line in cmd_output.splitlines():
41 line = line.rstrip()
42 if ':' in line:
43 if line.startswith("Package: "):
44 pkg = line.split(": ")[1]
45 elif line.startswith("Architecture: "):
46 arch = line.split(": ")[1]
47 elif line.startswith("Version: "):
48 ver = line.split(": ")[1]
49 elif line.startswith("File: ") or line.startswith("Filename:"):
50 filename = line.split(": ")[1]
51 if "/" in filename:
52 filename = os.path.basename(filename)
53 elif line.startswith("Depends: "):
54 depends = verregex.sub('', line.split(": ")[1])
55 for depend in depends.split(", "):
56 dep.append(depend)
57 elif line.startswith("Recommends: "):
58 recommends = verregex.sub('', line.split(": ")[1])
59 for recommend in recommends.split(", "):
60 dep.append("%s [REC]" % recommend)
61 elif line.startswith("PackageArch: "):
62 pkgarch = line.split(": ")[1]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050063
Patrick Williamsc0f7c042017-02-23 20:41:17 -060064 # When there is a blank line save the package information
65 elif not line:
66 # IPK doesn't include the filename
67 if not filename:
68 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
69 if pkg:
70 output[pkg] = {"arch":arch, "ver":ver,
71 "filename":filename, "deps": dep, "pkgarch":pkgarch }
72 pkg = ""
73 arch = ""
74 ver = ""
75 filename = ""
76 dep = []
77 pkgarch = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 if pkg:
80 if not filename:
81 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
82 output[pkg] = {"arch":arch, "ver":ver,
83 "filename":filename, "deps": dep }
84
85 return output
86
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080087def failed_postinsts_abort(pkgs, log_path):
88 bb.fatal("""Postinstall scriptlets of %s have failed. If the intention is to defer them to first boot,
89then please place them into pkg_postinst_ontarget_${PN} ().
90Deferring to first boot via 'exit 1' is no longer supported.
Brad Bishop316dfdd2018-06-25 12:45:53 -040091Details of the failure are in %s.""" %(pkgs, log_path))
Patrick Williamsc0f7c042017-02-23 20:41:17 -060092
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080093def generate_locale_archive(d, rootfs, target_arch, localedir):
94 # Pretty sure we don't need this for locale archive generation but
95 # keeping it to be safe...
96 locale_arch_options = { \
97 "arm": ["--uint32-align=4", "--little-endian"],
98 "armeb": ["--uint32-align=4", "--big-endian"],
99 "aarch64": ["--uint32-align=4", "--little-endian"],
100 "aarch64_be": ["--uint32-align=4", "--big-endian"],
101 "sh4": ["--uint32-align=4", "--big-endian"],
102 "powerpc": ["--uint32-align=4", "--big-endian"],
103 "powerpc64": ["--uint32-align=4", "--big-endian"],
104 "mips": ["--uint32-align=4", "--big-endian"],
105 "mipsisa32r6": ["--uint32-align=4", "--big-endian"],
106 "mips64": ["--uint32-align=4", "--big-endian"],
107 "mipsisa64r6": ["--uint32-align=4", "--big-endian"],
108 "mipsel": ["--uint32-align=4", "--little-endian"],
109 "mipsisa32r6el": ["--uint32-align=4", "--little-endian"],
110 "mips64el": ["--uint32-align=4", "--little-endian"],
111 "mipsisa64r6el": ["--uint32-align=4", "--little-endian"],
112 "riscv64": ["--uint32-align=4", "--little-endian"],
113 "riscv32": ["--uint32-align=4", "--little-endian"],
114 "i586": ["--uint32-align=4", "--little-endian"],
115 "i686": ["--uint32-align=4", "--little-endian"],
116 "x86_64": ["--uint32-align=4", "--little-endian"]
117 }
118 if target_arch in locale_arch_options:
119 arch_options = locale_arch_options[target_arch]
120 else:
121 bb.error("locale_arch_options not found for target_arch=" + target_arch)
122 bb.fatal("unknown arch:" + target_arch + " for locale_arch_options")
123
124 # Need to set this so cross-localedef knows where the archive is
125 env = dict(os.environ)
126 env["LOCALEARCHIVE"] = oe.path.join(localedir, "locale-archive")
127
128 for name in os.listdir(localedir):
129 path = os.path.join(localedir, name)
130 if os.path.isdir(path):
131 cmd = ["cross-localedef", "--verbose"]
132 cmd += arch_options
133 cmd += ["--add-to-archive", path]
134 subprocess.check_output(cmd, env=env, stderr=subprocess.STDOUT)
135
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600136class Indexer(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137 def __init__(self, d, deploy_dir):
138 self.d = d
139 self.deploy_dir = deploy_dir
140
141 @abstractmethod
142 def write_index(self):
143 pass
144
145
146class RpmIndexer(Indexer):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400148 self.do_write_index(self.deploy_dir)
149
150 def do_write_index(self, deploy_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500151 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500152 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
153 else:
154 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500156 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400157 result = create_index("%s --update -q %s" % (createrepo_c, deploy_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500158 if result:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500159 bb.fatal(result)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500161 # Sign repomd
162 if signer:
163 sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
164 is_ascii_sig = (sig_type.upper() != "BIN")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400165 signer.detach_sign(os.path.join(deploy_dir, 'repodata', 'repomd.xml'),
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500166 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
167 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
168 armor=is_ascii_sig)
169
Brad Bishop316dfdd2018-06-25 12:45:53 -0400170class RpmSubdirIndexer(RpmIndexer):
171 def write_index(self):
172 bb.note("Generating package index for %s" %(self.deploy_dir))
173 self.do_write_index(self.deploy_dir)
174 for entry in os.walk(self.deploy_dir):
175 if os.path.samefile(self.deploy_dir, entry[0]):
176 for dir in entry[1]:
177 if dir != 'repodata':
178 dir_path = oe.path.join(self.deploy_dir, dir)
179 bb.note("Generating package index for %s" %(dir_path))
180 self.do_write_index(dir_path)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500181
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500182class OpkgIndexer(Indexer):
183 def write_index(self):
184 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
185 "SDK_PACKAGE_ARCHS",
Brad Bishop316dfdd2018-06-25 12:45:53 -0400186 ]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500187
188 opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500189 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
190 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500191 else:
192 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500193
194 if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
195 open(os.path.join(self.deploy_dir, "Packages"), "w").close()
196
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500197 index_cmds = set()
198 index_sign_files = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199 for arch_var in arch_vars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500200 archs = self.d.getVar(arch_var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201 if archs is None:
202 continue
203
204 for arch in archs.split():
205 pkgs_dir = os.path.join(self.deploy_dir, arch)
206 pkgs_file = os.path.join(pkgs_dir, "Packages")
207
208 if not os.path.isdir(pkgs_dir):
209 continue
210
211 if not os.path.exists(pkgs_file):
212 open(pkgs_file, "w").close()
213
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500214 index_cmds.add('%s -r %s -p %s -m %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500215 (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
216
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500217 index_sign_files.add(pkgs_file)
218
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219 if len(index_cmds) == 0:
220 bb.note("There are no packages in %s!" % self.deploy_dir)
221 return
222
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800223 oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500225 if signer:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500226 feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500227 is_ascii_sig = (feed_sig_type.upper() != "BIN")
228 for f in index_sign_files:
229 signer.detach_sign(f,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500230 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
231 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500232 armor=is_ascii_sig)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500233
234
235class DpkgIndexer(Indexer):
236 def _create_configs(self):
237 bb.utils.mkdirhier(self.apt_conf_dir)
238 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
239 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
240 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
241
242 with open(os.path.join(self.apt_conf_dir, "preferences"),
243 "w") as prefs_file:
244 pass
245 with open(os.path.join(self.apt_conf_dir, "sources.list"),
246 "w+") as sources_file:
247 pass
248
249 with open(self.apt_conf_file, "w") as apt_conf:
250 with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
251 "apt", "apt.conf.sample")) as apt_conf_sample:
252 for line in apt_conf_sample.read().split("\n"):
253 line = re.sub("#ROOTFS#", "/dev/null", line)
254 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
255 apt_conf.write(line + "\n")
256
257 def write_index(self):
258 self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
259 "apt-ftparchive")
260 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
261 self._create_configs()
262
263 os.environ['APT_CONFIG'] = self.apt_conf_file
264
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500265 pkg_archs = self.d.getVar('PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266 if pkg_archs is not None:
267 arch_list = pkg_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500268 sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269 if sdk_pkg_archs is not None:
270 for a in sdk_pkg_archs.split():
271 if a not in pkg_archs:
272 arch_list.append(a)
273
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500274 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500275 arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
276
277 apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
278 gzip = bb.utils.which(os.getenv('PATH'), "gzip")
279
280 index_cmds = []
281 deb_dirs_found = False
282 for arch in arch_list:
283 arch_dir = os.path.join(self.deploy_dir, arch)
284 if not os.path.isdir(arch_dir):
285 continue
286
287 cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
288
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500289 cmd += "%s -fcn Packages > Packages.gz;" % gzip
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290
291 with open(os.path.join(arch_dir, "Release"), "w+") as release:
292 release.write("Label: %s\n" % arch)
293
294 cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
295
296 index_cmds.append(cmd)
297
298 deb_dirs_found = True
299
300 if not deb_dirs_found:
301 bb.note("There are no packages in %s" % self.deploy_dir)
302 return
303
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800304 oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500305 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500306 raise NotImplementedError('Package feed signing not implementd for dpkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307
308
309
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600310class PkgsList(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311 def __init__(self, d, rootfs_dir):
312 self.d = d
313 self.rootfs_dir = rootfs_dir
314
315 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500316 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 pass
318
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500319class RpmPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500320 def list_pkgs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500321 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR')).list_installed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500322
323class OpkgPkgsList(PkgsList):
324 def __init__(self, d, rootfs_dir, config_file):
325 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
326
327 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
328 self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500329 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500331 def list_pkgs(self, format=None):
332 cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500333
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500334 # opkg returns success even when it printed some
335 # "Collected errors:" report to stderr. Mixing stderr into
336 # stdout then leads to random failures later on when
337 # parsing the output. To avoid this we need to collect both
338 # output streams separately and check for empty stderr.
339 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
340 cmd_output, cmd_stderr = p.communicate()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600341 cmd_output = cmd_output.decode("utf-8")
342 cmd_stderr = cmd_stderr.decode("utf-8")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500343 if p.returncode or cmd_stderr:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500344 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500345 "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600347 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500348
349
350class DpkgPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500351
352 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500353 cmd = [bb.utils.which(os.getenv('PATH'), "dpkg-query"),
354 "--admindir=%s/var/lib/dpkg" % self.rootfs_dir,
355 "-W"]
356
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500357 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 -0500358
359 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600360 cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361 except subprocess.CalledProcessError as e:
362 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600363 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500364
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600365 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500366
367
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600368class PackageManager(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369 """
370 This is an abstract class. Do not instantiate this directly.
371 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500372
Brad Bishop316dfdd2018-06-25 12:45:53 -0400373 def __init__(self, d, target_rootfs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500374 self.d = d
Brad Bishop316dfdd2018-06-25 12:45:53 -0400375 self.target_rootfs = target_rootfs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376 self.deploy_dir = None
377 self.deploy_lock = None
Brad Bishop316dfdd2018-06-25 12:45:53 -0400378 self._initialize_intercepts()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500379
Brad Bishop316dfdd2018-06-25 12:45:53 -0400380 def _initialize_intercepts(self):
381 bb.note("Initializing intercept dir for %s" % self.target_rootfs)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400382 # As there might be more than one instance of PackageManager operating at the same time
383 # we need to isolate the intercept_scripts directories from each other,
384 # hence the ugly hash digest in dir name.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800385 self.intercepts_dir = os.path.join(self.d.getVar('WORKDIR'), "intercept_scripts-%s" %
386 (hashlib.sha256(self.target_rootfs.encode()).hexdigest()))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400387
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800388 postinst_intercepts = (self.d.getVar("POSTINST_INTERCEPTS") or "").split()
389 if not postinst_intercepts:
390 postinst_intercepts_path = self.d.getVar("POSTINST_INTERCEPTS_PATH")
391 if not postinst_intercepts_path:
392 postinst_intercepts_path = self.d.getVar("POSTINST_INTERCEPTS_DIR") or self.d.expand("${COREBASE}/scripts/postinst-intercepts")
393 postinst_intercepts = oe.path.which_wild('*', postinst_intercepts_path)
394
395 bb.debug(1, 'Collected intercepts:\n%s' % ''.join(' %s\n' % i for i in postinst_intercepts))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400396 bb.utils.remove(self.intercepts_dir, True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800397 bb.utils.mkdirhier(self.intercepts_dir)
398 for intercept in postinst_intercepts:
399 bb.utils.copyfile(intercept, os.path.join(self.intercepts_dir, os.path.basename(intercept)))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400400
401 @abstractmethod
402 def _handle_intercept_failure(self, failed_script):
403 pass
404
405 def _postpone_to_first_boot(self, postinst_intercept_hook):
406 with open(postinst_intercept_hook) as intercept:
407 registered_pkgs = None
408 for line in intercept.read().split("\n"):
409 m = re.match("^##PKGS:(.*)", line)
410 if m is not None:
411 registered_pkgs = m.group(1).strip()
412 break
413
414 if registered_pkgs is not None:
415 bb.note("If an image is being built, the postinstalls for the following packages "
416 "will be postponed for first boot: %s" %
417 registered_pkgs)
418
419 # call the backend dependent handler
420 self._handle_intercept_failure(registered_pkgs)
421
422
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800423 def run_intercepts(self, populate_sdk=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400424 intercepts_dir = self.intercepts_dir
425
426 bb.note("Running intercept scripts:")
427 os.environ['D'] = self.target_rootfs
428 os.environ['STAGING_DIR_NATIVE'] = self.d.getVar('STAGING_DIR_NATIVE')
429 for script in os.listdir(intercepts_dir):
430 script_full = os.path.join(intercepts_dir, script)
431
432 if script == "postinst_intercept" or not os.access(script_full, os.X_OK):
433 continue
434
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800435 # we do not want to run any multilib variant of this
436 if script.startswith("delay_to_first_boot"):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400437 self._postpone_to_first_boot(script_full)
438 continue
439
440 bb.note("> Executing %s intercept ..." % script)
441
442 try:
443 output = subprocess.check_output(script_full, stderr=subprocess.STDOUT)
444 if output: bb.note(output.decode("utf-8"))
445 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400446 bb.note("Exit code %d. Output:\n%s" % (e.returncode, e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800447 if populate_sdk == 'host':
448 bb.warn("The postinstall intercept hook '%s' failed, details in %s/log.do_%s" % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
449 elif populate_sdk == 'target':
450 if "qemuwrapper: qemu usermode is not supported" in e.output.decode("utf-8"):
451 bb.warn("The postinstall intercept hook '%s' could not be executed due to missing qemu usermode support, details in %s/log.do_%s"
452 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
453 else:
454 bb.fatal("The postinstall intercept hook '%s' failed, details in %s/log.do_%s" % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
455 else:
456 if "qemuwrapper: qemu usermode is not supported" in e.output.decode("utf-8"):
457 bb.note("The postinstall intercept hook '%s' could not be executed due to missing qemu usermode support, details in %s/log.do_%s"
458 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
459 self._postpone_to_first_boot(script_full)
460 else:
461 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 -0400462
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500463 @abstractmethod
464 def update(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400465 """
466 Update the package manager package database.
467 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468 pass
469
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500470 @abstractmethod
471 def install(self, pkgs, attempt_only=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400472 """
473 Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
474 True, installation failures are ignored.
475 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476 pass
477
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478 @abstractmethod
479 def remove(self, pkgs, with_dependencies=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400480 """
481 Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
482 is False, then any dependencies are left in place.
483 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500484 pass
485
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486 @abstractmethod
487 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400488 """
489 This function creates the index files
490 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 pass
492
493 @abstractmethod
494 def remove_packaging_data(self):
495 pass
496
497 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500498 def list_installed(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500499 pass
500
501 @abstractmethod
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500502 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400503 """
504 Returns the path to a tmpdir where resides the contents of a package.
505 Deleting the tmpdir is responsability of the caller.
506 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500507 pass
508
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500509 @abstractmethod
510 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400511 """
512 Add remote package feeds into repository manager configuration. The parameters
513 for the feeds are set by feed_uris, feed_base_paths and feed_archs.
514 See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS
515 for their description.
516 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517 pass
518
Brad Bishop00111322018-04-01 22:23:53 -0400519 def install_glob(self, globs, sdk=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400520 """
521 Install all packages that match a glob.
522 """
Brad Bishop00111322018-04-01 22:23:53 -0400523 # TODO don't have sdk here but have a property on the superclass
524 # (and respect in install_complementary)
525 if sdk:
526 pkgdatadir = self.d.expand("${TMPDIR}/pkgdata/${SDK_SYS}")
527 else:
528 pkgdatadir = self.d.getVar("PKGDATA_DIR")
529
530 try:
531 bb.note("Installing globbed packages...")
532 cmd = ["oe-pkgdata-util", "-p", pkgdatadir, "list-pkgs", globs]
533 pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
534 self.install(pkgs.split(), attempt_only=True)
535 except subprocess.CalledProcessError as e:
536 # Return code 1 means no packages matched
537 if e.returncode != 1:
538 bb.fatal("Could not compute globbed packages list. Command "
539 "'%s' returned %d:\n%s" %
540 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
541
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500542 def install_complementary(self, globs=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400543 """
544 Install complementary packages based upon the list of currently installed
545 packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
546 these packages, if they don't exist then no error will occur. Note: every
547 backend needs to call this function explicitly after the normal package
548 installation
549 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550 if globs is None:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500551 globs = self.d.getVar('IMAGE_INSTALL_COMPLEMENTARY')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552 split_linguas = set()
553
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500554 for translation in self.d.getVar('IMAGE_LINGUAS').split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555 split_linguas.add(translation)
556 split_linguas.add(translation.split('-')[0])
557
558 split_linguas = sorted(split_linguas)
559
560 for lang in split_linguas:
561 globs += " *-locale-%s" % lang
562
563 if globs is None:
564 return
565
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500566 # we need to write the list of installed packages to a file because the
567 # oe-pkgdata-util reads it from a file
568 with tempfile.NamedTemporaryFile(mode="w+", prefix="installed-pkgs") as installed_pkgs:
569 pkgs = self.list_installed()
570 output = oe.utils.format_pkg_list(pkgs, "arch")
571 installed_pkgs.write(output)
572 installed_pkgs.flush()
573
Brad Bishop00111322018-04-01 22:23:53 -0400574 cmd = ["oe-pkgdata-util",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500575 "-p", self.d.getVar('PKGDATA_DIR'), "glob", installed_pkgs.name,
576 globs]
577 exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY')
578 if exclude:
579 cmd.extend(['--exclude=' + '|'.join(exclude.split())])
580 try:
581 bb.note("Installing complementary packages ...")
582 bb.note('Running %s' % cmd)
583 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishop00111322018-04-01 22:23:53 -0400584 self.install(complementary_pkgs.split(), attempt_only=True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500585 except subprocess.CalledProcessError as e:
586 bb.fatal("Could not compute complementary packages list. Command "
587 "'%s' returned %d:\n%s" %
588 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500589
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800590 target_arch = self.d.getVar('TARGET_ARCH')
591 localedir = oe.path.join(self.target_rootfs, self.d.getVar("libdir"), "locale")
592 if os.path.exists(localedir) and os.listdir(localedir):
593 generate_locale_archive(self.d, self.target_rootfs, target_arch, localedir)
594 # And now delete the binary locales
595 self.remove(fnmatch.filter(self.list_installed(), "glibc-binary-localedata-*"), False)
596
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597 def deploy_dir_lock(self):
598 if self.deploy_dir is None:
599 raise RuntimeError("deploy_dir is not set!")
600
601 lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
602
603 self.deploy_lock = bb.utils.lockfile(lock_file_name)
604
605 def deploy_dir_unlock(self):
606 if self.deploy_lock is None:
607 return
608
609 bb.utils.unlockfile(self.deploy_lock)
610
611 self.deploy_lock = None
612
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500613 def construct_uris(self, uris, base_paths):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400614 """
615 Construct URIs based on the following pattern: uri/base_path where 'uri'
616 and 'base_path' correspond to each element of the corresponding array
617 argument leading to len(uris) x len(base_paths) elements on the returned
618 array
619 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500620 def _append(arr1, arr2, sep='/'):
621 res = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600622 narr1 = [a.rstrip(sep) for a in arr1]
623 narr2 = [a.rstrip(sep).lstrip(sep) for a in arr2]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500624 for a1 in narr1:
625 if arr2:
626 for a2 in narr2:
627 res.append("%s%s%s" % (a1, sep, a2))
628 else:
629 res.append(a1)
630 return res
631 return _append(uris, base_paths)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500632
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800633def create_packages_dir(d, subrepo_dir, deploydir, taskname, filterbydependencies):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400634 """
635 Go through our do_package_write_X dependencies and hardlink the packages we depend
636 upon into the repo directory. This prevents us seeing other packages that may
637 have been built that we don't depend upon and also packages for architectures we don't
638 support.
639 """
640 import errno
641
642 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
643 mytaskname = d.getVar("BB_RUNTASK")
644 pn = d.getVar("PN")
645 seendirs = set()
646 multilibs = {}
647
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800648 bb.utils.remove(subrepo_dir, recurse=True)
649 bb.utils.mkdirhier(subrepo_dir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400650
651 # Detect bitbake -b usage
652 nodeps = d.getVar("BB_LIMITEDDEPS") or False
653 if nodeps or not filterbydependencies:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800654 oe.path.symlink(deploydir, subrepo_dir, True)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400655 return
656
657 start = None
658 for dep in taskdepdata:
659 data = taskdepdata[dep]
660 if data[1] == mytaskname and data[0] == pn:
661 start = dep
662 break
663 if start is None:
664 bb.fatal("Couldn't find ourself in BB_TASKDEPDATA?")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800665 pkgdeps = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400666 start = [start]
667 seen = set(start)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800668 # Support direct dependencies (do_rootfs -> do_package_write_X)
669 # or indirect dependencies within PN (do_populate_sdk_ext -> do_rootfs -> do_package_write_X)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400670 while start:
671 next = []
672 for dep2 in start:
673 for dep in taskdepdata[dep2][3]:
674 if taskdepdata[dep][0] != pn:
675 if "do_" + taskname in dep:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800676 pkgdeps.add(dep)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400677 elif dep not in seen:
678 next.append(dep)
679 seen.add(dep)
680 start = next
681
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800682 for dep in pkgdeps:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400683 c = taskdepdata[dep][0]
684 manifest, d2 = oe.sstatesig.find_sstate_manifest(c, taskdepdata[dep][2], taskname, d, multilibs)
685 if not manifest:
686 bb.fatal("No manifest generated from: %s in %s" % (c, taskdepdata[dep][2]))
687 if not os.path.exists(manifest):
688 continue
689 with open(manifest, "r") as f:
690 for l in f:
691 l = l.strip()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800692 deploydir = os.path.normpath(deploydir)
693 if bb.data.inherits_class('packagefeed-stability', d):
694 dest = l.replace(deploydir + "-prediff", "")
695 else:
696 dest = l.replace(deploydir, "")
697 dest = subrepo_dir + dest
Brad Bishop316dfdd2018-06-25 12:45:53 -0400698 if l.endswith("/"):
699 if dest not in seendirs:
700 bb.utils.mkdirhier(dest)
701 seendirs.add(dest)
702 continue
703 # Try to hardlink the file, copy if that fails
704 destdir = os.path.dirname(dest)
705 if destdir not in seendirs:
706 bb.utils.mkdirhier(destdir)
707 seendirs.add(destdir)
708 try:
709 os.link(l, dest)
710 except OSError as err:
711 if err.errno == errno.EXDEV:
712 bb.utils.copyfile(l, dest)
713 else:
714 raise
715
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716class RpmPM(PackageManager):
717 def __init__(self,
718 d,
719 target_rootfs,
720 target_vendor,
721 task_name='target',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722 arch_var=None,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500723 os_var=None,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400724 rpm_repo_workdir="oe-rootfs-repo",
725 filterbydependencies=True):
726 super(RpmPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727 self.target_vendor = target_vendor
728 self.task_name = task_name
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500729 if arch_var == None:
730 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
731 else:
732 self.archs = self.d.getVar(arch_var).replace("-","_")
733 if task_name == "host":
734 self.primary_arch = self.d.getVar('SDK_ARCH')
735 else:
736 self.primary_arch = self.d.getVar('MACHINE_ARCH')
737
738 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800739 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 -0500740
741 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
742 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
743 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800744 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 -0500745 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
746 self.task_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747 if not os.path.exists(self.d.expand('${T}/saved')):
748 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
749
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500750 def _configure_dnf(self):
751 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
752 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
753 # This prevents accidental matching against libsolv's built-in policies
754 if len(archs) <= 1:
755 archs = archs + ["bogusarch"]
756 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
757 bb.utils.mkdirhier(confdir)
758 open(confdir + "arch", 'w').write(":".join(archs))
759 distro_codename = self.d.getVar('DISTRO_CODENAME')
760 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500761
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500762 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500763
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500765 def _configure_rpm(self):
766 # We need to configure rpm to use our primary package architecture as the installation architecture,
767 # and to make it compatible with other package architectures that we use.
768 # Otherwise it will refuse to proceed with packages installation.
769 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
770 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
771 bb.utils.mkdirhier(platformconfdir)
772 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800773 with open(rpmrcconfdir + "rpmrc", 'w') as f:
774 f.write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
775 f.write("buildarch_compat: %s: noarch\n" % self.primary_arch)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500776
777 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
778 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
779 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500780 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500781 open(platformconfdir + "macros", 'a').write("%_prefer_color 7")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500782
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500783 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
784 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
785 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
786 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
787 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
788 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
789 try:
790 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
791 except subprocess.CalledProcessError as e:
792 bb.fatal("Importing GPG key failed. Command '%s' "
793 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500794
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795 def create_configs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500796 self._configure_dnf()
797 self._configure_rpm()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500798
799 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500800 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
801 lf = bb.utils.lockfile(lockfilename, False)
802 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
803 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500805 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
806 from urllib.parse import urlparse
807
808 if feed_uris == "":
809 return
810
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500811 gpg_opts = ''
812 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
813 gpg_opts += 'repo_gpgcheck=1\n'
814 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'))
815
Brad Bishop316dfdd2018-06-25 12:45:53 -0400816 if self.d.getVar('RPM_SIGN_PACKAGES') != '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500817 gpg_opts += 'gpgcheck=0\n'
818
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500819 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
820 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
821 for uri in remote_uris:
822 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
823 if feed_archs is not None:
824 for arch in feed_archs.split():
825 repo_uri = uri + "/" + arch
826 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
827 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
828 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500829 "[%s]\nname=%s\nbaseurl=%s\n%s\n" % (repo_id, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500830 else:
831 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
832 repo_uri = uri
833 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500834 "[%s]\nname=%s\nbaseurl=%s\n%s" % (repo_base, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500835
836 def _prepare_pkg_transaction(self):
837 os.environ['D'] = self.target_rootfs
838 os.environ['OFFLINE_ROOT'] = self.target_rootfs
839 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
840 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -0400841 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500842 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
843
844
845 def install(self, pkgs, attempt_only = False):
846 if len(pkgs) == 0:
847 return
848 self._prepare_pkg_transaction()
849
850 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
851 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
852 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
853
854 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
855 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
856 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
857 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
858 ["install"] +
859 pkgs)
860
861 failed_scriptlets_pkgnames = collections.OrderedDict()
862 for line in output.splitlines():
863 if line.startswith("Non-fatal POSTIN scriptlet failure in rpm package"):
864 failed_scriptlets_pkgnames[line.split()[-1]] = True
865
Brad Bishop316dfdd2018-06-25 12:45:53 -0400866 if len(failed_scriptlets_pkgnames) > 0:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800867 failed_postinsts_abort(list(failed_scriptlets_pkgnames.keys()), self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500868
869 def remove(self, pkgs, with_dependencies = True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800870 if not pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500871 return
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800872
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500873 self._prepare_pkg_transaction()
874
875 if with_dependencies:
876 self._invoke_dnf(["remove"] + pkgs)
877 else:
878 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500879 args = ["-e", "-v", "--nodeps", "--root=%s" %self.target_rootfs]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500880
881 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500882 bb.note("Running %s" % ' '.join([cmd] + args + pkgs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500883 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500884 bb.note(output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500885 except subprocess.CalledProcessError as e:
886 bb.fatal("Could not invoke rpm. Command "
887 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
888
889 def upgrade(self):
890 self._prepare_pkg_transaction()
891 self._invoke_dnf(["upgrade"])
892
893 def autoremove(self):
894 self._prepare_pkg_transaction()
895 self._invoke_dnf(["autoremove"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896
897 def remove_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500898 self._invoke_dnf(["clean", "all"])
899 for dir in self.packaging_data_dirs:
900 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901
902 def backup_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500903 # Save the packaging dirs for increment rpm image generation
904 if os.path.exists(self.saved_packaging_data):
905 bb.utils.remove(self.saved_packaging_data, True)
906 for i in self.packaging_data_dirs:
907 source_dir = oe.path.join(self.target_rootfs, i)
908 target_dir = oe.path.join(self.saved_packaging_data, i)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800909 if os.path.isdir(source_dir):
910 shutil.copytree(source_dir, target_dir, symlinks=True)
911 elif os.path.isfile(source_dir):
912 shutil.copy2(source_dir, target_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500913
914 def recovery_packaging_data(self):
915 # Move the rpmlib back
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500916 if os.path.exists(self.saved_packaging_data):
917 for i in self.packaging_data_dirs:
918 target_dir = oe.path.join(self.target_rootfs, i)
919 if os.path.exists(target_dir):
920 bb.utils.remove(target_dir, True)
921 source_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
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500927 def list_installed(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500928 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
929 print_output = False)
930 packages = {}
931 current_package = None
932 current_deps = None
933 current_state = "initial"
934 for line in output.splitlines():
935 if line.startswith("Package:"):
936 package_info = line.split(" ")[1:]
937 current_package = package_info[0]
938 package_arch = package_info[1]
939 package_version = package_info[2]
940 package_rpm = package_info[3]
941 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
942 current_deps = []
943 elif line.startswith("Dependencies:"):
944 current_state = "dependencies"
945 elif line.startswith("Recommendations"):
946 current_state = "recommendations"
947 elif line.startswith("DependenciesEndHere:"):
948 current_state = "initial"
949 packages[current_package]["deps"] = current_deps
950 elif len(line) > 0:
951 if current_state == "dependencies":
952 current_deps.append(line)
953 elif current_state == "recommendations":
954 current_deps.append("%s [REC]" % line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500956 return packages
957
958 def update(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500959 self._invoke_dnf(["makecache", "--refresh"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500960
961 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
962 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
963
964 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800965 standard_dnf_args = ["-v", "--rpmverbosity=debug", "-y",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500966 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
967 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
968 "--repofrompath=oe-repo,%s" % (self.rpm_repo_dir),
969 "--installroot=%s" % (self.target_rootfs),
970 "--setopt=logdir=%s" % (self.d.getVar('T'))
971 ]
972 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
Brad Bishop316dfdd2018-06-25 12:45:53 -0400973 bb.note('Running %s' % ' '.join(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500974 try:
975 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
976 if print_output:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800977 bb.debug(1, output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500978 return output
979 except subprocess.CalledProcessError as e:
980 if print_output:
981 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
982 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
983 else:
984 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
985 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
986 return e.output.decode("utf-8")
987
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500988 def dump_install_solution(self, pkgs):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500989 open(self.solution_manifest, 'w').write(" ".join(pkgs))
990 return pkgs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500991
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500992 def load_old_install_solution(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500993 if not os.path.exists(self.solution_manifest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500994 return []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500995
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500996 return open(self.solution_manifest, 'r').read().split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500997
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500998 def _script_num_prefix(self, path):
999 files = os.listdir(path)
1000 numbers = set()
1001 numbers.add(99)
1002 for f in files:
1003 numbers.add(int(f.split("-")[0]))
1004 return max(numbers) + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005
1006 def save_rpmpostinst(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001007 bb.note("Saving postinstall script of %s" % (pkg))
1008 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
1009 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001010
1011 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001012 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001013 except subprocess.CalledProcessError as e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001014 bb.fatal("Could not invoke rpm. Command "
1015 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001017 # may need to prepend #!/bin/sh to output
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001018
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001019 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
1020 bb.utils.mkdirhier(target_path)
1021 num = self._script_num_prefix(target_path)
1022 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
1023 open(saved_script_name, 'w').write(output)
1024 os.chmod(saved_script_name, 0o755)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001025
Brad Bishop316dfdd2018-06-25 12:45:53 -04001026 def _handle_intercept_failure(self, registered_pkgs):
1027 rpm_postinsts_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
1028 bb.utils.mkdirhier(rpm_postinsts_dir)
1029
1030 # Save the package postinstalls in /etc/rpm-postinsts
1031 for pkg in registered_pkgs.split():
1032 self.save_rpmpostinst(pkg)
1033
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001034 def extract(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001035 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
1036 pkg_name = output.splitlines()[-1]
1037 if not pkg_name.endswith(".rpm"):
1038 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
1039 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001040
1041 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
1042 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
1043
1044 if not os.path.isfile(pkg_path):
1045 bb.fatal("Unable to extract package for '%s'."
1046 "File %s doesn't exists" % (pkg, pkg_path))
1047
1048 tmp_dir = tempfile.mkdtemp()
1049 current_dir = os.getcwd()
1050 os.chdir(tmp_dir)
1051
1052 try:
1053 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
1054 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1055 except subprocess.CalledProcessError as e:
1056 bb.utils.remove(tmp_dir, recurse=True)
1057 bb.fatal("Unable to extract %s package. Command '%s' "
1058 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
1059 except OSError as e:
1060 bb.utils.remove(tmp_dir, recurse=True)
1061 bb.fatal("Unable to extract %s package. Command '%s' "
1062 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
1063
1064 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1065 os.chdir(current_dir)
1066
1067 return tmp_dir
1068
1069
1070class OpkgDpkgPM(PackageManager):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001071 def __init__(self, d, target_rootfs):
1072 """
1073 This is an abstract class. Do not instantiate this directly.
1074 """
1075 super(OpkgDpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001076
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001077 def package_info(self, pkg, cmd):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001078 """
1079 Returns a dictionary with the package info.
1080
1081 This method extracts the common parts for Opkg and Dpkg
1082 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001083
1084 try:
1085 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
1086 except subprocess.CalledProcessError as e:
1087 bb.fatal("Unable to list available packages. Command '%s' "
1088 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
1089 return opkg_query(output)
1090
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001091 def extract(self, pkg, pkg_info):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001092 """
1093 Returns the path to a tmpdir where resides the contents of a package.
1094
1095 Deleting the tmpdir is responsability of the caller.
1096
1097 This method extracts the common parts for Opkg and Dpkg
1098 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001099
1100 ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
1101 tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
1102 pkg_path = pkg_info[pkg]["filepath"]
1103
1104 if not os.path.isfile(pkg_path):
1105 bb.fatal("Unable to extract package for '%s'."
1106 "File %s doesn't exists" % (pkg, pkg_path))
1107
1108 tmp_dir = tempfile.mkdtemp()
1109 current_dir = os.getcwd()
1110 os.chdir(tmp_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001111 if self.d.getVar('IMAGE_PKGTYPE') == 'deb':
1112 data_tar = 'data.tar.xz'
1113 else:
1114 data_tar = 'data.tar.gz'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001115
1116 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001117 cmd = [ar_cmd, 'x', pkg_path]
1118 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
1119 cmd = [tar_cmd, 'xf', data_tar]
1120 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001121 except subprocess.CalledProcessError as e:
1122 bb.utils.remove(tmp_dir, recurse=True)
1123 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001124 "returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001125 except OSError as e:
1126 bb.utils.remove(tmp_dir, recurse=True)
1127 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001128 "returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129
1130 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1131 bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
1132 bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
1133 os.chdir(current_dir)
1134
1135 return tmp_dir
1136
Brad Bishop316dfdd2018-06-25 12:45:53 -04001137 def _handle_intercept_failure(self, registered_pkgs):
1138 self.mark_packages("unpacked", registered_pkgs.split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001139
1140class OpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001141 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 -04001142 super(OpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001143
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001144 self.config_file = config_file
1145 self.pkg_archs = archs
1146 self.task_name = task_name
1147
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001148 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), ipk_repo_workdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001149 self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
1150 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001151 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 -05001152 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001153
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001154 if prepare_index:
1155 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_IPK"), "package_write_ipk", filterbydependencies)
1156
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001157 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001158 if opkg_lib_dir[0] == "/":
1159 opkg_lib_dir = opkg_lib_dir[1:]
1160
1161 self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
1162
1163 bb.utils.mkdirhier(self.opkg_dir)
1164
1165 self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
1166 if not os.path.exists(self.d.expand('${T}/saved')):
1167 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
1168
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001169 self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001170 if self.from_feeds:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001171 self._create_custom_config()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001172 else:
1173 self._create_config()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001174
1175 self.indexer = OpkgIndexer(self.d, self.deploy_dir)
1176
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001177 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001178 """
1179 This function will change a package's status in /var/lib/opkg/status file.
1180 If 'packages' is None then the new_status will be applied to all
1181 packages
1182 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001183 status_file = os.path.join(self.opkg_dir, "status")
1184
1185 with open(status_file, "r") as sf:
1186 with open(status_file + ".tmp", "w+") as tmp_sf:
1187 if packages is None:
1188 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1189 r"Package: \1\n\2Status: \3%s" % status_tag,
1190 sf.read()))
1191 else:
1192 if type(packages).__name__ != "list":
1193 raise TypeError("'packages' should be a list object")
1194
1195 status = sf.read()
1196 for pkg in packages:
1197 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1198 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1199 status)
1200
1201 tmp_sf.write(status)
1202
1203 os.rename(status_file + ".tmp", status_file)
1204
1205 def _create_custom_config(self):
1206 bb.note("Building from feeds activated!")
1207
1208 with open(self.config_file, "w+") as config_file:
1209 priority = 1
1210 for arch in self.pkg_archs.split():
1211 config_file.write("arch %s %d\n" % (arch, priority))
1212 priority += 5
1213
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001214 for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001215 feed_match = re.match("^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
1216
1217 if feed_match is not None:
1218 feed_name = feed_match.group(1)
1219 feed_uri = feed_match.group(2)
1220
1221 bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
1222
1223 config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
1224
1225 """
1226 Allow to use package deploy directory contents as quick devel-testing
1227 feed. This creates individual feed configs for each arch subdir of those
1228 specified as compatible for the current machine.
1229 NOTE: Development-helper feature, NOT a full-fledged feed.
1230 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232 for arch in self.pkg_archs.split():
1233 cfg_file_name = os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001234 self.d.getVar("sysconfdir"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001235 "opkg",
1236 "local-%s-feed.conf" % arch)
1237
1238 with open(cfg_file_name, "w+") as cfg_file:
1239 cfg_file.write("src/gz local-%s %s/%s" %
1240 (arch,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001241 self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 arch))
1243
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001244 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001245 # There is no command line option for this anymore, we need to add
1246 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1247 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001248 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1249 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1250 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001251 cfg_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1252 cfg_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1253 cfg_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001254
1255
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001256 def _create_config(self):
1257 with open(self.config_file, "w+") as config_file:
1258 priority = 1
1259 for arch in self.pkg_archs.split():
1260 config_file.write("arch %s %d\n" % (arch, priority))
1261 priority += 5
1262
1263 config_file.write("src oe file:%s\n" % self.deploy_dir)
1264
1265 for arch in self.pkg_archs.split():
1266 pkgs_dir = os.path.join(self.deploy_dir, arch)
1267 if os.path.isdir(pkgs_dir):
1268 config_file.write("src oe-%s file:%s\n" %
1269 (arch, pkgs_dir))
1270
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001271 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001272 # There is no command line option for this anymore, we need to add
1273 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1274 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001275 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1276 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1277 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001278 config_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1279 config_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1280 config_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001281
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001282 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1283 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001284 return
1285
1286 rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
1287 % self.target_rootfs)
1288
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001289 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
1290 archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001291
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001292 with open(rootfs_config, "w+") as config_file:
1293 uri_iterator = 0
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001294 for uri in feed_uris:
1295 if archs:
1296 for arch in archs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001297 if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001298 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001299 bb.note('Adding opkg feed url-%s-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001300 (arch, uri_iterator, uri))
1301 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1302 (arch, uri_iterator, uri, arch))
1303 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001304 bb.note('Adding opkg feed url-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001305 (uri_iterator, uri))
1306 config_file.write("src/gz uri-%d %s\n" %
1307 (uri_iterator, uri))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001308
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001309 uri_iterator += 1
1310
1311 def update(self):
1312 self.deploy_dir_lock()
1313
1314 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1315
1316 try:
1317 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1318 except subprocess.CalledProcessError as e:
1319 self.deploy_dir_unlock()
1320 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001321 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001322
1323 self.deploy_dir_unlock()
1324
1325 def install(self, pkgs, attempt_only=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001326 if not pkgs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001327 return
1328
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001329 cmd = "%s %s" % (self.opkg_cmd, self.opkg_args)
1330 for exclude in (self.d.getVar("PACKAGE_EXCLUDE") or "").split():
1331 cmd += " --add-exclude %s" % exclude
1332 cmd += " install "
1333 cmd += " ".join(pkgs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334
1335 os.environ['D'] = self.target_rootfs
1336 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1337 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1338 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001339 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001340 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341
1342 try:
1343 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1344 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001345 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001346 bb.note(output)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001347 failed_pkgs = []
1348 for line in output.split('\n'):
1349 if line.endswith("configuration required on target."):
1350 bb.warn(line)
1351 failed_pkgs.append(line.split(".")[0])
1352 if failed_pkgs:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001353 failed_postinsts_abort(failed_pkgs, self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001354 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001355 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001356 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001357 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001358
1359 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001360 if not pkgs:
1361 return
1362
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001363 if with_dependencies:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001364 cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1366 else:
1367 cmd = "%s %s --force-depends remove %s" % \
1368 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1369
1370 try:
1371 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001372 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001373 bb.note(output)
1374 except subprocess.CalledProcessError as e:
1375 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001377
1378 def write_index(self):
1379 self.deploy_dir_lock()
1380
1381 result = self.indexer.write_index()
1382
1383 self.deploy_dir_unlock()
1384
1385 if result is not None:
1386 bb.fatal(result)
1387
1388 def remove_packaging_data(self):
1389 bb.utils.remove(self.opkg_dir, True)
1390 # create the directory back, it's needed by PM lock
1391 bb.utils.mkdirhier(self.opkg_dir)
1392
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001393 def remove_lists(self):
1394 if not self.from_feeds:
1395 bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
1396
1397 def list_installed(self):
1398 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001399
1400 def handle_bad_recommendations(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001401 bad_recommendations = self.d.getVar("BAD_RECOMMENDATIONS") or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402 if bad_recommendations.strip() == "":
1403 return
1404
1405 status_file = os.path.join(self.opkg_dir, "status")
1406
1407 # If status file existed, it means the bad recommendations has already
1408 # been handled
1409 if os.path.exists(status_file):
1410 return
1411
1412 cmd = "%s %s info " % (self.opkg_cmd, self.opkg_args)
1413
1414 with open(status_file, "w+") as status:
1415 for pkg in bad_recommendations.split():
1416 pkg_info = cmd + pkg
1417
1418 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001419 output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001420 except subprocess.CalledProcessError as e:
1421 bb.fatal("Cannot get package info. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001422 "returned %d:\n%s" % (pkg_info, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001423
1424 if output == "":
1425 bb.note("Ignored bad recommendation: '%s' is "
1426 "not a package" % pkg)
1427 continue
1428
1429 for line in output.split('\n'):
1430 if line.startswith("Status:"):
1431 status.write("Status: deinstall hold not-installed\n")
1432 else:
1433 status.write(line + "\n")
1434
1435 # Append a blank line after each package entry to ensure that it
1436 # is separated from the following entry
1437 status.write("\n")
1438
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001439 def dummy_install(self, pkgs):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001440 """
1441 The following function dummy installs pkgs and returns the log of output.
1442 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001443 if len(pkgs) == 0:
1444 return
1445
1446 # Create an temp dir as opkg root for dummy installation
1447 temp_rootfs = self.d.expand('${T}/opkg')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001448 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001449 if opkg_lib_dir[0] == "/":
1450 opkg_lib_dir = opkg_lib_dir[1:]
1451 temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001452 bb.utils.mkdirhier(temp_opkg_dir)
1453
1454 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001455 opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001456
1457 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1458 try:
1459 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1460 except subprocess.CalledProcessError as e:
1461 bb.fatal("Unable to update. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001462 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001463
1464 # Dummy installation
1465 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1466 opkg_args,
1467 ' '.join(pkgs))
1468 try:
1469 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1470 except subprocess.CalledProcessError as e:
1471 bb.fatal("Unable to dummy install packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001472 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001473
1474 bb.utils.remove(temp_rootfs, True)
1475
1476 return output
1477
1478 def backup_packaging_data(self):
1479 # Save the opkglib for increment ipk image generation
1480 if os.path.exists(self.saved_opkg_dir):
1481 bb.utils.remove(self.saved_opkg_dir, True)
1482 shutil.copytree(self.opkg_dir,
1483 self.saved_opkg_dir,
1484 symlinks=True)
1485
1486 def recover_packaging_data(self):
1487 # Move the opkglib back
1488 if os.path.exists(self.saved_opkg_dir):
1489 if os.path.exists(self.opkg_dir):
1490 bb.utils.remove(self.opkg_dir, True)
1491
1492 bb.note('Recover packaging data')
1493 shutil.copytree(self.saved_opkg_dir,
1494 self.opkg_dir,
1495 symlinks=True)
1496
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001497 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001498 """
1499 Returns a dictionary with the package info.
1500 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001501 cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
1502 pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001503
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001504 pkg_arch = pkg_info[pkg]["arch"]
1505 pkg_filename = pkg_info[pkg]["filename"]
1506 pkg_info[pkg]["filepath"] = \
1507 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1508
1509 return pkg_info
1510
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001511 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001512 """
1513 Returns the path to a tmpdir where resides the contents of a package.
1514
1515 Deleting the tmpdir is responsability of the caller.
1516 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001517 pkg_info = self.package_info(pkg)
1518 if not pkg_info:
1519 bb.fatal("Unable to get information for package '%s' while "
1520 "trying to extract the package." % pkg)
1521
1522 tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
1523 bb.utils.remove(os.path.join(tmp_dir, "data.tar.gz"))
1524
1525 return tmp_dir
1526
1527class DpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001528 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 -04001529 super(DpkgPM, self).__init__(d, target_rootfs)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001530 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), deb_repo_workdir)
1531
1532 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_DEB"), "package_write_deb", filterbydependencies)
1533
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534 if apt_conf_dir is None:
1535 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1536 else:
1537 self.apt_conf_dir = apt_conf_dir
1538 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1539 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001540 self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001541
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001542 self.apt_args = d.getVar("APT_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001543
1544 self.all_arch_list = archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001545 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001546 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1547
1548 self._create_configs(archs, base_archs)
1549
1550 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1551
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001552 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001553 """
1554 This function will change a package's status in /var/lib/dpkg/status file.
1555 If 'packages' is None then the new_status will be applied to all
1556 packages
1557 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001558 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1559
1560 with open(status_file, "r") as sf:
1561 with open(status_file + ".tmp", "w+") as tmp_sf:
1562 if packages is None:
1563 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1564 r"Package: \1\n\2Status: \3%s" % status_tag,
1565 sf.read()))
1566 else:
1567 if type(packages).__name__ != "list":
1568 raise TypeError("'packages' should be a list object")
1569
1570 status = sf.read()
1571 for pkg in packages:
1572 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1573 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1574 status)
1575
1576 tmp_sf.write(status)
1577
1578 os.rename(status_file + ".tmp", status_file)
1579
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001580 def run_pre_post_installs(self, package_name=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001581 """
1582 Run the pre/post installs for package "package_name". If package_name is
1583 None, then run all pre/post install scriptlets.
1584 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001585 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001586 ControlScript = collections.namedtuple("ControlScript", ["suffix", "name", "argument"])
1587 control_scripts = [
1588 ControlScript(".preinst", "Preinstall", "install"),
1589 ControlScript(".postinst", "Postinstall", "configure")]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001590 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1591 installed_pkgs = []
1592
1593 with open(status_file, "r") as status:
1594 for line in status.read().split('\n'):
1595 m = re.match("^Package: (.*)", line)
1596 if m is not None:
1597 installed_pkgs.append(m.group(1))
1598
1599 if package_name is not None and not package_name in installed_pkgs:
1600 return
1601
1602 os.environ['D'] = self.target_rootfs
1603 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1604 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1605 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001606 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001607 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001608
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001609 for pkg_name in installed_pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001610 for control_script in control_scripts:
1611 p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001612 if os.path.exists(p_full):
1613 try:
1614 bb.note("Executing %s for package: %s ..." %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001615 (control_script.name.lower(), pkg_name))
1616 output = subprocess.check_output([p_full, control_script.argument],
1617 stderr=subprocess.STDOUT).decode("utf-8")
1618 bb.note(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001619 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001620 bb.warn("%s for package %s failed with %d:\n%s" %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001621 (control_script.name, pkg_name, e.returncode,
1622 e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001623 failed_postinsts_abort([pkg_name], self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001624
1625 def update(self):
1626 os.environ['APT_CONFIG'] = self.apt_conf_file
1627
1628 self.deploy_dir_lock()
1629
1630 cmd = "%s update" % self.apt_get_cmd
1631
1632 try:
1633 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1634 except subprocess.CalledProcessError as e:
1635 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001636 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001637
1638 self.deploy_dir_unlock()
1639
1640 def install(self, pkgs, attempt_only=False):
1641 if attempt_only and len(pkgs) == 0:
1642 return
1643
1644 os.environ['APT_CONFIG'] = self.apt_conf_file
1645
1646 cmd = "%s %s install --force-yes --allow-unauthenticated %s" % \
1647 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1648
1649 try:
1650 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1651 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1652 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001653 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001654 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001655 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001656
1657 # rename *.dpkg-new files/dirs
1658 for root, dirs, files in os.walk(self.target_rootfs):
1659 for dir in dirs:
1660 new_dir = re.sub("\.dpkg-new", "", dir)
1661 if dir != new_dir:
1662 os.rename(os.path.join(root, dir),
1663 os.path.join(root, new_dir))
1664
1665 for file in files:
1666 new_file = re.sub("\.dpkg-new", "", file)
1667 if file != new_file:
1668 os.rename(os.path.join(root, file),
1669 os.path.join(root, new_file))
1670
1671
1672 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001673 if not pkgs:
1674 return
1675
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001676 if with_dependencies:
1677 os.environ['APT_CONFIG'] = self.apt_conf_file
1678 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1679 else:
1680 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1681 " -P --force-depends %s" % \
1682 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1683 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1684
1685 try:
1686 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1687 except subprocess.CalledProcessError as e:
1688 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001689 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001690
1691 def write_index(self):
1692 self.deploy_dir_lock()
1693
1694 result = self.indexer.write_index()
1695
1696 self.deploy_dir_unlock()
1697
1698 if result is not None:
1699 bb.fatal(result)
1700
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001701 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1702 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001703 return
1704
1705 sources_conf = os.path.join("%s/etc/apt/sources.list"
1706 % self.target_rootfs)
1707 arch_list = []
1708
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001709 if feed_archs is None:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001710 for arch in self.all_arch_list:
1711 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1712 continue
1713 arch_list.append(arch)
1714 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001715 arch_list = feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001716
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001717 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001718
1719 with open(sources_conf, "w+") as sources_file:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001720 for uri in feed_uris:
1721 if arch_list:
1722 for arch in arch_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001723 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001724 sources_file.write("deb %s/%s ./\n" %
1725 (uri, arch))
1726 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001728 sources_file.write("deb %s ./\n" % uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001729
1730 def _create_configs(self, archs, base_archs):
1731 base_archs = re.sub("_", "-", base_archs)
1732
1733 if os.path.exists(self.apt_conf_dir):
1734 bb.utils.remove(self.apt_conf_dir, True)
1735
1736 bb.utils.mkdirhier(self.apt_conf_dir)
1737 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1738 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001739 bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001740
1741 arch_list = []
1742 for arch in self.all_arch_list:
1743 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1744 continue
1745 arch_list.append(arch)
1746
1747 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1748 priority = 801
1749 for arch in arch_list:
1750 prefs_file.write(
1751 "Package: *\n"
1752 "Pin: release l=%s\n"
1753 "Pin-Priority: %d\n\n" % (arch, priority))
1754
1755 priority += 5
1756
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001757 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001758 for pkg in pkg_exclude.split():
1759 prefs_file.write(
1760 "Package: %s\n"
1761 "Pin: release *\n"
1762 "Pin-Priority: -1\n\n" % pkg)
1763
1764 arch_list.reverse()
1765
1766 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1767 for arch in arch_list:
1768 sources_file.write("deb file:%s/ ./\n" %
1769 os.path.join(self.deploy_dir, arch))
1770
1771 base_arch_list = base_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001772 multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001773 for variant in multilib_variants.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001774 localdata = bb.data.createCopy(self.d)
1775 variant_tune = localdata.getVar("DEFAULTTUNE_virtclass-multilib-" + variant, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001776 orig_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001777 localdata.setVar("DEFAULTTUNE", variant_tune)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001778 variant_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001779 if variant_arch not in base_arch_list:
1780 base_arch_list.append(variant_arch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001781
1782 with open(self.apt_conf_file, "w+") as apt_conf:
1783 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1784 for line in apt_conf_sample.read().split("\n"):
1785 match_arch = re.match(" Architecture \".*\";$", line)
1786 architectures = ""
1787 if match_arch:
1788 for base_arch in base_arch_list:
1789 architectures += "\"%s\";" % base_arch
1790 apt_conf.write(" Architectures {%s};\n" % architectures);
1791 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1792 else:
1793 line = re.sub("#ROOTFS#", self.target_rootfs, line)
1794 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
1795 apt_conf.write(line + "\n")
1796
1797 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1798 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1799
1800 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1801
1802 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1803 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1804 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1805 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1806
1807 def remove_packaging_data(self):
1808 bb.utils.remove(os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001809 self.d.getVar('opkglibdir')), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001810 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1811
1812 def fix_broken_dependencies(self):
1813 os.environ['APT_CONFIG'] = self.apt_conf_file
1814
1815 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1816
1817 try:
1818 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1819 except subprocess.CalledProcessError as e:
1820 bb.fatal("Cannot fix broken dependencies. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001821 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001822
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001823 def list_installed(self):
1824 return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001825
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001826 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001827 """
1828 Returns a dictionary with the package info.
1829 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001830 cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
1831 pkg_info = super(DpkgPM, self).package_info(pkg, cmd)
1832
1833 pkg_arch = pkg_info[pkg]["pkgarch"]
1834 pkg_filename = pkg_info[pkg]["filename"]
1835 pkg_info[pkg]["filepath"] = \
1836 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1837
1838 return pkg_info
1839
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001840 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001841 """
1842 Returns the path to a tmpdir where resides the contents of a package.
1843
1844 Deleting the tmpdir is responsability of the caller.
1845 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001846 pkg_info = self.package_info(pkg)
1847 if not pkg_info:
1848 bb.fatal("Unable to get information for package '%s' while "
1849 "trying to extract the package." % pkg)
1850
1851 tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info)
1852 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
1853
1854 return tmp_dir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001855
1856def generate_index_files(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001857 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001858
1859 indexer_map = {
Brad Bishop316dfdd2018-06-25 12:45:53 -04001860 "rpm": (RpmSubdirIndexer, d.getVar('DEPLOY_DIR_RPM')),
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001861 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK')),
1862 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001863 }
1864
1865 result = None
1866
1867 for pkg_class in classes:
1868 if not pkg_class in indexer_map:
1869 continue
1870
1871 if os.path.exists(indexer_map[pkg_class][1]):
1872 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1873
1874 if result is not None:
1875 bb.fatal(result)