blob: 3a2daadafa5803aa22fa7f78635d2757fbdd3168 [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
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015
16# this can be used by all PM backends to create the index files in parallel
17def create_index(arg):
18 index_cmd = arg
19
20 try:
21 bb.note("Executing '%s' ..." % index_cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060022 result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023 except subprocess.CalledProcessError as e:
24 return("Index creation command '%s' failed with return code %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -060025 (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050026
27 if result:
28 bb.note(result)
29
30 return None
31
Patrick Williamsc0f7c042017-02-23 20:41:17 -060032"""
33This method parse the output from the package managerand return
34a dictionary with the information of the packages. This is used
35when the packages are in deb or ipk format.
36"""
37def opkg_query(cmd_output):
38 verregex = re.compile(' \([=<>]* [^ )]*\)')
39 output = dict()
40 pkg = ""
41 arch = ""
42 ver = ""
43 filename = ""
44 dep = []
45 pkgarch = ""
46 for line in cmd_output.splitlines():
47 line = line.rstrip()
48 if ':' in line:
49 if line.startswith("Package: "):
50 pkg = line.split(": ")[1]
51 elif line.startswith("Architecture: "):
52 arch = line.split(": ")[1]
53 elif line.startswith("Version: "):
54 ver = line.split(": ")[1]
55 elif line.startswith("File: ") or line.startswith("Filename:"):
56 filename = line.split(": ")[1]
57 if "/" in filename:
58 filename = os.path.basename(filename)
59 elif line.startswith("Depends: "):
60 depends = verregex.sub('', line.split(": ")[1])
61 for depend in depends.split(", "):
62 dep.append(depend)
63 elif line.startswith("Recommends: "):
64 recommends = verregex.sub('', line.split(": ")[1])
65 for recommend in recommends.split(", "):
66 dep.append("%s [REC]" % recommend)
67 elif line.startswith("PackageArch: "):
68 pkgarch = line.split(": ")[1]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050069
Patrick Williamsc0f7c042017-02-23 20:41:17 -060070 # When there is a blank line save the package information
71 elif not line:
72 # IPK doesn't include the filename
73 if not filename:
74 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
75 if pkg:
76 output[pkg] = {"arch":arch, "ver":ver,
77 "filename":filename, "deps": dep, "pkgarch":pkgarch }
78 pkg = ""
79 arch = ""
80 ver = ""
81 filename = ""
82 dep = []
83 pkgarch = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084
Patrick Williamsc0f7c042017-02-23 20:41:17 -060085 if pkg:
86 if not filename:
87 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
88 output[pkg] = {"arch":arch, "ver":ver,
89 "filename":filename, "deps": dep }
90
91 return output
92
93
94class Indexer(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050095 def __init__(self, d, deploy_dir):
96 self.d = d
97 self.deploy_dir = deploy_dir
98
99 @abstractmethod
100 def write_index(self):
101 pass
102
103
104class RpmIndexer(Indexer):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500105 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500106 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
107 raise NotImplementedError('Package feed signing not yet implementd for rpm')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500108
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500109 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
110 result = create_index("%s --update -q %s" % (createrepo_c, self.deploy_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111 if result:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500112 bb.fatal(result)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500113
114class OpkgIndexer(Indexer):
115 def write_index(self):
116 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
117 "SDK_PACKAGE_ARCHS",
118 "MULTILIB_ARCHS"]
119
120 opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500121 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
122 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500123 else:
124 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500125
126 if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
127 open(os.path.join(self.deploy_dir, "Packages"), "w").close()
128
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500129 index_cmds = set()
130 index_sign_files = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131 for arch_var in arch_vars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500132 archs = self.d.getVar(arch_var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500133 if archs is None:
134 continue
135
136 for arch in archs.split():
137 pkgs_dir = os.path.join(self.deploy_dir, arch)
138 pkgs_file = os.path.join(pkgs_dir, "Packages")
139
140 if not os.path.isdir(pkgs_dir):
141 continue
142
143 if not os.path.exists(pkgs_file):
144 open(pkgs_file, "w").close()
145
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500146 index_cmds.add('%s -r %s -p %s -m %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147 (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
148
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500149 index_sign_files.add(pkgs_file)
150
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151 if len(index_cmds) == 0:
152 bb.note("There are no packages in %s!" % self.deploy_dir)
153 return
154
155 result = oe.utils.multiprocess_exec(index_cmds, create_index)
156 if result:
157 bb.fatal('%s' % ('\n'.join(result)))
158
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500159 if signer:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500160 feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500161 is_ascii_sig = (feed_sig_type.upper() != "BIN")
162 for f in index_sign_files:
163 signer.detach_sign(f,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500164 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
165 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500166 armor=is_ascii_sig)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500167
168
169class DpkgIndexer(Indexer):
170 def _create_configs(self):
171 bb.utils.mkdirhier(self.apt_conf_dir)
172 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
173 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
174 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
175
176 with open(os.path.join(self.apt_conf_dir, "preferences"),
177 "w") as prefs_file:
178 pass
179 with open(os.path.join(self.apt_conf_dir, "sources.list"),
180 "w+") as sources_file:
181 pass
182
183 with open(self.apt_conf_file, "w") as apt_conf:
184 with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
185 "apt", "apt.conf.sample")) as apt_conf_sample:
186 for line in apt_conf_sample.read().split("\n"):
187 line = re.sub("#ROOTFS#", "/dev/null", line)
188 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
189 apt_conf.write(line + "\n")
190
191 def write_index(self):
192 self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
193 "apt-ftparchive")
194 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
195 self._create_configs()
196
197 os.environ['APT_CONFIG'] = self.apt_conf_file
198
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500199 pkg_archs = self.d.getVar('PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500200 if pkg_archs is not None:
201 arch_list = pkg_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500202 sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500203 if sdk_pkg_archs is not None:
204 for a in sdk_pkg_archs.split():
205 if a not in pkg_archs:
206 arch_list.append(a)
207
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500208 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209 arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
210
211 apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
212 gzip = bb.utils.which(os.getenv('PATH'), "gzip")
213
214 index_cmds = []
215 deb_dirs_found = False
216 for arch in arch_list:
217 arch_dir = os.path.join(self.deploy_dir, arch)
218 if not os.path.isdir(arch_dir):
219 continue
220
221 cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
222
223 cmd += "%s -fc Packages > Packages.gz;" % gzip
224
225 with open(os.path.join(arch_dir, "Release"), "w+") as release:
226 release.write("Label: %s\n" % arch)
227
228 cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
229
230 index_cmds.append(cmd)
231
232 deb_dirs_found = True
233
234 if not deb_dirs_found:
235 bb.note("There are no packages in %s" % self.deploy_dir)
236 return
237
238 result = oe.utils.multiprocess_exec(index_cmds, create_index)
239 if result:
240 bb.fatal('%s' % ('\n'.join(result)))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500241 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500242 raise NotImplementedError('Package feed signing not implementd for dpkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500243
244
245
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600246class PkgsList(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500247 def __init__(self, d, rootfs_dir):
248 self.d = d
249 self.rootfs_dir = rootfs_dir
250
251 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500252 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500253 pass
254
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500255class RpmPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500256 def list_pkgs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500257 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR')).list_installed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500258
259class OpkgPkgsList(PkgsList):
260 def __init__(self, d, rootfs_dir, config_file):
261 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
262
263 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
264 self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500265 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500267 def list_pkgs(self, format=None):
268 cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500270 # opkg returns success even when it printed some
271 # "Collected errors:" report to stderr. Mixing stderr into
272 # stdout then leads to random failures later on when
273 # parsing the output. To avoid this we need to collect both
274 # output streams separately and check for empty stderr.
275 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
276 cmd_output, cmd_stderr = p.communicate()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600277 cmd_output = cmd_output.decode("utf-8")
278 cmd_stderr = cmd_stderr.decode("utf-8")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500279 if p.returncode or cmd_stderr:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500280 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500281 "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600283 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284
285
286class DpkgPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500287
288 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500289 cmd = [bb.utils.which(os.getenv('PATH'), "dpkg-query"),
290 "--admindir=%s/var/lib/dpkg" % self.rootfs_dir,
291 "-W"]
292
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500293 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 -0500294
295 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600296 cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500297 except subprocess.CalledProcessError as e:
298 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600299 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500300
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600301 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500302
303
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600304class PackageManager(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305 """
306 This is an abstract class. Do not instantiate this directly.
307 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500308
309 def __init__(self, d):
310 self.d = d
311 self.deploy_dir = None
312 self.deploy_lock = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313
314 """
315 Update the package manager package database.
316 """
317 @abstractmethod
318 def update(self):
319 pass
320
321 """
322 Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
323 True, installation failures are ignored.
324 """
325 @abstractmethod
326 def install(self, pkgs, attempt_only=False):
327 pass
328
329 """
330 Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
331 is False, the any dependencies are left in place.
332 """
333 @abstractmethod
334 def remove(self, pkgs, with_dependencies=True):
335 pass
336
337 """
338 This function creates the index files
339 """
340 @abstractmethod
341 def write_index(self):
342 pass
343
344 @abstractmethod
345 def remove_packaging_data(self):
346 pass
347
348 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500349 def list_installed(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500350 pass
351
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500352 """
353 Returns the path to a tmpdir where resides the contents of a package.
354
355 Deleting the tmpdir is responsability of the caller.
356
357 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500358 @abstractmethod
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500359 def extract(self, pkg):
360 pass
361
362 """
363 Add remote package feeds into repository manager configuration. The parameters
364 for the feeds are set by feed_uris, feed_base_paths and feed_archs.
365 See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS
366 for their description.
367 """
368 @abstractmethod
369 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500370 pass
371
372 """
373 Install complementary packages based upon the list of currently installed
374 packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
375 these packages, if they don't exist then no error will occur. Note: every
376 backend needs to call this function explicitly after the normal package
377 installation
378 """
379 def install_complementary(self, globs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500380 if globs is None:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500381 globs = self.d.getVar('IMAGE_INSTALL_COMPLEMENTARY')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500382 split_linguas = set()
383
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500384 for translation in self.d.getVar('IMAGE_LINGUAS').split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500385 split_linguas.add(translation)
386 split_linguas.add(translation.split('-')[0])
387
388 split_linguas = sorted(split_linguas)
389
390 for lang in split_linguas:
391 globs += " *-locale-%s" % lang
392
393 if globs is None:
394 return
395
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500396 # we need to write the list of installed packages to a file because the
397 # oe-pkgdata-util reads it from a file
398 with tempfile.NamedTemporaryFile(mode="w+", prefix="installed-pkgs") as installed_pkgs:
399 pkgs = self.list_installed()
400 output = oe.utils.format_pkg_list(pkgs, "arch")
401 installed_pkgs.write(output)
402 installed_pkgs.flush()
403
404 cmd = [bb.utils.which(os.getenv('PATH'), "oe-pkgdata-util"),
405 "-p", self.d.getVar('PKGDATA_DIR'), "glob", installed_pkgs.name,
406 globs]
407 exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY')
408 if exclude:
409 cmd.extend(['--exclude=' + '|'.join(exclude.split())])
410 try:
411 bb.note("Installing complementary packages ...")
412 bb.note('Running %s' % cmd)
413 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
414 except subprocess.CalledProcessError as e:
415 bb.fatal("Could not compute complementary packages list. Command "
416 "'%s' returned %d:\n%s" %
417 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
418 self.install(complementary_pkgs.split(), attempt_only=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500419
420 def deploy_dir_lock(self):
421 if self.deploy_dir is None:
422 raise RuntimeError("deploy_dir is not set!")
423
424 lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
425
426 self.deploy_lock = bb.utils.lockfile(lock_file_name)
427
428 def deploy_dir_unlock(self):
429 if self.deploy_lock is None:
430 return
431
432 bb.utils.unlockfile(self.deploy_lock)
433
434 self.deploy_lock = None
435
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500436 """
437 Construct URIs based on the following pattern: uri/base_path where 'uri'
438 and 'base_path' correspond to each element of the corresponding array
439 argument leading to len(uris) x len(base_paths) elements on the returned
440 array
441 """
442 def construct_uris(self, uris, base_paths):
443 def _append(arr1, arr2, sep='/'):
444 res = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600445 narr1 = [a.rstrip(sep) for a in arr1]
446 narr2 = [a.rstrip(sep).lstrip(sep) for a in arr2]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500447 for a1 in narr1:
448 if arr2:
449 for a2 in narr2:
450 res.append("%s%s%s" % (a1, sep, a2))
451 else:
452 res.append(a1)
453 return res
454 return _append(uris, base_paths)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500455
456class RpmPM(PackageManager):
457 def __init__(self,
458 d,
459 target_rootfs,
460 target_vendor,
461 task_name='target',
462 providename=None,
463 arch_var=None,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500464 os_var=None,
465 rpm_repo_workdir="oe-rootfs-repo"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466 super(RpmPM, self).__init__(d)
467 self.target_rootfs = target_rootfs
468 self.target_vendor = target_vendor
469 self.task_name = task_name
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500470 if arch_var == None:
471 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
472 else:
473 self.archs = self.d.getVar(arch_var).replace("-","_")
474 if task_name == "host":
475 self.primary_arch = self.d.getVar('SDK_ARCH')
476 else:
477 self.primary_arch = self.d.getVar('MACHINE_ARCH')
478
479 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
480 bb.utils.mkdirhier(self.rpm_repo_dir)
481 oe.path.symlink(self.d.getVar('DEPLOY_DIR_RPM'), oe.path.join(self.rpm_repo_dir, "rpm"), True)
482
483 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
484 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
485 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
486 self.packaging_data_dirs = ['var/lib/rpm', 'var/lib/dnf', 'var/cache/dnf']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500487 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
488 self.task_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500489 if not os.path.exists(self.d.expand('${T}/saved')):
490 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
491
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500492 def _configure_dnf(self):
493 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
494 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
495 # This prevents accidental matching against libsolv's built-in policies
496 if len(archs) <= 1:
497 archs = archs + ["bogusarch"]
498 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
499 bb.utils.mkdirhier(confdir)
500 open(confdir + "arch", 'w').write(":".join(archs))
501 distro_codename = self.d.getVar('DISTRO_CODENAME')
502 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500503
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500504 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500505
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500507 def _configure_rpm(self):
508 # We need to configure rpm to use our primary package architecture as the installation architecture,
509 # and to make it compatible with other package architectures that we use.
510 # Otherwise it will refuse to proceed with packages installation.
511 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
512 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
513 bb.utils.mkdirhier(platformconfdir)
514 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
515 open(rpmrcconfdir + "rpmrc", 'w').write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
516
517 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
518 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
519 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500520 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500521 open(platformconfdir + "macros", 'a').write("%_prefer_color 7")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500522
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500523 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
524 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
525 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
526 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
527 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
528 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
529 try:
530 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
531 except subprocess.CalledProcessError as e:
532 bb.fatal("Importing GPG key failed. Command '%s' "
533 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500534
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500535 def create_configs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500536 self._configure_dnf()
537 self._configure_rpm()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538
539 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500540 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
541 lf = bb.utils.lockfile(lockfilename, False)
542 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
543 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500545 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
546 from urllib.parse import urlparse
547
548 if feed_uris == "":
549 return
550
551 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
552 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
553 for uri in remote_uris:
554 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
555 if feed_archs is not None:
556 for arch in feed_archs.split():
557 repo_uri = uri + "/" + arch
558 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
559 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
560 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
561 "[%s]\nname=%s\nbaseurl=%s\n\n" % (repo_id, repo_name, repo_uri))
562 else:
563 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
564 repo_uri = uri
565 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
566 "[%s]\nname=%s\nbaseurl=%s\n" % (repo_base, repo_name, repo_uri))
567
568 def _prepare_pkg_transaction(self):
569 os.environ['D'] = self.target_rootfs
570 os.environ['OFFLINE_ROOT'] = self.target_rootfs
571 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
572 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
573 os.environ['INTERCEPT_DIR'] = oe.path.join(self.d.getVar('WORKDIR'),
574 "intercept_scripts")
575 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
576
577
578 def install(self, pkgs, attempt_only = False):
579 if len(pkgs) == 0:
580 return
581 self._prepare_pkg_transaction()
582
583 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
584 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
585 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
586
587 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
588 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
589 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
590 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
591 ["install"] +
592 pkgs)
593
594 failed_scriptlets_pkgnames = collections.OrderedDict()
595 for line in output.splitlines():
596 if line.startswith("Non-fatal POSTIN scriptlet failure in rpm package"):
597 failed_scriptlets_pkgnames[line.split()[-1]] = True
598
599 for pkg in failed_scriptlets_pkgnames.keys():
600 self.save_rpmpostinst(pkg)
601
602 def remove(self, pkgs, with_dependencies = True):
603 if len(pkgs) == 0:
604 return
605 self._prepare_pkg_transaction()
606
607 if with_dependencies:
608 self._invoke_dnf(["remove"] + pkgs)
609 else:
610 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
611 args = ["-e", "--nodeps", "--root=%s" %self.target_rootfs]
612
613 try:
614 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
615 except subprocess.CalledProcessError as e:
616 bb.fatal("Could not invoke rpm. Command "
617 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
618
619 def upgrade(self):
620 self._prepare_pkg_transaction()
621 self._invoke_dnf(["upgrade"])
622
623 def autoremove(self):
624 self._prepare_pkg_transaction()
625 self._invoke_dnf(["autoremove"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500626
627 def remove_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500628 self._invoke_dnf(["clean", "all"])
629 for dir in self.packaging_data_dirs:
630 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631
632 def backup_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500633 # Save the packaging dirs for increment rpm image generation
634 if os.path.exists(self.saved_packaging_data):
635 bb.utils.remove(self.saved_packaging_data, True)
636 for i in self.packaging_data_dirs:
637 source_dir = oe.path.join(self.target_rootfs, i)
638 target_dir = oe.path.join(self.saved_packaging_data, i)
639 shutil.copytree(source_dir, target_dir, symlinks=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500640
641 def recovery_packaging_data(self):
642 # Move the rpmlib back
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500643 if os.path.exists(self.saved_packaging_data):
644 for i in self.packaging_data_dirs:
645 target_dir = oe.path.join(self.target_rootfs, i)
646 if os.path.exists(target_dir):
647 bb.utils.remove(target_dir, True)
648 source_dir = oe.path.join(self.saved_packaging_data, i)
649 shutil.copytree(source_dir,
650 target_dir,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651 symlinks=True)
652
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500653 def list_installed(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500654 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
655 print_output = False)
656 packages = {}
657 current_package = None
658 current_deps = None
659 current_state = "initial"
660 for line in output.splitlines():
661 if line.startswith("Package:"):
662 package_info = line.split(" ")[1:]
663 current_package = package_info[0]
664 package_arch = package_info[1]
665 package_version = package_info[2]
666 package_rpm = package_info[3]
667 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
668 current_deps = []
669 elif line.startswith("Dependencies:"):
670 current_state = "dependencies"
671 elif line.startswith("Recommendations"):
672 current_state = "recommendations"
673 elif line.startswith("DependenciesEndHere:"):
674 current_state = "initial"
675 packages[current_package]["deps"] = current_deps
676 elif len(line) > 0:
677 if current_state == "dependencies":
678 current_deps.append(line)
679 elif current_state == "recommendations":
680 current_deps.append("%s [REC]" % line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500681
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500682 return packages
683
684 def update(self):
685 self._invoke_dnf(["makecache"])
686
687 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
688 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
689
690 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
691 standard_dnf_args = (["-v", "--rpmverbosity=debug"] if self.d.getVar('ROOTFS_RPM_DEBUG') else []) + ["-y",
692 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
693 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
694 "--repofrompath=oe-repo,%s" % (self.rpm_repo_dir),
695 "--installroot=%s" % (self.target_rootfs),
696 "--setopt=logdir=%s" % (self.d.getVar('T'))
697 ]
698 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
699 try:
700 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
701 if print_output:
702 bb.note(output)
703 return output
704 except subprocess.CalledProcessError as e:
705 if print_output:
706 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
707 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
708 else:
709 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
710 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
711 return e.output.decode("utf-8")
712
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713 def dump_install_solution(self, pkgs):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500714 open(self.solution_manifest, 'w').write(" ".join(pkgs))
715 return pkgs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717 def load_old_install_solution(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500718 if not os.path.exists(self.solution_manifest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500719 return []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500721 return open(self.solution_manifest, 'r').read().split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500723 def _script_num_prefix(self, path):
724 files = os.listdir(path)
725 numbers = set()
726 numbers.add(99)
727 for f in files:
728 numbers.add(int(f.split("-")[0]))
729 return max(numbers) + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730
731 def save_rpmpostinst(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500732 bb.note("Saving postinstall script of %s" % (pkg))
733 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
734 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735
736 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500737 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738 except subprocess.CalledProcessError as e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500739 bb.fatal("Could not invoke rpm. Command "
740 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500742 # may need to prepend #!/bin/sh to output
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500743
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500744 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
745 bb.utils.mkdirhier(target_path)
746 num = self._script_num_prefix(target_path)
747 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
748 open(saved_script_name, 'w').write(output)
749 os.chmod(saved_script_name, 0o755)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600751 def extract(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500752 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
753 pkg_name = output.splitlines()[-1]
754 if not pkg_name.endswith(".rpm"):
755 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
756 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600757
758 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
759 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
760
761 if not os.path.isfile(pkg_path):
762 bb.fatal("Unable to extract package for '%s'."
763 "File %s doesn't exists" % (pkg, pkg_path))
764
765 tmp_dir = tempfile.mkdtemp()
766 current_dir = os.getcwd()
767 os.chdir(tmp_dir)
768
769 try:
770 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
771 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
772 except subprocess.CalledProcessError as e:
773 bb.utils.remove(tmp_dir, recurse=True)
774 bb.fatal("Unable to extract %s package. Command '%s' "
775 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
776 except OSError as e:
777 bb.utils.remove(tmp_dir, recurse=True)
778 bb.fatal("Unable to extract %s package. Command '%s' "
779 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
780
781 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
782 os.chdir(current_dir)
783
784 return tmp_dir
785
786
787class OpkgDpkgPM(PackageManager):
788 """
789 This is an abstract class. Do not instantiate this directly.
790 """
791 def __init__(self, d):
792 super(OpkgDpkgPM, self).__init__(d)
793
794 """
795 Returns a dictionary with the package info.
796
797 This method extracts the common parts for Opkg and Dpkg
798 """
799 def package_info(self, pkg, cmd):
800
801 try:
802 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
803 except subprocess.CalledProcessError as e:
804 bb.fatal("Unable to list available packages. Command '%s' "
805 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
806 return opkg_query(output)
807
808 """
809 Returns the path to a tmpdir where resides the contents of a package.
810
811 Deleting the tmpdir is responsability of the caller.
812
813 This method extracts the common parts for Opkg and Dpkg
814 """
815 def extract(self, pkg, pkg_info):
816
817 ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
818 tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
819 pkg_path = pkg_info[pkg]["filepath"]
820
821 if not os.path.isfile(pkg_path):
822 bb.fatal("Unable to extract package for '%s'."
823 "File %s doesn't exists" % (pkg, pkg_path))
824
825 tmp_dir = tempfile.mkdtemp()
826 current_dir = os.getcwd()
827 os.chdir(tmp_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500828 if self.d.getVar('IMAGE_PKGTYPE') == 'deb':
829 data_tar = 'data.tar.xz'
830 else:
831 data_tar = 'data.tar.gz'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832
833 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500834 cmd = [ar_cmd, 'x', pkg_path]
835 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
836 cmd = [tar_cmd, 'xf', data_tar]
837 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 except subprocess.CalledProcessError as e:
839 bb.utils.remove(tmp_dir, recurse=True)
840 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500841 "returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 except OSError as e:
843 bb.utils.remove(tmp_dir, recurse=True)
844 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500845 "returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846
847 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
848 bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
849 bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
850 os.chdir(current_dir)
851
852 return tmp_dir
853
854
855class OpkgPM(OpkgDpkgPM):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500856 def __init__(self, d, target_rootfs, config_file, archs, task_name='target'):
857 super(OpkgPM, self).__init__(d)
858
859 self.target_rootfs = target_rootfs
860 self.config_file = config_file
861 self.pkg_archs = archs
862 self.task_name = task_name
863
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500864 self.deploy_dir = self.d.getVar("DEPLOY_DIR_IPK")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500865 self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
866 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600867 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 -0500868 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500869
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500870 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500871 if opkg_lib_dir[0] == "/":
872 opkg_lib_dir = opkg_lib_dir[1:]
873
874 self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
875
876 bb.utils.mkdirhier(self.opkg_dir)
877
878 self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
879 if not os.path.exists(self.d.expand('${T}/saved')):
880 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
881
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500882 self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500883 if self.from_feeds:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884 self._create_custom_config()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500885 else:
886 self._create_config()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500887
888 self.indexer = OpkgIndexer(self.d, self.deploy_dir)
889
890 """
891 This function will change a package's status in /var/lib/opkg/status file.
892 If 'packages' is None then the new_status will be applied to all
893 packages
894 """
895 def mark_packages(self, status_tag, packages=None):
896 status_file = os.path.join(self.opkg_dir, "status")
897
898 with open(status_file, "r") as sf:
899 with open(status_file + ".tmp", "w+") as tmp_sf:
900 if packages is None:
901 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
902 r"Package: \1\n\2Status: \3%s" % status_tag,
903 sf.read()))
904 else:
905 if type(packages).__name__ != "list":
906 raise TypeError("'packages' should be a list object")
907
908 status = sf.read()
909 for pkg in packages:
910 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
911 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
912 status)
913
914 tmp_sf.write(status)
915
916 os.rename(status_file + ".tmp", status_file)
917
918 def _create_custom_config(self):
919 bb.note("Building from feeds activated!")
920
921 with open(self.config_file, "w+") as config_file:
922 priority = 1
923 for arch in self.pkg_archs.split():
924 config_file.write("arch %s %d\n" % (arch, priority))
925 priority += 5
926
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500927 for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500928 feed_match = re.match("^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
929
930 if feed_match is not None:
931 feed_name = feed_match.group(1)
932 feed_uri = feed_match.group(2)
933
934 bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
935
936 config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
937
938 """
939 Allow to use package deploy directory contents as quick devel-testing
940 feed. This creates individual feed configs for each arch subdir of those
941 specified as compatible for the current machine.
942 NOTE: Development-helper feature, NOT a full-fledged feed.
943 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500944 if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500945 for arch in self.pkg_archs.split():
946 cfg_file_name = os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500947 self.d.getVar("sysconfdir"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500948 "opkg",
949 "local-%s-feed.conf" % arch)
950
951 with open(cfg_file_name, "w+") as cfg_file:
952 cfg_file.write("src/gz local-%s %s/%s" %
953 (arch,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500954 self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500955 arch))
956
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500957 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500958 # There is no command line option for this anymore, we need to add
959 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
960 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500961 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
962 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
963 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500964 cfg_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
965 cfg_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
966 cfg_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500967
968
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500969 def _create_config(self):
970 with open(self.config_file, "w+") as config_file:
971 priority = 1
972 for arch in self.pkg_archs.split():
973 config_file.write("arch %s %d\n" % (arch, priority))
974 priority += 5
975
976 config_file.write("src oe file:%s\n" % self.deploy_dir)
977
978 for arch in self.pkg_archs.split():
979 pkgs_dir = os.path.join(self.deploy_dir, arch)
980 if os.path.isdir(pkgs_dir):
981 config_file.write("src oe-%s file:%s\n" %
982 (arch, pkgs_dir))
983
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500984 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500985 # There is no command line option for this anymore, we need to add
986 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
987 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500988 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
989 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
990 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500991 config_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
992 config_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
993 config_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500994
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500995 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
996 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500997 return
998
999 rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
1000 % self.target_rootfs)
1001
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001002 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
1003 archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001004
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001005 with open(rootfs_config, "w+") as config_file:
1006 uri_iterator = 0
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001007 for uri in feed_uris:
1008 if archs:
1009 for arch in archs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001010 if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001011 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001012 bb.note('Adding opkg feed url-%s-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001013 (arch, uri_iterator, uri))
1014 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1015 (arch, uri_iterator, uri, arch))
1016 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001017 bb.note('Adding opkg feed url-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001018 (uri_iterator, uri))
1019 config_file.write("src/gz uri-%d %s\n" %
1020 (uri_iterator, uri))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001021
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001022 uri_iterator += 1
1023
1024 def update(self):
1025 self.deploy_dir_lock()
1026
1027 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1028
1029 try:
1030 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1031 except subprocess.CalledProcessError as e:
1032 self.deploy_dir_unlock()
1033 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001034 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001035
1036 self.deploy_dir_unlock()
1037
1038 def install(self, pkgs, attempt_only=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001039 if not pkgs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001040 return
1041
1042 cmd = "%s %s install %s" % (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1043
1044 os.environ['D'] = self.target_rootfs
1045 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1046 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1047 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001048 os.environ['INTERCEPT_DIR'] = os.path.join(self.d.getVar('WORKDIR'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001049 "intercept_scripts")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001050 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001051
1052 try:
1053 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1054 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001055 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001056 bb.note(output)
1057 except subprocess.CalledProcessError as e:
1058 (bb.fatal, bb.note)[attempt_only]("Unable to install packages. "
1059 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001060 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001061
1062 def remove(self, pkgs, with_dependencies=True):
1063 if with_dependencies:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001064 cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001065 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1066 else:
1067 cmd = "%s %s --force-depends remove %s" % \
1068 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1069
1070 try:
1071 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001072 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001073 bb.note(output)
1074 except subprocess.CalledProcessError as e:
1075 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001076 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001077
1078 def write_index(self):
1079 self.deploy_dir_lock()
1080
1081 result = self.indexer.write_index()
1082
1083 self.deploy_dir_unlock()
1084
1085 if result is not None:
1086 bb.fatal(result)
1087
1088 def remove_packaging_data(self):
1089 bb.utils.remove(self.opkg_dir, True)
1090 # create the directory back, it's needed by PM lock
1091 bb.utils.mkdirhier(self.opkg_dir)
1092
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001093 def remove_lists(self):
1094 if not self.from_feeds:
1095 bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
1096
1097 def list_installed(self):
1098 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001099
1100 def handle_bad_recommendations(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001101 bad_recommendations = self.d.getVar("BAD_RECOMMENDATIONS") or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001102 if bad_recommendations.strip() == "":
1103 return
1104
1105 status_file = os.path.join(self.opkg_dir, "status")
1106
1107 # If status file existed, it means the bad recommendations has already
1108 # been handled
1109 if os.path.exists(status_file):
1110 return
1111
1112 cmd = "%s %s info " % (self.opkg_cmd, self.opkg_args)
1113
1114 with open(status_file, "w+") as status:
1115 for pkg in bad_recommendations.split():
1116 pkg_info = cmd + pkg
1117
1118 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001119 output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001120 except subprocess.CalledProcessError as e:
1121 bb.fatal("Cannot get package info. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122 "returned %d:\n%s" % (pkg_info, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001123
1124 if output == "":
1125 bb.note("Ignored bad recommendation: '%s' is "
1126 "not a package" % pkg)
1127 continue
1128
1129 for line in output.split('\n'):
1130 if line.startswith("Status:"):
1131 status.write("Status: deinstall hold not-installed\n")
1132 else:
1133 status.write(line + "\n")
1134
1135 # Append a blank line after each package entry to ensure that it
1136 # is separated from the following entry
1137 status.write("\n")
1138
1139 '''
1140 The following function dummy installs pkgs and returns the log of output.
1141 '''
1142 def dummy_install(self, pkgs):
1143 if len(pkgs) == 0:
1144 return
1145
1146 # Create an temp dir as opkg root for dummy installation
1147 temp_rootfs = self.d.expand('${T}/opkg')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001148 opkg_lib_dir = self.d.getVar('OPKGLIBDIR', True)
1149 if opkg_lib_dir[0] == "/":
1150 opkg_lib_dir = opkg_lib_dir[1:]
1151 temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001152 bb.utils.mkdirhier(temp_opkg_dir)
1153
1154 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001155 opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001156
1157 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1158 try:
1159 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1160 except subprocess.CalledProcessError as e:
1161 bb.fatal("Unable to update. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001163
1164 # Dummy installation
1165 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1166 opkg_args,
1167 ' '.join(pkgs))
1168 try:
1169 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1170 except subprocess.CalledProcessError as e:
1171 bb.fatal("Unable to dummy install packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001172 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001173
1174 bb.utils.remove(temp_rootfs, True)
1175
1176 return output
1177
1178 def backup_packaging_data(self):
1179 # Save the opkglib for increment ipk image generation
1180 if os.path.exists(self.saved_opkg_dir):
1181 bb.utils.remove(self.saved_opkg_dir, True)
1182 shutil.copytree(self.opkg_dir,
1183 self.saved_opkg_dir,
1184 symlinks=True)
1185
1186 def recover_packaging_data(self):
1187 # Move the opkglib back
1188 if os.path.exists(self.saved_opkg_dir):
1189 if os.path.exists(self.opkg_dir):
1190 bb.utils.remove(self.opkg_dir, True)
1191
1192 bb.note('Recover packaging data')
1193 shutil.copytree(self.saved_opkg_dir,
1194 self.opkg_dir,
1195 symlinks=True)
1196
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001197 """
1198 Returns a dictionary with the package info.
1199 """
1200 def package_info(self, pkg):
1201 cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
1202 pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001203
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001204 pkg_arch = pkg_info[pkg]["arch"]
1205 pkg_filename = pkg_info[pkg]["filename"]
1206 pkg_info[pkg]["filepath"] = \
1207 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1208
1209 return pkg_info
1210
1211 """
1212 Returns the path to a tmpdir where resides the contents of a package.
1213
1214 Deleting the tmpdir is responsability of the caller.
1215 """
1216 def extract(self, pkg):
1217 pkg_info = self.package_info(pkg)
1218 if not pkg_info:
1219 bb.fatal("Unable to get information for package '%s' while "
1220 "trying to extract the package." % pkg)
1221
1222 tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
1223 bb.utils.remove(os.path.join(tmp_dir, "data.tar.gz"))
1224
1225 return tmp_dir
1226
1227class DpkgPM(OpkgDpkgPM):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001228 def __init__(self, d, target_rootfs, archs, base_archs, apt_conf_dir=None):
1229 super(DpkgPM, self).__init__(d)
1230 self.target_rootfs = target_rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 self.deploy_dir = self.d.getVar('DEPLOY_DIR_DEB')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001232 if apt_conf_dir is None:
1233 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1234 else:
1235 self.apt_conf_dir = apt_conf_dir
1236 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1237 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001238 self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001239
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001240 self.apt_args = d.getVar("APT_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001241
1242 self.all_arch_list = archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001243 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001244 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1245
1246 self._create_configs(archs, base_archs)
1247
1248 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1249
1250 """
1251 This function will change a package's status in /var/lib/dpkg/status file.
1252 If 'packages' is None then the new_status will be applied to all
1253 packages
1254 """
1255 def mark_packages(self, status_tag, packages=None):
1256 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1257
1258 with open(status_file, "r") as sf:
1259 with open(status_file + ".tmp", "w+") as tmp_sf:
1260 if packages is None:
1261 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1262 r"Package: \1\n\2Status: \3%s" % status_tag,
1263 sf.read()))
1264 else:
1265 if type(packages).__name__ != "list":
1266 raise TypeError("'packages' should be a list object")
1267
1268 status = sf.read()
1269 for pkg in packages:
1270 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1271 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1272 status)
1273
1274 tmp_sf.write(status)
1275
1276 os.rename(status_file + ".tmp", status_file)
1277
1278 """
1279 Run the pre/post installs for package "package_name". If package_name is
1280 None, then run all pre/post install scriptlets.
1281 """
1282 def run_pre_post_installs(self, package_name=None):
1283 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001284 ControlScript = collections.namedtuple("ControlScript", ["suffix", "name", "argument"])
1285 control_scripts = [
1286 ControlScript(".preinst", "Preinstall", "install"),
1287 ControlScript(".postinst", "Postinstall", "configure")]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001288 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1289 installed_pkgs = []
1290
1291 with open(status_file, "r") as status:
1292 for line in status.read().split('\n'):
1293 m = re.match("^Package: (.*)", line)
1294 if m is not None:
1295 installed_pkgs.append(m.group(1))
1296
1297 if package_name is not None and not package_name in installed_pkgs:
1298 return
1299
1300 os.environ['D'] = self.target_rootfs
1301 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1302 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1303 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001304 os.environ['INTERCEPT_DIR'] = os.path.join(self.d.getVar('WORKDIR'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001305 "intercept_scripts")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001306 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001307
1308 failed_pkgs = []
1309 for pkg_name in installed_pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310 for control_script in control_scripts:
1311 p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001312 if os.path.exists(p_full):
1313 try:
1314 bb.note("Executing %s for package: %s ..." %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001315 (control_script.name.lower(), pkg_name))
1316 output = subprocess.check_output([p_full, control_script.argument],
1317 stderr=subprocess.STDOUT).decode("utf-8")
1318 bb.note(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001319 except subprocess.CalledProcessError as e:
1320 bb.note("%s for package %s failed with %d:\n%s" %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001321 (control_script.name, pkg_name, e.returncode,
1322 e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001323 failed_pkgs.append(pkg_name)
1324 break
1325
1326 if len(failed_pkgs):
1327 self.mark_packages("unpacked", failed_pkgs)
1328
1329 def update(self):
1330 os.environ['APT_CONFIG'] = self.apt_conf_file
1331
1332 self.deploy_dir_lock()
1333
1334 cmd = "%s update" % self.apt_get_cmd
1335
1336 try:
1337 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1338 except subprocess.CalledProcessError as e:
1339 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001340 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001341
1342 self.deploy_dir_unlock()
1343
1344 def install(self, pkgs, attempt_only=False):
1345 if attempt_only and len(pkgs) == 0:
1346 return
1347
1348 os.environ['APT_CONFIG'] = self.apt_conf_file
1349
1350 cmd = "%s %s install --force-yes --allow-unauthenticated %s" % \
1351 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1352
1353 try:
1354 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1355 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1356 except subprocess.CalledProcessError as e:
1357 (bb.fatal, bb.note)[attempt_only]("Unable to install packages. "
1358 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001359 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001360
1361 # rename *.dpkg-new files/dirs
1362 for root, dirs, files in os.walk(self.target_rootfs):
1363 for dir in dirs:
1364 new_dir = re.sub("\.dpkg-new", "", dir)
1365 if dir != new_dir:
1366 os.rename(os.path.join(root, dir),
1367 os.path.join(root, new_dir))
1368
1369 for file in files:
1370 new_file = re.sub("\.dpkg-new", "", file)
1371 if file != new_file:
1372 os.rename(os.path.join(root, file),
1373 os.path.join(root, new_file))
1374
1375
1376 def remove(self, pkgs, with_dependencies=True):
1377 if with_dependencies:
1378 os.environ['APT_CONFIG'] = self.apt_conf_file
1379 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1380 else:
1381 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1382 " -P --force-depends %s" % \
1383 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1384 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1385
1386 try:
1387 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1388 except subprocess.CalledProcessError as e:
1389 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001390 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001391
1392 def write_index(self):
1393 self.deploy_dir_lock()
1394
1395 result = self.indexer.write_index()
1396
1397 self.deploy_dir_unlock()
1398
1399 if result is not None:
1400 bb.fatal(result)
1401
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001402 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1403 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001404 return
1405
1406 sources_conf = os.path.join("%s/etc/apt/sources.list"
1407 % self.target_rootfs)
1408 arch_list = []
1409
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001410 if feed_archs is None:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001411 for arch in self.all_arch_list:
1412 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1413 continue
1414 arch_list.append(arch)
1415 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001416 arch_list = feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001417
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001418 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001419
1420 with open(sources_conf, "w+") as sources_file:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001421 for uri in feed_uris:
1422 if arch_list:
1423 for arch in arch_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001424 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001425 sources_file.write("deb %s/%s ./\n" %
1426 (uri, arch))
1427 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001428 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001429 sources_file.write("deb %s ./\n" % uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001430
1431 def _create_configs(self, archs, base_archs):
1432 base_archs = re.sub("_", "-", base_archs)
1433
1434 if os.path.exists(self.apt_conf_dir):
1435 bb.utils.remove(self.apt_conf_dir, True)
1436
1437 bb.utils.mkdirhier(self.apt_conf_dir)
1438 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1439 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001440 bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441
1442 arch_list = []
1443 for arch in self.all_arch_list:
1444 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1445 continue
1446 arch_list.append(arch)
1447
1448 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1449 priority = 801
1450 for arch in arch_list:
1451 prefs_file.write(
1452 "Package: *\n"
1453 "Pin: release l=%s\n"
1454 "Pin-Priority: %d\n\n" % (arch, priority))
1455
1456 priority += 5
1457
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001458 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001459 for pkg in pkg_exclude.split():
1460 prefs_file.write(
1461 "Package: %s\n"
1462 "Pin: release *\n"
1463 "Pin-Priority: -1\n\n" % pkg)
1464
1465 arch_list.reverse()
1466
1467 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1468 for arch in arch_list:
1469 sources_file.write("deb file:%s/ ./\n" %
1470 os.path.join(self.deploy_dir, arch))
1471
1472 base_arch_list = base_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001473 multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001474 for variant in multilib_variants.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001475 localdata = bb.data.createCopy(self.d)
1476 variant_tune = localdata.getVar("DEFAULTTUNE_virtclass-multilib-" + variant, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001477 orig_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001478 localdata.setVar("DEFAULTTUNE", variant_tune)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001479 variant_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001480 if variant_arch not in base_arch_list:
1481 base_arch_list.append(variant_arch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001482
1483 with open(self.apt_conf_file, "w+") as apt_conf:
1484 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1485 for line in apt_conf_sample.read().split("\n"):
1486 match_arch = re.match(" Architecture \".*\";$", line)
1487 architectures = ""
1488 if match_arch:
1489 for base_arch in base_arch_list:
1490 architectures += "\"%s\";" % base_arch
1491 apt_conf.write(" Architectures {%s};\n" % architectures);
1492 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1493 else:
1494 line = re.sub("#ROOTFS#", self.target_rootfs, line)
1495 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
1496 apt_conf.write(line + "\n")
1497
1498 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1499 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1500
1501 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1502
1503 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1504 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1505 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1506 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1507
1508 def remove_packaging_data(self):
1509 bb.utils.remove(os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001510 self.d.getVar('opkglibdir')), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001511 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1512
1513 def fix_broken_dependencies(self):
1514 os.environ['APT_CONFIG'] = self.apt_conf_file
1515
1516 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1517
1518 try:
1519 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1520 except subprocess.CalledProcessError as e:
1521 bb.fatal("Cannot fix broken dependencies. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001522 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001523
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001524 def list_installed(self):
1525 return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001526
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001527 """
1528 Returns a dictionary with the package info.
1529 """
1530 def package_info(self, pkg):
1531 cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
1532 pkg_info = super(DpkgPM, self).package_info(pkg, cmd)
1533
1534 pkg_arch = pkg_info[pkg]["pkgarch"]
1535 pkg_filename = pkg_info[pkg]["filename"]
1536 pkg_info[pkg]["filepath"] = \
1537 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1538
1539 return pkg_info
1540
1541 """
1542 Returns the path to a tmpdir where resides the contents of a package.
1543
1544 Deleting the tmpdir is responsability of the caller.
1545 """
1546 def extract(self, pkg):
1547 pkg_info = self.package_info(pkg)
1548 if not pkg_info:
1549 bb.fatal("Unable to get information for package '%s' while "
1550 "trying to extract the package." % pkg)
1551
1552 tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info)
1553 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
1554
1555 return tmp_dir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001556
1557def generate_index_files(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001558 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001559
1560 indexer_map = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001561 "rpm": (RpmIndexer, d.getVar('DEPLOY_DIR_RPM')),
1562 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK')),
1563 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001564 }
1565
1566 result = None
1567
1568 for pkg_class in classes:
1569 if not pkg_class in indexer_map:
1570 continue
1571
1572 if os.path.exists(indexer_map[pkg_class][1]):
1573 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1574
1575 if result is not None:
1576 bb.fatal(result)
1577
1578if __name__ == "__main__":
1579 """
1580 We should be able to run this as a standalone script, from outside bitbake
1581 environment.
1582 """
1583 """
1584 TBD
1585 """