blob: 2d8aeba037cfcf69a3977cccbbd3be83737f63ce [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001from abc import ABCMeta, abstractmethod
2import os
3import glob
4import subprocess
5import shutil
6import multiprocessing
7import re
Patrick Williamsc0f7c042017-02-23 20:41:17 -06008import collections
Patrick Williamsc124f4f2015-09-15 14:41:29 -05009import bb
10import tempfile
11import oe.utils
Patrick Williamsc0f7c042017-02-23 20:41:17 -060012import oe.path
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050013import string
14from oe.gpg_sign import get_signer
Brad Bishop316dfdd2018-06-25 12:45:53 -040015import hashlib
Patrick Williamsc124f4f2015-09-15 14:41:29 -050016
17# this can be used by all PM backends to create the index files in parallel
18def create_index(arg):
19 index_cmd = arg
20
Brad Bishopd7bf8c12018-02-25 22:55:05 -050021 bb.note("Executing '%s' ..." % index_cmd)
22 result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023 if result:
24 bb.note(result)
25
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026def opkg_query(cmd_output):
Brad Bishop316dfdd2018-06-25 12:45:53 -040027 """
28 This method parse the output from the package managerand return
29 a dictionary with the information of the packages. This is used
30 when the packages are in deb or ipk format.
31 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -060032 verregex = re.compile(' \([=<>]* [^ )]*\)')
33 output = dict()
34 pkg = ""
35 arch = ""
36 ver = ""
37 filename = ""
38 dep = []
39 pkgarch = ""
40 for line in cmd_output.splitlines():
41 line = line.rstrip()
42 if ':' in line:
43 if line.startswith("Package: "):
44 pkg = line.split(": ")[1]
45 elif line.startswith("Architecture: "):
46 arch = line.split(": ")[1]
47 elif line.startswith("Version: "):
48 ver = line.split(": ")[1]
49 elif line.startswith("File: ") or line.startswith("Filename:"):
50 filename = line.split(": ")[1]
51 if "/" in filename:
52 filename = os.path.basename(filename)
53 elif line.startswith("Depends: "):
54 depends = verregex.sub('', line.split(": ")[1])
55 for depend in depends.split(", "):
56 dep.append(depend)
57 elif line.startswith("Recommends: "):
58 recommends = verregex.sub('', line.split(": ")[1])
59 for recommend in recommends.split(", "):
60 dep.append("%s [REC]" % recommend)
61 elif line.startswith("PackageArch: "):
62 pkgarch = line.split(": ")[1]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050063
Patrick Williamsc0f7c042017-02-23 20:41:17 -060064 # When there is a blank line save the package information
65 elif not line:
66 # IPK doesn't include the filename
67 if not filename:
68 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
69 if pkg:
70 output[pkg] = {"arch":arch, "ver":ver,
71 "filename":filename, "deps": dep, "pkgarch":pkgarch }
72 pkg = ""
73 arch = ""
74 ver = ""
75 filename = ""
76 dep = []
77 pkgarch = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 if pkg:
80 if not filename:
81 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
82 output[pkg] = {"arch":arch, "ver":ver,
83 "filename":filename, "deps": dep }
84
85 return output
86
Brad Bishop316dfdd2018-06-25 12:45:53 -040087# Note: this should be bb.fatal in the future.
88def failed_postinsts_warn(pkgs, log_path):
89 bb.warn("""Intentionally failing postinstall scriptlets of %s to defer them to first boot is deprecated. Please place them into pkg_postinst_ontarget_${PN} ().
90If deferring to first boot wasn't the intent, then scriptlet failure may mean an issue in the recipe, or a regression elsewhere.
91Details of the failure are in %s.""" %(pkgs, log_path))
Patrick Williamsc0f7c042017-02-23 20:41:17 -060092
93class Indexer(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050094 def __init__(self, d, deploy_dir):
95 self.d = d
96 self.deploy_dir = deploy_dir
97
98 @abstractmethod
99 def write_index(self):
100 pass
101
102
103class RpmIndexer(Indexer):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500104 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400105 self.do_write_index(self.deploy_dir)
106
107 def do_write_index(self, deploy_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500108 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500109 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
110 else:
111 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500113 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400114 result = create_index("%s --update -q %s" % (createrepo_c, deploy_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115 if result:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500116 bb.fatal(result)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500117
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500118 # Sign repomd
119 if signer:
120 sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
121 is_ascii_sig = (sig_type.upper() != "BIN")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400122 signer.detach_sign(os.path.join(deploy_dir, 'repodata', 'repomd.xml'),
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500123 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
124 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
125 armor=is_ascii_sig)
126
Brad Bishop316dfdd2018-06-25 12:45:53 -0400127class RpmSubdirIndexer(RpmIndexer):
128 def write_index(self):
129 bb.note("Generating package index for %s" %(self.deploy_dir))
130 self.do_write_index(self.deploy_dir)
131 for entry in os.walk(self.deploy_dir):
132 if os.path.samefile(self.deploy_dir, entry[0]):
133 for dir in entry[1]:
134 if dir != 'repodata':
135 dir_path = oe.path.join(self.deploy_dir, dir)
136 bb.note("Generating package index for %s" %(dir_path))
137 self.do_write_index(dir_path)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500138
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139class OpkgIndexer(Indexer):
140 def write_index(self):
141 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
142 "SDK_PACKAGE_ARCHS",
Brad Bishop316dfdd2018-06-25 12:45:53 -0400143 ]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500144
145 opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500146 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
147 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500148 else:
149 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
151 if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
152 open(os.path.join(self.deploy_dir, "Packages"), "w").close()
153
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500154 index_cmds = set()
155 index_sign_files = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500156 for arch_var in arch_vars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500157 archs = self.d.getVar(arch_var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500158 if archs is None:
159 continue
160
161 for arch in archs.split():
162 pkgs_dir = os.path.join(self.deploy_dir, arch)
163 pkgs_file = os.path.join(pkgs_dir, "Packages")
164
165 if not os.path.isdir(pkgs_dir):
166 continue
167
168 if not os.path.exists(pkgs_file):
169 open(pkgs_file, "w").close()
170
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500171 index_cmds.add('%s -r %s -p %s -m %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172 (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
173
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500174 index_sign_files.add(pkgs_file)
175
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500176 if len(index_cmds) == 0:
177 bb.note("There are no packages in %s!" % self.deploy_dir)
178 return
179
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500180 oe.utils.multiprocess_exec(index_cmds, create_index)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500181
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500182 if signer:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500183 feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500184 is_ascii_sig = (feed_sig_type.upper() != "BIN")
185 for f in index_sign_files:
186 signer.detach_sign(f,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500187 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
188 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500189 armor=is_ascii_sig)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500190
191
192class DpkgIndexer(Indexer):
193 def _create_configs(self):
194 bb.utils.mkdirhier(self.apt_conf_dir)
195 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
196 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
197 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
198
199 with open(os.path.join(self.apt_conf_dir, "preferences"),
200 "w") as prefs_file:
201 pass
202 with open(os.path.join(self.apt_conf_dir, "sources.list"),
203 "w+") as sources_file:
204 pass
205
206 with open(self.apt_conf_file, "w") as apt_conf:
207 with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
208 "apt", "apt.conf.sample")) as apt_conf_sample:
209 for line in apt_conf_sample.read().split("\n"):
210 line = re.sub("#ROOTFS#", "/dev/null", line)
211 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
212 apt_conf.write(line + "\n")
213
214 def write_index(self):
215 self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
216 "apt-ftparchive")
217 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
218 self._create_configs()
219
220 os.environ['APT_CONFIG'] = self.apt_conf_file
221
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500222 pkg_archs = self.d.getVar('PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500223 if pkg_archs is not None:
224 arch_list = pkg_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500225 sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500226 if sdk_pkg_archs is not None:
227 for a in sdk_pkg_archs.split():
228 if a not in pkg_archs:
229 arch_list.append(a)
230
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500231 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500232 arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
233
234 apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
235 gzip = bb.utils.which(os.getenv('PATH'), "gzip")
236
237 index_cmds = []
238 deb_dirs_found = False
239 for arch in arch_list:
240 arch_dir = os.path.join(self.deploy_dir, arch)
241 if not os.path.isdir(arch_dir):
242 continue
243
244 cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
245
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500246 cmd += "%s -fcn Packages > Packages.gz;" % gzip
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500247
248 with open(os.path.join(arch_dir, "Release"), "w+") as release:
249 release.write("Label: %s\n" % arch)
250
251 cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
252
253 index_cmds.append(cmd)
254
255 deb_dirs_found = True
256
257 if not deb_dirs_found:
258 bb.note("There are no packages in %s" % self.deploy_dir)
259 return
260
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500261 oe.utils.multiprocess_exec(index_cmds, create_index)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500262 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500263 raise NotImplementedError('Package feed signing not implementd for dpkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264
265
266
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600267class PkgsList(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268 def __init__(self, d, rootfs_dir):
269 self.d = d
270 self.rootfs_dir = rootfs_dir
271
272 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500273 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 pass
275
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276class RpmPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500277 def list_pkgs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500278 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR')).list_installed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279
280class OpkgPkgsList(PkgsList):
281 def __init__(self, d, rootfs_dir, config_file):
282 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
283
284 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
285 self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500286 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500287
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500288 def list_pkgs(self, format=None):
289 cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500291 # opkg returns success even when it printed some
292 # "Collected errors:" report to stderr. Mixing stderr into
293 # stdout then leads to random failures later on when
294 # parsing the output. To avoid this we need to collect both
295 # output streams separately and check for empty stderr.
296 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
297 cmd_output, cmd_stderr = p.communicate()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600298 cmd_output = cmd_output.decode("utf-8")
299 cmd_stderr = cmd_stderr.decode("utf-8")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500300 if p.returncode or cmd_stderr:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500301 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500302 "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600304 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305
306
307class DpkgPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500308
309 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310 cmd = [bb.utils.which(os.getenv('PATH'), "dpkg-query"),
311 "--admindir=%s/var/lib/dpkg" % self.rootfs_dir,
312 "-W"]
313
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500314 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 -0500315
316 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600317 cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318 except subprocess.CalledProcessError as e:
319 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600320 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600322 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323
324
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600325class PackageManager(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326 """
327 This is an abstract class. Do not instantiate this directly.
328 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500329
Brad Bishop316dfdd2018-06-25 12:45:53 -0400330 def __init__(self, d, target_rootfs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500331 self.d = d
Brad Bishop316dfdd2018-06-25 12:45:53 -0400332 self.target_rootfs = target_rootfs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500333 self.deploy_dir = None
334 self.deploy_lock = None
Brad Bishop316dfdd2018-06-25 12:45:53 -0400335 self._initialize_intercepts()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500336
Brad Bishop316dfdd2018-06-25 12:45:53 -0400337 def _initialize_intercepts(self):
338 bb.note("Initializing intercept dir for %s" % self.target_rootfs)
339 postinst_intercepts_dir = self.d.getVar("POSTINST_INTERCEPTS_DIR")
340 if not postinst_intercepts_dir:
341 postinst_intercepts_dir = self.d.expand("${COREBASE}/scripts/postinst-intercepts")
342 # As there might be more than one instance of PackageManager operating at the same time
343 # we need to isolate the intercept_scripts directories from each other,
344 # hence the ugly hash digest in dir name.
345 self.intercepts_dir = os.path.join(self.d.getVar('WORKDIR'),
346 "intercept_scripts-%s" %(hashlib.sha256(self.target_rootfs.encode()).hexdigest()) )
347
348 bb.utils.remove(self.intercepts_dir, True)
349 shutil.copytree(postinst_intercepts_dir, self.intercepts_dir)
350
351 @abstractmethod
352 def _handle_intercept_failure(self, failed_script):
353 pass
354
355 def _postpone_to_first_boot(self, postinst_intercept_hook):
356 with open(postinst_intercept_hook) as intercept:
357 registered_pkgs = None
358 for line in intercept.read().split("\n"):
359 m = re.match("^##PKGS:(.*)", line)
360 if m is not None:
361 registered_pkgs = m.group(1).strip()
362 break
363
364 if registered_pkgs is not None:
365 bb.note("If an image is being built, the postinstalls for the following packages "
366 "will be postponed for first boot: %s" %
367 registered_pkgs)
368
369 # call the backend dependent handler
370 self._handle_intercept_failure(registered_pkgs)
371
372
373 def run_intercepts(self):
374 intercepts_dir = self.intercepts_dir
375
376 bb.note("Running intercept scripts:")
377 os.environ['D'] = self.target_rootfs
378 os.environ['STAGING_DIR_NATIVE'] = self.d.getVar('STAGING_DIR_NATIVE')
379 for script in os.listdir(intercepts_dir):
380 script_full = os.path.join(intercepts_dir, script)
381
382 if script == "postinst_intercept" or not os.access(script_full, os.X_OK):
383 continue
384
385 if script == "delay_to_first_boot":
386 self._postpone_to_first_boot(script_full)
387 continue
388
389 bb.note("> Executing %s intercept ..." % script)
390
391 try:
392 output = subprocess.check_output(script_full, stderr=subprocess.STDOUT)
393 if output: bb.note(output.decode("utf-8"))
394 except subprocess.CalledProcessError as e:
395 bb.warn("The postinstall intercept hook '%s' failed, details in %s/log.do_%s" % (script, self.d.getVar('T'), self.d.getVar('BB_CURRENTTASK')))
396 bb.note("Exit code %d. Output:\n%s" % (e.returncode, e.output.decode("utf-8")))
397 self._postpone_to_first_boot(script_full)
398
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500399 @abstractmethod
400 def update(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400401 """
402 Update the package manager package database.
403 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404 pass
405
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406 @abstractmethod
407 def install(self, pkgs, attempt_only=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400408 """
409 Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
410 True, installation failures are ignored.
411 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500412 pass
413
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500414 @abstractmethod
415 def remove(self, pkgs, with_dependencies=True):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400416 """
417 Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
418 is False, then any dependencies are left in place.
419 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420 pass
421
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500422 @abstractmethod
423 def write_index(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400424 """
425 This function creates the index files
426 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500427 pass
428
429 @abstractmethod
430 def remove_packaging_data(self):
431 pass
432
433 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500434 def list_installed(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500435 pass
436
437 @abstractmethod
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500438 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400439 """
440 Returns the path to a tmpdir where resides the contents of a package.
441 Deleting the tmpdir is responsability of the caller.
442 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500443 pass
444
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500445 @abstractmethod
446 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400447 """
448 Add remote package feeds into repository manager configuration. The parameters
449 for the feeds are set by feed_uris, feed_base_paths and feed_archs.
450 See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS
451 for their description.
452 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500453 pass
454
Brad Bishop00111322018-04-01 22:23:53 -0400455 def install_glob(self, globs, sdk=False):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400456 """
457 Install all packages that match a glob.
458 """
Brad Bishop00111322018-04-01 22:23:53 -0400459 # TODO don't have sdk here but have a property on the superclass
460 # (and respect in install_complementary)
461 if sdk:
462 pkgdatadir = self.d.expand("${TMPDIR}/pkgdata/${SDK_SYS}")
463 else:
464 pkgdatadir = self.d.getVar("PKGDATA_DIR")
465
466 try:
467 bb.note("Installing globbed packages...")
468 cmd = ["oe-pkgdata-util", "-p", pkgdatadir, "list-pkgs", globs]
469 pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
470 self.install(pkgs.split(), attempt_only=True)
471 except subprocess.CalledProcessError as e:
472 # Return code 1 means no packages matched
473 if e.returncode != 1:
474 bb.fatal("Could not compute globbed packages list. Command "
475 "'%s' returned %d:\n%s" %
476 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
477
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500478 def install_complementary(self, globs=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400479 """
480 Install complementary packages based upon the list of currently installed
481 packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
482 these packages, if they don't exist then no error will occur. Note: every
483 backend needs to call this function explicitly after the normal package
484 installation
485 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486 if globs is None:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500487 globs = self.d.getVar('IMAGE_INSTALL_COMPLEMENTARY')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 split_linguas = set()
489
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500490 for translation in self.d.getVar('IMAGE_LINGUAS').split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491 split_linguas.add(translation)
492 split_linguas.add(translation.split('-')[0])
493
494 split_linguas = sorted(split_linguas)
495
496 for lang in split_linguas:
497 globs += " *-locale-%s" % lang
498
499 if globs is None:
500 return
501
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500502 # we need to write the list of installed packages to a file because the
503 # oe-pkgdata-util reads it from a file
504 with tempfile.NamedTemporaryFile(mode="w+", prefix="installed-pkgs") as installed_pkgs:
505 pkgs = self.list_installed()
506 output = oe.utils.format_pkg_list(pkgs, "arch")
507 installed_pkgs.write(output)
508 installed_pkgs.flush()
509
Brad Bishop00111322018-04-01 22:23:53 -0400510 cmd = ["oe-pkgdata-util",
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500511 "-p", self.d.getVar('PKGDATA_DIR'), "glob", installed_pkgs.name,
512 globs]
513 exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY')
514 if exclude:
515 cmd.extend(['--exclude=' + '|'.join(exclude.split())])
516 try:
517 bb.note("Installing complementary packages ...")
518 bb.note('Running %s' % cmd)
519 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishop00111322018-04-01 22:23:53 -0400520 self.install(complementary_pkgs.split(), attempt_only=True)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500521 except subprocess.CalledProcessError as e:
522 bb.fatal("Could not compute complementary packages list. Command "
523 "'%s' returned %d:\n%s" %
524 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525
526 def deploy_dir_lock(self):
527 if self.deploy_dir is None:
528 raise RuntimeError("deploy_dir is not set!")
529
530 lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
531
532 self.deploy_lock = bb.utils.lockfile(lock_file_name)
533
534 def deploy_dir_unlock(self):
535 if self.deploy_lock is None:
536 return
537
538 bb.utils.unlockfile(self.deploy_lock)
539
540 self.deploy_lock = None
541
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500542 def construct_uris(self, uris, base_paths):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400543 """
544 Construct URIs based on the following pattern: uri/base_path where 'uri'
545 and 'base_path' correspond to each element of the corresponding array
546 argument leading to len(uris) x len(base_paths) elements on the returned
547 array
548 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500549 def _append(arr1, arr2, sep='/'):
550 res = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600551 narr1 = [a.rstrip(sep) for a in arr1]
552 narr2 = [a.rstrip(sep).lstrip(sep) for a in arr2]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500553 for a1 in narr1:
554 if arr2:
555 for a2 in narr2:
556 res.append("%s%s%s" % (a1, sep, a2))
557 else:
558 res.append(a1)
559 return res
560 return _append(uris, base_paths)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500561
Brad Bishop316dfdd2018-06-25 12:45:53 -0400562def create_packages_dir(d, rpm_repo_dir, deploydir, taskname, filterbydependencies):
563 """
564 Go through our do_package_write_X dependencies and hardlink the packages we depend
565 upon into the repo directory. This prevents us seeing other packages that may
566 have been built that we don't depend upon and also packages for architectures we don't
567 support.
568 """
569 import errno
570
571 taskdepdata = d.getVar("BB_TASKDEPDATA", False)
572 mytaskname = d.getVar("BB_RUNTASK")
573 pn = d.getVar("PN")
574 seendirs = set()
575 multilibs = {}
576
577 rpm_subrepo_dir = oe.path.join(rpm_repo_dir, "rpm")
578
579 bb.utils.remove(rpm_subrepo_dir, recurse=True)
580 bb.utils.mkdirhier(rpm_subrepo_dir)
581
582 # Detect bitbake -b usage
583 nodeps = d.getVar("BB_LIMITEDDEPS") or False
584 if nodeps or not filterbydependencies:
585 oe.path.symlink(deploydir, rpm_subrepo_dir, True)
586 return
587
588 start = None
589 for dep in taskdepdata:
590 data = taskdepdata[dep]
591 if data[1] == mytaskname and data[0] == pn:
592 start = dep
593 break
594 if start is None:
595 bb.fatal("Couldn't find ourself in BB_TASKDEPDATA?")
596 rpmdeps = set()
597 start = [start]
598 seen = set(start)
599 # Support direct dependencies (do_rootfs -> rpms)
600 # or indirect dependencies within PN (do_populate_sdk_ext -> do_rootfs -> rpms)
601 while start:
602 next = []
603 for dep2 in start:
604 for dep in taskdepdata[dep2][3]:
605 if taskdepdata[dep][0] != pn:
606 if "do_" + taskname in dep:
607 rpmdeps.add(dep)
608 elif dep not in seen:
609 next.append(dep)
610 seen.add(dep)
611 start = next
612
613 for dep in rpmdeps:
614 c = taskdepdata[dep][0]
615 manifest, d2 = oe.sstatesig.find_sstate_manifest(c, taskdepdata[dep][2], taskname, d, multilibs)
616 if not manifest:
617 bb.fatal("No manifest generated from: %s in %s" % (c, taskdepdata[dep][2]))
618 if not os.path.exists(manifest):
619 continue
620 with open(manifest, "r") as f:
621 for l in f:
622 l = l.strip()
623 dest = l.replace(deploydir, "")
624 dest = rpm_subrepo_dir + dest
625 if l.endswith("/"):
626 if dest not in seendirs:
627 bb.utils.mkdirhier(dest)
628 seendirs.add(dest)
629 continue
630 # Try to hardlink the file, copy if that fails
631 destdir = os.path.dirname(dest)
632 if destdir not in seendirs:
633 bb.utils.mkdirhier(destdir)
634 seendirs.add(destdir)
635 try:
636 os.link(l, dest)
637 except OSError as err:
638 if err.errno == errno.EXDEV:
639 bb.utils.copyfile(l, dest)
640 else:
641 raise
642
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643class RpmPM(PackageManager):
644 def __init__(self,
645 d,
646 target_rootfs,
647 target_vendor,
648 task_name='target',
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500649 arch_var=None,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500650 os_var=None,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400651 rpm_repo_workdir="oe-rootfs-repo",
652 filterbydependencies=True):
653 super(RpmPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500654 self.target_vendor = target_vendor
655 self.task_name = task_name
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500656 if arch_var == None:
657 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
658 else:
659 self.archs = self.d.getVar(arch_var).replace("-","_")
660 if task_name == "host":
661 self.primary_arch = self.d.getVar('SDK_ARCH')
662 else:
663 self.primary_arch = self.d.getVar('MACHINE_ARCH')
664
665 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400666 create_packages_dir(self.d, self.rpm_repo_dir, d.getVar("DEPLOY_DIR_RPM"), "package_write_rpm", filterbydependencies)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500667
668 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
669 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
670 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
671 self.packaging_data_dirs = ['var/lib/rpm', 'var/lib/dnf', 'var/cache/dnf']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
673 self.task_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500674 if not os.path.exists(self.d.expand('${T}/saved')):
675 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
676
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500677 def _configure_dnf(self):
678 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
679 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
680 # This prevents accidental matching against libsolv's built-in policies
681 if len(archs) <= 1:
682 archs = archs + ["bogusarch"]
683 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
684 bb.utils.mkdirhier(confdir)
685 open(confdir + "arch", 'w').write(":".join(archs))
686 distro_codename = self.d.getVar('DISTRO_CODENAME')
687 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500688
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500689 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500690
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500691
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500692 def _configure_rpm(self):
693 # We need to configure rpm to use our primary package architecture as the installation architecture,
694 # and to make it compatible with other package architectures that we use.
695 # Otherwise it will refuse to proceed with packages installation.
696 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
697 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
698 bb.utils.mkdirhier(platformconfdir)
699 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
700 open(rpmrcconfdir + "rpmrc", 'w').write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
701
702 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
703 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
704 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500705 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706 open(platformconfdir + "macros", 'a').write("%_prefer_color 7")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500707
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500708 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
709 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
710 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
711 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
712 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
713 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
714 try:
715 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
716 except subprocess.CalledProcessError as e:
717 bb.fatal("Importing GPG key failed. Command '%s' "
718 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500719
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720 def create_configs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500721 self._configure_dnf()
722 self._configure_rpm()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500723
724 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500725 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
726 lf = bb.utils.lockfile(lockfilename, False)
727 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
728 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500729
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500730 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
731 from urllib.parse import urlparse
732
733 if feed_uris == "":
734 return
735
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500736 gpg_opts = ''
737 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
738 gpg_opts += 'repo_gpgcheck=1\n'
739 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'))
740
Brad Bishop316dfdd2018-06-25 12:45:53 -0400741 if self.d.getVar('RPM_SIGN_PACKAGES') != '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500742 gpg_opts += 'gpgcheck=0\n'
743
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500744 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
745 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
746 for uri in remote_uris:
747 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
748 if feed_archs is not None:
749 for arch in feed_archs.split():
750 repo_uri = uri + "/" + arch
751 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
752 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
753 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500754 "[%s]\nname=%s\nbaseurl=%s\n%s\n" % (repo_id, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500755 else:
756 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
757 repo_uri = uri
758 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500759 "[%s]\nname=%s\nbaseurl=%s\n%s" % (repo_base, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500760
761 def _prepare_pkg_transaction(self):
762 os.environ['D'] = self.target_rootfs
763 os.environ['OFFLINE_ROOT'] = self.target_rootfs
764 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
765 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -0400766 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500767 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
768
769
770 def install(self, pkgs, attempt_only = False):
771 if len(pkgs) == 0:
772 return
773 self._prepare_pkg_transaction()
774
775 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
776 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
777 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
778
779 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
780 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
781 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
782 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
783 ["install"] +
784 pkgs)
785
786 failed_scriptlets_pkgnames = collections.OrderedDict()
787 for line in output.splitlines():
788 if line.startswith("Non-fatal POSTIN scriptlet failure in rpm package"):
789 failed_scriptlets_pkgnames[line.split()[-1]] = True
790
Brad Bishop316dfdd2018-06-25 12:45:53 -0400791 if len(failed_scriptlets_pkgnames) > 0:
792 failed_postinsts_warn(list(failed_scriptlets_pkgnames.keys()), self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500793 for pkg in failed_scriptlets_pkgnames.keys():
794 self.save_rpmpostinst(pkg)
795
796 def remove(self, pkgs, with_dependencies = True):
797 if len(pkgs) == 0:
798 return
799 self._prepare_pkg_transaction()
800
801 if with_dependencies:
802 self._invoke_dnf(["remove"] + pkgs)
803 else:
804 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500805 args = ["-e", "-v", "--nodeps", "--root=%s" %self.target_rootfs]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500806
807 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500808 bb.note("Running %s" % ' '.join([cmd] + args + pkgs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500809 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500810 bb.note(output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500811 except subprocess.CalledProcessError as e:
812 bb.fatal("Could not invoke rpm. Command "
813 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
814
815 def upgrade(self):
816 self._prepare_pkg_transaction()
817 self._invoke_dnf(["upgrade"])
818
819 def autoremove(self):
820 self._prepare_pkg_transaction()
821 self._invoke_dnf(["autoremove"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500822
823 def remove_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500824 self._invoke_dnf(["clean", "all"])
825 for dir in self.packaging_data_dirs:
826 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500827
828 def backup_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500829 # Save the packaging dirs for increment rpm image generation
830 if os.path.exists(self.saved_packaging_data):
831 bb.utils.remove(self.saved_packaging_data, True)
832 for i in self.packaging_data_dirs:
833 source_dir = oe.path.join(self.target_rootfs, i)
834 target_dir = oe.path.join(self.saved_packaging_data, i)
835 shutil.copytree(source_dir, target_dir, symlinks=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500836
837 def recovery_packaging_data(self):
838 # Move the rpmlib back
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500839 if os.path.exists(self.saved_packaging_data):
840 for i in self.packaging_data_dirs:
841 target_dir = oe.path.join(self.target_rootfs, i)
842 if os.path.exists(target_dir):
843 bb.utils.remove(target_dir, True)
844 source_dir = oe.path.join(self.saved_packaging_data, i)
845 shutil.copytree(source_dir,
846 target_dir,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500847 symlinks=True)
848
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500849 def list_installed(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500850 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
851 print_output = False)
852 packages = {}
853 current_package = None
854 current_deps = None
855 current_state = "initial"
856 for line in output.splitlines():
857 if line.startswith("Package:"):
858 package_info = line.split(" ")[1:]
859 current_package = package_info[0]
860 package_arch = package_info[1]
861 package_version = package_info[2]
862 package_rpm = package_info[3]
863 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
864 current_deps = []
865 elif line.startswith("Dependencies:"):
866 current_state = "dependencies"
867 elif line.startswith("Recommendations"):
868 current_state = "recommendations"
869 elif line.startswith("DependenciesEndHere:"):
870 current_state = "initial"
871 packages[current_package]["deps"] = current_deps
872 elif len(line) > 0:
873 if current_state == "dependencies":
874 current_deps.append(line)
875 elif current_state == "recommendations":
876 current_deps.append("%s [REC]" % line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500877
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500878 return packages
879
880 def update(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500881 self._invoke_dnf(["makecache", "--refresh"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500882
883 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
884 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
885
886 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
887 standard_dnf_args = (["-v", "--rpmverbosity=debug"] if self.d.getVar('ROOTFS_RPM_DEBUG') else []) + ["-y",
888 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
889 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
890 "--repofrompath=oe-repo,%s" % (self.rpm_repo_dir),
891 "--installroot=%s" % (self.target_rootfs),
892 "--setopt=logdir=%s" % (self.d.getVar('T'))
893 ]
894 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
Brad Bishop316dfdd2018-06-25 12:45:53 -0400895 bb.note('Running %s' % ' '.join(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500896 try:
897 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
898 if print_output:
899 bb.note(output)
900 return output
901 except subprocess.CalledProcessError as e:
902 if print_output:
903 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
904 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
905 else:
906 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
907 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
908 return e.output.decode("utf-8")
909
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910 def dump_install_solution(self, pkgs):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500911 open(self.solution_manifest, 'w').write(" ".join(pkgs))
912 return pkgs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500913
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500914 def load_old_install_solution(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500915 if not os.path.exists(self.solution_manifest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500916 return []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500918 return open(self.solution_manifest, 'r').read().split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500919
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500920 def _script_num_prefix(self, path):
921 files = os.listdir(path)
922 numbers = set()
923 numbers.add(99)
924 for f in files:
925 numbers.add(int(f.split("-")[0]))
926 return max(numbers) + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500927
928 def save_rpmpostinst(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500929 bb.note("Saving postinstall script of %s" % (pkg))
930 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
931 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932
933 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500934 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500935 except subprocess.CalledProcessError as e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500936 bb.fatal("Could not invoke rpm. Command "
937 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500938
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500939 # may need to prepend #!/bin/sh to output
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500940
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500941 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
942 bb.utils.mkdirhier(target_path)
943 num = self._script_num_prefix(target_path)
944 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
945 open(saved_script_name, 'w').write(output)
946 os.chmod(saved_script_name, 0o755)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500947
Brad Bishop316dfdd2018-06-25 12:45:53 -0400948 def _handle_intercept_failure(self, registered_pkgs):
949 rpm_postinsts_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/')
950 bb.utils.mkdirhier(rpm_postinsts_dir)
951
952 # Save the package postinstalls in /etc/rpm-postinsts
953 for pkg in registered_pkgs.split():
954 self.save_rpmpostinst(pkg)
955
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600956 def extract(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500957 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
958 pkg_name = output.splitlines()[-1]
959 if not pkg_name.endswith(".rpm"):
960 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
961 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600962
963 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
964 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
965
966 if not os.path.isfile(pkg_path):
967 bb.fatal("Unable to extract package for '%s'."
968 "File %s doesn't exists" % (pkg, pkg_path))
969
970 tmp_dir = tempfile.mkdtemp()
971 current_dir = os.getcwd()
972 os.chdir(tmp_dir)
973
974 try:
975 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
976 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
977 except subprocess.CalledProcessError as e:
978 bb.utils.remove(tmp_dir, recurse=True)
979 bb.fatal("Unable to extract %s package. Command '%s' "
980 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
981 except OSError as e:
982 bb.utils.remove(tmp_dir, recurse=True)
983 bb.fatal("Unable to extract %s package. Command '%s' "
984 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
985
986 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
987 os.chdir(current_dir)
988
989 return tmp_dir
990
991
992class OpkgDpkgPM(PackageManager):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400993 def __init__(self, d, target_rootfs):
994 """
995 This is an abstract class. Do not instantiate this directly.
996 """
997 super(OpkgDpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600998
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600999 def package_info(self, pkg, cmd):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001000 """
1001 Returns a dictionary with the package info.
1002
1003 This method extracts the common parts for Opkg and Dpkg
1004 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005
1006 try:
1007 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
1008 except subprocess.CalledProcessError as e:
1009 bb.fatal("Unable to list available packages. Command '%s' "
1010 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
1011 return opkg_query(output)
1012
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001013 def extract(self, pkg, pkg_info):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001014 """
1015 Returns the path to a tmpdir where resides the contents of a package.
1016
1017 Deleting the tmpdir is responsability of the caller.
1018
1019 This method extracts the common parts for Opkg and Dpkg
1020 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001021
1022 ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
1023 tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
1024 pkg_path = pkg_info[pkg]["filepath"]
1025
1026 if not os.path.isfile(pkg_path):
1027 bb.fatal("Unable to extract package for '%s'."
1028 "File %s doesn't exists" % (pkg, pkg_path))
1029
1030 tmp_dir = tempfile.mkdtemp()
1031 current_dir = os.getcwd()
1032 os.chdir(tmp_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001033 if self.d.getVar('IMAGE_PKGTYPE') == 'deb':
1034 data_tar = 'data.tar.xz'
1035 else:
1036 data_tar = 'data.tar.gz'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001037
1038 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001039 cmd = [ar_cmd, 'x', pkg_path]
1040 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
1041 cmd = [tar_cmd, 'xf', data_tar]
1042 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001043 except subprocess.CalledProcessError as e:
1044 bb.utils.remove(tmp_dir, recurse=True)
1045 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001046 "returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001047 except OSError as e:
1048 bb.utils.remove(tmp_dir, recurse=True)
1049 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001050 "returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001051
1052 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
1053 bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
1054 bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
1055 os.chdir(current_dir)
1056
1057 return tmp_dir
1058
Brad Bishop316dfdd2018-06-25 12:45:53 -04001059 def _handle_intercept_failure(self, registered_pkgs):
1060 self.mark_packages("unpacked", registered_pkgs.split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001061
1062class OpkgPM(OpkgDpkgPM):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001063 def __init__(self, d, target_rootfs, config_file, archs, task_name='target'):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001064 super(OpkgPM, self).__init__(d, target_rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001065
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001066 self.config_file = config_file
1067 self.pkg_archs = archs
1068 self.task_name = task_name
1069
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001070 self.deploy_dir = self.d.getVar("DEPLOY_DIR_IPK")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001071 self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
1072 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001073 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 -05001074 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001075
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001076 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001077 if opkg_lib_dir[0] == "/":
1078 opkg_lib_dir = opkg_lib_dir[1:]
1079
1080 self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
1081
1082 bb.utils.mkdirhier(self.opkg_dir)
1083
1084 self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
1085 if not os.path.exists(self.d.expand('${T}/saved')):
1086 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
1087
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001088 self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001089 if self.from_feeds:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001090 self._create_custom_config()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001091 else:
1092 self._create_config()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001093
1094 self.indexer = OpkgIndexer(self.d, self.deploy_dir)
1095
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001096 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001097 """
1098 This function will change a package's status in /var/lib/opkg/status file.
1099 If 'packages' is None then the new_status will be applied to all
1100 packages
1101 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001102 status_file = os.path.join(self.opkg_dir, "status")
1103
1104 with open(status_file, "r") as sf:
1105 with open(status_file + ".tmp", "w+") as tmp_sf:
1106 if packages is None:
1107 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1108 r"Package: \1\n\2Status: \3%s" % status_tag,
1109 sf.read()))
1110 else:
1111 if type(packages).__name__ != "list":
1112 raise TypeError("'packages' should be a list object")
1113
1114 status = sf.read()
1115 for pkg in packages:
1116 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1117 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1118 status)
1119
1120 tmp_sf.write(status)
1121
1122 os.rename(status_file + ".tmp", status_file)
1123
1124 def _create_custom_config(self):
1125 bb.note("Building from feeds activated!")
1126
1127 with open(self.config_file, "w+") as config_file:
1128 priority = 1
1129 for arch in self.pkg_archs.split():
1130 config_file.write("arch %s %d\n" % (arch, priority))
1131 priority += 5
1132
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001133 for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001134 feed_match = re.match("^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
1135
1136 if feed_match is not None:
1137 feed_name = feed_match.group(1)
1138 feed_uri = feed_match.group(2)
1139
1140 bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
1141
1142 config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
1143
1144 """
1145 Allow to use package deploy directory contents as quick devel-testing
1146 feed. This creates individual feed configs for each arch subdir of those
1147 specified as compatible for the current machine.
1148 NOTE: Development-helper feature, NOT a full-fledged feed.
1149 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001150 if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001151 for arch in self.pkg_archs.split():
1152 cfg_file_name = os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001153 self.d.getVar("sysconfdir"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001154 "opkg",
1155 "local-%s-feed.conf" % arch)
1156
1157 with open(cfg_file_name, "w+") as cfg_file:
1158 cfg_file.write("src/gz local-%s %s/%s" %
1159 (arch,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001160 self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001161 arch))
1162
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001163 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001164 # There is no command line option for this anymore, we need to add
1165 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1166 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001167 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1168 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1169 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001170 cfg_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1171 cfg_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1172 cfg_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001173
1174
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001175 def _create_config(self):
1176 with open(self.config_file, "w+") as config_file:
1177 priority = 1
1178 for arch in self.pkg_archs.split():
1179 config_file.write("arch %s %d\n" % (arch, priority))
1180 priority += 5
1181
1182 config_file.write("src oe file:%s\n" % self.deploy_dir)
1183
1184 for arch in self.pkg_archs.split():
1185 pkgs_dir = os.path.join(self.deploy_dir, arch)
1186 if os.path.isdir(pkgs_dir):
1187 config_file.write("src oe-%s file:%s\n" %
1188 (arch, pkgs_dir))
1189
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001190 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001191 # There is no command line option for this anymore, we need to add
1192 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
1193 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001194 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1195 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1196 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001197 config_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1198 config_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1199 config_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001200
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001201 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1202 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001203 return
1204
1205 rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
1206 % self.target_rootfs)
1207
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001208 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
1209 archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001210
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001211 with open(rootfs_config, "w+") as config_file:
1212 uri_iterator = 0
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001213 for uri in feed_uris:
1214 if archs:
1215 for arch in archs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001216 if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001217 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001218 bb.note('Adding opkg feed url-%s-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001219 (arch, uri_iterator, uri))
1220 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1221 (arch, uri_iterator, uri, arch))
1222 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001223 bb.note('Adding opkg feed url-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001224 (uri_iterator, uri))
1225 config_file.write("src/gz uri-%d %s\n" %
1226 (uri_iterator, uri))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001227
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001228 uri_iterator += 1
1229
1230 def update(self):
1231 self.deploy_dir_lock()
1232
1233 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1234
1235 try:
1236 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1237 except subprocess.CalledProcessError as e:
1238 self.deploy_dir_unlock()
1239 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001240 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001241
1242 self.deploy_dir_unlock()
1243
1244 def install(self, pkgs, attempt_only=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001245 if not pkgs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001246 return
1247
1248 cmd = "%s %s install %s" % (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1249
1250 os.environ['D'] = self.target_rootfs
1251 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1252 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1253 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001254 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001255 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001256
1257 try:
1258 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1259 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001260 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001261 bb.note(output)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001262 failed_pkgs = []
1263 for line in output.split('\n'):
1264 if line.endswith("configuration required on target."):
1265 bb.warn(line)
1266 failed_pkgs.append(line.split(".")[0])
1267 if failed_pkgs:
1268 failed_postinsts_warn(failed_pkgs, self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001269 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001270 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001271 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001272 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001273
1274 def remove(self, pkgs, with_dependencies=True):
1275 if with_dependencies:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001276 cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001277 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1278 else:
1279 cmd = "%s %s --force-depends remove %s" % \
1280 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1281
1282 try:
1283 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001284 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001285 bb.note(output)
1286 except subprocess.CalledProcessError as e:
1287 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001288 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001289
1290 def write_index(self):
1291 self.deploy_dir_lock()
1292
1293 result = self.indexer.write_index()
1294
1295 self.deploy_dir_unlock()
1296
1297 if result is not None:
1298 bb.fatal(result)
1299
1300 def remove_packaging_data(self):
1301 bb.utils.remove(self.opkg_dir, True)
1302 # create the directory back, it's needed by PM lock
1303 bb.utils.mkdirhier(self.opkg_dir)
1304
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001305 def remove_lists(self):
1306 if not self.from_feeds:
1307 bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
1308
1309 def list_installed(self):
1310 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001311
1312 def handle_bad_recommendations(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001313 bad_recommendations = self.d.getVar("BAD_RECOMMENDATIONS") or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001314 if bad_recommendations.strip() == "":
1315 return
1316
1317 status_file = os.path.join(self.opkg_dir, "status")
1318
1319 # If status file existed, it means the bad recommendations has already
1320 # been handled
1321 if os.path.exists(status_file):
1322 return
1323
1324 cmd = "%s %s info " % (self.opkg_cmd, self.opkg_args)
1325
1326 with open(status_file, "w+") as status:
1327 for pkg in bad_recommendations.split():
1328 pkg_info = cmd + pkg
1329
1330 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001331 output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001332 except subprocess.CalledProcessError as e:
1333 bb.fatal("Cannot get package info. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001334 "returned %d:\n%s" % (pkg_info, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001335
1336 if output == "":
1337 bb.note("Ignored bad recommendation: '%s' is "
1338 "not a package" % pkg)
1339 continue
1340
1341 for line in output.split('\n'):
1342 if line.startswith("Status:"):
1343 status.write("Status: deinstall hold not-installed\n")
1344 else:
1345 status.write(line + "\n")
1346
1347 # Append a blank line after each package entry to ensure that it
1348 # is separated from the following entry
1349 status.write("\n")
1350
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001351 def dummy_install(self, pkgs):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001352 """
1353 The following function dummy installs pkgs and returns the log of output.
1354 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001355 if len(pkgs) == 0:
1356 return
1357
1358 # Create an temp dir as opkg root for dummy installation
1359 temp_rootfs = self.d.expand('${T}/opkg')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001360 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001361 if opkg_lib_dir[0] == "/":
1362 opkg_lib_dir = opkg_lib_dir[1:]
1363 temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001364 bb.utils.mkdirhier(temp_opkg_dir)
1365
1366 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001367 opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001368
1369 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1370 try:
1371 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1372 except subprocess.CalledProcessError as e:
1373 bb.fatal("Unable to update. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001374 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001375
1376 # Dummy installation
1377 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1378 opkg_args,
1379 ' '.join(pkgs))
1380 try:
1381 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1382 except subprocess.CalledProcessError as e:
1383 bb.fatal("Unable to dummy install packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001384 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001385
1386 bb.utils.remove(temp_rootfs, True)
1387
1388 return output
1389
1390 def backup_packaging_data(self):
1391 # Save the opkglib for increment ipk image generation
1392 if os.path.exists(self.saved_opkg_dir):
1393 bb.utils.remove(self.saved_opkg_dir, True)
1394 shutil.copytree(self.opkg_dir,
1395 self.saved_opkg_dir,
1396 symlinks=True)
1397
1398 def recover_packaging_data(self):
1399 # Move the opkglib back
1400 if os.path.exists(self.saved_opkg_dir):
1401 if os.path.exists(self.opkg_dir):
1402 bb.utils.remove(self.opkg_dir, True)
1403
1404 bb.note('Recover packaging data')
1405 shutil.copytree(self.saved_opkg_dir,
1406 self.opkg_dir,
1407 symlinks=True)
1408
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001409 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001410 """
1411 Returns a dictionary with the package info.
1412 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001413 cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
1414 pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001415
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001416 pkg_arch = pkg_info[pkg]["arch"]
1417 pkg_filename = pkg_info[pkg]["filename"]
1418 pkg_info[pkg]["filepath"] = \
1419 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1420
1421 return pkg_info
1422
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001423 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001424 """
1425 Returns the path to a tmpdir where resides the contents of a package.
1426
1427 Deleting the tmpdir is responsability of the caller.
1428 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001429 pkg_info = self.package_info(pkg)
1430 if not pkg_info:
1431 bb.fatal("Unable to get information for package '%s' while "
1432 "trying to extract the package." % pkg)
1433
1434 tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
1435 bb.utils.remove(os.path.join(tmp_dir, "data.tar.gz"))
1436
1437 return tmp_dir
1438
1439class DpkgPM(OpkgDpkgPM):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001440 def __init__(self, d, target_rootfs, archs, base_archs, apt_conf_dir=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001441 super(DpkgPM, self).__init__(d, target_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001442 self.deploy_dir = self.d.getVar('DEPLOY_DIR_DEB')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001443 if apt_conf_dir is None:
1444 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1445 else:
1446 self.apt_conf_dir = apt_conf_dir
1447 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1448 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001449 self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001450
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001451 self.apt_args = d.getVar("APT_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001452
1453 self.all_arch_list = archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001454 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001455 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1456
1457 self._create_configs(archs, base_archs)
1458
1459 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1460
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001461 def mark_packages(self, status_tag, packages=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001462 """
1463 This function will change a package's status in /var/lib/dpkg/status file.
1464 If 'packages' is None then the new_status will be applied to all
1465 packages
1466 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001467 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1468
1469 with open(status_file, "r") as sf:
1470 with open(status_file + ".tmp", "w+") as tmp_sf:
1471 if packages is None:
1472 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1473 r"Package: \1\n\2Status: \3%s" % status_tag,
1474 sf.read()))
1475 else:
1476 if type(packages).__name__ != "list":
1477 raise TypeError("'packages' should be a list object")
1478
1479 status = sf.read()
1480 for pkg in packages:
1481 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1482 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1483 status)
1484
1485 tmp_sf.write(status)
1486
1487 os.rename(status_file + ".tmp", status_file)
1488
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001489 def run_pre_post_installs(self, package_name=None):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001490 """
1491 Run the pre/post installs for package "package_name". If package_name is
1492 None, then run all pre/post install scriptlets.
1493 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001494 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001495 ControlScript = collections.namedtuple("ControlScript", ["suffix", "name", "argument"])
1496 control_scripts = [
1497 ControlScript(".preinst", "Preinstall", "install"),
1498 ControlScript(".postinst", "Postinstall", "configure")]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001499 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1500 installed_pkgs = []
1501
1502 with open(status_file, "r") as status:
1503 for line in status.read().split('\n'):
1504 m = re.match("^Package: (.*)", line)
1505 if m is not None:
1506 installed_pkgs.append(m.group(1))
1507
1508 if package_name is not None and not package_name in installed_pkgs:
1509 return
1510
1511 os.environ['D'] = self.target_rootfs
1512 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1513 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1514 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop316dfdd2018-06-25 12:45:53 -04001515 os.environ['INTERCEPT_DIR'] = self.intercepts_dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001516 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001517
1518 failed_pkgs = []
1519 for pkg_name in installed_pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001520 for control_script in control_scripts:
1521 p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522 if os.path.exists(p_full):
1523 try:
1524 bb.note("Executing %s for package: %s ..." %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001525 (control_script.name.lower(), pkg_name))
1526 output = subprocess.check_output([p_full, control_script.argument],
1527 stderr=subprocess.STDOUT).decode("utf-8")
1528 bb.note(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001529 except subprocess.CalledProcessError as e:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001530 bb.warn("%s for package %s failed with %d:\n%s" %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001531 (control_script.name, pkg_name, e.returncode,
1532 e.output.decode("utf-8")))
Brad Bishop316dfdd2018-06-25 12:45:53 -04001533 failed_postinsts_warn([pkg_name], self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534 failed_pkgs.append(pkg_name)
1535 break
1536
1537 if len(failed_pkgs):
1538 self.mark_packages("unpacked", failed_pkgs)
1539
1540 def update(self):
1541 os.environ['APT_CONFIG'] = self.apt_conf_file
1542
1543 self.deploy_dir_lock()
1544
1545 cmd = "%s update" % self.apt_get_cmd
1546
1547 try:
1548 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1549 except subprocess.CalledProcessError as e:
1550 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001551 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001552
1553 self.deploy_dir_unlock()
1554
1555 def install(self, pkgs, attempt_only=False):
1556 if attempt_only and len(pkgs) == 0:
1557 return
1558
1559 os.environ['APT_CONFIG'] = self.apt_conf_file
1560
1561 cmd = "%s %s install --force-yes --allow-unauthenticated %s" % \
1562 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1563
1564 try:
1565 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1566 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1567 except subprocess.CalledProcessError as e:
Brad Bishop00111322018-04-01 22:23:53 -04001568 (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001569 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001570 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001571
1572 # rename *.dpkg-new files/dirs
1573 for root, dirs, files in os.walk(self.target_rootfs):
1574 for dir in dirs:
1575 new_dir = re.sub("\.dpkg-new", "", dir)
1576 if dir != new_dir:
1577 os.rename(os.path.join(root, dir),
1578 os.path.join(root, new_dir))
1579
1580 for file in files:
1581 new_file = re.sub("\.dpkg-new", "", file)
1582 if file != new_file:
1583 os.rename(os.path.join(root, file),
1584 os.path.join(root, new_file))
1585
1586
1587 def remove(self, pkgs, with_dependencies=True):
1588 if with_dependencies:
1589 os.environ['APT_CONFIG'] = self.apt_conf_file
1590 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1591 else:
1592 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1593 " -P --force-depends %s" % \
1594 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1595 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1596
1597 try:
1598 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1599 except subprocess.CalledProcessError as e:
1600 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001601 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001602
1603 def write_index(self):
1604 self.deploy_dir_lock()
1605
1606 result = self.indexer.write_index()
1607
1608 self.deploy_dir_unlock()
1609
1610 if result is not None:
1611 bb.fatal(result)
1612
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001613 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1614 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001615 return
1616
1617 sources_conf = os.path.join("%s/etc/apt/sources.list"
1618 % self.target_rootfs)
1619 arch_list = []
1620
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001621 if feed_archs is None:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001622 for arch in self.all_arch_list:
1623 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1624 continue
1625 arch_list.append(arch)
1626 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001627 arch_list = feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001628
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001629 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001630
1631 with open(sources_conf, "w+") as sources_file:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001632 for uri in feed_uris:
1633 if arch_list:
1634 for arch in arch_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001635 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001636 sources_file.write("deb %s/%s ./\n" %
1637 (uri, arch))
1638 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001639 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001640 sources_file.write("deb %s ./\n" % uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001641
1642 def _create_configs(self, archs, base_archs):
1643 base_archs = re.sub("_", "-", base_archs)
1644
1645 if os.path.exists(self.apt_conf_dir):
1646 bb.utils.remove(self.apt_conf_dir, True)
1647
1648 bb.utils.mkdirhier(self.apt_conf_dir)
1649 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1650 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001651 bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001652
1653 arch_list = []
1654 for arch in self.all_arch_list:
1655 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1656 continue
1657 arch_list.append(arch)
1658
1659 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1660 priority = 801
1661 for arch in arch_list:
1662 prefs_file.write(
1663 "Package: *\n"
1664 "Pin: release l=%s\n"
1665 "Pin-Priority: %d\n\n" % (arch, priority))
1666
1667 priority += 5
1668
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001669 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001670 for pkg in pkg_exclude.split():
1671 prefs_file.write(
1672 "Package: %s\n"
1673 "Pin: release *\n"
1674 "Pin-Priority: -1\n\n" % pkg)
1675
1676 arch_list.reverse()
1677
1678 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1679 for arch in arch_list:
1680 sources_file.write("deb file:%s/ ./\n" %
1681 os.path.join(self.deploy_dir, arch))
1682
1683 base_arch_list = base_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001684 multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001685 for variant in multilib_variants.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001686 localdata = bb.data.createCopy(self.d)
1687 variant_tune = localdata.getVar("DEFAULTTUNE_virtclass-multilib-" + variant, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001688 orig_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001689 localdata.setVar("DEFAULTTUNE", variant_tune)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001690 variant_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001691 if variant_arch not in base_arch_list:
1692 base_arch_list.append(variant_arch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001693
1694 with open(self.apt_conf_file, "w+") as apt_conf:
1695 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1696 for line in apt_conf_sample.read().split("\n"):
1697 match_arch = re.match(" Architecture \".*\";$", line)
1698 architectures = ""
1699 if match_arch:
1700 for base_arch in base_arch_list:
1701 architectures += "\"%s\";" % base_arch
1702 apt_conf.write(" Architectures {%s};\n" % architectures);
1703 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1704 else:
1705 line = re.sub("#ROOTFS#", self.target_rootfs, line)
1706 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
1707 apt_conf.write(line + "\n")
1708
1709 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1710 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1711
1712 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1713
1714 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1715 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1716 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1717 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1718
1719 def remove_packaging_data(self):
1720 bb.utils.remove(os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001721 self.d.getVar('opkglibdir')), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001722 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1723
1724 def fix_broken_dependencies(self):
1725 os.environ['APT_CONFIG'] = self.apt_conf_file
1726
1727 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1728
1729 try:
1730 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1731 except subprocess.CalledProcessError as e:
1732 bb.fatal("Cannot fix broken dependencies. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001733 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001734
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001735 def list_installed(self):
1736 return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001737
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001738 def package_info(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001739 """
1740 Returns a dictionary with the package info.
1741 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001742 cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
1743 pkg_info = super(DpkgPM, self).package_info(pkg, cmd)
1744
1745 pkg_arch = pkg_info[pkg]["pkgarch"]
1746 pkg_filename = pkg_info[pkg]["filename"]
1747 pkg_info[pkg]["filepath"] = \
1748 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1749
1750 return pkg_info
1751
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001752 def extract(self, pkg):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001753 """
1754 Returns the path to a tmpdir where resides the contents of a package.
1755
1756 Deleting the tmpdir is responsability of the caller.
1757 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001758 pkg_info = self.package_info(pkg)
1759 if not pkg_info:
1760 bb.fatal("Unable to get information for package '%s' while "
1761 "trying to extract the package." % pkg)
1762
1763 tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info)
1764 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
1765
1766 return tmp_dir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001767
1768def generate_index_files(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001769 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001770
1771 indexer_map = {
Brad Bishop316dfdd2018-06-25 12:45:53 -04001772 "rpm": (RpmSubdirIndexer, d.getVar('DEPLOY_DIR_RPM')),
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001773 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK')),
1774 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001775 }
1776
1777 result = None
1778
1779 for pkg_class in classes:
1780 if not pkg_class in indexer_map:
1781 continue
1782
1783 if os.path.exists(indexer_map[pkg_class][1]):
1784 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1785
1786 if result is not None:
1787 bb.fatal(result)