blob: 2835c1ddf8e1539fd5b3c284c52d9d0675d4cfe4 [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 """
Brad Bishop19323692019-04-05 15:28:33 -040032 verregex = re.compile(r' \([=<>]* [^ )]*\)')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060033 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 = { \
Brad Bishop19323692019-04-05 15:28:33 -040097 "arc": ["--uint32-align=4", "--little-endian"],
98 "arceb": ["--uint32-align=4", "--big-endian"],
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080099 "arm": ["--uint32-align=4", "--little-endian"],
100 "armeb": ["--uint32-align=4", "--big-endian"],
101 "aarch64": ["--uint32-align=4", "--little-endian"],
102 "aarch64_be": ["--uint32-align=4", "--big-endian"],
103 "sh4": ["--uint32-align=4", "--big-endian"],
104 "powerpc": ["--uint32-align=4", "--big-endian"],
105 "powerpc64": ["--uint32-align=4", "--big-endian"],
106 "mips": ["--uint32-align=4", "--big-endian"],
107 "mipsisa32r6": ["--uint32-align=4", "--big-endian"],
108 "mips64": ["--uint32-align=4", "--big-endian"],
109 "mipsisa64r6": ["--uint32-align=4", "--big-endian"],
110 "mipsel": ["--uint32-align=4", "--little-endian"],
111 "mipsisa32r6el": ["--uint32-align=4", "--little-endian"],
112 "mips64el": ["--uint32-align=4", "--little-endian"],
113 "mipsisa64r6el": ["--uint32-align=4", "--little-endian"],
114 "riscv64": ["--uint32-align=4", "--little-endian"],
115 "riscv32": ["--uint32-align=4", "--little-endian"],
116 "i586": ["--uint32-align=4", "--little-endian"],
117 "i686": ["--uint32-align=4", "--little-endian"],
118 "x86_64": ["--uint32-align=4", "--little-endian"]
119 }
120 if target_arch in locale_arch_options:
121 arch_options = locale_arch_options[target_arch]
122 else:
123 bb.error("locale_arch_options not found for target_arch=" + target_arch)
124 bb.fatal("unknown arch:" + target_arch + " for locale_arch_options")
125
126 # Need to set this so cross-localedef knows where the archive is
127 env = dict(os.environ)
128 env["LOCALEARCHIVE"] = oe.path.join(localedir, "locale-archive")
129
130 for name in os.listdir(localedir):
131 path = os.path.join(localedir, name)
132 if os.path.isdir(path):
133 cmd = ["cross-localedef", "--verbose"]
134 cmd += arch_options
135 cmd += ["--add-to-archive", path]
136 subprocess.check_output(cmd, env=env, stderr=subprocess.STDOUT)
137
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600138class Indexer(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 def __init__(self, d, deploy_dir):
140 self.d = d
141 self.deploy_dir = deploy_dir
142
143 @abstractmethod
144 def write_index(self):
145 pass
146
147
148class RpmIndexer(Indexer):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500149 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400150 self.do_write_index(self.deploy_dir)
151
152 def do_write_index(self, deploy_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500153 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500154 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
155 else:
156 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500157
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500158 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400159 result = create_index("%s --update -q %s" % (createrepo_c, deploy_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160 if result:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500161 bb.fatal(result)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500163 # Sign repomd
164 if signer:
165 sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
166 is_ascii_sig = (sig_type.upper() != "BIN")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400167 signer.detach_sign(os.path.join(deploy_dir, 'repodata', 'repomd.xml'),
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500168 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
169 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
170 armor=is_ascii_sig)
171
Brad Bishop316dfdd2018-06-25 12:45:53 -0400172class RpmSubdirIndexer(RpmIndexer):
173 def write_index(self):
174 bb.note("Generating package index for %s" %(self.deploy_dir))
175 self.do_write_index(self.deploy_dir)
176 for entry in os.walk(self.deploy_dir):
177 if os.path.samefile(self.deploy_dir, entry[0]):
178 for dir in entry[1]:
179 if dir != 'repodata':
180 dir_path = oe.path.join(self.deploy_dir, dir)
181 bb.note("Generating package index for %s" %(dir_path))
182 self.do_write_index(dir_path)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500183
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500184class OpkgIndexer(Indexer):
185 def write_index(self):
186 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
187 "SDK_PACKAGE_ARCHS",
Brad Bishop316dfdd2018-06-25 12:45:53 -0400188 ]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500189
190 opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500191 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
192 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500193 else:
194 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195
196 if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
197 open(os.path.join(self.deploy_dir, "Packages"), "w").close()
198
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500199 index_cmds = set()
200 index_sign_files = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201 for arch_var in arch_vars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500202 archs = self.d.getVar(arch_var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500203 if archs is None:
204 continue
205
206 for arch in archs.split():
207 pkgs_dir = os.path.join(self.deploy_dir, arch)
208 pkgs_file = os.path.join(pkgs_dir, "Packages")
209
210 if not os.path.isdir(pkgs_dir):
211 continue
212
213 if not os.path.exists(pkgs_file):
214 open(pkgs_file, "w").close()
215
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500216 index_cmds.add('%s -r %s -p %s -m %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500217 (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
218
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500219 index_sign_files.add(pkgs_file)
220
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221 if len(index_cmds) == 0:
222 bb.note("There are no packages in %s!" % self.deploy_dir)
223 return
224
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800225 oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500226
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500227 if signer:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500228 feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500229 is_ascii_sig = (feed_sig_type.upper() != "BIN")
230 for f in index_sign_files:
231 signer.detach_sign(f,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500232 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
233 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500234 armor=is_ascii_sig)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235
236
237class DpkgIndexer(Indexer):
238 def _create_configs(self):
239 bb.utils.mkdirhier(self.apt_conf_dir)
240 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
241 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
242 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
243
244 with open(os.path.join(self.apt_conf_dir, "preferences"),
245 "w") as prefs_file:
246 pass
247 with open(os.path.join(self.apt_conf_dir, "sources.list"),
248 "w+") as sources_file:
249 pass
250
251 with open(self.apt_conf_file, "w") as apt_conf:
252 with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
253 "apt", "apt.conf.sample")) as apt_conf_sample:
254 for line in apt_conf_sample.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -0400255 line = re.sub(r"#ROOTFS#", "/dev/null", line)
256 line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500257 apt_conf.write(line + "\n")
258
259 def write_index(self):
260 self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
261 "apt-ftparchive")
262 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
263 self._create_configs()
264
265 os.environ['APT_CONFIG'] = self.apt_conf_file
266
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500267 pkg_archs = self.d.getVar('PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268 if pkg_archs is not None:
269 arch_list = pkg_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500270 sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271 if sdk_pkg_archs is not None:
272 for a in sdk_pkg_archs.split():
273 if a not in pkg_archs:
274 arch_list.append(a)
275
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500276 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277 arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
278
279 apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
280 gzip = bb.utils.which(os.getenv('PATH'), "gzip")
281
282 index_cmds = []
283 deb_dirs_found = False
284 for arch in arch_list:
285 arch_dir = os.path.join(self.deploy_dir, arch)
286 if not os.path.isdir(arch_dir):
287 continue
288
289 cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
290
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500291 cmd += "%s -fcn Packages > Packages.gz;" % gzip
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500292
293 with open(os.path.join(arch_dir, "Release"), "w+") as release:
294 release.write("Label: %s\n" % arch)
295
296 cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
297
298 index_cmds.append(cmd)
299
300 deb_dirs_found = True
301
302 if not deb_dirs_found:
303 bb.note("There are no packages in %s" % self.deploy_dir)
304 return
305
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800306 oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500307 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500308 raise NotImplementedError('Package feed signing not implementd for dpkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500309
310
311
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600312class PkgsList(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313 def __init__(self, d, rootfs_dir):
314 self.d = d
315 self.rootfs_dir = rootfs_dir
316
317 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500318 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500319 pass
320
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321class RpmPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500322 def list_pkgs(self):
Brad Bishop19323692019-04-05 15:28:33 -0400323 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR'), needfeed=False).list_installed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324
325class OpkgPkgsList(PkgsList):
326 def __init__(self, d, rootfs_dir, config_file):
327 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
328
329 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
330 self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500331 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500332
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500333 def list_pkgs(self, format=None):
334 cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500336 # opkg returns success even when it printed some
337 # "Collected errors:" report to stderr. Mixing stderr into
338 # stdout then leads to random failures later on when
339 # parsing the output. To avoid this we need to collect both
340 # output streams separately and check for empty stderr.
341 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
342 cmd_output, cmd_stderr = p.communicate()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600343 cmd_output = cmd_output.decode("utf-8")
344 cmd_stderr = cmd_stderr.decode("utf-8")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500345 if p.returncode or cmd_stderr:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500347 "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500348
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600349 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500350
351
352class DpkgPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500353
354 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355 cmd = [bb.utils.which(os.getenv('PATH'), "dpkg-query"),
356 "--admindir=%s/var/lib/dpkg" % self.rootfs_dir,
357 "-W"]
358
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500359 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 -0500360
361 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600362 cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500363 except subprocess.CalledProcessError as e:
364 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600365 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500366
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500368
369
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600370class PackageManager(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371 """
372 This is an abstract class. Do not instantiate this directly.
373 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500374
Brad Bishop316dfdd2018-06-25 12:45:53 -0400375 def __init__(self, d, target_rootfs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376 self.d = d
Brad Bishop316dfdd2018-06-25 12:45:53 -0400377 self.target_rootfs = target_rootfs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500378 self.deploy_dir = None
379 self.deploy_lock = None
Brad Bishop316dfdd2018-06-25 12:45:53 -0400380 self._initialize_intercepts()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500381
Brad Bishop316dfdd2018-06-25 12:45:53 -0400382 def _initialize_intercepts(self):
383 bb.note("Initializing intercept dir for %s" % self.target_rootfs)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400384 # As there might be more than one instance of PackageManager operating at the same time
385 # we need to isolate the intercept_scripts directories from each other,
386 # hence the ugly hash digest in dir name.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800387 self.intercepts_dir = os.path.join(self.d.getVar('WORKDIR'), "intercept_scripts-%s" %
388 (hashlib.sha256(self.target_rootfs.encode()).hexdigest()))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400389
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800390 postinst_intercepts = (self.d.getVar("POSTINST_INTERCEPTS") or "").split()
391 if not postinst_intercepts:
392 postinst_intercepts_path = self.d.getVar("POSTINST_INTERCEPTS_PATH")
393 if not postinst_intercepts_path:
394 postinst_intercepts_path = self.d.getVar("POSTINST_INTERCEPTS_DIR") or self.d.expand("${COREBASE}/scripts/postinst-intercepts")
395 postinst_intercepts = oe.path.which_wild('*', postinst_intercepts_path)
396
397 bb.debug(1, 'Collected intercepts:\n%s' % ''.join(' %s\n' % i for i in postinst_intercepts))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400398 bb.utils.remove(self.intercepts_dir, True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800399 bb.utils.mkdirhier(self.intercepts_dir)
400 for intercept in postinst_intercepts:
401 bb.utils.copyfile(intercept, os.path.join(self.intercepts_dir, os.path.basename(intercept)))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400402
403 @abstractmethod
404 def _handle_intercept_failure(self, failed_script):
405 pass
406
407 def _postpone_to_first_boot(self, postinst_intercept_hook):
408 with open(postinst_intercept_hook) as intercept:
409 registered_pkgs = None
410 for line in intercept.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -0400411 m = re.match(r"^##PKGS:(.*)", line)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400412 if m is not None:
413 registered_pkgs = m.group(1).strip()
414 break
415
416 if registered_pkgs is not None:
417 bb.note("If an image is being built, the postinstalls for the following packages "
418 "will be postponed for first boot: %s" %
419 registered_pkgs)
420
421 # call the backend dependent handler
422 self._handle_intercept_failure(registered_pkgs)
423
424
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800425 def run_intercepts(self, populate_sdk=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400426 intercepts_dir = self.intercepts_dir
427
428 bb.note("Running intercept scripts:")
429 os.environ['D'] = self.target_rootfs
430 os.environ['STAGING_DIR_NATIVE'] = self.d.getVar('STAGING_DIR_NATIVE')
431 for script in os.listdir(intercepts_dir):
432 script_full = os.path.join(intercepts_dir, script)
433
434 if script == "postinst_intercept" or not os.access(script_full, os.X_OK):
435 continue
436
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800437 # we do not want to run any multilib variant of this
438 if script.startswith("delay_to_first_boot"):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400439 self._postpone_to_first_boot(script_full)
440 continue
441
Brad Bishop19323692019-04-05 15:28:33 -0400442 if populate_sdk == 'host' and self.d.getVar('SDK_OS') == 'mingw32':
443 bb.note("The postinstall intercept hook '%s' could not be executed due to missing wine support, details in %s/log.do_%s"
444 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
445 continue
446
Brad Bishop316dfdd2018-06-25 12:45:53 -0400447 bb.note("> Executing %s intercept ..." % script)
448
449 try:
450 output = subprocess.check_output(script_full, stderr=subprocess.STDOUT)
451 if output: bb.note(output.decode("utf-8"))
452 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400453 bb.note("Exit code %d. Output:\n%s" % (e.returncode, e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800454 if populate_sdk == 'host':
Brad Bishop19323692019-04-05 15:28:33 -0400455 bb.fatal("The postinstall intercept hook '%s' failed, details in %s/log.do_%s" % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800456 elif populate_sdk == 'target':
457 if "qemuwrapper: qemu usermode is not supported" in e.output.decode("utf-8"):
Brad Bishop19323692019-04-05 15:28:33 -0400458 bb.note("The postinstall intercept hook '%s' could not be executed due to missing qemu usermode support, details in %s/log.do_%s"
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800459 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
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')))
462 else:
463 if "qemuwrapper: qemu usermode is not supported" in e.output.decode("utf-8"):
464 bb.note("The postinstall intercept hook '%s' could not be executed due to missing qemu usermode support, details in %s/log.do_%s"
465 % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
466 self._postpone_to_first_boot(script_full)
467 else:
468 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 -0400469
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500470 @abstractmethod
471 def update(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400472 """
473 Update the package manager package database.
474 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500475 pass
476
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500477 @abstractmethod
478 def install(self, pkgs, attempt_only=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400479 """
480 Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
481 True, installation failures are ignored.
482 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500483 pass
484
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500485 @abstractmethod
486 def remove(self, pkgs, with_dependencies=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400487 """
488 Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
489 is False, then any dependencies are left in place.
490 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 pass
492
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493 @abstractmethod
494 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400495 """
496 This function creates the index files
497 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500498 pass
499
500 @abstractmethod
501 def remove_packaging_data(self):
502 pass
503
504 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500505 def list_installed(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506 pass
507
508 @abstractmethod
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500509 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400510 """
511 Returns the path to a tmpdir where resides the contents of a package.
512 Deleting the tmpdir is responsability of the caller.
513 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500514 pass
515
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500516 @abstractmethod
517 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400518 """
519 Add remote package feeds into repository manager configuration. The parameters
520 for the feeds are set by feed_uris, feed_base_paths and feed_archs.
521 See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS
522 for their description.
523 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524 pass
525
Brad Bishop00111322018-04-01 22:23:53 -0400526 def install_glob(self, globs, sdk=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400527 """
528 Install all packages that match a glob.
529 """
Brad Bishop00111322018-04-01 22:23:53 -0400530 # TODO don't have sdk here but have a property on the superclass
531 # (and respect in install_complementary)
532 if sdk:
533 pkgdatadir = self.d.expand("${TMPDIR}/pkgdata/${SDK_SYS}")
534 else:
535 pkgdatadir = self.d.getVar("PKGDATA_DIR")
536
537 try:
538 bb.note("Installing globbed packages...")
539 cmd = ["oe-pkgdata-util", "-p", pkgdatadir, "list-pkgs", globs]
540 pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
541 self.install(pkgs.split(), attempt_only=True)
542 except subprocess.CalledProcessError as e:
543 # Return code 1 means no packages matched
544 if e.returncode != 1:
545 bb.fatal("Could not compute globbed packages list. Command "
546 "'%s' returned %d:\n%s" %
547 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
548
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500549 def install_complementary(self, globs=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400550 """
551 Install complementary packages based upon the list of currently installed
552 packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
553 these packages, if they don't exist then no error will occur. Note: every
554 backend needs to call this function explicitly after the normal package
555 installation
556 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500557 if globs is None:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500558 globs = self.d.getVar('IMAGE_INSTALL_COMPLEMENTARY')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500559 split_linguas = set()
560
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500561 for translation in self.d.getVar('IMAGE_LINGUAS').split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500562 split_linguas.add(translation)
563 split_linguas.add(translation.split('-')[0])
564
565 split_linguas = sorted(split_linguas)
566
567 for lang in split_linguas:
568 globs += " *-locale-%s" % lang
569
570 if globs is None:
571 return
572
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500573 # we need to write the list of installed packages to a file because the
574 # oe-pkgdata-util reads it from a file
575 with tempfile.NamedTemporaryFile(mode="w+", prefix="installed-pkgs") as installed_pkgs:
576 pkgs = self.list_installed()
577 output = oe.utils.format_pkg_list(pkgs, "arch")
578 installed_pkgs.write(output)
579 installed_pkgs.flush()
580
Brad Bishop00111322018-04-01 22:23:53 -0400581 cmd = ["oe-pkgdata-util",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500582 "-p", self.d.getVar('PKGDATA_DIR'), "glob", installed_pkgs.name,
583 globs]
584 exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY')
585 if exclude:
586 cmd.extend(['--exclude=' + '|'.join(exclude.split())])
587 try:
588 bb.note("Installing complementary packages ...")
589 bb.note('Running %s' % cmd)
590 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishop00111322018-04-01 22:23:53 -0400591 self.install(complementary_pkgs.split(), attempt_only=True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500592 except subprocess.CalledProcessError as e:
593 bb.fatal("Could not compute complementary packages list. Command "
594 "'%s' returned %d:\n%s" %
595 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800597 target_arch = self.d.getVar('TARGET_ARCH')
598 localedir = oe.path.join(self.target_rootfs, self.d.getVar("libdir"), "locale")
599 if os.path.exists(localedir) and os.listdir(localedir):
600 generate_locale_archive(self.d, self.target_rootfs, target_arch, localedir)
601 # And now delete the binary locales
602 self.remove(fnmatch.filter(self.list_installed(), "glibc-binary-localedata-*"), False)
603
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500604 def deploy_dir_lock(self):
605 if self.deploy_dir is None:
606 raise RuntimeError("deploy_dir is not set!")
607
608 lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
609
610 self.deploy_lock = bb.utils.lockfile(lock_file_name)
611
612 def deploy_dir_unlock(self):
613 if self.deploy_lock is None:
614 return
615
616 bb.utils.unlockfile(self.deploy_lock)
617
618 self.deploy_lock = None
619
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500620 def construct_uris(self, uris, base_paths):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400621 """
622 Construct URIs based on the following pattern: uri/base_path where 'uri'
623 and 'base_path' correspond to each element of the corresponding array
624 argument leading to len(uris) x len(base_paths) elements on the returned
625 array
626 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500627 def _append(arr1, arr2, sep='/'):
628 res = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600629 narr1 = [a.rstrip(sep) for a in arr1]
630 narr2 = [a.rstrip(sep).lstrip(sep) for a in arr2]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500631 for a1 in narr1:
632 if arr2:
633 for a2 in narr2:
634 res.append("%s%s%s" % (a1, sep, a2))
635 else:
636 res.append(a1)
637 return res
638 return _append(uris, base_paths)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500639
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800640def create_packages_dir(d, subrepo_dir, deploydir, taskname, filterbydependencies):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400641 """
642 Go through our do_package_write_X dependencies and hardlink the packages we depend
643 upon into the repo directory. This prevents us seeing other packages that may
644 have been built that we don't depend upon and also packages for architectures we don't
645 support.
646 """
647 import errno
648
649 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
650 mytaskname = d.getVar("BB_RUNTASK")
651 pn = d.getVar("PN")
652 seendirs = set()
653 multilibs = {}
654
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800655 bb.utils.remove(subrepo_dir, recurse=True)
656 bb.utils.mkdirhier(subrepo_dir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400657
658 # Detect bitbake -b usage
659 nodeps = d.getVar("BB_LIMITEDDEPS") or False
660 if nodeps or not filterbydependencies:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800661 oe.path.symlink(deploydir, subrepo_dir, True)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400662 return
663
664 start = None
665 for dep in taskdepdata:
666 data = taskdepdata[dep]
667 if data[1] == mytaskname and data[0] == pn:
668 start = dep
669 break
670 if start is None:
671 bb.fatal("Couldn't find ourself in BB_TASKDEPDATA?")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800672 pkgdeps = set()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400673 start = [start]
674 seen = set(start)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800675 # Support direct dependencies (do_rootfs -> do_package_write_X)
676 # or indirect dependencies within PN (do_populate_sdk_ext -> do_rootfs -> do_package_write_X)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400677 while start:
678 next = []
679 for dep2 in start:
680 for dep in taskdepdata[dep2][3]:
681 if taskdepdata[dep][0] != pn:
682 if "do_" + taskname in dep:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800683 pkgdeps.add(dep)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400684 elif dep not in seen:
685 next.append(dep)
686 seen.add(dep)
687 start = next
688
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800689 for dep in pkgdeps:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400690 c = taskdepdata[dep][0]
691 manifest, d2 = oe.sstatesig.find_sstate_manifest(c, taskdepdata[dep][2], taskname, d, multilibs)
692 if not manifest:
693 bb.fatal("No manifest generated from: %s in %s" % (c, taskdepdata[dep][2]))
694 if not os.path.exists(manifest):
695 continue
696 with open(manifest, "r") as f:
697 for l in f:
698 l = l.strip()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800699 deploydir = os.path.normpath(deploydir)
700 if bb.data.inherits_class('packagefeed-stability', d):
701 dest = l.replace(deploydir + "-prediff", "")
702 else:
703 dest = l.replace(deploydir, "")
704 dest = subrepo_dir + dest
Brad Bishop316dfdd2018-06-25 12:45:53 -0400705 if l.endswith("/"):
706 if dest not in seendirs:
707 bb.utils.mkdirhier(dest)
708 seendirs.add(dest)
709 continue
710 # Try to hardlink the file, copy if that fails
711 destdir = os.path.dirname(dest)
712 if destdir not in seendirs:
713 bb.utils.mkdirhier(destdir)
714 seendirs.add(destdir)
715 try:
716 os.link(l, dest)
717 except OSError as err:
718 if err.errno == errno.EXDEV:
719 bb.utils.copyfile(l, dest)
720 else:
721 raise
722
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500723class RpmPM(PackageManager):
724 def __init__(self,
725 d,
726 target_rootfs,
727 target_vendor,
728 task_name='target',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500729 arch_var=None,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500730 os_var=None,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400731 rpm_repo_workdir="oe-rootfs-repo",
Brad Bishop19323692019-04-05 15:28:33 -0400732 filterbydependencies=True,
733 needfeed=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400734 super(RpmPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735 self.target_vendor = target_vendor
736 self.task_name = task_name
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500737 if arch_var == None:
738 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
739 else:
740 self.archs = self.d.getVar(arch_var).replace("-","_")
741 if task_name == "host":
742 self.primary_arch = self.d.getVar('SDK_ARCH')
743 else:
744 self.primary_arch = self.d.getVar('MACHINE_ARCH')
745
Brad Bishop19323692019-04-05 15:28:33 -0400746 if needfeed:
747 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
748 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 -0500749
750 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
751 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
752 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800753 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 -0500754 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
755 self.task_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756 if not os.path.exists(self.d.expand('${T}/saved')):
757 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
758
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500759 def _configure_dnf(self):
760 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
761 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
762 # This prevents accidental matching against libsolv's built-in policies
763 if len(archs) <= 1:
764 archs = archs + ["bogusarch"]
765 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
766 bb.utils.mkdirhier(confdir)
767 open(confdir + "arch", 'w').write(":".join(archs))
768 distro_codename = self.d.getVar('DISTRO_CODENAME')
769 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500771 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500774 def _configure_rpm(self):
775 # We need to configure rpm to use our primary package architecture as the installation architecture,
776 # and to make it compatible with other package architectures that we use.
777 # Otherwise it will refuse to proceed with packages installation.
778 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
779 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
780 bb.utils.mkdirhier(platformconfdir)
781 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800782 with open(rpmrcconfdir + "rpmrc", 'w') as f:
783 f.write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
784 f.write("buildarch_compat: %s: noarch\n" % self.primary_arch)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500785
786 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
787 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
788 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500789 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500790 open(platformconfdir + "macros", 'a').write("%_prefer_color 7")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500791
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500792 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
793 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
794 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
795 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
796 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
797 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
798 try:
799 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
800 except subprocess.CalledProcessError as e:
801 bb.fatal("Importing GPG key failed. Command '%s' "
802 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500803
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804 def create_configs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500805 self._configure_dnf()
806 self._configure_rpm()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500807
808 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500809 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
810 lf = bb.utils.lockfile(lockfilename, False)
811 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
812 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500814 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
815 from urllib.parse import urlparse
816
817 if feed_uris == "":
818 return
819
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500820 gpg_opts = ''
821 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
822 gpg_opts += 'repo_gpgcheck=1\n'
823 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'))
824
Brad Bishop316dfdd2018-06-25 12:45:53 -0400825 if self.d.getVar('RPM_SIGN_PACKAGES') != '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500826 gpg_opts += 'gpgcheck=0\n'
827
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500828 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
829 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
830 for uri in remote_uris:
831 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
832 if feed_archs is not None:
833 for arch in feed_archs.split():
834 repo_uri = uri + "/" + arch
835 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
836 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
837 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500838 "[%s]\nname=%s\nbaseurl=%s\n%s\n" % (repo_id, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500839 else:
840 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
841 repo_uri = uri
842 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500843 "[%s]\nname=%s\nbaseurl=%s\n%s" % (repo_base, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500844
845 def _prepare_pkg_transaction(self):
846 os.environ['D'] = self.target_rootfs
847 os.environ['OFFLINE_ROOT'] = self.target_rootfs
848 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
849 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -0400850 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500851 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
852
853
854 def install(self, pkgs, attempt_only = False):
855 if len(pkgs) == 0:
856 return
857 self._prepare_pkg_transaction()
858
859 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
860 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
861 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
862
863 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
864 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
865 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
866 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
867 ["install"] +
868 pkgs)
869
870 failed_scriptlets_pkgnames = collections.OrderedDict()
871 for line in output.splitlines():
Brad Bishop19323692019-04-05 15:28:33 -0400872 if line.startswith("Error in POSTIN scriptlet in rpm package"):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500873 failed_scriptlets_pkgnames[line.split()[-1]] = True
874
Brad Bishop316dfdd2018-06-25 12:45:53 -0400875 if len(failed_scriptlets_pkgnames) > 0:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800876 failed_postinsts_abort(list(failed_scriptlets_pkgnames.keys()), self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500877
878 def remove(self, pkgs, with_dependencies = True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800879 if not pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500880 return
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800881
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500882 self._prepare_pkg_transaction()
883
884 if with_dependencies:
885 self._invoke_dnf(["remove"] + pkgs)
886 else:
887 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500888 args = ["-e", "-v", "--nodeps", "--root=%s" %self.target_rootfs]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500889
890 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500891 bb.note("Running %s" % ' '.join([cmd] + args + pkgs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500892 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500893 bb.note(output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500894 except subprocess.CalledProcessError as e:
895 bb.fatal("Could not invoke rpm. Command "
896 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
897
898 def upgrade(self):
899 self._prepare_pkg_transaction()
900 self._invoke_dnf(["upgrade"])
901
902 def autoremove(self):
903 self._prepare_pkg_transaction()
904 self._invoke_dnf(["autoremove"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500905
906 def remove_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500907 self._invoke_dnf(["clean", "all"])
908 for dir in self.packaging_data_dirs:
909 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910
911 def backup_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500912 # Save the packaging dirs for increment rpm image generation
913 if os.path.exists(self.saved_packaging_data):
914 bb.utils.remove(self.saved_packaging_data, True)
915 for i in self.packaging_data_dirs:
916 source_dir = oe.path.join(self.target_rootfs, i)
917 target_dir = oe.path.join(self.saved_packaging_data, i)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800918 if os.path.isdir(source_dir):
919 shutil.copytree(source_dir, target_dir, symlinks=True)
920 elif os.path.isfile(source_dir):
921 shutil.copy2(source_dir, target_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922
923 def recovery_packaging_data(self):
924 # Move the rpmlib back
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500925 if os.path.exists(self.saved_packaging_data):
926 for i in self.packaging_data_dirs:
927 target_dir = oe.path.join(self.target_rootfs, i)
928 if os.path.exists(target_dir):
929 bb.utils.remove(target_dir, True)
930 source_dir = oe.path.join(self.saved_packaging_data, i)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800931 if os.path.isdir(source_dir):
932 shutil.copytree(source_dir, target_dir, symlinks=True)
933 elif os.path.isfile(source_dir):
934 shutil.copy2(source_dir, target_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500936 def list_installed(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500937 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
938 print_output = False)
939 packages = {}
940 current_package = None
941 current_deps = None
942 current_state = "initial"
943 for line in output.splitlines():
944 if line.startswith("Package:"):
945 package_info = line.split(" ")[1:]
946 current_package = package_info[0]
947 package_arch = package_info[1]
948 package_version = package_info[2]
949 package_rpm = package_info[3]
950 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
951 current_deps = []
952 elif line.startswith("Dependencies:"):
953 current_state = "dependencies"
954 elif line.startswith("Recommendations"):
955 current_state = "recommendations"
956 elif line.startswith("DependenciesEndHere:"):
957 current_state = "initial"
958 packages[current_package]["deps"] = current_deps
959 elif len(line) > 0:
960 if current_state == "dependencies":
961 current_deps.append(line)
962 elif current_state == "recommendations":
963 current_deps.append("%s [REC]" % line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500964
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500965 return packages
966
967 def update(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500968 self._invoke_dnf(["makecache", "--refresh"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500969
970 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
971 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
972
973 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
Brad Bishop19323692019-04-05 15:28:33 -0400974 standard_dnf_args = ["-v", "--rpmverbosity=info", "-y",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500975 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
976 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500977 "--installroot=%s" % (self.target_rootfs),
978 "--setopt=logdir=%s" % (self.d.getVar('T'))
979 ]
Brad Bishop19323692019-04-05 15:28:33 -0400980 if hasattr(self, "rpm_repo_dir"):
981 standard_dnf_args.append("--repofrompath=oe-repo,%s" % (self.rpm_repo_dir))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500982 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
Brad Bishop316dfdd2018-06-25 12:45:53 -0400983 bb.note('Running %s' % ' '.join(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500984 try:
985 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
986 if print_output:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800987 bb.debug(1, output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500988 return output
989 except subprocess.CalledProcessError as e:
990 if print_output:
991 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
992 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
993 else:
994 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
995 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
996 return e.output.decode("utf-8")
997
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500998 def dump_install_solution(self, pkgs):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500999 open(self.solution_manifest, 'w').write(" ".join(pkgs))
1000 return pkgs
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001001
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001002 def load_old_install_solution(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001003 if not os.path.exists(self.solution_manifest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001004 return []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 return open(self.solution_manifest, 'r').read().split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001007
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 def _script_num_prefix(self, path):
1009 files = os.listdir(path)
1010 numbers = set()
1011 numbers.add(99)
1012 for f in files:
1013 numbers.add(int(f.split("-")[0]))
1014 return max(numbers) + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001015
1016 def save_rpmpostinst(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001017 bb.note("Saving postinstall script of %s" % (pkg))
1018 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
1019 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001020
1021 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001022 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001023 except subprocess.CalledProcessError as e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001024 bb.fatal("Could not invoke rpm. Command "
1025 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001026
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001027 # may need to prepend #!/bin/sh to output
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001028
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001029 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
1030 bb.utils.mkdirhier(target_path)
1031 num = self._script_num_prefix(target_path)
1032 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
1033 open(saved_script_name, 'w').write(output)
1034 os.chmod(saved_script_name, 0o755)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001035
Brad Bishop316dfdd2018-06-25 12:45:53 -04001036 def _handle_intercept_failure(self, registered_pkgs):
1037 rpm_postinsts_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
1038 bb.utils.mkdirhier(rpm_postinsts_dir)
1039
1040 # Save the package postinstalls in /etc/rpm-postinsts
1041 for pkg in registered_pkgs.split():
1042 self.save_rpmpostinst(pkg)
1043
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001044 def extract(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001045 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
1046 pkg_name = output.splitlines()[-1]
1047 if not pkg_name.endswith(".rpm"):
1048 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
1049 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001050
1051 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
1052 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
1053
1054 if not os.path.isfile(pkg_path):
1055 bb.fatal("Unable to extract package for '%s'."
1056 "File %s doesn't exists" % (pkg, pkg_path))
1057
1058 tmp_dir = tempfile.mkdtemp()
1059 current_dir = os.getcwd()
1060 os.chdir(tmp_dir)
1061
1062 try:
1063 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
1064 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1065 except subprocess.CalledProcessError as e:
1066 bb.utils.remove(tmp_dir, recurse=True)
1067 bb.fatal("Unable to extract %s package. Command '%s' "
1068 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
1069 except OSError as e:
1070 bb.utils.remove(tmp_dir, recurse=True)
1071 bb.fatal("Unable to extract %s package. Command '%s' "
1072 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
1073
1074 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1075 os.chdir(current_dir)
1076
1077 return tmp_dir
1078
1079
1080class OpkgDpkgPM(PackageManager):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001081 def __init__(self, d, target_rootfs):
1082 """
1083 This is an abstract class. Do not instantiate this directly.
1084 """
1085 super(OpkgDpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001086
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001087 def package_info(self, pkg, cmd):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001088 """
1089 Returns a dictionary with the package info.
1090
1091 This method extracts the common parts for Opkg and Dpkg
1092 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001093
1094 try:
1095 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
1096 except subprocess.CalledProcessError as e:
1097 bb.fatal("Unable to list available packages. Command '%s' "
1098 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
1099 return opkg_query(output)
1100
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001101 def extract(self, pkg, pkg_info):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001102 """
1103 Returns the path to a tmpdir where resides the contents of a package.
1104
1105 Deleting the tmpdir is responsability of the caller.
1106
1107 This method extracts the common parts for Opkg and Dpkg
1108 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001109
1110 ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
1111 tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
1112 pkg_path = pkg_info[pkg]["filepath"]
1113
1114 if not os.path.isfile(pkg_path):
1115 bb.fatal("Unable to extract package for '%s'."
1116 "File %s doesn't exists" % (pkg, pkg_path))
1117
1118 tmp_dir = tempfile.mkdtemp()
1119 current_dir = os.getcwd()
1120 os.chdir(tmp_dir)
Brad Bishop19323692019-04-05 15:28:33 -04001121 data_tar = 'data.tar.xz'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122
1123 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001124 cmd = [ar_cmd, 'x', pkg_path]
1125 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
1126 cmd = [tar_cmd, 'xf', data_tar]
1127 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001128 except subprocess.CalledProcessError as e:
1129 bb.utils.remove(tmp_dir, recurse=True)
1130 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001131 "returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001132 except OSError as e:
1133 bb.utils.remove(tmp_dir, recurse=True)
1134 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001135 "returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001136
1137 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1138 bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
1139 bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
1140 os.chdir(current_dir)
1141
1142 return tmp_dir
1143
Brad Bishop316dfdd2018-06-25 12:45:53 -04001144 def _handle_intercept_failure(self, registered_pkgs):
1145 self.mark_packages("unpacked", registered_pkgs.split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001146
1147class OpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001148 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 -04001149 super(OpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001150
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151 self.config_file = config_file
1152 self.pkg_archs = archs
1153 self.task_name = task_name
1154
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001155 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), ipk_repo_workdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001156 self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
1157 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001158 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 -05001159 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001160
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001161 if prepare_index:
1162 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_IPK"), "package_write_ipk", filterbydependencies)
1163
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001164 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001165 if opkg_lib_dir[0] == "/":
1166 opkg_lib_dir = opkg_lib_dir[1:]
1167
1168 self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
1169
1170 bb.utils.mkdirhier(self.opkg_dir)
1171
1172 self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
1173 if not os.path.exists(self.d.expand('${T}/saved')):
1174 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
1175
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001176 self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001177 if self.from_feeds:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001178 self._create_custom_config()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001179 else:
1180 self._create_config()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001181
1182 self.indexer = OpkgIndexer(self.d, self.deploy_dir)
1183
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001185 """
1186 This function will change a package's status in /var/lib/opkg/status file.
1187 If 'packages' is None then the new_status will be applied to all
1188 packages
1189 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001190 status_file = os.path.join(self.opkg_dir, "status")
1191
1192 with open(status_file, "r") as sf:
1193 with open(status_file + ".tmp", "w+") as tmp_sf:
1194 if packages is None:
1195 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1196 r"Package: \1\n\2Status: \3%s" % status_tag,
1197 sf.read()))
1198 else:
1199 if type(packages).__name__ != "list":
1200 raise TypeError("'packages' should be a list object")
1201
1202 status = sf.read()
1203 for pkg in packages:
1204 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1205 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1206 status)
1207
1208 tmp_sf.write(status)
1209
1210 os.rename(status_file + ".tmp", status_file)
1211
1212 def _create_custom_config(self):
1213 bb.note("Building from feeds activated!")
1214
1215 with open(self.config_file, "w+") as config_file:
1216 priority = 1
1217 for arch in self.pkg_archs.split():
1218 config_file.write("arch %s %d\n" % (arch, priority))
1219 priority += 5
1220
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001221 for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
Brad Bishop19323692019-04-05 15:28:33 -04001222 feed_match = re.match(r"^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001223
1224 if feed_match is not None:
1225 feed_name = feed_match.group(1)
1226 feed_uri = feed_match.group(2)
1227
1228 bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
1229
1230 config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
1231
1232 """
1233 Allow to use package deploy directory contents as quick devel-testing
1234 feed. This creates individual feed configs for each arch subdir of those
1235 specified as compatible for the current machine.
1236 NOTE: Development-helper feature, NOT a full-fledged feed.
1237 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001238 if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001239 for arch in self.pkg_archs.split():
1240 cfg_file_name = os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001241 self.d.getVar("sysconfdir"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001242 "opkg",
1243 "local-%s-feed.conf" % arch)
1244
1245 with open(cfg_file_name, "w+") as cfg_file:
1246 cfg_file.write("src/gz local-%s %s/%s" %
1247 (arch,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001248 self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001249 arch))
1250
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001251 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001252 # There is no command line option for this anymore, we need to add
1253 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1254 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001255 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1256 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1257 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001258 cfg_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1259 cfg_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1260 cfg_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001261
1262
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001263 def _create_config(self):
1264 with open(self.config_file, "w+") as config_file:
1265 priority = 1
1266 for arch in self.pkg_archs.split():
1267 config_file.write("arch %s %d\n" % (arch, priority))
1268 priority += 5
1269
1270 config_file.write("src oe file:%s\n" % self.deploy_dir)
1271
1272 for arch in self.pkg_archs.split():
1273 pkgs_dir = os.path.join(self.deploy_dir, arch)
1274 if os.path.isdir(pkgs_dir):
1275 config_file.write("src oe-%s file:%s\n" %
1276 (arch, pkgs_dir))
1277
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001278 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001279 # There is no command line option for this anymore, we need to add
1280 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1281 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001282 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1283 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1284 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001285 config_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1286 config_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1287 config_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001288
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001289 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1290 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001291 return
1292
1293 rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
1294 % self.target_rootfs)
1295
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001296 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
1297 archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001298
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299 with open(rootfs_config, "w+") as config_file:
1300 uri_iterator = 0
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001301 for uri in feed_uris:
1302 if archs:
1303 for arch in archs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001304 if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001305 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001306 bb.note('Adding opkg feed url-%s-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001307 (arch, uri_iterator, uri))
1308 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1309 (arch, uri_iterator, uri, arch))
1310 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001311 bb.note('Adding opkg feed url-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001312 (uri_iterator, uri))
1313 config_file.write("src/gz uri-%d %s\n" %
1314 (uri_iterator, uri))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001315
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001316 uri_iterator += 1
1317
1318 def update(self):
1319 self.deploy_dir_lock()
1320
1321 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1322
1323 try:
1324 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1325 except subprocess.CalledProcessError as e:
1326 self.deploy_dir_unlock()
1327 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001328 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001329
1330 self.deploy_dir_unlock()
1331
1332 def install(self, pkgs, attempt_only=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001333 if not pkgs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334 return
1335
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001336 cmd = "%s %s" % (self.opkg_cmd, self.opkg_args)
1337 for exclude in (self.d.getVar("PACKAGE_EXCLUDE") or "").split():
1338 cmd += " --add-exclude %s" % exclude
Brad Bishop19323692019-04-05 15:28:33 -04001339 for bad_recommendation in (self.d.getVar("BAD_RECOMMENDATIONS") or "").split():
1340 cmd += " --add-ignore-recommends %s" % bad_recommendation
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001341 cmd += " install "
1342 cmd += " ".join(pkgs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001343
1344 os.environ['D'] = self.target_rootfs
1345 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1346 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1347 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001348 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001349 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001350
1351 try:
1352 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1353 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001354 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355 bb.note(output)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001356 failed_pkgs = []
1357 for line in output.split('\n'):
1358 if line.endswith("configuration required on target."):
1359 bb.warn(line)
1360 failed_pkgs.append(line.split(".")[0])
1361 if failed_pkgs:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001362 failed_postinsts_abort(failed_pkgs, self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001363 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001364 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001365 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001366 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001367
1368 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001369 if not pkgs:
1370 return
1371
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001372 if with_dependencies:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001373 cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001374 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1375 else:
1376 cmd = "%s %s --force-depends remove %s" % \
1377 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1378
1379 try:
1380 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001381 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001382 bb.note(output)
1383 except subprocess.CalledProcessError as e:
1384 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001385 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001386
1387 def write_index(self):
1388 self.deploy_dir_lock()
1389
1390 result = self.indexer.write_index()
1391
1392 self.deploy_dir_unlock()
1393
1394 if result is not None:
1395 bb.fatal(result)
1396
1397 def remove_packaging_data(self):
1398 bb.utils.remove(self.opkg_dir, True)
1399 # create the directory back, it's needed by PM lock
1400 bb.utils.mkdirhier(self.opkg_dir)
1401
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001402 def remove_lists(self):
1403 if not self.from_feeds:
1404 bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
1405
1406 def list_installed(self):
1407 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001408
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001409 def dummy_install(self, pkgs):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001410 """
1411 The following function dummy installs pkgs and returns the log of output.
1412 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001413 if len(pkgs) == 0:
1414 return
1415
1416 # Create an temp dir as opkg root for dummy installation
1417 temp_rootfs = self.d.expand('${T}/opkg')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001418 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001419 if opkg_lib_dir[0] == "/":
1420 opkg_lib_dir = opkg_lib_dir[1:]
1421 temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001422 bb.utils.mkdirhier(temp_opkg_dir)
1423
1424 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001425 opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001426
1427 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1428 try:
1429 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1430 except subprocess.CalledProcessError as e:
1431 bb.fatal("Unable to update. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001432 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001433
1434 # Dummy installation
1435 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1436 opkg_args,
1437 ' '.join(pkgs))
1438 try:
1439 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1440 except subprocess.CalledProcessError as e:
1441 bb.fatal("Unable to dummy install packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001443
1444 bb.utils.remove(temp_rootfs, True)
1445
1446 return output
1447
1448 def backup_packaging_data(self):
1449 # Save the opkglib for increment ipk image generation
1450 if os.path.exists(self.saved_opkg_dir):
1451 bb.utils.remove(self.saved_opkg_dir, True)
1452 shutil.copytree(self.opkg_dir,
1453 self.saved_opkg_dir,
1454 symlinks=True)
1455
1456 def recover_packaging_data(self):
1457 # Move the opkglib back
1458 if os.path.exists(self.saved_opkg_dir):
1459 if os.path.exists(self.opkg_dir):
1460 bb.utils.remove(self.opkg_dir, True)
1461
1462 bb.note('Recover packaging data')
1463 shutil.copytree(self.saved_opkg_dir,
1464 self.opkg_dir,
1465 symlinks=True)
1466
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001467 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001468 """
1469 Returns a dictionary with the package info.
1470 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001471 cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
1472 pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001473
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001474 pkg_arch = pkg_info[pkg]["arch"]
1475 pkg_filename = pkg_info[pkg]["filename"]
1476 pkg_info[pkg]["filepath"] = \
1477 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1478
1479 return pkg_info
1480
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001481 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001482 """
1483 Returns the path to a tmpdir where resides the contents of a package.
1484
1485 Deleting the tmpdir is responsability of the caller.
1486 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001487 pkg_info = self.package_info(pkg)
1488 if not pkg_info:
1489 bb.fatal("Unable to get information for package '%s' while "
1490 "trying to extract the package." % pkg)
1491
1492 tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
Brad Bishop19323692019-04-05 15:28:33 -04001493 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001494
1495 return tmp_dir
1496
1497class DpkgPM(OpkgDpkgPM):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001498 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 -04001499 super(DpkgPM, self).__init__(d, target_rootfs)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001500 self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), deb_repo_workdir)
1501
1502 create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_DEB"), "package_write_deb", filterbydependencies)
1503
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001504 if apt_conf_dir is None:
1505 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1506 else:
1507 self.apt_conf_dir = apt_conf_dir
1508 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1509 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001510 self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001511
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001512 self.apt_args = d.getVar("APT_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001513
1514 self.all_arch_list = archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001515 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001516 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1517
1518 self._create_configs(archs, base_archs)
1519
1520 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1521
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001523 """
1524 This function will change a package's status in /var/lib/dpkg/status file.
1525 If 'packages' is None then the new_status will be applied to all
1526 packages
1527 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001528 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1529
1530 with open(status_file, "r") as sf:
1531 with open(status_file + ".tmp", "w+") as tmp_sf:
1532 if packages is None:
1533 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1534 r"Package: \1\n\2Status: \3%s" % status_tag,
1535 sf.read()))
1536 else:
1537 if type(packages).__name__ != "list":
1538 raise TypeError("'packages' should be a list object")
1539
1540 status = sf.read()
1541 for pkg in packages:
1542 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1543 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1544 status)
1545
1546 tmp_sf.write(status)
1547
1548 os.rename(status_file + ".tmp", status_file)
1549
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001550 def run_pre_post_installs(self, package_name=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001551 """
1552 Run the pre/post installs for package "package_name". If package_name is
1553 None, then run all pre/post install scriptlets.
1554 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001555 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001556 ControlScript = collections.namedtuple("ControlScript", ["suffix", "name", "argument"])
1557 control_scripts = [
1558 ControlScript(".preinst", "Preinstall", "install"),
1559 ControlScript(".postinst", "Postinstall", "configure")]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001560 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1561 installed_pkgs = []
1562
1563 with open(status_file, "r") as status:
1564 for line in status.read().split('\n'):
Brad Bishop19323692019-04-05 15:28:33 -04001565 m = re.match(r"^Package: (.*)", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001566 if m is not None:
1567 installed_pkgs.append(m.group(1))
1568
1569 if package_name is not None and not package_name in installed_pkgs:
1570 return
1571
1572 os.environ['D'] = self.target_rootfs
1573 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1574 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1575 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001576 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001577 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001578
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001579 for pkg_name in installed_pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001580 for control_script in control_scripts:
1581 p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001582 if os.path.exists(p_full):
1583 try:
1584 bb.note("Executing %s for package: %s ..." %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001585 (control_script.name.lower(), pkg_name))
1586 output = subprocess.check_output([p_full, control_script.argument],
1587 stderr=subprocess.STDOUT).decode("utf-8")
1588 bb.note(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001589 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001590 bb.warn("%s for package %s failed with %d:\n%s" %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001591 (control_script.name, pkg_name, e.returncode,
1592 e.output.decode("utf-8")))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001593 failed_postinsts_abort([pkg_name], self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001594
1595 def update(self):
1596 os.environ['APT_CONFIG'] = self.apt_conf_file
1597
1598 self.deploy_dir_lock()
1599
1600 cmd = "%s update" % self.apt_get_cmd
1601
1602 try:
1603 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1604 except subprocess.CalledProcessError as e:
1605 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001606 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001607
1608 self.deploy_dir_unlock()
1609
1610 def install(self, pkgs, attempt_only=False):
1611 if attempt_only and len(pkgs) == 0:
1612 return
1613
1614 os.environ['APT_CONFIG'] = self.apt_conf_file
1615
1616 cmd = "%s %s install --force-yes --allow-unauthenticated %s" % \
1617 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1618
1619 try:
1620 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1621 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1622 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001623 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001624 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001625 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001626
1627 # rename *.dpkg-new files/dirs
1628 for root, dirs, files in os.walk(self.target_rootfs):
1629 for dir in dirs:
Brad Bishop19323692019-04-05 15:28:33 -04001630 new_dir = re.sub(r"\.dpkg-new", "", dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001631 if dir != new_dir:
1632 os.rename(os.path.join(root, dir),
1633 os.path.join(root, new_dir))
1634
1635 for file in files:
Brad Bishop19323692019-04-05 15:28:33 -04001636 new_file = re.sub(r"\.dpkg-new", "", file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001637 if file != new_file:
1638 os.rename(os.path.join(root, file),
1639 os.path.join(root, new_file))
1640
1641
1642 def remove(self, pkgs, with_dependencies=True):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001643 if not pkgs:
1644 return
1645
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001646 if with_dependencies:
1647 os.environ['APT_CONFIG'] = self.apt_conf_file
1648 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1649 else:
1650 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1651 " -P --force-depends %s" % \
1652 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1653 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1654
1655 try:
1656 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1657 except subprocess.CalledProcessError as e:
1658 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001659 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001660
1661 def write_index(self):
1662 self.deploy_dir_lock()
1663
1664 result = self.indexer.write_index()
1665
1666 self.deploy_dir_unlock()
1667
1668 if result is not None:
1669 bb.fatal(result)
1670
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001671 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1672 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001673 return
1674
1675 sources_conf = os.path.join("%s/etc/apt/sources.list"
1676 % self.target_rootfs)
1677 arch_list = []
1678
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001679 if feed_archs is None:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001680 for arch in self.all_arch_list:
1681 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1682 continue
1683 arch_list.append(arch)
1684 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001685 arch_list = feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001686
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001687 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688
1689 with open(sources_conf, "w+") as sources_file:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001690 for uri in feed_uris:
1691 if arch_list:
1692 for arch in arch_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001693 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001694 sources_file.write("deb %s/%s ./\n" %
1695 (uri, arch))
1696 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001697 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001698 sources_file.write("deb %s ./\n" % uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001699
1700 def _create_configs(self, archs, base_archs):
Brad Bishop19323692019-04-05 15:28:33 -04001701 base_archs = re.sub(r"_", r"-", base_archs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001702
1703 if os.path.exists(self.apt_conf_dir):
1704 bb.utils.remove(self.apt_conf_dir, True)
1705
1706 bb.utils.mkdirhier(self.apt_conf_dir)
1707 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1708 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001709 bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001710
1711 arch_list = []
1712 for arch in self.all_arch_list:
1713 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1714 continue
1715 arch_list.append(arch)
1716
1717 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1718 priority = 801
1719 for arch in arch_list:
1720 prefs_file.write(
1721 "Package: *\n"
1722 "Pin: release l=%s\n"
1723 "Pin-Priority: %d\n\n" % (arch, priority))
1724
1725 priority += 5
1726
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001727 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001728 for pkg in pkg_exclude.split():
1729 prefs_file.write(
1730 "Package: %s\n"
1731 "Pin: release *\n"
1732 "Pin-Priority: -1\n\n" % pkg)
1733
1734 arch_list.reverse()
1735
1736 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1737 for arch in arch_list:
1738 sources_file.write("deb file:%s/ ./\n" %
1739 os.path.join(self.deploy_dir, arch))
1740
1741 base_arch_list = base_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001742 multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001743 for variant in multilib_variants.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001744 localdata = bb.data.createCopy(self.d)
1745 variant_tune = localdata.getVar("DEFAULTTUNE_virtclass-multilib-" + variant, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001746 orig_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001747 localdata.setVar("DEFAULTTUNE", variant_tune)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001748 variant_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001749 if variant_arch not in base_arch_list:
1750 base_arch_list.append(variant_arch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001751
1752 with open(self.apt_conf_file, "w+") as apt_conf:
1753 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1754 for line in apt_conf_sample.read().split("\n"):
Brad Bishop19323692019-04-05 15:28:33 -04001755 match_arch = re.match(r" Architecture \".*\";$", line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001756 architectures = ""
1757 if match_arch:
1758 for base_arch in base_arch_list:
1759 architectures += "\"%s\";" % base_arch
1760 apt_conf.write(" Architectures {%s};\n" % architectures);
1761 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1762 else:
Brad Bishop19323692019-04-05 15:28:33 -04001763 line = re.sub(r"#ROOTFS#", self.target_rootfs, line)
1764 line = re.sub(r"#APTCONF#", self.apt_conf_dir, line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001765 apt_conf.write(line + "\n")
1766
1767 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1768 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1769
1770 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1771
1772 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1773 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1774 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1775 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1776
1777 def remove_packaging_data(self):
1778 bb.utils.remove(os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001779 self.d.getVar('opkglibdir')), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001780 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1781
1782 def fix_broken_dependencies(self):
1783 os.environ['APT_CONFIG'] = self.apt_conf_file
1784
1785 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1786
1787 try:
1788 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1789 except subprocess.CalledProcessError as e:
1790 bb.fatal("Cannot fix broken dependencies. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001791 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001792
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001793 def list_installed(self):
1794 return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001795
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001796 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001797 """
1798 Returns a dictionary with the package info.
1799 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001800 cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
1801 pkg_info = super(DpkgPM, self).package_info(pkg, cmd)
1802
1803 pkg_arch = pkg_info[pkg]["pkgarch"]
1804 pkg_filename = pkg_info[pkg]["filename"]
1805 pkg_info[pkg]["filepath"] = \
1806 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1807
1808 return pkg_info
1809
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001810 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001811 """
1812 Returns the path to a tmpdir where resides the contents of a package.
1813
1814 Deleting the tmpdir is responsability of the caller.
1815 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001816 pkg_info = self.package_info(pkg)
1817 if not pkg_info:
1818 bb.fatal("Unable to get information for package '%s' while "
1819 "trying to extract the package." % pkg)
1820
1821 tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info)
1822 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
1823
1824 return tmp_dir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001825
1826def generate_index_files(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001827 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001828
1829 indexer_map = {
Brad Bishop316dfdd2018-06-25 12:45:53 -04001830 "rpm": (RpmSubdirIndexer, d.getVar('DEPLOY_DIR_RPM')),
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001831 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK')),
1832 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001833 }
1834
1835 result = None
1836
1837 for pkg_class in classes:
1838 if not pkg_class in indexer_map:
1839 continue
1840
1841 if os.path.exists(indexer_map[pkg_class][1]):
1842 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1843
1844 if result is not None:
1845 bb.fatal(result)